Merge tag 'android-15.0.0_r32' of https://android.googlesource.com/platform/build/soong into HEAD

Android 15.0.0 Release 32 (BP1A.250505.005)

Change-Id: I4d4089ca9f4a9c113a8b4f7104aae0bcb9baa4e3

# -----BEGIN PGP SIGNATURE-----
#
# iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCaBqG7QAKCRDorT+BmrEO
# eD5/AJ9Qhikcz0UKFmAeaDNUtNQBzNJpkgCfTwX+qTs9G/fvJnyzyJoOqDRTHk0=
# =BPrV
# -----END PGP SIGNATURE-----
# gpg: Signature faite le mar 06 mai 2025 18:02:21 EDT
# gpg:                avec la clef DSA 4340D13570EF945E83810964E8AD3F819AB10E78
# gpg: Impossible de vérifier la signature : Pas de clef publique
diff --git a/Android.bp b/Android.bp
index 535246e..434ee9f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,6 +3,7 @@
     default_visibility: [
         "//build/soong:__subpackages__",
     ],
+    default_team: "trendy_team_build",
 }
 
 subdirs = [
@@ -116,6 +117,11 @@
     visibility: ["//visibility:public"],
 }
 
+art_boot_images {
+    name: "art_boot_images",
+    visibility: ["//art:__subpackages__"],
+}
+
 // Pseudo-test that's run on checkbuilds to ensure that get_clang_version can
 // parse cc/config/global.go.
 genrule {
@@ -136,19 +142,45 @@
     visibility: ["//visibility:public"],
 }
 
+// Defaults to share configs between "baremetal" Soong modules, currently only
+// used for code running in kernel context within Android Virtualization
+// Framework guests.
+cc_defaults {
+    name: "cc_baremetal_defaults",
+    arch: {
+        arm64: {
+            cflags: [
+                // Prevent the compiler from optimizing code using SVE, as the
+                // baremetal environment might not have configured the hardware.
+                "-Xclang -target-feature",
+                "-Xclang -sve",
+            ],
+        },
+    },
+    defaults_visibility: ["//visibility:public"],
+}
+
 product_config {
     name: "product_config",
-    visibility: ["//device/google/cuttlefish/system_image"],
+    visibility: [
+        "//build/make/target/product/generic",
+        "//build/soong/fsgen",
+    ],
 }
 
 build_prop {
     name: "system-build.prop",
     stem: "build.prop",
     product_config: ":product_config",
-    // Currently, only microdroid and cf system image can refer to system-build.prop
+    footer_files: [
+        ":applied_backported_fixes",
+    ],
+    // Currently, only microdroid, Ravenwood, and cf system image can refer to system-build.prop
     visibility: [
-        "//device/google/cuttlefish/system_image",
+        "//build/make/target/product/generic",
+        "//build/make/target/product/gsi",
         "//packages/modules/Virtualization/build/microdroid",
+        "//frameworks/base/ravenwood",
     ],
 }
 
@@ -158,7 +190,7 @@
     system_ext_specific: true,
     product_config: ":product_config",
     relative_install_path: "etc", // system_ext/etc/build.prop
-    visibility: ["//visibility:private"],
+    visibility: ["//build/make/target/product/gsi"],
 }
 
 build_prop {
@@ -167,7 +199,7 @@
     product_specific: true,
     product_config: ":product_config",
     relative_install_path: "etc", // product/etc/build.prop
-    visibility: ["//visibility:private"],
+    visibility: ["//build/make/target/product/gsi"],
 }
 
 build_prop {
@@ -178,3 +210,39 @@
     relative_install_path: "etc", // odm/etc/build.prop
     visibility: ["//visibility:private"],
 }
+
+build_prop {
+    name: "system_dlkm-build.prop",
+    stem: "build.prop",
+    system_dlkm_specific: true,
+    product_config: ":product_config",
+    relative_install_path: "etc", // system_dlkm/etc/build.prop
+    visibility: ["//visibility:private"],
+}
+
+build_prop {
+    name: "vendor_dlkm-build.prop",
+    stem: "build.prop",
+    vendor_dlkm_specific: true,
+    product_config: ":product_config",
+    relative_install_path: "etc", // vendor_dlkm/etc/build.prop
+    visibility: ["//visibility:private"],
+}
+
+build_prop {
+    name: "odm_dlkm-build.prop",
+    stem: "build.prop",
+    odm_dlkm_specific: true,
+    product_config: ":product_config",
+    relative_install_path: "etc", // odm_dlkm/etc/build.prop
+    visibility: ["//visibility:private"],
+}
+
+build_prop {
+    name: "ramdisk-build.prop",
+    stem: "build.prop",
+    ramdisk: true,
+    product_config: ":product_config",
+    relative_install_path: "etc/ramdisk", // ramdisk/system/etc/ramdisk/build.prop
+    visibility: ["//visibility:private"],
+}
diff --git a/README.md b/README.md
index ad282a5..6d1a2c5 100644
--- a/README.md
+++ b/README.md
@@ -673,8 +673,7 @@
 SOONG_DELVE=2345 SOONG_DELVE_STEPS='build,modulegraph' m
 ```
 results in only `build` (main build step) and `modulegraph` being run in the debugger.
-The allowed step names are `bp2build_files`, `bp2build_workspace`, `build`,
-`modulegraph`, `queryview`, `soong_docs`.
+The allowed step names are `build`, `soong_docs`.
 
 Note setting or unsetting `SOONG_DELVE` causes a recompilation of `soong_build`. This
 is because in order to debug the binary, it needs to be built with debug
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index d9a862c..9a9e568 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -15,12 +15,12 @@
 package aconfig
 
 import (
+	"android/soong/android"
 	"path/filepath"
 	"slices"
+	"strconv"
 	"strings"
 
-	"android/soong/android"
-
 	"github.com/google/blueprint"
 )
 
@@ -185,6 +185,13 @@
 				defaultPermission = confPerm
 			}
 		}
+		var allowReadWrite bool
+		if requireAllReadOnly, ok := ctx.Config().GetBuildFlag("RELEASE_ACONFIG_REQUIRE_ALL_READ_ONLY"); ok {
+			// The build flag (RELEASE_ACONFIG_REQUIRE_ALL_READ_ONLY) is the negation of the aconfig flag
+			// (allow-read-write) for historical reasons.
+			// Bool build flags are always "" for false, and generally "true" for true.
+			allowReadWrite = requireAllReadOnly == ""
+		}
 		inputFiles := make([]android.Path, len(declarationFiles))
 		copy(inputFiles, declarationFiles)
 		inputFiles = append(inputFiles, valuesFiles[config]...)
@@ -194,6 +201,7 @@
 			"declarations":       android.JoinPathsWithPrefix(declarationFiles, "--declarations "),
 			"values":             joinAndPrefix(" --values ", values[config]),
 			"default-permission": optionalVariable(" --default-permission ", defaultPermission),
+			"allow-read-write":   optionalVariable(" --allow-read-write ", strconv.FormatBool(allowReadWrite)),
 		}
 		if len(module.properties.Container) > 0 {
 			args["container"] = "--container " + module.properties.Container
diff --git a/aconfig/build_flags/Android.bp b/aconfig/build_flags/Android.bp
index b3c7339..139aeac 100644
--- a/aconfig/build_flags/Android.bp
+++ b/aconfig/build_flags/Android.bp
@@ -13,10 +13,11 @@
         "soong-android",
     ],
     srcs: [
-        "all_build_flag_declarations.go",
         "build_flags.go",
+        "build_flags_singleton.go",
         "declarations.go",
         "init.go",
+        "release_configs.go",
     ],
     testSrcs: [
     ],
diff --git a/aconfig/build_flags/all_build_flag_declarations.go b/aconfig/build_flags/all_build_flag_declarations.go
deleted file mode 100644
index 5f02912..0000000
--- a/aconfig/build_flags/all_build_flag_declarations.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// 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.OtherModuleProvider(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
index e878b5a..94e1eb1 100644
--- a/aconfig/build_flags/build_flags.go
+++ b/aconfig/build_flags/build_flags.go
@@ -35,7 +35,7 @@
 type buildFlags struct {
 	android.ModuleBase
 
-	outputPath android.OutputPath
+	outputPath android.Path
 }
 
 func buildFlagsFactory() android.Module {
@@ -48,7 +48,7 @@
 	// 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
+	outputPath := android.PathForModuleOut(ctx, outJsonFileName)
 
 	// The 'release-config' command is called for every build, and generates the
 	// build_flags_<partition>.json file.
@@ -56,11 +56,12 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   android.CpIfChanged,
 		Input:  srcPath,
-		Output: m.outputPath,
+		Output: outputPath,
 	})
 
 	installPath := android.PathForModuleInstall(ctx, "etc")
-	ctx.InstallFile(installPath, outJsonFileName, m.outputPath)
+	ctx.InstallFile(installPath, outJsonFileName, outputPath)
+	m.outputPath = outputPath
 }
 
 func (m *buildFlags) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/aconfig/build_flags/build_flags_singleton.go b/aconfig/build_flags/build_flags_singleton.go
new file mode 100644
index 0000000..3b40755
--- /dev/null
+++ b/aconfig/build_flags/build_flags_singleton.go
@@ -0,0 +1,123 @@
+// 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 {
+	flagsBinaryProtoPath   android.OutputPath
+	flagsTextProtoPath     android.OutputPath
+	configsBinaryProtoPath android.OutputPath
+	configsTextProtoPath   android.OutputPath
+}
+
+func (this *allBuildFlagDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	// Find all of the build_flag_declarations modules
+	var flagsFiles android.Paths
+	// Find all of the release_config_contribution modules
+	var contributionDirs android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		decl, ok := android.OtherModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
+		if ok {
+			flagsFiles = append(flagsFiles, decl.IntermediateCacheOutputPath)
+		}
+
+		contrib, ok := android.OtherModuleProvider(ctx, module, ReleaseConfigContributionsProviderKey)
+		if ok {
+			contributionDirs = append(contributionDirs, contrib.ContributionDir)
+		}
+	})
+
+	// Generate build action for build_flag (binary proto output)
+	this.flagsBinaryProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allDeclarationsRule,
+		Inputs:      flagsFiles,
+		Output:      this.flagsBinaryProtoPath,
+		Description: "all_build_flag_declarations",
+		Args: map[string]string{
+			"intermediates": android.JoinPathsWithPrefix(flagsFiles, "--intermediate "),
+		},
+	})
+	ctx.Phony("all_build_flag_declarations", this.flagsBinaryProtoPath)
+
+	// Generate build action for build_flag (text proto output)
+	this.flagsTextProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.textproto")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allDeclarationsRuleTextProto,
+		Input:       this.flagsBinaryProtoPath,
+		Output:      this.flagsTextProtoPath,
+		Description: "all_build_flag_declarations_textproto",
+	})
+	ctx.Phony("all_build_flag_declarations_textproto", this.flagsTextProtoPath)
+
+	// Generate build action for release_configs (binary proto output)
+	this.configsBinaryProtoPath = android.PathForIntermediates(ctx, "all_release_config_contributions.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allReleaseConfigContributionsRule,
+		Inputs:      contributionDirs,
+		Output:      this.configsBinaryProtoPath,
+		Description: "all_release_config_contributions",
+		Args: map[string]string{
+			"dirs":   android.JoinPathsWithPrefix(contributionDirs, "--dir "),
+			"format": "pb",
+		},
+	})
+	ctx.Phony("all_release_config_contributions", this.configsBinaryProtoPath)
+
+	this.configsTextProtoPath = android.PathForIntermediates(ctx, "all_release_config_contributions.textproto")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allReleaseConfigContributionsRule,
+		Inputs:      contributionDirs,
+		Output:      this.configsTextProtoPath,
+		Description: "all_release_config_contributions_textproto",
+		Args: map[string]string{
+			"dirs":   android.JoinPathsWithPrefix(contributionDirs, "--dir "),
+			"format": "textproto",
+		},
+	})
+	ctx.Phony("all_release_config_contributions_textproto", this.configsTextProtoPath)
+
+	// Add a simple target for ci/build_metadata to use.
+	ctx.Phony("release_config_metadata",
+		this.flagsBinaryProtoPath,
+		this.flagsTextProtoPath,
+		this.configsBinaryProtoPath,
+		this.configsTextProtoPath,
+	)
+}
+
+func (this *allBuildFlagDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoal("droid", this.flagsBinaryProtoPath)
+	for _, goal := range []string{"docs", "droid", "sdk", "release_config_metadata"} {
+		ctx.DistForGoalWithFilename(goal, this.flagsBinaryProtoPath, "build_flags/all_flags.pb")
+		ctx.DistForGoalWithFilename(goal, this.flagsTextProtoPath, "build_flags/all_flags.textproto")
+		ctx.DistForGoalWithFilename(goal, this.configsBinaryProtoPath, "build_flags/all_release_config_contributions.pb")
+		ctx.DistForGoalWithFilename(goal, this.configsTextProtoPath, "build_flags/all_release_config_contributions.textproto")
+	}
+}
diff --git a/aconfig/build_flags/declarations.go b/aconfig/build_flags/declarations.go
index e927db2..4a54269 100644
--- a/aconfig/build_flags/declarations.go
+++ b/aconfig/build_flags/declarations.go
@@ -35,7 +35,7 @@
 
 	// Properties for "aconfig_declarations"
 	properties struct {
-		// aconfig files, relative to this Android.bp file
+		// build flag declaration files, relative to this Android.bp file
 		Srcs []string `android:"path"`
 	}
 }
diff --git a/aconfig/build_flags/init.go b/aconfig/build_flags/init.go
index dc1369c..a7575e8 100644
--- a/aconfig/build_flags/init.go
+++ b/aconfig/build_flags/init.go
@@ -65,15 +65,32 @@
 				"${buildFlagDeclarations}",
 			},
 		})
+
+	allReleaseConfigContributionsRule = pctx.AndroidStaticRule("all-release-config-contributions-dump",
+		blueprint.RuleParams{
+			Command: `${releaseConfigContributions} ${dirs} --format ${format} --output ${out}`,
+			CommandDeps: []string{
+				"${releaseConfigContributions}",
+			},
+		}, "dirs", "format")
+	allReleaseConfigContributionsRuleText = pctx.AndroidStaticRule("all-release-config-contributions-dumptext",
+		blueprint.RuleParams{
+			Command: `${releaseConfigContributions} ${dirs} --format ${format} --output ${out}`,
+			CommandDeps: []string{
+				"${releaseConfigContributions}",
+			},
+		}, "dirs", "format")
 )
 
 func init() {
 	RegisterBuildComponents(android.InitRegistrationContext)
 	pctx.Import("android/soong/android")
 	pctx.HostBinToolVariable("buildFlagDeclarations", "build-flag-declarations")
+	pctx.HostBinToolVariable("releaseConfigContributions", "release-config-contributions")
 }
 
 func RegisterBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("build_flag_declarations", DeclarationsFactory)
+	ctx.RegisterModuleType("release_config_contributions", ReleaseConfigContributionsFactory)
 	ctx.RegisterParallelSingletonType("all_build_flag_declarations", AllBuildFlagDeclarationsFactory)
 }
diff --git a/aconfig/build_flags/release_configs.go b/aconfig/build_flags/release_configs.go
new file mode 100644
index 0000000..3fa8a7c
--- /dev/null
+++ b/aconfig/build_flags/release_configs.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 (
+	"path/filepath"
+
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+type ReleaseConfigContributionsProviderData struct {
+	ContributionDir android.SourcePath
+}
+
+var ReleaseConfigContributionsProviderKey = blueprint.NewProvider[ReleaseConfigContributionsProviderData]()
+
+// Soong uses `release_config_contributions` modules to produce the
+// `build_flags/all_release_config_contributions.*` artifacts, listing *all* of
+// the directories in the source tree that contribute to each release config,
+// whether or not they are actually used for the lunch product.
+//
+// This artifact helps flagging automation determine in which directory a flag
+// should be placed by default.
+type ReleaseConfigContributionsModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	// Properties for "release_config_contributions"
+	properties struct {
+		// The `release_configs/*.textproto` files provided by this
+		// directory, relative to this Android.bp file
+		Srcs []string `android:"path"`
+	}
+}
+
+func ReleaseConfigContributionsFactory() android.Module {
+	module := &ReleaseConfigContributionsModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+
+	return module
+}
+
+func (module *ReleaseConfigContributionsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	srcs := android.PathsForModuleSrc(ctx, module.properties.Srcs)
+	if len(srcs) == 0 {
+		return
+	}
+	contributionDir := filepath.Dir(filepath.Dir(srcs[0].String()))
+	for _, file := range srcs {
+		if filepath.Dir(filepath.Dir(file.String())) != contributionDir {
+			ctx.ModuleErrorf("Cannot include %s with %s contributions", file, contributionDir)
+		}
+		if filepath.Base(filepath.Dir(file.String())) != "release_configs" || file.Ext() != ".textproto" {
+			ctx.ModuleErrorf("Invalid contribution file %s", file)
+		}
+	}
+	android.SetProvider(ctx, ReleaseConfigContributionsProviderKey, ReleaseConfigContributionsProviderData{
+		ContributionDir: android.PathForSource(ctx, contributionDir),
+	})
+
+}
diff --git a/aconfig/codegen/aconfig_declarations_group.go b/aconfig/codegen/aconfig_declarations_group.go
index 13daf47..6811d06 100644
--- a/aconfig/codegen/aconfig_declarations_group.go
+++ b/aconfig/codegen/aconfig_declarations_group.go
@@ -59,7 +59,7 @@
 func AconfigDeclarationsGroupFactory() android.Module {
 	module := &AconfigDeclarationsGroup{}
 	module.AddProperties(&module.properties)
-	android.InitAndroidModule(module)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	return module
 }
diff --git a/aconfig/codegen/aconfig_declarations_group_test.go b/aconfig/codegen/aconfig_declarations_group_test.go
index c69d21f..ef954ce 100644
--- a/aconfig/codegen/aconfig_declarations_group_test.go
+++ b/aconfig/codegen/aconfig_declarations_group_test.go
@@ -67,7 +67,7 @@
 	`)
 
 	// Check if aconfig_declarations_group module depends on the aconfig_library modules
-	java.CheckModuleDependencies(t, result.TestContext, "my_group", "", []string{
+	java.CheckModuleDependencies(t, result.TestContext, "my_group", "android_common", []string{
 		`bar-java`,
 		`foo-java`,
 	})
diff --git a/aconfig/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go
index b5cf687..8c4bfe6 100644
--- a/aconfig/codegen/cc_aconfig_library.go
+++ b/aconfig/codegen/cc_aconfig_library.go
@@ -63,7 +63,7 @@
 	callbacks := &CcAconfigLibraryCallbacks{
 		properties: &CcAconfigLibraryProperties{},
 	}
-	return cc.GeneratedCcLibraryModuleFactory("cc_aconfig_library", callbacks)
+	return cc.GeneratedCcLibraryModuleFactory(callbacks)
 }
 
 func (this *CcAconfigLibraryCallbacks) GeneratorInit(ctx cc.BaseModuleContext) {
diff --git a/aconfig/codegen/cc_aconfig_library_test.go b/aconfig/codegen/cc_aconfig_library_test.go
index 2f6c1a6..c308ed4 100644
--- a/aconfig/codegen/cc_aconfig_library_test.go
+++ b/aconfig/codegen/cc_aconfig_library_test.go
@@ -211,7 +211,7 @@
 
 	module := result.ModuleForTests("my_cc_library", "android_vendor_arm64_armv8-a_shared").Module()
 
-	entry := android.AndroidMkEntriesForTest(t, result.TestContext, module)[0]
+	entry := android.AndroidMkInfoForTest(t, result.TestContext, module).PrimaryInfo
 
 	makeVar := entry.EntryMap["LOCAL_ACONFIG_FILES"]
 	android.EnsureListContainsSuffix(t, makeVar, "my_aconfig_declarations_foo/intermediate.pb")
diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
index ebca413..9f399bf 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -72,7 +72,7 @@
 		module.AddSharedLibrary("aconfig-annotations-lib")
 		// TODO(b/303773055): Remove the annotation after access issue is resolved.
 		module.AddSharedLibrary("unsupportedappusage")
-		module.AddSharedLibrary("aconfig_storage_reader_java")
+		module.AddSharedLibrary("aconfig_storage_stub")
 	}
 }
 
diff --git a/aconfig/init.go b/aconfig/init.go
index 6f91d8e..621d619 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -32,6 +32,7 @@
 				` ${declarations}` +
 				` ${values}` +
 				` ${default-permission}` +
+				` ${allow-read-write}` +
 				` --cache ${out}.tmp` +
 				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
 			//				` --build-id ${release_version}` +
@@ -39,7 +40,7 @@
 				"${aconfig}",
 			},
 			Restat: true,
-		}, "release_version", "package", "container", "declarations", "values", "default-permission")
+		}, "release_version", "package", "container", "declarations", "values", "default-permission", "allow-read-write")
 
 	// For create-device-config-sysprops: Generate aconfig flag value map text file
 	aconfigTextRule = pctx.AndroidStaticRule("aconfig_text",
diff --git a/aidl_library/Android.bp b/aidl_library/Android.bp
index 07472a4..3c386fb 100644
--- a/aidl_library/Android.bp
+++ b/aidl_library/Android.bp
@@ -20,6 +20,7 @@
     name: "soong-aidl-library",
     pkgPath: "android/soong/aidl_library",
     deps: [
+        "blueprint-depset",
         "soong-android",
     ],
     srcs: [
diff --git a/aidl_library/aidl_library.go b/aidl_library/aidl_library.go
index 0141545..1e0ab30 100644
--- a/aidl_library/aidl_library.go
+++ b/aidl_library/aidl_library.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -58,17 +59,17 @@
 	// The direct aidl files of the module
 	Srcs android.Paths
 	// The include dirs to the direct aidl files and those provided from transitive aidl_library deps
-	IncludeDirs android.DepSet[android.Path]
+	IncludeDirs depset.DepSet[android.Path]
 	// The direct hdrs and hdrs from transitive deps
-	Hdrs android.DepSet[android.Path]
+	Hdrs depset.DepSet[android.Path]
 }
 
 // AidlLibraryProvider provides the srcs and the transitive include dirs
 var AidlLibraryProvider = blueprint.NewProvider[AidlLibraryInfo]()
 
 func (lib *AidlLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	includeDirsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.PREORDER)
-	hdrsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.PREORDER)
+	includeDirsDepSetBuilder := depset.NewBuilder[android.Path](depset.PREORDER)
+	hdrsDepSetBuilder := depset.NewBuilder[android.Path](depset.PREORDER)
 
 	if len(lib.properties.Srcs) == 0 && len(lib.properties.Hdrs) == 0 {
 		ctx.ModuleErrorf("at least srcs or hdrs prop must be non-empty")
@@ -100,15 +101,15 @@
 
 	for _, dep := range ctx.GetDirectDepsWithTag(aidlLibraryTag) {
 		if info, ok := android.OtherModuleProvider(ctx, dep, AidlLibraryProvider); ok {
-			includeDirsDepSetBuilder.Transitive(&info.IncludeDirs)
-			hdrsDepSetBuilder.Transitive(&info.Hdrs)
+			includeDirsDepSetBuilder.Transitive(info.IncludeDirs)
+			hdrsDepSetBuilder.Transitive(info.Hdrs)
 		}
 	}
 
 	android.SetProvider(ctx, AidlLibraryProvider, AidlLibraryInfo{
 		Srcs:        srcs,
-		IncludeDirs: *includeDirsDepSetBuilder.Build(),
-		Hdrs:        *hdrsDepSetBuilder.Build(),
+		IncludeDirs: includeDirsDepSetBuilder.Build(),
+		Hdrs:        hdrsDepSetBuilder.Build(),
 	})
 }
 
diff --git a/android/Android.bp b/android/Android.bp
index 2adedfe..dfea8f9 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -8,6 +8,8 @@
     deps: [
         "blueprint",
         "blueprint-bootstrap",
+        "blueprint-depset",
+        "blueprint-gobtools",
         "blueprint-metrics",
         "sbox_proto",
         "soong",
@@ -29,6 +31,7 @@
     srcs: [
         "aconfig_providers.go",
         "all_teams.go",
+        "android_info.go",
         "androidmk.go",
         "apex.go",
         "apex_contributions.go",
@@ -50,8 +53,8 @@
         "deapexer.go",
         "defaults.go",
         "defs.go",
-        "depset_generic.go",
         "deptag.go",
+        "dirgroup.go",
         "early_module_context.go",
         "expand.go",
         "filegroup.go",
@@ -71,6 +74,7 @@
         "module.go",
         "module_context.go",
         "module_info_json.go",
+        "module_proxy.go",
         "mutator.go",
         "namespace.go",
         "neverallow.go",
@@ -88,7 +92,6 @@
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "product_config.go",
-        "product_config_to_bp.go",
         "proto.go",
         "provider.go",
         "raw_files.go",
@@ -106,10 +109,11 @@
         "test_asserts.go",
         "test_suites.go",
         "testing.go",
-        "updatable_modules.go",
         "util.go",
         "variable.go",
+        "vendor_api_levels.go",
         "vintf_fragment.go",
+        "vintf_data.go",
         "visibility.go",
     ],
     testSrcs: [
@@ -119,11 +123,11 @@
         "apex_test.go",
         "arch_test.go",
         "blueprint_e2e_test.go",
+        "build_prop_test.go",
         "config_test.go",
         "configured_jars_test.go",
         "csuite_config_test.go",
         "defaults_test.go",
-        "depset_test.go",
         "deptag_test.go",
         "expand_test.go",
         "filegroup_test.go",
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index d2a9622..210a656 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -107,7 +107,7 @@
 	mergedAconfigFiles := make(map[string]Paths)
 	mergedModeInfos := make(map[string]ModeInfo)
 
-	ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) {
+	ctx.VisitDirectDepsProxy(func(module ModuleProxy) {
 		if aconfig_dep, ok := OtherModuleProvider(ctx, module, CodegenInfoProvider); ok && len(aconfig_dep.ModeInfos) > 0 {
 			maps.Copy(mergedModeInfos, aconfig_dep.ModeInfos)
 		}
diff --git a/android/android_info.go b/android/android_info.go
new file mode 100644
index 0000000..a8d3d4e
--- /dev/null
+++ b/android/android_info.go
@@ -0,0 +1,91 @@
+// Copyright 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.
+
+package android
+
+import (
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+var (
+	mergeAndRemoveComments = pctx.AndroidStaticRule("merge_and_remove_comments",
+		blueprint.RuleParams{
+			Command: "cat $in | grep -v '#' > $out",
+		},
+	)
+	androidInfoTxtToProp = pctx.AndroidStaticRule("android_info_txt_to_prop",
+		blueprint.RuleParams{
+			Command: "grep 'require version-' $in | sed -e 's/require version-/ro.build.expect./g' > $out",
+		},
+	)
+)
+
+type androidInfoProperties struct {
+	// Name of output file. Defaults to module name
+	Stem *string
+
+	// Paths of board-info.txt files.
+	Board_info_files []string `android:"path"`
+
+	// Name of bootloader board. If board_info_files is empty, `board={bootloader_board_name}` will
+	// be printed to output. Ignored if board_info_files is not empty.
+	Bootloader_board_name *string
+}
+
+type androidInfoModule struct {
+	ModuleBase
+
+	properties androidInfoProperties
+}
+
+func (p *androidInfoModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	if len(p.properties.Board_info_files) > 0 && p.properties.Bootloader_board_name != nil {
+		ctx.ModuleErrorf("Either Board_info_files or Bootloader_board_name should be set. Please remove one of them\n")
+		return
+	}
+	androidInfoTxtName := proptools.StringDefault(p.properties.Stem, ctx.ModuleName()+".txt")
+	androidInfoTxt := PathForModuleOut(ctx, androidInfoTxtName)
+	androidInfoProp := androidInfoTxt.ReplaceExtension(ctx, "prop")
+
+	if boardInfoFiles := PathsForModuleSrc(ctx, p.properties.Board_info_files); len(boardInfoFiles) > 0 {
+		ctx.Build(pctx, BuildParams{
+			Rule:   mergeAndRemoveComments,
+			Inputs: boardInfoFiles,
+			Output: androidInfoTxt,
+		})
+	} else if bootloaderBoardName := proptools.String(p.properties.Bootloader_board_name); bootloaderBoardName != "" {
+		WriteFileRule(ctx, androidInfoTxt, "board="+bootloaderBoardName)
+	} else {
+		WriteFileRule(ctx, androidInfoTxt, "")
+	}
+
+	// Create android_info.prop
+	ctx.Build(pctx, BuildParams{
+		Rule:   androidInfoTxtToProp,
+		Input:  androidInfoTxt,
+		Output: androidInfoProp,
+	})
+
+	ctx.SetOutputFiles(Paths{androidInfoProp}, "")
+}
+
+// android_info module generate a file named android-info.txt that contains various information
+// about the device we're building for.  This file is typically packaged up with everything else.
+func AndroidInfoFactory() Module {
+	module := &androidInfoModule{}
+	module.AddProperties(&module.properties)
+	InitAndroidModule(module)
+	return module
+}
diff --git a/android/androidmk.go b/android/androidmk.go
index cac2cfe..590cce3 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -463,18 +463,18 @@
 func generateDistContributionsForMake(distContributions *distContributions) []string {
 	var ret []string
 	for _, d := range distContributions.copiesForGoals {
-		ret = append(ret, fmt.Sprintf(".PHONY: %s\n", d.goals))
+		ret = append(ret, fmt.Sprintf(".PHONY: %s", d.goals))
 		// Create dist-for-goals calls for each of the copy instructions.
 		for _, c := range d.copies {
 			if distContributions.licenseMetadataFile != nil {
 				ret = append(
 					ret,
-					fmt.Sprintf("$(if $(strip $(ALL_TARGETS.%s.META_LIC)),,$(eval ALL_TARGETS.%s.META_LIC := %s))\n",
+					fmt.Sprintf("$(if $(strip $(ALL_TARGETS.%s.META_LIC)),,$(eval ALL_TARGETS.%s.META_LIC := %s))",
 						c.from.String(), c.from.String(), distContributions.licenseMetadataFile.String()))
 			}
 			ret = append(
 				ret,
-				fmt.Sprintf("$(call dist-for-goals,%s,%s:%s)\n", d.goals, c.from.String(), c.dest))
+				fmt.Sprintf("$(call dist-for-goals,%s,%s:%s)", d.goals, c.from.String(), c.dest))
 		}
 	}
 
@@ -523,7 +523,7 @@
 	a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
 
 	for _, distString := range a.GetDistForGoals(mod) {
-		fmt.Fprintf(&a.header, distString)
+		fmt.Fprintln(&a.header, distString)
 	}
 
 	fmt.Fprintf(&a.header, "\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s\n", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod))
@@ -807,9 +807,8 @@
 	// Additional cases here require review for correct license propagation to make.
 	var err error
 
-	if info, ok := ctx.otherModuleProvider(mod, AndroidMkInfoProvider); ok {
-		androidMkEntriesInfos := info.(*AndroidMkProviderInfo)
-		err = translateAndroidMkEntriesInfoModule(ctx, w, moduleInfoJSONs, mod, androidMkEntriesInfos)
+	if info, ok := OtherModuleProvider(ctx, mod, AndroidMkInfoProvider); ok {
+		err = translateAndroidMkEntriesInfoModule(ctx, w, moduleInfoJSONs, mod, info)
 	} else {
 		switch x := mod.(type) {
 		case AndroidMkDataProvider:
@@ -1100,6 +1099,10 @@
 	EntryOrder []string
 }
 
+type AndroidMkProviderInfoProducer interface {
+	PrepareAndroidMKProviderInfo(config Config) *AndroidMkProviderInfo
+}
+
 // TODO: rename it to AndroidMkEntriesProvider after AndroidMkEntriesProvider interface is gone.
 var AndroidMkInfoProvider = blueprint.NewProvider[*AndroidMkProviderInfo]()
 
@@ -1272,7 +1275,7 @@
 		a.HeaderStrings = append(a.HeaderStrings, distString)
 	}
 
-	a.HeaderStrings = append(a.HeaderStrings, fmt.Sprintf("\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s\n", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod)))
+	a.HeaderStrings = append(a.HeaderStrings, fmt.Sprintf("\ninclude $(CLEAR_VARS)  # type: %s, name: %s, variant: %s", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod)))
 
 	// Collect make variable assignment entries.
 	helperInfo.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
@@ -1300,6 +1303,14 @@
 		helperInfo.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
 	}
 
+	if info.UncheckedModule {
+		helperInfo.SetBool("LOCAL_DONT_CHECK_MODULE", true)
+	} else if info.CheckbuildTarget != nil {
+		helperInfo.SetPath("LOCAL_CHECKED_MODULE", info.CheckbuildTarget)
+	} else {
+		helperInfo.SetOptionalPath("LOCAL_CHECKED_MODULE", a.OutputFile)
+	}
+
 	if len(info.TestData) > 0 {
 		helperInfo.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...)
 	}
@@ -1364,25 +1375,6 @@
 		helperInfo.SetString("LOCAL_IS_HOST_MODULE", "true")
 	}
 
-	prefix := ""
-	if base.ArchSpecific() {
-		switch base.Os().Class {
-		case Host:
-			if base.Target().HostCross {
-				prefix = "HOST_CROSS_"
-			} else {
-				prefix = "HOST_"
-			}
-		case Device:
-			prefix = "TARGET_"
-
-		}
-
-		if base.Arch().ArchType != ctx.Config().Targets[base.Os()][0].Arch.ArchType {
-			prefix = "2ND_" + prefix
-		}
-	}
-
 	if licenseMetadata, ok := OtherModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
 		helperInfo.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
 	}
@@ -1423,8 +1415,8 @@
 		return
 	}
 
-	combinedHeaderString := strings.Join(a.HeaderStrings, "\n")
-	combinedFooterString := strings.Join(a.FooterStrings, "\n")
+	combinedHeaderString := strings.Join(a.HeaderStrings, "\n") + "\n"
+	combinedFooterString := strings.Join(a.FooterStrings, "\n") + "\n"
 	w.Write([]byte(combinedHeaderString))
 	for _, name := range a.EntryOrder {
 		AndroidMkEmitAssignList(w, name, a.EntryMap[name])
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index c37eeab..f63b227 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -195,8 +195,7 @@
 $(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))
 $(call dist-for-goals,my_goal,one.out:one.out)
 $(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))
-$(call dist-for-goals,my_goal,two.out:other.out)
-`, strings.Join(makeOutput, ""))
+$(call dist-for-goals,my_goal,two.out:other.out)`, strings.Join(makeOutput, "\n"))
 }
 
 func TestGetDistForGoals(t *testing.T) {
@@ -235,28 +234,28 @@
 			`
 
 	expectedAndroidMkLines := []string{
-		".PHONY: my_second_goal\n",
-		"$(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))\n",
-		"$(call dist-for-goals,my_second_goal,two.out:two.out)\n",
-		"$(if $(strip $(ALL_TARGETS.three/four.out.META_LIC)),,$(eval ALL_TARGETS.three/four.out.META_LIC := meta_lic))\n",
-		"$(call dist-for-goals,my_second_goal,three/four.out:four.out)\n",
-		".PHONY: my_third_goal\n",
-		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
-		"$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)\n",
-		".PHONY: my_fourth_goal\n",
-		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
-		"$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)\n",
-		".PHONY: my_fifth_goal\n",
-		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
-		"$(call dist-for-goals,my_fifth_goal,one.out:new-name)\n",
-		".PHONY: my_sixth_goal\n",
-		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))\n",
-		"$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)\n",
-		".PHONY: my_goal my_other_goal\n",
-		"$(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))\n",
-		"$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)\n",
-		"$(if $(strip $(ALL_TARGETS.three/four.out.META_LIC)),,$(eval ALL_TARGETS.three/four.out.META_LIC := meta_lic))\n",
-		"$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
+		".PHONY: my_second_goal",
+		"$(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))",
+		"$(call dist-for-goals,my_second_goal,two.out:two.out)",
+		"$(if $(strip $(ALL_TARGETS.three/four.out.META_LIC)),,$(eval ALL_TARGETS.three/four.out.META_LIC := meta_lic))",
+		"$(call dist-for-goals,my_second_goal,three/four.out:four.out)",
+		".PHONY: my_third_goal",
+		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))",
+		"$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)",
+		".PHONY: my_fourth_goal",
+		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))",
+		"$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)",
+		".PHONY: my_fifth_goal",
+		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))",
+		"$(call dist-for-goals,my_fifth_goal,one.out:new-name)",
+		".PHONY: my_sixth_goal",
+		"$(if $(strip $(ALL_TARGETS.one.out.META_LIC)),,$(eval ALL_TARGETS.one.out.META_LIC := meta_lic))",
+		"$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)",
+		".PHONY: my_goal my_other_goal",
+		"$(if $(strip $(ALL_TARGETS.two.out.META_LIC)),,$(eval ALL_TARGETS.two.out.META_LIC := meta_lic))",
+		"$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)",
+		"$(if $(strip $(ALL_TARGETS.three/four.out.META_LIC)),,$(eval ALL_TARGETS.three/four.out.META_LIC := meta_lic))",
+		"$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)",
 	}
 
 	ctx, module := buildContextAndCustomModuleFoo(t, bp)
diff --git a/android/apex.go b/android/apex.go
index 114fe29..db93912 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"reflect"
 	"slices"
 	"sort"
 	"strconv"
@@ -62,19 +61,6 @@
 	// that are merged together.
 	InApexVariants []string
 
-	// List of APEX Soong module names that this module is part of. Note that the list includes
-	// different variations of the same APEX. For example, if module `foo` is included in the
-	// apex `com.android.foo`, and also if there is an override_apex module
-	// `com.mycompany.android.foo` overriding `com.android.foo`, then this list contains both
-	// `com.android.foo` and `com.mycompany.android.foo`.  If the APEX Soong module is a
-	// prebuilt, the name here doesn't have the `prebuilt_` prefix.
-	InApexModules []string
-
-	// Pointers to the ApexContents struct each of which is for apexBundle modules that this
-	// module is part of. The ApexContents gives information about which modules the apexBundle
-	// has and whether a module became part of the apexBundle via a direct dependency or not.
-	ApexContents []*ApexContents
-
 	// True if this is for a prebuilt_apex.
 	//
 	// If true then this will customize the apex processing to make it suitable for handling
@@ -105,7 +91,6 @@
 	(*d)["Apex"] = map[string]interface{}{
 		"ApexVariationName": i.ApexVariationName,
 		"MinSdkVersion":     i.MinSdkVersion,
-		"InApexModules":     i.InApexModules,
 		"InApexVariants":    i.InApexVariants,
 		"ForPrebuiltApex":   i.ForPrebuiltApex,
 	}
@@ -140,15 +125,6 @@
 	return false
 }
 
-func (i ApexInfo) InApexModule(apexModuleName string) bool {
-	for _, a := range i.InApexModules {
-		if a == apexModuleName {
-			return true
-		}
-	}
-	return false
-}
-
 // To satisfy the comparable interface
 func (i ApexInfo) Equal(other any) bool {
 	otherApexInfo, ok := other.(ApexInfo)
@@ -156,21 +132,11 @@
 		i.MinSdkVersion == otherApexInfo.MinSdkVersion &&
 		i.Updatable == otherApexInfo.Updatable &&
 		i.UsePlatformApis == otherApexInfo.UsePlatformApis &&
-		reflect.DeepEqual(i.InApexVariants, otherApexInfo.InApexVariants) &&
-		reflect.DeepEqual(i.InApexModules, otherApexInfo.InApexModules)
+		slices.Equal(i.InApexVariants, otherApexInfo.InApexVariants)
 }
 
-// ApexTestForInfo stores the contents of APEXes for which this module is a test - although this
-// module is not part of the APEX - and thus has access to APEX internals.
-type ApexTestForInfo struct {
-	ApexContents []*ApexContents
-}
-
-var ApexTestForInfoProvider = blueprint.NewMutatorProvider[ApexTestForInfo]("apex_test_for")
-
 // ApexBundleInfo contains information about the dependencies of an apex
 type ApexBundleInfo struct {
-	Contents *ApexContents
 }
 
 var ApexBundleInfoProvider = blueprint.NewMutatorProvider[ApexBundleInfo]("apex_info")
@@ -228,15 +194,8 @@
 	// this after apex.apexMutator is run.
 	InAnyApex() bool
 
-	// Returns true if this module is directly in any APEX. Call this AFTER apex.apexMutator is
-	// run.
-	DirectlyInAnyApex() bool
-
-	// NotInPlatform tells whether or not this module is included in an APEX and therefore
-	// shouldn't be exposed to the platform (i.e. outside of the APEX) directly. A module is
-	// considered to be included in an APEX either when there actually is an APEX that
-	// explicitly has the module as its dependency or the module is not available to the
-	// platform, which indicates that the module belongs to at least one or more other APEXes.
+	// NotInPlatform returns true if the module is not available to the platform due to
+	// apex_available being set and not containing "//apex_available:platform".
 	NotInPlatform() bool
 
 	// Tests if this module could have APEX variants. Even when a module type implements
@@ -269,12 +228,6 @@
 	// check-platform-availability mutator in the apex package.
 	SetNotAvailableForPlatform()
 
-	// Returns the list of APEXes that this module is a test for. The module has access to the
-	// private part of the listed APEXes even when it is not included in the APEXes. This by
-	// default returns nil. A module type should override the default implementation. For
-	// example, cc_test module type returns the value of test_for here.
-	TestFor() []string
-
 	// Returns nil (success) if this module should support the given sdk version. Returns an
 	// error if not. No default implementation is provided for this method. A module type
 	// implementing this interface should provide an implementation. A module supports an sdk
@@ -299,20 +252,6 @@
 	// Default is ["//apex_available:platform"].
 	Apex_available []string
 
-	// See ApexModule.InAnyApex()
-	InAnyApex bool `blueprint:"mutated"`
-
-	// See ApexModule.DirectlyInAnyApex()
-	DirectlyInAnyApex bool `blueprint:"mutated"`
-
-	// AnyVariantDirectlyInAnyApex is true in the primary variant of a module if _any_ variant
-	// of the module is directly in any apex. This includes host, arch, asan, etc. variants. It
-	// is unused in any variant that is not the primary variant. Ideally this wouldn't be used,
-	// as it incorrectly mixes arch variants if only one arch is in an apex, but a few places
-	// depend on it, for example when an ASAN variant is created before the apexMutator. Call
-	// this after apex.apexMutator is run.
-	AnyVariantDirectlyInAnyApex bool `blueprint:"mutated"`
-
 	// See ApexModule.NotAvailableForPlatform()
 	NotAvailableForPlatform bool `blueprint:"mutated"`
 
@@ -349,16 +288,6 @@
 	AlwaysRequireApexVariant() bool
 }
 
-// Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex state
-// from the parent to the child. For example, stubs libraries are marked as DirectlyInAnyApex if
-// their implementation is in an apex.
-type CopyDirectlyInAnyApexTag interface {
-	blueprint.DependencyTag
-
-	// Method that differentiates this interface from others.
-	CopyDirectlyInAnyApex()
-}
-
 // Interface that identifies dependencies to skip Apex dependency check
 type SkipApexAllowedDependenciesCheck interface {
 	// Returns true to skip the Apex dependency check, which limits the allowed dependency in build.
@@ -409,40 +338,27 @@
 func (m *ApexModuleBase) BuildForApex(apex ApexInfo) {
 	m.apexInfosLock.Lock()
 	defer m.apexInfosLock.Unlock()
-	for i, v := range m.apexInfos {
-		if v.ApexVariationName == apex.ApexVariationName {
-			if len(apex.InApexModules) != 1 {
-				panic(fmt.Errorf("Newly created apexInfo must be for a single APEX"))
-			}
-			// Even when the ApexVariantNames are the same, the given ApexInfo might
-			// actually be for different APEX. This can happen when an APEX is
-			// overridden via override_apex. For example, there can be two apexes
-			// `com.android.foo` (from the `apex` module type) and
-			// `com.mycompany.android.foo` (from the `override_apex` module type), both
-			// of which has the same ApexVariantName `com.android.foo`. Add the apex
-			// name to the list so that it's not lost.
-			if !InList(apex.InApexModules[0], v.InApexModules) {
-				m.apexInfos[i].InApexModules = append(m.apexInfos[i].InApexModules, apex.InApexModules[0])
-			}
-			return
-		}
+	if slices.ContainsFunc(m.apexInfos, func(existing ApexInfo) bool {
+		return existing.ApexVariationName == apex.ApexVariationName
+	}) {
+		return
 	}
 	m.apexInfos = append(m.apexInfos, apex)
 }
 
 // Implements ApexModule
 func (m *ApexModuleBase) InAnyApex() bool {
-	return m.ApexProperties.InAnyApex
-}
-
-// Implements ApexModule
-func (m *ApexModuleBase) DirectlyInAnyApex() bool {
-	return m.ApexProperties.DirectlyInAnyApex
+	for _, apex_name := range m.ApexProperties.Apex_available {
+		if apex_name != AvailableToPlatform {
+			return true
+		}
+	}
+	return false
 }
 
 // Implements ApexModule
 func (m *ApexModuleBase) NotInPlatform() bool {
-	return m.ApexProperties.AnyVariantDirectlyInAnyApex || !m.AvailableFor(AvailableToPlatform)
+	return !m.AvailableFor(AvailableToPlatform)
 }
 
 // Implements ApexModule
@@ -457,13 +373,6 @@
 	return false
 }
 
-// Implements ApexModule
-func (m *ApexModuleBase) TestFor() []string {
-	// If needed, this will be overridden by concrete types inheriting
-	// ApexModuleBase
-	return nil
-}
-
 // Returns the test apexes that this module is included in.
 func (m *ApexModuleBase) TestApexes() []string {
 	return m.ApexProperties.TestApexes
@@ -606,8 +515,6 @@
 		if index, exists := seen[mergedName]; exists {
 			// Variants having the same mergedName are deduped
 			merged[index].InApexVariants = append(merged[index].InApexVariants, variantName)
-			merged[index].InApexModules = append(merged[index].InApexModules, apexInfo.InApexModules...)
-			merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...)
 			merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
 			// Platform APIs is allowed for this module only when all APEXes containing
 			// the module are with `use_platform_apis: true`.
@@ -617,8 +524,6 @@
 			seen[mergedName] = len(merged)
 			apexInfo.ApexVariationName = mergedName
 			apexInfo.InApexVariants = CopyOf(apexInfo.InApexVariants)
-			apexInfo.InApexModules = CopyOf(apexInfo.InApexModules)
-			apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...)
 			apexInfo.TestApexes = CopyOf(apexInfo.TestApexes)
 			merged = append(merged, apexInfo)
 		}
@@ -706,15 +611,6 @@
 		apexInfos, _ = mergeApexVariations(apexInfos)
 	}
 
-	var inApex ApexMembership
-	for _, a := range apexInfos {
-		for _, apexContents := range a.ApexContents {
-			inApex = inApex.merge(apexContents.contents[ctx.ModuleName()])
-		}
-	}
-	base.ApexProperties.InAnyApex = true
-	base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex
-
 	if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) && module.NotAvailableForPlatform() {
 		// Do not install the module for platform, but still allow it to output
 		// uninstallable AndroidMk entries in certain cases when they have side
@@ -804,115 +700,6 @@
 	})
 }
 
-// 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 transitive dependencies with a CopyDirectlyInAnyApexTag
-// dependency tag.
-func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) {
-	base := am.apexModuleBase()
-	// Copy DirectlyInAnyApex and InAnyApex from any transitive dependencies with a
-	// CopyDirectlyInAnyApexTag dependency tag.
-	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 {
-		// Variants of a module are always visited sequentially in order, so it is safe to
-		// write to another variant of this module. For a BottomUpMutator the
-		// PrimaryModule() is visited first and FinalModule() is visited last.
-		mctx.FinalModule().(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex = true
-	}
-
-	// If this is the FinalModule (last visited module) copy
-	// AnyVariantDirectlyInAnyApex to all the other variants
-	if am == mctx.FinalModule().(ApexModule) {
-		mctx.VisitAllModuleVariants(func(variant Module) {
-			variant.(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex =
-				base.ApexProperties.AnyVariantDirectlyInAnyApex
-		})
-	}
-}
-
-// ApexMembership tells how a module became part of an APEX.
-type ApexMembership int
-
-const (
-	notInApex        ApexMembership = 0
-	indirectlyInApex                = iota
-	directlyInApex
-)
-
-// ApexContents gives an information about member modules of an apexBundle.  Each apexBundle has an
-// apexContents, and modules in that apex have a provider containing the apexContents of each
-// apexBundle they are part of.
-type ApexContents struct {
-	// map from a module name to its membership in this apexBundle
-	contents map[string]ApexMembership
-}
-
-// NewApexContents creates and initializes an ApexContents that is suitable
-// for use with an apex module.
-//   - contents is a map from a module name to information about its membership within
-//     the apex.
-func NewApexContents(contents map[string]ApexMembership) *ApexContents {
-	return &ApexContents{
-		contents: contents,
-	}
-}
-
-// Updates an existing membership by adding a new direct (or indirect) membership
-func (i ApexMembership) Add(direct bool) ApexMembership {
-	if direct || i == directlyInApex {
-		return directlyInApex
-	}
-	return indirectlyInApex
-}
-
-// Merges two membership into one. Merging is needed because a module can be a part of an apexBundle
-// in many different paths. For example, it could be dependend on by the apexBundle directly, but at
-// the same time, there might be an indirect dependency to the module. In that case, the more
-// specific dependency (the direct one) is chosen.
-func (i ApexMembership) merge(other ApexMembership) ApexMembership {
-	if other == directlyInApex || i == directlyInApex {
-		return directlyInApex
-	}
-
-	if other == indirectlyInApex || i == indirectlyInApex {
-		return indirectlyInApex
-	}
-	return notInApex
-}
-
-// Tests whether a module named moduleName is directly included in the apexBundle where this
-// ApexContents is tagged.
-func (ac *ApexContents) DirectlyInApex(moduleName string) bool {
-	return ac.contents[moduleName] == directlyInApex
-}
-
-// Tests whether a module named moduleName is included in the apexBundle where this ApexContent is
-// tagged.
-func (ac *ApexContents) InApex(moduleName string) bool {
-	return ac.contents[moduleName] != notInApex
-}
-
-// Tests whether a module named moduleName is directly depended on by all APEXes in an ApexInfo.
-func DirectlyInAllApexes(apexInfo ApexInfo, moduleName string) bool {
-	for _, contents := range apexInfo.ApexContents {
-		if !contents.DirectlyInApex(moduleName) {
-			return false
-		}
-	}
-	return true
-}
-
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 //Below are routines for extra safety checks.
 //
@@ -939,8 +726,8 @@
 type DepNameToDepInfoMap map[string]ApexModuleDepInfo
 
 type ApexBundleDepsInfo struct {
-	flatListPath OutputPath
-	fullListPath OutputPath
+	flatListPath Path
+	fullListPath Path
 }
 
 type ApexBundleDepsInfoIntf interface {
@@ -977,20 +764,22 @@
 		fmt.Fprintf(&flatContent, "%s\n", toName)
 	}
 
-	d.fullListPath = PathForModuleOut(ctx, "depsinfo", "fulllist.txt").OutputPath
-	WriteFileRule(ctx, d.fullListPath, fullContent.String())
+	fullListPath := PathForModuleOut(ctx, "depsinfo", "fulllist.txt")
+	WriteFileRule(ctx, fullListPath, fullContent.String())
+	d.fullListPath = fullListPath
 
-	d.flatListPath = PathForModuleOut(ctx, "depsinfo", "flatlist.txt").OutputPath
-	WriteFileRule(ctx, d.flatListPath, flatContent.String())
+	flatListPath := PathForModuleOut(ctx, "depsinfo", "flatlist.txt")
+	WriteFileRule(ctx, flatListPath, flatContent.String())
+	d.flatListPath = flatListPath
 
-	ctx.Phony(fmt.Sprintf("%s-depsinfo", ctx.ModuleName()), d.fullListPath, d.flatListPath)
+	ctx.Phony(fmt.Sprintf("%s-depsinfo", ctx.ModuleName()), fullListPath, flatListPath)
 }
 
 // Function called while walking an APEX's payload dependencies.
 //
 // Return true if the `to` module should be visited, false otherwise.
-type PayloadDepsCallback func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool
-type WalkPayloadDepsFunc func(ctx ModuleContext, do PayloadDepsCallback)
+type PayloadDepsCallback func(ctx BaseModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool
+type WalkPayloadDepsFunc func(ctx BaseModuleContext, do PayloadDepsCallback)
 
 // ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks
 type ModuleWithMinSdkVersionCheck interface {
@@ -1017,7 +806,7 @@
 		return
 	}
 
-	walk(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
+	walk(ctx, func(ctx BaseModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
 		if externalDep {
 			// external deps are outside the payload boundary, which is "stable"
 			// interface. We don't have to check min_sdk_version for external
@@ -1062,12 +851,6 @@
 	return apiLevel
 }
 
-// Implemented by apexBundle.
-type ApexTestInterface interface {
-	// Return true if the apex bundle is an apex_test
-	IsTestApex() bool
-}
-
 var ApexExportsInfoProvider = blueprint.NewProvider[ApexExportsInfo]()
 
 // ApexExportsInfo contains information about the artifacts provided by apexes to dexpreopt and hiddenapi
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index 4cd8dda..ce34278 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -26,7 +26,7 @@
 func RegisterApexContributionsBuildComponents(ctx RegistrationContext) {
 	ctx.RegisterModuleType("apex_contributions", apexContributionsFactory)
 	ctx.RegisterModuleType("apex_contributions_defaults", apexContributionsDefaultsFactory)
-	ctx.RegisterSingletonModuleType("all_apex_contributions", allApexContributionsFactory)
+	ctx.RegisterModuleType("all_apex_contributions", allApexContributionsFactory)
 }
 
 type apexContributions struct {
@@ -87,10 +87,10 @@
 // Based on product_config, it will create a dependency on the selected
 // apex_contributions per mainline module
 type allApexContributions struct {
-	SingletonModuleBase
+	ModuleBase
 }
 
-func allApexContributionsFactory() SingletonModule {
+func allApexContributionsFactory() Module {
 	module := &allApexContributions{}
 	InitAndroidModule(module)
 	return module
@@ -191,7 +191,7 @@
 
 // This module type does not have any build actions.
 func (a *allApexContributions) GenerateAndroidBuildActions(ctx ModuleContext) {
-}
-
-func (a *allApexContributions) GenerateSingletonBuildActions(ctx SingletonContext) {
+	if ctx.ModuleName() != "all_apex_contributions" {
+		ctx.ModuleErrorf("There can only be 1 all_apex_contributions module in build/soong")
+	}
 }
diff --git a/android/apex_test.go b/android/apex_test.go
index 347bf7d..78597b2 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -37,7 +37,6 @@
 					ApexVariationName: "foo",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"foo"},
-					InApexModules:     []string{"foo"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
@@ -46,7 +45,6 @@
 					ApexVariationName: "apex10000",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"foo"},
-					InApexModules:     []string{"foo"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
@@ -61,14 +59,12 @@
 					ApexVariationName: "foo",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"foo"},
-					InApexModules:     []string{"foo"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 				{
 					ApexVariationName: "bar",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"bar"},
-					InApexModules:     []string{"bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
@@ -77,7 +73,6 @@
 					ApexVariationName: "apex10000",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"foo", "bar"},
-					InApexModules:     []string{"foo", "bar"},
 				}},
 			wantAliases: [][2]string{
 				{"foo", "apex10000"},
@@ -91,14 +86,12 @@
 					ApexVariationName: "foo",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"foo"},
-					InApexModules:     []string{"foo"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 				{
 					ApexVariationName: "bar",
 					MinSdkVersion:     uncheckedFinalApiLevel(30),
 					InApexVariants:    []string{"bar"},
-					InApexModules:     []string{"bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
@@ -107,14 +100,12 @@
 					ApexVariationName: "apex10000",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"foo"},
-					InApexModules:     []string{"foo"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 				{
 					ApexVariationName: "apex30",
 					MinSdkVersion:     uncheckedFinalApiLevel(30),
 					InApexVariants:    []string{"bar"},
-					InApexModules:     []string{"bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
@@ -130,7 +121,6 @@
 					ApexVariationName: "foo",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"foo"},
-					InApexModules:     []string{"foo"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 				{
@@ -138,7 +128,6 @@
 					MinSdkVersion:     FutureApiLevel,
 					Updatable:         true,
 					InApexVariants:    []string{"bar"},
-					InApexModules:     []string{"bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
@@ -148,7 +137,6 @@
 					MinSdkVersion:     FutureApiLevel,
 					Updatable:         true,
 					InApexVariants:    []string{"foo", "bar"},
-					InApexModules:     []string{"foo", "bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
@@ -164,7 +152,6 @@
 					ApexVariationName: "foo",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"foo"},
-					InApexModules:     []string{"foo"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 				{
@@ -172,7 +159,6 @@
 					MinSdkVersion:     FutureApiLevel,
 					Updatable:         true,
 					InApexVariants:    []string{"bar"},
-					InApexModules:     []string{"bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 				// This one should not be merged in with the others because it is for
@@ -182,7 +168,6 @@
 					MinSdkVersion:     FutureApiLevel,
 					Updatable:         true,
 					InApexVariants:    []string{"baz"},
-					InApexModules:     []string{"baz"},
 					ForPrebuiltApex:   ForPrebuiltApex,
 				},
 			},
@@ -192,7 +177,6 @@
 					MinSdkVersion:     FutureApiLevel,
 					Updatable:         true,
 					InApexVariants:    []string{"foo", "bar"},
-					InApexModules:     []string{"foo", "bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 				{
@@ -200,7 +184,6 @@
 					MinSdkVersion:     FutureApiLevel,
 					Updatable:         true,
 					InApexVariants:    []string{"baz"},
-					InApexModules:     []string{"baz"},
 					ForPrebuiltApex:   ForPrebuiltApex,
 				},
 			},
@@ -216,7 +199,6 @@
 					ApexVariationName: "foo",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"foo"},
-					InApexModules:     []string{"foo"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 				{
@@ -224,7 +206,6 @@
 					MinSdkVersion:     FutureApiLevel,
 					UsePlatformApis:   true,
 					InApexVariants:    []string{"bar"},
-					InApexModules:     []string{"bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
@@ -233,7 +214,6 @@
 					ApexVariationName: "apex10000",
 					MinSdkVersion:     FutureApiLevel,
 					InApexVariants:    []string{"foo", "bar"},
-					InApexModules:     []string{"foo", "bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
@@ -250,7 +230,6 @@
 					MinSdkVersion:     FutureApiLevel,
 					UsePlatformApis:   true,
 					InApexVariants:    []string{"foo"},
-					InApexModules:     []string{"foo"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 				{
@@ -258,7 +237,6 @@
 					MinSdkVersion:     FutureApiLevel,
 					UsePlatformApis:   true,
 					InApexVariants:    []string{"bar"},
-					InApexModules:     []string{"bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
@@ -268,7 +246,6 @@
 					MinSdkVersion:     FutureApiLevel,
 					UsePlatformApis:   true,
 					InApexVariants:    []string{"foo", "bar"},
-					InApexModules:     []string{"foo", "bar"},
 					ForPrebuiltApex:   NotForPrebuiltApex,
 				},
 			},
diff --git a/android/arch.go b/android/arch.go
index 942727a..3cd6e4b 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -138,6 +138,16 @@
 	return a.Name
 }
 
+func (a ArchType) Bitness() string {
+	if a.Multilib == "lib32" {
+		return "32"
+	}
+	if a.Multilib == "lib64" {
+		return "64"
+	}
+	panic("Bitness is not defined for the common variant")
+}
+
 const COMMON_VARIANT = "common"
 
 var (
@@ -557,10 +567,6 @@
 //	"32": compile for only a single 32-bit Target supported by the OsClass.
 //	"64": compile for only a single 64-bit Target supported by the OsClass.
 //	"common": compile a for a single Target that will work on all Targets supported by the OsClass (for example Java).
-//	"common_first": compile a for a Target that will work on all Targets supported by the OsClass
-//	    (same as "common"), plus a second Target for the preferred Target supported by the OsClass
-//	    (same as "first").  This is used for java_binary that produces a common .jar and a wrapper
-//	    executable script.
 //
 // Once the list of Targets is determined, the module is split into a variant for each Target.
 //
@@ -692,11 +698,9 @@
 		return ""
 	}
 
-	if incomingVariation == "" {
-		multilib, _ := decodeMultilib(ctx, base)
-		if multilib == "common" {
-			return "common"
-		}
+	multilib, _ := decodeMultilib(ctx, base)
+	if multilib == "common" {
+		return "common"
 	}
 	return incomingVariation
 }
@@ -746,8 +750,7 @@
 	// Create a dependency for Darwin Universal binaries from the primary to secondary
 	// architecture. The module itself will be responsible for calling lipo to merge the outputs.
 	if os == Darwin {
-		isUniversalBinary := (allArchInfo.Multilib == "darwin_universal" && len(allArchInfo.Targets) == 2) ||
-			allArchInfo.Multilib == "darwin_universal_common_first" && len(allArchInfo.Targets) == 3
+		isUniversalBinary := allArchInfo.Multilib == "darwin_universal" && len(allArchInfo.Targets) == 2
 		isPrimary := variation == ctx.Config().BuildArch.String()
 		hasSecondaryConfigured := len(ctx.Config().Targets[Darwin]) > 1
 		if isUniversalBinary && isPrimary && hasSecondaryConfigured {
@@ -815,11 +818,7 @@
 		//  !UseTargetVariants, as the module has opted into handling the arch-specific logic on
 		//    its own.
 		if os == Darwin && multilib != "common" && multilib != "32" {
-			if multilib == "common_first" {
-				multilib = "darwin_universal_common_first"
-			} else {
-				multilib = "darwin_universal"
-			}
+			multilib = "darwin_universal"
 		}
 
 		return multilib, ""
@@ -1359,7 +1358,7 @@
 
 // Returns the structs corresponding to the properties specific to the given
 // architecture and OS in archProperties.
-func getArchProperties(ctx BaseMutatorContext, archProperties interface{}, arch Arch, os OsType, nativeBridgeEnabled bool) []reflect.Value {
+func getArchProperties(ctx BaseModuleContext, archProperties interface{}, arch Arch, os OsType, nativeBridgeEnabled bool) []reflect.Value {
 	result := make([]reflect.Value, 0)
 	archPropValues := reflect.ValueOf(archProperties).Elem()
 
@@ -1931,13 +1930,6 @@
 	switch multilib {
 	case "common":
 		buildTargets = getCommonTargets(targets)
-	case "common_first":
-		buildTargets = getCommonTargets(targets)
-		if prefer32 {
-			buildTargets = append(buildTargets, FirstTarget(targets, "lib32", "lib64")...)
-		} else {
-			buildTargets = append(buildTargets, FirstTarget(targets, "lib64", "lib32")...)
-		}
 	case "both":
 		if prefer32 {
 			buildTargets = append(buildTargets, filterMultilibTargets(targets, "lib32")...)
@@ -1968,10 +1960,6 @@
 		// Reverse the targets so that the first architecture can depend on the second
 		// architecture module in order to merge the outputs.
 		ReverseSliceInPlace(buildTargets)
-	case "darwin_universal_common_first":
-		archTargets := filterMultilibTargets(targets, "lib64")
-		ReverseSliceInPlace(archTargets)
-		buildTargets = append(getCommonTargets(targets), archTargets...)
 	default:
 		return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", "prefer32" or "first_prefer32" found %q`,
 			multilib)
diff --git a/android/arch_list.go b/android/arch_list.go
index 9501c87..389f194 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -29,6 +29,7 @@
 		"armv9-2a",
 	},
 	X86: {
+		"alderlake",
 		"amberlake",
 		"atom",
 		"broadwell",
@@ -53,6 +54,7 @@
 		"x86_64",
 	},
 	X86_64: {
+		"alderlake",
 		"amberlake",
 		"broadwell",
 		"goldmont",
@@ -110,9 +112,6 @@
 }
 
 var archFeatures = map[ArchType][]string{
-	Arm: {
-		"neon",
-	},
 	Arm64: {
 		"dotprod",
 	},
@@ -142,17 +141,6 @@
 }
 
 var androidArchFeatureMap = map[ArchType]map[string][]string{
-	Arm: {
-		"armv7-a-neon": {
-			"neon",
-		},
-		"armv8-a": {
-			"neon",
-		},
-		"armv8-2a": {
-			"neon",
-		},
-	},
 	Arm64: {
 		"armv8-2a-dotprod": {
 			"dotprod",
@@ -165,6 +153,16 @@
 		},
 	},
 	X86: {
+		"alderlake": {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"avx",
+			"avx2",
+			"aes_ni",
+			"popcnt",
+		},
 		"amberlake": {
 			"ssse3",
 			"sse4",
@@ -341,6 +339,16 @@
 			"sse4_2",
 			"popcnt",
 		},
+		"alderlake": {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"avx",
+			"avx2",
+			"aes_ni",
+			"popcnt",
+		},
 		"amberlake": {
 			"ssse3",
 			"sse4",
diff --git a/android/arch_test.go b/android/arch_test.go
index 57c9010..7914884 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -560,15 +560,7 @@
 				prepareForArchTest,
 				// Test specific preparer
 				OptionalFixturePreparer(tt.preparer),
-				// Prepare for native bridge test
-				FixtureModifyConfig(func(config Config) {
-					config.Targets[Android] = []Target{
-						{Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
-						{Android, Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
-						{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64", false},
-						{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm", false},
-					}
-				}),
+				PrepareForNativeBridgeEnabled,
 				FixtureWithRootAndroidBp(bp),
 			).RunTest(t)
 
diff --git a/android/base_module_context.go b/android/base_module_context.go
index bb81377..06819d6 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"regexp"
+	"slices"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -33,6 +34,8 @@
 
 	blueprintBaseModuleContext() blueprint.BaseModuleContext
 
+	EqualModules(m1, m2 Module) bool
+
 	// OtherModuleName returns the name of another Module.  See BaseModuleContext.ModuleName for more information.
 	// It is intended for use inside the visit functions of Visit* and WalkDeps.
 	OtherModuleName(m blueprint.Module) string
@@ -85,6 +88,10 @@
 	// This method shouldn't be used directly, prefer the type-safe android.OtherModuleProvider instead.
 	otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 
+	// OtherModuleIsAutoGenerated returns true if the module is auto generated by another module
+	// instead of being defined in Android.bp file.
+	OtherModuleIsAutoGenerated(m blueprint.Module) bool
+
 	// Provider returns the value for a provider for the current module.  If the value is
 	// not set it returns nil and false.  It panics if called before the appropriate
 	// mutator or GenerateBuildActions pass for the provider.  The value returned may be a deep
@@ -106,41 +113,43 @@
 	// GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if
 	// none exists.  It panics if the dependency does not have the specified tag.  It skips any
 	// dependencies that are not an android.Module.
-	GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
+	GetDirectDepWithTag(name string, tag blueprint.DependencyTag) Module
 
 	// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified
 	// name, or nil if none exists.  If there are multiple dependencies on the same module it returns
 	// the first DependencyTag.
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 
-	// VisitDirectDepsBlueprint calls visit for each direct dependency.  If there are multiple
-	// direct dependencies on the same module visit will be called multiple times on that module
-	// and OtherModuleDependencyTag will return a different tag for each.
-	//
-	// The Module passed to the visit function should not be retained outside of the visit
-	// function, it may be invalidated by future mutators.
-	VisitDirectDepsBlueprint(visit func(blueprint.Module))
-
-	// VisitDirectDepsIgnoreBlueprint calls visit for each direct dependency.  If there are multiple
-	// direct dependencies on the same module visit will be called multiple times on that module
-	// and OtherModuleDependencyTag will return a different tag for each.  It silently ignores any
-	// dependencies that are not an android.Module.
-	//
-	// The Module passed to the visit function should not be retained outside of the visit
-	// function, it may be invalidated by future mutators.
-	VisitDirectDepsIgnoreBlueprint(visit func(Module))
-
 	// VisitDirectDeps calls visit for each direct dependency.  If there are multiple
 	// direct dependencies on the same module visit will be called multiple times on that module
 	// and OtherModuleDependencyTag will return a different tag for each.  It raises an error if any of the
-	// dependencies are not an android.Module.
+	// dependencies are disabled.
 	//
 	// The Module passed to the visit function should not be retained outside of the visit
 	// function, it may be invalidated by future mutators.
 	VisitDirectDeps(visit func(Module))
 
+	// VisitDirectDepsProxy calls visit for each direct dependency.  If there are multiple
+	// direct dependencies on the same module visit will be called multiple times on that module
+	// and OtherModuleDependencyTag will return a different tag for each. It raises an error if any of the
+	// dependencies are disabled.
+	//
+	// The ModuleProxy passed to the visit function should not be retained outside of the visit
+	// function, it may be invalidated by future mutators.
+	VisitDirectDepsProxy(visit func(proxy ModuleProxy))
+
+	// VisitDirectDepsProxyAllowDisabled calls visit for each direct dependency.  If there are
+	// multiple direct dependencies on the same module visit will be called multiple times on
+	// that module and OtherModuleDependencyTag will return a different tag for each.
+	//
+	// The ModuleProxy passed to the visit function should not be retained outside of the visit function, it may be
+	// invalidated by future mutators.
+	VisitDirectDepsProxyAllowDisabled(visit func(proxy ModuleProxy))
+
 	VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
 
+	VisitDirectDepsProxyWithTag(tag blueprint.DependencyTag, visit func(proxy ModuleProxy))
+
 	// VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit.  If there are
 	// multiple direct dependencies on the same module pred and visit will be called multiple times on that module and
 	// OtherModuleDependencyTag will return a different tag for each.  It skips any
@@ -164,16 +173,15 @@
 	// invalidated by future mutators.
 	WalkDeps(visit func(child, parent Module) bool)
 
-	// WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency
-	// tree in top down order.  visit may be called multiple times for the same (child, parent)
-	// pair if there are multiple direct dependencies between the child and parent with different
-	// tags.  OtherModuleDependencyTag will return the tag for the currently visited
-	// (child, parent) pair.  If visit returns false WalkDeps will not continue recursing down
-	// to child.
+	// WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order.  visit may
+	// be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the
+	// child and parent with different tags.  OtherModuleDependencyTag will return the tag for the currently visited
+	// (child, parent) pair.  If visit returns false WalkDeps will not continue recursing down to child.  It skips
+	// any dependencies that are not an android.Module.
 	//
 	// The Modules passed to the visit function should not be retained outside of the visit function, they may be
 	// invalidated by future mutators.
-	WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool)
+	WalkDepsProxy(visit func(child, parent ModuleProxy) bool)
 
 	// GetWalkPath is supposed to be called in visit function passed in WalkDeps()
 	// and returns a top-down dependency path from a start module to current child module.
@@ -191,12 +199,24 @@
 	// singleton actions that are only done once for all variants of a module.
 	FinalModule() Module
 
+	// IsFinalModule returns if the current module is the last variant.  Variants of a module are always visited in
+	// order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all
+	// variants using VisitAllModuleVariants if the current module is the last one. This can be used to perform
+	// singleton actions that are only done once for all variants of a module.
+	IsFinalModule(module Module) bool
+
 	// VisitAllModuleVariants calls visit for each variant of the current module.  Variants of a module are always
 	// visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read
-	// from all variants if the current module == FinalModule().  Otherwise, care must be taken to not access any
+	// from all variants if the current module is the last one. Otherwise, care must be taken to not access any
 	// data modified by the current mutator.
 	VisitAllModuleVariants(visit func(Module))
 
+	// VisitAllModuleVariantProxies calls visit for each variant of the current module.  Variants of a module are always
+	// visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read
+	// from all variants if the current module is the last one. Otherwise, care must be taken to not access any
+	// data modified by the current mutator.
+	VisitAllModuleVariantProxies(visit func(proxy ModuleProxy))
+
 	// GetTagPath is supposed to be called in visit function passed in WalkDeps()
 	// and returns a top-down dependency tags path from a start module to current child module.
 	// It has one less entry than GetWalkPath() as it contains the dependency tags that
@@ -220,10 +240,6 @@
 	// EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context
 	// can be used to evaluate the final value of Configurable properties.
 	EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue
-
-	// HasMutatorFinished returns true if the given mutator has finished running.
-	// It will panic if given an invalid mutator name.
-	HasMutatorFinished(mutatorName string) bool
 }
 
 type baseModuleContext struct {
@@ -238,15 +254,28 @@
 
 }
 
-func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string {
-	return b.bp.OtherModuleName(m)
+func getWrappedModule(module blueprint.Module) blueprint.Module {
+	if mp, isProxy := module.(ModuleProxy); isProxy {
+		return mp.module
+	}
+	return module
 }
-func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) }
+
+func (b *baseModuleContext) EqualModules(m1, m2 Module) bool {
+	return b.bp.EqualModules(getWrappedModule(m1), getWrappedModule(m2))
+}
+
+func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string {
+	return b.bp.OtherModuleName(getWrappedModule(m))
+}
+func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string {
+	return b.bp.OtherModuleDir(getWrappedModule(m))
+}
 func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) {
-	b.bp.OtherModuleErrorf(m, fmt, args...)
+	b.bp.OtherModuleErrorf(getWrappedModule(m), fmt, args...)
 }
 func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag {
-	return b.bp.OtherModuleDependencyTag(m)
+	return b.bp.OtherModuleDependencyTag(getWrappedModule(m))
 }
 func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) }
 func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool {
@@ -259,13 +288,17 @@
 	return b.bp.OtherModuleReverseDependencyVariantExists(name)
 }
 func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string {
-	return b.bp.OtherModuleType(m)
+	return b.bp.OtherModuleType(getWrappedModule(m))
 }
 
 func (b *baseModuleContext) otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
 	return b.bp.OtherModuleProvider(m, provider)
 }
 
+func (b *baseModuleContext) OtherModuleIsAutoGenerated(m blueprint.Module) bool {
+	return b.bp.OtherModuleIsAutoGenerated(m)
+}
+
 func (b *baseModuleContext) provider(provider blueprint.AnyProviderKey) (any, bool) {
 	return b.bp.Provider(provider)
 }
@@ -274,12 +307,11 @@
 	b.bp.SetProvider(provider, value)
 }
 
-func (b *baseModuleContext) HasMutatorFinished(mutatorName string) bool {
-	return b.bp.HasMutatorFinished(mutatorName)
-}
-
-func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
-	return b.bp.GetDirectDepWithTag(name, tag)
+func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) Module {
+	if module := b.bp.GetDirectDepWithTag(name, tag); module != nil {
+		return module.(Module)
+	}
+	return nil
 }
 
 func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext {
@@ -311,6 +343,7 @@
 type AllowDisabledModuleDependency interface {
 	blueprint.DependencyTag
 	AllowDisabledModuleDependency(target Module) bool
+	AllowDisabledModuleDependencyProxy(ctx OtherModuleProviderContext, target ModuleProxy) bool
 }
 
 type AlwaysAllowDisabledModuleDependencyTag struct{}
@@ -319,7 +352,11 @@
 	return true
 }
 
-func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool, ignoreBlueprint bool) Module {
+func (t AlwaysAllowDisabledModuleDependencyTag) AllowDisabledModuleDependencyProxy(OtherModuleProviderContext, ModuleProxy) bool {
+	return true
+}
+
+func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module {
 	aModule, _ := module.(Module)
 
 	if !strict {
@@ -327,10 +364,7 @@
 	}
 
 	if aModule == nil {
-		if !ignoreBlueprint {
-			b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag)
-		}
-		return nil
+		panic(fmt.Errorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag))
 	}
 
 	if !aModule.Enabled(b) {
@@ -346,6 +380,28 @@
 	return aModule
 }
 
+func (b *baseModuleContext) validateAndroidModuleProxy(
+	module blueprint.ModuleProxy, tag blueprint.DependencyTag, strict bool) *ModuleProxy {
+	aModule := ModuleProxy{module: module}
+
+	if !strict {
+		return &aModule
+	}
+
+	if !OtherModuleProviderOrDefault(b, module, CommonModuleInfoKey).Enabled {
+		if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependencyProxy(b, aModule) {
+			if b.Config().AllowMissingDependencies() {
+				b.AddMissingDependencies([]string{b.OtherModuleName(aModule)})
+			} else {
+				b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule))
+			}
+		}
+		return nil
+	}
+
+	return &aModule
+}
+
 type dep struct {
 	mod blueprint.Module
 	tag blueprint.DependencyTag
@@ -353,15 +409,8 @@
 
 func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep {
 	var deps []dep
-	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
-		if aModule, _ := module.(Module); aModule != nil {
-			if aModule.base().BaseModuleName() == name {
-				returnedTag := b.bp.OtherModuleDependencyTag(aModule)
-				if tag == nil || returnedTag == tag {
-					deps = append(deps, dep{aModule, returnedTag})
-				}
-			}
-		} else if b.bp.OtherModuleName(module) == name {
+	b.VisitDirectDeps(func(module Module) {
+		if module.base().BaseModuleName() == name {
 			returnedTag := b.bp.OtherModuleDependencyTag(module)
 			if tag == nil || returnedTag == tag {
 				deps = append(deps, dep{module, returnedTag})
@@ -404,11 +453,9 @@
 
 func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module {
 	var deps []Module
-	b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
-		if aModule, _ := module.(Module); aModule != nil {
-			if b.bp.OtherModuleDependencyTag(aModule) == tag {
-				deps = append(deps, aModule)
-			}
+	b.VisitDirectDeps(func(module Module) {
+		if b.bp.OtherModuleDependencyTag(module) == tag {
+			deps = append(deps, module)
 		}
 	})
 	return deps
@@ -421,41 +468,51 @@
 	return b.getDirectDepFirstTag(name)
 }
 
-func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
-	b.bp.VisitDirectDeps(visit)
-}
-
 func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) {
-	b.visitDirectDeps(visit, false)
-}
-
-func (b *baseModuleContext) VisitDirectDepsIgnoreBlueprint(visit func(Module)) {
-	b.visitDirectDeps(visit, true)
-}
-
-func (b *baseModuleContext) visitDirectDeps(visit func(Module), ignoreBlueprint bool) {
 	b.bp.VisitDirectDeps(func(module blueprint.Module) {
-		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, ignoreBlueprint); aModule != nil {
+		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
 			visit(aModule)
 		}
 	})
 }
 
+func (b *baseModuleContext) VisitDirectDepsProxy(visit func(ModuleProxy)) {
+	b.bp.VisitDirectDepsProxy(func(module blueprint.ModuleProxy) {
+		if aModule := b.validateAndroidModuleProxy(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
+			visit(*aModule)
+		}
+	})
+}
+
+func (b *baseModuleContext) VisitDirectDepsProxyAllowDisabled(visit func(proxy ModuleProxy)) {
+	b.bp.VisitDirectDepsProxy(visitProxyAdaptor(visit))
+}
+
 func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
 	b.bp.VisitDirectDeps(func(module blueprint.Module) {
 		if b.bp.OtherModuleDependencyTag(module) == tag {
-			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
+			if aModule := b.validateAndroidModule(module, tag, b.strictVisitDeps); aModule != nil {
 				visit(aModule)
 			}
 		}
 	})
 }
 
+func (b *baseModuleContext) VisitDirectDepsProxyWithTag(tag blueprint.DependencyTag, visit func(proxy ModuleProxy)) {
+	b.bp.VisitDirectDepsProxy(func(module blueprint.ModuleProxy) {
+		if b.bp.OtherModuleDependencyTag(module) == tag {
+			if aModule := b.validateAndroidModuleProxy(module, tag, b.strictVisitDeps); aModule != nil {
+				visit(*aModule)
+			}
+		}
+	})
+}
+
 func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
 	b.bp.VisitDirectDepsIf(
 		// pred
 		func(module blueprint.Module) bool {
-			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
+			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
 				return pred(aModule)
 			} else {
 				return false
@@ -469,7 +526,7 @@
 
 func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
 	b.bp.VisitDepsDepthFirst(func(module blueprint.Module) {
-		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
+		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
 			visit(aModule)
 		}
 	})
@@ -479,7 +536,7 @@
 	b.bp.VisitDepsDepthFirstIf(
 		// pred
 		func(module blueprint.Module) bool {
-			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil {
+			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
 				return pred(aModule)
 			} else {
 				return false
@@ -491,10 +548,6 @@
 		})
 }
 
-func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) {
-	b.bp.WalkDeps(visit)
-}
-
 func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
 	b.walkPath = []Module{b.Module()}
 	b.tagPath = []blueprint.DependencyTag{}
@@ -516,8 +569,25 @@
 	})
 }
 
+func (b *baseModuleContext) WalkDepsProxy(visit func(ModuleProxy, ModuleProxy) bool) {
+	b.walkPath = []Module{ModuleProxy{blueprint.CreateModuleProxy(b.Module())}}
+	b.tagPath = []blueprint.DependencyTag{}
+	b.bp.WalkDepsProxy(func(child, parent blueprint.ModuleProxy) bool {
+		childAndroidModule := ModuleProxy{child}
+		parentAndroidModule := ModuleProxy{parent}
+		// record walkPath before visit
+		for b.walkPath[len(b.walkPath)-1] != parentAndroidModule {
+			b.walkPath = b.walkPath[0 : len(b.walkPath)-1]
+			b.tagPath = b.tagPath[0 : len(b.tagPath)-1]
+		}
+		b.walkPath = append(b.walkPath, childAndroidModule)
+		b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule))
+		return visit(childAndroidModule, parentAndroidModule)
+	})
+}
+
 func (b *baseModuleContext) GetWalkPath() []Module {
-	return b.walkPath
+	return slices.Clone(b.walkPath)
 }
 
 func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag {
@@ -530,6 +600,10 @@
 	})
 }
 
+func (b *baseModuleContext) VisitAllModuleVariantProxies(visit func(ModuleProxy)) {
+	b.bp.VisitAllModuleVariantProxies(visitProxyAdaptor(visit))
+}
+
 func (b *baseModuleContext) PrimaryModule() Module {
 	return b.bp.PrimaryModule().(Module)
 }
@@ -538,6 +612,10 @@
 	return b.bp.FinalModule().(Module)
 }
 
+func (b *baseModuleContext) IsFinalModule(module Module) bool {
+	return b.bp.IsFinalModule(module)
+}
+
 // IsMetaDependencyTag returns true for cross-cutting metadata dependencies.
 func IsMetaDependencyTag(tag blueprint.DependencyTag) bool {
 	if tag == licenseKindTag {
diff --git a/android/build_prop.go b/android/build_prop.go
index ede93ed..2f71bc0 100644
--- a/android/build_prop.go
+++ b/android/build_prop.go
@@ -15,12 +15,18 @@
 package android
 
 import (
+	"fmt"
+
 	"github.com/google/blueprint/proptools"
 )
 
 func init() {
-	ctx := InitRegistrationContext
-	ctx.RegisterModuleType("build_prop", buildPropFactory)
+	registerBuildPropComponents(InitRegistrationContext)
+}
+
+func registerBuildPropComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("build_prop", BuildPropFactory)
+	ctx.RegisterModuleType("android_info", AndroidInfoFactory)
 }
 
 type buildPropProperties struct {
@@ -38,6 +44,10 @@
 	// Path to a JSON file containing product configs.
 	Product_config *string `android:"path"`
 
+	// Path to android-info.txt file containing board specific info.
+	// This is empty for build.prop of all partitions except vendor.
+	Android_info *string `android:"path"`
+
 	// Optional subdirectory under which this file is installed into
 	Relative_install_path *string
 }
@@ -47,7 +57,7 @@
 
 	properties buildPropProperties
 
-	outputFilePath OutputPath
+	outputFilePath Path
 	installPath    InstallPath
 }
 
@@ -65,6 +75,12 @@
 		return ctx.Config().ProductPropFiles(ctx)
 	} else if partition == "odm" {
 		return ctx.Config().OdmPropFiles(ctx)
+	} else if partition == "vendor" {
+		if p.properties.Android_info != nil {
+			androidInfo := PathForModuleSrc(ctx, proptools.String(p.properties.Android_info))
+			return append(ctx.Config().VendorPropFiles(ctx), androidInfo)
+		}
+		return ctx.Config().VendorPropFiles(ctx)
 	}
 	return nil
 }
@@ -95,30 +111,28 @@
 		return "product"
 	} else if p.SystemExtSpecific() {
 		return "system_ext"
+	} else if p.InstallInSystemDlkm() {
+		return "system_dlkm"
+	} else if p.InstallInVendorDlkm() {
+		return "vendor_dlkm"
+	} else if p.InstallInOdmDlkm() {
+		return "odm_dlkm"
+	} else if p.InstallInRamdisk() {
+		// From this hardcoding in make:
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/sysprop.mk;l=311;drc=274435657e4682e5cee3fffd11fb301ab32a828d
+		return "bootimage"
 	}
 	return "system"
 }
 
-var validPartitions = []string{
-	"system",
-	"system_ext",
-	"product",
-	"odm",
-}
-
 func (p *buildPropModule) GenerateAndroidBuildActions(ctx ModuleContext) {
-	p.outputFilePath = PathForModuleOut(ctx, "build.prop").OutputPath
-	if !ctx.Config().KatiEnabled() {
-		WriteFileRule(ctx, p.outputFilePath, "# no build.prop if kati is disabled")
-		ctx.SetOutputFiles(Paths{p.outputFilePath}, "")
-		return
+	if !p.SocSpecific() && p.properties.Android_info != nil {
+		ctx.ModuleErrorf("Android_info cannot be set if build.prop is not installed in vendor partition")
 	}
 
+	outputFilePath := PathForModuleOut(ctx, "build.prop")
+
 	partition := p.partition(ctx.DeviceConfig())
-	if !InList(partition, validPartitions) {
-		ctx.PropertyErrorf("partition", "unsupported partition %q: only %q are supported", partition, validPartitions)
-		return
-	}
 
 	rule := NewRuleBuilder(pctx, ctx)
 
@@ -145,7 +159,7 @@
 	cmd.FlagWithInput("--product-config=", PathForModuleSrc(ctx, proptools.String(p.properties.Product_config)))
 	cmd.FlagWithArg("--partition=", partition)
 	cmd.FlagForEachInput("--prop-files=", p.propFiles(ctx))
-	cmd.FlagWithOutput("--out=", p.outputFilePath)
+	cmd.FlagWithOutput("--out=", outputFilePath)
 
 	postProcessCmd := rule.Command().BuiltTool("post_process_props")
 	if ctx.DeviceConfig().BuildBrokenDupSysprop() {
@@ -158,17 +172,35 @@
 		// still need to pass an empty string to kernel-version-file-for-uffd-gc
 		postProcessCmd.FlagWithArg("--kernel-version-file-for-uffd-gc ", `""`)
 	}
-	postProcessCmd.Text(p.outputFilePath.String())
+	postProcessCmd.Text(outputFilePath.String())
 	postProcessCmd.Flags(p.properties.Block_list)
 
-	rule.Command().Text("echo").Text(proptools.NinjaAndShellEscape("# end of file")).FlagWithArg(">> ", p.outputFilePath.String())
+	for _, footer := range p.properties.Footer_files {
+		path := PathForModuleSrc(ctx, footer)
+		rule.appendText(outputFilePath, "####################################")
+		rule.appendTextf(outputFilePath, "# Adding footer from %v", footer)
+		rule.appendTextf(outputFilePath, "# with path %v", path)
+		rule.appendText(outputFilePath, "####################################")
+		rule.Command().Text("cat").FlagWithInput("", path).FlagWithArg(">> ", outputFilePath.String())
+	}
+
+	rule.appendText(outputFilePath, "# end of file")
 
 	rule.Build(ctx.ModuleName(), "generating build.prop")
 
 	p.installPath = PathForModuleInstall(ctx, proptools.String(p.properties.Relative_install_path))
-	ctx.InstallFile(p.installPath, p.stem(), p.outputFilePath)
+	ctx.InstallFile(p.installPath, p.stem(), outputFilePath)
 
-	ctx.SetOutputFiles(Paths{p.outputFilePath}, "")
+	ctx.SetOutputFiles(Paths{outputFilePath}, "")
+	p.outputFilePath = outputFilePath
+}
+
+func (r *RuleBuilder) appendText(path ModuleOutPath, text string) {
+	r.Command().Text("echo").Text(proptools.NinjaAndShellEscape(text)).FlagWithArg(">> ", path.String())
+}
+
+func (r *RuleBuilder) appendTextf(path ModuleOutPath, format string, a ...any) {
+	r.appendText(path, fmt.Sprintf(format, a...))
 }
 
 func (p *buildPropModule) AndroidMkEntries() []AndroidMkEntries {
@@ -187,7 +219,7 @@
 // build_prop module generates {partition}/build.prop file. At first common build properties are
 // printed based on Soong config variables. And then prop_files are printed as-is. Finally,
 // post_process_props tool is run to check if the result build.prop is valid or not.
-func buildPropFactory() Module {
+func BuildPropFactory() Module {
 	module := &buildPropModule{}
 	module.AddProperties(&module.properties)
 	InitAndroidArchModule(module, DeviceSupported, MultilibCommon)
diff --git a/android/build_prop_test.go b/android/build_prop_test.go
new file mode 100644
index 0000000..e75975a
--- /dev/null
+++ b/android/build_prop_test.go
@@ -0,0 +1,41 @@
+// 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 android
+
+import (
+	"testing"
+)
+
+func TestPropFileInputs(t *testing.T) {
+	bp := `
+build_prop {
+    name: "vendor-build.prop",
+    stem: "build.prop",
+    vendor: true,
+    android_info: ":board-info",
+    //product_config: ":product_config",
+}
+android_info {
+    name: "board-info",
+    stem: "android-info.txt",
+}
+`
+
+	res := GroupFixturePreparers(
+		FixtureRegisterWithContext(registerBuildPropComponents),
+	).RunTestWithBp(t, bp)
+	buildPropCmd := res.ModuleForTests("vendor-build.prop", "").Rule("vendor-build.prop_.vendor-build.prop").RuleParams.Command
+	AssertStringDoesContain(t, "Could not find android-info in prop files of vendor build.prop", buildPropCmd, "--prop-files=out/soong/.intermediates/board-info/android-info.prop")
+}
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
index 38f1382..0b876c3 100644
--- a/android/compliance_metadata.go
+++ b/android/compliance_metadata.go
@@ -17,13 +17,13 @@
 import (
 	"bytes"
 	"encoding/csv"
-	"encoding/gob"
 	"fmt"
 	"slices"
 	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/gobtools"
 )
 
 var (
@@ -126,32 +126,32 @@
 	properties map[string]string
 }
 
+type complianceMetadataInfoGob struct {
+	Properties map[string]string
+}
+
 func NewComplianceMetadataInfo() *ComplianceMetadataInfo {
 	return &ComplianceMetadataInfo{
 		properties: map[string]string{},
 	}
 }
 
-func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := encoder.Encode(c.properties)
-	if err != nil {
-		return nil, err
+func (m *ComplianceMetadataInfo) ToGob() *complianceMetadataInfoGob {
+	return &complianceMetadataInfoGob{
+		Properties: m.properties,
 	}
+}
 
-	return w.Bytes(), nil
+func (m *ComplianceMetadataInfo) FromGob(data *complianceMetadataInfoGob) {
+	m.properties = data.Properties
+}
+
+func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[complianceMetadataInfoGob](c)
 }
 
 func (c *ComplianceMetadataInfo) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := decoder.Decode(&c.properties)
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return gobtools.CustomGobDecode[complianceMetadataInfoGob](data, c)
 }
 
 func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) {
diff --git a/android/config.go b/android/config.go
index 00fc823..b811c55 100644
--- a/android/config.go
+++ b/android/config.go
@@ -84,7 +84,6 @@
 	SoongOutDir    string
 	SoongVariables string
 
-	BazelQueryViewDir string
 	ModuleGraphFile   string
 	ModuleActionsFile string
 	DocFile           string
@@ -99,11 +98,6 @@
 	// Don't use bazel at all during module analysis.
 	AnalysisNoBazel SoongBuildMode = iota
 
-	// Generate BUILD files which faithfully represent the dependency graph of
-	// blueprint modules. Individual BUILD targets will not, however, faitfhully
-	// express build semantics.
-	GenerateQueryView
-
 	// Create a JSON representation of the module graph and exit.
 	GenerateModuleGraph
 
@@ -250,6 +244,13 @@
 		Bool(c.config.productVariables.ReleaseDefaultModuleBuildFromSource)
 }
 
+func (c Config) ReleaseDefaultUpdatableModuleVersion() string {
+	if val, exists := c.GetBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION"); exists {
+		return val
+	}
+	panic("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION is missing from build flags.")
+}
+
 func (c Config) ReleaseDisableVerifyOverlaps() bool {
 	return c.config.productVariables.GetBuildFlagBool("RELEASE_DISABLE_VERIFY_OVERLAPS_CHECK")
 }
@@ -280,6 +281,14 @@
 	return c.config.productVariables.GetBuildFlagBool("RELEASE_READ_FROM_NEW_STORAGE")
 }
 
+func (c Config) ReleaseCreateAconfigStorageFile() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_CREATE_ACONFIG_STORAGE_FILE")
+}
+
+func (c Config) ReleaseUseSystemFeatureBuildFlags() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_USE_SYSTEM_FEATURE_BUILD_FLAGS")
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -313,6 +322,9 @@
 	AndroidCommonTarget      Target // the Target for common modules for the Android device
 	AndroidFirstDeviceTarget Target // the first Target for modules for the Android device
 
+	// Flags for Partial Compile, derived from SOONG_PARTIAL_COMPILE.
+	partialCompileFlags partialCompileFlags
+
 	// multilibConflicts for an ArchType is true if there is earlier configured
 	// device architecture with the same multilib value.
 	multilibConflicts map[ArchType]bool
@@ -362,6 +374,16 @@
 	ensureAllowlistIntegrity bool
 }
 
+type partialCompileFlags struct {
+	// Is partial compilation enabled at all?
+	enabled bool
+
+	// Whether to use d8 instead of r8
+	use_d8 bool
+
+	// Add others as needed.
+}
+
 type deviceConfig struct {
 	config *config
 	OncePer
@@ -371,6 +393,91 @@
 	SetDefaultConfig()
 }
 
+// Parse SOONG_PARTIAL_COMPILE.
+//
+// SOONG_PARTIAL_COMPILE determines which features are enabled or disabled in
+// rule generation.  Changing this environment variable causes reanalysis.
+//
+// SOONG_USE_PARTIAL_COMPILE determines whether or not we **use** PARTIAL_COMPILE.
+// Rule generation must support both cases, since changing it does not cause
+// reanalysis.
+//
+// The user-facing documentation shows:
+//
+// - empty or not set: "The current default state"
+// - "true" or "on": enable all stable partial compile features.
+// - "false" or "off": disable partial compile completely.
+//
+// What we actually allow is a comma separated list of tokens, whose first
+// character may be "+" (enable) or "-" (disable).  If neither is present, "+"
+// is assumed.  For example, "on,+use_d8" will enable partial compilation, and
+// additionally set the use_d8 flag (regardless of whether it is opt-in or
+// opt-out).
+//
+// To add a new feature to the list, add the field in the struct
+// `partialCompileFlags` above, and then add the name of the field in the
+// switch statement below.
+var defaultPartialCompileFlags = partialCompileFlags{
+	// Set any opt-out flags here.  Opt-in flags are off by default.
+	enabled: false,
+}
+
+func (c *config) parsePartialCompileFlags(isEngBuild bool) (partialCompileFlags, error) {
+	if !isEngBuild {
+		return partialCompileFlags{}, nil
+	}
+	value := c.Getenv("SOONG_PARTIAL_COMPILE")
+	if value == "" {
+		return defaultPartialCompileFlags, nil
+	}
+
+	ret := defaultPartialCompileFlags
+	tokens := strings.Split(strings.ToLower(value), ",")
+	makeVal := func(state string, defaultValue bool) bool {
+		switch state {
+		case "":
+			return defaultValue
+		case "-":
+			return false
+		case "+":
+			return true
+		}
+		return false
+	}
+	for _, tok := range tokens {
+		var state string
+		if len(tok) == 0 {
+			continue
+		}
+		switch tok[0:1] {
+		case "":
+			// Ignore empty tokens.
+			continue
+		case "-", "+":
+			state = tok[0:1]
+			tok = tok[1:]
+		default:
+			// Treat `feature` as `+feature`.
+			state = "+"
+		}
+		switch tok {
+		case "true":
+			ret = defaultPartialCompileFlags
+			ret.enabled = true
+		case "false":
+			// Set everything to false.
+			ret = partialCompileFlags{}
+		case "enabled":
+			ret.enabled = makeVal(state, defaultPartialCompileFlags.enabled)
+		case "use_d8":
+			ret.use_d8 = makeVal(state, defaultPartialCompileFlags.use_d8)
+		default:
+			return partialCompileFlags{}, fmt.Errorf("Unknown SOONG_PARTIAL_COMPILE value: %v", tok)
+		}
+	}
+	return ret, nil
+}
+
 func loadConfig(config *config) error {
 	return loadFromConfigFile(&config.productVariables, absolutePath(config.ProductVariablesFileName))
 }
@@ -516,6 +623,8 @@
 
 		buildFromSourceStub: cmdArgs.BuildFromSourceStub,
 	}
+	variant, ok := os.LookupEnv("TARGET_BUILD_VARIANT")
+	isEngBuild := !ok || variant == "eng"
 
 	config.deviceConfig = &deviceConfig{
 		config: config,
@@ -557,6 +666,11 @@
 		return Config{}, err
 	}
 
+	config.partialCompileFlags, err = config.parsePartialCompileFlags(isEngBuild)
+	if err != nil {
+		return Config{}, err
+	}
+
 	// Make the CommonOS OsType available for all products.
 	targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]}
 
@@ -605,7 +719,6 @@
 			config.BuildMode = mode
 		}
 	}
-	setBuildMode(cmdArgs.BazelQueryViewDir, GenerateQueryView)
 	setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
 	setBuildMode(cmdArgs.DocFile, GenerateDocFile)
 
@@ -758,7 +871,7 @@
 }
 
 func (c *config) TargetsJava21() bool {
-	return c.IsEnvTrue("EXPERIMENTAL_TARGET_JAVA_VERSION_21")
+	return c.productVariables.GetBuildFlagBool("RELEASE_TARGET_JAVA_21")
 }
 
 // EnvDeps returns the environment variables this build depends on. The first
@@ -988,6 +1101,10 @@
 	return ApiLevelOrPanic(ctx, codename)
 }
 
+func (c *config) PartialCompileFlags() partialCompileFlags {
+	return c.partialCompileFlags
+}
+
 func (c *config) AppsDefaultVersionName() string {
 	return String(c.productVariables.AppsDefaultVersionName)
 }
@@ -1252,7 +1369,7 @@
 }
 
 func (c *config) LibartImgHostBaseAddress() string {
-	return "0x60000000"
+	return "0x70000000"
 }
 
 func (c *config) LibartImgDeviceBaseAddress() string {
@@ -1278,6 +1395,7 @@
 	}
 	return false
 }
+
 func (c *config) EnforceRROExcludedOverlay(path string) bool {
 	excluded := c.productVariables.EnforceRROExcludedOverlays
 	if len(excluded) > 0 {
@@ -1286,6 +1404,11 @@
 	return false
 }
 
+func (c *config) EnforceRROGlobally() bool {
+	enforceList := c.productVariables.EnforceRROTargets
+	return InList("*", enforceList)
+}
+
 func (c *config) ExportedNamespaces() []string {
 	return append([]string(nil), c.productVariables.NamespacesToExport...)
 }
@@ -1396,6 +1519,13 @@
 	return "64"
 }
 
+func (c *deviceConfig) RecoveryPath() string {
+	if c.config.productVariables.RecoveryPath != nil {
+		return *c.config.productVariables.RecoveryPath
+	}
+	return "recovery"
+}
+
 func (c *deviceConfig) VendorPath() string {
 	if c.config.productVariables.VendorPath != nil {
 		return *c.config.productVariables.VendorPath
@@ -1403,6 +1533,17 @@
 	return "vendor"
 }
 
+func (c *deviceConfig) VendorDlkmPath() string {
+	if c.config.productVariables.VendorDlkmPath != nil {
+		return *c.config.productVariables.VendorDlkmPath
+	}
+	return "vendor_dlkm"
+}
+
+func (c *deviceConfig) BuildingVendorImage() bool {
+	return proptools.Bool(c.config.productVariables.BuildingVendorImage)
+}
+
 func (c *deviceConfig) CurrentApiLevelForVendorModules() string {
 	return StringDefault(c.config.productVariables.DeviceCurrentApiLevelForVendorModules, "current")
 }
@@ -1426,6 +1567,17 @@
 	return "odm"
 }
 
+func (c *deviceConfig) BuildingOdmImage() bool {
+	return proptools.Bool(c.config.productVariables.BuildingOdmImage)
+}
+
+func (c *deviceConfig) OdmDlkmPath() string {
+	if c.config.productVariables.OdmDlkmPath != nil {
+		return *c.config.productVariables.OdmDlkmPath
+	}
+	return "odm_dlkm"
+}
+
 func (c *deviceConfig) ProductPath() string {
 	if c.config.productVariables.ProductPath != nil {
 		return *c.config.productVariables.ProductPath
@@ -1433,6 +1585,10 @@
 	return "product"
 }
 
+func (c *deviceConfig) BuildingProductImage() bool {
+	return proptools.Bool(c.config.productVariables.BuildingProductImage)
+}
+
 func (c *deviceConfig) SystemExtPath() string {
 	if c.config.productVariables.SystemExtPath != nil {
 		return *c.config.productVariables.SystemExtPath
@@ -1440,6 +1596,35 @@
 	return "system_ext"
 }
 
+func (c *deviceConfig) SystemDlkmPath() string {
+	if c.config.productVariables.SystemDlkmPath != nil {
+		return *c.config.productVariables.SystemDlkmPath
+	}
+	return "system_dlkm"
+}
+
+func (c *deviceConfig) OemPath() string {
+	if c.config.productVariables.OemPath != nil {
+		return *c.config.productVariables.OemPath
+	}
+	return "oem"
+}
+
+func (c *deviceConfig) UserdataPath() string {
+	if c.config.productVariables.UserdataPath != nil {
+		return *c.config.productVariables.UserdataPath
+	}
+	return "data"
+}
+
+func (c *deviceConfig) BuildingUserdataImage() bool {
+	return proptools.Bool(c.config.productVariables.BuildingUserdataImage)
+}
+
+func (c *deviceConfig) BuildingRecoveryImage() bool {
+	return proptools.Bool(c.config.productVariables.BuildingRecoveryImage)
+}
+
 func (c *deviceConfig) BtConfigIncludeDir() string {
 	return String(c.config.productVariables.BtConfigIncludeDir)
 }
@@ -1664,8 +1849,8 @@
 	return Bool(c.productVariables.CompressedApex) && !c.UnbundledBuildApps()
 }
 
-func (c *config) ApexTrimEnabled() bool {
-	return Bool(c.productVariables.TrimmedApex)
+func (c *config) DefaultApexPayloadType() string {
+	return StringDefault(c.productVariables.DefaultApexPayloadType, "ext4")
 }
 
 func (c *config) UseSoongSystemImage() bool {
@@ -1691,14 +1876,6 @@
 	return Bool(c.productVariables.EnforceProductPartitionInterface)
 }
 
-func (c *config) EnforceInterPartitionJavaSdkLibrary() bool {
-	return Bool(c.productVariables.EnforceInterPartitionJavaSdkLibrary)
-}
-
-func (c *config) InterPartitionJavaLibraryAllowList() []string {
-	return c.productVariables.InterPartitionJavaLibraryAllowList
-}
-
 func (c *config) ProductHiddenAPIStubs() []string {
 	return c.productVariables.ProductHiddenAPIStubs
 }
@@ -1982,6 +2159,10 @@
 	return c.productVariables.GetBuildFlagBool("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH")
 }
 
+func (c *config) UseDexV41() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_USE_DEX_V41")
+}
+
 var (
 	mainlineApexContributionBuildFlagsToApexNames = map[string]string{
 		"RELEASE_APEX_CONTRIBUTIONS_ADBD":                    "com.android.adbd",
@@ -2081,6 +2262,14 @@
 	return PathsForSource(ctx, c.productVariables.OdmPropFiles)
 }
 
+func (c *config) VendorPropFiles(ctx PathContext) Paths {
+	return PathsForSource(ctx, c.productVariables.VendorPropFiles)
+}
+
+func (c *config) ExtraAllowedDepsTxt() string {
+	return String(c.productVariables.ExtraAllowedDepsTxt)
+}
+
 func (c *config) EnableUffdGc() string {
 	return String(c.productVariables.EnableUffdGc)
 }
@@ -2100,3 +2289,34 @@
 func (c *config) BoardAvbSystemAddHashtreeFooterArgs() []string {
 	return c.productVariables.BoardAvbSystemAddHashtreeFooterArgs
 }
+
+// Returns true if RELEASE_INSTALL_APEX_SYSTEMSERVER_DEXPREOPT_SAME_PARTITION is set to true.
+// If true, dexpreopt files of apex system server jars will be installed in the same partition as the parent apex.
+// If false, all these files will be installed in /system partition.
+func (c Config) InstallApexSystemServerDexpreoptSamePartition() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_INSTALL_APEX_SYSTEMSERVER_DEXPREOPT_SAME_PARTITION")
+}
+
+func (c *config) DeviceMatrixFile() []string {
+	return c.productVariables.DeviceMatrixFile
+}
+
+func (c *config) ProductManifestFiles() []string {
+	return c.productVariables.ProductManifestFiles
+}
+
+func (c *config) SystemManifestFile() []string {
+	return c.productVariables.SystemManifestFile
+}
+
+func (c *config) SystemExtManifestFiles() []string {
+	return c.productVariables.SystemExtManifestFiles
+}
+
+func (c *config) DeviceManifestFiles() []string {
+	return c.productVariables.DeviceManifestFiles
+}
+
+func (c *config) OdmManifestFiles() []string {
+	return c.productVariables.OdmManifestFiles
+}
diff --git a/android/config_test.go b/android/config_test.go
index 7732168..adb5ffa 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -212,3 +212,48 @@
 		assertStringEquals(t, "apex1:jarA", list5.String())
 	})
 }
+
+func (p partialCompileFlags) updateEnabled(value bool) partialCompileFlags {
+	p.enabled = value
+	return p
+}
+
+func (p partialCompileFlags) updateUseD8(value bool) partialCompileFlags {
+	p.use_d8 = value
+	return p
+}
+
+func TestPartialCompile(t *testing.T) {
+	mockConfig := func(value string) *config {
+		c := &config{
+			env: map[string]string{
+				"SOONG_PARTIAL_COMPILE": value,
+			},
+		}
+		return c
+	}
+	tests := []struct {
+		value      string
+		isEngBuild bool
+		expected   partialCompileFlags
+	}{
+		{"", true, defaultPartialCompileFlags},
+		{"false", true, partialCompileFlags{}},
+		{"true", true, defaultPartialCompileFlags.updateEnabled(true)},
+		{"true", false, partialCompileFlags{}},
+		{"true,use_d8", true, defaultPartialCompileFlags.updateEnabled(true).updateUseD8(true)},
+		{"true,-use_d8", true, defaultPartialCompileFlags.updateEnabled(true).updateUseD8(false)},
+		{"use_d8,false", true, partialCompileFlags{}},
+		{"false,+use_d8", true, partialCompileFlags{}.updateUseD8(true)},
+	}
+
+	for _, test := range tests {
+		t.Run(test.value, func(t *testing.T) {
+			config := mockConfig(test.value)
+			flags, _ := config.parsePartialCompileFlags(test.isEngBuild)
+			if flags != test.expected {
+				t.Errorf("expected %v found %v", test.expected, flags)
+			}
+		})
+	}
+}
diff --git a/android/container.go b/android/container.go
index c048d6c..27b17ed 100644
--- a/android/container.go
+++ b/android/container.go
@@ -93,7 +93,7 @@
 
 	// TODO(b/363016634): Remove from the allowlist when the module is converted
 	// to java_sdk_library and the java_aconfig_library modules depend on the stub.
-	"aconfig_storage_reader_java",
+	"aconfig_storage_stub",
 
 	// framework-res provides core resources essential for building apps and system UI.
 	// This module is implicitly added as a dependency for java modules even when the
@@ -382,7 +382,7 @@
 
 func (c *ContainersInfo) ApexNames() (ret []string) {
 	for _, apex := range c.belongingApexes {
-		ret = append(ret, apex.InApexModules...)
+		ret = append(ret, apex.InApexVariants...)
 	}
 	slices.Sort(ret)
 	return ret
@@ -479,7 +479,7 @@
 func checkContainerViolations(ctx ModuleContext) {
 	if _, ok := ctx.Module().(InstallableModule); ok {
 		containersInfo, _ := getContainerModuleInfo(ctx, ctx.Module())
-		ctx.VisitDirectDepsIgnoreBlueprint(func(dep Module) {
+		ctx.VisitDirectDeps(func(dep Module) {
 			if !dep.Enabled(ctx) {
 				return
 			}
diff --git a/android/csuite_config.go b/android/csuite_config.go
index 20bd035..26ad6e1 100644
--- a/android/csuite_config.go
+++ b/android/csuite_config.go
@@ -30,11 +30,11 @@
 type CSuiteConfig struct {
 	ModuleBase
 	properties     csuiteConfigProperties
-	OutputFilePath OutputPath
+	OutputFilePath Path
 }
 
 func (me *CSuiteConfig) GenerateAndroidBuildActions(ctx ModuleContext) {
-	me.OutputFilePath = PathForModuleOut(ctx, me.BaseModuleName()).OutputPath
+	me.OutputFilePath = PathForModuleOut(ctx, me.BaseModuleName())
 }
 
 func (me *CSuiteConfig) AndroidMkEntries() []AndroidMkEntries {
diff --git a/android/defaults.go b/android/defaults.go
index 3d06c69..510ebe0 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -273,8 +273,8 @@
 }
 
 func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel()
-	ctx.BottomUp("defaults", defaultsMutator).Parallel()
+	ctx.BottomUp("defaults_deps", defaultsDepsMutator)
+	ctx.BottomUp("defaults", defaultsMutator).UsesCreateModule()
 }
 
 func defaultsDepsMutator(ctx BottomUpMutatorContext) {
diff --git a/android/depset_generic.go b/android/depset_generic.go
deleted file mode 100644
index 690987a..0000000
--- a/android/depset_generic.go
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2020 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 android
-
-import (
-	"bytes"
-	"encoding/gob"
-	"errors"
-	"fmt"
-)
-
-// DepSet is designed to be conceptually compatible with Bazel's depsets:
-// https://docs.bazel.build/versions/master/skylark/depsets.html
-
-type DepSetOrder int
-
-const (
-	PREORDER DepSetOrder = iota
-	POSTORDER
-	TOPOLOGICAL
-)
-
-func (o DepSetOrder) String() string {
-	switch o {
-	case PREORDER:
-		return "PREORDER"
-	case POSTORDER:
-		return "POSTORDER"
-	case TOPOLOGICAL:
-		return "TOPOLOGICAL"
-	default:
-		panic(fmt.Errorf("Invalid DepSetOrder %d", o))
-	}
-}
-
-type depSettableType comparable
-
-// A DepSet efficiently stores a slice of an arbitrary type from transitive dependencies without
-// copying. It is stored as a DAG of DepSet nodes, each of which has some direct contents and a list
-// of dependency DepSet nodes.
-//
-// A DepSet has an order that will be used to walk the DAG when ToList() is called.  The order
-// can be POSTORDER, PREORDER, or TOPOLOGICAL.  POSTORDER and PREORDER orders return a postordered
-// or preordered left to right flattened list.  TOPOLOGICAL returns a list that guarantees that
-// elements of children are listed after all of their parents (unless there are duplicate direct
-// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the
-// duplicated element is not guaranteed).
-//
-// A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the slice for direct contents
-// and the *DepSets of dependencies. A DepSet is immutable once created.
-type DepSet[T depSettableType] struct {
-	preorder   bool
-	reverse    bool
-	order      DepSetOrder
-	direct     []T
-	transitive []*DepSet[T]
-}
-
-func (d *DepSet[T]) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(d.preorder), encoder.Encode(d.reverse),
-		encoder.Encode(d.order), encoder.Encode(d.direct), encoder.Encode(d.transitive))
-	if err != nil {
-		return nil, err
-	}
-
-	return w.Bytes(), nil
-}
-
-func (d *DepSet[T]) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&d.preorder), decoder.Decode(&d.reverse),
-		decoder.Decode(&d.order), decoder.Decode(&d.direct), decoder.Decode(&d.transitive))
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
-func NewDepSet[T depSettableType](order DepSetOrder, direct []T, transitive []*DepSet[T]) *DepSet[T] {
-	var directCopy []T
-	var transitiveCopy []*DepSet[T]
-	for _, t := range transitive {
-		if t.order != order {
-			panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
-				order, t.order))
-		}
-	}
-
-	if order == TOPOLOGICAL {
-		// TOPOLOGICAL is implemented as a postorder traversal followed by reversing the output.
-		// Pre-reverse the inputs here so their order is maintained in the output.
-		directCopy = ReverseSlice(direct)
-		transitiveCopy = ReverseSlice(transitive)
-	} else {
-		directCopy = append([]T(nil), direct...)
-		transitiveCopy = append([]*DepSet[T](nil), transitive...)
-	}
-
-	return &DepSet[T]{
-		preorder:   order == PREORDER,
-		reverse:    order == TOPOLOGICAL,
-		order:      order,
-		direct:     directCopy,
-		transitive: transitiveCopy,
-	}
-}
-
-// DepSetBuilder is used to create an immutable DepSet.
-type DepSetBuilder[T depSettableType] struct {
-	order      DepSetOrder
-	direct     []T
-	transitive []*DepSet[T]
-}
-
-// NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order and
-// type, represented by a slice of type that will be in the DepSet.
-func NewDepSetBuilder[T depSettableType](order DepSetOrder) *DepSetBuilder[T] {
-	return &DepSetBuilder[T]{
-		order: order,
-	}
-}
-
-// DirectSlice adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
-// contents are to the right of any existing direct contents.
-func (b *DepSetBuilder[T]) DirectSlice(direct []T) *DepSetBuilder[T] {
-	b.direct = append(b.direct, direct...)
-	return b
-}
-
-// Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
-// contents are to the right of any existing direct contents.
-func (b *DepSetBuilder[T]) Direct(direct ...T) *DepSetBuilder[T] {
-	b.direct = append(b.direct, direct...)
-	return b
-}
-
-// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added
-// transitive contents are to the right of any existing transitive contents.
-func (b *DepSetBuilder[T]) Transitive(transitive ...*DepSet[T]) *DepSetBuilder[T] {
-	for _, t := range transitive {
-		if t.order != b.order {
-			panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
-				b.order, t.order))
-		}
-	}
-	b.transitive = append(b.transitive, transitive...)
-	return b
-}
-
-// Returns the DepSet being built by this DepSetBuilder.  The DepSetBuilder retains its contents
-// for creating more depSets.
-func (b *DepSetBuilder[T]) Build() *DepSet[T] {
-	return NewDepSet(b.order, b.direct, b.transitive)
-}
-
-// walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set,
-// otherwise postordered.
-func (d *DepSet[T]) walk(visit func([]T)) {
-	visited := make(map[*DepSet[T]]bool)
-
-	var dfs func(d *DepSet[T])
-	dfs = func(d *DepSet[T]) {
-		visited[d] = true
-		if d.preorder {
-			visit(d.direct)
-		}
-		for _, dep := range d.transitive {
-			if !visited[dep] {
-				dfs(dep)
-			}
-		}
-
-		if !d.preorder {
-			visit(d.direct)
-		}
-	}
-
-	dfs(d)
-}
-
-// ToList returns the DepSet flattened to a list.  The order in the list is based on the order
-// of the DepSet.  POSTORDER and PREORDER orders return a postordered or preordered left to right
-// flattened list.  TOPOLOGICAL returns a list that guarantees that elements of children are listed
-// after all of their parents (unless there are duplicate direct elements in the DepSet or any of
-// its transitive dependencies, in which case the ordering of the duplicated element is not
-// guaranteed).
-func (d *DepSet[T]) ToList() []T {
-	if d == nil {
-		return nil
-	}
-	var list []T
-	d.walk(func(paths []T) {
-		list = append(list, paths...)
-	})
-	list = firstUniqueInPlace(list)
-	if d.reverse {
-		ReverseSliceInPlace(list)
-	}
-	return list
-}
diff --git a/android/depset_test.go b/android/depset_test.go
deleted file mode 100644
index 376dffa..0000000
--- a/android/depset_test.go
+++ /dev/null
@@ -1,295 +0,0 @@
-// Copyright 2020 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 android
-
-import (
-	"fmt"
-	"reflect"
-	"strings"
-	"testing"
-)
-
-func ExampleDepSet_ToList_postordered() {
-	a := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("a")).Build()
-	b := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build()
-	c := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build()
-	d := NewDepSetBuilder[Path](POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
-
-	fmt.Println(Paths(d.ToList()).Strings())
-	// Output: [a b c d]
-}
-
-func ExampleDepSet_ToList_preordered() {
-	a := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("a")).Build()
-	b := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("b")).Transitive(a).Build()
-	c := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("c")).Transitive(a).Build()
-	d := NewDepSetBuilder[Path](PREORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
-
-	fmt.Println(Paths(d.ToList()).Strings())
-	// Output: [d b a c]
-}
-
-func ExampleDepSet_ToList_topological() {
-	a := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("a")).Build()
-	b := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("b")).Transitive(a).Build()
-	c := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("c")).Transitive(a).Build()
-	d := NewDepSetBuilder[Path](TOPOLOGICAL).Direct(PathForTesting("d")).Transitive(b, c).Build()
-
-	fmt.Println(Paths(d.ToList()).Strings())
-	// Output: [d b c a]
-}
-
-// Tests based on Bazel's ExpanderTestBase.java to ensure compatibility
-// https://github.com/bazelbuild/bazel/blob/master/src/test/java/com/google/devtools/build/lib/collect/nestedset/ExpanderTestBase.java
-func TestDepSet(t *testing.T) {
-	a := PathForTesting("a")
-	b := PathForTesting("b")
-	c := PathForTesting("c")
-	c2 := PathForTesting("c2")
-	d := PathForTesting("d")
-	e := PathForTesting("e")
-
-	tests := []struct {
-		name                             string
-		depSet                           func(t *testing.T, order DepSetOrder) *DepSet[Path]
-		postorder, preorder, topological []string
-	}{
-		{
-			name: "simple",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				return NewDepSet[Path](order, Paths{c, a, b}, nil)
-			},
-			postorder:   []string{"c", "a", "b"},
-			preorder:    []string{"c", "a", "b"},
-			topological: []string{"c", "a", "b"},
-		},
-		{
-			name: "simpleNoDuplicates",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				return NewDepSet[Path](order, Paths{c, a, a, a, b}, nil)
-			},
-			postorder:   []string{"c", "a", "b"},
-			preorder:    []string{"c", "a", "b"},
-			topological: []string{"c", "a", "b"},
-		},
-		{
-			name: "nesting",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				subset := NewDepSet[Path](order, Paths{c, a, e}, nil)
-				return NewDepSet[Path](order, Paths{b, d}, []*DepSet[Path]{subset})
-			},
-			postorder:   []string{"c", "a", "e", "b", "d"},
-			preorder:    []string{"b", "d", "c", "a", "e"},
-			topological: []string{"b", "d", "c", "a", "e"},
-		},
-		{
-			name: "builderReuse",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				assertEquals := func(t *testing.T, w, g Paths) {
-					t.Helper()
-					if !reflect.DeepEqual(w, g) {
-						t.Errorf("want %q, got %q", w, g)
-					}
-				}
-				builder := NewDepSetBuilder[Path](order)
-				assertEquals(t, nil, builder.Build().ToList())
-
-				builder.Direct(b)
-				assertEquals(t, Paths{b}, builder.Build().ToList())
-
-				builder.Direct(d)
-				assertEquals(t, Paths{b, d}, builder.Build().ToList())
-
-				child := NewDepSetBuilder[Path](order).Direct(c, a, e).Build()
-				builder.Transitive(child)
-				return builder.Build()
-			},
-			postorder:   []string{"c", "a", "e", "b", "d"},
-			preorder:    []string{"b", "d", "c", "a", "e"},
-			topological: []string{"b", "d", "c", "a", "e"},
-		},
-		{
-			name: "builderChaining",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				return NewDepSetBuilder[Path](order).Direct(b).Direct(d).
-					Transitive(NewDepSetBuilder[Path](order).Direct(c, a, e).Build()).Build()
-			},
-			postorder:   []string{"c", "a", "e", "b", "d"},
-			preorder:    []string{"b", "d", "c", "a", "e"},
-			topological: []string{"b", "d", "c", "a", "e"},
-		},
-		{
-			name: "transitiveDepsHandledSeparately",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				subset := NewDepSetBuilder[Path](order).Direct(c, a, e).Build()
-				builder := NewDepSetBuilder[Path](order)
-				// The fact that we add the transitive subset between the Direct(b) and Direct(d)
-				// calls should not change the result.
-				builder.Direct(b)
-				builder.Transitive(subset)
-				builder.Direct(d)
-				return builder.Build()
-			},
-			postorder:   []string{"c", "a", "e", "b", "d"},
-			preorder:    []string{"b", "d", "c", "a", "e"},
-			topological: []string{"b", "d", "c", "a", "e"},
-		},
-		{
-			name: "nestingNoDuplicates",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				subset := NewDepSetBuilder[Path](order).Direct(c, a, e).Build()
-				return NewDepSetBuilder[Path](order).Direct(b, d, e).Transitive(subset).Build()
-			},
-			postorder:   []string{"c", "a", "e", "b", "d"},
-			preorder:    []string{"b", "d", "e", "c", "a"},
-			topological: []string{"b", "d", "c", "a", "e"},
-		},
-		{
-			name: "chain",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				c := NewDepSetBuilder[Path](order).Direct(c).Build()
-				b := NewDepSetBuilder[Path](order).Direct(b).Transitive(c).Build()
-				a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Build()
-
-				return a
-			},
-			postorder:   []string{"c", "b", "a"},
-			preorder:    []string{"a", "b", "c"},
-			topological: []string{"a", "b", "c"},
-		},
-		{
-			name: "diamond",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				d := NewDepSetBuilder[Path](order).Direct(d).Build()
-				c := NewDepSetBuilder[Path](order).Direct(c).Transitive(d).Build()
-				b := NewDepSetBuilder[Path](order).Direct(b).Transitive(d).Build()
-				a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Transitive(c).Build()
-
-				return a
-			},
-			postorder:   []string{"d", "b", "c", "a"},
-			preorder:    []string{"a", "b", "d", "c"},
-			topological: []string{"a", "b", "c", "d"},
-		},
-		{
-			name: "extendedDiamond",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				d := NewDepSetBuilder[Path](order).Direct(d).Build()
-				e := NewDepSetBuilder[Path](order).Direct(e).Build()
-				b := NewDepSetBuilder[Path](order).Direct(b).Transitive(d).Transitive(e).Build()
-				c := NewDepSetBuilder[Path](order).Direct(c).Transitive(e).Transitive(d).Build()
-				a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Transitive(c).Build()
-				return a
-			},
-			postorder:   []string{"d", "e", "b", "c", "a"},
-			preorder:    []string{"a", "b", "d", "e", "c"},
-			topological: []string{"a", "b", "c", "e", "d"},
-		},
-		{
-			name: "extendedDiamondRightArm",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				d := NewDepSetBuilder[Path](order).Direct(d).Build()
-				e := NewDepSetBuilder[Path](order).Direct(e).Build()
-				b := NewDepSetBuilder[Path](order).Direct(b).Transitive(d).Transitive(e).Build()
-				c2 := NewDepSetBuilder[Path](order).Direct(c2).Transitive(e).Transitive(d).Build()
-				c := NewDepSetBuilder[Path](order).Direct(c).Transitive(c2).Build()
-				a := NewDepSetBuilder[Path](order).Direct(a).Transitive(b).Transitive(c).Build()
-				return a
-			},
-			postorder:   []string{"d", "e", "b", "c2", "c", "a"},
-			preorder:    []string{"a", "b", "d", "e", "c", "c2"},
-			topological: []string{"a", "b", "c", "c2", "e", "d"},
-		},
-		{
-			name: "orderConflict",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				child1 := NewDepSetBuilder[Path](order).Direct(a, b).Build()
-				child2 := NewDepSetBuilder[Path](order).Direct(b, a).Build()
-				parent := NewDepSetBuilder[Path](order).Transitive(child1).Transitive(child2).Build()
-				return parent
-			},
-			postorder:   []string{"a", "b"},
-			preorder:    []string{"a", "b"},
-			topological: []string{"b", "a"},
-		},
-		{
-			name: "orderConflictNested",
-			depSet: func(t *testing.T, order DepSetOrder) *DepSet[Path] {
-				a := NewDepSetBuilder[Path](order).Direct(a).Build()
-				b := NewDepSetBuilder[Path](order).Direct(b).Build()
-				child1 := NewDepSetBuilder[Path](order).Transitive(a).Transitive(b).Build()
-				child2 := NewDepSetBuilder[Path](order).Transitive(b).Transitive(a).Build()
-				parent := NewDepSetBuilder[Path](order).Transitive(child1).Transitive(child2).Build()
-				return parent
-			},
-			postorder:   []string{"a", "b"},
-			preorder:    []string{"a", "b"},
-			topological: []string{"b", "a"},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			t.Run("postorder", func(t *testing.T) {
-				depSet := tt.depSet(t, POSTORDER)
-				if g, w := Paths(depSet.ToList()).Strings(), tt.postorder; !reflect.DeepEqual(g, w) {
-					t.Errorf("expected ToList() = %q, got %q", w, g)
-				}
-			})
-			t.Run("preorder", func(t *testing.T) {
-				depSet := tt.depSet(t, PREORDER)
-				if g, w := Paths(depSet.ToList()).Strings(), tt.preorder; !reflect.DeepEqual(g, w) {
-					t.Errorf("expected ToList() = %q, got %q", w, g)
-				}
-			})
-			t.Run("topological", func(t *testing.T) {
-				depSet := tt.depSet(t, TOPOLOGICAL)
-				if g, w := Paths(depSet.ToList()).Strings(), tt.topological; !reflect.DeepEqual(g, w) {
-					t.Errorf("expected ToList() = %q, got %q", w, g)
-				}
-			})
-		})
-	}
-}
-
-func TestDepSetInvalidOrder(t *testing.T) {
-	orders := []DepSetOrder{POSTORDER, PREORDER, TOPOLOGICAL}
-
-	run := func(t *testing.T, order1, order2 DepSetOrder) {
-		defer func() {
-			if r := recover(); r != nil {
-				if err, ok := r.(error); !ok {
-					t.Fatalf("expected panic error, got %v", err)
-				} else if !strings.Contains(err.Error(), "incompatible order") {
-					t.Fatalf("expected incompatible order error, got %v", err)
-				}
-			}
-		}()
-		NewDepSet(order1, nil, []*DepSet[Path]{NewDepSet[Path](order2, nil, nil)})
-		t.Fatal("expected panic")
-	}
-
-	for _, order1 := range orders {
-		t.Run(order1.String(), func(t *testing.T) {
-			for _, order2 := range orders {
-				t.Run(order2.String(), func(t *testing.T) {
-					if order1 != order2 {
-						run(t, order1, order2)
-					}
-				})
-			}
-		})
-	}
-}
diff --git a/android/dirgroup.go b/android/dirgroup.go
new file mode 100644
index 0000000..62fbaa5
--- /dev/null
+++ b/android/dirgroup.go
@@ -0,0 +1,62 @@
+// 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 android
+
+import (
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	RegisterDirgroupBuildComponents(InitRegistrationContext)
+}
+
+func RegisterDirgroupBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("dirgroup", DirGroupFactory)
+}
+
+type dirGroupProperties struct {
+	// dirs lists directories that will be included in this dirgroup
+	Dirs proptools.Configurable[[]string] `android:"path"`
+}
+
+type dirGroup struct {
+	ModuleBase
+	DefaultableModuleBase
+	properties dirGroupProperties
+}
+
+type DirInfo struct {
+	Dirs DirectoryPaths
+}
+
+var DirProvider = blueprint.NewProvider[DirInfo]()
+
+// dirgroup contains a list of dirs that are referenced by other modules
+// properties using the syntax ":<name>". dirgroup are also be used to export
+// dirs across package boundaries. Currently the only allowed usage is genrule's
+// dir_srcs property.
+func DirGroupFactory() Module {
+	module := &dirGroup{}
+	module.AddProperties(&module.properties)
+	InitAndroidModule(module)
+	InitDefaultableModule(module)
+	return module
+}
+
+func (fg *dirGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
+	dirs := DirectoryPathsForModuleSrc(ctx, fg.properties.Dirs.GetOrDefault(ctx, nil))
+	SetProvider(ctx, DirProvider, DirInfo{Dirs: dirs})
+}
diff --git a/android/early_module_context.go b/android/early_module_context.go
index 23f4c90..5e971ef 100644
--- a/android/early_module_context.go
+++ b/android/early_module_context.go
@@ -21,15 +21,26 @@
 	"github.com/google/blueprint"
 )
 
+// ModuleErrorContext provides only methods to report errors about the current module.
+type ModuleErrorContext interface {
+	// ModuleErrorf reports an error at the line number of the module type in the module definition.
+	ModuleErrorf(fmt string, args ...interface{})
+
+	// PropertyErrorf reports an error at the line number of a property in the module definition.
+	PropertyErrorf(property, fmt string, args ...interface{})
+}
+
 // EarlyModuleContext provides methods that can be called early, as soon as the properties have
 // been parsed into the module and before any mutators have run.
 type EarlyModuleContext interface {
+	ModuleErrorContext
+
 	// Module returns the current module as a Module.  It should rarely be necessary, as the module already has a
 	// reference to itself.
 	Module() Module
 
 	// ModuleName returns the name of the module.  This is generally the value that was returned by Module.Name() when
-	// the module was created, but may have been modified by calls to BaseMutatorContext.Rename.
+	// the module was created, but may have been modified by calls to BottomUpMutatorContext.Rename.
 	ModuleName() string
 
 	// ModuleDir returns the path to the directory that contains the definition of the module.
@@ -49,12 +60,6 @@
 	// Errorf reports an error at the specified position of the module definition file.
 	Errorf(pos scanner.Position, fmt string, args ...interface{})
 
-	// ModuleErrorf reports an error at the line number of the module type in the module definition.
-	ModuleErrorf(fmt string, args ...interface{})
-
-	// PropertyErrorf reports an error at the line number of a property in the module definition.
-	PropertyErrorf(property, fmt string, args ...interface{})
-
 	// OtherModulePropertyErrorf reports an error at the line number of a property in the given module definition.
 	OtherModulePropertyErrorf(module Module, property, fmt string, args ...interface{})
 
@@ -93,6 +98,10 @@
 	// Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the
 	// default SimpleNameInterface if Context.SetNameInterface was not called.
 	Namespace() *Namespace
+
+	// HasMutatorFinished returns true if the given mutator has finished running.
+	// It will panic if given an invalid mutator name.
+	HasMutatorFinished(mutatorName string) bool
 }
 
 // Deprecated: use EarlyModuleContext instead
@@ -175,3 +184,7 @@
 func (e *earlyModuleContext) OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{}) {
 	e.EarlyModuleContext.OtherModulePropertyErrorf(module, property, fmt, args...)
 }
+
+func (e *earlyModuleContext) HasMutatorFinished(mutatorName string) bool {
+	return e.EarlyModuleContext.HasMutatorFinished(mutatorName)
+}
diff --git a/android/filegroup.go b/android/filegroup.go
index ff0f74e..67e5add1f 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -41,6 +41,14 @@
 
 	Exclude_srcs proptools.Configurable[[]string] `android:"path"`
 
+	// Sources the will be included in the filegroup, but any module dependencies will be added
+	// using the device os and the device's first architecture's variant.
+	Device_first_srcs proptools.Configurable[[]string] `android:"path_device_first"`
+
+	// Sources the will be included in the filegroup, but any module dependencies will be added
+	// using the device os and the common architecture's variant.
+	Device_common_srcs proptools.Configurable[[]string] `android:"path_device_common"`
+
 	// 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,
 	// the base path is stripped off the path and the remaining path is used as the
@@ -90,11 +98,13 @@
 }
 
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
-	fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs.GetOrDefault(ctx, nil), fg.properties.Exclude_srcs.GetOrDefault(ctx, nil))
+	srcs := PathsForModuleSrcExcludes(ctx, fg.properties.Srcs.GetOrDefault(ctx, nil), fg.properties.Exclude_srcs.GetOrDefault(ctx, nil))
+	srcs = append(srcs, PathsForModuleSrc(ctx, fg.properties.Device_first_srcs.GetOrDefault(ctx, nil))...)
+	srcs = append(srcs, PathsForModuleSrc(ctx, fg.properties.Device_common_srcs.GetOrDefault(ctx, nil))...)
 	if fg.properties.Path != nil {
-		fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
+		srcs = PathsWithModuleSrcSubDir(ctx, srcs, String(fg.properties.Path))
 	}
-	SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: fg.srcs.Strings()})
+	SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
 
 	var aconfigDeclarations []string
 	var intermediateCacheOutputPaths Paths
@@ -108,6 +118,8 @@
 			maps.Copy(modeInfos, dep.ModeInfos)
 		}
 	})
+
+	fg.srcs = srcs
 	SetProvider(ctx, CodegenInfoProvider, CodegenInfo{
 		AconfigDeclarations:          aconfigDeclarations,
 		IntermediateCacheOutputPaths: intermediateCacheOutputPaths,
@@ -139,3 +151,15 @@
 
 	return module
 }
+
+// Collect information for opening IDE project files in java/jdeps.go.
+// Copied from build/soong/genrule/genrule.go
+func (fg *fileGroup) IDEInfo(ctx BaseModuleContext, dpInfo *IdeInfo) {
+	dpInfo.Srcs = append(dpInfo.Srcs, fg.Srcs().Strings()...)
+	for _, src := range fg.properties.Srcs.GetOrDefault(ctx, nil) {
+		if mod, _ := SrcIsModuleWithTag(src); mod != "" {
+			// Register the module name without any tags in `Deps`
+			dpInfo.Deps = append(dpInfo.Deps, mod)
+		}
+	}
+}
diff --git a/android/filegroup_test.go b/android/filegroup_test.go
index 14e9368..670037d 100644
--- a/android/filegroup_test.go
+++ b/android/filegroup_test.go
@@ -1,55 +1,9 @@
 package android
 
 import (
-	"path/filepath"
 	"testing"
 )
 
-func TestFileGroupWithPathProp(t *testing.T) {
-	// TODO(b/247782695), TODO(b/242847534) Fix mixed builds for filegroups
-	t.Skip("Re-enable once filegroups are corrected for mixed builds")
-	outBaseDir := "outputbase"
-	pathPrefix := outBaseDir + "/execroot/__main__"
-	expectedOutputfile := filepath.Join(pathPrefix, "a/b/c/d/test.aidl")
-
-	testCases := []struct {
-		bp  string
-		rel string
-	}{
-		{
-			bp: `
-	filegroup {
-		name: "baz",
-		srcs: ["a/b/c/d/test.aidl"],
-		path: "a/b",
-		bazel_module: { label: "//:baz" },
-	}
-`,
-			rel: "c/d/test.aidl",
-		},
-		{
-			bp: `
-	filegroup {
-		name: "baz",
-		srcs: ["a/b/c/d/test.aidl"],
-		bazel_module: { label: "//:baz" },
-	}
-`,
-			rel: "a/b/c/d/test.aidl",
-		},
-	}
-
-	for _, testCase := range testCases {
-		result := GroupFixturePreparers(
-			PrepareForTestWithFilegroup,
-		).RunTestWithBp(t, testCase.bp)
-
-		fg := result.Module("baz", "").(*fileGroup)
-		AssertStringEquals(t, "src relativeRoot", testCase.rel, fg.srcs[0].Rel())
-		AssertStringEquals(t, "src full path", expectedOutputfile, fg.srcs[0].String())
-	}
-}
-
 func TestFilegroupDefaults(t *testing.T) {
 	bp := FixtureAddTextFile("p/Android.bp", `
 		filegroup_defaults {
diff --git a/android/hooks.go b/android/hooks.go
index 2ad3b5f..f8022d0 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -37,6 +37,7 @@
 	AppendProperties(...interface{})
 	PrependProperties(...interface{})
 	CreateModule(ModuleFactory, ...interface{}) Module
+	CreateModuleInDirectory(ModuleFactory, string, ...interface{}) Module
 
 	registerScopedModuleType(name string, factory blueprint.ModuleFactory)
 	moduleFactories() map[string]blueprint.ModuleFactory
@@ -58,6 +59,16 @@
 	})
 }
 
+func AddLoadHookWithPriority(m blueprint.Module, hook func(LoadHookContext), priority int) {
+	blueprint.AddLoadHookWithPriority(m, func(ctx blueprint.LoadHookContext) {
+		actx := &loadHookContext{
+			earlyModuleContext: m.(Module).base().earlyModuleContextFactory(ctx),
+			bp:                 ctx,
+		}
+		hook(actx)
+	}, priority)
+}
+
 type loadHookContext struct {
 	earlyModuleContext
 	bp     blueprint.LoadHookContext
@@ -93,12 +104,43 @@
 	return l.bp.CreateModule(factory, name, props...)
 }
 
-type createModuleContext interface {
-	Module() Module
-	createModule(blueprint.ModuleFactory, string, ...interface{}) blueprint.Module
+func (l *loadHookContext) createModuleInDirectory(factory blueprint.ModuleFactory, name, moduleDir string, props ...interface{}) blueprint.Module {
+	return l.bp.CreateModuleInDirectory(factory, name, moduleDir, props...)
 }
 
-func createModule(ctx createModuleContext, factory ModuleFactory, ext string, props ...interface{}) Module {
+type specifyDirectory struct {
+	specified bool
+	directory string
+}
+
+func doesNotSpecifyDirectory() specifyDirectory {
+	return specifyDirectory{
+		specified: false,
+		directory: "",
+	}
+}
+
+func specifiesDirectory(directory string) specifyDirectory {
+	return specifyDirectory{
+		specified: true,
+		directory: directory,
+	}
+}
+
+type createModuleContext interface {
+	Module() Module
+	HasMutatorFinished(mutatorName string) bool
+	createModule(blueprint.ModuleFactory, string, ...interface{}) blueprint.Module
+	createModuleInDirectory(blueprint.ModuleFactory, string, string, ...interface{}) blueprint.Module
+}
+
+func createModule(ctx createModuleContext, factory ModuleFactory, ext string, specifyDirectory specifyDirectory, props ...interface{}) Module {
+	if ctx.HasMutatorFinished("defaults") {
+		// Creating modules late is oftentimes problematic, because they don't have earlier
+		// mutators run on them. Prevent making modules after the defaults mutator has run.
+		panic("Cannot create a module after the defaults mutator has finished")
+	}
+
 	inherited := []interface{}{&ctx.Module().base().commonProperties}
 
 	var typeName string
@@ -112,7 +154,12 @@
 	}
 	typeName = typeName + "_" + ext
 
-	module := ctx.createModule(ModuleFactoryAdaptor(factory), typeName, append(inherited, props...)...).(Module)
+	var module Module
+	if specifyDirectory.specified {
+		module = ctx.createModuleInDirectory(ModuleFactoryAdaptor(factory), typeName, specifyDirectory.directory, append(inherited, props...)...).(Module)
+	} else {
+		module = ctx.createModule(ModuleFactoryAdaptor(factory), typeName, append(inherited, props...)...).(Module)
+	}
 
 	if ctx.Module().base().variableProperties != nil && module.base().variableProperties != nil {
 		src := ctx.Module().base().variableProperties
@@ -132,7 +179,11 @@
 }
 
 func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
-	return createModule(l, factory, "_loadHookModule", props...)
+	return createModule(l, factory, "_loadHookModule", doesNotSpecifyDirectory(), props...)
+}
+
+func (l *loadHookContext) CreateModuleInDirectory(factory ModuleFactory, directory string, props ...interface{}) Module {
+	return createModule(l, factory, "_loadHookModule", specifiesDirectory(directory), props...)
 }
 
 func (l *loadHookContext) registerScopedModuleType(name string, factory blueprint.ModuleFactory) {
diff --git a/android/image.go b/android/image.go
index 6e5a551..78343db 100644
--- a/android/image.go
+++ b/android/image.go
@@ -14,44 +14,61 @@
 
 package android
 
+type ImageInterfaceContext interface {
+	ArchModuleContext
+
+	Module() Module
+
+	ModuleErrorf(fmt string, args ...interface{})
+	PropertyErrorf(property, fmt string, args ...interface{})
+
+	DeviceSpecific() bool
+	SocSpecific() bool
+	ProductSpecific() bool
+	SystemExtSpecific() bool
+	Platform() bool
+
+	Config() Config
+}
+
 // ImageInterface is implemented by modules that need to be split by the imageTransitionMutator.
 type ImageInterface interface {
 	// ImageMutatorBegin is called before any other method in the ImageInterface.
-	ImageMutatorBegin(ctx BaseModuleContext)
+	ImageMutatorBegin(ctx ImageInterfaceContext)
 
 	// VendorVariantNeeded should return true if the module needs a vendor variant (installed on the vendor image).
-	VendorVariantNeeded(ctx BaseModuleContext) bool
+	VendorVariantNeeded(ctx ImageInterfaceContext) bool
 
 	// ProductVariantNeeded should return true if the module needs a product variant (installed on the product image).
-	ProductVariantNeeded(ctx BaseModuleContext) bool
+	ProductVariantNeeded(ctx ImageInterfaceContext) bool
 
 	// CoreVariantNeeded should return true if the module needs a core variant (installed on the system image).
-	CoreVariantNeeded(ctx BaseModuleContext) bool
+	CoreVariantNeeded(ctx ImageInterfaceContext) bool
 
 	// RamdiskVariantNeeded should return true if the module needs a ramdisk variant (installed on the
 	// ramdisk partition).
-	RamdiskVariantNeeded(ctx BaseModuleContext) bool
+	RamdiskVariantNeeded(ctx ImageInterfaceContext) bool
 
 	// VendorRamdiskVariantNeeded should return true if the module needs a vendor ramdisk variant (installed on the
 	// vendor ramdisk partition).
-	VendorRamdiskVariantNeeded(ctx BaseModuleContext) bool
+	VendorRamdiskVariantNeeded(ctx ImageInterfaceContext) bool
 
 	// DebugRamdiskVariantNeeded should return true if the module needs a debug ramdisk variant (installed on the
 	// debug ramdisk partition: $(PRODUCT_OUT)/debug_ramdisk).
-	DebugRamdiskVariantNeeded(ctx BaseModuleContext) bool
+	DebugRamdiskVariantNeeded(ctx ImageInterfaceContext) bool
 
 	// RecoveryVariantNeeded should return true if the module needs a recovery variant (installed on the
 	// recovery partition).
-	RecoveryVariantNeeded(ctx BaseModuleContext) bool
+	RecoveryVariantNeeded(ctx ImageInterfaceContext) bool
 
 	// ExtraImageVariations should return a list of the additional variations needed for the module.  After the
 	// variants are created the SetImageVariation method will be called on each newly created variant with the
 	// its variation.
-	ExtraImageVariations(ctx BaseModuleContext) []string
+	ExtraImageVariations(ctx ImageInterfaceContext) []string
 
 	// SetImageVariation is called for each newly created image variant. The receiver is the original
 	// module, "variation" is the name of the newly created variant. "variation" is set on the receiver.
-	SetImageVariation(ctx BaseModuleContext, variation string)
+	SetImageVariation(ctx ImageInterfaceContext, variation string)
 }
 
 const (
@@ -81,16 +98,53 @@
 	DebugRamdiskVariation string = "debug_ramdisk"
 )
 
+type imageInterfaceContextAdapter struct {
+	IncomingTransitionContext
+	kind moduleKind
+}
+
+var _ ImageInterfaceContext = (*imageInterfaceContextAdapter)(nil)
+
+func (e *imageInterfaceContextAdapter) Platform() bool {
+	return e.kind == platformModule
+}
+
+func (e *imageInterfaceContextAdapter) DeviceSpecific() bool {
+	return e.kind == deviceSpecificModule
+}
+
+func (e *imageInterfaceContextAdapter) SocSpecific() bool {
+	return e.kind == socSpecificModule
+}
+
+func (e *imageInterfaceContextAdapter) ProductSpecific() bool {
+	return e.kind == productSpecificModule
+}
+
+func (e *imageInterfaceContextAdapter) SystemExtSpecific() bool {
+	return e.kind == systemExtSpecificModule
+}
+
+// imageMutatorBeginMutator calls ImageMutatorBegin on all modules that may have image variants.
+// This happens right before the imageTransitionMutator runs. It's needed to initialize these
+// modules so that they return the correct results for all the other ImageInterface methods,
+// which the imageTransitionMutator will call. Transition mutators should also not mutate modules
+// (except in their Mutate() function), which this method does, so we run it in a separate mutator
+// first.
+func imageMutatorBeginMutator(ctx BottomUpMutatorContext) {
+	if m, ok := ctx.Module().(ImageInterface); ok && ctx.Os() == Android {
+		m.ImageMutatorBegin(ctx)
+	}
+}
+
 // imageTransitionMutator creates variants for modules that implement the ImageInterface that
 // allow them to build differently for each partition (recovery, core, vendor, etc.).
 type imageTransitionMutator struct{}
 
-func (imageTransitionMutator) Split(ctx BaseModuleContext) []string {
+func getImageVariations(ctx ImageInterfaceContext) []string {
 	var variations []string
 
 	if m, ok := ctx.Module().(ImageInterface); ctx.Os() == Android && ok {
-		m.ImageMutatorBegin(ctx)
-
 		if m.CoreVariantNeeded(ctx) {
 			variations = append(variations, CoreVariation)
 		}
@@ -124,6 +178,10 @@
 	return variations
 }
 
+func (imageTransitionMutator) Split(ctx BaseModuleContext) []string {
+	return getImageVariations(ctx)
+}
+
 func (imageTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
 	return sourceVariation
 }
@@ -132,6 +190,16 @@
 	if _, ok := ctx.Module().(ImageInterface); ctx.Os() != Android || !ok {
 		return CoreVariation
 	}
+	variations := getImageVariations(&imageInterfaceContextAdapter{
+		IncomingTransitionContext: ctx,
+		kind:                      determineModuleKind(ctx.Module().base(), ctx),
+	})
+	// If there's only 1 possible variation, use that. This is a holdover from when blueprint,
+	// when adding dependencies, would use the only variant of a module regardless of its variations
+	// if only 1 variant existed.
+	if len(variations) == 1 {
+		return variations[0]
+	}
 	return incomingVariation
 }
 
diff --git a/android/init.go b/android/init.go
index b462292..d3a13d0 100644
--- a/android/init.go
+++ b/android/init.go
@@ -17,7 +17,13 @@
 import "encoding/gob"
 
 func init() {
+	gob.Register(extraFilesZip{})
+	gob.Register(InstallPath{})
+	gob.Register(ModuleGenPath{})
+	gob.Register(ModuleObjPath{})
 	gob.Register(ModuleOutPath{})
+	gob.Register(OutputPath{})
 	gob.Register(PhonyPath{})
+	gob.Register(SourcePath{})
 	gob.Register(unstableInfo{})
 }
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 0ac975f..3df36e6 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"github.com/google/blueprint/depset"
 	"sort"
 	"strings"
 
@@ -61,13 +62,9 @@
 	var allDepMetadataFiles Paths
 	var allDepMetadataArgs []string
 	var allDepOutputFiles Paths
-	var allDepMetadataDepSets []*DepSet[Path]
+	var allDepMetadataDepSets []depset.DepSet[Path]
 
-	ctx.VisitDirectDepsBlueprint(func(bpdep blueprint.Module) {
-		dep, _ := bpdep.(Module)
-		if dep == nil {
-			return
-		}
+	ctx.VisitDirectDeps(func(dep Module) {
 		if !dep.Enabled(ctx) {
 			return
 		}
@@ -137,7 +134,7 @@
 		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_text.Strings()), "-n "))
 
 	if isContainer {
-		transitiveDeps := Paths(NewDepSet[Path](TOPOLOGICAL, nil, allDepMetadataDepSets).ToList())
+		transitiveDeps := Paths(depset.New[Path](depset.TOPOLOGICAL, nil, allDepMetadataDepSets).ToList())
 		args = append(args,
 			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(transitiveDeps.Strings()), "-d "))
 		orderOnlyDeps = append(orderOnlyDeps, transitiveDeps...)
@@ -180,7 +177,7 @@
 
 	SetProvider(ctx, LicenseMetadataProvider, &LicenseMetadataInfo{
 		LicenseMetadataPath:   licenseMetadataFile,
-		LicenseMetadataDepSet: NewDepSet(TOPOLOGICAL, Paths{licenseMetadataFile}, allDepMetadataDepSets),
+		LicenseMetadataDepSet: depset.New(depset.TOPOLOGICAL, Paths{licenseMetadataFile}, allDepMetadataDepSets),
 	})
 }
 
@@ -208,7 +205,7 @@
 // LicenseMetadataInfo stores the license metadata path for a module.
 type LicenseMetadataInfo struct {
 	LicenseMetadataPath   Path
-	LicenseMetadataDepSet *DepSet[Path]
+	LicenseMetadataDepSet depset.DepSet[Path]
 }
 
 // licenseAnnotationsFromTag returns the LicenseAnnotations for a tag (if any) converted into
diff --git a/android/licenses.go b/android/licenses.go
index be1eede..53d0555 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -15,7 +15,10 @@
 package android
 
 import (
+	"fmt"
+	"path/filepath"
 	"reflect"
+	"strings"
 	"sync"
 
 	"github.com/google/blueprint"
@@ -106,19 +109,19 @@
 //
 // This goes before defaults expansion so the defaults can pick up the package default.
 func RegisterLicensesPackageMapper(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("licensesPackageMapper", licensesPackageMapper).Parallel()
+	ctx.BottomUp("licensesPackageMapper", licensesPackageMapper)
 }
 
 // Registers the function that gathers the license dependencies for each module.
 //
 // This goes after defaults expansion so that it can pick up default licenses and before visibility enforcement.
 func RegisterLicensesPropertyGatherer(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("licensesPropertyGatherer", licensesPropertyGatherer).Parallel()
+	ctx.BottomUp("licensesPropertyGatherer", licensesPropertyGatherer)
 }
 
 // Registers the function that verifies the licenses and license_kinds dependency types for each module.
 func RegisterLicensesDependencyChecker(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("licensesPropertyChecker", licensesDependencyChecker).Parallel()
+	ctx.BottomUp("licensesPropertyChecker", licensesDependencyChecker)
 }
 
 // Maps each package to its default applicable licenses.
@@ -155,7 +158,25 @@
 	}
 
 	licenses := getLicenses(ctx, m)
-	ctx.AddVariationDependencies(nil, licensesTag, licenses...)
+
+	var fullyQualifiedLicenseNames []string
+	for _, license := range licenses {
+		fullyQualifiedLicenseName := license
+		if !strings.HasPrefix(license, "//") {
+			licenseModuleDir := ctx.OtherModuleDir(m)
+			for licenseModuleDir != "." && !ctx.OtherModuleExists(fmt.Sprintf("//%s:%s", licenseModuleDir, license)) {
+				licenseModuleDir = filepath.Dir(licenseModuleDir)
+			}
+			if licenseModuleDir == "." {
+				fullyQualifiedLicenseName = license
+			} else {
+				fullyQualifiedLicenseName = fmt.Sprintf("//%s:%s", licenseModuleDir, license)
+			}
+		}
+		fullyQualifiedLicenseNames = append(fullyQualifiedLicenseNames, fullyQualifiedLicenseName)
+	}
+
+	ctx.AddVariationDependencies(nil, licensesTag, fullyQualifiedLicenseNames...)
 }
 
 // Verifies the license and license_kind dependencies are each the correct kind of module.
diff --git a/android/module.go b/android/module.go
index e2b7e11..a9f6b94 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,9 +15,6 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
 	"net/url"
 	"path/filepath"
@@ -27,6 +24,8 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
+	"github.com/google/blueprint/gobtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -81,10 +80,15 @@
 	InstallInOdm() bool
 	InstallInProduct() bool
 	InstallInVendor() bool
+	InstallInSystemExt() bool
+	InstallInSystemDlkm() bool
+	InstallInVendorDlkm() bool
+	InstallInOdmDlkm() bool
 	InstallForceOS() (*OsType, *ArchType)
 	PartitionTag(DeviceConfig) string
 	HideFromMake()
 	IsHideFromMake() bool
+	SkipInstall()
 	IsSkipInstall() bool
 	MakeUninstallable()
 	ReplacedByPrebuilt()
@@ -113,8 +117,17 @@
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
 	VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string
+	VintfFragments(ctx ConfigurableEvaluatorContext) []string
 
 	ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator
+
+	// The usage of this method is experimental and should not be used outside of fsgen package.
+	// This will be removed once product packaging migration to Soong is complete.
+	DecodeMultilib(ctx ConfigContext) (string, string)
+
+	// WARNING: This should not be used outside build/soong/fsgen
+	// Overrides returns the list of modules which should not be installed if this module is installed.
+	Overrides() []string
 }
 
 // Qualified id for a module
@@ -325,6 +338,7 @@
 		}
 		Android struct {
 			Compile_multilib *string
+			Enabled          *bool
 		}
 	}
 
@@ -377,6 +391,15 @@
 	// Whether this module is installed to debug ramdisk
 	Debug_ramdisk *bool
 
+	// Install to partition system_dlkm when set to true.
+	System_dlkm_specific *bool
+
+	// Install to partition vendor_dlkm when set to true.
+	Vendor_dlkm_specific *bool
+
+	// Install to partition odm_dlkm when set to true.
+	Odm_dlkm_specific *bool
+
 	// Whether this module is built for non-native architectures (also known as native bridge binary)
 	Native_bridge_supported *bool `android:"arch_variant"`
 
@@ -541,6 +564,13 @@
 	}
 }
 
+func (t *CommonTestOptions) SetAndroidMkInfoEntries(entries *AndroidMkInfo) {
+	entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(t.Unit_test))
+	if len(t.Tags) > 0 {
+		entries.AddStrings("LOCAL_TEST_OPTIONS_TAGS", t.Tags...)
+	}
+}
+
 // The key to use in TaggedDistFiles when a Dist structure does not specify a
 // tag property. This intentionally does not use "" as the default because that
 // would mean that an empty tag would have a different meaning when used in a dist
@@ -607,10 +637,9 @@
 type Multilib string
 
 const (
-	MultilibBoth        Multilib = "both"
-	MultilibFirst       Multilib = "first"
-	MultilibCommon      Multilib = "common"
-	MultilibCommonFirst Multilib = "common_first"
+	MultilibBoth   Multilib = "both"
+	MultilibFirst  Multilib = "first"
+	MultilibCommon Multilib = "common"
 )
 
 type HostOrDeviceSupported int
@@ -1057,8 +1086,34 @@
 }{}
 
 func addVintfFragmentDeps(ctx BottomUpMutatorContext) {
+	// Vintf manifests in the recovery partition will be ignored.
+	if !ctx.Device() || ctx.Module().InstallInRecovery() {
+		return
+	}
+
+	deviceConfig := ctx.DeviceConfig()
+
 	mod := ctx.Module()
-	ctx.AddDependency(mod, vintfDepTag, mod.VintfFragmentModuleNames(ctx)...)
+	vintfModules := ctx.AddDependency(mod, vintfDepTag, mod.VintfFragmentModuleNames(ctx)...)
+
+	modPartition := mod.PartitionTag(deviceConfig)
+	for _, vintf := range vintfModules {
+		if vintf == nil {
+			// TODO(b/372091092): Remove this. Having it gives us missing dependency errors instead
+			// of nil pointer dereference errors, but we should resolve the missing dependencies.
+			continue
+		}
+		if vintfModule, ok := vintf.(*vintfFragmentModule); ok {
+			vintfPartition := vintfModule.PartitionTag(deviceConfig)
+			if modPartition != vintfPartition {
+				ctx.ModuleErrorf("Module %q(%q) and Vintf_fragment %q(%q) are installed to different partitions.",
+					mod.Name(), modPartition,
+					vintfModule.Name(), vintfPartition)
+			}
+		} else {
+			ctx.ModuleErrorf("Only vintf_fragment type module should be listed in vintf_fragment_modules : %q", vintf.Name())
+		}
+	}
 }
 
 // AddProperties "registers" the provided props
@@ -1329,6 +1384,12 @@
 		if config.SystemExtPath() == "system_ext" {
 			partition = "system_ext"
 		}
+	} else if m.InstallInRamdisk() {
+		partition = "ramdisk"
+	} else if m.InstallInVendorRamdisk() {
+		partition = "vendor_ramdisk"
+	} else if m.InstallInRecovery() {
+		partition = "recovery"
 	}
 	return partition
 }
@@ -1381,6 +1442,7 @@
 func (m *ModuleBase) MakeUninstallable() {
 	m.commonProperties.UninstallableApexPlatformVariant = true
 	m.HideFromMake()
+	m.SkipInstall()
 }
 
 func (m *ModuleBase) ReplacedByPrebuilt() {
@@ -1410,9 +1472,9 @@
 
 // computeInstallDeps finds the installed paths of all dependencies that have a dependency
 // tag that is annotated as needing installation via the isInstallDepNeeded method.
-func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*DepSet[InstallPath], []*DepSet[PackagingSpec]) {
-	var installDeps []*DepSet[InstallPath]
-	var packagingSpecs []*DepSet[PackagingSpec]
+func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]depset.DepSet[InstallPath], []depset.DepSet[PackagingSpec]) {
+	var installDeps []depset.DepSet[InstallPath]
+	var packagingSpecs []depset.DepSet[PackagingSpec]
 	ctx.VisitDirectDeps(func(dep Module) {
 		if isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
 			// Installation is still handled by Make, so anything hidden from Make is not
@@ -1485,10 +1547,26 @@
 	return Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Soc_specific) || Bool(m.commonProperties.Proprietary)
 }
 
+func (m *ModuleBase) InstallInSystemExt() bool {
+	return Bool(m.commonProperties.System_ext_specific)
+}
+
 func (m *ModuleBase) InstallInRoot() bool {
 	return false
 }
 
+func (m *ModuleBase) InstallInSystemDlkm() bool {
+	return Bool(m.commonProperties.System_dlkm_specific)
+}
+
+func (m *ModuleBase) InstallInVendorDlkm() bool {
+	return Bool(m.commonProperties.Vendor_dlkm_specific)
+}
+
+func (m *ModuleBase) InstallInOdmDlkm() bool {
+	return Bool(m.commonProperties.Odm_dlkm_specific)
+}
+
 func (m *ModuleBase) InstallForceOS() (*OsType, *ArchType) {
 	return nil, nil
 }
@@ -1554,6 +1632,10 @@
 	return m.base().commonProperties.Vintf_fragment_modules.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
 }
 
+func (m *ModuleBase) VintfFragments(ctx ConfigurableEvaluatorContext) []string {
+	return m.base().commonProperties.Vintf_fragments.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
+}
+
 func (m *ModuleBase) generateVariantTarget(ctx *moduleContext) {
 	namespacePrefix := ctx.Namespace().id
 	if namespacePrefix != "" {
@@ -1571,25 +1653,27 @@
 func (m *ModuleBase) generateModuleTarget(ctx *moduleContext) {
 	var allInstalledFiles InstallPaths
 	var allCheckbuildTargets Paths
-	ctx.VisitAllModuleVariants(func(module Module) {
-		a := module.base()
+	ctx.VisitAllModuleVariantProxies(func(module ModuleProxy) {
 		var checkbuildTarget Path
 		var uncheckedModule bool
-		if a == m {
+		var skipAndroidMkProcessing bool
+		if ctx.EqualModules(m.module, module) {
 			allInstalledFiles = append(allInstalledFiles, ctx.installFiles...)
 			checkbuildTarget = ctx.checkbuildTarget
 			uncheckedModule = ctx.uncheckedModule
+			skipAndroidMkProcessing = shouldSkipAndroidMkProcessing(ctx, m)
 		} else {
 			info := OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider)
 			allInstalledFiles = append(allInstalledFiles, info.InstallFiles...)
 			checkbuildTarget = info.CheckbuildTarget
 			uncheckedModule = info.UncheckedModule
+			skipAndroidMkProcessing = OtherModuleProviderOrDefault(ctx, module, CommonModuleInfoKey).SkipAndroidMkProcessing
 		}
 		// A module's -checkbuild phony targets should
 		// not be created if the module is not exported to make.
 		// Those could depend on the build target and fail to compile
 		// for the current build target.
-		if (!ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(ctx, a)) && !uncheckedModule && checkbuildTarget != nil {
+		if (!ctx.Config().KatiEnabled() || !skipAndroidMkProcessing) && !uncheckedModule && checkbuildTarget != nil {
 			allCheckbuildTargets = append(allCheckbuildTargets, checkbuildTarget)
 		}
 	})
@@ -1630,7 +1714,7 @@
 	}
 }
 
-func determineModuleKind(m *ModuleBase, ctx blueprint.EarlyModuleContext) moduleKind {
+func determineModuleKind(m *ModuleBase, ctx ModuleErrorContext) moduleKind {
 	var socSpecific = Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Proprietary) || Bool(m.commonProperties.Soc_specific)
 	var deviceSpecific = Bool(m.commonProperties.Device_specific)
 	var productSpecific = Bool(m.commonProperties.Product_specific)
@@ -1741,12 +1825,12 @@
 	KatiInstalls             katiInstalls
 	KatiSymlinks             katiInstalls
 	TestData                 []DataPath
-	TransitivePackagingSpecs *DepSet[PackagingSpec]
+	TransitivePackagingSpecs depset.DepSet[PackagingSpec]
 	LicenseMetadataFile      WritablePath
 
 	// The following fields are private before, make it private again once we have
 	// better solution.
-	TransitiveInstallFiles *DepSet[InstallPath]
+	TransitiveInstallFiles depset.DepSet[InstallPath]
 	// katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are
 	// allowed to have duplicates across modules and variants.
 	KatiInitRcInstalls           katiInstalls
@@ -1762,6 +1846,12 @@
 
 var InstallFilesProvider = blueprint.NewProvider[InstallFilesInfo]()
 
+type SourceFilesInfo struct {
+	Srcs Paths
+}
+
+var SourceFilesInfoKey = blueprint.NewProvider[SourceFilesInfo]()
+
 type FinalModuleBuildTargetsInfo struct {
 	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
 	// Only set on the final variant of each module
@@ -1772,6 +1862,31 @@
 
 var FinalModuleBuildTargetsProvider = blueprint.NewProvider[FinalModuleBuildTargetsInfo]()
 
+type CommonModuleInfo struct {
+	Enabled bool
+	// Whether the module has been replaced by a prebuilt
+	ReplacedByPrebuilt bool
+	// The Target of artifacts that this module variant is responsible for creating.
+	CompileTarget           Target
+	SkipAndroidMkProcessing bool
+	BaseModuleName          string
+	CanHaveApexVariants     bool
+}
+
+var CommonModuleInfoKey = blueprint.NewProvider[CommonModuleInfo]()
+
+type PrebuiltModuleProviderData struct {
+	// Empty for now
+}
+
+var PrebuiltModuleProviderKey = blueprint.NewProvider[PrebuiltModuleProviderData]()
+
+type HostToolProviderData struct {
+	HostToolPath OptionalPath
+}
+
+var HostToolProviderKey = blueprint.NewProvider[HostToolProviderData]()
+
 func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
 	ctx := &moduleContext{
 		module:            m.module,
@@ -1792,7 +1907,7 @@
 	// set the TransitiveInstallFiles to only the transitive dependencies to be used as the dependencies
 	// of installed files of this module.  It will be replaced by a depset including the installed
 	// files of this module at the end for use by modules that depend on this one.
-	ctx.TransitiveInstallFiles = NewDepSet[InstallPath](TOPOLOGICAL, nil, dependencyInstallFiles)
+	ctx.TransitiveInstallFiles = depset.New[InstallPath](depset.TOPOLOGICAL, nil, dependencyInstallFiles)
 
 	// Temporarily continue to call blueprintCtx.GetMissingDependencies() to maintain the previous behavior of never
 	// reporting missing dependency errors in Blueprint when AllowMissingDependencies == true.
@@ -1840,11 +1955,7 @@
 
 	if m.Enabled(ctx) {
 		// ensure all direct android.Module deps are enabled
-		ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) {
-			if m, ok := bm.(Module); ok {
-				ctx.validateAndroidModule(bm, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps, false)
-			}
-		})
+		ctx.VisitDirectDepsProxy(func(m ModuleProxy) {})
 
 		if m.Device() {
 			// Handle any init.rc and vintf fragment files requested by the module.  All files installed by this
@@ -1946,16 +2057,20 @@
 		ctx.GetMissingDependencies()
 	}
 
-	if m == ctx.FinalModule().(Module).base() {
+	if sourceFileProducer, ok := m.module.(SourceFileProducer); ok {
+		SetProvider(ctx, SourceFilesInfoKey, SourceFilesInfo{Srcs: sourceFileProducer.Srcs()})
+	}
+
+	if ctx.IsFinalModule(m.module) {
 		m.generateModuleTarget(ctx)
 		if ctx.Failed() {
 			return
 		}
 	}
 
-	ctx.TransitiveInstallFiles = NewDepSet[InstallPath](TOPOLOGICAL, ctx.installFiles, dependencyInstallFiles)
+	ctx.TransitiveInstallFiles = depset.New[InstallPath](depset.TOPOLOGICAL, ctx.installFiles, dependencyInstallFiles)
 	installFiles.TransitiveInstallFiles = ctx.TransitiveInstallFiles
-	installFiles.TransitivePackagingSpecs = NewDepSet[PackagingSpec](TOPOLOGICAL, ctx.packagingSpecs, dependencyPackagingSpecs)
+	installFiles.TransitivePackagingSpecs = depset.New[PackagingSpec](depset.TOPOLOGICAL, ctx.packagingSpecs, dependencyPackagingSpecs)
 
 	SetProvider(ctx, InstallFilesProvider, installFiles)
 	buildLicenseMetadata(ctx, ctx.licenseMetadataFile)
@@ -2019,6 +2134,32 @@
 		})
 	}
 	buildComplianceMetadataProvider(ctx, m)
+
+	commonData := CommonModuleInfo{
+		ReplacedByPrebuilt:      m.commonProperties.ReplacedByPrebuilt,
+		CompileTarget:           m.commonProperties.CompileTarget,
+		SkipAndroidMkProcessing: shouldSkipAndroidMkProcessing(ctx, m),
+		BaseModuleName:          m.BaseModuleName(),
+	}
+	if m.commonProperties.ForcedDisabled {
+		commonData.Enabled = false
+	} else {
+		commonData.Enabled = m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled)
+	}
+	am, ok := m.module.(ApexModule)
+	commonData.CanHaveApexVariants = ok && am.CanHaveApexVariants()
+	SetProvider(ctx, CommonModuleInfoKey, commonData)
+	if p, ok := m.module.(PrebuiltInterface); ok && p.Prebuilt() != nil {
+		SetProvider(ctx, PrebuiltModuleProviderKey, PrebuiltModuleProviderData{})
+	}
+	if h, ok := m.module.(HostToolProvider); ok {
+		SetProvider(ctx, HostToolProviderKey, HostToolProviderData{
+			HostToolPath: h.HostToolPath()})
+	}
+
+	if p, ok := m.module.(AndroidMkProviderInfoProducer); ok && !commonData.SkipAndroidMkProcessing {
+		SetProvider(ctx, AndroidMkInfoProvider, p.PrepareAndroidMKProviderInfo(ctx.Config()))
+	}
 }
 
 func SetJarJarPrefixHandler(handler func(ModuleContext)) {
@@ -2098,36 +2239,47 @@
 	orderOnlyDeps Paths
 	executable    bool
 	extraFiles    *extraFilesZip
-
-	absFrom string
+	absFrom       string
 }
 
-func (p *katiInstall) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.from), encoder.Encode(p.to),
-		encoder.Encode(p.implicitDeps), encoder.Encode(p.orderOnlyDeps),
-		encoder.Encode(p.executable), encoder.Encode(p.extraFiles),
-		encoder.Encode(p.absFrom))
-	if err != nil {
-		return nil, err
-	}
-
-	return w.Bytes(), nil
+type katiInstallGob struct {
+	From          Path
+	To            InstallPath
+	ImplicitDeps  Paths
+	OrderOnlyDeps Paths
+	Executable    bool
+	ExtraFiles    *extraFilesZip
+	AbsFrom       string
 }
 
-func (p *katiInstall) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.from), decoder.Decode(&p.to),
-		decoder.Decode(&p.implicitDeps), decoder.Decode(&p.orderOnlyDeps),
-		decoder.Decode(&p.executable), decoder.Decode(&p.extraFiles),
-		decoder.Decode(&p.absFrom))
-	if err != nil {
-		return err
+func (k *katiInstall) ToGob() *katiInstallGob {
+	return &katiInstallGob{
+		From:          k.from,
+		To:            k.to,
+		ImplicitDeps:  k.implicitDeps,
+		OrderOnlyDeps: k.orderOnlyDeps,
+		Executable:    k.executable,
+		ExtraFiles:    k.extraFiles,
+		AbsFrom:       k.absFrom,
 	}
+}
 
-	return nil
+func (k *katiInstall) FromGob(data *katiInstallGob) {
+	k.from = data.From
+	k.to = data.To
+	k.implicitDeps = data.ImplicitDeps
+	k.orderOnlyDeps = data.OrderOnlyDeps
+	k.executable = data.Executable
+	k.extraFiles = data.ExtraFiles
+	k.absFrom = data.AbsFrom
+}
+
+func (k *katiInstall) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[katiInstallGob](k)
+}
+
+func (k *katiInstall) GobDecode(data []byte) error {
+	return gobtools.CustomGobDecode[katiInstallGob](data, k)
 }
 
 type extraFilesZip struct {
@@ -2135,26 +2287,29 @@
 	dir InstallPath
 }
 
-func (p *extraFilesZip) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.zip), encoder.Encode(p.dir))
-	if err != nil {
-		return nil, err
-	}
-
-	return w.Bytes(), nil
+type extraFilesZipGob struct {
+	Zip Path
+	Dir InstallPath
 }
 
-func (p *extraFilesZip) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.zip), decoder.Decode(&p.dir))
-	if err != nil {
-		return err
+func (e *extraFilesZip) ToGob() *extraFilesZipGob {
+	return &extraFilesZipGob{
+		Zip: e.zip,
+		Dir: e.dir,
 	}
+}
 
-	return nil
+func (e *extraFilesZip) FromGob(data *extraFilesZipGob) {
+	e.zip = data.Zip
+	e.dir = data.Dir
+}
+
+func (e *extraFilesZip) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[extraFilesZipGob](e)
+}
+
+func (e *extraFilesZip) GobDecode(data []byte) error {
+	return gobtools.CustomGobDecode[extraFilesZipGob](data, e)
 }
 
 type katiInstalls []katiInstall
@@ -2206,6 +2361,14 @@
 	return proptools.Bool(m.commonProperties.Native_bridge_supported)
 }
 
+func (m *ModuleBase) DecodeMultilib(ctx ConfigContext) (string, string) {
+	return decodeMultilib(ctx, m)
+}
+
+func (m *ModuleBase) Overrides() []string {
+	return m.commonProperties.Overrides
+}
+
 type ConfigContext interface {
 	Config() Config
 }
@@ -2269,11 +2432,15 @@
 		}
 		variable := condition.Arg(0)
 		switch variable {
+		case "build_from_text_stub":
+			return proptools.ConfigurableValueBool(ctx.Config().BuildFromTextStub())
 		case "debuggable":
 			return proptools.ConfigurableValueBool(ctx.Config().Debuggable())
 		case "use_debug_art":
 			// TODO(b/234351700): Remove once ART does not have separated debug APEX
 			return proptools.ConfigurableValueBool(ctx.Config().UseDebugArt())
+		case "selinux_ignore_neverallows":
+			return proptools.ConfigurableValueBool(ctx.Config().SelinuxIgnoreNeverallows())
 		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))
@@ -2298,6 +2465,8 @@
 					return proptools.ConfigurableValueString(v)
 				case "bool":
 					return proptools.ConfigurableValueBool(v == "true")
+				case "string_list":
+					return proptools.ConfigurableValueStringList(strings.Split(v, " "))
 				default:
 					panic("unhandled soong config variable type: " + ty)
 				}
@@ -2494,7 +2663,7 @@
 
 // OutputFilesForModule returns the output file paths with the given tag. On error, including if the
 // module produced zero paths, it reports errors to the ctx and returns nil.
-func OutputFilesForModule(ctx PathContext, module blueprint.Module, tag string) Paths {
+func OutputFilesForModule(ctx PathContext, module Module, tag string) Paths {
 	paths, err := outputFilesForModule(ctx, module, tag)
 	if err != nil {
 		reportPathError(ctx, err)
@@ -2505,7 +2674,7 @@
 
 // OutputFileForModule returns the output file paths with the given tag.  On error, including if the
 // module produced zero or multiple paths, it reports errors to the ctx and returns nil.
-func OutputFileForModule(ctx PathContext, module blueprint.Module, tag string) Path {
+func OutputFileForModule(ctx PathContext, module Module, tag string) Path {
 	paths, err := outputFilesForModule(ctx, module, tag)
 	if err != nil {
 		reportPathError(ctx, err)
@@ -2538,20 +2707,34 @@
 	return paths[0]
 }
 
-func outputFilesForModule(ctx PathContext, module blueprint.Module, tag string) (Paths, error) {
+type OutputFilesProviderModuleContext interface {
+	OtherModuleProviderContext
+	Module() Module
+	GetOutputFiles() OutputFilesInfo
+	EqualModules(m1, m2 Module) bool
+}
+
+func outputFilesForModule(ctx PathContext, module Module, tag string) (Paths, error) {
 	outputFilesFromProvider, err := outputFilesForModuleFromProvider(ctx, module, tag)
 	if outputFilesFromProvider != nil || err != OutputFilesProviderNotSet {
 		return outputFilesFromProvider, err
 	}
-	if sourceFileProducer, ok := module.(SourceFileProducer); ok {
-		if tag != "" {
-			return nil, fmt.Errorf("module %q is a SourceFileProducer, which does not support tag %q", pathContextName(ctx, module), tag)
+
+	if octx, ok := ctx.(OutputFilesProviderModuleContext); ok {
+		if octx.EqualModules(octx.Module(), module) {
+			if sourceFileProducer, ok := module.(SourceFileProducer); ok {
+				return sourceFileProducer.Srcs(), nil
+			}
+		} else if sourceFiles, ok := OtherModuleProvider(octx, module, SourceFilesInfoKey); ok {
+			if tag != "" {
+				return nil, fmt.Errorf("module %q is a SourceFileProducer, which does not support tag %q", pathContextName(ctx, module), tag)
+			}
+			paths := sourceFiles.Srcs
+			return paths, nil
 		}
-		paths := sourceFileProducer.Srcs()
-		return paths, nil
-	} else {
-		return nil, fmt.Errorf("module %q is not a SourceFileProducer or having valid output file for tag %q", pathContextName(ctx, module), tag)
 	}
+
+	return nil, fmt.Errorf("module %q is not a SourceFileProducer or having valid output file for tag %q", pathContextName(ctx, module), tag)
 }
 
 // This method uses OutputFilesProvider for output files
@@ -2560,26 +2743,19 @@
 // from outputFiles property of module base, to avoid both setting and
 // reading OutputFilesProvider before GenerateBuildActions is finished.
 // If a module doesn't have the OutputFilesProvider, nil is returned.
-func outputFilesForModuleFromProvider(ctx PathContext, module blueprint.Module, tag string) (Paths, error) {
+func outputFilesForModuleFromProvider(ctx PathContext, module Module, tag string) (Paths, error) {
 	var outputFiles OutputFilesInfo
 	fromProperty := false
 
-	type OutputFilesProviderModuleContext interface {
-		OtherModuleProviderContext
-		Module() Module
-		GetOutputFiles() OutputFilesInfo
-	}
-
 	if mctx, isMctx := ctx.(OutputFilesProviderModuleContext); isMctx {
-		if mctx.Module() != module {
+		if !mctx.EqualModules(mctx.Module(), module) {
 			outputFiles, _ = OtherModuleProvider(mctx, module, OutputFilesProvider)
 		} else {
 			outputFiles = mctx.GetOutputFiles()
 			fromProperty = true
 		}
 	} else if cta, isCta := ctx.(*singletonContextAdaptor); isCta {
-		providerData, _ := cta.otherModuleProvider(module, OutputFilesProvider)
-		outputFiles, _ = providerData.(OutputFilesInfo)
+		outputFiles, _ = OtherModuleProvider(cta, module, OutputFilesProvider)
 	} else {
 		return nil, fmt.Errorf("unsupported context %q in method outputFilesForModuleFromProvider", reflect.TypeOf(ctx))
 	}
diff --git a/android/module_context.go b/android/module_context.go
index 2bf2a8f..ae7b54f 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -18,9 +18,11 @@
 	"fmt"
 	"path"
 	"path/filepath"
+	"slices"
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -194,6 +196,9 @@
 	InstallInOdm() bool
 	InstallInProduct() bool
 	InstallInVendor() bool
+	InstallInSystemDlkm() bool
+	InstallInVendorDlkm() bool
+	InstallInOdmDlkm() bool
 	InstallForceOS() (*OsType, *ArchType)
 
 	RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string
@@ -261,7 +266,7 @@
 	// the OutputFilesProvider in GenerateBuildActions
 	outputFiles OutputFilesInfo
 
-	TransitiveInstallFiles *DepSet[InstallPath]
+	TransitiveInstallFiles depset.DepSet[InstallPath]
 
 	// set of dependency module:location mappings used to populate the license metadata for
 	// apex containers.
@@ -434,9 +439,11 @@
 	return missingDeps
 }
 
-func (m *moduleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
-	module, _ := m.getDirectDepInternal(name, tag)
-	return module
+func (m *moduleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) Module {
+	if module, _ := m.getDirectDepInternal(name, tag); module != nil {
+		return module.(Module)
+	}
+	return nil
 }
 
 func (m *moduleContext) ModuleSubDir() string {
@@ -491,15 +498,23 @@
 	return m.module.InstallInVendor()
 }
 
+func (m *moduleContext) InstallInSystemDlkm() bool {
+	return m.module.InstallInSystemDlkm()
+}
+
+func (m *moduleContext) InstallInVendorDlkm() bool {
+	return m.module.InstallInVendorDlkm()
+}
+
+func (m *moduleContext) InstallInOdmDlkm() bool {
+	return m.module.InstallInOdmDlkm()
+}
+
 func (m *moduleContext) skipInstall() bool {
 	if m.module.base().commonProperties.SkipInstall {
 		return true
 	}
 
-	if m.module.base().commonProperties.HideFromMake {
-		return true
-	}
-
 	// We'll need a solution for choosing which of modules with the same name in different
 	// namespaces to install.  For now, reuse the list of namespaces exported to Make as the
 	// list of namespaces to install in a Soong-only build.
@@ -518,6 +533,10 @@
 		return false
 	}
 
+	if m.module.base().commonProperties.HideFromMake {
+		return false
+	}
+
 	if proptools.Bool(m.module.base().commonProperties.No_full_install) {
 		return false
 	}
@@ -561,9 +580,22 @@
 	m.aconfigFilePaths = paths
 }
 
+func (m *moduleContext) getOwnerAndOverrides() (string, []string) {
+	owner := m.ModuleName()
+	overrides := slices.Clone(m.Module().base().commonProperties.Overrides)
+	if b, ok := m.Module().(OverridableModule); ok {
+		if b.GetOverriddenBy() != "" {
+			// overriding variant of base module
+			overrides = append(overrides, m.ModuleName()) // com.android.foo
+			owner = m.Module().Name()                     // com.company.android.foo
+		}
+	}
+	return owner, overrides
+}
+
 func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec {
 	licenseFiles := m.Module().EffectiveLicenseFiles()
-	overrides := CopyOf(m.Module().base().commonProperties.Overrides)
+	owner, overrides := m.getOwnerAndOverrides()
 	spec := PackagingSpec{
 		relPathInPackage:      Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
 		srcPath:               srcPath,
@@ -575,7 +607,7 @@
 		aconfigPaths:          m.getAconfigPaths(),
 		archType:              m.target.Arch.ArchType,
 		overrides:             &overrides,
-		owner:                 m.ModuleName(),
+		owner:                 owner,
 	}
 	m.packagingSpecs = append(m.packagingSpecs, spec)
 	return spec
@@ -694,7 +726,7 @@
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
 
-	overrides := CopyOf(m.Module().base().commonProperties.Overrides)
+	owner, overrides := m.getOwnerAndOverrides()
 	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
 		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
 		srcPath:          nil,
@@ -705,7 +737,7 @@
 		aconfigPaths:     m.getAconfigPaths(),
 		archType:         m.target.Arch.ArchType,
 		overrides:        &overrides,
-		owner:            m.ModuleName(),
+		owner:            owner,
 	})
 
 	return fullInstallPath
@@ -741,7 +773,7 @@
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
 
-	overrides := CopyOf(m.Module().base().commonProperties.Overrides)
+	owner, overrides := m.getOwnerAndOverrides()
 	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
 		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
 		srcPath:          nil,
@@ -752,7 +784,7 @@
 		aconfigPaths:     m.getAconfigPaths(),
 		archType:         m.target.Arch.ArchType,
 		overrides:        &overrides,
-		owner:            m.ModuleName(),
+		owner:            owner,
 	})
 
 	return fullInstallPath
@@ -799,6 +831,11 @@
 }
 
 func (m *moduleContext) SetOutputFiles(outputFiles Paths, tag string) {
+	for _, outputFile := range outputFiles {
+		if outputFile == nil {
+			panic("outputfiles cannot be nil")
+		}
+	}
 	if tag == "" {
 		if len(m.outputFiles.DefaultOutputFiles) > 0 {
 			m.ModuleErrorf("Module %s default OutputFiles cannot be overwritten", m.ModuleName())
diff --git a/android/module_info_json.go b/android/module_info_json.go
index ee552dc..d102dd2 100644
--- a/android/module_info_json.go
+++ b/android/module_info_json.go
@@ -6,6 +6,7 @@
 	"slices"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/gobtools"
 )
 
 type CoreModuleInfoJSON struct {
@@ -20,8 +21,7 @@
 	Required           []string `json:"required,omitempty"`            // $(sort $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET))
 }
 
-type ModuleInfoJSON struct {
-	core                CoreModuleInfoJSON
+type ExtraModuleInfoJSON struct {
 	SubName             string   `json:"-"`
 	Uninstallable       bool     `json:"-"`
 	Class               []string `json:"class,omitempty"`                 // $(sort $(ALL_MODULES.$(m).CLASS))
@@ -45,6 +45,11 @@
 	TestConfig          []string `json:"test_config,omitempty"`          // $(strip $(ALL_MODULES.$(m).TEST_CONFIG) $(ALL_MODULES.$(m).EXTRA_TEST_CONFIGS)
 }
 
+type ModuleInfoJSON struct {
+	core CoreModuleInfoJSON
+	ExtraModuleInfoJSON
+}
+
 //ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS := $(sort \
 //$(ALL_DEPS.$(LOCAL_MODULE).ALL_DEPS) \
 //$(LOCAL_STATIC_LIBRARIES) \
@@ -60,7 +65,7 @@
 
 type combinedModuleInfoJSON struct {
 	*CoreModuleInfoJSON
-	*ModuleInfoJSON
+	*ExtraModuleInfoJSON
 }
 
 func encodeModuleInfoJSON(w io.Writer, moduleInfoJSON *ModuleInfoJSON) error {
@@ -99,7 +104,27 @@
 	sortAndUnique(&moduleInfoJSONCopy.TestConfig)
 
 	encoder := json.NewEncoder(w)
-	return encoder.Encode(combinedModuleInfoJSON{&moduleInfoJSONCopy.core, &moduleInfoJSONCopy})
+	return encoder.Encode(combinedModuleInfoJSON{&moduleInfoJSONCopy.core, &moduleInfoJSONCopy.ExtraModuleInfoJSON})
+}
+
+func (p *ModuleInfoJSON) ToGob() *combinedModuleInfoJSON {
+	return &combinedModuleInfoJSON{
+		CoreModuleInfoJSON:  &p.core,
+		ExtraModuleInfoJSON: &p.ExtraModuleInfoJSON,
+	}
+}
+
+func (p *ModuleInfoJSON) FromGob(data *combinedModuleInfoJSON) {
+	p.core = *data.CoreModuleInfoJSON
+	p.ExtraModuleInfoJSON = *data.ExtraModuleInfoJSON
+}
+
+func (m *ModuleInfoJSON) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[combinedModuleInfoJSON](m)
+}
+
+func (m *ModuleInfoJSON) GobDecode(data []byte) error {
+	return gobtools.CustomGobDecode[combinedModuleInfoJSON](data, m)
 }
 
 var ModuleInfoJSONProvider = blueprint.NewProvider[*ModuleInfoJSON]()
diff --git a/android/module_proxy.go b/android/module_proxy.go
new file mode 100644
index 0000000..30459b9
--- /dev/null
+++ b/android/module_proxy.go
@@ -0,0 +1,233 @@
+package android
+
+import (
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+type ModuleProxy struct {
+	module blueprint.ModuleProxy
+}
+
+var _ Module = (*ModuleProxy)(nil)
+
+func (m ModuleProxy) Name() string {
+	return m.module.Name()
+}
+
+func (m ModuleProxy) GenerateBuildActions(context blueprint.ModuleContext) {
+	m.module.GenerateBuildActions(context)
+}
+
+func (m ModuleProxy) GenerateAndroidBuildActions(context ModuleContext) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ComponentDepsMutator(ctx BottomUpMutatorContext) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) DepsMutator(context BottomUpMutatorContext) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) base() *ModuleBase {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Disable() {
+
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Enabled(ctx ConfigurableEvaluatorContext) bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Target() Target {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) MultiTargets() []Target {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ImageVariation() blueprint.Variation {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Owner() string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInData() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInTestcases() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInSanitizerDir() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInRamdisk() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInVendorRamdisk() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInDebugRamdisk() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInRecovery() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInRoot() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInOdm() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInProduct() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInVendor() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInSystemExt() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInSystemDlkm() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInVendorDlkm() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInOdmDlkm() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallForceOS() (*OsType, *ArchType) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) PartitionTag(d DeviceConfig) string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) HideFromMake() {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) IsHideFromMake() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) SkipInstall() {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) IsSkipInstall() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) MakeUninstallable() {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ReplacedByPrebuilt() {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) IsReplacedByPrebuilt() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ExportedToMake() bool {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) EffectiveLicenseKinds() []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) EffectiveLicenseFiles() Paths {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) AddProperties(props ...interface{}) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) GetProperties() []interface{} {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) BuildParamsForTests() []BuildParams {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) VariablesForTests() map[string]string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) String() string {
+	return m.module.Name()
+}
+
+func (m ModuleProxy) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) visibilityProperties() []visibilityProperty {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) HostRequiredModuleNames() []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) TargetRequiredModuleNames() []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) DecodeMultilib(ctx ConfigContext) (string, string) {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) Overrides() []string {
+	panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) VintfFragments(ctx ConfigurableEvaluatorContext) []string {
+	panic("method is not implemented on ModuleProxy")
+}
diff --git a/android/module_test.go b/android/module_test.go
index d64e3a5..d5bf941 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -998,6 +998,10 @@
 	return OutputFilesInfo{}
 }
 
+func (p *pathContextAddMissingDependenciesWrapper) EqualModules(m1, m2 Module) bool {
+	return m1 == m2
+}
+
 func TestOutputFileForModule(t *testing.T) {
 	testcases := []struct {
 		name        string
@@ -1080,3 +1084,29 @@
 		})
 	}
 }
+
+func TestVintfFragmentModulesChecksPartition(t *testing.T) {
+	bp := `
+	vintf_fragment {
+		name: "vintfModA",
+		src: "test_vintf_file",
+		vendor: true,
+	}
+	deps {
+		name: "modA",
+		vintf_fragment_modules: [
+			"vintfModA",
+		]
+	}
+	`
+
+	testPreparer := GroupFixturePreparers(
+		PrepareForTestWithAndroidBuildComponents,
+		prepareForModuleTests,
+	)
+
+	testPreparer.
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(
+			"Module .+ and Vintf_fragment .+ are installed to different partitions.")).
+		RunTestWithBp(t, bp)
+}
diff --git a/android/mutator.go b/android/mutator.go
index 9404945..fdd16a8 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -26,17 +26,17 @@
 //   run Pre-deps mutators
 //   run depsMutator
 //   run PostDeps mutators
-//   run FinalDeps mutators (CreateVariations disallowed in this phase)
+//   run FinalDeps mutators (TransitionMutators disallowed in this phase)
 //   continue on to GenerateAndroidBuildActions
 
 // collateGloballyRegisteredMutators constructs the list of mutators that have been registered
 // with the InitRegistrationContext and will be used at runtime.
 func collateGloballyRegisteredMutators() sortableComponents {
-	return collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps)
+	return collateRegisteredMutators(preArch, preDeps, postDeps, postApex, finalDeps)
 }
 
 // collateRegisteredMutators constructs a single list of mutators from the separate lists.
-func collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) sortableComponents {
+func collateRegisteredMutators(preArch, preDeps, postDeps, postApex, finalDeps []RegisterMutatorFunc) sortableComponents {
 	mctx := &registerMutatorsContext{}
 
 	register := func(funcs []RegisterMutatorFunc) {
@@ -53,6 +53,8 @@
 
 	register(postDeps)
 
+	register(postApex)
+
 	mctx.finalPhase = true
 	register(finalDeps)
 
@@ -68,7 +70,7 @@
 	TopDown(name string, m TopDownMutator) MutatorHandle
 	BottomUp(name string, m BottomUpMutator) MutatorHandle
 	BottomUpBlueprint(name string, m blueprint.BottomUpMutator) MutatorHandle
-	Transition(name string, m TransitionMutator)
+	Transition(name string, m TransitionMutator) TransitionMutatorHandle
 }
 
 type RegisterMutatorFunc func(RegisterMutatorsContext)
@@ -149,6 +151,7 @@
 
 func registerArchMutator(ctx RegisterMutatorsContext) {
 	ctx.Transition("os", &osTransitionMutator{})
+	ctx.BottomUp("image_begin", imageMutatorBeginMutator)
 	ctx.Transition("image", &imageTransitionMutator{})
 	ctx.Transition("arch", &archTransitionMutator{})
 }
@@ -166,6 +169,8 @@
 	RegisterOverridePostDepsMutators,
 }
 
+var postApex = []RegisterMutatorFunc{}
+
 var finalDeps = []RegisterMutatorFunc{}
 
 func PreArchMutators(f RegisterMutatorFunc) {
@@ -180,29 +185,18 @@
 	postDeps = append(postDeps, f)
 }
 
-func FinalDepsMutators(f RegisterMutatorFunc) {
-	finalDeps = append(finalDeps, f)
+func PostApexMutators(f RegisterMutatorFunc) {
+	postApex = append(postApex, f)
 }
 
-type BaseMutatorContext interface {
-	BaseModuleContext
-
-	// MutatorName returns the name that this mutator was registered with.
-	MutatorName() string
-
-	// Rename all variants of a module.  The new name is not visible to calls to ModuleName,
-	// AddDependency or OtherModuleName until after this mutator pass is complete.
-	Rename(name string)
-
-	// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
-	// the specified property structs to it as if the properties were set in a blueprint file.
-	CreateModule(ModuleFactory, ...interface{}) Module
+func FinalDepsMutators(f RegisterMutatorFunc) {
+	finalDeps = append(finalDeps, f)
 }
 
 type TopDownMutator func(TopDownMutatorContext)
 
 type TopDownMutatorContext interface {
-	BaseMutatorContext
+	BaseModuleContext
 }
 
 type topDownMutatorContext struct {
@@ -213,65 +207,41 @@
 type BottomUpMutator func(BottomUpMutatorContext)
 
 type BottomUpMutatorContext interface {
-	BaseMutatorContext
+	BaseModuleContext
 
 	// AddDependency adds a dependency to the given module.  It returns a slice of modules for each
 	// dependency (some entries may be nil).
 	//
-	// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
-	// new dependencies have had the current mutator called on them.  If the mutator is not
-	// parallel this method does not affect the ordering of the current mutator pass, but will
-	// be ordered correctly for all future mutator passes.
+	// This method will pause until the new dependencies have had the current mutator called on them.
 	AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module
 
 	// AddReverseDependency adds a dependency from the destination to the given module.
 	// Does not affect the ordering of the current mutator pass, but will be ordered
 	// correctly for all future mutator passes.  All reverse dependencies for a destination module are
 	// collected until the end of the mutator pass, sorted by name, and then appended to the destination
-	// module's dependency list.
+	// module's dependency list.  May only  be called by mutators that were marked with
+	// UsesReverseDependencies during registration.
 	AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string)
 
-	// CreateVariations splits  a module into multiple variants, one for each name in the variationNames
-	// parameter.  It returns a list of new modules in the same order as the variationNames
-	// list.
-	//
-	// If any of the dependencies of the module being operated on were already split
-	// by calling CreateVariations with the same name, the dependency will automatically
-	// be updated to point the matching variant.
-	//
-	// If a module is split, and then a module depending on the first module is not split
-	// when the Mutator is later called on it, the dependency of the depending module will
-	// automatically be updated to point to the first variant.
-	CreateVariations(...string) []Module
-
-	// CreateLocationVariations splits a module into multiple variants, one for each name in the variantNames
-	// parameter.  It returns a list of new modules in the same order as the variantNames
-	// list.
-	//
-	// Local variations do not affect automatic dependency resolution - dependencies added
-	// to the split module via deps or DynamicDependerModule must exactly match a variant
-	// that contains all the non-local variations.
-	CreateLocalVariations(...string) []Module
-
-	// SetDependencyVariation sets all dangling dependencies on the current module to point to the variation
-	// with given name. This function ignores the default variation set by SetDefaultDependencyVariation.
-	SetDependencyVariation(string)
-
-	// SetDefaultDependencyVariation sets the default variation when a dangling reference is detected
-	// during the subsequent calls on Create*Variations* functions. To reset, set it to nil.
-	SetDefaultDependencyVariation(*string)
-
 	// AddVariationDependencies adds deps as dependencies of the current module, but uses the variations
 	// argument to select which variant of the dependency to use.  It returns a slice of modules for
 	// each dependency (some entries may be nil).  A variant of the dependency must exist that matches
 	// all the non-local variations of the current module, plus the variations argument.
 	//
-	// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
-	// new dependencies have had the current mutator called on them.  If the mutator is not
-	// parallel this method does not affect the ordering of the current mutator pass, but will
-	// be ordered correctly for all future mutator passes.
+	// This method will pause until the new dependencies have had the current mutator called on them.
 	AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []blueprint.Module
 
+	// AddReverseVariationDependency adds a dependency from the named module to the current
+	// module. The given variations will be added to the current module's varations, and then the
+	// result will be used to find the correct variation of the depending module, which must exist.
+	//
+	// Does not affect the ordering of the current mutator pass, but will be ordered
+	// correctly for all future mutator passes.  All reverse dependencies for a destination module are
+	// collected until the end of the mutator pass, sorted by name, and then appended to the destination
+	// module's dependency list.  May only  be called by mutators that were marked with
+	// UsesReverseDependencies during registration.
+	AddReverseVariationDependency([]blueprint.Variation, blueprint.DependencyTag, string)
+
 	// AddFarVariationDependencies adds deps as dependencies of the current module, but uses the
 	// variations argument to select which variant of the dependency to use.  It returns a slice of
 	// modules for each dependency (some entries may be nil).  A variant of the dependency must
@@ -281,52 +251,31 @@
 	// Unlike AddVariationDependencies, the variations of the current module are ignored - the
 	// dependency only needs to match the supplied variations.
 	//
-	// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
-	// new dependencies have had the current mutator called on them.  If the mutator is not
-	// parallel this method does not affect the ordering of the current mutator pass, but will
-	// be ordered correctly for all future mutator passes.
+	// This method will pause until the new dependencies have had the current mutator called on them.
 	AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string) []blueprint.Module
 
-	// AddInterVariantDependency adds a dependency between two variants of the same module.  Variants are always
-	// ordered in the same orderas they were listed in CreateVariations, and AddInterVariantDependency does not change
-	// that ordering, but it associates a DependencyTag with the dependency and makes it visible to VisitDirectDeps,
-	// WalkDeps, etc.
-	AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module)
-
 	// ReplaceDependencies finds all the variants of the module with the specified name, then
 	// replaces all dependencies onto those variants with the current variant of this module.
-	// Replacements don't take effect until after the mutator pass is finished.
+	// Replacements don't take effect until after the mutator pass is finished.  May only
+	// be called by mutators that were marked with UsesReplaceDependencies during registration.
 	ReplaceDependencies(string)
 
 	// ReplaceDependenciesIf finds all the variants of the module with the specified name, then
 	// replaces all dependencies onto those variants with the current variant of this module
 	// as long as the supplied predicate returns true.
-	// Replacements don't take effect until after the mutator pass is finished.
+	// Replacements don't take effect until after the mutator pass is finished.  May only
+	// be called by mutators that were marked with UsesReplaceDependencies during registration.
 	ReplaceDependenciesIf(string, blueprint.ReplaceDependencyPredicate)
 
-	// AliasVariation takes a variationName that was passed to CreateVariations for this module,
-	// and creates an alias from the current variant (before the mutator has run) to the new
-	// variant.  The alias will be valid until the next time a mutator calls CreateVariations or
-	// CreateLocalVariations on this module without also calling AliasVariation.  The alias can
-	// be used to add dependencies on the newly created variant using the variant map from
-	// before CreateVariations was run.
-	AliasVariation(variationName string)
+	// Rename all variants of a module.  The new name is not visible to calls to ModuleName,
+	// AddDependency or OtherModuleName until after this mutator pass is complete.  May only be called
+	// by mutators that were marked with UsesRename during registration.
+	Rename(name string)
 
-	// CreateAliasVariation takes a toVariationName that was passed to CreateVariations for this
-	// module, and creates an alias from a new fromVariationName variant the toVariationName
-	// variant.  The alias will be valid until the next time a mutator calls CreateVariations or
-	// CreateLocalVariations on this module without also calling AliasVariation.  The alias can
-	// be used to add dependencies on the toVariationName variant using the fromVariationName
-	// variant.
-	CreateAliasVariation(fromVariationName, toVariationName string)
-
-	// SetVariationProvider sets the value for a provider for the given newly created variant of
-	// the current module, i.e. one of the Modules returned by CreateVariations..  It panics if
-	// not called during the appropriate mutator or GenerateBuildActions pass for the provider,
-	// if the value is not of the appropriate type, or if the module is not a newly created
-	// variant of the current module.  The value should not be modified after being passed to
-	// SetVariationProvider.
-	SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{})
+	// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
+	// the specified property structs to it as if the properties were set in a blueprint file.  May only
+	// be called by mutators that were marked with UsesCreateModule during registration.
+	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
 // An outgoingTransitionContextImpl and incomingTransitionContextImpl is created for every dependency of every module
@@ -391,6 +340,7 @@
 type IncomingTransitionContext interface {
 	ArchModuleContext
 	ModuleProviderContext
+	ModuleErrorContext
 
 	// Module returns the target of the dependency edge for which the transition
 	// is being computed
@@ -591,6 +541,14 @@
 	return c.bp.Provider(provider)
 }
 
+func (c *incomingTransitionContextImpl) ModuleErrorf(fmt string, args ...interface{}) {
+	c.bp.ModuleErrorf(fmt, args)
+}
+
+func (c *incomingTransitionContextImpl) PropertyErrorf(property, fmt string, args ...interface{}) {
+	c.bp.PropertyErrorf(property, fmt, args)
+}
+
 func (a *androidTransitionMutator) IncomingTransition(bpctx blueprint.IncomingTransitionContext, incomingVariation string) string {
 	if m, ok := bpctx.Module().(Module); ok {
 		ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl)
@@ -621,7 +579,7 @@
 	}
 }
 
-func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) {
+func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) TransitionMutatorHandle {
 	atm := &androidTransitionMutator{
 		finalPhase: x.finalPhase,
 		mutator:    m,
@@ -629,8 +587,10 @@
 	}
 	mutator := &mutator{
 		name:              name,
-		transitionMutator: atm}
+		transitionMutator: atm,
+	}
 	x.mutators = append(x.mutators, mutator)
+	return mutator
 }
 
 func (x *registerMutatorsContext) mutatorName(name string) string {
@@ -667,24 +627,114 @@
 	} else if mutator.topDownMutator != nil {
 		handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator)
 	} else if mutator.transitionMutator != nil {
-		blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
+		handle := blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
+		if mutator.neverFar {
+			handle.NeverFar()
+		}
 	}
-	if mutator.parallel {
-		handle.Parallel()
+
+	// Forward booleans set on the MutatorHandle to the blueprint.MutatorHandle.
+	if mutator.usesRename {
+		handle.UsesRename()
+	}
+	if mutator.usesReverseDependencies {
+		handle.UsesReverseDependencies()
+	}
+	if mutator.usesReplaceDependencies {
+		handle.UsesReplaceDependencies()
+	}
+	if mutator.usesCreateModule {
+		handle.UsesCreateModule()
+	}
+	if mutator.mutatesDependencies {
+		handle.MutatesDependencies()
+	}
+	if mutator.mutatesGlobalState {
+		handle.MutatesGlobalState()
 	}
 }
 
 type MutatorHandle interface {
+	// Parallel sets the mutator to visit modules in parallel while maintaining ordering.  Calling any
+	// method on the mutator context is thread-safe, but the mutator must handle synchronization
+	// for any modifications to global state or any modules outside the one it was invoked on.
+	// Deprecated: all Mutators are parallel by default.
 	Parallel() MutatorHandle
+
+	// UsesRename marks the mutator as using the BottomUpMutatorContext.Rename method, which prevents
+	// coalescing adjacent mutators into a single mutator pass.
+	UsesRename() MutatorHandle
+
+	// UsesReverseDependencies marks the mutator as using the BottomUpMutatorContext.AddReverseDependency
+	// method, which prevents coalescing adjacent mutators into a single mutator pass.
+	UsesReverseDependencies() MutatorHandle
+
+	// UsesReplaceDependencies marks the mutator as using the BottomUpMutatorContext.ReplaceDependencies
+	// method, which prevents coalescing adjacent mutators into a single mutator pass.
+	UsesReplaceDependencies() MutatorHandle
+
+	// UsesCreateModule marks the mutator as using the BottomUpMutatorContext.CreateModule method,
+	// which prevents coalescing adjacent mutators into a single mutator pass.
+	UsesCreateModule() MutatorHandle
+
+	// MutatesDependencies marks the mutator as modifying properties in dependencies, which prevents
+	// coalescing adjacent mutators into a single mutator pass.
+	MutatesDependencies() MutatorHandle
+
+	// MutatesGlobalState marks the mutator as modifying global state, which prevents coalescing
+	// adjacent mutators into a single mutator pass.
+	MutatesGlobalState() MutatorHandle
+}
+
+type TransitionMutatorHandle interface {
+	// NeverFar causes the variations created by this mutator to never be ignored when adding
+	// far variation dependencies. Normally, far variation dependencies ignore all the variants
+	// of the source module, and only use the variants explicitly requested by the
+	// AddFarVariationDependencies call.
+	NeverFar() MutatorHandle
 }
 
 func (mutator *mutator) Parallel() MutatorHandle {
-	mutator.parallel = true
+	return mutator
+}
+
+func (mutator *mutator) UsesRename() MutatorHandle {
+	mutator.usesRename = true
+	return mutator
+}
+
+func (mutator *mutator) UsesReverseDependencies() MutatorHandle {
+	mutator.usesReverseDependencies = true
+	return mutator
+}
+
+func (mutator *mutator) UsesReplaceDependencies() MutatorHandle {
+	mutator.usesReplaceDependencies = true
+	return mutator
+}
+
+func (mutator *mutator) UsesCreateModule() MutatorHandle {
+	mutator.usesCreateModule = true
+	return mutator
+}
+
+func (mutator *mutator) MutatesDependencies() MutatorHandle {
+	mutator.mutatesDependencies = true
+	return mutator
+}
+
+func (mutator *mutator) MutatesGlobalState() MutatorHandle {
+	mutator.mutatesGlobalState = true
+	return mutator
+}
+
+func (mutator *mutator) NeverFar() MutatorHandle {
+	mutator.neverFar = true
 	return mutator
 }
 
 func RegisterComponentsMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("component-deps", componentDepsMutator).Parallel()
+	ctx.BottomUp("component-deps", componentDepsMutator)
 }
 
 // A special mutator that runs just prior to the deps mutator to allow the dependencies
@@ -702,7 +752,7 @@
 }
 
 func registerDepsMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("deps", depsMutator).Parallel()
+	ctx.BottomUp("deps", depsMutator).UsesReverseDependencies()
 }
 
 // android.topDownMutatorContext either has to embed blueprint.TopDownMutatorContext, in which case every method that
@@ -711,32 +761,6 @@
 // non-overridden method has to be forwarded.  There are fewer non-overridden methods, so use the latter.  The following
 // methods forward to the identical blueprint versions for topDownMutatorContext and bottomUpMutatorContext.
 
-func (t *topDownMutatorContext) MutatorName() string {
-	return t.bp.MutatorName()
-}
-
-func (t *topDownMutatorContext) Rename(name string) {
-	t.bp.Rename(name)
-	t.Module().base().commonProperties.DebugName = name
-}
-
-func (t *topDownMutatorContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module {
-	return t.bp.CreateModule(factory, name, props...)
-}
-
-func (t *topDownMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
-	return createModule(t, factory, "_topDownMutatorModule", props...)
-}
-
-func (t *topDownMutatorContext) createModuleWithoutInheritance(factory ModuleFactory, props ...interface{}) Module {
-	module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), "", props...).(Module)
-	return module
-}
-
-func (b *bottomUpMutatorContext) MutatorName() string {
-	return b.bp.MutatorName()
-}
-
 func (b *bottomUpMutatorContext) Rename(name string) {
 	b.bp.Rename(name)
 	b.Module().base().commonProperties.DebugName = name
@@ -746,8 +770,12 @@
 	return b.bp.CreateModule(factory, name, props...)
 }
 
+func (b *bottomUpMutatorContext) createModuleInDirectory(factory blueprint.ModuleFactory, name string, _ string, props ...interface{}) blueprint.Module {
+	panic("createModuleInDirectory is not implemented for bottomUpMutatorContext")
+}
+
 func (b *bottomUpMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
-	return createModule(b, factory, "_bottomUpMutatorModule", props...)
+	return createModule(b, factory, "_bottomUpMutatorModule", doesNotSpecifyDirectory(), props...)
 }
 
 func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module {
@@ -764,48 +792,11 @@
 	b.bp.AddReverseDependency(module, tag, name)
 }
 
-func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []Module {
-	if b.finalPhase {
-		panic("CreateVariations not allowed in FinalDepsMutators")
+func (b *bottomUpMutatorContext) AddReverseVariationDependency(variations []blueprint.Variation, tag blueprint.DependencyTag, name string) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
 	}
-
-	modules := b.bp.CreateVariations(variations...)
-
-	aModules := make([]Module, len(modules))
-	for i := range variations {
-		aModules[i] = modules[i].(Module)
-		base := aModules[i].base()
-		base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName())
-		base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i])
-	}
-
-	return aModules
-}
-
-func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []Module {
-	if b.finalPhase {
-		panic("CreateLocalVariations not allowed in FinalDepsMutators")
-	}
-
-	modules := b.bp.CreateLocalVariations(variations...)
-
-	aModules := make([]Module, len(modules))
-	for i := range variations {
-		aModules[i] = modules[i].(Module)
-		base := aModules[i].base()
-		base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName())
-		base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i])
-	}
-
-	return aModules
-}
-
-func (b *bottomUpMutatorContext) SetDependencyVariation(variation string) {
-	b.bp.SetDependencyVariation(variation)
-}
-
-func (b *bottomUpMutatorContext) SetDefaultDependencyVariation(variation *string) {
-	b.bp.SetDefaultDependencyVariation(variation)
+	b.bp.AddReverseVariationDependency(variations, tag, name)
 }
 
 func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
@@ -825,10 +816,6 @@
 	return b.bp.AddFarVariationDependencies(variations, tag, names...)
 }
 
-func (b *bottomUpMutatorContext) AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module) {
-	b.bp.AddInterVariantDependency(tag, from, to)
-}
-
 func (b *bottomUpMutatorContext) ReplaceDependencies(name string) {
 	if b.baseModuleContext.checkedMissingDeps() {
 		panic("Adding deps not allowed after checking for missing deps")
@@ -842,15 +829,3 @@
 	}
 	b.bp.ReplaceDependenciesIf(name, predicate)
 }
-
-func (b *bottomUpMutatorContext) AliasVariation(variationName string) {
-	b.bp.AliasVariation(variationName)
-}
-
-func (b *bottomUpMutatorContext) CreateAliasVariation(fromVariationName, toVariationName string) {
-	b.bp.CreateAliasVariation(fromVariationName, toVariationName)
-}
-
-func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{}) {
-	b.bp.SetVariationProvider(module, provider, value)
-}
diff --git a/android/mutator_test.go b/android/mutator_test.go
index b3ef00f..1d5f890 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -17,6 +17,8 @@
 import (
 	"fmt"
 	"strings"
+	"sync"
+	"sync/atomic"
 	"testing"
 
 	"github.com/google/blueprint"
@@ -134,10 +136,6 @@
 						return []string{"a", "b"}
 					},
 				})
-				ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) {
-					moduleStrings = append(moduleStrings, ctx.Module().String())
-					ctx.Rename(ctx.Module().base().Name() + "_renamed1")
-				})
 			})
 
 			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
@@ -161,8 +159,8 @@
 				})
 				ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) {
 					moduleStrings = append(moduleStrings, ctx.Module().String())
-					ctx.Rename(ctx.Module().base().Name() + "_renamed2")
-				})
+					ctx.Rename(ctx.Module().base().Name() + "_renamed1")
+				}).UsesRename()
 				ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
 					moduleStrings = append(moduleStrings, ctx.Module().String())
 				})
@@ -181,17 +179,23 @@
 		"foo{pre_arch:b}",
 		"foo{pre_arch:a}",
 
-		// After rename_top_down (reversed because pre_deps TransitionMutator.Split is TopDown).
-		"foo_renamed1{pre_arch:b}",
-		"foo_renamed1{pre_arch:a}",
-
 		// After pre_deps (reversed because post_deps TransitionMutator.Split is TopDown).
-		"foo_renamed1{pre_arch:b,pre_deps:d}",
-		"foo_renamed1{pre_arch:b,pre_deps:c}",
-		"foo_renamed1{pre_arch:a,pre_deps:d}",
-		"foo_renamed1{pre_arch:a,pre_deps:c}",
+		"foo{pre_arch:b,pre_deps:d}",
+		"foo{pre_arch:b,pre_deps:c}",
+		"foo{pre_arch:a,pre_deps:d}",
+		"foo{pre_arch:a,pre_deps:c}",
 
 		// After post_deps.
+		"foo{pre_arch:a,pre_deps:c,post_deps:e}",
+		"foo{pre_arch:a,pre_deps:c,post_deps:f}",
+		"foo{pre_arch:a,pre_deps:d,post_deps:e}",
+		"foo{pre_arch:a,pre_deps:d,post_deps:f}",
+		"foo{pre_arch:b,pre_deps:c,post_deps:e}",
+		"foo{pre_arch:b,pre_deps:c,post_deps:f}",
+		"foo{pre_arch:b,pre_deps:d,post_deps:e}",
+		"foo{pre_arch:b,pre_deps:d,post_deps:f}",
+
+		// After rename_bottom_up.
 		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}",
 		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:f}",
 		"foo_renamed1{pre_arch:a,pre_deps:d,post_deps:e}",
@@ -200,16 +204,6 @@
 		"foo_renamed1{pre_arch:b,pre_deps:c,post_deps:f}",
 		"foo_renamed1{pre_arch:b,pre_deps:d,post_deps:e}",
 		"foo_renamed1{pre_arch:b,pre_deps:d,post_deps:f}",
-
-		// After rename_bottom_up.
-		"foo_renamed2{pre_arch:a,pre_deps:c,post_deps:e}",
-		"foo_renamed2{pre_arch:a,pre_deps:c,post_deps:f}",
-		"foo_renamed2{pre_arch:a,pre_deps:d,post_deps:e}",
-		"foo_renamed2{pre_arch:a,pre_deps:d,post_deps:f}",
-		"foo_renamed2{pre_arch:b,pre_deps:c,post_deps:e}",
-		"foo_renamed2{pre_arch:b,pre_deps:c,post_deps:f}",
-		"foo_renamed2{pre_arch:b,pre_deps:d,post_deps:e}",
-		"foo_renamed2{pre_arch:b,pre_deps:d,post_deps:f}",
 	}
 
 	AssertDeepEquals(t, "module String() values", want, moduleStrings)
@@ -228,7 +222,7 @@
 		}
 	`
 
-	finalGot := map[string]int{}
+	finalGot := sync.Map{}
 
 	GroupFixturePreparers(
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
@@ -259,9 +253,11 @@
 					}
 				})
 				ctx.BottomUp("final", func(ctx BottomUpMutatorContext) {
-					finalGot[ctx.Module().String()] += 1
+					counter, _ := finalGot.LoadOrStore(ctx.Module().String(), &atomic.Int64{})
+					counter.(*atomic.Int64).Add(1)
 					ctx.VisitDirectDeps(func(mod Module) {
-						finalGot[fmt.Sprintf("%s -> %s", ctx.Module().String(), mod)] += 1
+						counter, _ := finalGot.LoadOrStore(fmt.Sprintf("%s -> %s", ctx.Module().String(), mod), &atomic.Int64{})
+						counter.(*atomic.Int64).Add(1)
 					})
 				})
 			})
@@ -284,10 +280,16 @@
 		"foo{variant:b} -> common_dep_2{variant:a}": 1,
 	}
 
-	AssertDeepEquals(t, "final", finalWant, finalGot)
+	finalGotMap := make(map[string]int)
+	finalGot.Range(func(k, v any) bool {
+		finalGotMap[k.(string)] = int(v.(*atomic.Int64).Load())
+		return true
+	})
+
+	AssertDeepEquals(t, "final", finalWant, finalGotMap)
 }
 
-func TestNoCreateVariationsInFinalDeps(t *testing.T) {
+func TestTransitionMutatorInFinalDeps(t *testing.T) {
 	GroupFixturePreparers(
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
diff --git a/android/namespace.go b/android/namespace.go
index 3b9ae3a..c3ee20f 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -460,7 +460,7 @@
 }
 
 func RegisterNamespaceMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("namespace_deps", namespaceMutator).Parallel()
+	ctx.BottomUp("namespace_deps", namespaceMutator).MutatesGlobalState()
 }
 
 func namespaceMutator(ctx BottomUpMutatorContext) {
diff --git a/android/namespace_test.go b/android/namespace_test.go
index ea51c6e..0327e78 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -646,7 +646,7 @@
 		ctx.RegisterModuleType("test_module", newTestModule)
 		ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
 		ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-			ctx.BottomUp("rename", renameMutator)
+			ctx.BottomUp("rename", renameMutator).UsesRename()
 		})
 	}),
 )
@@ -709,9 +709,6 @@
 }
 
 func (m *testModule) DepsMutator(ctx BottomUpMutatorContext) {
-	if m.properties.Rename != "" {
-		ctx.Rename(m.properties.Rename)
-	}
 	for _, d := range m.properties.Deps {
 		ctx.AddDependency(ctx.Module(), nil, d)
 	}
diff --git a/android/neverallow.go b/android/neverallow.go
index b89d150..fdcbe1c 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -44,7 +44,7 @@
 // - it has none of the "Without" properties matched (same rules as above)
 
 func registerNeverallowMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("neverallow", neverallowMutator).Parallel()
+	ctx.BottomUp("neverallow", neverallowMutator)
 }
 
 var neverallows = []Rule{}
@@ -55,12 +55,16 @@
 	AddNeverAllowRules(createJavaDeviceForHostRules()...)
 	AddNeverAllowRules(createCcSdkVariantRules()...)
 	AddNeverAllowRules(createUncompressDexRules()...)
-	AddNeverAllowRules(createInitFirstStageRules()...)
+	AddNeverAllowRules(createInstallInRootAllowingRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
 	AddNeverAllowRules(createCcStubsRule())
-	AddNeverAllowRules(createJavaExcludeStaticLibsRule())
 	AddNeverAllowRules(createProhibitHeaderOnlyRule())
 	AddNeverAllowRules(createLimitNdkExportRule()...)
+	AddNeverAllowRules(createLimitDirgroupRule()...)
+	AddNeverAllowRules(createFilesystemIsAutoGeneratedRule())
+	AddNeverAllowRules(createKotlinPluginRule()...)
+	AddNeverAllowRules(createPrebuiltEtcBpDefineRule())
+	AddNeverAllowRules(createAutogenRroBpDefineRule())
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -232,15 +236,21 @@
 	}
 }
 
-func createInitFirstStageRules() []Rule {
+func createInstallInRootAllowingRules() []Rule {
 	return []Rule{
 		NeverAllow().
 			Without("name", "init_first_stage_defaults").
 			Without("name", "init_first_stage").
 			Without("name", "init_first_stage.microdroid").
+			Without("name", "librecovery_ui_ext").
 			With("install_in_root", "true").
 			NotModuleType("prebuilt_root").
-			Because("install_in_root is only for init_first_stage."),
+			NotModuleType("prebuilt_vendor").
+			NotModuleType("prebuilt_sbin").
+			NotModuleType("prebuilt_system").
+			NotModuleType("prebuilt_first_stage_ramdisk").
+			NotModuleType("prebuilt_res").
+			Because("install_in_root is only for init_first_stage or librecovery_ui_ext."),
 	}
 }
 
@@ -253,14 +263,6 @@
 	}
 }
 
-func createJavaExcludeStaticLibsRule() Rule {
-	return NeverAllow().
-		NotIn("build/soong", "libcore", "frameworks/base/api").
-		ModuleType("java_library").
-		WithMatcher("exclude_static_libs", isSetMatcherInstance).
-		Because("exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api")
-}
-
 func createProhibitHeaderOnlyRule() Rule {
 	return NeverAllow().
 		Without("name", "framework-minus-apex-headers").
@@ -284,6 +286,90 @@
 	}
 }
 
+func createLimitDirgroupRule() []Rule {
+	reason := "dirgroup module and dir_srcs / keep_gendir property of genrule is allowed only to Trusty build rule."
+	return []Rule{
+		NeverAllow().
+			ModuleType("dirgroup").
+			WithMatcher("visibility", NotInList([]string{"//trusty/vendor/google/aosp/scripts"})).Because(reason),
+		NeverAllow().
+			ModuleType("dirgroup").
+			Without("visibility", "//trusty/vendor/google/aosp/scripts").Because(reason),
+		NeverAllow().
+			ModuleType("genrule").
+			Without("name", "trusty-arm64.lk.elf.gen").
+			Without("name", "trusty-arm64-virt-test-debug.lk.elf.gen").
+			Without("name", "trusty-x86_64.lk.elf.gen").
+			Without("name", "trusty-x86_64-test.lk.elf.gen").
+			WithMatcher("dir_srcs", isSetMatcherInstance).Because(reason),
+		NeverAllow().
+			ModuleType("genrule").
+			Without("name", "trusty-arm64.lk.elf.gen").
+			Without("name", "trusty-arm64-virt-test-debug.lk.elf.gen").
+			Without("name", "trusty-x86_64.lk.elf.gen").
+			Without("name", "trusty-x86_64-test.lk.elf.gen").
+			With("keep_gendir", "true").Because(reason),
+	}
+}
+
+func createFilesystemIsAutoGeneratedRule() Rule {
+	return NeverAllow().
+		NotIn("build/soong/fsgen").
+		ModuleType("filesystem", "android_system_image").
+		WithMatcher("is_auto_generated", isSetMatcherInstance).
+		Because("is_auto_generated property is only allowed for filesystem modules in build/soong/fsgen directory")
+}
+
+func createKotlinPluginRule() []Rule {
+	kotlinPluginProjectsAllowedList := []string{
+		// TODO: Migrate compose plugin to the bundled compiler plugin
+		// Actual path prebuilts/sdk/current/androidx/m2repository/androidx/compose/compiler/compiler-hosted
+		"prebuilts/sdk/current/androidx",
+		"external/kotlinc",
+	}
+
+	return []Rule{
+		NeverAllow().
+			NotIn(kotlinPluginProjectsAllowedList...).
+			ModuleType("kotlin_plugin").
+			Because("kotlin_plugin can only be used in allowed projects"),
+	}
+}
+
+// These module types are introduced to convert PRODUCT_COPY_FILES to Soong,
+// and is only intended to be used by filesystem_creator.
+func createPrebuiltEtcBpDefineRule() Rule {
+	return NeverAllow().
+		ModuleType(
+			"prebuilt_usr_srec",
+			"prebuilt_priv_app",
+			"prebuilt_rfs",
+			"prebuilt_framework",
+			"prebuilt_wlc_upt",
+			"prebuilt_odm",
+			"prebuilt_vendor_dlkm",
+			"prebuilt_bt_firmware",
+			"prebuilt_tvservice",
+			"prebuilt_optee",
+			"prebuilt_tvconfig",
+			"prebuilt_vendor",
+			"prebuilt_sbin",
+			"prebuilt_system",
+			"prebuilt_first_stage_ramdisk",
+		).
+		DefinedInBpFile().
+		Because("module type not allowed to be defined in bp file")
+}
+
+func createAutogenRroBpDefineRule() Rule {
+	return NeverAllow().
+		ModuleType(
+			"autogen_runtime_resource_overlay",
+		).
+		DefinedInBpFile().
+		Because("Module type will be autogenerated by soong. Use runtime_resource_overlay instead")
+}
+
 func neverallowMutator(ctx BottomUpMutatorContext) {
 	m, ok := ctx.Module().(Module)
 	if !ok {
@@ -317,6 +403,10 @@
 			continue
 		}
 
+		if !n.appliesToBpDefinedModule(ctx) {
+			continue
+		}
+
 		ctx.ModuleErrorf("violates " + n.String())
 	}
 }
@@ -440,6 +530,8 @@
 
 	WithoutMatcher(properties string, matcher ValueMatcher) Rule
 
+	DefinedInBpFile() Rule
+
 	Because(reason string) Rule
 }
 
@@ -461,6 +553,8 @@
 	unlessProps ruleProperties
 
 	onlyBootclasspathJar bool
+
+	definedInBp bool
 }
 
 // Create a new NeverAllow rule.
@@ -534,6 +628,13 @@
 	return r
 }
 
+// DefinedInBpFile specifies that this rule applies to modules that are defined
+// in bp files, and does not apply to modules that are auto generated by other modules.
+func (r *rule) DefinedInBpFile() Rule {
+	r.definedInBp = true
+	return r
+}
+
 func selectMatcher(expected string) ValueMatcher {
 	if expected == "*" {
 		return anyMatcherInstance
@@ -619,6 +720,9 @@
 }
 
 func (r *rule) appliesToModuleType(moduleType string) bool {
+	// Remove prefix for auto-generated modules
+	moduleType = strings.TrimSuffix(moduleType, "__loadHookModule")
+	moduleType = strings.TrimSuffix(moduleType, "__bottomUpMutatorModule")
 	return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
 }
 
@@ -628,6 +732,13 @@
 	return includeProps && !excludeProps
 }
 
+func (r *rule) appliesToBpDefinedModule(ctx BottomUpMutatorContext) bool {
+	if !r.definedInBp {
+		return true
+	}
+	return !ctx.OtherModuleIsAutoGenerated(ctx.Module()) == r.definedInBp
+}
+
 func StartsWith(prefix string) ValueMatcher {
 	return &startsWithMatcher{prefix}
 }
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index b2620ef..c74d5ff 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -344,23 +344,6 @@
 			`module "outside_allowed_list": violates neverallow`,
 		},
 	},
-	// Test for the rule restricting use of exclude_static_libs
-	{
-		name: `"exclude_static_libs" outside allowed directory`,
-		fs: map[string][]byte{
-			"a/b/Android.bp": []byte(`
-				java_library {
-					name: "baz",
-					exclude_static_libs: [
-						"bar",
-					],
-				}
-			`),
-		},
-		expectedErrors: []string{
-			`exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api`,
-		},
-	},
 	// Test for only allowing headers_only for framework-minus-apex-headers
 	{
 		name: `"headers_only" outside framework-minus-apex-headers modules`,
@@ -376,6 +359,35 @@
 			`headers_only can only be used for generating framework-minus-apex headers for non-updatable modules`,
 		},
 	},
+	// Test for the rule restricting use of is_auto_generated
+	{
+		name: `"is_auto_generated" outside allowed directory`,
+		fs: map[string][]byte{
+			"a/b/Android.bp": []byte(`
+				filesystem {
+					name: "baaz",
+					is_auto_generated: true,
+				}
+			`),
+		},
+		expectedErrors: []string{
+			`is_auto_generated property is only allowed for filesystem modules in build/soong/fsgen directory`,
+		},
+	},
+	// Test for the rule restricting use of prebuilt_* module
+	{
+		name: `"prebuilt_usr_srec" defined in Android.bp file`,
+		fs: map[string][]byte{
+			"a/b/Android.bp": []byte(`
+				prebuilt_usr_srec {
+					name: "foo",
+				}
+			`),
+		},
+		expectedErrors: []string{
+			`module type not allowed to be defined in bp file`,
+		},
+	},
 }
 
 var prepareForNeverAllowTest = GroupFixturePreparers(
@@ -384,6 +396,8 @@
 		ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
 		ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
 		ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
+		ctx.RegisterModuleType("filesystem", newMockFilesystemModule)
+		ctx.RegisterModuleType("prebuilt_usr_srec", newMockPrebuiltUsrSrecModule)
 	}),
 )
 
@@ -483,3 +497,16 @@
 
 func (p *mockJavaLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
 }
+
+type mockPrebuiltUsrSrecModule struct {
+	ModuleBase
+}
+
+func (p *mockPrebuiltUsrSrecModule) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+func newMockPrebuiltUsrSrecModule() Module {
+	m := &mockPrebuiltUsrSrecModule{}
+	InitAndroidModule(m)
+	return m
+}
diff --git a/android/override_module.go b/android/override_module.go
index f69f963..50ddc9b 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -234,17 +234,18 @@
 // Mutators for override/overridable modules. All the fun happens in these functions. It is critical
 // 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("override_deps", overrideModuleDepsMutator).MutatesDependencies() // modifies deps via addOverride
 	ctx.Transition("override", &overrideTransitionMutator{})
+	ctx.BottomUp("override_apply", overrideApplyMutator).MutatesDependencies()
 	// overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
 	// add deps from overridable properties.
-	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel()
+	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator)
 	// Because overridableModuleDepsMutator is run after PrebuiltPostDepsMutator,
 	// prebuilt's ReplaceDependencies doesn't affect to those deps added by overridable properties.
 	// By running PrebuiltPostDepsMutator again after overridableModuleDepsMutator, deps via overridable properties
 	// can be replaced with prebuilts.
-	ctx.BottomUp("replace_deps_on_prebuilts_for_overridable_deps_again", PrebuiltPostDepsMutator).Parallel()
-	ctx.BottomUp("replace_deps_on_override", replaceDepsOnOverridingModuleMutator).Parallel()
+	ctx.BottomUp("replace_deps_on_prebuilts_for_overridable_deps_again", PrebuiltPostDepsMutator).UsesReplaceDependencies()
+	ctx.BottomUp("replace_deps_on_override", replaceDepsOnOverridingModuleMutator).UsesReplaceDependencies()
 }
 
 type overrideBaseDependencyTag struct {
@@ -330,6 +331,9 @@
 }
 
 func (overrideTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+}
+
+func overrideApplyMutator(ctx BottomUpMutatorContext) {
 	if o, ok := ctx.Module().(OverrideModule); ok {
 		overridableDeps := ctx.GetDirectDepsWithTag(overrideBaseDepTag)
 		if len(overridableDeps) > 1 {
diff --git a/android/packaging.go b/android/packaging.go
index 0909936..d96cccd 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -15,15 +15,12 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
 	"path/filepath"
 	"sort"
-	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/gobtools"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -67,34 +64,56 @@
 	owner string
 }
 
-func (p *PackagingSpec) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.relPathInPackage), encoder.Encode(p.srcPath),
-		encoder.Encode(p.symlinkTarget), encoder.Encode(p.executable),
-		encoder.Encode(p.effectiveLicenseFiles), encoder.Encode(p.partition),
-		encoder.Encode(p.skipInstall), encoder.Encode(p.aconfigPaths),
-		encoder.Encode(p.archType))
-	if err != nil {
-		return nil, err
-	}
+type packagingSpecGob struct {
+	RelPathInPackage      string
+	SrcPath               Path
+	SymlinkTarget         string
+	Executable            bool
+	EffectiveLicenseFiles *Paths
+	Partition             string
+	SkipInstall           bool
+	AconfigPaths          *Paths
+	ArchType              ArchType
+	Overrides             *[]string
+	Owner                 string
+}
 
-	return w.Bytes(), nil
+func (p *PackagingSpec) ToGob() *packagingSpecGob {
+	return &packagingSpecGob{
+		RelPathInPackage:      p.relPathInPackage,
+		SrcPath:               p.srcPath,
+		SymlinkTarget:         p.symlinkTarget,
+		Executable:            p.executable,
+		EffectiveLicenseFiles: p.effectiveLicenseFiles,
+		Partition:             p.partition,
+		SkipInstall:           p.skipInstall,
+		AconfigPaths:          p.aconfigPaths,
+		ArchType:              p.archType,
+		Overrides:             p.overrides,
+		Owner:                 p.owner,
+	}
+}
+
+func (p *PackagingSpec) FromGob(data *packagingSpecGob) {
+	p.relPathInPackage = data.RelPathInPackage
+	p.srcPath = data.SrcPath
+	p.symlinkTarget = data.SymlinkTarget
+	p.executable = data.Executable
+	p.effectiveLicenseFiles = data.EffectiveLicenseFiles
+	p.partition = data.Partition
+	p.skipInstall = data.SkipInstall
+	p.aconfigPaths = data.AconfigPaths
+	p.archType = data.ArchType
+	p.overrides = data.Overrides
+	p.owner = data.Owner
+}
+
+func (p *PackagingSpec) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[packagingSpecGob](p)
 }
 
 func (p *PackagingSpec) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.relPathInPackage), decoder.Decode(&p.srcPath),
-		decoder.Decode(&p.symlinkTarget), decoder.Decode(&p.executable),
-		decoder.Decode(&p.effectiveLicenseFiles), decoder.Decode(&p.partition),
-		decoder.Decode(&p.skipInstall), decoder.Decode(&p.aconfigPaths),
-		decoder.Decode(&p.archType))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return gobtools.CustomGobDecode[packagingSpecGob](data, p)
 }
 
 func (p *PackagingSpec) Equals(other *PackagingSpec) bool {
@@ -145,6 +164,10 @@
 	return p.partition
 }
 
+func (p *PackagingSpec) SetPartition(partition string) {
+	p.partition = partition
+}
+
 func (p *PackagingSpec) SkipInstall() bool {
 	return p.skipInstall
 }
@@ -166,6 +189,7 @@
 	// GatherPackagingSpecs gathers PackagingSpecs of transitive dependencies.
 	GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec
 	GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec
+	GatherPackagingSpecsWithFilterAndModifier(ctx ModuleContext, filter func(PackagingSpec) bool, modifier func(*PackagingSpec)) map[string]PackagingSpec
 
 	// CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
 	// returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
@@ -187,38 +211,49 @@
 	// 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
+
+	// If this is set to try by a module type inheriting PackagingBase, the module type is
+	// allowed to utilize High_priority_deps.
+	AllowHighPriorityDeps bool
 }
 
-type depsProperty struct {
+type DepsProperty struct {
+	// Deps that have higher priority in packaging when there is a packaging conflict.
+	// For example, if multiple files are being installed to same filepath, the install file
+	// of the module listed in this property will have a higher priority over those in other
+	// deps properties.
+	High_priority_deps []string `android:"arch_variant"`
+
 	// Modules to include in this package
 	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"`
-	Both     depsProperty `android:"arch_variant"`
-	Prefer32 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 {
-	Arm64  depsProperty
-	Arm    depsProperty
-	X86_64 depsProperty
-	X86    depsProperty
+	Arm64  DepsProperty
+	Arm    DepsProperty
+	X86_64 DepsProperty
+	X86    DepsProperty
 }
 
 type PackagingProperties struct {
-	Deps     proptools.Configurable[[]string] `android:"arch_variant"`
-	Multilib packagingMultilibProperties      `android:"arch_variant"`
+	DepsProperty
+
+	Multilib packagingMultilibProperties `android:"arch_variant"`
 	Arch     packagingArchProperties
 }
 
 func InitPackageModule(p PackageModule) {
 	base := p.packagingBase()
-	p.AddProperties(&base.properties)
+	p.AddProperties(&base.properties, &base.properties.DepsProperty)
 }
 
 func (p *PackagingBase) packagingBase() *PackagingBase {
@@ -229,55 +264,72 @@
 // the current archicture when this module is not configured for multi target. When configured for
 // 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 {
-	get := func(prop proptools.Configurable[[]string]) []string {
-		return prop.GetOrDefault(ctx, nil)
+// It returns two lists, the normal and high priority deps, respectively.
+func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) ([]string, []string) {
+	var normalDeps []string
+	var highPriorityDeps []string
+
+	get := func(prop DepsProperty) {
+		normalDeps = append(normalDeps, prop.Deps.GetOrDefault(ctx, nil)...)
+		highPriorityDeps = append(highPriorityDeps, prop.High_priority_deps...)
+	}
+	has := func(prop DepsProperty) bool {
+		return len(prop.Deps.GetOrDefault(ctx, nil)) > 0 || len(prop.High_priority_deps) > 0
 	}
 
-	var ret []string
 	if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
-		ret = append(ret, get(p.properties.Deps)...)
+		get(p.properties.DepsProperty)
 	} else if arch.Multilib == "lib32" {
-		ret = append(ret, get(p.properties.Multilib.Lib32.Deps)...)
+		get(p.properties.Multilib.Lib32)
 		// multilib.prefer32.deps are added for lib32 only when they support 32-bit arch
-		for _, dep := range get(p.properties.Multilib.Prefer32.Deps) {
+		for _, dep := range p.properties.Multilib.Prefer32.Deps.GetOrDefault(ctx, nil) {
 			if checkIfOtherModuleSupportsLib32(ctx, dep) {
-				ret = append(ret, dep)
+				normalDeps = append(normalDeps, dep)
+			}
+		}
+		for _, dep := range p.properties.Multilib.Prefer32.High_priority_deps {
+			if checkIfOtherModuleSupportsLib32(ctx, dep) {
+				highPriorityDeps = append(highPriorityDeps, dep)
 			}
 		}
 	} else if arch.Multilib == "lib64" {
-		ret = append(ret, get(p.properties.Multilib.Lib64.Deps)...)
+		get(p.properties.Multilib.Lib64)
 		// 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) {
+		for _, dep := range p.properties.Multilib.Prefer32.Deps.GetOrDefault(ctx, nil) {
 			if !checkIfOtherModuleSupportsLib32(ctx, dep) {
-				ret = append(ret, dep)
+				normalDeps = append(normalDeps, dep)
+			}
+		}
+		for _, dep := range p.properties.Multilib.Prefer32.High_priority_deps {
+			if !checkIfOtherModuleSupportsLib32(ctx, dep) {
+				highPriorityDeps = append(highPriorityDeps, dep)
 			}
 		}
 	} else if arch == Common {
-		ret = append(ret, get(p.properties.Multilib.Common.Deps)...)
+		get(p.properties.Multilib.Common)
 	}
 
 	if p.DepsCollectFirstTargetOnly {
-		if len(get(p.properties.Multilib.First.Deps)) > 0 {
+		if has(p.properties.Multilib.First) {
 			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)...)
+				get(p.properties.Multilib.Both)
 				if i == 0 {
-					ret = append(ret, get(p.properties.Deps)...)
+					get(p.properties.DepsProperty)
 				}
 			}
 		}
 	} else {
-		if len(get(p.properties.Multilib.Both.Deps)) > 0 {
+		if has(p.properties.Multilib.Both) {
 			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)...)
+				get(p.properties.DepsProperty)
 				if i == 0 {
-					ret = append(ret, get(p.properties.Multilib.First.Deps)...)
+					get(p.properties.Multilib.First)
 				}
 			}
 		}
@@ -286,17 +338,21 @@
 	if ctx.Arch().ArchType == Common {
 		switch arch {
 		case Arm64:
-			ret = append(ret, get(p.properties.Arch.Arm64.Deps)...)
+			get(p.properties.Arch.Arm64)
 		case Arm:
-			ret = append(ret, get(p.properties.Arch.Arm.Deps)...)
+			get(p.properties.Arch.Arm)
 		case X86_64:
-			ret = append(ret, get(p.properties.Arch.X86_64.Deps)...)
+			get(p.properties.Arch.X86_64)
 		case X86:
-			ret = append(ret, get(p.properties.Arch.X86.Deps)...)
+			get(p.properties.Arch.X86)
 		}
 	}
 
-	return FirstUniqueStrings(ret)
+	if len(highPriorityDeps) > 0 && !p.AllowHighPriorityDeps {
+		ctx.ModuleErrorf("Usage of high_priority_deps is not allowed for %s module type", ctx.ModuleType())
+	}
+
+	return FirstUniqueStrings(normalDeps), FirstUniqueStrings(highPriorityDeps)
 }
 
 func getSupportedTargets(ctx BaseModuleContext) []Target {
@@ -340,6 +396,8 @@
 	IsPackagingItem() bool
 }
 
+var _ PackagingItem = (*PackagingItemAlwaysDepTag)(nil)
+
 // DepTag provides default implementation of PackagingItem interface.
 // PackagingBase-derived modules can define their own dependency tag by embedding this, which
 // can be passed to AddDeps() or AddDependencies().
@@ -351,21 +409,58 @@
 	return true
 }
 
+type highPriorityDepTag struct {
+	blueprint.BaseDependencyTag
+	PackagingItemAlwaysDepTag
+}
+
 // See PackageModule.AddDeps
 func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
+	addDep := func(t Target, dep string, highPriority bool) {
+		if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
+			return
+		}
+		targetVariation := t.Variations()
+		sharedVariation := blueprint.Variation{
+			Mutator:   "link",
+			Variation: "shared",
+		}
+		// If a shared variation exists, use that. Static variants do not provide any standalone files
+		// for packaging.
+		if ctx.OtherModuleFarDependencyVariantExists([]blueprint.Variation{sharedVariation}, dep) {
+			targetVariation = append(targetVariation, sharedVariation)
+		}
+		depTagToUse := depTag
+		if highPriority {
+			depTagToUse = highPriorityDepTag{}
+		}
+
+		ctx.AddFarVariationDependencies(targetVariation, depTagToUse, dep)
+	}
 	for _, t := range getSupportedTargets(ctx) {
-		for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) {
-			if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
-				continue
-			}
-			ctx.AddFarVariationDependencies(t.Variations(), depTag, dep)
+		normalDeps, highPriorityDeps := p.getDepsForArch(ctx, t.Arch.ArchType)
+		for _, dep := range normalDeps {
+			addDep(t, dep, false)
+		}
+		for _, dep := range highPriorityDeps {
+			addDep(t, dep, true)
 		}
 	}
 }
 
-func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec {
-	// all packaging specs gathered from the dep.
-	var all []PackagingSpec
+// See PackageModule.GatherPackagingSpecs
+func (p *PackagingBase) GatherPackagingSpecsWithFilterAndModifier(ctx ModuleContext, filter func(PackagingSpec) bool, modifier func(*PackagingSpec)) map[string]PackagingSpec {
+	// packaging specs gathered from the dep that are not high priorities.
+	var regularPriorities []PackagingSpec
+
+	// all packaging specs gathered from the high priority deps.
+	var highPriorities []PackagingSpec
+
+	// Name of the dependency which requested the packaging spec.
+	// If this dep is overridden, the packaging spec will not be installed via this dependency chain.
+	// (the packaging spec might still be installed if there are some other deps which depend on it).
+	var depNames []string
+
 	// list of module names overridden
 	var overridden []string
 
@@ -384,8 +479,9 @@
 		return false
 	}
 
-	ctx.VisitDirectDeps(func(child Module) {
-		if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
+	ctx.VisitDirectDepsProxy(func(child ModuleProxy) {
+		depTag := ctx.OtherModuleDependencyTag(child)
+		if pi, ok := depTag.(PackagingItem); !ok || !pi.IsPackagingItem() {
 			return
 		}
 		for _, ps := range OtherModuleProviderOrDefault(
@@ -399,24 +495,44 @@
 					continue
 				}
 			}
-			all = append(all, ps)
+
+			if modifier != nil {
+				modifier(&ps)
+			}
+
+			if _, ok := depTag.(highPriorityDepTag); ok {
+				highPriorities = append(highPriorities, ps)
+			} else {
+				regularPriorities = append(regularPriorities, ps)
+			}
+
+			depNames = append(depNames, child.Name())
 			if ps.overrides != nil {
 				overridden = append(overridden, *ps.overrides...)
 			}
 		}
 	})
 
-	// all minus packaging specs that are overridden
-	var filtered []PackagingSpec
-	for _, ps := range all {
-		if ps.owner != "" && InList(ps.owner, overridden) {
-			continue
+	filterOverridden := func(input []PackagingSpec) []PackagingSpec {
+		// input minus packaging specs that are overridden
+		var filtered []PackagingSpec
+		for index, ps := range input {
+			if ps.owner != "" && InList(ps.owner, overridden) {
+				continue
+			}
+			// The dependency which requested this packaging spec has been overridden.
+			if InList(depNames[index], overridden) {
+				continue
+			}
+			filtered = append(filtered, ps)
 		}
-		filtered = append(filtered, ps)
+		return filtered
 	}
 
+	filteredRegularPriority := filterOverridden(regularPriorities)
+
 	m := make(map[string]PackagingSpec)
-	for _, ps := range filtered {
+	for _, ps := range filteredRegularPriority {
 		dstPath := ps.relPathInPackage
 		if existingPs, ok := m[dstPath]; ok {
 			if !existingPs.Equals(&ps) {
@@ -426,10 +542,30 @@
 		}
 		m[dstPath] = ps
 	}
+
+	filteredHighPriority := filterOverridden(highPriorities)
+	highPriorityPs := make(map[string]PackagingSpec)
+	for _, ps := range filteredHighPriority {
+		dstPath := ps.relPathInPackage
+		if existingPs, ok := highPriorityPs[dstPath]; ok {
+			if !existingPs.Equals(&ps) {
+				ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps)
+			}
+			continue
+		}
+		highPriorityPs[dstPath] = ps
+		m[dstPath] = ps
+	}
+
 	return m
 }
 
 // See PackageModule.GatherPackagingSpecs
+func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec {
+	return p.GatherPackagingSpecsWithFilterAndModifier(ctx, filter, nil)
+}
+
+// See PackageModule.GatherPackagingSpecs
 func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
 	return p.GatherPackagingSpecsWithFilter(ctx, nil)
 }
@@ -457,10 +593,6 @@
 	}
 
 	seenDir := make(map[string]bool)
-	preparerPath := PathForModuleOut(ctx, "preparer.sh")
-	cmd := builder.Command().Tool(preparerPath)
-	var sb strings.Builder
-	sb.WriteString("set -e\n")
 
 	dirs := make([]WritablePath, 0, len(dirsToSpecs))
 	for dir, _ := range dirsToSpecs {
@@ -479,22 +611,19 @@
 			entries = append(entries, ps.relPathInPackage)
 			if _, ok := seenDir[destDir]; !ok {
 				seenDir[destDir] = true
-				sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir))
+				builder.Command().Textf("mkdir -p %s", destDir)
 			}
 			if ps.symlinkTarget == "" {
-				cmd.Implicit(ps.srcPath)
-				sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath))
+				builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
 			} else {
-				sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath))
+				builder.Command().Textf("ln -sf %s %s", ps.symlinkTarget, destPath)
 			}
 			if ps.executable {
-				sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath))
+				builder.Command().Textf("chmod a+x %s", destPath)
 			}
 		}
 	}
 
-	WriteExecutableFileRuleVerbatim(ctx, preparerPath, sb.String())
-
 	return entries
 }
 
diff --git a/android/packaging_test.go b/android/packaging_test.go
index f5b1020..9c6760c 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -64,7 +64,7 @@
 	ModuleBase
 	PackagingBase
 	properties struct {
-		Install_deps []string `android:`
+		Install_deps []string
 	}
 	entries []string
 }
diff --git a/android/path_properties.go b/android/path_properties.go
index 6210aee..55a4dc0 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"reflect"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -27,7 +28,7 @@
 // to the output file of the referenced module.
 
 func registerPathDepsMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("pathdeps", pathDepsMutator).Parallel()
+	ctx.BottomUp("pathdeps", pathDepsMutator)
 }
 
 // The pathDepsMutator automatically adds dependencies on any module that is listed with the
@@ -38,20 +39,35 @@
 		// squashed into the real modules.
 		return
 	}
+	if !ctx.Module().Enabled(ctx) {
+		return
+	}
 	props := ctx.Module().base().GetProperties()
 	addPathDepsForProps(ctx, props)
 }
 
 func addPathDepsForProps(ctx BottomUpMutatorContext, props []interface{}) {
 	// Iterate through each property struct of the module extracting the contents of all properties
-	// tagged with `android:"path"`.
+	// tagged with `android:"path"` or one of the variant-specifying tags.
 	var pathProperties []string
+	var pathDeviceFirstProperties []string
+	var pathDeviceFirstPrefer32Properties []string
+	var pathDeviceCommonProperties []string
+	var pathCommonOsProperties []string
 	for _, ps := range props {
-		pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ctx, ps)...)
+		pathProperties = append(pathProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path")...)
+		pathDeviceFirstProperties = append(pathDeviceFirstProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_first")...)
+		pathDeviceFirstPrefer32Properties = append(pathDeviceFirstPrefer32Properties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_first_prefer32")...)
+		pathDeviceCommonProperties = append(pathDeviceCommonProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_common")...)
+		pathCommonOsProperties = append(pathCommonOsProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_common_os")...)
 	}
 
 	// Remove duplicates to avoid multiple dependencies.
 	pathProperties = FirstUniqueStrings(pathProperties)
+	pathDeviceFirstProperties = FirstUniqueStrings(pathDeviceFirstProperties)
+	pathDeviceFirstPrefer32Properties = FirstUniqueStrings(pathDeviceFirstPrefer32Properties)
+	pathDeviceCommonProperties = FirstUniqueStrings(pathDeviceCommonProperties)
+	pathCommonOsProperties = FirstUniqueStrings(pathCommonOsProperties)
 
 	// Add dependencies to anything that is a module reference.
 	for _, s := range pathProperties {
@@ -59,12 +75,54 @@
 			ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(m, t), m)
 		}
 	}
+	// For properties tagged "path_device_first", use the first arch device variant when adding
+	// dependencies. This allows host modules to have some properties that add dependencies on
+	// device modules.
+	for _, s := range pathDeviceFirstProperties {
+		if m, t := SrcIsModuleWithTag(s); m != "" {
+			ctx.AddVariationDependencies(ctx.Config().AndroidFirstDeviceTarget.Variations(), sourceOrOutputDepTag(m, t), m)
+		}
+	}
+	// properties tagged path_device_first_prefer32 get the first 32 bit target if one is available,
+	// otherwise they use the first 64 bit target
+	if len(pathDeviceFirstPrefer32Properties) > 0 {
+		var targets []Target
+		if ctx.Config().IgnorePrefer32OnDevice() {
+			targets, _ = decodeMultilibTargets("first", ctx.Config().Targets[Android], false)
+		} else {
+			targets, _ = decodeMultilibTargets("first_prefer32", ctx.Config().Targets[Android], false)
+		}
+		if len(targets) == 0 {
+			ctx.ModuleErrorf("Could not find a first_prefer32 target")
+		} else {
+			for _, s := range pathDeviceFirstPrefer32Properties {
+				if m, t := SrcIsModuleWithTag(s); m != "" {
+					ctx.AddVariationDependencies(targets[0].Variations(), sourceOrOutputDepTag(m, t), m)
+				}
+			}
+		}
+	}
+	// properties tagged "path_device_common" get the device common variant
+	for _, s := range pathDeviceCommonProperties {
+		if m, t := SrcIsModuleWithTag(s); m != "" {
+			ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), sourceOrOutputDepTag(m, t), m)
+		}
+	}
+	// properties tagged "path_common_os" get the CommonOs variant
+	for _, s := range pathCommonOsProperties {
+		if m, t := SrcIsModuleWithTag(s); m != "" {
+			ctx.AddVariationDependencies([]blueprint.Variation{
+				{Mutator: "os", Variation: "common_os"},
+				{Mutator: "arch", Variation: ""},
+			}, sourceOrOutputDepTag(m, t), m)
+		}
+	}
 }
 
-// pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with
-// android:"path" to extract all their values from a property struct, returning them as a single
+// taggedPropertiesForPropertyStruct uses the indexes of properties that are tagged with
+// android:"tagValue" to extract all their values from a property struct, returning them as a single
 // slice of strings.
-func pathPropertiesForPropertyStruct(ctx BottomUpMutatorContext, ps interface{}) []string {
+func taggedPropertiesForPropertyStruct(ctx BottomUpMutatorContext, ps interface{}, tagValue string) []string {
 	v := reflect.ValueOf(ps)
 	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
 		panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type()))
@@ -79,7 +137,7 @@
 	v = v.Elem()
 
 	// Get or create the list of indexes of properties that are tagged with `android:"path"`.
-	pathPropertyIndexes := pathPropertyIndexesForPropertyStruct(ps)
+	pathPropertyIndexes := taggedPropertyIndexesForPropertyStruct(ps, tagValue)
 
 	var ret []string
 
@@ -172,12 +230,20 @@
 
 var pathPropertyIndexesCache OncePer
 
-// pathPropertyIndexesForPropertyStruct returns a list of all of the indexes of properties in
-// property struct type that are tagged with `android:"path"`.  Each index is a []int suitable for
-// passing to reflect.Value.FieldByIndex.  The value is cached in a global cache by type.
-func pathPropertyIndexesForPropertyStruct(ps interface{}) [][]int {
-	key := NewCustomOnceKey(reflect.TypeOf(ps))
+// taggedPropertyIndexesForPropertyStruct returns a list of all of the indexes of properties in
+// property struct type that are tagged with `android:"tagValue"`.  Each index is a []int suitable
+// for passing to reflect.Value.FieldByIndex.  The value is cached in a global cache by type and
+// tagValue.
+func taggedPropertyIndexesForPropertyStruct(ps interface{}, tagValue string) [][]int {
+	type pathPropertyIndexesOnceKey struct {
+		propStructType reflect.Type
+		tagValue       string
+	}
+	key := NewCustomOnceKey(pathPropertyIndexesOnceKey{
+		propStructType: reflect.TypeOf(ps),
+		tagValue:       tagValue,
+	})
 	return pathPropertyIndexesCache.Once(key, func() interface{} {
-		return proptools.PropertyIndexesWithTag(ps, "android", "path")
+		return proptools.PropertyIndexesWithTag(ps, "android", tagValue)
 	}).([][]int)
 }
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index 07b4869..6f44f28 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -64,7 +64,7 @@
 	if p.props.Foo != "" {
 		// Make sure there is only one dependency on a module listed in a property present in multiple property structs
 		m := SrcIsModule(p.props.Foo)
-		if GetModuleFromPathDep(ctx, m, "") == nil {
+		if GetModuleProxyFromPathDep(ctx, m, "") == nil {
 			ctx.ModuleErrorf("GetDirectDepWithTag failed")
 		}
 	}
diff --git a/android/paths.go b/android/paths.go
index 28656c3..bbf10d1 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -15,9 +15,6 @@
 package android
 
 import (
-	"bytes"
-	"encoding/gob"
-	"errors"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -27,6 +24,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/gobtools"
 	"github.com/google/blueprint/pathtools"
 )
 
@@ -92,7 +90,9 @@
 type ModuleWithDepsPathContext interface {
 	EarlyModulePathContext
 	OtherModuleProviderContext
-	VisitDirectDepsBlueprint(visit func(blueprint.Module))
+	VisitDirectDeps(visit func(Module))
+	VisitDirectDepsProxy(visit func(ModuleProxy))
+	VisitDirectDepsProxyWithTag(tag blueprint.DependencyTag, visit func(ModuleProxy))
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
 	HasMutatorFinished(mutatorName string) bool
 }
@@ -119,6 +119,9 @@
 	InstallInOdm() bool
 	InstallInProduct() bool
 	InstallInVendor() bool
+	InstallInSystemDlkm() bool
+	InstallInVendorDlkm() bool
+	InstallInOdmDlkm() bool
 	InstallForceOS() (*OsType, *ArchType)
 }
 
@@ -172,6 +175,18 @@
 	return ctx.Module().InstallInVendor()
 }
 
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInSystemDlkm() bool {
+	return ctx.Module().InstallInSystemDlkm()
+}
+
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendorDlkm() bool {
+	return ctx.Module().InstallInVendorDlkm()
+}
+
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInOdmDlkm() bool {
+	return ctx.Module().InstallInOdmDlkm()
+}
+
 func (ctx *baseModuleContextToModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
 	return ctx.Module().InstallForceOS()
 }
@@ -342,6 +357,11 @@
 	invalidReason string // Not applicable if path != nil. "" if the reason is unknown.
 }
 
+type optionalPathGob struct {
+	Path          Path
+	InvalidReason string
+}
+
 // OptionalPathForPath returns an OptionalPath containing the path.
 func OptionalPathForPath(path Path) OptionalPath {
 	return OptionalPath{path: path}
@@ -353,6 +373,26 @@
 	return OptionalPath{invalidReason: reason}
 }
 
+func (p *OptionalPath) ToGob() *optionalPathGob {
+	return &optionalPathGob{
+		Path:          p.path,
+		InvalidReason: p.invalidReason,
+	}
+}
+
+func (p *OptionalPath) FromGob(data *optionalPathGob) {
+	p.path = data.Path
+	p.invalidReason = data.InvalidReason
+}
+
+func (p OptionalPath) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[optionalPathGob](&p)
+}
+
+func (p *OptionalPath) GobDecode(data []byte) error {
+	return gobtools.CustomGobDecode[optionalPathGob](data, p)
+}
+
 // Valid returns whether there is a valid path
 func (p OptionalPath) Valid() bool {
 	return p.path != nil
@@ -528,6 +568,78 @@
 	return ret
 }
 
+type directoryPath struct {
+	basePath
+}
+
+func (d *directoryPath) String() string {
+	return d.basePath.String()
+}
+
+func (d *directoryPath) base() basePath {
+	return d.basePath
+}
+
+// DirectoryPath represents a source path for directories. Incompatible with Path by design.
+type DirectoryPath interface {
+	String() string
+	base() basePath
+}
+
+var _ DirectoryPath = (*directoryPath)(nil)
+
+type DirectoryPaths []DirectoryPath
+
+// DirectoryPathsForModuleSrcExcludes returns a Paths{} containing the resolved references in
+// directory paths. Elements of paths are resolved as:
+//   - filepath, relative to local module directory, resolves as a filepath relative to the local
+//     source directory
+//   - other modules using the ":name" syntax. These modules must implement DirProvider.
+func DirectoryPathsForModuleSrc(ctx ModuleMissingDepsPathContext, paths []string) DirectoryPaths {
+	var ret DirectoryPaths
+
+	for _, path := range paths {
+		if m, t := SrcIsModuleWithTag(path); m != "" {
+			module := GetModuleProxyFromPathDep(ctx, m, t)
+			if module == nil {
+				ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
+				continue
+			}
+			if t != "" {
+				ctx.ModuleErrorf("DirProvider dependency %q does not support the tag %q", module, t)
+				continue
+			}
+			mctx, ok := ctx.(OtherModuleProviderContext)
+			if !ok {
+				panic(fmt.Errorf("%s is not an OtherModuleProviderContext", ctx))
+			}
+			if dirProvider, ok := OtherModuleProvider(mctx, *module, DirProvider); ok {
+				ret = append(ret, dirProvider.Dirs...)
+			} else {
+				ReportPathErrorf(ctx, "module %q does not implement DirProvider", module)
+			}
+		} else {
+			p := pathForModuleSrc(ctx, path)
+			if isDir, err := ctx.Config().fs.IsDir(p.String()); err != nil {
+				ReportPathErrorf(ctx, "%s: %s", p, err.Error())
+			} else if !isDir {
+				ReportPathErrorf(ctx, "module directory path %q is not a directory", p)
+			} else {
+				ret = append(ret, &directoryPath{basePath{path: p.path, rel: p.rel}})
+			}
+		}
+	}
+
+	seen := make(map[DirectoryPath]bool, len(ret))
+	for _, path := range ret {
+		if seen[path] {
+			ReportPathErrorf(ctx, "duplicated path %q", path)
+		}
+		seen[path] = true
+	}
+	return ret
+}
+
 // OutputPaths is a slice of OutputPath objects, with helpers to operate on the collection.
 type OutputPaths []OutputPath
 
@@ -559,14 +671,15 @@
 // If the dependency is not found, a missingErrorDependency is returned.
 // If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned.
 func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag string) (Paths, error) {
-	module := GetModuleFromPathDep(ctx, moduleName, tag)
+	module := GetModuleProxyFromPathDep(ctx, moduleName, tag)
 	if module == nil {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
-	if aModule, ok := module.(Module); ok && !aModule.Enabled(ctx) {
+	if !OtherModuleProviderOrDefault(ctx, *module, CommonModuleInfoKey).Enabled {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
-	outputFiles, err := outputFilesForModule(ctx, module, tag)
+
+	outputFiles, err := outputFilesForModule(ctx, *module, tag)
 	if outputFiles != nil && err == nil {
 		return outputFiles, nil
 	} else {
@@ -574,7 +687,7 @@
 	}
 }
 
-// GetModuleFromPathDep will return the module that was added as a dependency automatically for
+// GetModuleProxyFromPathDep will return the module that was added as a dependency automatically for
 // properties tagged with `android:"path"` or manually using ExtractSourceDeps or
 // ExtractSourcesDeps.
 //
@@ -584,6 +697,27 @@
 //
 // If tag is "" then the returned module will be the dependency that was added for ":moduleName".
 // Otherwise, it is the dependency that was added for ":moduleName{tag}".
+func GetModuleProxyFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) *ModuleProxy {
+	var found *ModuleProxy
+	// The sourceOrOutputDepTag uniquely identifies the module dependency as it contains both the
+	// module name and the tag. Dependencies added automatically for properties tagged with
+	// `android:"path"` are deduped so are guaranteed to be unique. It is possible for duplicate
+	// dependencies to be added manually using ExtractSourcesDeps or ExtractSourceDeps but even then
+	// it will always be the case that the dependencies will be identical, i.e. the same tag and same
+	// moduleName referring to the same dependency module.
+	//
+	// It does not matter whether the moduleName is a fully qualified name or if the module
+	// dependency is a prebuilt module. All that matters is the same information is supplied to
+	// create the tag here as was supplied to create the tag when the dependency was added so that
+	// this finds the matching dependency module.
+	expectedTag := sourceOrOutputDepTag(moduleName, tag)
+	ctx.VisitDirectDepsProxyWithTag(expectedTag, func(module ModuleProxy) {
+		found = &module
+	})
+	return found
+}
+
+// Deprecated: use GetModuleProxyFromPathDep
 func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) blueprint.Module {
 	var found blueprint.Module
 	// The sourceOrOutputDepTag uniquely identifies the module dependency as it contains both the
@@ -598,7 +732,7 @@
 	// create the tag here as was supplied to create the tag when the dependency was added so that
 	// this finds the matching dependency module.
 	expectedTag := sourceOrOutputDepTag(moduleName, tag)
-	ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+	ctx.VisitDirectDeps(func(module Module) {
 		depTag := ctx.OtherModuleDependencyTag(module)
 		if depTag == expectedTag {
 			found = module
@@ -1065,26 +1199,29 @@
 	rel  string
 }
 
-func (p basePath) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.path), encoder.Encode(p.rel))
-	if err != nil {
-		return nil, err
-	}
+type basePathGob struct {
+	Path string
+	Rel  string
+}
 
-	return w.Bytes(), nil
+func (p *basePath) ToGob() *basePathGob {
+	return &basePathGob{
+		Path: p.path,
+		Rel:  p.rel,
+	}
+}
+
+func (p *basePath) FromGob(data *basePathGob) {
+	p.path = data.Path
+	p.rel = data.Rel
+}
+
+func (p basePath) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[basePathGob](&p)
 }
 
 func (p *basePath) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.path), decoder.Decode(&p.rel))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return gobtools.CustomGobDecode[basePathGob](data, p)
 }
 
 func (p basePath) Ext() string {
@@ -1387,26 +1524,32 @@
 	fullPath string
 }
 
-func (p OutputPath) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.outDir), encoder.Encode(p.fullPath))
-	if err != nil {
-		return nil, err
-	}
+type outputPathGob struct {
+	BasePath basePath
+	OutDir   string
+	FullPath string
+}
 
-	return w.Bytes(), nil
+func (p *OutputPath) ToGob() *outputPathGob {
+	return &outputPathGob{
+		BasePath: p.basePath,
+		OutDir:   p.outDir,
+		FullPath: p.fullPath,
+	}
+}
+
+func (p *OutputPath) FromGob(data *outputPathGob) {
+	p.basePath = data.BasePath
+	p.outDir = data.OutDir
+	p.fullPath = data.FullPath
+}
+
+func (p OutputPath) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[outputPathGob](&p)
 }
 
 func (p *OutputPath) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.outDir), decoder.Decode(&p.fullPath))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return gobtools.CustomGobDecode[outputPathGob](data, p)
 }
 
 func (p OutputPath) withRel(rel string) OutputPath {
@@ -1806,30 +1949,41 @@
 	fullPath string
 }
 
-func (p *InstallPath) GobEncode() ([]byte, error) {
-	w := new(bytes.Buffer)
-	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.soongOutDir),
-		encoder.Encode(p.partitionDir), encoder.Encode(p.partition),
-		encoder.Encode(p.makePath), encoder.Encode(p.fullPath))
-	if err != nil {
-		return nil, err
-	}
+type installPathGob struct {
+	BasePath     basePath
+	SoongOutDir  string
+	PartitionDir string
+	Partition    string
+	MakePath     bool
+	FullPath     string
+}
 
-	return w.Bytes(), nil
+func (p *InstallPath) ToGob() *installPathGob {
+	return &installPathGob{
+		BasePath:     p.basePath,
+		SoongOutDir:  p.soongOutDir,
+		PartitionDir: p.partitionDir,
+		Partition:    p.partition,
+		MakePath:     p.makePath,
+		FullPath:     p.fullPath,
+	}
+}
+
+func (p *InstallPath) FromGob(data *installPathGob) {
+	p.basePath = data.BasePath
+	p.soongOutDir = data.SoongOutDir
+	p.partitionDir = data.PartitionDir
+	p.partition = data.Partition
+	p.makePath = data.MakePath
+	p.fullPath = data.FullPath
+}
+
+func (p InstallPath) GobEncode() ([]byte, error) {
+	return gobtools.CustomGobEncode[installPathGob](&p)
 }
 
 func (p *InstallPath) GobDecode(data []byte) error {
-	r := bytes.NewBuffer(data)
-	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.soongOutDir),
-		decoder.Decode(&p.partitionDir), decoder.Decode(&p.partition),
-		decoder.Decode(&p.makePath), decoder.Decode(&p.fullPath))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return gobtools.CustomGobDecode[installPathGob](data, p)
 }
 
 // Will panic if called from outside a test environment.
@@ -2012,6 +2166,10 @@
 	return base.Join(ctx, paths...)
 }
 
+func PathForSuiteInstall(ctx PathContext, suite string, pathComponents ...string) InstallPath {
+	return pathForPartitionInstallDir(ctx, "test_suites", "test_suites", false).Join(ctx, suite).Join(ctx, pathComponents...)
+}
+
 func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
 	rel := Rel(ctx, strings.TrimSuffix(path.PartitionDir(), path.partition), path.String())
 	return "/" + rel
@@ -2066,6 +2224,12 @@
 			partition = ctx.DeviceConfig().SystemExtPath()
 		} else if ctx.InstallInRoot() {
 			partition = "root"
+		} else if ctx.InstallInSystemDlkm() {
+			partition = ctx.DeviceConfig().SystemDlkmPath()
+		} else if ctx.InstallInVendorDlkm() {
+			partition = ctx.DeviceConfig().VendorDlkmPath()
+		} else if ctx.InstallInOdmDlkm() {
+			partition = ctx.DeviceConfig().OdmDlkmPath()
 		} else {
 			partition = "system"
 		}
@@ -2269,6 +2433,9 @@
 	inOdm           bool
 	inProduct       bool
 	inVendor        bool
+	inSystemDlkm    bool
+	inVendorDlkm    bool
+	inOdmDlkm       bool
 	forceOS         *OsType
 	forceArch       *ArchType
 }
@@ -2323,6 +2490,18 @@
 	return m.inVendor
 }
 
+func (m testModuleInstallPathContext) InstallInSystemDlkm() bool {
+	return m.inSystemDlkm
+}
+
+func (m testModuleInstallPathContext) InstallInVendorDlkm() bool {
+	return m.inVendorDlkm
+}
+
+func (m testModuleInstallPathContext) InstallInOdmDlkm() bool {
+	return m.inOdmDlkm
+}
+
 func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
 	return m.forceOS, m.forceArch
 }
@@ -2484,3 +2663,19 @@
 	}
 	return false
 }
+
+// ToRelativeSourcePath converts absolute source path to the path relative to the source root.
+// This throws an error if the input path is outside of the source root and cannot be converted
+// to the relative path.
+// This should be rarely used given that the source path is relative in Soong.
+func ToRelativeSourcePath(ctx PathContext, path string) string {
+	ret := path
+	if filepath.IsAbs(path) {
+		relPath, err := filepath.Rel(absSrcDir, path)
+		if err != nil || strings.HasPrefix(relPath, "..") {
+			ReportPathErrorf(ctx, "%s is outside of the source root", path)
+		}
+		ret = relPath
+	}
+	return ret
+}
diff --git a/android/paths_test.go b/android/paths_test.go
index 941f0ca..5e618f9 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -1592,6 +1592,12 @@
 	})
 }
 
+func TestDirectoryPathIsIncompatibleWithPath(t *testing.T) {
+	d := (DirectoryPath)(&directoryPath{})
+	_, ok := d.(Path)
+	AssertBoolEquals(t, "directoryPath shouldn't implement Path", ok, false)
+}
+
 func ExampleOutputPath_ReplaceExtension() {
 	ctx := &configErrorWrapper{
 		config: TestConfig("out", nil, "", nil),
diff --git a/android/prebuilt.go b/android/prebuilt.go
index fd5a6ea..0ac67b3 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -272,6 +272,25 @@
 	InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs")
 }
 
+// InitConfigurablePrebuiltModuleString is the same as InitPrebuiltModule, but uses a
+// Configurable string property instead of a regular list of strings. It only produces a single
+// source file.
+func InitConfigurablePrebuiltModuleString(module PrebuiltInterface, srcs *proptools.Configurable[string], propertyName string) {
+	if srcs == nil {
+		panic(fmt.Errorf("%s must not be nil", propertyName))
+	}
+
+	srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
+		src := srcs.GetOrDefault(ctx, "")
+		if src == "" {
+			return nil
+		}
+		return []string{src}
+	}
+
+	InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, propertyName)
+}
+
 func InitSingleSourcePrebuiltModule(module PrebuiltInterface, srcProps interface{}, srcField string) {
 	srcPropsValue := reflect.ValueOf(srcProps).Elem()
 	srcStructField, _ := srcPropsValue.Type().FieldByName(srcField)
@@ -359,13 +378,13 @@
 //
 // This function is for use on dependencies after PrebuiltPostDepsMutator has
 // run - any dependency that is registered before that will already reference
-// the right module. This function is only safe to call after all mutators that
-// may call CreateVariations, e.g. in GenerateAndroidBuildActions.
+// the right module. This function is only safe to call after all TransitionMutators
+// have run, e.g. in GenerateAndroidBuildActions.
 func PrebuiltGetPreferred(ctx BaseModuleContext, module Module) Module {
-	if !module.IsReplacedByPrebuilt() {
+	if !OtherModuleProviderOrDefault(ctx, module, CommonModuleInfoKey).ReplacedByPrebuilt {
 		return module
 	}
-	if IsModulePrebuilt(module) {
+	if _, ok := OtherModuleProvider(ctx, module, PrebuiltModuleProviderKey); ok {
 		// If we're given a prebuilt then assume there's no source module around.
 		return module
 	}
@@ -373,11 +392,11 @@
 	sourceModDepFound := false
 	var prebuiltMod Module
 
-	ctx.WalkDeps(func(child, parent Module) bool {
+	ctx.WalkDepsProxy(func(child, parent ModuleProxy) bool {
 		if prebuiltMod != nil {
 			return false
 		}
-		if parent == ctx.Module() {
+		if ctx.EqualModules(parent, ctx.Module()) {
 			// First level: Only recurse if the module is found as a direct dependency.
 			sourceModDepFound = child == module
 			return sourceModDepFound
@@ -400,13 +419,13 @@
 }
 
 func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel()
+	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).UsesRename()
 }
 
 func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).Parallel()
-	ctx.BottomUp("prebuilt_select", PrebuiltSelectModuleMutator).Parallel()
-	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel()
+	ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).UsesReverseDependencies()
+	ctx.BottomUp("prebuilt_select", PrebuiltSelectModuleMutator)
+	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).UsesReplaceDependencies()
 }
 
 // Returns the name of the source module corresponding to a prebuilt module
@@ -550,6 +569,7 @@
 		for _, moduleInFamily := range allModulesInFamily {
 			if moduleInFamily.Name() != selectedModuleInFamily.Name() {
 				moduleInFamily.HideFromMake()
+				moduleInFamily.SkipInstall()
 				// If this is a prebuilt module, unset properties.UsePrebuilt
 				// properties.UsePrebuilt might evaluate to true via soong config var fallback mechanism
 				// Set it to false explicitly so that the following mutator does not replace rdeps to this unselected prebuilt
@@ -620,6 +640,7 @@
 			}
 		} else {
 			m.HideFromMake()
+			m.SkipInstall()
 		}
 	}
 }
@@ -677,7 +698,7 @@
 //
 // Even though this is a cc_prebuilt_library_shared, we create both the variants today
 // https://source.corp.google.com/h/googleplex-android/platform/build/soong/+/e08e32b45a18a77bc3c3e751f730539b1b374f1b:cc/library.go;l=2113-2116;drc=2c4a9779cd1921d0397a12b3d3521f4c9b30d747;bpv=1;bpt=0
-func (p *Prebuilt) variantIsDisabled(ctx BaseMutatorContext, prebuilt Module) bool {
+func (p *Prebuilt) variantIsDisabled(ctx BaseModuleContext, prebuilt Module) bool {
 	return p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0
 }
 
@@ -687,7 +708,7 @@
 
 // usePrebuilt returns true if a prebuilt should be used instead of the source module.  The prebuilt
 // will be used if it is marked "prefer" or if the source module is disabled.
-func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt Module) bool {
+func (p *Prebuilt) usePrebuilt(ctx BaseModuleContext, source Module, prebuilt Module) bool {
 	isMainlinePrebuilt := func(prebuilt Module) bool {
 		apex, ok := prebuilt.(apexVariationName)
 		if !ok {
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 5e4af0b..b90ef3b 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -508,11 +508,10 @@
 }
 
 func (p *prebuiltModule) GenerateAndroidBuildActions(ctx ModuleContext) {
-	var src Path
 	if len(p.properties.Srcs) >= 1 {
-		src = p.prebuilt.SingleSourcePath(ctx)
+		src := p.prebuilt.SingleSourcePath(ctx)
+		ctx.SetOutputFiles(Paths{src}, "")
 	}
-	ctx.SetOutputFiles(Paths{src}, "")
 }
 
 func (p *prebuiltModule) Prebuilt() *Prebuilt {
diff --git a/android/product_config.go b/android/product_config.go
index ce3acc9..850f003 100644
--- a/android/product_config.go
+++ b/android/product_config.go
@@ -32,7 +32,7 @@
 		ctx.ModuleErrorf("There can only be one product_config module in build/soong")
 		return
 	}
-	outputFilePath := PathForModuleOut(ctx, p.Name()+".json").OutputPath
+	outputFilePath := PathForModuleOut(ctx, p.Name()+".json")
 
 	// DeviceProduct can be null so calling ctx.Config().DeviceProduct() may cause null dereference
 	targetProduct := proptools.String(ctx.Config().config.productVariables.DeviceProduct)
diff --git a/android/product_config_to_bp.go b/android/product_config_to_bp.go
deleted file mode 100644
index 680328f..0000000
--- a/android/product_config_to_bp.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// 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 android
-
-func init() {
-	ctx := InitRegistrationContext
-	ctx.RegisterParallelSingletonType("product_config_to_bp_singleton", productConfigToBpSingletonFactory)
-}
-
-type productConfigToBpSingleton struct{}
-
-func (s *productConfigToBpSingleton) GenerateBuildActions(ctx SingletonContext) {
-	// TODO: update content from make-based product config
-	var content string
-	generatedBp := PathForOutput(ctx, "soong_generated_product_config.bp")
-	WriteFileRule(ctx, generatedBp, content)
-	ctx.Phony("product_config_to_bp", generatedBp)
-}
-
-// productConfigToBpSingleton generates a bp file from make-based product config
-func productConfigToBpSingletonFactory() Singleton {
-	return &productConfigToBpSingleton{}
-}
diff --git a/android/proto.go b/android/proto.go
index 0d8e097..66faa20 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -74,14 +74,14 @@
 		flags = append(flags, JoinWithPrefix(rootProtoIncludeDirs.Strings(), "-I"))
 	}
 
-	ctx.VisitDirectDepsWithTag(ProtoPluginDepTag, func(dep Module) {
-		if hostTool, ok := dep.(HostToolProvider); !ok || !hostTool.HostToolPath().Valid() {
+	ctx.VisitDirectDepsProxyWithTag(ProtoPluginDepTag, func(dep ModuleProxy) {
+		if h, ok := OtherModuleProvider(ctx, dep, HostToolProviderKey); !ok || !h.HostToolPath.Valid() {
 			ctx.PropertyErrorf("proto.plugin", "module %q is not a host tool provider",
 				ctx.OtherModuleName(dep))
 		} else {
 			plugin := String(p.Proto.Plugin)
-			deps = append(deps, hostTool.HostToolPath().Path())
-			flags = append(flags, "--plugin=protoc-gen-"+plugin+"="+hostTool.HostToolPath().String())
+			deps = append(deps, h.HostToolPath.Path())
+			flags = append(flags, "--plugin=protoc-gen-"+plugin+"="+h.HostToolPath.String())
 		}
 	})
 
diff --git a/android/provider.go b/android/provider.go
index 5ded4cc..81d17a1 100644
--- a/android/provider.go
+++ b/android/provider.go
@@ -24,7 +24,7 @@
 // OtherModuleProviderContext is a helper interface that accepts ModuleContext, BottomUpMutatorContext, or
 // TopDownMutatorContext.
 func OtherModuleProvider[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
-	value, ok := ctx.otherModuleProvider(module, provider)
+	value, ok := ctx.otherModuleProvider(getWrappedModule(module), provider)
 	if !ok {
 		var k K
 		return k, false
diff --git a/android/register.go b/android/register.go
index eb6a35e..8d2f19e 100644
--- a/android/register.go
+++ b/android/register.go
@@ -91,7 +91,14 @@
 	bottomUpMutator   blueprint.BottomUpMutator
 	topDownMutator    blueprint.TopDownMutator
 	transitionMutator blueprint.TransitionMutator
-	parallel          bool
+
+	usesRename              bool
+	usesReverseDependencies bool
+	usesReplaceDependencies bool
+	usesCreateModule        bool
+	mutatesDependencies     bool
+	mutatesGlobalState      bool
+	neverFar                bool
 }
 
 var _ sortableComponent = &mutator{}
@@ -235,6 +242,7 @@
 
 	PreDepsMutators(f RegisterMutatorFunc)
 	PostDepsMutators(f RegisterMutatorFunc)
+	PostApexMutators(f RegisterMutatorFunc)
 	FinalDepsMutators(f RegisterMutatorFunc)
 }
 
@@ -326,6 +334,10 @@
 	PostDepsMutators(f)
 }
 
+func (ctx *initRegistrationContext) PostApexMutators(f RegisterMutatorFunc) {
+	PostApexMutators(f)
+}
+
 func (ctx *initRegistrationContext) FinalDepsMutators(f RegisterMutatorFunc) {
 	FinalDepsMutators(f)
 }
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 18bbcab..db56c3f 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -38,6 +38,9 @@
 const sboxToolsSubDir = "tools"
 const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
 
+const nsjailToolsSubDir = "tools"
+const nsjailOutDir = "out"
+
 // RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
 // graph.
 type RuleBuilder struct {
@@ -59,6 +62,10 @@
 	sboxManifestPath WritablePath
 	missingDeps      []string
 	args             map[string]string
+	nsjail           bool
+	nsjailKeepGendir bool
+	nsjailBasePath   WritablePath
+	nsjailImplicits  Paths
 }
 
 // NewRuleBuilder returns a newly created RuleBuilder.
@@ -165,12 +172,55 @@
 	if len(r.commands) > 0 {
 		panic("Sbox() may not be called after Command()")
 	}
+	if r.nsjail {
+		panic("Sbox() may not be called after Nsjail()")
+	}
 	r.sbox = true
 	r.outDir = outputDir
 	r.sboxManifestPath = manifestPath
 	return r
 }
 
+// Nsjail marks the rule as needing to be wrapped by nsjail. The outputDir should point to the
+// output directory that nsjail will mount to out/. It should not be written to by any other rule.
+// baseDir should point to a location where nsjail will mount to /nsjail_build_sandbox, which will
+// be the working directory of the command.
+func (r *RuleBuilder) Nsjail(outputDir WritablePath, baseDir WritablePath) *RuleBuilder {
+	if len(r.commands) > 0 {
+		panic("Nsjail() may not be called after Command()")
+	}
+	if r.sbox {
+		panic("Nsjail() may not be called after Sbox()")
+	}
+	r.nsjail = true
+	r.outDir = outputDir
+	r.nsjailBasePath = baseDir
+	return r
+}
+
+// NsjailImplicits adds implicit inputs that are not directly mounted. This is useful when
+// the rule mounts directories, as files within those directories can be globbed and
+// tracked as dependencies with NsjailImplicits().
+func (r *RuleBuilder) NsjailImplicits(inputs Paths) *RuleBuilder {
+	if !r.nsjail {
+		panic("NsjailImplicits() must be called after Nsjail()")
+	}
+	r.nsjailImplicits = append(r.nsjailImplicits, inputs...)
+	return r
+}
+
+// By default, nsjail rules truncate outputDir and baseDir before running commands, similar to Sbox
+// rules which always run commands in a fresh sandbox. Calling NsjailKeepGendir keeps outputDir and
+// baseDir as-is, leaving previous artifacts. This is useful when the rules support incremental
+// builds.
+func (r *RuleBuilder) NsjailKeepGendir() *RuleBuilder {
+	if !r.nsjail {
+		panic("NsjailKeepGendir() must be called after Nsjail()")
+	}
+	r.nsjailKeepGendir = true
+	return r
+}
+
 // SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
 // sandbox.
 func (r *RuleBuilder) SandboxTools() *RuleBuilder {
@@ -451,21 +501,15 @@
 		Inputs(depFiles.Paths())
 }
 
-// BuildWithNinjaVars adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
-// Outputs. This function will not escape Ninja variables, so it may be used to write sandbox manifests using Ninja variables.
-func (r *RuleBuilder) BuildWithUnescapedNinjaVars(name string, desc string) {
-	r.build(name, desc, false)
-}
-
 // Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
 // Outputs.
 func (r *RuleBuilder) Build(name string, desc string) {
-	r.build(name, desc, true)
+	r.build(name, desc)
 }
 
 var sandboxEnvOnceKey = NewOnceKey("sandbox_environment_variables")
 
-func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) {
+func (r *RuleBuilder) build(name string, desc string) {
 	name = ninjaNameEscape(name)
 
 	if len(r.missingDeps) > 0 {
@@ -514,7 +558,86 @@
 
 	commandString := strings.Join(commands, " && ")
 
-	if r.sbox {
+	if !r.sbox {
+		// If not using sbox the rule will run the command directly, put the hash of the
+		// list of input files in a comment at the end of the command line to ensure ninja
+		// reruns the rule when the list of input files changes.
+		commandString += " # hash of input list: " + hashSrcFiles(inputs)
+	}
+
+	if r.nsjail {
+		var nsjailCmd strings.Builder
+		nsjailPath := r.ctx.Config().PrebuiltBuildTool(r.ctx, "nsjail")
+		if !r.nsjailKeepGendir {
+			nsjailCmd.WriteString("rm -rf ")
+			nsjailCmd.WriteString(r.nsjailBasePath.String())
+			nsjailCmd.WriteRune(' ')
+			nsjailCmd.WriteString(r.outDir.String())
+			nsjailCmd.WriteString(" && ")
+		}
+		nsjailCmd.WriteString("mkdir -p ")
+		nsjailCmd.WriteString(r.nsjailBasePath.String())
+		nsjailCmd.WriteRune(' ')
+		nsjailCmd.WriteString(r.outDir.String())
+		nsjailCmd.WriteString(" && ")
+		nsjailCmd.WriteString(nsjailPath.String())
+		nsjailCmd.WriteRune(' ')
+		nsjailCmd.WriteString("-B $PWD/")
+		nsjailCmd.WriteString(r.nsjailBasePath.String())
+		nsjailCmd.WriteString(":nsjail_build_sandbox")
+
+		// out is mounted to $(genDir).
+		nsjailCmd.WriteString(" -B $PWD/")
+		nsjailCmd.WriteString(r.outDir.String())
+		nsjailCmd.WriteString(":nsjail_build_sandbox/out")
+
+		addBindMount := func(src, dst string) {
+			nsjailCmd.WriteString(" -R $PWD/")
+			nsjailCmd.WriteString(src)
+			nsjailCmd.WriteString(":nsjail_build_sandbox/")
+			nsjailCmd.WriteString(dst)
+		}
+
+		for _, input := range inputs {
+			addBindMount(input.String(), r.nsjailPathForInputRel(input))
+		}
+		for _, tool := range tools {
+			addBindMount(tool.String(), nsjailPathForToolRel(r.ctx, tool))
+		}
+		inputs = append(inputs, tools...)
+		for _, c := range r.commands {
+			for _, directory := range c.implicitDirectories {
+				addBindMount(directory.String(), directory.String())
+				// TODO(b/375551969): Add implicitDirectories to BuildParams, rather than relying on implicits
+				inputs = append(inputs, SourcePath{basePath: directory.base()})
+			}
+			for _, tool := range c.packagedTools {
+				addBindMount(tool.srcPath.String(), nsjailPathForPackagedToolRel(tool))
+				inputs = append(inputs, tool.srcPath)
+			}
+		}
+
+		// These five directories are necessary to run native host tools like /bin/bash and py3-cmd.
+		nsjailCmd.WriteString(" -R /bin")
+		nsjailCmd.WriteString(" -R /lib")
+		nsjailCmd.WriteString(" -R /lib64")
+		nsjailCmd.WriteString(" -R /dev")
+		nsjailCmd.WriteString(" -R /usr")
+
+		nsjailCmd.WriteString(" -m none:/tmp:tmpfs:size=1073741824") // 1GB, should be enough
+		nsjailCmd.WriteString(" -D nsjail_build_sandbox")
+		nsjailCmd.WriteString(" --disable_rlimits")
+		nsjailCmd.WriteString(" --skip_setsid") // ABFS relies on process-groups to track file operations
+		nsjailCmd.WriteString(" -q")
+		nsjailCmd.WriteString(" -- ")
+		nsjailCmd.WriteString("/bin/bash -c ")
+		nsjailCmd.WriteString(proptools.ShellEscape(commandString))
+
+		commandString = nsjailCmd.String()
+
+		inputs = append(inputs, nsjailPath)
+		inputs = append(inputs, r.nsjailImplicits...)
+	} else if r.sbox {
 		// If running the command inside sbox, write the rule data out to an sbox
 		// manifest.textproto.
 		manifest := sbox_proto.Manifest{}
@@ -658,30 +781,7 @@
 		if err != nil {
 			ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
 		}
-		if ninjaEscapeCommandString {
-			WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
-		} else {
-			// We need  to have a rule to write files that is
-			// defined on the RuleBuilder's pctx in order to
-			// write Ninja variables in the string.
-			// The WriteFileRule function above rule can only write
-			// raw strings because it is defined on the android
-			// package's pctx, and it can't access variables defined
-			// in another context.
-			r.ctx.Build(r.pctx, BuildParams{
-				Rule: r.ctx.Rule(r.pctx, "unescapedWriteFile", blueprint.RuleParams{
-					Command:        `rm -rf ${out} && cat ${out}.rsp > ${out}`,
-					Rspfile:        "${out}.rsp",
-					RspfileContent: "${content}",
-					Description:    "write file",
-				}, "content"),
-				Output:      r.sboxManifestPath,
-				Description: "write sbox manifest " + r.sboxManifestPath.Base(),
-				Args: map[string]string{
-					"content": string(pbText),
-				},
-			})
-		}
+		WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
 
 		// Generate a new string to use as the command line of the sbox rule.  This uses
 		// a RuleBuilderCommand as a convenience method of building the command line, then
@@ -734,11 +834,6 @@
 			rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
 			commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
 		}
-	} else {
-		// If not using sbox the rule will run the command directly, put the hash of the
-		// list of input files in a comment at the end of the command line to ensure ninja
-		// reruns the rule when the list of input files changes.
-		commandString += " # hash of input list: " + hashSrcFiles(inputs)
 	}
 
 	// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
@@ -780,10 +875,20 @@
 		pool = localPool
 	}
 
-	if ninjaEscapeCommandString {
-		commandString = proptools.NinjaEscape(commandString)
+	// If the command length is getting close to linux's maximum, dump it to a file, which allows
+	// for longer commands.
+	if len(commandString) > 100000 {
+		hasher := sha256.New()
+		hasher.Write([]byte(output.String()))
+		script := PathForOutput(r.ctx, "rule_builder_scripts", fmt.Sprintf("%x.sh", hasher.Sum(nil)))
+		commandString = "set -eu\n\n" + commandString + "\n"
+		WriteExecutableFileRuleVerbatim(r.ctx, script, commandString)
+		inputs = append(inputs, script)
+		commandString = script.String()
 	}
 
+	commandString = proptools.NinjaEscape(commandString)
+
 	args_vars := make([]string, len(r.args))
 	i := 0
 	for k, _ := range r.args {
@@ -819,16 +924,17 @@
 type RuleBuilderCommand struct {
 	rule *RuleBuilder
 
-	buf           strings.Builder
-	inputs        Paths
-	implicits     Paths
-	orderOnlys    Paths
-	validations   Paths
-	outputs       WritablePaths
-	depFiles      WritablePaths
-	tools         Paths
-	packagedTools []PackagingSpec
-	rspFiles      []rspFileAndPaths
+	buf                 strings.Builder
+	inputs              Paths
+	implicits           Paths
+	orderOnlys          Paths
+	validations         Paths
+	outputs             WritablePaths
+	depFiles            WritablePaths
+	tools               Paths
+	packagedTools       []PackagingSpec
+	rspFiles            []rspFileAndPaths
+	implicitDirectories DirectoryPaths
 }
 
 type rspFileAndPaths struct {
@@ -853,6 +959,10 @@
 	c.implicits = append(c.implicits, path)
 }
 
+func (c *RuleBuilderCommand) addImplicitDirectory(path DirectoryPath) {
+	c.implicitDirectories = append(c.implicitDirectories, path)
+}
+
 func (c *RuleBuilderCommand) addOrderOnly(path Path) {
 	checkPathNotNil(path)
 	c.orderOnlys = append(c.orderOnlys, path)
@@ -869,6 +979,8 @@
 			rel = filepath.Join(sboxSandboxBaseDir, rel)
 		}
 		return rel
+	} else if c.rule.nsjail {
+		return c.rule.nsjailPathForInputRel(path)
 	}
 	return path.String()
 }
@@ -894,6 +1006,10 @@
 		// Errors will be handled in RuleBuilder.Build where we have a context to report them
 		rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
 		return filepath.Join(sboxOutDir, rel)
+	} else if c.rule.nsjail {
+		// Errors will be handled in RuleBuilder.Build where we have a context to report them
+		rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
+		return filepath.Join(nsjailOutDir, rel)
 	}
 	return path.String()
 }
@@ -945,15 +1061,49 @@
 	return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
 }
 
+func nsjailPathForToolRel(ctx BuilderContext, path Path) string {
+	// Errors will be handled in RuleBuilder.Build where we have a context to report them
+	toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "")
+	relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
+	if isRelOutSoong {
+		// The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
+		return filepath.Join(nsjailToolsSubDir, "out", relOutSoong)
+	}
+	// The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
+	return filepath.Join(nsjailToolsSubDir, "src", path.String())
+}
+
+func (r *RuleBuilder) nsjailPathForInputRel(path Path) string {
+	rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String())
+	if isRelSboxOut {
+		return filepath.Join(nsjailOutDir, rel)
+	}
+	return path.String()
+}
+
+func (r *RuleBuilder) nsjailPathsForInputsRel(paths Paths) []string {
+	ret := make([]string, len(paths))
+	for i, path := range paths {
+		ret[i] = r.nsjailPathForInputRel(path)
+	}
+	return ret
+}
+
+func nsjailPathForPackagedToolRel(spec PackagingSpec) string {
+	return filepath.Join(nsjailToolsSubDir, "out", spec.relPathInPackage)
+}
+
 // PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
 // tool after copying it into the sandbox.  This can be used  on the RuleBuilder command line to
 // reference the tool.
 func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string {
-	if !c.rule.sboxTools {
-		panic("PathForPackagedTool() requires SandboxTools()")
+	if c.rule.sboxTools {
+		return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
+	} else if c.rule.nsjail {
+		return nsjailPathForPackagedToolRel(spec)
+	} else {
+		panic("PathForPackagedTool() requires SandboxTools() or Nsjail()")
 	}
-
-	return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
 }
 
 // PathForTool takes a path to a tool, which may be an output file or a source file, and returns
@@ -962,6 +1112,8 @@
 func (c *RuleBuilderCommand) PathForTool(path Path) string {
 	if c.rule.sbox && c.rule.sboxTools {
 		return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
+	} else if c.rule.nsjail {
+		return nsjailPathForToolRel(c.rule.ctx, path)
 	}
 	return path.String()
 }
@@ -976,6 +1128,12 @@
 			ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)))
 		}
 		return ret
+	} else if c.rule.nsjail {
+		var ret []string
+		for _, path := range paths {
+			ret = append(ret, nsjailPathForToolRel(c.rule.ctx, path))
+		}
+		return ret
 	}
 	return paths.Strings()
 }
@@ -983,20 +1141,22 @@
 // PackagedTool adds the specified tool path to the command line.  It can only be used with tool
 // sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
 func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
-	if !c.rule.sboxTools {
-		panic("PackagedTool() requires SandboxTools()")
-	}
-
 	c.packagedTools = append(c.packagedTools, spec)
-	c.Text(sboxPathForPackagedToolRel(spec))
+	if c.rule.sboxTools {
+		c.Text(sboxPathForPackagedToolRel(spec))
+	} else if c.rule.nsjail {
+		c.Text(nsjailPathForPackagedToolRel(spec))
+	} else {
+		panic("PackagedTool() requires SandboxTools() or Nsjail()")
+	}
 	return c
 }
 
 // ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
 // line.  It can only be used with tool sandboxing enabled by SandboxTools().
 func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
-	if !c.rule.sboxTools {
-		panic("ImplicitPackagedTool() requires SandboxTools()")
+	if !c.rule.sboxTools && !c.rule.nsjail {
+		panic("ImplicitPackagedTool() requires SandboxTools() or Nsjail()")
 	}
 
 	c.packagedTools = append(c.packagedTools, spec)
@@ -1006,8 +1166,8 @@
 // ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
 // line.  It can only be used with tool sandboxing enabled by SandboxTools().
 func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
-	if !c.rule.sboxTools {
-		panic("ImplicitPackagedTools() requires SandboxTools()")
+	if !c.rule.sboxTools && !c.rule.nsjail {
+		panic("ImplicitPackagedTools() requires SandboxTools() or Nsjail()")
 	}
 
 	c.packagedTools = append(c.packagedTools, specs...)
@@ -1165,6 +1325,16 @@
 	return c
 }
 
+// ImplicitDirectory adds the specified input directory to the dependencies without modifying the
+// command line. Added directories will be bind-mounted for the nsjail.
+func (c *RuleBuilderCommand) ImplicitDirectory(path DirectoryPath) *RuleBuilderCommand {
+	if !c.rule.nsjail {
+		panic("ImplicitDirectory() must be called after Nsjail()")
+	}
+	c.addImplicitDirectory(path)
+	return c
+}
+
 // GetImplicits returns the command's implicit inputs.
 func (c *RuleBuilderCommand) GetImplicits() Paths {
 	return c.implicits
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 6a8a964..e1a1e08 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -475,10 +475,9 @@
 		Srcs  []string
 		Flags []string
 
-		Restat              bool
-		Sbox                bool
-		Sbox_inputs         bool
-		Unescape_ninja_vars bool
+		Restat      bool
+		Sbox        bool
+		Sbox_inputs bool
 	}
 }
 
@@ -498,7 +497,7 @@
 
 	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, t.properties.Flags,
 		out, outDep, outDir,
-		manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs, t.properties.Unescape_ninja_vars,
+		manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs,
 		rspFile, rspFileContents, rspFile2, rspFileContents2)
 }
 
@@ -523,14 +522,14 @@
 	manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
 
 	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, nil, out, outDep, outDir,
-		manifestPath, true, false, false, false,
+		manifestPath, true, false, false,
 		rspFile, rspFileContents, rspFile2, rspFileContents2)
 }
 
 func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, validation Path,
 	flags []string,
 	out, outDep, outDir, manifestPath WritablePath,
-	restat, sbox, sboxInputs, unescapeNinjaVars bool,
+	restat, sbox, sboxInputs bool,
 	rspFile WritablePath, rspFileContents Paths, rspFile2 WritablePath, rspFileContents2 Paths) {
 
 	rule := NewRuleBuilder(pctx_ruleBuilderTest, ctx)
@@ -558,11 +557,7 @@
 		rule.Restat()
 	}
 
-	if unescapeNinjaVars {
-		rule.BuildWithUnescapedNinjaVars("rule", "desc")
-	} else {
-		rule.Build("rule", "desc")
-	}
+	rule.Build("rule", "desc")
 }
 
 var prepareForRuleBuilderTest = FixtureRegisterWithContext(func(ctx RegistrationContext) {
@@ -777,48 +772,3 @@
 		})
 	}
 }
-
-func TestRuleBuilderWithNinjaVarEscaping(t *testing.T) {
-	bp := `
-		rule_builder_test {
-			name: "foo_sbox_escaped",
-			flags: ["${cmdFlags}"],
-			sbox: true,
-			sbox_inputs: true,
-		}
-		rule_builder_test {
-			name: "foo_sbox_unescaped",
-			flags: ["${cmdFlags}"],
-			sbox: true,
-			sbox_inputs: true,
-			unescape_ninja_vars: true,
-		}
-	`
-	result := GroupFixturePreparers(
-		prepareForRuleBuilderTest,
-		FixtureWithRootAndroidBp(bp),
-	).RunTest(t)
-
-	escapedNinjaMod := result.ModuleForTests("foo_sbox_escaped", "").Output("sbox.textproto")
-	AssertStringEquals(t, "expected rule", "android/soong/android.rawFileCopy", escapedNinjaMod.Rule.String())
-	AssertStringDoesContain(
-		t,
-		"",
-		ContentFromFileRuleForTests(t, result.TestContext, escapedNinjaMod),
-		"${cmdFlags}",
-	)
-
-	unescapedNinjaMod := result.ModuleForTests("foo_sbox_unescaped", "").Rule("unescapedWriteFile")
-	AssertStringDoesContain(
-		t,
-		"",
-		unescapedNinjaMod.BuildParams.Args["content"],
-		"${cmdFlags}",
-	)
-	AssertStringDoesNotContain(
-		t,
-		"",
-		unescapedNinjaMod.BuildParams.Args["content"],
-		"$${cmdFlags}",
-	)
-}
diff --git a/android/sbom.go b/android/sbom.go
index 2a5499e..f2b9c0f 100644
--- a/android/sbom.go
+++ b/android/sbom.go
@@ -15,9 +15,7 @@
 package android
 
 import (
-	"io"
 	"path/filepath"
-	"strings"
 
 	"github.com/google/blueprint"
 )
@@ -55,21 +53,7 @@
 	if !ctx.Config().HasDeviceProduct() {
 		return
 	}
-	// Get all METADATA files and add them as implicit input
-	metadataFileListFile := PathForArbitraryOutput(ctx, ".module_paths", "METADATA.list")
-	f, err := ctx.Config().fs.Open(metadataFileListFile.String())
-	if err != nil {
-		panic(err)
-	}
-	b, err := io.ReadAll(f)
-	if err != nil {
-		panic(err)
-	}
-	allMetadataFiles := strings.Split(string(b), "\n")
-	implicits := []Path{metadataFileListFile}
-	for _, path := range allMetadataFiles {
-		implicits = append(implicits, PathForSource(ctx, path))
-	}
+	implicits := []Path{}
 	prodVars := ctx.Config().productVariables
 	buildFingerprintFile := PathForArbitraryOutput(ctx, "target", "product", String(prodVars.DeviceName), "build_fingerprint.txt")
 	implicits = append(implicits, buildFingerprintFile)
diff --git a/android/selects_test.go b/android/selects_test.go
index 90d7091..1397ed8 100644
--- a/android/selects_test.go
+++ b/android/selects_test.go
@@ -1031,6 +1031,54 @@
 				my_string_list: &[]string{"d2", "e2", "f2", "a1", "b1", "c1"},
 			},
 		},
+		{
+			name: "string list variables",
+			bp: `
+my_module_type {
+	name: "foo",
+	my_string_list: ["a"] + select(soong_config_variable("my_namespace", "my_var"), {
+		any @ my_var: my_var,
+		default: [],
+	}),
+}
+`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_var": "b c",
+				},
+			},
+			vendorVarTypes: map[string]map[string]string{
+				"my_namespace": {
+					"my_var": "string_list",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string_list: &[]string{"a", "b", "c"},
+			},
+		},
+		{
+			name: "string list variables don't match string matchers",
+			bp: `
+my_module_type {
+	name: "foo",
+	my_string_list: ["a"] + select(soong_config_variable("my_namespace", "my_var"), {
+		"foo": ["b"],
+		default: [],
+	}),
+}
+`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_var": "b c",
+				},
+			},
+			vendorVarTypes: map[string]map[string]string{
+				"my_namespace": {
+					"my_var": "string_list",
+				},
+			},
+			expectedError: `Expected all branches of a select on condition soong_config_variable\("my_namespace", "my_var"\) to have type string_list, found string`,
+		},
 	}
 
 	for _, tc := range testCases {
diff --git a/android/singleton.go b/android/singleton.go
index 913bf6a..0754b0c 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -64,6 +64,7 @@
 
 	VisitAllModulesBlueprint(visit func(blueprint.Module))
 	VisitAllModules(visit func(Module))
+	VisitAllModuleProxies(visit func(proxy ModuleProxy))
 	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
 
 	VisitDirectDeps(module Module, visit func(Module))
@@ -77,8 +78,10 @@
 
 	VisitAllModuleVariants(module Module, visit func(Module))
 
+	VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy))
+
 	PrimaryModule(module Module) Module
-	FinalModule(module Module) Module
+	IsFinalModule(module Module) bool
 
 	AddNinjaFileDeps(deps ...string)
 
@@ -193,7 +196,7 @@
 }
 
 // visitAdaptor wraps a visit function that takes an android.Module parameter into
-// a function that takes an blueprint.Module parameter and only calls the visit function if the
+// a function that takes a blueprint.Module parameter and only calls the visit function if the
 // blueprint.Module is an android.Module.
 func visitAdaptor(visit func(Module)) func(blueprint.Module) {
 	return func(module blueprint.Module) {
@@ -203,6 +206,16 @@
 	}
 }
 
+// visitProxyAdaptor wraps a visit function that takes an android.ModuleProxy parameter into
+// a function that takes a blueprint.ModuleProxy parameter.
+func visitProxyAdaptor(visit func(proxy ModuleProxy)) func(proxy blueprint.ModuleProxy) {
+	return func(module blueprint.ModuleProxy) {
+		visit(ModuleProxy{
+			module: module,
+		})
+	}
+}
+
 // predAdaptor wraps a pred function that takes an android.Module parameter
 // into a function that takes an blueprint.Module parameter and only calls the visit function if the
 // blueprint.Module is an android.Module, otherwise returns false.
@@ -224,6 +237,10 @@
 	s.SingletonContext.VisitAllModules(visitAdaptor(visit))
 }
 
+func (s *singletonContextAdaptor) VisitAllModuleProxies(visit func(proxy ModuleProxy)) {
+	s.SingletonContext.VisitAllModuleProxies(visitProxyAdaptor(visit))
+}
+
 func (s *singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
 	s.SingletonContext.VisitAllModulesIf(predAdaptor(pred), visitAdaptor(visit))
 }
@@ -248,12 +265,16 @@
 	s.SingletonContext.VisitAllModuleVariants(module, visitAdaptor(visit))
 }
 
+func (s *singletonContextAdaptor) VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) {
+	s.SingletonContext.VisitAllModuleVariantProxies(module, visitProxyAdaptor(visit))
+}
+
 func (s *singletonContextAdaptor) PrimaryModule(module Module) Module {
 	return s.SingletonContext.PrimaryModule(module).(Module)
 }
 
-func (s *singletonContextAdaptor) FinalModule(module Module) Module {
-	return s.SingletonContext.FinalModule(module).(Module)
+func (s *singletonContextAdaptor) IsFinalModule(module Module) bool {
+	return s.SingletonContext.IsFinalModule(module)
 }
 
 func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name string) []Module {
diff --git a/android/test_config.go b/android/test_config.go
index f251038..3609e6b 100644
--- a/android/test_config.go
+++ b/android/test_config.go
@@ -45,6 +45,7 @@
 			Platform_version_active_codenames:   []string{"S", "Tiramisu"},
 			DeviceSystemSdkVersions:             []string{"29", "30", "S"},
 			Platform_systemsdk_versions:         []string{"29", "30", "S", "Tiramisu"},
+			VendorApiLevel:                      stringPtr("202404"),
 			AAPTConfig:                          []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
 			AAPTPreferredConfig:                 stringPtr("xhdpi"),
 			AAPTCharacteristics:                 stringPtr("nosdcard"),
diff --git a/android/testing.go b/android/testing.go
index 196b22e..765839f 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -19,6 +19,7 @@
 	"fmt"
 	"path/filepath"
 	"regexp"
+	"runtime"
 	"sort"
 	"strings"
 	"sync"
@@ -189,6 +190,26 @@
 	})
 }
 
+// PrepareForNativeBridgeEnabled sets configuration with targets including:
+// - X86_64 (primary)
+// - X86 (secondary)
+// - Arm64 on X86_64 (native bridge)
+// - Arm on X86 (native bridge)
+var PrepareForNativeBridgeEnabled = FixtureModifyConfig(
+	func(config Config) {
+		config.Targets[Android] = []Target{
+			{Os: Android, Arch: Arch{ArchType: X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}},
+				NativeBridge: NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+			{Os: Android, Arch: Arch{ArchType: X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}},
+				NativeBridge: NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+			{Os: Android, Arch: Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}},
+				NativeBridge: NativeBridgeEnabled, NativeBridgeHostArchName: "x86_64", NativeBridgeRelativePath: "arm64"},
+			{Os: Android, Arch: Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
+				NativeBridge: NativeBridgeEnabled, NativeBridgeHostArchName: "x86", NativeBridgeRelativePath: "arm"},
+		}
+	},
+)
+
 func NewTestArchContext(config Config) *TestContext {
 	ctx := NewTestContext(config)
 	ctx.preDeps = append(ctx.preDeps, registerArchMutator)
@@ -197,8 +218,8 @@
 
 type TestContext struct {
 	*Context
-	preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
-	NameResolver                          *NameResolver
+	preArch, preDeps, postDeps, postApex, finalDeps []RegisterMutatorFunc
+	NameResolver                                    *NameResolver
 
 	// The list of singletons registered for the test.
 	singletons sortableComponents
@@ -229,6 +250,10 @@
 	ctx.postDeps = append(ctx.postDeps, f)
 }
 
+func (ctx *TestContext) PostApexMutators(f RegisterMutatorFunc) {
+	ctx.postApex = append(ctx.postApex, f)
+}
+
 func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) {
 	ctx.finalDeps = append(ctx.finalDeps, f)
 }
@@ -449,7 +474,7 @@
 func (ctx *TestContext) Register() {
 	globalOrder := globallyRegisteredComponentsOrder()
 
-	mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
+	mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.postApex, ctx.finalDeps)
 	// Ensure that the mutators used in the test are in the same order as they are used at runtime.
 	globalOrder.mutatorOrder.enforceOrdering(mutators)
 	mutators.registerAll(ctx.Context)
@@ -1127,11 +1152,6 @@
 	config.katiEnabled = true
 }
 
-func SetTrimmedApexEnabledForTests(config Config) {
-	config.productVariables.TrimmedApex = new(bool)
-	*config.productVariables.TrimmedApex = true
-}
-
 func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
 	t.Helper()
 	var p AndroidMkEntriesProvider
@@ -1148,6 +1168,30 @@
 	return entriesList
 }
 
+func AndroidMkInfoForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) *AndroidMkProviderInfo {
+	if runtime.GOOS == "darwin" && mod.(Module).base().Os() != Darwin {
+		// The AndroidMkInfo provider is not set in this case.
+		t.Skip("AndroidMkInfo provider is not set on darwin")
+	}
+
+	t.Helper()
+	var ok bool
+	if _, ok = mod.(AndroidMkProviderInfoProducer); !ok {
+		t.Errorf("module does not implement AndroidMkProviderInfoProducer: " + mod.Name())
+	}
+
+	info := OtherModuleProviderOrDefault(ctx, mod, AndroidMkInfoProvider)
+	aconfigUpdateAndroidMkInfos(ctx, mod.(Module), info)
+	info.PrimaryInfo.fillInEntries(ctx, mod)
+	if len(info.ExtraInfo) > 0 {
+		for _, ei := range info.ExtraInfo {
+			ei.fillInEntries(ctx, mod)
+		}
+	}
+
+	return info
+}
+
 func AndroidMkDataForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) AndroidMkData {
 	t.Helper()
 	var p AndroidMkDataProvider
diff --git a/android/updatable_modules.go b/android/updatable_modules.go
deleted file mode 100644
index d2595ed..0000000
--- a/android/updatable_modules.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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.
-
-package android
-
-// This file contains branch specific constants. They are stored in a separate
-// file to minimise the potential of merge conflicts between branches when
-// the code from the package is changed.
-
-// The default manifest version for all the modules on this branch.
-// This version code will be used only if there is no version field in the
-// module's apex_manifest.json. Release branches have their version injected
-// into apex_manifest.json by the tooling and will not use the version set
-// here. Developers can also set the version field locally in the
-// apex_manifest.json to build a module with a specific version.
-//
-// The value follows the schema from go/mainline-version-codes, and is chosen
-// based on the branch such that the builds from testing and development
-// branches will have a version higher than the prebuilts.
-// Versions per branch:
-// * x-dev           - xx0090000 (where xx is the branch SDK level)
-// * AOSP            - xx9990000
-// * x-mainline-prod - xx9990000
-// * master          - 990090000
-const DefaultUpdatableModuleVersion = "352090000"
diff --git a/android/util.go b/android/util.go
index 2d269b7..3fc4608 100644
--- a/android/util.go
+++ b/android/util.go
@@ -660,3 +660,13 @@
 	v, loaded := m.Map.LoadOrStore(key, value)
 	return v.(V), loaded
 }
+
+// AppendIfNotZero append the given value to the slice if it is not the zero value
+// for its type.
+func AppendIfNotZero[T comparable](slice []T, value T) []T {
+	var zeroValue T // Get the zero value of the type T
+	if value != zeroValue {
+		return append(slice, value)
+	}
+	return slice
+}
diff --git a/android/variable.go b/android/variable.go
index e0d512d..e012103 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -29,7 +29,7 @@
 
 func registerVariableBuildComponents(ctx RegistrationContext) {
 	ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("variable", VariableMutator).Parallel()
+		ctx.BottomUp("variable", VariableMutator)
 	})
 }
 
@@ -193,6 +193,9 @@
 			Required               []string
 			Vintf_fragment_modules []string
 		}
+		SelinuxIgnoreNeverallows struct {
+			Required []string
+		}
 	} `android:"arch_variant"`
 }
 
@@ -212,6 +215,7 @@
 	Platform_display_version_name          *string  `json:",omitempty"`
 	Platform_version_name                  *string  `json:",omitempty"`
 	Platform_sdk_version                   *int     `json:",omitempty"`
+	Platform_sdk_minor_version             *int     `json:",omitempty"`
 	Platform_sdk_codename                  *string  `json:",omitempty"`
 	Platform_sdk_version_or_codename       *string  `json:",omitempty"`
 	Platform_sdk_final                     *bool    `json:",omitempty"`
@@ -238,7 +242,8 @@
 	DeviceMaxPageSizeSupported            *string  `json:",omitempty"`
 	DeviceNoBionicPageSizeMacro           *bool    `json:",omitempty"`
 
-	VendorApiLevel *string `json:",omitempty"`
+	VendorApiLevel             *string `json:",omitempty"`
+	VendorApiLevelPropOverride *string `json:",omitempty"`
 
 	DeviceSecondaryArch        *string  `json:",omitempty"`
 	DeviceSecondaryArchVariant *string  `json:",omitempty"`
@@ -334,10 +339,21 @@
 	HWASanIncludePaths []string `json:",omitempty"`
 	HWASanExcludePaths []string `json:",omitempty"`
 
-	VendorPath    *string `json:",omitempty"`
-	OdmPath       *string `json:",omitempty"`
-	ProductPath   *string `json:",omitempty"`
-	SystemExtPath *string `json:",omitempty"`
+	VendorPath            *string `json:",omitempty"`
+	VendorDlkmPath        *string `json:",omitempty"`
+	BuildingVendorImage   *bool   `json:",omitempty"`
+	OdmPath               *string `json:",omitempty"`
+	BuildingOdmImage      *bool   `json:",omitempty"`
+	OdmDlkmPath           *string `json:",omitempty"`
+	ProductPath           *string `json:",omitempty"`
+	BuildingProductImage  *bool   `json:",omitempty"`
+	SystemExtPath         *string `json:",omitempty"`
+	SystemDlkmPath        *string `json:",omitempty"`
+	OemPath               *string `json:",omitempty"`
+	UserdataPath          *string `json:",omitempty"`
+	BuildingUserdataImage *bool   `json:",omitempty"`
+	RecoveryPath          *string `json:",omitempty"`
+	BuildingRecoveryImage *bool   `json:",omitempty"`
 
 	ClangTidy  *bool   `json:",omitempty"`
 	TidyChecks *string `json:",omitempty"`
@@ -395,10 +411,10 @@
 
 	Ndk_abis *bool `json:",omitempty"`
 
-	TrimmedApex                  *bool `json:",omitempty"`
-	ForceApexSymlinkOptimization *bool `json:",omitempty"`
-	CompressedApex               *bool `json:",omitempty"`
-	Aml_abis                     *bool `json:",omitempty"`
+	ForceApexSymlinkOptimization *bool   `json:",omitempty"`
+	CompressedApex               *bool   `json:",omitempty"`
+	DefaultApexPayloadType       *string `json:",omitempty"`
+	Aml_abis                     *bool   `json:",omitempty"`
 
 	DexpreoptGlobalConfig *string `json:",omitempty"`
 
@@ -428,9 +444,6 @@
 
 	EnforceProductPartitionInterface *bool `json:",omitempty"`
 
-	EnforceInterPartitionJavaSdkLibrary *bool    `json:",omitempty"`
-	InterPartitionJavaLibraryAllowList  []string `json:",omitempty"`
-
 	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
 
 	BoardKernelBinaries                []string `json:",omitempty"`
@@ -478,6 +491,8 @@
 
 	ProductManufacturer string `json:",omitempty"`
 	ProductBrand        string `json:",omitempty"`
+	ProductDevice       string `json:",omitempty"`
+	ProductModel        string `json:",omitempty"`
 
 	ReleaseVersion          string   `json:",omitempty"`
 	ReleaseAconfigValueSets []string `json:",omitempty"`
@@ -516,6 +531,7 @@
 	SystemExtPropFiles []string `json:",omitempty"`
 	ProductPropFiles   []string `json:",omitempty"`
 	OdmPropFiles       []string `json:",omitempty"`
+	VendorPropFiles    []string `json:",omitempty"`
 
 	EnableUffdGc *string `json:",omitempty"`
 
@@ -523,6 +539,19 @@
 	BoardAvbSystemAddHashtreeFooterArgs    []string `json:",omitempty"`
 	DeviceFrameworkCompatibilityMatrixFile []string `json:",omitempty"`
 	DeviceProductCompatibilityMatrixFile   []string `json:",omitempty"`
+
+	PartitionVarsForSoongMigrationOnlyDoNotUse PartitionVariables
+
+	ExtraAllowedDepsTxt *string `json:",omitempty"`
+
+	AdbKeys *string `json:",omitempty"`
+
+	DeviceMatrixFile       []string `json:",omitempty"`
+	ProductManifestFiles   []string `json:",omitempty"`
+	SystemManifestFile     []string `json:",omitempty"`
+	SystemExtManifestFiles []string `json:",omitempty"`
+	DeviceManifestFiles    []string `json:",omitempty"`
+	OdmManifestFiles       []string `json:",omitempty"`
 }
 
 type PartitionQualifiedVariablesType struct {
@@ -553,6 +582,19 @@
 	BoardAvbRollbackIndexLocation string `json:",omitempty"`
 }
 
+type BoardSuperPartitionGroupProps struct {
+	GroupSize     string   `json:",omitempty"`
+	PartitionList []string `json:",omitempty"`
+}
+
+type ChainedAvbPartitionProps struct {
+	Partitions            []string `json:",omitempty"`
+	Key                   string   `json:",omitempty"`
+	Algorithm             string   `json:",omitempty"`
+	RollbackIndex         string   `json:",omitempty"`
+	RollbackIndexLocation string   `json:",omitempty"`
+}
+
 type PartitionVariables struct {
 	ProductDirectory            string `json:",omitempty"`
 	PartitionQualifiedVariables map[string]PartitionQualifiedVariablesType
@@ -573,13 +615,80 @@
 	BoardExt4ShareDupBlocks        string `json:",omitempty"`
 	BoardFlashLogicalBlockSize     string `json:",omitempty"`
 	BoardFlashEraseBlockSize       string `json:",omitempty"`
-	BoardUsesRecoveryAsBoot        bool   `json:",omitempty"`
 	ProductUseDynamicPartitionSize bool   `json:",omitempty"`
 	CopyImagesForTargetFilesZip    bool   `json:",omitempty"`
 
-	BoardAvbEnable bool `json:",omitempty"`
+	VendorSecurityPatch string `json:",omitempty"`
 
-	ProductPackages []string `json:",omitempty"`
+	// Boot image stuff
+	BuildingRamdiskImage            bool     `json:",omitempty"`
+	ProductBuildBootImage           bool     `json:",omitempty"`
+	ProductBuildVendorBootImage     string   `json:",omitempty"`
+	ProductBuildInitBootImage       bool     `json:",omitempty"`
+	BoardUsesRecoveryAsBoot         bool     `json:",omitempty"`
+	BoardPrebuiltBootimage          string   `json:",omitempty"`
+	BoardPrebuiltInitBootimage      string   `json:",omitempty"`
+	BoardBootimagePartitionSize     string   `json:",omitempty"`
+	BoardInitBootimagePartitionSize string   `json:",omitempty"`
+	BoardBootHeaderVersion          string   `json:",omitempty"`
+	TargetKernelPath                string   `json:",omitempty"`
+	BoardUsesGenericKernelImage     bool     `json:",omitempty"`
+	BootSecurityPatch               string   `json:",omitempty"`
+	InitBootSecurityPatch           string   `json:",omitempty"`
+	BoardIncludeDtbInBootimg        bool     `json:",omitempty"`
+	InternalKernelCmdline           []string `json:",omitempty"`
+	InternalBootconfig              []string `json:",omitempty"`
+	InternalBootconfigFile          string   `json:",omitempty"`
+
+	// Super image stuff
+	ProductUseDynamicPartitions       bool                                     `json:",omitempty"`
+	ProductRetrofitDynamicPartitions  bool                                     `json:",omitempty"`
+	ProductBuildSuperPartition        bool                                     `json:",omitempty"`
+	BoardSuperPartitionSize           string                                   `json:",omitempty"`
+	BoardSuperPartitionMetadataDevice string                                   `json:",omitempty"`
+	BoardSuperPartitionBlockDevices   []string                                 `json:",omitempty"`
+	BoardSuperPartitionGroups         map[string]BoardSuperPartitionGroupProps `json:",omitempty"`
+	ProductVirtualAbOta               bool                                     `json:",omitempty"`
+	ProductVirtualAbOtaRetrofit       bool                                     `json:",omitempty"`
+	AbOtaUpdater                      bool                                     `json:",omitempty"`
+
+	// Avb (android verified boot) stuff
+	BoardAvbEnable          bool                                `json:",omitempty"`
+	BoardAvbAlgorithm       string                              `json:",omitempty"`
+	BoardAvbKeyPath         string                              `json:",omitempty"`
+	BoardAvbRollbackIndex   string                              `json:",omitempty"`
+	BuildingVbmetaImage     bool                                `json:",omitempty"`
+	ChainedVbmetaPartitions map[string]ChainedAvbPartitionProps `json:",omitempty"`
+
+	ProductPackages         []string `json:",omitempty"`
+	ProductPackagesDebug    []string `json:",omitempty"`
+	VendorLinkerConfigSrcs  []string `json:",omitempty"`
+	ProductLinkerConfigSrcs []string `json:",omitempty"`
+
+	BoardInfoFiles      []string `json:",omitempty"`
+	BootLoaderBoardName string   `json:",omitempty"`
+
+	ProductCopyFiles []string `json:",omitempty"`
+
+	BuildingSystemDlkmImage   bool     `json:",omitempty"`
+	SystemKernelModules       []string `json:",omitempty"`
+	SystemKernelBlocklistFile string   `json:",omitempty"`
+	SystemKernelLoadModules   []string `json:",omitempty"`
+	BuildingVendorDlkmImage   bool     `json:",omitempty"`
+	VendorKernelModules       []string `json:",omitempty"`
+	VendorKernelBlocklistFile string   `json:",omitempty"`
+	BuildingOdmDlkmImage      bool     `json:",omitempty"`
+	OdmKernelModules          []string `json:",omitempty"`
+	OdmKernelBlocklistFile    string   `json:",omitempty"`
+
+	VendorRamdiskKernelModules       []string `json:",omitempty"`
+	VendorRamdiskKernelBlocklistFile string   `json:",omitempty"`
+	VendorRamdiskKernelLoadModules   []string `json:",omitempty"`
+	VendorRamdiskKernelOptionsFile   string   `json:",omitempty"`
+
+	ProductFsverityGenerateMetadata bool `json:",omitempty"`
+
+	TargetScreenDensity string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
@@ -630,7 +739,6 @@
 		Malloc_zero_contents:         boolPtr(true),
 		Malloc_pattern_fill_contents: boolPtr(false),
 		Safestack:                    boolPtr(false),
-		TrimmedApex:                  boolPtr(false),
 		Build_from_text_stub:         boolPtr(false),
 
 		BootJars:     ConfiguredJarList{apexes: []string{}, jars: []string{}},
diff --git a/android/vendor_api_levels.go b/android/vendor_api_levels.go
new file mode 100644
index 0000000..4d364fd
--- /dev/null
+++ b/android/vendor_api_levels.go
@@ -0,0 +1,49 @@
+// 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 android
+
+import (
+	"fmt"
+	"strconv"
+)
+
+func getSdkVersionOfVendorApiLevel(apiLevel int) (int, bool) {
+	ok := true
+	sdkVersion := -1
+	switch apiLevel {
+	case 202404:
+		sdkVersion = 35
+	case 202504:
+		sdkVersion = 36
+	default:
+		ok = false
+	}
+	return sdkVersion, ok
+}
+
+func GetSdkVersionForVendorApiLevel(vendorApiLevel string) (ApiLevel, error) {
+	vendorApiLevelInt, err := strconv.Atoi(vendorApiLevel)
+	if err != nil {
+		return NoneApiLevel, fmt.Errorf("The vendor API level %q must be able to be parsed as an integer", vendorApiLevel)
+	}
+	if vendorApiLevelInt < 35 {
+		return uncheckedFinalApiLevel(vendorApiLevelInt), nil
+	}
+
+	if sdkInt, ok := getSdkVersionOfVendorApiLevel(vendorApiLevelInt); ok {
+		return uncheckedFinalApiLevel(sdkInt), nil
+	}
+	return NoneApiLevel, fmt.Errorf("Unknown vendor API level %q. Requires updating the map in vendor_api_level.go?", vendorApiLevel)
+}
diff --git a/android/vintf_data.go b/android/vintf_data.go
new file mode 100644
index 0000000..401f4d2
--- /dev/null
+++ b/android/vintf_data.go
@@ -0,0 +1,171 @@
+// 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 android
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+const (
+	deviceCmType          = "device_cm"
+	systemManifestType    = "system_manifest"
+	productManifestType   = "product_manifest"
+	systemExtManifestType = "system_ext_manifest"
+	vendorManifestType    = "vendor_manifest"
+	odmManifestType       = "odm_manifest"
+
+	defaultDcm               = "system/libhidl/vintfdata/device_compatibility_matrix.default.xml"
+	defaultSystemManifest    = "system/libhidl/vintfdata/manifest.xml"
+	defaultSystemExtManifest = "system/libhidl/vintfdata/system_ext_manifest.default.xml"
+)
+
+type vintfDataProperties struct {
+	// Optional name for the installed file. If unspecified it will be manifest.xml by default.
+	Filename *string
+
+	// Type of the vintf data type, the allowed type are device_compatibility_matrix, system_manifest,
+	// product_manifest, and system_ext_manifest.
+	Type *string
+}
+
+type vintfDataRule struct {
+	ModuleBase
+
+	properties vintfDataProperties
+
+	installDirPath InstallPath
+	outputFilePath Path
+	noAction       bool
+}
+
+func init() {
+	registerVintfDataComponents(InitRegistrationContext)
+}
+
+func registerVintfDataComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("vintf_data", vintfDataFactory)
+}
+
+// vintf_fragment module processes vintf fragment file and installs under etc/vintf/manifest.
+func vintfDataFactory() Module {
+	m := &vintfDataRule{}
+	m.AddProperties(
+		&m.properties,
+	)
+	InitAndroidArchModule(m, DeviceSupported, MultilibFirst)
+
+	return m
+}
+
+func (m *vintfDataRule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	builder := NewRuleBuilder(pctx, ctx)
+	gensrc := PathForModuleOut(ctx, "manifest.xml")
+	assembleVintfEnvs := []string{}
+	inputPaths := make(Paths, 0)
+
+	switch proptools.String(m.properties.Type) {
+	case deviceCmType:
+		assembleVintfEnvs = append(assembleVintfEnvs, fmt.Sprintf("BOARD_SYSTEMSDK_VERSIONS=\"%s\"", strings.Join(ctx.DeviceConfig().SystemSdkVersions(), " ")))
+
+		deviceMatrixs := PathsForSource(ctx, ctx.Config().DeviceMatrixFile())
+		if len(deviceMatrixs) > 0 {
+			inputPaths = append(inputPaths, deviceMatrixs...)
+		} else {
+			inputPaths = append(inputPaths, PathForSource(ctx, defaultDcm))
+		}
+	case systemManifestType:
+		assembleVintfEnvs = append(assembleVintfEnvs, fmt.Sprintf("PLATFORM_SYSTEMSDK_VERSIONS=\"%s\"", strings.Join(ctx.DeviceConfig().PlatformSystemSdkVersions(), " ")))
+
+		inputPaths = append(inputPaths, PathForSource(ctx, defaultSystemManifest))
+		systemManifestFiles := PathsForSource(ctx, ctx.Config().SystemManifestFile())
+		if len(systemManifestFiles) > 0 {
+			inputPaths = append(inputPaths, systemManifestFiles...)
+		}
+	case productManifestType:
+		productManifestFiles := PathsForSource(ctx, ctx.Config().ProductManifestFiles())
+		// Only need to generate the manifest if PRODUCT_MANIFEST_FILES not defined.
+		if len(productManifestFiles) == 0 {
+			m.noAction = true
+			return
+		}
+
+		inputPaths = append(inputPaths, productManifestFiles...)
+	case systemExtManifestType:
+		assembleVintfEnvs = append(assembleVintfEnvs, fmt.Sprintf("PROVIDED_VNDK_VERSIONS=\"%s\"", strings.Join(ctx.DeviceConfig().ExtraVndkVersions(), " ")))
+
+		inputPaths = append(inputPaths, PathForSource(ctx, defaultSystemExtManifest))
+		systemExtManifestFiles := PathsForSource(ctx, ctx.Config().SystemExtManifestFiles())
+		if len(systemExtManifestFiles) > 0 {
+			inputPaths = append(inputPaths, systemExtManifestFiles...)
+		}
+	case vendorManifestType:
+		assembleVintfEnvs = append(assembleVintfEnvs, fmt.Sprintf("BOARD_SEPOLICY_VERS=\"%s\"", ctx.DeviceConfig().BoardSepolicyVers()))
+		assembleVintfEnvs = append(assembleVintfEnvs, fmt.Sprintf("PRODUCT_ENFORCE_VINTF_MANIFEST=%t", *ctx.Config().productVariables.Enforce_vintf_manifest))
+		deviceManifestFiles := PathsForSource(ctx, ctx.Config().DeviceManifestFiles())
+		// Only need to generate the manifest if DEVICE_MANIFEST_FILE is defined.
+		if len(deviceManifestFiles) == 0 {
+			m.noAction = true
+			return
+		}
+
+		inputPaths = append(inputPaths, deviceManifestFiles...)
+	case odmManifestType:
+		assembleVintfEnvs = append(assembleVintfEnvs, "VINTF_IGNORE_TARGET_FCM_VERSION=true")
+		odmManifestFiles := PathsForSource(ctx, ctx.Config().OdmManifestFiles())
+		// Only need to generate the manifest if ODM_MANIFEST_FILES is defined.
+		if len(odmManifestFiles) == 0 {
+			m.noAction = true
+			return
+		}
+
+		inputPaths = append(inputPaths, odmManifestFiles...)
+	default:
+		panic(fmt.Errorf("For %s: The attribute 'type' value only allowed device_cm, system_manifest, product_manifest, system_ext_manifest!", ctx.Module().Name()))
+	}
+
+	// Process vintf fragment source file with assemble_vintf tool
+	builder.Command().
+		Flags(assembleVintfEnvs).
+		BuiltTool("assemble_vintf").
+		FlagWithArg("-i ", strings.Join(inputPaths.Strings(), ":")).
+		FlagWithOutput("-o ", gensrc)
+
+	builder.Build("assemble_vintf", "Process vintf data "+gensrc.String())
+
+	m.installDirPath = PathForModuleInstall(ctx, "etc", "vintf")
+	m.outputFilePath = gensrc
+
+	installFileName := "manifest.xml"
+	if filename := proptools.String(m.properties.Filename); filename != "" {
+		installFileName = filename
+	}
+
+	ctx.InstallFile(m.installDirPath, installFileName, gensrc)
+}
+
+// Make this module visible to AndroidMK so it can be referenced from modules defined from Android.mk files
+func (m *vintfDataRule) AndroidMkEntries() []AndroidMkEntries {
+	if m.noAction {
+		return []AndroidMkEntries{}
+	}
+
+	return []AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: OptionalPathForPath(m.outputFilePath),
+	}}
+}
diff --git a/android/vintf_fragment.go b/android/vintf_fragment.go
index 329eac9..a3343fd 100644
--- a/android/vintf_fragment.go
+++ b/android/vintf_fragment.go
@@ -25,7 +25,7 @@
 	properties vintfFragmentProperties
 
 	installDirPath InstallPath
-	outputFilePath OutputPath
+	outputFilePath Path
 }
 
 func init() {
@@ -44,7 +44,7 @@
 	m.AddProperties(
 		&m.properties,
 	)
-	InitAndroidArchModule(m, DeviceSupported, MultilibFirst)
+	InitAndroidArchModule(m, DeviceSupported, MultilibCommon)
 
 	return m
 }
@@ -64,7 +64,7 @@
 	builder.Build("assemble_vintf", "Process vintf fragment "+processedVintfFragment.String())
 
 	m.installDirPath = PathForModuleInstall(ctx, "etc", "vintf", "manifest")
-	m.outputFilePath = processedVintfFragment.OutputPath
+	m.outputFilePath = processedVintfFragment
 
 	ctx.InstallFile(m.installDirPath, processedVintfFragment.Base(), processedVintfFragment)
 }
diff --git a/android/vintf_fragment_test.go b/android/vintf_fragment_test.go
index 8be534c..cd90b98 100644
--- a/android/vintf_fragment_test.go
+++ b/android/vintf_fragment_test.go
@@ -29,7 +29,7 @@
 
 	testResult := PrepareForTestWithAndroidBuildComponents.RunTestWithBp(t, bp)
 
-	vintfFragmentBuild := testResult.TestContext.ModuleForTests("test_vintf_fragment", "android_arm64_armv8-a").Rule("assemble_vintf")
+	vintfFragmentBuild := testResult.TestContext.ModuleForTests("test_vintf_fragment", "android_common").Rule("assemble_vintf")
 	if !strings.Contains(vintfFragmentBuild.RuleParams.Command, "assemble_vintf") {
 		t.Errorf("Vintf_manifest build command does not process with assemble_vintf : " + vintfFragmentBuild.RuleParams.Command)
 	}
diff --git a/android/visibility.go b/android/visibility.go
index 61f2200..cee465e 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -268,7 +268,7 @@
 // The rule checker needs to be registered before defaults expansion to correctly check that
 // //visibility:xxx isn't combined with other packages in the same list in any one module.
 func RegisterVisibilityRuleChecker(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("visibilityRuleChecker", visibilityRuleChecker).Parallel()
+	ctx.BottomUp("visibilityRuleChecker", visibilityRuleChecker)
 }
 
 // Registers the function that gathers the visibility rules for each module.
@@ -278,12 +278,12 @@
 // the complete visibility lists from flat lists and after the package info is gathered to ensure
 // that default_visibility is available.
 func RegisterVisibilityRuleGatherer(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("visibilityRuleGatherer", visibilityRuleGatherer).Parallel()
+	ctx.BottomUp("visibilityRuleGatherer", visibilityRuleGatherer)
 }
 
 // This must be registered after the deps have been resolved.
 func RegisterVisibilityRuleEnforcer(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
+	ctx.BottomUp("visibilityRuleEnforcer", visibilityRuleEnforcer)
 }
 
 // Checks the per-module visibility rule lists before defaults expansion.
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 1a2eeca..277be0f 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -2098,8 +2098,9 @@
 }
 
 type mockFilesystemModuleProperties struct {
-	Partition_type *string
-	Deps           []string
+	Partition_type    *string
+	Deps              []string
+	Is_auto_generated *bool
 }
 
 type mockFilesystemModule struct {
diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go
index e238f8b..21baf6b 100644
--- a/androidmk/parser/parser_test.go
+++ b/androidmk/parser/parser_test.go
@@ -142,7 +142,7 @@
 		t.Fatalf("Unexpected errors while parsing: %v", errs)
 	}
 
-	if got[0].End() < got[len(got) -1].Pos() {
-		t.Errorf("Rule's end (%d) is smaller than directive that inside of rule's start (%v)\n", got[0].End(), got[len(got) -1].Pos())
+	if got[0].End() < got[len(got)-1].Pos() {
+		t.Errorf("Rule's end (%d) is smaller than directive that inside of rule's start (%v)\n", got[0].End(), got[len(got)-1].Pos())
 	}
 }
diff --git a/apex/Android.bp b/apex/Android.bp
index 4848513..870ca7e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -7,6 +7,7 @@
     pkgPath: "android/soong/apex",
     deps: [
         "blueprint",
+        "blueprint-bpmodify",
         "soong",
         "soong-aconfig",
         "soong-aconfig-codegen",
@@ -33,6 +34,7 @@
         "vndk.go",
     ],
     testSrcs: [
+        "aconfig_test.go",
         "apex_test.go",
         "bootclasspath_fragment_test.go",
         "classpath_element_test.go",
diff --git a/apex/aconfig_test.go b/apex/aconfig_test.go
index bb811f5..0eb8ef4 100644
--- a/apex/aconfig_test.go
+++ b/apex/aconfig_test.go
@@ -33,6 +33,7 @@
 })
 
 func TestValidationAcrossContainersExportedPass(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name string
 		bp   string
@@ -59,6 +60,7 @@
 					apex_available: [
 						"myapex",
 					],
+					compile_dex: true,
 				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_foo",
@@ -294,6 +296,7 @@
 	}
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			android.GroupFixturePreparers(
 				java.PrepareForTestWithJavaDefaultModules,
 				cc.PrepareForTestWithCcBuildComponents,
@@ -309,6 +312,7 @@
 }
 
 func TestValidationAcrossContainersNotExportedFail(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name          string
 		expectedError string
@@ -336,6 +340,7 @@
 					apex_available: [
 						"myapex",
 					],
+					compile_dex: true,
 				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_foo",
@@ -679,7 +684,7 @@
 				}
 				filegroup {
 						name: "my_filegroup_foo_srcjars",
-						srcs: [
+						device_common_srcs: [
 								":my_aconfig_declarations_group_foo{.srcjars}",
 						],
 				}
@@ -709,6 +714,7 @@
 	}
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			errorHandler := android.FixtureExpectsNoErrors
 			if test.expectedError != "" {
 				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
@@ -730,6 +736,7 @@
 }
 
 func TestValidationNotPropagateAcrossShared(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name string
 		bp   string
@@ -756,6 +763,7 @@
 					apex_available: [
 						"myapex",
 					],
+					compile_dex: true,
 				}
 				java_library {
 					name: "my_java_library_foo",
@@ -786,6 +794,7 @@
 	}
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			android.GroupFixturePreparers(
 				java.PrepareForTestWithJavaDefaultModules,
 				cc.PrepareForTestWithCcBuildComponents,
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 933682a..ec5ca15 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -297,7 +297,7 @@
 				fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", a.installedFilesFile.String())
 			}
 			for _, dist := range data.Entries.GetDistForGoals(a) {
-				fmt.Fprintf(w, dist)
+				fmt.Fprintln(w, dist)
 			}
 
 			distCoverageFiles(w, "ndk_apis_usedby_apex", a.nativeApisUsedByModuleFile.String())
diff --git a/apex/apex.go b/apex/apex.go
index d3c1ed0..9fdb2a2 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -20,15 +20,18 @@
 	"fmt"
 	"path/filepath"
 	"regexp"
+	"slices"
 	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 	"android/soong/bpf"
 	"android/soong/cc"
+	"android/soong/dexpreopt"
 	prebuilt_etc "android/soong/etc"
 	"android/soong/filesystem"
 	"android/soong/java"
@@ -54,22 +57,16 @@
 }
 
 func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
+	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).UsesReverseDependencies()
 }
 
 func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.TopDown("apex_info", apexInfoMutator).Parallel()
-	ctx.BottomUp("apex_unique", apexUniqueVariationsMutator).Parallel()
-	ctx.BottomUp("apex_test_for_deps", apexTestForDepsMutator).Parallel()
-	ctx.BottomUp("apex_test_for", apexTestForMutator).Parallel()
+	ctx.TopDown("apex_info", apexInfoMutator)
+	ctx.BottomUp("apex_unique", apexUniqueVariationsMutator)
 	// Run mark_platform_availability before the apexMutator as the apexMutator needs to know whether
 	// it should create a platform variant.
-	ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
+	ctx.BottomUp("mark_platform_availability", markPlatformAvailability)
 	ctx.Transition("apex", &apexTransitionMutator{})
-	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
-	ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
-	// Register after apex_info mutator so that it can use ApexVariationName
-	ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel()
 }
 
 type apexBundleProperties struct {
@@ -107,11 +104,10 @@
 	Rros []string
 
 	// List of bootclasspath fragments that are embedded inside this APEX bundle.
-	Bootclasspath_fragments []string
+	Bootclasspath_fragments proptools.Configurable[[]string]
 
 	// List of systemserverclasspath fragments that are embedded inside this APEX bundle.
-	Systemserverclasspath_fragments        proptools.Configurable[[]string]
-	ResolvedSystemserverclasspathFragments []string `blueprint:"mutated"`
+	Systemserverclasspath_fragments proptools.Configurable[[]string]
 
 	// List of java libraries that are embedded inside this APEX bundle.
 	Java_libs []string
@@ -202,7 +198,7 @@
 	Native_shared_libs proptools.Configurable[[]string]
 
 	// List of JNI libraries that are embedded inside this APEX.
-	Jni_libs []string
+	Jni_libs proptools.Configurable[[]string]
 
 	// List of rust dyn libraries that are embedded inside this APEX.
 	Rust_dyn_libs []string
@@ -286,9 +282,9 @@
 }
 
 // Merge combines another ApexNativeDependencies into this one
-func (a *ResolvedApexNativeDependencies) Merge(ctx android.BaseMutatorContext, b ApexNativeDependencies) {
+func (a *ResolvedApexNativeDependencies) Merge(ctx android.BaseModuleContext, b ApexNativeDependencies) {
 	a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs.GetOrDefault(ctx, nil)...)
-	a.Jni_libs = append(a.Jni_libs, b.Jni_libs...)
+	a.Jni_libs = append(a.Jni_libs, b.Jni_libs.GetOrDefault(ctx, nil)...)
 	a.Rust_dyn_libs = append(a.Rust_dyn_libs, b.Rust_dyn_libs...)
 	a.Binaries = append(a.Binaries, b.Binaries.GetOrDefault(ctx, nil)...)
 	a.Tests = append(a.Tests, b.Tests...)
@@ -390,7 +386,7 @@
 
 	// Apex Container package name. Override value for attribute package:name in
 	// AndroidManifest.xml
-	Package_name string
+	Package_name proptools.Configurable[string]
 
 	// A txt file containing list of files that are allowed to be included in this APEX.
 	Allowed_files *string `android:"path"`
@@ -431,6 +427,7 @@
 	archProperties        apexArchBundleProperties
 	overridableProperties overridableProperties
 	vndkProperties        apexVndkProperties // only for apex_vndk modules
+	testProperties        apexTestProperties // only for apex_test modules
 
 	///////////////////////////////////////////////////////////////////////////////////////////
 	// Inputs
@@ -458,6 +455,12 @@
 	// GenerateAndroidBuildActions.
 	filesInfo []apexFile
 
+	// List of files that were excluded by the unwanted_transitive_deps property.
+	unwantedTransitiveFilesInfo []apexFile
+
+	// List of files that were excluded due to conflicts with other variants of the same module.
+	duplicateTransitiveFilesInfo []apexFile
+
 	// List of other module names that should be installed when this APEX gets installed (LOCAL_REQUIRED_MODULES).
 	makeModulesToInstall []string
 
@@ -501,7 +504,7 @@
 
 	// Text file having the list of individual files that are included in this APEX. Used for
 	// debugging purpose.
-	installedFilesFile android.WritablePath
+	installedFilesFile android.Path
 
 	// List of module names that this APEX is including (to be shown via *-deps-info target).
 	// Used for debugging purpose.
@@ -576,7 +579,7 @@
 	dataPaths                 []android.DataPath // becomes LOCAL_TEST_DATA
 
 	jacocoReportClassesFile android.Path     // only for javalibs and apps
-	lintDepSets             java.LintDepSets // only for javalibs and apps
+	lintInfo                *java.LintInfo   // only for javalibs and apps
 	certificate             java.Certificate // only for apps
 	overriddenPackageName   string           // only for apps
 
@@ -604,9 +607,6 @@
 		}
 		ret.moduleDir = ctx.OtherModuleDir(module)
 		ret.partition = module.PartitionTag(ctx.DeviceConfig())
-		ret.requiredModuleNames = module.RequiredModuleNames(ctx)
-		ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
-		ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
 		ret.multilib = module.Target().Arch.ArchType.Multilib
 	}
 	return ret
@@ -720,7 +720,6 @@
 	androidAppTag  = &dependencyTag{name: "androidApp", payload: true}
 	bpfTag         = &dependencyTag{name: "bpf", payload: true}
 	certificateTag = &dependencyTag{name: "certificate"}
-	dclaTag        = &dependencyTag{name: "dcla"}
 	executableTag  = &dependencyTag{name: "executable", payload: true}
 	fsTag          = &dependencyTag{name: "filesystem", payload: true}
 	bcpfTag        = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
@@ -733,7 +732,6 @@
 	prebuiltTag     = &dependencyTag{name: "prebuilt", payload: true}
 	rroTag          = &dependencyTag{name: "rro", payload: true}
 	sharedLibTag    = &dependencyTag{name: "sharedLib", payload: true}
-	testForTag      = &dependencyTag{name: "test for"}
 	testTag         = &dependencyTag{name: "test", payload: true}
 	shBinaryTag     = &dependencyTag{name: "shBinary", payload: true}
 )
@@ -827,6 +825,7 @@
 		deps.Merge(ctx, a.properties.Multilib.Both)
 		deps.Merge(ctx, ApexNativeDependencies{
 			Native_shared_libs: a.properties.Native_shared_libs,
+			Rust_dyn_libs:      a.properties.Rust_dyn_libs,
 			Tests:              a.properties.Tests,
 			Jni_libs:           a.properties.Jni_libs,
 		})
@@ -839,7 +838,7 @@
 			deps.Merge(ctx, ApexNativeDependencies{
 				Native_shared_libs: proptools.NewConfigurable[[]string](nil, nil),
 				Tests:              nil,
-				Jni_libs:           nil,
+				Jni_libs:           proptools.NewConfigurable[[]string](nil, nil),
 				Binaries:           a.properties.Binaries,
 			})
 		}
@@ -881,13 +880,11 @@
 		}
 	}
 
-	a.properties.ResolvedSystemserverclasspathFragments = a.properties.Systemserverclasspath_fragments.GetOrDefault(ctx, nil)
-
 	// Common-arch dependencies come next
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
 	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.properties.Rros...)
-	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...)
-	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.ResolvedSystemserverclasspathFragments...)
+	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments.GetOrDefault(ctx, nil)...)
+	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments.GetOrDefault(ctx, nil)...)
 	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
 	ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
@@ -937,33 +934,6 @@
 	}
 }
 
-func apexDCLADepsMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Config().ApexTrimEnabled() {
-		return
-	}
-	if a, ok := mctx.Module().(*apexBundle); ok && a.overridableProperties.Trim_against != nil {
-		commonVariation := mctx.Config().AndroidCommonTarget.Variations()
-		mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(a.overridableProperties.Trim_against))
-	} else if o, ok := mctx.Module().(*OverrideApex); ok {
-		for _, p := range o.GetProperties() {
-			properties, ok := p.(*overridableProperties)
-			if !ok {
-				continue
-			}
-			if properties.Trim_against != nil {
-				commonVariation := mctx.Config().AndroidCommonTarget.Variations()
-				mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(properties.Trim_against))
-			}
-		}
-	}
-}
-
-type DCLAInfo struct {
-	ProvidedLibs []string
-}
-
-var DCLAInfoProvider = blueprint.NewMutatorProvider[DCLAInfo]("apex_info")
-
 var _ ApexInfoMutator = (*apexBundle)(nil)
 
 func (a *apexBundle) ApexVariationName() string {
@@ -1013,25 +983,7 @@
 		return true
 	}
 
-	// Records whether a certain module is included in this apexBundle via direct dependency or
-	// inndirect dependency.
-	contents := make(map[string]android.ApexMembership)
-	mctx.WalkDeps(func(child, parent android.Module) bool {
-		if !continueApexDepsWalk(child, parent) {
-			return false
-		}
-		// If the parent is apexBundle, this child is directly depended.
-		_, directDep := parent.(*apexBundle)
-		depName := mctx.OtherModuleName(child)
-		contents[depName] = contents[depName].Add(directDep)
-		return true
-	})
-
-	// The membership information is saved for later access
-	apexContents := android.NewApexContents(contents)
-	android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{
-		Contents: apexContents,
-	})
+	android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{})
 
 	minSdkVersion := a.minSdkVersion(mctx)
 	// When min_sdk_version is not set, the apex is built against FutureApiLevel.
@@ -1059,8 +1011,6 @@
 		Updatable:         a.Updatable(),
 		UsePlatformApis:   a.UsePlatformApis(),
 		InApexVariants:    []string{apexVariationName},
-		InApexModules:     []string{a.Name()}, // could be com.mycompany.android.foo
-		ApexContents:      []*android.ApexContents{apexContents},
 		TestApexes:        testApexes,
 		BaseApexName:      mctx.ModuleName(),
 		ApexAvailableName: proptools.String(a.properties.Apex_available_name),
@@ -1072,12 +1022,6 @@
 		child.(android.ApexModule).BuildForApex(apexInfo) // leave a mark!
 		return true
 	})
-
-	if a.dynamic_common_lib_apex() {
-		android.SetProvider(mctx, DCLAInfoProvider, DCLAInfo{
-			ProvidedLibs: a.properties.Native_shared_libs.GetOrDefault(mctx, nil),
-		})
-	}
 }
 
 type ApexInfoMutator interface {
@@ -1105,51 +1049,6 @@
 	if am, ok := mctx.Module().(android.ApexModule); ok {
 		android.ApexInfoMutator(mctx, am)
 	}
-	enforceAppUpdatability(mctx)
-}
-
-// apexStrictUpdatibilityLintMutator propagates strict_updatability_linting to transitive deps of a mainline module
-// This check is enforced for updatable modules
-func apexStrictUpdatibilityLintMutator(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled(mctx) {
-		return
-	}
-	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
-			// These libs are transitive deps of the sdk. See java/sdk.go:decodeSdkDep
-			// Only skip libraries defined in libcore root, not subdirectories
-			if mctx.OtherModuleDir(child) == "libcore" {
-				// Do not traverse transitive deps of libcore/ libs
-				return false
-			}
-			if android.InList(child.Name(), skipLintJavalibAllowlist) {
-				return false
-			}
-			if lintable, ok := child.(java.LintDepSetsIntf); ok {
-				lintable.SetStrictUpdatabilityLinting(true)
-			}
-			// visit transitive deps
-			return true
-		})
-	}
-}
-
-// enforceAppUpdatability propagates updatable=true to apps of updatable apexes
-func enforceAppUpdatability(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled(mctx) {
-		return
-	}
-	if apex, ok := mctx.Module().(*apexBundle); ok && apex.Updatable() {
-		// checking direct deps is sufficient since apex->apk is a direct edge, even when inherited via apex_defaults
-		mctx.VisitDirectDeps(func(module android.Module) {
-			// ignore android_test_app
-			if app, ok := module.(*java.AndroidApp); ok {
-				app.SetUpdatable(true)
-			}
-		})
-	}
 }
 
 // TODO: b/215736885 Whittle the denylist
@@ -1196,20 +1095,9 @@
 		"test_jitzygote_com.android.art",
 		// go/keep-sorted end
 	}
-
-	// TODO: b/215736885 Remove this list
-	skipLintJavalibAllowlist = []string{
-		"conscrypt.module.platform.api.stubs",
-		"conscrypt.module.public.api.stubs",
-		"conscrypt.module.public.api.stubs.system",
-		"conscrypt.module.public.api.stubs.module_lib",
-		"framework-media.stubs",
-		"framework-media.stubs.system",
-		"framework-media.stubs.module_lib",
-	}
 )
 
-func (a *apexBundle) checkStrictUpdatabilityLinting(mctx android.TopDownMutatorContext) bool {
+func (a *apexBundle) checkStrictUpdatabilityLinting(mctx android.ModuleContext) bool {
 	// The allowlist contains the base apex name, so use that instead of the ApexVariationName
 	return a.Updatable() && !android.InList(mctx.ModuleName(), skipStrictUpdatabilityLintAllowlist)
 }
@@ -1226,40 +1114,6 @@
 	}
 }
 
-// apexTestForDepsMutator checks if this module is a test for an apex. If so, add a dependency on
-// the apex in order to retrieve its contents later.
-// TODO(jiyong): move this to android/apex.go?
-func apexTestForDepsMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled(mctx) {
-		return
-	}
-	if am, ok := mctx.Module().(android.ApexModule); ok {
-		if testFor := am.TestFor(); len(testFor) > 0 {
-			mctx.AddFarVariationDependencies([]blueprint.Variation{
-				{Mutator: "os", Variation: am.Target().OsVariation()},
-				{"arch", "common"},
-			}, testForTag, testFor...)
-		}
-	}
-}
-
-// TODO(jiyong): move this to android/apex.go?
-func apexTestForMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled(mctx) {
-		return
-	}
-	if _, ok := mctx.Module().(android.ApexModule); ok {
-		var contents []*android.ApexContents
-		for _, testFor := range mctx.GetDirectDepsWithTag(testForTag) {
-			abInfo, _ := android.OtherModuleProvider(mctx, testFor, android.ApexBundleInfoProvider)
-			contents = append(contents, abInfo.Contents)
-		}
-		android.SetProvider(mctx, android.ApexTestForInfoProvider, android.ApexTestForInfo{
-			ApexContents: contents,
-		})
-	}
-}
-
 // markPlatformAvailability marks whether or not a module can be available to platform. A module
 // cannot be available to platform if 1) it is explicitly marked as not available (i.e.
 // "//apex_available:platform" is absent) or 2) it depends on another module that isn't (or can't
@@ -1358,17 +1212,6 @@
 	return true
 }
 
-// See android.UpdateDirectlyInAnyApex
-// TODO(jiyong): move this to android/apex.go?
-func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled(mctx) {
-		return
-	}
-	if am, ok := mctx.Module().(android.ApexModule); ok {
-		android.UpdateDirectlyInAnyApex(mctx, am)
-	}
-}
-
 const (
 	// File extensions of an APEX for different packaging methods
 	imageApexSuffix  = ".apex"
@@ -1444,6 +1287,23 @@
 	return proptools.BoolDefault(a.properties.Platform_apis, false)
 }
 
+type apexValidationType int
+
+const (
+	hostApexVerifier apexValidationType = iota
+	apexSepolicyTests
+)
+
+func (a *apexBundle) skipValidation(validationType apexValidationType) bool {
+	switch validationType {
+	case hostApexVerifier:
+		return proptools.Bool(a.testProperties.Skip_validations.Host_apex_verifier)
+	case apexSepolicyTests:
+		return proptools.Bool(a.testProperties.Skip_validations.Apex_sepolicy_tests)
+	}
+	panic("Unknown validation type")
+}
+
 // getCertString returns the name of the cert that should be used to sign this APEX. This is
 // basically from the "certificate" property, but could be overridden by the device config.
 func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
@@ -1481,19 +1341,6 @@
 	return proptools.BoolDefault(a.properties.Dynamic_common_lib_apex, false)
 }
 
-// See the list of libs to trim
-func (a *apexBundle) libs_to_trim(ctx android.ModuleContext) []string {
-	dclaModules := ctx.GetDirectDepsWithTag(dclaTag)
-	if len(dclaModules) > 1 {
-		panic(fmt.Errorf("expected exactly at most one dcla dependency, got %d", len(dclaModules)))
-	}
-	if len(dclaModules) > 0 {
-		DCLAInfo, _ := android.OtherModuleProvider(ctx, dclaModules[0], DCLAInfoProvider)
-		return DCLAInfo.ProvidedLibs
-	}
-	return []string{}
-}
-
 // These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
 // members) can be sanitized, either forcibly, or by the global configuration. For some of the
 // sanitizers, extra dependencies can be forcibly added as well.
@@ -1650,7 +1497,6 @@
 	BaseModuleName() string
 	DexJarBuildPath(ctx android.ModuleErrorfContext) java.OptionalDexJarPath
 	JacocoReportClassesFile() android.Path
-	LintDepSets() java.LintDepSets
 	Stem() string
 }
 
@@ -1670,7 +1516,9 @@
 	dirInApex := "javalib"
 	af := newApexFile(ctx, dexImplementationJar, module.BaseModuleName(), dirInApex, javaSharedLib, module)
 	af.jacocoReportClassesFile = module.JacocoReportClassesFile()
-	af.lintDepSets = module.LintDepSets()
+	if lintInfo, ok := android.OtherModuleProvider(ctx, module, java.LintProvider); ok {
+		af.lintInfo = lintInfo
+	}
 	af.customStem = module.Stem() + ".jar"
 	// TODO: b/338641779 - Remove special casing of sdkLibrary once bcpf and sscpf depends
 	// on the implementation library
@@ -1708,7 +1556,6 @@
 	JacocoReportClassesFile() android.Path
 	Certificate() java.Certificate
 	BaseModuleName() string
-	LintDepSets() java.LintDepSets
 	PrivAppAllowlist() android.OptionalPath
 }
 
@@ -1744,7 +1591,9 @@
 
 	af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp)
 	af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
-	af.lintDepSets = aapp.LintDepSets()
+	if lintInfo, ok := android.OtherModuleProvider(ctx, aapp, java.LintProvider); ok {
+		af.lintInfo = lintInfo
+	}
 	af.certificate = aapp.Certificate()
 
 	if app, ok := aapp.(interface {
@@ -1795,7 +1644,7 @@
 // visited module, the `do` callback is executed. Returning true in the callback continues the visit
 // to the child modules. Returning false makes the visit to continue in the sibling or the parent
 // modules. This is used in check* functions below.
-func (a *apexBundle) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
+func (a *apexBundle) WalkPayloadDeps(ctx android.BaseModuleContext, do android.PayloadDepsCallback) {
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		am, ok := child.(android.ApexModule)
 		if !ok || !am.CanHaveApexVariants() {
@@ -1822,6 +1671,32 @@
 	})
 }
 
+func (a *apexBundle) WalkPayloadDepsProxy(ctx android.BaseModuleContext,
+	do func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool) {
+	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+		if !android.OtherModuleProviderOrDefault(ctx, child, android.CommonModuleInfoKey).CanHaveApexVariants {
+			return false
+		}
+		// Filter-out unwanted depedendencies
+		depTag := ctx.OtherModuleDependencyTag(child)
+		if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
+			return false
+		}
+		if dt, ok := depTag.(*dependencyTag); ok && !dt.payload {
+			return false
+		}
+		if depTag == android.RequiredDepTag {
+			return false
+		}
+
+		ai, _ := android.OtherModuleProvider(ctx, child, android.ApexInfoProvider)
+		externalDep := !android.InList(ctx.ModuleName(), ai.InApexVariants)
+
+		// Visit actually
+		return do(ctx, parent, child, externalDep)
+	})
+}
+
 // filesystem type of the apex_payload.img inside the APEX. Currently, ext4 and f2fs are supported.
 type fsType int
 
@@ -1877,7 +1752,8 @@
 }
 
 func (a *apexBundle) setPayloadFsType(ctx android.ModuleContext) {
-	switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
+	defaultFsType := ctx.Config().DefaultApexPayloadType()
+	switch proptools.StringDefault(a.properties.Payload_fs_type, defaultFsType) {
 	case ext4FsType:
 		a.payloadFsType = ext4
 	case f2fsFsType:
@@ -1890,7 +1766,13 @@
 }
 
 func (a *apexBundle) isCompressable() bool {
-	return proptools.BoolDefault(a.overridableProperties.Compressible, false) && !a.testApex
+	if a.testApex {
+		return false
+	}
+	if a.payloadFsType == erofs {
+		return false
+	}
+	return proptools.Bool(a.overridableProperties.Compressible)
 }
 
 func (a *apexBundle) commonBuildActions(ctx android.ModuleContext) bool {
@@ -1899,6 +1781,7 @@
 	a.CheckMinSdkVersion(ctx)
 	a.checkStaticLinkingToStubLibraries(ctx)
 	a.checkStaticExecutables(ctx)
+	a.enforceAppUpdatability(ctx)
 	if len(a.properties.Tests) > 0 && !a.testApex {
 		ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
 		return false
@@ -1921,6 +1804,14 @@
 
 	// visitor skips these from this list of module names
 	unwantedTransitiveDeps []string
+
+	// unwantedTransitiveFilesInfo contains files that would have been in the apex
+	// except that they were listed in unwantedTransitiveDeps.
+	unwantedTransitiveFilesInfo []apexFile
+
+	// duplicateTransitiveFilesInfo contains files that would ahve been in the apex
+	// except that another variant of the same module was already in the apex.
+	duplicateTransitiveFilesInfo []apexFile
 }
 
 func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) {
@@ -1931,6 +1822,7 @@
 		// Needs additional verification for the resulting APEX to ensure that skipped artifacts don't make problems.
 		// For example, DT_NEEDED modules should be found within the APEX unless they are marked in `requiredNativeLibs`.
 		if f.transitiveDep && f.module != nil && android.InList(mctx.OtherModuleName(f.module), vctx.unwantedTransitiveDeps) {
+			vctx.unwantedTransitiveFilesInfo = append(vctx.unwantedTransitiveFilesInfo, f)
 			continue
 		}
 		dest := filepath.Join(f.installDir, f.builtFile.Base())
@@ -1941,6 +1833,8 @@
 				mctx.ModuleErrorf("apex file %v is provided by two different files %v and %v",
 					dest, e.builtFile, f.builtFile)
 				return
+			} else {
+				vctx.duplicateTransitiveFilesInfo = append(vctx.duplicateTransitiveFilesInfo, f)
 			}
 			// If a module is directly included and also transitively depended on
 			// consider it as directly included.
@@ -1955,6 +1849,7 @@
 	for _, v := range encountered {
 		vctx.filesInfo = append(vctx.filesInfo, v)
 	}
+
 	sort.Slice(vctx.filesInfo, func(i, j int) bool {
 		// Sort by destination path so as to ensure consistent ordering even if the source of the files
 		// changes.
@@ -1962,12 +1857,38 @@
 	})
 }
 
-func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, child, parent blueprint.Module) bool {
+// enforcePartitionTagOnApexSystemServerJar checks that the partition tags of an apex system server jar  matches
+// the partition tags of the top-level apex.
+// e.g. if the top-level apex sets system_ext_specific to true, the javalib must set this property to true as well.
+// This check ensures that the dexpreopt artifacts of the apex system server jar is installed in the same partition
+// as the apex.
+func (a *apexBundle) enforcePartitionTagOnApexSystemServerJar(ctx android.ModuleContext) {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	ctx.VisitDirectDepsProxyWithTag(sscpfTag, func(child android.ModuleProxy) {
+		info, ok := android.OtherModuleProvider(ctx, child, java.LibraryNameToPartitionInfoProvider)
+		if !ok {
+			ctx.ModuleErrorf("Could not find partition info of apex system server jars.")
+		}
+		apexPartition := ctx.Module().PartitionTag(ctx.DeviceConfig())
+		for javalib, javalibPartition := range info.LibraryNameToPartition {
+			if !global.AllApexSystemServerJars(ctx).ContainsJar(javalib) {
+				continue // not an apex system server jar
+			}
+			if apexPartition != javalibPartition {
+				ctx.ModuleErrorf(`
+%s is an apex systemserver jar, but its partition does not match the partition of its containing apex. Expected %s, Got %s`,
+					javalib, apexPartition, javalibPartition)
+			}
+		}
+	})
+}
+
+func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, child, parent android.Module) bool {
 	depTag := ctx.OtherModuleDependencyTag(child)
 	if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
 		return false
 	}
-	if mod, ok := child.(android.Module); ok && !mod.Enabled(ctx) {
+	if !child.Enabled(ctx) {
 		return false
 	}
 	depName := ctx.OtherModuleName(child)
@@ -2157,8 +2078,7 @@
 			af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
 			af.transitiveDep = true
 
-			abInfo, _ := android.ModuleProvider(ctx, android.ApexBundleInfoProvider)
-			if !abInfo.Contents.DirectlyInApex(depName) && (ch.IsStubs() || ch.HasStubsVariants()) {
+			if ch.IsStubs() || ch.HasStubsVariants() {
 				// If the dependency is a stubs lib, don't include it in this APEX,
 				// but make sure that the lib is installed on the device.
 				// In case no APEX is having the lib, the lib is installed to the system
@@ -2169,7 +2089,7 @@
 				//
 				// Skip the dependency in unbundled builds where the device image is not
 				// being built.
-				if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() && !ctx.Config().UnbundledBuild() {
+				if ch.IsStubsImplementationRequired() && !am.NotInPlatform() && !ctx.Config().UnbundledBuild() {
 					// we need a module name for Make
 					name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
 					if !android.InList(name, a.makeModulesToInstall) {
@@ -2266,8 +2186,6 @@
 			ctx.PropertyErrorf("systemserverclasspath_fragments",
 				"systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
 		}
-	} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
-		// nothing
 	} else if depTag == android.DarwinUniversalVariantTag {
 		// nothing
 	} else if depTag == android.RequiredDepTag {
@@ -2311,7 +2229,7 @@
 		checkDuplicate:         a.shouldCheckDuplicate(ctx),
 		unwantedTransitiveDeps: a.properties.Unwanted_transitive_deps,
 	}
-	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) })
+	ctx.WalkDeps(func(child, parent android.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) })
 	vctx.normalizeFileInfo(ctx)
 	if a.privateKeyFile == nil {
 		if ctx.Config().AllowMissingDependencies() {
@@ -2359,6 +2277,8 @@
 	// 3) some fields in apexBundle struct are configured
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = vctx.filesInfo
+	a.unwantedTransitiveFilesInfo = vctx.unwantedTransitiveFilesInfo
+	a.duplicateTransitiveFilesInfo = vctx.duplicateTransitiveFilesInfo
 
 	a.setPayloadFsType(ctx)
 	a.setSystemLibLink(ctx)
@@ -2384,6 +2304,9 @@
 	a.required = append(a.required, a.VintfFragmentModuleNames(ctx)...)
 
 	a.setOutputFiles(ctx)
+	a.enforcePartitionTagOnApexSystemServerJar(ctx)
+
+	a.verifyNativeImplementationLibs(ctx)
 }
 
 // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@@ -2400,7 +2323,7 @@
 // Apexes built from source retrieve this information by visiting `bootclasspath_fragments`
 // Used by dex_bootjars to generate the boot image
 func (a *apexBundle) provideApexExportsInfo(ctx android.ModuleContext) {
-	ctx.VisitDirectDepsWithTag(bcpfTag, func(child android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(bcpfTag, func(child android.ModuleProxy) {
 		if info, ok := android.OtherModuleProvider(ctx, child, java.BootclasspathFragmentApexContentInfoProvider); ok {
 			exports := android.ApexExportsInfo{
 				ApexName:                      a.ApexVariationName(),
@@ -2424,6 +2347,24 @@
 	}
 }
 
+// enforceAppUpdatability propagates updatable=true to apps of updatable apexes
+func (a *apexBundle) enforceAppUpdatability(mctx android.ModuleContext) {
+	if !a.Enabled(mctx) {
+		return
+	}
+	if a.Updatable() {
+		// checking direct deps is sufficient since apex->apk is a direct edge, even when inherited via apex_defaults
+		mctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
+			if appInfo, ok := android.OtherModuleProvider(mctx, module, java.AppInfoProvider); ok {
+				// ignore android_test_app
+				if !appInfo.TestHelperApp && !appInfo.Updatable {
+					mctx.ModuleErrorf("app dependency %s must have updatable: true", mctx.OtherModuleName(module))
+				}
+			}
+		})
+	}
+}
+
 // apexBootclasspathFragmentFiles returns the list of apexFile structures defining the files that
 // the bootclasspath_fragment contributes to the apex.
 func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module blueprint.Module) []apexFile {
@@ -2521,10 +2462,14 @@
 	return module
 }
 
-func ApexBundleFactory(testApex bool) android.Module {
-	bundle := newApexBundle()
-	bundle.testApex = testApex
-	return bundle
+type apexTestProperties struct {
+	// Boolean flags for validation checks. Test APEXes can turn on/off individual checks.
+	Skip_validations struct {
+		// Skips `Apex_sepolicy_tests` check if true
+		Apex_sepolicy_tests *bool
+		// Skips `Host_apex_verifier` check if true
+		Host_apex_verifier *bool
+	}
 }
 
 // apex_test is an APEX for testing. The difference from the ordinary apex module type is that
@@ -2532,6 +2477,7 @@
 func TestApexBundleFactory() android.Module {
 	bundle := newApexBundle()
 	bundle.testApex = true
+	bundle.AddProperties(&bundle.testProperties)
 	return bundle
 }
 
@@ -2639,23 +2585,24 @@
 		return
 	}
 
-	abInfo, _ := android.ModuleProvider(ctx, android.ApexBundleInfoProvider)
+	librariesDirectlyInApex := make(map[string]bool)
+	ctx.VisitDirectDepsProxyWithTag(sharedLibTag, func(dep android.ModuleProxy) {
+		librariesDirectlyInApex[ctx.OtherModuleName(dep)] = true
+	})
 
-	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
-		if ccm, ok := to.(*cc.Module); ok {
-			apexName := ctx.ModuleName()
-			fromName := ctx.OtherModuleName(from)
-			toName := ctx.OtherModuleName(to)
-
+	a.WalkPayloadDepsProxy(ctx, func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool {
+		if ccInfo, ok := android.OtherModuleProvider(ctx, to, cc.CcInfoProvider); ok {
 			// If `to` is not actually in the same APEX as `from` then it does not need
 			// apex_available and neither do any of its dependencies.
-			//
-			// It is ok to call DepIsInSameApex() directly from within WalkPayloadDeps().
-			if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+			if externalDep {
 				// As soon as the dependency graph crosses the APEX boundary, don't go further.
 				return false
 			}
 
+			apexName := ctx.ModuleName()
+			fromName := ctx.OtherModuleName(from)
+			toName := ctx.OtherModuleName(to)
+
 			// The dynamic linker and crash_dump tool in the runtime APEX is the only
 			// exception to this rule. It can't make the static dependencies dynamic
 			// because it can't do the dynamic linking for itself.
@@ -2665,12 +2612,11 @@
 				return false
 			}
 
-			isStubLibraryFromOtherApex := ccm.HasStubsVariants() && !abInfo.Contents.DirectlyInApex(toName)
+			isStubLibraryFromOtherApex := ccInfo.HasStubsVariants && !librariesDirectlyInApex[toName]
 			if isStubLibraryFromOtherApex && !externalDep {
 				ctx.ModuleErrorf("%q required by %q is a native library providing stub. "+
 					"It shouldn't be included in this APEX via static linking. Dependency path: %s", to.String(), fromName, ctx.GetPathString(false))
 			}
-
 		}
 		return true
 	})
@@ -2698,7 +2644,7 @@
 
 // checkClasspathFragments enforces that all classpath fragments in deps generate classpaths.proto config.
 func (a *apexBundle) checkClasspathFragments(ctx android.ModuleContext) {
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		if tag := ctx.OtherModuleDependencyTag(module); tag == bcpfTag || tag == sscpfTag {
 			info, _ := android.OtherModuleProvider(ctx, module, java.ClasspathFragmentProtoContentInfoProvider)
 			if !info.ClasspathFragmentProtoGenerated {
@@ -2712,16 +2658,12 @@
 func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
 	// Visit direct deps only. As long as we guarantee top-level deps are using stable SDKs,
 	// java's checkLinkType guarantees correct usage for transitive deps
-	ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		switch tag {
 		case javaLibTag, androidAppTag:
-			if m, ok := module.(interface {
-				CheckStableSdkVersion(ctx android.BaseModuleContext) error
-			}); ok {
-				if err := m.CheckStableSdkVersion(ctx); err != nil {
-					ctx.ModuleErrorf("cannot depend on \"%v\": %v", ctx.OtherModuleName(module), err)
-				}
+			if err := java.CheckStableSdkVersion(ctx, module); err != nil {
+				ctx.ModuleErrorf("cannot depend on \"%v\": %v", ctx.OtherModuleName(module), err)
 			}
 		}
 	})
@@ -2753,7 +2695,7 @@
 		return
 	}
 
-	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		// As soon as the dependency graph crosses the APEX boundary, don't go further.
 		if externalDep {
 			return false
@@ -2805,12 +2747,12 @@
 
 // checkStaticExecutable ensures that executables in an APEX are not static.
 func (a *apexBundle) checkStaticExecutables(ctx android.ModuleContext) {
-	ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		if ctx.OtherModuleDependencyTag(module) != executableTag {
 			return
 		}
 
-		if l, ok := module.(cc.LinkableInterface); ok && l.StaticExecutable() {
+		if android.OtherModuleProviderOrDefault(ctx, module, cc.LinkableInfoKey).StaticExecutable {
 			apex := a.ApexVariationName()
 			exec := ctx.OtherModuleName(module)
 			if isStaticExecutableAllowed(apex, exec) {
@@ -2836,8 +2778,8 @@
 // Collect information for opening IDE project files in java/jdeps.go.
 func (a *apexBundle) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
 	dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...)
-	dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...)
-	dpInfo.Deps = append(dpInfo.Deps, a.properties.ResolvedSystemserverclasspathFragments...)
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments.GetOrDefault(ctx, nil)...)
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments.GetOrDefault(ctx, nil)...)
 }
 
 func init() {
@@ -2916,6 +2858,104 @@
 	}
 }
 
-func (a *apexBundle) IsTestApex() bool {
-	return a.testApex
+// verifyNativeImplementationLibs compares the list of transitive implementation libraries used to link native
+// libraries in the apex against the list of implementation libraries in the apex, ensuring that none of the
+// libraries in the apex have references to private APIs from outside the apex.
+func (a *apexBundle) verifyNativeImplementationLibs(ctx android.ModuleContext) {
+	var directImplementationLibs android.Paths
+	var transitiveImplementationLibs []depset.DepSet[android.Path]
+
+	if a.properties.IsCoverageVariant {
+		return
+	}
+
+	if a.testApex {
+		return
+	}
+
+	if a.UsePlatformApis() {
+		return
+	}
+
+	checkApexTag := func(tag blueprint.DependencyTag) bool {
+		switch tag {
+		case sharedLibTag, jniLibTag, executableTag, androidAppTag:
+			return true
+		default:
+			return false
+		}
+	}
+
+	checkTransitiveTag := func(tag blueprint.DependencyTag) bool {
+		switch {
+		case cc.IsSharedDepTag(tag), java.IsJniDepTag(tag), rust.IsRlibDepTag(tag), rust.IsDylibDepTag(tag), checkApexTag(tag):
+			return true
+		default:
+			return false
+		}
+	}
+
+	var appEmbeddedJNILibs android.Paths
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
+		tag := ctx.OtherModuleDependencyTag(dep)
+		if !checkApexTag(tag) {
+			return
+		}
+		if tag == sharedLibTag || tag == jniLibTag {
+			outputFile := android.OutputFileForModule(ctx, dep, "")
+			directImplementationLibs = append(directImplementationLibs, outputFile)
+		}
+		if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+			transitiveImplementationLibs = append(transitiveImplementationLibs, info.ImplementationDeps)
+		}
+		if info, ok := android.OtherModuleProvider(ctx, dep, java.AppInfoProvider); ok {
+			appEmbeddedJNILibs = append(appEmbeddedJNILibs, info.EmbeddedJNILibs...)
+		}
+	})
+
+	depSet := depset.New(depset.PREORDER, directImplementationLibs, transitiveImplementationLibs)
+	allImplementationLibs := depSet.ToList()
+
+	allFileInfos := slices.Concat(a.filesInfo, a.unwantedTransitiveFilesInfo, a.duplicateTransitiveFilesInfo)
+
+	for _, lib := range allImplementationLibs {
+		inApex := slices.ContainsFunc(allFileInfos, func(fi apexFile) bool {
+			return fi.builtFile == lib
+		})
+		inApkInApex := slices.Contains(appEmbeddedJNILibs, lib)
+
+		if !inApex && !inApkInApex {
+			ctx.ModuleErrorf("library in apex transitively linked against implementation library %q not in apex", lib)
+			var depPath []android.Module
+			ctx.WalkDeps(func(child, parent android.Module) bool {
+				if depPath != nil {
+					return false
+				}
+
+				tag := ctx.OtherModuleDependencyTag(child)
+
+				if parent == ctx.Module() {
+					if !checkApexTag(tag) {
+						return false
+					}
+				}
+
+				if checkTransitiveTag(tag) {
+					if android.OutputFileForModule(ctx, child, "") == lib {
+						depPath = ctx.GetWalkPath()
+					}
+					return true
+				}
+
+				return false
+			})
+			if depPath != nil {
+				ctx.ModuleErrorf("dependency path:")
+				for _, m := range depPath {
+					ctx.ModuleErrorf("   %s", ctx.OtherModuleName(m))
+				}
+				return
+			}
+		}
+	}
 }
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index f405cb2..d46104e 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -18,6 +18,7 @@
 
 import (
 	"encoding/json"
+	"strings"
 
 	"github.com/google/blueprint"
 
@@ -58,17 +59,19 @@
 
 	// Diff two given lists while ignoring comments in the allowed deps file.
 	diffAllowedApexDepsInfoRule = pctx.AndroidStaticRule("diffAllowedApexDepsInfoRule", blueprint.RuleParams{
-		Description: "Diff ${allowed_deps} and ${new_allowed_deps}",
+		Description: "Diff ${allowed_deps_list} and ${new_allowed_deps}",
 		Command: `
-			if grep -v '^#' ${allowed_deps} | diff -B - ${new_allowed_deps}; then
+			if grep -v -h '^#' ${allowed_deps_list} | sort -u -f| diff -B -u - ${new_allowed_deps}; then
 			   touch ${out};
 			else
-				echo -e "\n******************************";
+				echo;
+				echo "******************************";
 				echo "ERROR: go/apex-allowed-deps-error contains more information";
 				echo "******************************";
 				echo "Detected changes to allowed dependencies in updatable modules.";
 				echo "To fix and update packages/modules/common/build/allowed_deps.txt, please run:";
-				echo -e "$$ (croot && packages/modules/common/build/update-apex-allowed-deps.sh)\n";
+				echo "$$ (croot && packages/modules/common/build/update-apex-allowed-deps.sh)";
+				echo;
 				echo "When submitting the generated CL, you must include the following information";
 				echo "in the commit message if you are adding a new dependency:";
 				echo "Apex-Size-Increase: Expected binary size increase for affected APEXes (or the size of the .jar / .so file of the new library)";
@@ -77,14 +80,20 @@
 				echo "Test-Info: What’s the testing strategy for the new dependency? Does it have its own tests, and are you adding integration tests? How/when are the tests run?";
 				echo "You do not need OWNERS approval to submit the change, but mainline-modularization@";
 				echo "will periodically review additions and may require changes.";
-				echo -e "******************************\n";
+				echo "******************************";
+				echo;
 				exit 1;
 			fi;
 		`,
-	}, "allowed_deps", "new_allowed_deps")
+	}, "allowed_deps_list", "new_allowed_deps")
 )
 
 func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	allowedDepsSources := []android.OptionalPath{android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")}
+	extraAllowedDepsPath := ctx.Config().ExtraAllowedDepsTxt()
+	if extraAllowedDepsPath != "" {
+		allowedDepsSources = append(allowedDepsSources, android.ExistentPathForSource(ctx, extraAllowedDepsPath))
+	}
 	updatableFlatLists := android.Paths{}
 	ctx.VisitAllModules(func(module android.Module) {
 		if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok {
@@ -96,37 +105,42 @@
 			}
 		}
 	})
-
-	allowedDepsSource := android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")
 	newAllowedDeps := android.PathForOutput(ctx, "apex", "depsinfo", "new-allowed-deps.txt")
 	s.allowedApexDepsInfoCheckResult = android.PathForOutput(ctx, newAllowedDeps.Rel()+".check")
-
-	if !allowedDepsSource.Valid() {
+	hasOneValidDepsPath := false
+	for _, allowedDepsSource := range allowedDepsSources {
+		if allowedDepsSource.Valid() {
+			hasOneValidDepsPath = true
+			updatableFlatLists = append(updatableFlatLists, allowedDepsSource.Path())
+		}
+	}
+	allowedDepsStrList := make([]string, len(allowedDepsSources))
+	for _, value := range allowedDepsSources {
+		allowedDepsStrList = append(allowedDepsStrList, value.String())
+	}
+	allowedDepsListString := strings.Join(allowedDepsStrList, " ")
+	if !hasOneValidDepsPath {
 		// Unbundled projects may not have packages/modules/common/ checked out; ignore those.
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.Touch,
 			Output: s.allowedApexDepsInfoCheckResult,
 		})
 	} else {
-		allowedDeps := allowedDepsSource.Path()
-
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   generateApexDepsInfoFilesRule,
-			Inputs: append(updatableFlatLists, allowedDeps),
+			Inputs: updatableFlatLists,
 			Output: newAllowedDeps,
 		})
-
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   diffAllowedApexDepsInfoRule,
 			Input:  newAllowedDeps,
 			Output: s.allowedApexDepsInfoCheckResult,
 			Args: map[string]string{
-				"allowed_deps":     allowedDeps.String(),
-				"new_allowed_deps": newAllowedDeps.String(),
+				"allowed_deps_list": allowedDepsListString,
+				"new_allowed_deps":  newAllowedDeps.String(),
 			},
 		})
 	}
-
 	ctx.Phony("apex-allowed-deps-check", s.allowedApexDepsInfoCheckResult)
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 394f4ae..dd55152 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -20,6 +20,7 @@
 	"path/filepath"
 	"reflect"
 	"regexp"
+	"slices"
 	"sort"
 	"strconv"
 	"strings"
@@ -28,6 +29,7 @@
 	"android/soong/aconfig/codegen"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/bpmodify"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -82,34 +84,6 @@
 	return files.AddToFixture()
 }
 
-func withTargets(targets map[android.OsType][]android.Target) android.FixturePreparer {
-	return android.FixtureModifyConfig(func(config android.Config) {
-		for k, v := range targets {
-			config.Targets[k] = v
-		}
-	})
-}
-
-// withNativeBridgeTargets sets configuration with targets including:
-// - X86_64 (primary)
-// - X86 (secondary)
-// - Arm64 on X86_64 (native bridge)
-// - Arm on X86 (native bridge)
-var withNativeBridgeEnabled = android.FixtureModifyConfig(
-	func(config android.Config) {
-		config.Targets[android.Android] = []android.Target{
-			{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}},
-				NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
-			{Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}},
-				NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
-			{Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}},
-				NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86_64", NativeBridgeRelativePath: "arm64"},
-			{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
-				NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86", NativeBridgeRelativePath: "arm"},
-		}
-	},
-)
-
 func withManifestPackageNameOverrides(specs []string) android.FixturePreparer {
 	return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 		variables.ManifestPackageNameOverrides = specs
@@ -233,6 +207,10 @@
 	"system/sepolicy/apex/myapex-file_contexts": nil,
 })
 
+var prepareForTestWithOtherapex = android.FixtureMergeMockFs(android.MockFS{
+	"system/sepolicy/apex/otherapex-file_contexts": nil,
+})
+
 // ensure that 'result' equals 'expected'
 func ensureEquals(t *testing.T, result string, expected string) {
 	t.Helper()
@@ -316,6 +294,7 @@
 
 // Minimal test
 func TestBasicApex(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_defaults {
 			name: "myapex-defaults",
@@ -400,7 +379,7 @@
 			name: "foo.rust",
 			srcs: ["foo.rs"],
 			rlibs: ["libfoo.rlib.rust"],
-			rustlibs: ["libfoo.dylib.rust"],
+			rustlibs: ["libfoo.transitive.dylib.rust"],
 			apex_available: ["myapex"],
 		}
 
@@ -427,6 +406,13 @@
 			apex_available: ["myapex"],
 		}
 
+		rust_library_dylib {
+			name: "libfoo.transitive.dylib.rust",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			apex_available: ["myapex"],
+		}
+
 		rust_ffi_shared {
 			name: "libfoo.ffi",
 			srcs: ["foo.rs"],
@@ -462,7 +448,7 @@
 				"//apex_available:platform",
 				"myapex",
 			],
-    }
+		}
 
 		cc_library_static {
 			name: "libstatic",
@@ -489,6 +475,7 @@
 				"//apex_available:platform",
 				"myapex",
 			],
+			compile_dex: true,
 		}
 
 		dex_import {
@@ -554,6 +541,7 @@
 	ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common_apex10000")
 	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.rlib.rust"), "android_arm64_armv8-a_rlib_dylib-std_apex10000")
 	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.dylib.rust"), "android_arm64_armv8-a_dylib_apex10000")
+	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.transitive.dylib.rust"), "android_arm64_armv8-a_dylib_apex10000")
 	ensureListContains(t, ctx.ModuleVariantsForTests("libbar.ffi"), "android_arm64_armv8-a_shared_apex10000")
 	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.shared_from_rust"), "android_arm64_armv8-a_shared_apex10000")
 
@@ -563,6 +551,7 @@
 	ensureContains(t, copyCmds, "image.apex/javalib/myjar_stem.jar")
 	ensureContains(t, copyCmds, "image.apex/javalib/myjar_dex.jar")
 	ensureContains(t, copyCmds, "image.apex/lib64/libfoo.dylib.rust.dylib.so")
+	ensureContains(t, copyCmds, "image.apex/lib64/libfoo.transitive.dylib.rust.dylib.so")
 	ensureContains(t, copyCmds, "image.apex/lib64/libfoo.ffi.so")
 	ensureContains(t, copyCmds, "image.apex/lib64/libbar.ffi.so")
 	ensureContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
@@ -613,6 +602,7 @@
 }
 
 func TestDefaults(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_defaults {
 			name: "myapex-defaults",
@@ -655,6 +645,7 @@
 			sdk_version: "none",
 			system_modules: "none",
 			apex_available: [ "myapex" ],
+			compile_dex: true,
 		}
 
 		android_app {
@@ -695,6 +686,7 @@
 }
 
 func TestApexManifest(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -717,6 +709,7 @@
 }
 
 func TestApexManifestMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_defaults {
 			name: "my_defaults",
@@ -790,6 +783,7 @@
 }
 
 func TestApexWithDessertSha(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_defaults {
 			name: "my_defaults",
@@ -863,6 +857,7 @@
 }
 
 func TestFileContexts(t *testing.T) {
+	t.Parallel()
 	for _, vendor := range []bool{true, false} {
 		prop := ""
 		if vendor {
@@ -897,6 +892,7 @@
 }
 
 func TestApexWithStubs(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -915,7 +911,7 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["mylib2", "mylib3", "my_prebuilt_platform_lib", "my_prebuilt_platform_stub_only_lib"],
+			shared_libs: ["mylib2", "mylib3#impl", "my_prebuilt_platform_lib", "my_prebuilt_platform_stub_only_lib"],
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "myapex" ],
@@ -1011,7 +1007,7 @@
 	// ... and not linking to the non-stub (impl) variant of mylib2
 	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
 
-	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex)
+	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because the dependency is added with mylib3#impl)
 	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_apex10000/mylib3.so")
 	// .. and not linking to the stubs variant of mylib3
 	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12/mylib3.so")
@@ -1068,6 +1064,7 @@
 }
 
 func TestApexShouldNotEmbedStubVariant(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `module "myapex" .*: native_shared_libs: "libbar" is a stub`, `
 		apex {
 			name: "myapex",
@@ -1094,6 +1091,7 @@
 }
 
 func TestApexCanUsePrivateApis(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -1185,7 +1183,7 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["mylib2", "mylib3"],
+			shared_libs: ["mylib2", "mylib3#impl"],
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "myapex" ],
@@ -1248,7 +1246,7 @@
 	// ... and not linking to the non-stub (impl) variant of mylib2
 	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
 
-	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex)
+	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because the dependency is added with mylib3#impl)
 	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_apex29/mylib3.so")
 	// .. and not linking to the stubs variant of mylib3
 	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_29/mylib3.so")
@@ -1344,6 +1342,7 @@
 }
 
 func TestApexWithExplicitStubsDependency(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex2",
@@ -1430,6 +1429,7 @@
 }
 
 func TestApexWithRuntimeLibsDependency(t *testing.T) {
+	t.Parallel()
 	/*
 		myapex
 		  |
@@ -1558,6 +1558,7 @@
 )
 
 func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForTestOfRuntimeApexWithHwasan).RunTestWithBp(t, `
 		cc_library {
 			name: "libc",
@@ -1611,6 +1612,7 @@
 }
 
 func TestRuntimeApexShouldInstallHwasanIfHwaddressSanitized(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestOfRuntimeApexWithHwasan,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -1666,6 +1668,7 @@
 }
 
 func TestApexDependsOnLLNDKTransitively(t *testing.T) {
+	t.Parallel()
 	testcases := []struct {
 		name          string
 		minSdkVersion string
@@ -1690,6 +1693,7 @@
 	}
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			ctx := testApex(t, `
 			apex {
 				name: "myapex",
@@ -1757,6 +1761,7 @@
 }
 
 func TestApexWithSystemLibsStubs(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -1774,8 +1779,8 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			system_shared_libs: ["libc", "libm"],
-			shared_libs: ["libdl#27"],
+			system_shared_libs: ["libc"],
+			shared_libs: ["libdl#27", "libm#impl"],
 			stl: "none",
 			apex_available: [ "myapex" ],
 		}
@@ -1849,6 +1854,7 @@
 }
 
 func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) {
+	t.Parallel()
 	// there are three links between liba --> libz.
 	// 1) myapex -> libx -> liba -> libz    : this should be #30 link
 	// 2) otherapex -> liby -> liba -> libz : this should be #30 link
@@ -1937,6 +1943,7 @@
 }
 
 func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -1989,6 +1996,7 @@
 }
 
 func TestApexMinSdkVersion_SupportsCodeNames_JavaLibs(t *testing.T) {
+	t.Parallel()
 	testApex(t, `
 		apex {
 			name: "myapex",
@@ -2009,6 +2017,7 @@
 			apex_available: [ "myapex" ],
 			sdk_version: "current",
 			min_sdk_version: "S", // should be okay
+			compile_dex: true,
 		}
 	`,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -2019,6 +2028,7 @@
 }
 
 func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2066,6 +2076,7 @@
 }
 
 func TestApexMinSdkVersion_InVendorApex(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2119,6 +2130,7 @@
 }
 
 func TestTrackAllowedDeps(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2185,7 +2197,155 @@
 		flatlist, "yourlib(minSdkVersion:29)")
 }
 
+func TestTrackCustomAllowedDepsInvalidDefaultTxt(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: true,
+			native_shared_libs: [
+				"mylib",
+				"yourlib",
+			],
+			min_sdk_version: "29",
+		}
+
+		apex {
+			name: "myapex2",
+			key: "myapex.key",
+			updatable: false,
+			native_shared_libs: ["yourlib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libbar"],
+			min_sdk_version: "29",
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: { versions: ["29", "30"] },
+		}
+
+		cc_library {
+			name: "yourlib",
+			srcs: ["mylib.cpp"],
+			min_sdk_version: "29",
+			apex_available: ["myapex", "myapex2", "//apex_available:platform"],
+		}
+	`, withFiles(android.MockFS{
+		"packages/modules/common/build/custom_allowed_deps.txt": nil,
+	}),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.ExtraAllowedDepsTxt = proptools.StringPtr("packages/modules/common/build/custom_allowed_deps.txt")
+			},
+		))
+
+	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
+	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt")
+	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
+
+	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
+	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		myapex.Output("depsinfo/flatlist.txt")), "\n")
+	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
+		flatlist, "libbar(minSdkVersion:(no version)) (external)")
+	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
+		flatlist, "mylib:(minSdkVersion:29)")
+	android.AssertStringListContains(t, "track platform-available lib",
+		flatlist, "yourlib(minSdkVersion:29)")
+}
+
+func TestTrackCustomAllowedDepsWithDefaultTxt(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: true,
+			native_shared_libs: [
+				"mylib",
+				"yourlib",
+			],
+			min_sdk_version: "29",
+		}
+
+		apex {
+			name: "myapex2",
+			key: "myapex.key",
+			updatable: false,
+			native_shared_libs: ["yourlib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libbar"],
+			min_sdk_version: "29",
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: { versions: ["29", "30"] },
+		}
+
+		cc_library {
+			name: "yourlib",
+			srcs: ["mylib.cpp"],
+			min_sdk_version: "29",
+			apex_available: ["myapex", "myapex2", "//apex_available:platform"],
+		}
+	`, withFiles(android.MockFS{
+		"packages/modules/common/build/custom_allowed_deps.txt": nil,
+		"packages/modules/common/build/allowed_deps.txt":        nil,
+	}),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.ExtraAllowedDepsTxt = proptools.StringPtr("packages/modules/common/build/custom_allowed_deps.txt")
+			},
+		))
+
+	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
+	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt")
+	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt")
+
+	myapex := ctx.ModuleForTests("myapex", "android_common_myapex")
+	flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx,
+		myapex.Output("depsinfo/flatlist.txt")), "\n")
+	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
+		flatlist, "libbar(minSdkVersion:(no version)) (external)")
+	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
+		flatlist, "mylib:(minSdkVersion:29)")
+	android.AssertStringListContains(t, "track platform-available lib",
+		flatlist, "yourlib(minSdkVersion:29)")
+}
+
 func TestTrackAllowedDeps_SkipWithoutAllowedDepsTxt(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2207,6 +2367,7 @@
 }
 
 func TestPlatformUsesLatestStubsFromApexes(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2262,6 +2423,7 @@
 )
 
 func TestQApexesUseLatestStubsInBundledBuildsAndHWASAN(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2301,6 +2463,7 @@
 }
 
 func TestQTargetApexUsesStaticUnwinder(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2331,6 +2494,7 @@
 }
 
 func TestApexMinSdkVersion_ErrorIfIncompatibleVersion(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `module "mylib".*: should support min_sdk_version\(29\)`, `
 		apex {
 			name: "myapex",
@@ -2403,6 +2567,7 @@
 				"myapex",
 			],
 			min_sdk_version: "30",
+			compile_dex: true,
 		}
 	`)
 
@@ -2430,12 +2595,14 @@
 			// Compile against core API surface
 			sdk_version: "core_current",
 			min_sdk_version: "30",
+			compile_dex: true,
 		}
 	`)
 
 }
 
 func TestApexMinSdkVersion_Okay(t *testing.T) {
+	t.Parallel()
 	testApex(t, `
 		apex {
 			name: "myapex",
@@ -2476,6 +2643,7 @@
 			],
 			apex_available: ["myapex"],
 			min_sdk_version: "29",
+			compile_dex: true,
 		}
 
 		java_library {
@@ -2496,6 +2664,7 @@
 }
 
 func TestApexMinSdkVersion_MinApiForArch(t *testing.T) {
+	t.Parallel()
 	// Tests that an apex dependency with min_sdk_version higher than the
 	// min_sdk_version of the apex is allowed as long as the dependency's
 	// min_sdk_version is less than or equal to the api level that the
@@ -2528,6 +2697,7 @@
 }
 
 func TestJavaStableSdkVersion(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name          string
 		expectedError string
@@ -2553,6 +2723,7 @@
 					srcs: ["foo/bar/MyClass.java"],
 					sdk_version: "test_current",
 					apex_available: ["myapex"],
+					compile_dex: true,
 				}
 			`,
 		},
@@ -2577,6 +2748,7 @@
 					sdk_version: "current",
 					apex_available: ["myapex"],
 					min_sdk_version: "29",
+					compile_dex: true,
 				}
 			`,
 		},
@@ -2600,6 +2772,7 @@
 					srcs: ["foo/bar/MyClass.java"],
 					sdk_version: "test_current",
 					apex_available: ["myapex"],
+					compile_dex: true,
 				}
 			`,
 		},
@@ -2623,6 +2796,7 @@
 					srcs: ["foo/bar/MyClass.java"],
 					sdk_version: "core_platform",
 					apex_available: ["myapex"],
+					compile_dex: true,
 				}
 			`,
 			preparer: java.FixtureUseLegacyCorePlatformApi("myjar-uses-legacy"),
@@ -2651,6 +2825,7 @@
 					sdk_version: "current",
 					apex_available: ["myapex"],
 					static_libs: ["transitive-jar"],
+					compile_dex: true,
 				}
 				java_library {
 					name: "transitive-jar",
@@ -2667,6 +2842,7 @@
 			continue
 		}
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			errorHandler := android.FixtureExpectsNoErrors
 			if test.expectedError != "" {
 				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
@@ -2725,6 +2901,7 @@
 }
 
 func TestApexMinSdkVersion_ErrorIfDepIsNewer_Java(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `module "bar".*: should support min_sdk_version\(29\) for "myapex"`, `
 		apex {
 			name: "myapex",
@@ -2761,6 +2938,7 @@
 }
 
 func TestApexMinSdkVersion_OkayEvenWhenDepIsNewer_IfItSatisfiesApexMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2775,8 +2953,7 @@
 			private_key: "testkey.pem",
 		}
 
-		// mylib in myapex will link to mylib2#current
-		// mylib in otherapex will link to mylib2(non-stub) in otherapex as well
+		// mylib will link to mylib2#current
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
@@ -2810,10 +2987,11 @@
 		ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so")
 	}
 	expectLink("mylib", "shared_apex29", "mylib2", "shared_current")
-	expectLink("mylib", "shared_apex30", "mylib2", "shared_apex30")
+	expectLink("mylib", "shared_apex30", "mylib2", "shared_current")
 }
 
 func TestApexMinSdkVersion_WorksWithSdkCodename(t *testing.T) {
+	t.Parallel()
 	withSAsActiveCodeNames := android.FixtureModifyProductVariables(
 		func(variables android.FixtureProductVariables) {
 			variables.Platform_sdk_codename = proptools.StringPtr("S")
@@ -2846,6 +3024,7 @@
 }
 
 func TestApexMinSdkVersion_WorksWithActiveCodenames(t *testing.T) {
+	t.Parallel()
 	withSAsActiveCodeNames := android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 		variables.Platform_sdk_codename = proptools.StringPtr("S")
 		variables.Platform_version_active_codenames = []string{"S", "T"}
@@ -2884,6 +3063,7 @@
 }
 
 func TestFilesInSubDir(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2953,6 +3133,7 @@
 }
 
 func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2997,7 +3178,7 @@
 				},
 			},
 		}
-	`, withNativeBridgeEnabled)
+	`, android.PrepareForNativeBridgeEnabled)
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{
 		"bin/foo/bar/mybin",
 		"bin/foo/bar/mybin64",
@@ -3011,6 +3192,7 @@
 }
 
 func TestVendorApex(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForApexTest,
 		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
@@ -3061,6 +3243,7 @@
 }
 
 func TestProductVariant(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -3093,6 +3276,7 @@
 }
 
 func TestApex_withPrebuiltFirmware(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name           string
 		additionalProp string
@@ -3102,6 +3286,7 @@
 	}
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			ctx := testApex(t, `
 				apex {
 					name: "myapex",
@@ -3130,6 +3315,7 @@
 }
 
 func TestAndroidMk_VendorApexRequired(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -3192,6 +3378,7 @@
 }
 
 func TestStaticLinking(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -3234,6 +3421,7 @@
 }
 
 func TestKeys(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex_keytest",
@@ -3291,7 +3479,9 @@
 }
 
 func TestCertificate(t *testing.T) {
+	t.Parallel()
 	t.Run("if unspecified, it defaults to DefaultAppCertificate", func(t *testing.T) {
+		t.Parallel()
 		ctx := testApex(t, `
 			apex {
 				name: "myapex",
@@ -3310,6 +3500,7 @@
 		}
 	})
 	t.Run("override when unspecified", func(t *testing.T) {
+		t.Parallel()
 		ctx := testApex(t, `
 			apex {
 				name: "myapex_keytest",
@@ -3333,6 +3524,7 @@
 		}
 	})
 	t.Run("if specified as :module, it respects the prop", func(t *testing.T) {
+		t.Parallel()
 		ctx := testApex(t, `
 			apex {
 				name: "myapex",
@@ -3356,6 +3548,7 @@
 		}
 	})
 	t.Run("override when specifiec as <:module>", func(t *testing.T) {
+		t.Parallel()
 		ctx := testApex(t, `
 			apex {
 				name: "myapex_keytest",
@@ -3380,6 +3573,7 @@
 		}
 	})
 	t.Run("if specified as name, finds it from DefaultDevKeyDir", func(t *testing.T) {
+		t.Parallel()
 		ctx := testApex(t, `
 			apex {
 				name: "myapex",
@@ -3399,6 +3593,7 @@
 		}
 	})
 	t.Run("override when specified as <name>", func(t *testing.T) {
+		t.Parallel()
 		ctx := testApex(t, `
 			apex {
 				name: "myapex_keytest",
@@ -3425,6 +3620,7 @@
 }
 
 func TestMacro(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -3518,6 +3714,7 @@
 }
 
 func TestHeaderLibsDependency(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -3721,6 +3918,7 @@
 }
 
 func TestVndkApexVersion(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_vndk {
 			name: "com.android.vndk.v27",
@@ -3790,6 +3988,7 @@
 }
 
 func TestVndkApexNameRule(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_vndk {
 			name: "com.android.vndk.v29",
@@ -3822,6 +4021,7 @@
 }
 
 func TestVndkApexDoesntSupportNativeBridgeSupported(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `module "com.android.vndk.v30" .*: native_bridge_supported: .* doesn't support native bridge binary`, `
 		apex_vndk {
 			name: "com.android.vndk.v30",
@@ -3852,6 +4052,7 @@
 }
 
 func TestVndkApexWithBinder32(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_vndk {
 			name: "com.android.vndk.v27",
@@ -3906,11 +4107,20 @@
 			"libvndk27binder32.so": nil,
 		}),
 		withBinder32bit,
-		withTargets(map[android.OsType][]android.Target{
-			android.Android: {
-				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
-					NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
-			},
+		android.FixtureModifyConfig(func(config android.Config) {
+			target := android.Target{
+				Os: android.Android,
+				Arch: android.Arch{
+					ArchType:    android.Arm,
+					ArchVariant: "armv7-a-neon",
+					Abi:         []string{"armeabi-v7a"},
+				},
+				NativeBridge:             android.NativeBridgeDisabled,
+				NativeBridgeHostArchName: "",
+				NativeBridgeRelativePath: "",
+			}
+			config.Targets[android.Android] = []android.Target{target}
+			config.AndroidFirstDeviceTarget = target
 		}),
 	)
 
@@ -3921,6 +4131,7 @@
 }
 
 func TestDependenciesInApexManifest(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex_nodep",
@@ -4054,6 +4265,7 @@
 }
 
 func TestOverrideApexManifestDefaultVersion(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -4088,6 +4300,7 @@
 }
 
 func TestCompileMultilibProp(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		compileMultiLibProp string
 		containedLibs       []string
@@ -4158,6 +4371,7 @@
 }
 
 func TestNonTestApex(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -4211,6 +4425,7 @@
 }
 
 func TestTestApex(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_test {
 			name: "myapex",
@@ -4260,6 +4475,7 @@
 }
 
 func TestLibzVendorIsntStable(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 	apex {
 		name: "myapex",
@@ -4326,6 +4542,7 @@
 }
 
 func TestApexWithTarget(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -4417,6 +4634,7 @@
 }
 
 func TestApexWithArch(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -4492,6 +4710,7 @@
 }
 
 func TestApexWithShBinary(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -4522,6 +4741,7 @@
 }
 
 func TestApexInVariousPartition(t *testing.T) {
+	t.Parallel()
 	testcases := []struct {
 		propName, partition string
 	}{
@@ -4534,6 +4754,7 @@
 	}
 	for _, tc := range testcases {
 		t.Run(tc.propName+":"+tc.partition, func(t *testing.T) {
+			t.Parallel()
 			ctx := testApex(t, `
 				apex {
 					name: "myapex",
@@ -4560,6 +4781,7 @@
 }
 
 func TestFileContexts_FindInDefaultLocationIfNotSet(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -4579,6 +4801,7 @@
 }
 
 func TestFileContexts_ShouldBeUnderSystemSepolicyForSystemApexes(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `"myapex" .*: file_contexts: should be under system/sepolicy`, `
 		apex {
 			name: "myapex",
@@ -4598,6 +4821,7 @@
 }
 
 func TestFileContexts_ProductSpecificApexes(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `"myapex" .*: file_contexts: cannot find`, `
 		apex {
 			name: "myapex",
@@ -4637,6 +4861,7 @@
 }
 
 func TestFileContexts_SetViaFileGroup(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -4665,6 +4890,7 @@
 }
 
 func TestApexKeyFromOtherModule(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_key {
 			name: "myapex.key",
@@ -4698,6 +4924,7 @@
 }
 
 func TestPrebuilt(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		prebuilt_apex {
 			name: "myapex",
@@ -4732,6 +4959,7 @@
 }
 
 func TestPrebuiltMissingSrc(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `module "myapex" variant "android_common_myapex".*: prebuilt_apex does not support "arm64_armv8-a"`, `
 		prebuilt_apex {
 			name: "myapex",
@@ -4740,6 +4968,7 @@
 }
 
 func TestPrebuiltFilenameOverride(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		prebuilt_apex {
 			name: "myapex",
@@ -4763,6 +4992,7 @@
 }
 
 func TestApexSetFilenameOverride(t *testing.T) {
+	t.Parallel()
 	testApex(t, `
 		apex_set {
  			name: "com.company.android.myapex",
@@ -4792,6 +5022,7 @@
 }
 
 func TestPrebuiltOverrides(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		prebuilt_apex {
 			name: "myapex.prebuilt",
@@ -4818,6 +5049,7 @@
 }
 
 func TestPrebuiltApexName(t *testing.T) {
+	t.Parallel()
 	testApex(t, `
 		prebuilt_apex {
 			name: "com.company.android.myapex",
@@ -4836,6 +5068,7 @@
 }
 
 func TestPrebuiltApexNameWithPlatformBootclasspath(t *testing.T) {
+	t.Parallel()
 	_ = android.GroupFixturePreparers(
 		java.PrepareForTestWithJavaDefaultModules,
 		PrepareForTestWithApexBuildComponents,
@@ -4886,6 +5119,7 @@
 }
 
 func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		java.FixtureConfigureApexBootJars("myapex:libfoo", "myapex:libbar"),
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
@@ -4931,6 +5165,7 @@
 	}
 
 	t.Run("prebuilt only", func(t *testing.T) {
+		t.Parallel()
 		bp := `
 		prebuilt_apex {
 			name: "myapex",
@@ -4992,6 +5227,7 @@
 	})
 
 	t.Run("apex_set only", func(t *testing.T) {
+		t.Parallel()
 		bp := `
 		apex_set {
 			name: "myapex",
@@ -5082,6 +5318,7 @@
 	})
 
 	t.Run("prebuilt with source library preferred", func(t *testing.T) {
+		t.Parallel()
 		bp := `
 		prebuilt_apex {
 			name: "myapex",
@@ -5155,6 +5392,7 @@
 	})
 
 	t.Run("prebuilt library preferred with source", func(t *testing.T) {
+		t.Parallel()
 		bp := `
 		apex {
 			name: "myapex",
@@ -5257,6 +5495,7 @@
 	})
 
 	t.Run("prebuilt with source apex preferred", func(t *testing.T) {
+		t.Parallel()
 		bp := `
 		apex {
 			name: "myapex",
@@ -5355,6 +5594,7 @@
 	})
 
 	t.Run("prebuilt preferred with source apex disabled", func(t *testing.T) {
+		t.Parallel()
 		bp := `
 		apex {
 			name: "myapex",
@@ -5429,11 +5669,11 @@
 			apex_available: ["myapex"],
 			shared_library: false,
 			permitted_packages: ["bar"],
+			prefer: true,
 		}
 
 		java_sdk_library {
 			name: "libbar",
-			enabled: false,
 			srcs: ["foo/bar/MyClass.java"],
 			unsafe_ignore_missing_latest_api: true,
 			apex_available: ["myapex"],
@@ -5450,7 +5690,7 @@
 			android.PrepareForTestWithAllowMissingDependencies,
 			android.FixtureMergeMockFs(map[string][]byte{
 				"build/soong/scripts/check_boot_jars/package_allowed_list.txt": nil,
-				"frameworks/base/config/boot-profile.txt":                      nil,
+				"frameworks/base/boot/boot-profile.txt":                        nil,
 			}),
 		)
 
@@ -5466,6 +5706,7 @@
 	})
 
 	t.Run("Co-existing unflagged apexes should create a duplicate module error", func(t *testing.T) {
+		t.Parallel()
 		bp := `
 		// Source
 		apex {
@@ -5545,6 +5786,7 @@
 }
 
 func TestApexWithTests(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_test {
 			name: "myapex",
@@ -5620,6 +5862,7 @@
 }
 
 func TestErrorsIfDepsAreNotEnabled(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `module "myapex" .* depends on disabled module "libfoo"`, `
 		apex {
 			name: "myapex",
@@ -5661,6 +5904,7 @@
 			system_modules: "none",
 			enabled: false,
 			apex_available: ["myapex"],
+			compile_dex: true,
 		}
 	`)
 }
@@ -5695,6 +5939,7 @@
 }
 
 func TestApexWithApps(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -5789,6 +6034,7 @@
 }
 
 func TestApexWithAppImportBuildId(t *testing.T) {
+	t.Parallel()
 	invalidBuildIds := []string{"../", "a b", "a/b", "a/b/../c", "/a"}
 	for _, id := range invalidBuildIds {
 		message := fmt.Sprintf("Unable to use build id %s as filename suffix", id)
@@ -5819,6 +6065,7 @@
 }
 
 func TestApexWithAppImports(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -5868,6 +6115,7 @@
 }
 
 func TestApexWithAppImportsPrefer(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -5910,6 +6158,7 @@
 }
 
 func TestApexWithTestHelperApp(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -5943,6 +6192,7 @@
 }
 
 func TestApexPropertiesShouldBeDefaultable(t *testing.T) {
+	t.Parallel()
 	// libfoo's apex_available comes from cc_defaults
 	testApexError(t, `requires "libfoo" that doesn't list the APEX under 'apex_available'.`, `
 	apex {
@@ -5979,6 +6229,7 @@
 }
 
 func TestApexAvailable_DirectDep(t *testing.T) {
+	t.Parallel()
 	// libfoo is not available to myapex, but only to otherapex
 	testApexError(t, "requires \"libfoo\" that doesn't list the APEX under 'apex_available'.", `
 	apex {
@@ -6097,6 +6348,7 @@
 }
 
 func TestApexAvailable_IndirectDep(t *testing.T) {
+	t.Parallel()
 	// libbbaz is an indirect dep
 	testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'.\n\nDependency path:
 .*via tag apex\.dependencyTag\{"sharedLib"\}
@@ -6227,6 +6479,7 @@
 }
 
 func TestApexAvailable_IndirectStaticDep(t *testing.T) {
+	t.Parallel()
 	testApex(t, `
 	apex {
 		name: "myapex",
@@ -6293,6 +6546,7 @@
 }
 
 func TestApexAvailable_InvalidApexName(t *testing.T) {
+	t.Parallel()
 	testApexError(t, "\"otherapex\" is not a valid module name", `
 	apex {
 		name: "myapex",
@@ -6354,7 +6608,9 @@
 }
 
 func TestApexAvailable_ApexAvailableNameWithVersionCodeError(t *testing.T) {
+	t.Parallel()
 	t.Run("negative variant_version produces error", func(t *testing.T) {
+		t.Parallel()
 		testApexError(t, "expected an integer between 0-9; got -1", `
 			apex {
 				name: "myapex",
@@ -6372,6 +6628,7 @@
 	})
 
 	t.Run("variant_version greater than 9 produces error", func(t *testing.T) {
+		t.Parallel()
 		testApexError(t, "expected an integer between 0-9; got 10", `
 			apex {
 				name: "myapex",
@@ -6390,6 +6647,7 @@
 }
 
 func TestApexAvailable_ApexAvailableNameWithVersionCode(t *testing.T) {
+	t.Parallel()
 	context := android.GroupFixturePreparers(
 		android.PrepareForIntegrationTestWithAndroid,
 		PrepareForTestWithApexBuildComponents,
@@ -6425,14 +6683,14 @@
 	`)
 
 	fooManifestRule := result.ModuleForTests("foo", "android_common_foo").Rule("apexManifestRule")
-	fooExpectedDefaultVersion := android.DefaultUpdatableModuleVersion
+	fooExpectedDefaultVersion := testDefaultUpdatableModuleVersion
 	fooActualDefaultVersion := fooManifestRule.Args["default_version"]
 	if fooActualDefaultVersion != fooExpectedDefaultVersion {
 		t.Errorf("expected to find defaultVersion %q; got %q", fooExpectedDefaultVersion, fooActualDefaultVersion)
 	}
 
 	barManifestRule := result.ModuleForTests("bar", "android_common_bar").Rule("apexManifestRule")
-	defaultVersionInt, _ := strconv.Atoi(android.DefaultUpdatableModuleVersion)
+	defaultVersionInt, _ := strconv.Atoi(testDefaultUpdatableModuleVersion)
 	barExpectedDefaultVersion := fmt.Sprint(defaultVersionInt + 3)
 	barActualDefaultVersion := barManifestRule.Args["default_version"]
 	if barActualDefaultVersion != barExpectedDefaultVersion {
@@ -6447,7 +6705,9 @@
 }
 
 func TestApexAvailable_ApexAvailableName(t *testing.T) {
+	t.Parallel()
 	t.Run("using name of apex that sets apex_available_name is not allowed", func(t *testing.T) {
+		t.Parallel()
 		testApexError(t, "Consider adding \"myapex\" to 'apex_available' property of \"AppFoo\"", `
 			apex {
 				name: "myapex_sminus",
@@ -6481,6 +6741,7 @@
 	})
 
 	t.Run("apex_available_name allows module to be used in two different apexes", func(t *testing.T) {
+		t.Parallel()
 		testApex(t, `
 			apex {
 				name: "myapex_sminus",
@@ -6514,6 +6775,7 @@
 	})
 
 	t.Run("override_apexes work with apex_available_name", func(t *testing.T) {
+		t.Parallel()
 		testApex(t, `
 			override_apex {
 				name: "myoverrideapex_sminus",
@@ -6567,6 +6829,7 @@
 }
 
 func TestApexAvailable_ApexAvailableNameWithOverrides(t *testing.T) {
+	t.Parallel()
 	context := android.GroupFixturePreparers(
 		android.PrepareForIntegrationTestWithAndroid,
 		PrepareForTestWithApexBuildComponents,
@@ -6624,6 +6887,7 @@
 }
 
 func TestApexAvailable_CheckForPlatform(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 	apex {
 		name: "myapex",
@@ -6687,6 +6951,7 @@
 }
 
 func TestApexAvailable_CreatedForApex(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 	apex {
 		name: "myapex",
@@ -6722,6 +6987,7 @@
 }
 
 func TestApexAvailable_PrefixMatch(t *testing.T) {
+	t.Parallel()
 
 	for _, tc := range []struct {
 		name          string
@@ -6759,6 +7025,7 @@
 		},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			errorHandler := android.FixtureExpectsNoErrors
 			if tc.expectedError != "" {
 				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedError)
@@ -6814,7 +7081,53 @@
 	`)
 }
 
+func TestApexValidation_TestApexCanSkipInitRcCheck(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, `
+		apex_test {
+			name: "myapex",
+			key: "myapex.key",
+			skip_validations: {
+				host_apex_verifier: true,
+			},
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	validations := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk").Validations.Strings()
+	if android.SuffixInList(validations, "host_apex_verifier.timestamp") {
+		t.Error("should not run host_apex_verifier")
+	}
+}
+
+func TestApexValidation_TestApexCheckInitRc(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, `
+		apex_test {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	validations := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk").Validations.Strings()
+	if !android.SuffixInList(validations, "host_apex_verifier.timestamp") {
+		t.Error("should run host_apex_verifier")
+	}
+}
+
 func TestOverrideApex(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -6965,6 +7278,7 @@
 }
 
 func TestMinSdkVersionOverride(t *testing.T) {
+	t.Parallel()
 	// Override from 29 to 31
 	minSdkOverride31 := "31"
 	ctx := testApex(t, `
@@ -7024,6 +7338,7 @@
 }
 
 func TestMinSdkVersionOverrideToLowerVersionNoOp(t *testing.T) {
+	t.Parallel()
 	// Attempt to override from 31 to 29, should be a NOOP
 	minSdkOverride29 := "29"
 	ctx := testApex(t, `
@@ -7083,6 +7398,7 @@
 }
 
 func TestLegacyAndroid10Support(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -7142,6 +7458,7 @@
 }
 
 func TestJavaSDKLibrary(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -7181,6 +7498,7 @@
 }
 
 func TestJavaSDKLibraryOverrideApexes(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		override_apex {
 			name: "mycompanyapex",
@@ -7221,6 +7539,7 @@
 }
 
 func TestJavaSDKLibrary_WithinApex(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -7242,6 +7561,7 @@
 			apex_available: ["myapex"],
 			sdk_version: "none",
 			system_modules: "none",
+			compile_dex: true,
 		}
 
 		java_library {
@@ -7251,6 +7571,7 @@
 			apex_available: ["myapex"],
 			sdk_version: "none",
 			system_modules: "none",
+			compile_dex: true,
 		}
 
 		prebuilt_apis {
@@ -7274,6 +7595,7 @@
 }
 
 func TestJavaSDKLibrary_CrossBoundary(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -7325,6 +7647,7 @@
 }
 
 func TestJavaSDKLibrary_ImportPreferred(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		prebuilt_apis {
 			name: "sdk",
@@ -7358,6 +7681,7 @@
 			apex_available: ["myapex"],
 			sdk_version: "none",
 			system_modules: "none",
+			compile_dex: true,
 		}
 `),
 			"source/a.java":          nil,
@@ -7379,6 +7703,7 @@
 			public: {
 				enabled: true,
 			},
+			compile_dex: true,
 		}
 `),
 			"prebuilt/a.jar": nil,
@@ -7395,6 +7720,7 @@
 			public: {
 				jars: ["a.jar"],
 			},
+			compile_dex: true,
 		}
 `),
 		}), withFiles(filesForSdkLibrary),
@@ -7415,6 +7741,7 @@
 }
 
 func TestJavaSDKLibrary_ImportOnly(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `java_libs: "foo" is not configured to be compiled into dex`, `
 		apex {
 			name: "myapex",
@@ -7442,6 +7769,7 @@
 }
 
 func TestCompatConfig(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForApexTest,
 		java.PrepareForTestWithPlatformCompatConfig,
@@ -7471,6 +7799,7 @@
 			sdk_version: "none",
 			system_modules: "none",
 			apex_available: [ "myapex" ],
+			compile_dex: true,
 		}
 
 		// Make sure that a preferred prebuilt does not affect the apex contents.
@@ -7488,6 +7817,7 @@
 }
 
 func TestNoDupeApexFiles(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		android.PrepareForTestWithAndroidBuildComponents,
 		PrepareForTestWithApexBuildComponents,
@@ -7524,6 +7854,7 @@
 }
 
 func TestApexUnwantedTransitiveDeps(t *testing.T) {
+	t.Parallel()
 	bp := `
 	apex {
 		name: "myapex",
@@ -7560,6 +7891,7 @@
 }
 
 func TestRejectNonInstallableJavaLibrary(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `"myjar" is not configured to be compiled into dex`, `
 		apex {
 			name: "myapex",
@@ -7585,46 +7917,8 @@
 	`)
 }
 
-func TestCarryRequiredModuleNames(t *testing.T) {
-	ctx := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["mylib"],
-			updatable: false,
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			required: ["a", "b"],
-			host_required: ["c", "d"],
-			target_required: ["e", "f"],
-			apex_available: [ "myapex" ],
-		}
-	`)
-
-	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
-	name := apexBundle.BaseModuleName()
-	prefix := "TARGET_"
-	var builder strings.Builder
-	data.Custom(&builder, name, prefix, "", data)
-	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := mylib.myapex:64 a b\n")
-	ensureContains(t, androidMk, "LOCAL_HOST_REQUIRED_MODULES := c d\n")
-	ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES := e f\n")
-}
-
 func TestSymlinksFromApexToSystem(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex {
 			name: "myapex",
@@ -7705,6 +7999,7 @@
 				"//apex_available:platform",
 			],
 			min_sdk_version: "33",
+			compile_dex: true,
 		}
 
 		java_library {
@@ -7779,6 +8074,7 @@
 }
 
 func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -7831,6 +8127,7 @@
 }
 
 func TestApexWithJniLibs(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -7920,6 +8217,7 @@
 }
 
 func TestApexMutatorsDontRunIfDisabled(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -7944,6 +8242,7 @@
 }
 
 func TestAppBundle(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -7975,6 +8274,7 @@
 }
 
 func TestAppSetBundle(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -8042,6 +8342,7 @@
 }
 
 func TestApexSetApksModuleAssignment(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_set {
 			name: "myapex",
@@ -8120,6 +8421,7 @@
 }
 
 func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, `
 		apex {
 			name: "myapex",
@@ -8136,6 +8438,7 @@
 }
 
 func TestUpdatableDefault_should_set_min_sdk_version(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, `
 		apex {
 			name: "myapex",
@@ -8151,6 +8454,7 @@
 }
 
 func TestUpdatable_should_not_set_generate_classpaths_proto(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `"mysystemserverclasspathfragment" .* it must not set generate_classpaths_proto to false`, `
 		apex {
 			name: "myapex",
@@ -8195,8 +8499,10 @@
 }
 
 func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
+	t.Parallel()
 	preparer := java.FixtureConfigureApexBootJars("myapex:libfoo")
 	t.Run("prebuilt no source", func(t *testing.T) {
+		t.Parallel()
 		fragment := java.ApexVariantReference{
 			Apex:   proptools.StringPtr("myapex"),
 			Module: proptools.StringPtr("my-bootclasspath-fragment"),
@@ -8282,6 +8588,7 @@
 }
 
 func TestApexPermittedPackagesRules(t *testing.T) {
+	t.Parallel()
 	testcases := []struct {
 		name                 string
 		expectedError        string
@@ -8301,6 +8608,7 @@
 					apex_available: ["myapex"],
 					sdk_version: "none",
 					system_modules: "none",
+					compile_dex: true,
 				}
 				java_library {
 					name: "nonbcp_lib2",
@@ -8309,6 +8617,7 @@
 					permitted_packages: ["a.b"],
 					sdk_version: "none",
 					system_modules: "none",
+					compile_dex: true,
 				}
 				apex {
 					name: "myapex",
@@ -8334,6 +8643,7 @@
 					permitted_packages: ["foo.bar"],
 					sdk_version: "none",
 					system_modules: "none",
+					compile_dex: true,
 				}
 				java_library {
 					name: "bcp_lib2",
@@ -8342,6 +8652,7 @@
 					permitted_packages: ["foo.bar", "bar.baz"],
 					sdk_version: "none",
 					system_modules: "none",
+					compile_dex: true,
 				}
 				apex {
 					name: "myapex",
@@ -8372,6 +8683,7 @@
 					sdk_version: "none",
 					min_sdk_version: "29",
 					system_modules: "none",
+					compile_dex: true,
 				}
 				java_library {
 					name: "bcp_lib_unrestricted",
@@ -8381,6 +8693,7 @@
 					sdk_version: "none",
 					min_sdk_version: "29",
 					system_modules: "none",
+					compile_dex: true,
 				}
 				apex {
 					name: "myapex",
@@ -8401,205 +8714,20 @@
 	}
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			rules := createBcpPermittedPackagesRules(tc.bcpPermittedPackages)
 			testBootJarPermittedPackagesRules(t, tc.expectedError, tc.bp, tc.bootJars, rules)
 		})
 	}
 }
 
-func TestTestFor(t *testing.T) {
-	ctx := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["mylib", "myprivlib"],
-			updatable: false,
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			stubs: {
-				versions: ["1"],
-			},
-			apex_available: ["myapex"],
-		}
-
-		cc_library {
-			name: "myprivlib",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: ["myapex"],
-		}
-
-
-		cc_test {
-			name: "mytest",
-			gtest: false,
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			shared_libs: ["mylib", "myprivlib", "mytestlib"],
-			test_for: ["myapex"]
-		}
-
-		cc_library {
-			name: "mytestlib",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			shared_libs: ["mylib", "myprivlib"],
-			stl: "none",
-			test_for: ["myapex"],
-		}
-
-		cc_benchmark {
-			name: "mybench",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			shared_libs: ["mylib", "myprivlib"],
-			stl: "none",
-			test_for: ["myapex"],
-		}
-	`)
-
-	ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
-		ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
-		mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
-		android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
-	}
-
-	// These modules are tests for the apex, therefore are linked to the
-	// actual implementation of mylib instead of its stub.
-	ensureLinkedLibIs("mytest", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
-	ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
-	ensureLinkedLibIs("mybench", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
-}
-
-func TestIndirectTestFor(t *testing.T) {
-	ctx := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["mylib", "myprivlib"],
-			updatable: false,
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			stubs: {
-				versions: ["1"],
-			},
-			apex_available: ["myapex"],
-		}
-
-		cc_library {
-			name: "myprivlib",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			shared_libs: ["mylib"],
-			apex_available: ["myapex"],
-		}
-
-		cc_library {
-			name: "mytestlib",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			shared_libs: ["myprivlib"],
-			stl: "none",
-			test_for: ["myapex"],
-		}
-	`)
-
-	ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
-		ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
-		mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
-		android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
-	}
-
-	// The platform variant of mytestlib links to the platform variant of the
-	// internal myprivlib.
-	ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/myprivlib/", "android_arm64_armv8-a_shared/myprivlib.so")
-
-	// The platform variant of myprivlib links to the platform variant of mylib
-	// and bypasses its stubs.
-	ensureLinkedLibIs("myprivlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
-}
-
-func TestTestForForLibInOtherApex(t *testing.T) {
-	// This case is only allowed for known overlapping APEXes, i.e. the ART APEXes.
-	_ = testApex(t, `
-		apex {
-			name: "com.android.art",
-			key: "myapex.key",
-			native_shared_libs: ["libnativebridge"],
-			updatable: false,
-		}
-
-		apex {
-			name: "com.android.art.debug",
-			key: "myapex.key",
-			native_shared_libs: ["libnativebridge", "libnativebrdige_test"],
-			updatable: false,
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "libnativebridge",
-			srcs: ["libnativebridge.cpp"],
-			system_shared_libs: [],
-			stl: "none",
-			stubs: {
-				versions: ["1"],
-			},
-			apex_available: ["com.android.art", "com.android.art.debug"],
-		}
-
-		cc_library {
-			name: "libnativebrdige_test",
-			srcs: ["mylib.cpp"],
-			system_shared_libs: [],
-			shared_libs: ["libnativebridge"],
-			stl: "none",
-			apex_available: ["com.android.art.debug"],
-			test_for: ["com.android.art"],
-		}
-	`,
-		android.MockFS{
-			"system/sepolicy/apex/com.android.art-file_contexts":       nil,
-			"system/sepolicy/apex/com.android.art.debug-file_contexts": nil,
-		}.AddToFixture())
-}
-
 // TODO(jungjw): Move this to proptools
 func intPtr(i int) *int {
 	return &i
 }
 
 func TestApexSet(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_set {
 			name: "myapex",
@@ -8644,6 +8772,7 @@
 }
 
 func TestApexSet_NativeBridge(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex_set {
 			name: "myapex",
@@ -8668,6 +8797,7 @@
 }
 
 func TestNoStaticLinkingToStubsLib(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `.*required by "mylib" is a native library providing stub.*`, `
 		apex {
 			name: "myapex",
@@ -8705,6 +8835,7 @@
 }
 
 func TestApexKeysTxt(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -8726,6 +8857,7 @@
 }
 
 func TestApexKeysTxtOverrides(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -8770,6 +8902,7 @@
 }
 
 func TestAllowedFiles(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -8826,6 +8959,7 @@
 }
 
 func TestNonPreferredPrebuiltDependency(t *testing.T) {
+	t.Parallel()
 	testApex(t, `
 		apex {
 			name: "myapex",
@@ -8862,6 +8996,7 @@
 }
 
 func TestCompressedApex(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -8898,9 +9033,38 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.capex\n")
 }
 
+func TestCompressedApexIsDisabledWhenUsingErofs(t *testing.T) {
+	t.Parallel()
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			compressible: true,
+			updatable: false,
+			payload_fs_type: "erofs",
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.CompressedApex = proptools.BoolPtr(true)
+		}),
+	)
+
+	compressRule := ctx.ModuleForTests("myapex", "android_common_myapex").MaybeRule("compressRule")
+	if compressRule.Rule != nil {
+		t.Error("erofs apex should not be compressed")
+	}
+}
+
 func TestApexSet_ShouldRespectCompressedApexFlag(t *testing.T) {
+	t.Parallel()
 	for _, compressionEnabled := range []bool{true, false} {
 		t.Run(fmt.Sprintf("compressionEnabled=%v", compressionEnabled), func(t *testing.T) {
+			t.Parallel()
 			ctx := testApex(t, `
 				apex_set {
 					name: "com.company.android.myapex",
@@ -8923,6 +9087,7 @@
 }
 
 func TestPreferredPrebuiltSharedLibDep(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -8975,6 +9140,7 @@
 }
 
 func TestExcludeDependency(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -9025,6 +9191,7 @@
 }
 
 func TestPrebuiltStubLibDep(t *testing.T) {
+	t.Parallel()
 	bpBase := `
 		apex {
 			name: "myapex",
@@ -9117,13 +9284,15 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			for _, otherApexEnabled := range test.otherApexEnabled {
 				t.Run("otherapex_enabled_"+otherApexEnabled, func(t *testing.T) {
+					t.Parallel()
 					ctx := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp)
 
 					type modAndMkEntries struct {
 						mod       *cc.Module
-						mkEntries android.AndroidMkEntries
+						mkEntries android.AndroidMkInfo
 					}
 					entries := []*modAndMkEntries{}
 
@@ -9137,7 +9306,10 @@
 							if !mod.Enabled(android.PanickingConfigAndErrorContext(ctx)) || mod.IsHideFromMake() {
 								continue
 							}
-							for _, ent := range android.AndroidMkEntriesForTest(t, ctx, mod) {
+							info := android.AndroidMkInfoForTest(t, ctx, mod)
+							ents := []android.AndroidMkInfo{info.PrimaryInfo}
+							ents = append(ents, info.ExtraInfo...)
+							for _, ent := range ents {
 								if ent.Disabled {
 									continue
 								}
@@ -9186,6 +9358,7 @@
 }
 
 func TestApexJavaCoverage(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex {
 			name: "myapex",
@@ -9264,6 +9437,7 @@
 }
 
 func TestProhibitStaticExecutable(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `executable mybin is static`, `
 		apex {
 			name: "myapex",
@@ -9315,6 +9489,7 @@
 }
 
 func TestAndroidMk_DexpreoptBuiltInstalledForApex(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -9348,6 +9523,7 @@
 }
 
 func TestAndroidMk_RequiredModules(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -9387,6 +9563,7 @@
 }
 
 func TestAndroidMk_RequiredDeps(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -9411,6 +9588,7 @@
 }
 
 func TestApexOutputFileProducer(t *testing.T) {
+	t.Parallel()
 	for _, tc := range []struct {
 		name          string
 		ref           string
@@ -9428,6 +9606,7 @@
 		},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			ctx := testApex(t, `
 					apex {
 						name: "myapex",
@@ -9459,6 +9638,7 @@
 }
 
 func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		PrepareForTestWithApexBuildComponents,
 		prepareForTestWithMyapex,
@@ -9471,6 +9651,7 @@
 
 	// Test java_sdk_library in bootclasspath_fragment may define higher min_sdk_version than the apex
 	t.Run("bootclasspath_fragment jar has higher min_sdk_version than apex", func(t *testing.T) {
+		t.Parallel()
 		preparer.RunTestWithBp(t, `
 			apex {
 				name: "myapex",
@@ -9527,6 +9708,7 @@
 
 	// Test java_sdk_library in systemserverclasspath_fragment may define higher min_sdk_version than the apex
 	t.Run("systemserverclasspath_fragment jar has higher min_sdk_version than apex", func(t *testing.T) {
+		t.Parallel()
 		preparer.RunTestWithBp(t, `
 			apex {
 				name: "myapex",
@@ -9576,6 +9758,7 @@
 	})
 
 	t.Run("bootclasspath_fragment jar must set min_sdk_version", func(t *testing.T) {
+		t.Parallel()
 		preparer.
 			RunTestWithBp(t, `
 				apex {
@@ -9614,6 +9797,7 @@
 	})
 
 	t.Run("systemserverclasspath_fragment jar must set min_sdk_version", func(t *testing.T) {
+		t.Parallel()
 		preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "mysystemserverclasspathlib".*must set min_sdk_version`)).
 			RunTestWithBp(t, `
 				apex {
@@ -9664,6 +9848,7 @@
 }
 
 func TestApexStrictUpdtabilityLint(t *testing.T) {
+	t.Parallel()
 	bpTemplate := `
 		apex {
 			name: "myapex",
@@ -9685,6 +9870,7 @@
 			},
 			sdk_version: "current",
 			min_sdk_version: "29",
+			compile_dex: true,
 		}
 		`
 	fs := android.MockFS{
@@ -9692,125 +9878,91 @@
 	}
 
 	testCases := []struct {
-		testCaseName              string
-		apexUpdatable             bool
-		javaStrictUpdtabilityLint bool
-		lintFileExists            bool
-		disallowedFlagExpected    bool
+		testCaseName                    string
+		apexUpdatable                   bool
+		javaStrictUpdtabilityLint       bool
+		lintFileExists                  bool
+		disallowedFlagExpectedOnApex    bool
+		disallowedFlagExpectedOnJavalib bool
 	}{
 		{
-			testCaseName:              "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
-			apexUpdatable:             true,
-			javaStrictUpdtabilityLint: true,
-			lintFileExists:            false,
-			disallowedFlagExpected:    false,
+			testCaseName:                    "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
+			apexUpdatable:                   true,
+			javaStrictUpdtabilityLint:       true,
+			lintFileExists:                  false,
+			disallowedFlagExpectedOnApex:    false,
+			disallowedFlagExpectedOnJavalib: false,
 		},
 		{
-			testCaseName:              "non-updatable apex respects strict_updatability of javalib",
-			apexUpdatable:             false,
-			javaStrictUpdtabilityLint: false,
-			lintFileExists:            true,
-			disallowedFlagExpected:    false,
+			testCaseName:                    "non-updatable apex respects strict_updatability of javalib",
+			apexUpdatable:                   false,
+			javaStrictUpdtabilityLint:       false,
+			lintFileExists:                  true,
+			disallowedFlagExpectedOnApex:    false,
+			disallowedFlagExpectedOnJavalib: false,
 		},
 		{
-			testCaseName:              "non-updatable apex respects strict updatability of javalib",
-			apexUpdatable:             false,
-			javaStrictUpdtabilityLint: true,
-			lintFileExists:            true,
-			disallowedFlagExpected:    true,
+			testCaseName:                    "non-updatable apex respects strict updatability of javalib",
+			apexUpdatable:                   false,
+			javaStrictUpdtabilityLint:       true,
+			lintFileExists:                  true,
+			disallowedFlagExpectedOnApex:    false,
+			disallowedFlagExpectedOnJavalib: true,
 		},
 		{
-			testCaseName:              "updatable apex sets strict updatability of javalib to true",
-			apexUpdatable:             true,
-			javaStrictUpdtabilityLint: false, // will be set to true by mutator
-			lintFileExists:            true,
-			disallowedFlagExpected:    true,
+			testCaseName:                    "updatable apex checks strict updatability of javalib",
+			apexUpdatable:                   true,
+			javaStrictUpdtabilityLint:       false,
+			lintFileExists:                  true,
+			disallowedFlagExpectedOnApex:    true,
+			disallowedFlagExpectedOnJavalib: false,
 		},
 	}
 
 	for _, testCase := range testCases {
-		fixtures := []android.FixturePreparer{}
-		baselineProperty := ""
-		if testCase.lintFileExists {
-			fixtures = append(fixtures, fs.AddToFixture())
-			baselineProperty = "baseline_filename: \"lint-baseline.xml\""
-		}
-		bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint, baselineProperty)
-
-		result := testApex(t, bp, fixtures...)
-		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
-		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi")
-
-		if disallowedFlagActual != testCase.disallowedFlagExpected {
-			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
-		}
-	}
-}
-
-func TestUpdatabilityLintSkipLibcore(t *testing.T) {
-	bp := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			java_libs: ["myjavalib"],
-			updatable: true,
-			min_sdk_version: "29",
-		}
-		apex_key {
-			name: "myapex.key",
-		}
-		java_library {
-			name: "myjavalib",
-			srcs: ["MyClass.java"],
-			apex_available: [ "myapex" ],
-			sdk_version: "current",
-			min_sdk_version: "29",
-			lint: {
-				baseline_filename: "lint-baseline.xml",
+		t.Run(testCase.testCaseName, func(t *testing.T) {
+			t.Parallel()
+			fixtures := []android.FixturePreparer{}
+			baselineProperty := ""
+			if testCase.lintFileExists {
+				fixtures = append(fixtures, fs.AddToFixture())
+				baselineProperty = "baseline_filename: \"lint-baseline.xml\""
 			}
-		}
-		`
+			bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint, baselineProperty)
 
-	testCases := []struct {
-		testCaseName           string
-		moduleDirectory        string
-		disallowedFlagExpected bool
-	}{
-		{
-			testCaseName:           "lintable module defined outside libcore",
-			moduleDirectory:        "",
-			disallowedFlagExpected: true,
-		},
-		{
-			testCaseName:           "lintable module defined in libcore root directory",
-			moduleDirectory:        "libcore/",
-			disallowedFlagExpected: false,
-		},
-		{
-			testCaseName:           "lintable module defined in libcore child directory",
-			moduleDirectory:        "libcore/childdir/",
-			disallowedFlagExpected: true,
-		},
-	}
+			result := testApex(t, bp, fixtures...)
 
-	for _, testCase := range testCases {
-		lintFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"lint-baseline.xml", "")
-		bpFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"Android.bp", bp)
-		result := testApex(t, "", lintFileCreator, bpFileCreator)
-		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-		sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
-		cmdFlags := fmt.Sprintf("--baseline %vlint-baseline.xml --disallowed_issues NewApi", testCase.moduleDirectory)
-		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, cmdFlags)
+			checkModule := func(m android.TestingBuildParams, name string, expectStrictUpdatability bool) {
+				if expectStrictUpdatability {
+					if m.Rule == nil {
+						t.Errorf("expected strict updatability check rule on %s", name)
+					} else {
+						android.AssertStringDoesContain(t, fmt.Sprintf("strict updatability check rule for %s", name),
+							m.RuleParams.Command, "--disallowed_issues NewApi")
+						android.AssertStringListContains(t, fmt.Sprintf("strict updatability check baselines for %s", name),
+							m.Inputs.Strings(), "lint-baseline.xml")
+					}
+				} else {
+					if m.Rule != nil {
+						t.Errorf("expected no strict updatability check rule on %s", name)
+					}
+				}
+			}
 
-		if disallowedFlagActual != testCase.disallowedFlagExpected {
-			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
-		}
+			myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+			apex := result.ModuleForTests("myapex", "android_common_myapex")
+			apexStrictUpdatabilityCheck := apex.MaybeOutput("lint_strict_updatability_check.stamp")
+			javalibStrictUpdatabilityCheck := myjavalib.MaybeOutput("lint_strict_updatability_check.stamp")
+
+			checkModule(apexStrictUpdatabilityCheck, "myapex", testCase.disallowedFlagExpectedOnApex)
+			checkModule(javalibStrictUpdatabilityCheck, "myjavalib", testCase.disallowedFlagExpectedOnJavalib)
+		})
 	}
 }
 
 // checks transtive deps of an apex coming from bootclasspath_fragment
 func TestApexStrictUpdtabilityLintBcpFragmentDeps(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex {
 			name: "myapex",
@@ -9847,14 +9999,16 @@
 	}
 
 	result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture())
-	myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") {
-		t.Errorf("Strict updabality lint missing in myjavalib coming from bootclasspath_fragment mybootclasspath-fragment\nActual lint cmd: %v", *sboxProto.Commands[0].Command)
-	}
+	apex := result.ModuleForTests("myapex", "android_common_myapex")
+	apexStrictUpdatabilityCheck := apex.Output("lint_strict_updatability_check.stamp")
+	android.AssertStringDoesContain(t, "strict updatability check rule for myapex",
+		apexStrictUpdatabilityCheck.RuleParams.Command, "--disallowed_issues NewApi")
+	android.AssertStringListContains(t, "strict updatability check baselines for myapex",
+		apexStrictUpdatabilityCheck.Inputs.Strings(), "lint-baseline.xml")
 }
 
 func TestApexLintBcpFragmentSdkLibDeps(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex {
 			name: "myapex",
@@ -9906,11 +10060,12 @@
 
 // updatable apexes should propagate updatable=true to its apps
 func TestUpdatableApexEnforcesAppUpdatability(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			updatable: %v,
+			updatable: true,
 			apps: [
 				"myapp",
 			],
@@ -9921,7 +10076,6 @@
 		}
 		android_app {
 			name: "myapp",
-			updatable: %v,
 			apex_available: [
 				"myapex",
 			],
@@ -9929,99 +10083,14 @@
 			min_sdk_version: "30",
 		}
 		`
-	testCases := []struct {
-		name                      string
-		apex_is_updatable_bp      bool
-		app_is_updatable_bp       bool
-		app_is_updatable_expected bool
-	}{
-		{
-			name:                      "Non-updatable apex respects updatable property of non-updatable app",
-			apex_is_updatable_bp:      false,
-			app_is_updatable_bp:       false,
-			app_is_updatable_expected: false,
-		},
-		{
-			name:                      "Non-updatable apex respects updatable property of updatable app",
-			apex_is_updatable_bp:      false,
-			app_is_updatable_bp:       true,
-			app_is_updatable_expected: true,
-		},
-		{
-			name:                      "Updatable apex respects updatable property of updatable app",
-			apex_is_updatable_bp:      true,
-			app_is_updatable_bp:       true,
-			app_is_updatable_expected: true,
-		},
-		{
-			name:                      "Updatable apex sets updatable=true on non-updatable app",
-			apex_is_updatable_bp:      true,
-			app_is_updatable_bp:       false,
-			app_is_updatable_expected: true,
-		},
-	}
-	for _, testCase := range testCases {
-		result := testApex(t, fmt.Sprintf(bp, testCase.apex_is_updatable_bp, testCase.app_is_updatable_bp))
-		myapp := result.ModuleForTests("myapp", "android_common").Module().(*java.AndroidApp)
-		android.AssertBoolEquals(t, testCase.name, testCase.app_is_updatable_expected, myapp.Updatable())
-	}
-}
-
-func TestTrimmedApex(t *testing.T) {
-	bp := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["libfoo","libbaz"],
-			min_sdk_version: "29",
-			trim_against: "mydcla",
-    }
-		apex {
-			name: "mydcla",
-			key: "myapex.key",
-			native_shared_libs: ["libfoo","libbar"],
-			min_sdk_version: "29",
-			file_contexts: ":myapex-file_contexts",
-			dynamic_common_lib_apex: true,
-		}
-		apex_key {
-			name: "myapex.key",
-		}
-		cc_library {
-			name: "libfoo",
-			shared_libs: ["libc"],
-			apex_available: ["myapex","mydcla"],
-			min_sdk_version: "29",
-		}
-		cc_library {
-			name: "libbar",
-			shared_libs: ["libc"],
-			apex_available: ["myapex","mydcla"],
-			min_sdk_version: "29",
-		}
-		cc_library {
-			name: "libbaz",
-			shared_libs: ["libc"],
-			apex_available: ["myapex","mydcla"],
-			min_sdk_version: "29",
-		}
-		`
-	ctx := testApex(t, bp)
-	module := ctx.ModuleForTests("myapex", "android_common_myapex")
-	apexRule := module.MaybeRule("apexRule")
-	if apexRule.Rule == nil {
-		t.Errorf("Expecting regular apex rule but a non regular apex rule found")
-	}
-
-	ctx = testApex(t, bp, android.FixtureModifyConfig(android.SetTrimmedApexEnabledForTests))
-	trimmedApexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("TrimmedApexRule")
-	libs_to_trim := trimmedApexRule.Args["libs_to_trim"]
-	android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libfoo")
-	android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libbar")
-	android.AssertStringDoesNotContain(t, "unexpected libs in the libs to trim", libs_to_trim, "libbaz")
+	_ = android.GroupFixturePreparers(
+		prepareForApexTest,
+	).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("app dependency myapp must have updatable: true")).
+		RunTestWithBp(t, bp)
 }
 
 func TestCannedFsConfig(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -10042,6 +10111,7 @@
 }
 
 func TestCannedFsConfig_HasCustomConfig(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -10064,6 +10134,7 @@
 }
 
 func TestStubLibrariesMultipleApexViolation(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		desc          string
 		hasStubs      bool
@@ -10138,6 +10209,7 @@
 }
 
 func TestFileSystemShouldSkipApexLibraries(t *testing.T) {
+	t.Parallel()
 	context := android.GroupFixturePreparers(
 		android.PrepareForIntegrationTestWithAndroid,
 		cc.PrepareForIntegrationTestWithCc,
@@ -10151,7 +10223,10 @@
 			deps: [
 				"libfoo",
 			],
-			linker_config_src: "linker.config.json",
+			linker_config: {
+				gen_linker_config: true,
+				linker_config_srcs: ["linker.config.json"],
+			},
 		}
 
 		cc_library {
@@ -10207,6 +10282,7 @@
 `
 
 func TestAconfigFilesJavaDeps(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, apex_default_bp+`
 		apex {
 			name: "myapex",
@@ -10229,6 +10305,7 @@
 			apex_available: [
 				"myapex",
 			],
+			compile_dex: true,
 		}
 
 		java_library {
@@ -10240,6 +10317,7 @@
 			apex_available: [
 				"myapex",
 			],
+			compile_dex: true,
 		}
 
 		aconfig_declarations {
@@ -10276,14 +10354,15 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 12 {
-		t.Fatalf("Expected 12 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 14 {
+		t.Fatalf("Expected 14 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/package.map .*/image.apex/etc/package.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.map .*/image.apex/etc/flag.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.val .*/image.apex/etc/flag.val")
+	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.info.*/image.apex/etc/flag.info")
 
 	inputs := []string{
 		"my_aconfig_declarations_foo/intermediate.pb",
@@ -10293,9 +10372,11 @@
 	VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_info_file", inputs, "android_common_myapex/flag.info", "myapex", "flag_info")
 }
 
 func TestAconfigFilesJavaAndCcDeps(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, apex_default_bp+`
 		apex {
 			name: "myapex",
@@ -10323,6 +10404,7 @@
 			apex_available: [
 				"myapex",
 			],
+			compile_dex: true,
 		}
 
 		cc_library {
@@ -10411,14 +10493,15 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 16 {
-		t.Fatalf("Expected 16 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 18 {
+		t.Fatalf("Expected 18 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/package.map .*/image.apex/etc/package.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.map .*/image.apex/etc/flag.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.val .*/image.apex/etc/flag.val")
+	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.info .*/image.apex/etc/flag.info")
 
 	inputs := []string{
 		"my_aconfig_declarations_foo/intermediate.pb",
@@ -10429,9 +10512,11 @@
 	VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_info_file", inputs, "android_common_myapex/flag.info", "myapex", "flag_info")
 }
 
 func TestAconfigFilesRustDeps(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, apex_default_bp+`
 		apex {
 			name: "myapex",
@@ -10579,14 +10664,15 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 36 {
-		t.Fatalf("Expected 36 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 38 {
+		t.Fatalf("Expected 38 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/package.map .*/image.apex/etc/package.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.map .*/image.apex/etc/flag.map")
 	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.val .*/image.apex/etc/flag.val")
+	ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.info .*/image.apex/etc/flag.info")
 
 	inputs := []string{
 		"my_aconfig_declarations_foo/intermediate.pb",
@@ -10598,6 +10684,7 @@
 	VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map")
 	VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val")
+	VerifyAconfigRule(t, &mod, "create_aconfig_flag_info_file", inputs, "android_common_myapex/flag.info", "myapex", "flag_info")
 }
 
 func VerifyAconfigRule(t *testing.T, mod *android.TestingModule, desc string, inputs []string, output string, container string, file_type string) {
@@ -10621,6 +10708,7 @@
 }
 
 func TestAconfigFilesOnlyMatchCurrentApex(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, apex_default_bp+`
 		apex {
 			name: "myapex",
@@ -10643,6 +10731,7 @@
 			apex_available: [
 				"myapex",
 			],
+			compile_dex: true,
 		}
 
 		java_library {
@@ -10654,6 +10743,7 @@
 			apex_available: [
 				"myapex",
 			],
+			compile_dex: true,
 		}
 
 		aconfig_declarations {
@@ -10705,6 +10795,7 @@
 }
 
 func TestAconfigFilesRemoveDuplicates(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, apex_default_bp+`
 		apex {
 			name: "myapex",
@@ -10727,6 +10818,7 @@
 			apex_available: [
 				"myapex",
 			],
+			compile_dex: true,
 		}
 
 		java_library {
@@ -10738,6 +10830,7 @@
 			apex_available: [
 				"myapex",
 			],
+			compile_dex: true,
 		}
 
 		aconfig_declarations {
@@ -10784,6 +10877,7 @@
 // Test that the boot jars come from the _selected_ apex prebuilt
 // RELEASE_APEX_CONTIRBUTIONS_* build flags will be used to select the correct prebuilt for a specific release config
 func TestBootDexJarsMultipleApexPrebuilts(t *testing.T) {
+	t.Parallel()
 	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
 		t.Helper()
 		s := ctx.ModuleForTests("dex_bootjars", "android_common")
@@ -10972,6 +11066,7 @@
 // Test that product packaging installs the selected mainline module (either source or a specific prebuilt)
 // RELEASE_APEX_CONTIRBUTIONS_* build flags will be used to select the correct prebuilt for a specific release config
 func TestInstallationRulesForMultipleApexPrebuilts(t *testing.T) {
+	t.Parallel()
 	// for a mainline module family, check that only the flagged soong module is visible to make
 	checkHideFromMake := func(t *testing.T, ctx *android.TestContext, visibleModuleName string, hiddenModuleNames []string) {
 		variation := func(moduleName string) string {
@@ -11117,6 +11212,7 @@
 
 // Test that product packaging installs the selected mainline module in workspaces withtout source mainline module
 func TestInstallationRulesForMultipleApexPrebuiltsWithoutSource(t *testing.T) {
+	t.Parallel()
 	// for a mainline module family, check that only the flagged soong module is visible to make
 	checkHideFromMake := func(t *testing.T, ctx *android.TestContext, visibleModuleNames []string, hiddenModuleNames []string) {
 		variation := func(moduleName string) string {
@@ -11224,6 +11320,7 @@
 }
 
 func TestAconfifDeclarationsValidation(t *testing.T) {
+	t.Parallel()
 	aconfigDeclarationLibraryString := func(moduleNames []string) (ret string) {
 		for _, moduleName := range moduleNames {
 			ret += fmt.Sprintf(`
@@ -11257,7 +11354,7 @@
 		}
 		filegroup {
 			name: "qux-filegroup",
-			srcs: [
+			device_common_srcs: [
 				":qux-lib{.generated_srcjars}",
 			],
 		}
@@ -11340,6 +11437,7 @@
 }
 
 func TestMultiplePrebuiltsWithSameBase(t *testing.T) {
+	t.Parallel()
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -11379,6 +11477,7 @@
 }
 
 func TestApexMinSdkVersionOverride(t *testing.T) {
+	t.Parallel()
 	checkMinSdkVersion := func(t *testing.T, module android.TestingModule, expectedMinSdkVersion string) {
 		args := module.Rule("apexRule").Args
 		optFlags := args["opt_flags"]
@@ -11414,6 +11513,7 @@
 			apex_available: ["com.android.apex30"],
 			min_sdk_version: "30",
 			sdk_version: "current",
+			compile_dex: true,
 		}
 
 		override_apex {
@@ -11455,6 +11555,7 @@
 }
 
 func TestOverrideApexWithPrebuiltApexPreferred(t *testing.T) {
+	t.Parallel()
 	context := android.GroupFixturePreparers(
 		android.PrepareForIntegrationTestWithAndroid,
 		PrepareForTestWithApexBuildComponents,
@@ -11490,6 +11591,7 @@
 }
 
 func TestUpdatableApexMinSdkVersionCurrent(t *testing.T) {
+	t.Parallel()
 	testApexError(t, `"myapex" .*: updatable: updatable APEXes should not set min_sdk_version to current. Please use a finalized API level or a recognized in-development codename`, `
 		apex {
 			name: "myapex",
@@ -11507,6 +11609,7 @@
 }
 
 func TestPrebuiltStubNoinstall(t *testing.T) {
+	t.Parallel()
 	testFunc := func(t *testing.T, expectLibfooOnSystemLib bool, fs android.MockFS) {
 		result := android.GroupFixturePreparers(
 			prepareForApexTest,
@@ -11586,6 +11689,7 @@
 	`)
 
 	t.Run("prebuilt stub (without source): no install", func(t *testing.T) {
+		t.Parallel()
 		testFunc(
 			t,
 			/*expectLibfooOnSystemLib=*/ false,
@@ -11610,6 +11714,7 @@
 	`)
 
 	t.Run("prebuilt stub (with disabled source): no install", func(t *testing.T) {
+		t.Parallel()
 		testFunc(
 			t,
 			/*expectLibfooOnSystemLib=*/ false,
@@ -11625,6 +11730,7 @@
 }
 
 func TestSdkLibraryTransitiveClassLoaderContext(t *testing.T) {
+	t.Parallel()
 	// This test case tests that listing the impl lib instead of the top level java_sdk_library
 	// in libs of android_app and java_library does not lead to class loader context device/host
 	// path mismatch errors.
@@ -11699,6 +11805,7 @@
 				"com.android.foo30",
 			],
 			sdk_version: "core_current",
+			compile_dex: true,
 		}
 
 		java_library {
@@ -11727,6 +11834,485 @@
 			sdk_version: "core_current",
 			min_sdk_version: "30",
 			manifest: "AndroidManifest.xml",
+			updatable: true,
 		}
        `)
 }
+
+// If an apex sets system_ext_specific: true, its systemserverclasspath libraries must set this property as well.
+func TestApexSSCPJarMustBeInSamePartitionAsApex(t *testing.T) {
+	t.Parallel()
+	testApexError(t, `foo is an apex systemserver jar, but its partition does not match the partition of its containing apex`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			systemserverclasspath_fragments: [
+				"mysystemserverclasspathfragment",
+			],
+			min_sdk_version: "29",
+			updatable: true,
+			system_ext_specific: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			min_sdk_version: "29",
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+			sdk_version: "current",
+		}
+
+		systemserverclasspath_fragment {
+			name: "mysystemserverclasspathfragment",
+			contents: [
+				"foo",
+			],
+			apex_available: [
+				"myapex",
+			],
+		}
+	`,
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+	)
+}
+
+// partitions should not package the artifacts that are included inside the apex.
+func TestFilesystemWithApexDeps(t *testing.T) {
+	t.Parallel()
+	result := testApex(t, `
+		android_filesystem {
+			name: "myfilesystem",
+			deps: ["myapex"],
+		}
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			binaries: ["binfoo"],
+			native_shared_libs: ["libfoo"],
+			apps: ["appfoo"],
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		cc_binary {
+			name: "binfoo",
+			apex_available: ["myapex"],
+		}
+		cc_library {
+			name: "libfoo",
+			apex_available: ["myapex"],
+		}
+		android_app {
+			name: "appfoo",
+			sdk_version: "current",
+			apex_available: ["myapex"],
+		}
+	`, filesystem.PrepareForTestWithFilesystemBuildComponents)
+
+	partition := result.ModuleForTests("myfilesystem", "android_common")
+	fileList := android.ContentFromFileRuleForTests(t, result, partition.Output("fileList"))
+	android.AssertDeepEquals(t, "filesystem with apex", "apex/myapex.apex\n", fileList)
+}
+
+func TestApexVerifyNativeImplementationLibs(t *testing.T) {
+	t.Parallel()
+
+	extractDepenencyPathFromErrors := func(errs []error) []string {
+		i := slices.IndexFunc(errs, func(err error) bool {
+			return strings.Contains(err.Error(), "dependency path:")
+		})
+		if i < 0 {
+			return nil
+		}
+		var dependencyPath []string
+		for _, err := range errs[i+1:] {
+			s := err.Error()
+			lastSpace := strings.LastIndexByte(s, ' ')
+			if lastSpace >= 0 {
+				dependencyPath = append(dependencyPath, s[lastSpace+1:])
+			}
+		}
+		return dependencyPath
+	}
+
+	checkErrors := func(wantDependencyPath []string) func(t *testing.T, result *android.TestResult) {
+		return func(t *testing.T, result *android.TestResult) {
+			t.Helper()
+			if len(result.Errs) == 0 {
+				t.Fatalf("expected errors")
+			}
+			t.Log("found errors:")
+			for _, err := range result.Errs {
+				t.Log(err)
+			}
+			if g, w := result.Errs[0].Error(), "library in apex transitively linked against implementation library"; !strings.Contains(g, w) {
+				t.Fatalf("expected error %q, got %q", w, g)
+			}
+			dependencyPath := extractDepenencyPathFromErrors(result.Errs)
+			if g, w := dependencyPath, wantDependencyPath; !slices.Equal(g, w) {
+				t.Errorf("expected dependency path %q, got %q", w, g)
+			}
+		}
+	}
+
+	addToSharedLibs := func(module, lib string) func(bp *bpmodify.Blueprint) {
+		return func(bp *bpmodify.Blueprint) {
+			m := bp.ModulesByName(module)
+			props, err := m.GetOrCreateProperty(bpmodify.List, "shared_libs")
+			if err != nil {
+				panic(err)
+			}
+			props.AddStringToList(lib)
+		}
+	}
+
+	bpTemplate := `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["mylib"],
+		rust_dyn_libs: ["libmyrust"],
+		binaries: ["mybin", "myrustbin"],
+		jni_libs: ["libjni"],
+		apps: ["myapp"],
+		updatable: false,
+	}
+
+	apex {
+		name: "otherapex",
+		key: "myapex.key",
+		native_shared_libs: ["libotherapex"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "mylib",
+		srcs: ["foo.cpp"],
+		apex_available: ["myapex"],
+	}
+
+	cc_binary {
+		name: "mybin",
+		srcs: ["foo.cpp"],
+		apex_available: ["myapex"],
+	}
+
+	rust_library {
+		name: "libmyrust",
+		crate_name: "myrust",
+		srcs: ["src/lib.rs"],
+		rustlibs: ["libmyrust_transitive_dylib"],
+		rlibs: ["libmyrust_transitive_rlib"],
+		apex_available: ["myapex"],
+	}
+
+	rust_library{
+		name: "libmyrust_transitive_dylib",
+		crate_name: "myrust_transitive_dylib",
+		srcs: ["src/lib.rs"],
+		apex_available: ["myapex"],
+	}
+
+	rust_library {
+		name: "libmyrust_transitive_rlib",
+		crate_name: "myrust_transitive_rlib",
+		srcs: ["src/lib.rs"],
+		apex_available: ["myapex"],
+	}
+
+	rust_binary {
+		name: "myrustbin",
+		srcs: ["src/main.rs"],
+		apex_available: ["myapex"],
+	}
+
+	cc_library {
+		name: "libbar",
+		sdk_version: "current",
+		srcs: ["bar.cpp"],
+		apex_available: ["myapex"],
+		stl: "none",
+	}
+
+	android_app {
+		name: "myapp",
+		jni_libs: ["libembeddedjni"],
+		use_embedded_native_libs: true,
+		sdk_version: "current",
+		apex_available: ["myapex"],
+	}
+
+	cc_library {
+		name: "libembeddedjni",
+		sdk_version: "current",
+		srcs: ["bar.cpp"],
+		apex_available: ["myapex"],
+		stl: "none",
+	}
+
+	cc_library {
+		name: "libjni",
+		sdk_version: "current",
+		srcs: ["bar.cpp"],
+		apex_available: ["myapex"],
+		stl: "none",
+	}
+
+	cc_library {
+		name: "libotherapex",
+		sdk_version: "current",
+		srcs: ["otherapex.cpp"],
+		apex_available: ["otherapex"],
+		stubs: {
+			symbol_file: "libotherapex.map.txt",
+			versions: ["1", "2", "3"],
+		},
+		stl: "none",
+	}
+
+	cc_library {
+		name: "libplatform",
+		sdk_version: "current",
+		srcs: ["libplatform.cpp"],
+		stubs: {
+			symbol_file: "libplatform.map.txt",
+			versions: ["1", "2", "3"],
+		},
+		stl: "none",
+		system_shared_libs: [],
+	}
+	`
+
+	testCases := []struct {
+		name           string
+		bpModifier     func(bp *bpmodify.Blueprint)
+		dependencyPath []string
+	}{
+		{
+			name:           "library dependency in other apex",
+			bpModifier:     addToSharedLibs("mylib", "libotherapex#impl"),
+			dependencyPath: []string{"myapex", "mylib", "libotherapex"},
+		},
+		{
+			name: "transitive library dependency in other apex",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("mylib", "libbar")(bp)
+				addToSharedLibs("libbar", "libotherapex#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "mylib", "libbar", "libotherapex"},
+		},
+		{
+			name:           "library dependency in platform",
+			bpModifier:     addToSharedLibs("mylib", "libplatform#impl"),
+			dependencyPath: []string{"myapex", "mylib", "libplatform"},
+		},
+		{
+			name:           "jni library dependency in other apex",
+			bpModifier:     addToSharedLibs("libjni", "libotherapex#impl"),
+			dependencyPath: []string{"myapex", "libjni", "libotherapex"},
+		},
+		{
+			name: "transitive jni library dependency in other apex",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("libjni", "libbar")(bp)
+				addToSharedLibs("libbar", "libotherapex#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "libjni", "libbar", "libotherapex"},
+		},
+		{
+			name:           "jni library dependency in platform",
+			bpModifier:     addToSharedLibs("libjni", "libplatform#impl"),
+			dependencyPath: []string{"myapex", "libjni", "libplatform"},
+		},
+		{
+			name: "transitive jni library dependency in platform",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("libjni", "libbar")(bp)
+				addToSharedLibs("libbar", "libplatform#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "libjni", "libbar", "libplatform"},
+		},
+		{
+			name:           "app jni library dependency in other apex",
+			bpModifier:     addToSharedLibs("libembeddedjni", "libotherapex#impl"),
+			dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libotherapex"},
+		},
+		{
+			name: "transitive app jni library dependency in other apex",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("libembeddedjni", "libbar")(bp)
+				addToSharedLibs("libbar", "libotherapex#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libbar", "libotherapex"},
+		},
+		{
+			name:           "app jni library dependency in platform",
+			bpModifier:     addToSharedLibs("libembeddedjni", "libplatform#impl"),
+			dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libplatform"},
+		},
+		{
+			name: "transitive app jni library dependency in platform",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("libembeddedjni", "libbar")(bp)
+				addToSharedLibs("libbar", "libplatform#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libbar", "libplatform"},
+		},
+		{
+			name:           "binary dependency in other apex",
+			bpModifier:     addToSharedLibs("mybin", "libotherapex#impl"),
+			dependencyPath: []string{"myapex", "mybin", "libotherapex"},
+		},
+		{
+			name: "transitive binary dependency in other apex",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("mybin", "libbar")(bp)
+				addToSharedLibs("libbar", "libotherapex#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "mybin", "libbar", "libotherapex"},
+		},
+		{
+			name:           "binary dependency in platform",
+			bpModifier:     addToSharedLibs("mybin", "libplatform#impl"),
+			dependencyPath: []string{"myapex", "mybin", "libplatform"},
+		},
+		{
+			name: "transitive binary dependency in platform",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("mybin", "libbar")(bp)
+				addToSharedLibs("libbar", "libplatform#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "mybin", "libbar", "libplatform"},
+		},
+
+		{
+			name:           "rust library dependency in other apex",
+			bpModifier:     addToSharedLibs("libmyrust", "libotherapex#impl"),
+			dependencyPath: []string{"myapex", "libmyrust", "libotherapex"},
+		},
+		{
+			name: "transitive rust library dependency in other apex",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("libmyrust", "libbar")(bp)
+				addToSharedLibs("libbar", "libotherapex#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "libmyrust", "libbar", "libotherapex"},
+		},
+		{
+			name:           "rust library dependency in platform",
+			bpModifier:     addToSharedLibs("libmyrust", "libplatform#impl"),
+			dependencyPath: []string{"myapex", "libmyrust", "libplatform"},
+		},
+		{
+			name: "transitive rust library dependency in platform",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("libmyrust", "libbar")(bp)
+				addToSharedLibs("libbar", "libplatform#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "libmyrust", "libbar", "libplatform"},
+		},
+		{
+			name: "transitive rust library dylib dependency in other apex",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("libmyrust_transitive_dylib", "libotherapex#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_dylib", "libotherapex"},
+		},
+		{
+			name: "transitive rust library dylib dependency in platform",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("libmyrust_transitive_dylib", "libplatform#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_dylib", "libplatform"},
+		},
+		{
+			name: "transitive rust library rlib dependency in other apex",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("libmyrust_transitive_rlib", "libotherapex#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_rlib", "libotherapex"},
+		},
+		{
+			name: "transitive rust library rlib dependency in platform",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("libmyrust_transitive_rlib", "libplatform#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_rlib", "libplatform"},
+		},
+		{
+			name:           "rust binary dependency in other apex",
+			bpModifier:     addToSharedLibs("myrustbin", "libotherapex#impl"),
+			dependencyPath: []string{"myapex", "myrustbin", "libotherapex"},
+		},
+		{
+			name: "transitive rust binary dependency in other apex",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("myrustbin", "libbar")(bp)
+				addToSharedLibs("libbar", "libotherapex#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "myrustbin", "libbar", "libotherapex"},
+		},
+		{
+			name:           "rust binary dependency in platform",
+			bpModifier:     addToSharedLibs("myrustbin", "libplatform#impl"),
+			dependencyPath: []string{"myapex", "myrustbin", "libplatform"},
+		},
+		{
+			name: "transitive rust binary dependency in platform",
+			bpModifier: func(bp *bpmodify.Blueprint) {
+				addToSharedLibs("myrustbin", "libbar")(bp)
+				addToSharedLibs("libbar", "libplatform#impl")(bp)
+			},
+			dependencyPath: []string{"myapex", "myrustbin", "libbar", "libplatform"},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			bp, err := bpmodify.NewBlueprint("", []byte(bpTemplate))
+			if err != nil {
+				t.Fatal(err)
+			}
+			if testCase.bpModifier != nil {
+				func() {
+					defer func() {
+						if r := recover(); r != nil {
+							t.Fatalf("panic in bpModifier: %v", r)
+						}
+					}()
+					testCase.bpModifier(bp)
+				}()
+			}
+			android.GroupFixturePreparers(
+				android.PrepareForTestWithAndroidBuildComponents,
+				cc.PrepareForTestWithCcBuildComponents,
+				java.PrepareForTestWithDexpreopt,
+				rust.PrepareForTestWithRustDefaultModules,
+				PrepareForTestWithApexBuildComponents,
+				prepareForTestWithMyapex,
+				prepareForTestWithOtherapex,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.BuildId = proptools.StringPtr("TEST.BUILD_ID")
+				}),
+			).ExtendWithErrorHandler(android.FixtureCustomErrorHandler(checkErrors(testCase.dependencyPath))).
+				RunTestWithBp(t, bp.String())
+		})
+	}
+}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index e44d3f5..71a8246 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -47,6 +47,7 @@
 )
 
 func TestBootclasspathFragments_FragmentDependency(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		// Configure some libraries in the art bootclasspath_fragment and platform_bootclasspath.
@@ -178,6 +179,7 @@
 }
 
 func TestBootclasspathFragmentInArtApex(t *testing.T) {
+	t.Parallel()
 	commonPreparer := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		prepareForTestWithArtApex,
@@ -298,6 +300,7 @@
 	}
 
 	t.Run("boot image files from source", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -329,6 +332,7 @@
 	})
 
 	t.Run("boot image files from source of override apex", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -349,6 +353,7 @@
 	})
 
 	t.Run("generate boot image profile even if dexpreopt is disabled", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -369,6 +374,7 @@
 	})
 
 	t.Run("boot image disable generate profile", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -387,6 +393,7 @@
 	})
 
 	t.Run("boot image files with preferred prebuilt", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -431,6 +438,7 @@
 	})
 
 	t.Run("source with inconsistency between config and contents", func(t *testing.T) {
+		t.Parallel()
 		android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -444,6 +452,7 @@
 	})
 
 	t.Run("prebuilt with inconsistency between config and contents", func(t *testing.T) {
+		t.Parallel()
 		android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -457,6 +466,7 @@
 	})
 
 	t.Run("preferred prebuilt with inconsistency between config and contents", func(t *testing.T) {
+		t.Parallel()
 		android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -473,6 +483,7 @@
 	})
 
 	t.Run("source preferred and prebuilt with inconsistency between config and contents", func(t *testing.T) {
+		t.Parallel()
 		android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -491,6 +502,7 @@
 }
 
 func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) {
+	t.Parallel()
 	preparers := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		prepareForTestWithArtApex,
@@ -571,6 +583,7 @@
 	`
 
 	t.Run("disabled alternative APEX", func(t *testing.T) {
+		t.Parallel()
 		result := preparers.RunTestWithBp(t, fmt.Sprintf(bp, "enabled: false,"))
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
@@ -614,6 +627,7 @@
 }
 
 func TestBootclasspathFragmentContentsNoName(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		prepareForTestWithMyapex,
@@ -722,6 +736,7 @@
 // TestBootclasspathFragment_HiddenAPIList checks to make sure that the correct parameters are
 // passed to the hiddenapi list tool.
 func TestBootclasspathFragment_HiddenAPIList(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		prepareForTestWithArtApex,
@@ -877,6 +892,7 @@
 // additional_stubs: ["android-non-updatable"] causes the source android-non-updatable modules to be
 // added to the hiddenapi list tool.
 func TestBootclasspathFragment_AndroidNonUpdatable_FromSource(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		prepareForTestWithArtApex,
@@ -1050,6 +1066,7 @@
 }
 
 func TestBootclasspathFragment_AndroidNonUpdatable_FromText(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		prepareForTestWithArtApex,
@@ -1217,6 +1234,7 @@
 // setting additional_stubs: ["android-non-updatable"] causes the prebuilt android-non-updatable
 // modules to be added to the hiddenapi list tool.
 func TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		java.PrepareForTestWithDexpreopt,
@@ -1377,6 +1395,7 @@
 }
 
 func TestBootclasspathFragmentProtoContainsMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		prepareForTestWithMyapex,
diff --git a/apex/builder.go b/apex/builder.go
index a851d12..41e2511 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -43,7 +43,6 @@
 	pctx.Import("android/soong/java")
 	pctx.HostBinToolVariable("apexer", "apexer")
 	pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing")
-	pctx.HostBinToolVariable("apexer_with_trim_preprocessing", "apexer_with_trim_preprocessing")
 
 	// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
 	// projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
@@ -96,6 +95,7 @@
 	{"package.map", "create_aconfig_package_map_file", "package_map"},
 	{"flag.map", "create_aconfig_flag_map_file", "flag_map"},
 	{"flag.val", "create_aconfig_flag_val_file", "flag_val"},
+	{"flag.info", "create_aconfig_flag_info_file", "flag_info"},
 }
 
 var (
@@ -172,34 +172,6 @@
 	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
 		"opt_flags", "manifest", "is_DCLA")
 
-	TrimmedApexRule = pctx.StaticRule("TrimmedApexRule", blueprint.RuleParams{
-		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
-			`(. ${out}.copy_commands) && ` +
-			`APEXER_TOOL_PATH=${tool_path} ` +
-			`${apexer_with_trim_preprocessing} ` +
-			`--apexer ${apexer} ` +
-			`--canned_fs_config ${canned_fs_config} ` +
-			`--manifest ${manifest} ` +
-			`--libs_to_trim ${libs_to_trim} ` +
-			`${image_dir} ` +
-			`${out} ` +
-			`-- ` +
-			`--include_build_info ` +
-			`--force ` +
-			`--payload_type image ` +
-			`--key ${key} ` +
-			`--file_contexts ${file_contexts} ` +
-			`${opt_flags} `,
-		CommandDeps: []string{"${apexer_with_trim_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}",
-			"${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}",
-			"${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}",
-			"prebuilts/sdk/current/public/android.jar"},
-		Rspfile:        "${out}.copy_commands",
-		RspfileContent: "${copy_commands}",
-		Description:    "APEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
-		"opt_flags", "manifest", "libs_to_trim")
-
 	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
 		blueprint.RuleParams{
 			Command:     `${aapt2} convert --output-format proto $in -o $out`,
@@ -224,7 +196,7 @@
 		Command: `diff --unchanged-group-format='' \` +
 			`--changed-group-format='%<' \` +
 			`${image_content_file} ${allowed_files_file} || (` +
-			`echo -e "New unexpected files were added to ${apex_module_name}." ` +
+			`echo "New unexpected files were added to ${apex_module_name}." ` +
 			` "To fix the build run following command:" && ` +
 			`echo "system/apex/tools/update_allowed_list.sh ${allowed_files_file} ${image_content_file}" && ` +
 			`exit 1); touch ${out}`,
@@ -252,10 +224,10 @@
 
 	apexHostVerifierRule = pctx.StaticRule("apexHostVerifierRule", blueprint.RuleParams{
 		Command: `${host_apex_verifier} --deapexer=${deapexer} --debugfs=${debugfs_static} ` +
-			`--fsckerofs=${fsck_erofs} --apex=${in} && touch ${out}`,
+			`--fsckerofs=${fsck_erofs} --apex=${in} --partition_tag=${partition_tag} && touch ${out}`,
 		CommandDeps: []string{"${host_apex_verifier}", "${deapexer}", "${debugfs_static}", "${fsck_erofs}"},
 		Description: "run host_apex_verifier",
-	})
+	}, "partition_tag")
 
 	assembleVintfRule = pctx.StaticRule("assembleVintfRule", blueprint.RuleParams{
 		Command:     `rm -f $out && VINTF_IGNORE_TARGET_FCM_VERSION=true ${assemble_vintf} -i $in -o $out`,
@@ -359,14 +331,14 @@
 	}
 
 	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
-	defaultVersion := android.DefaultUpdatableModuleVersion
+	defaultVersion := ctx.Config().ReleaseDefaultUpdatableModuleVersion()
 	if a.properties.Variant_version != nil {
 		defaultVersionInt, err := strconv.Atoi(defaultVersion)
 		if err != nil {
-			ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to be an int, but got %s", defaultVersion)
+			ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to be an int, but got %s", defaultVersion)
 		}
 		if defaultVersionInt%10 != 0 {
-			ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to end in a zero, but got %s", defaultVersion)
+			ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to end in a zero, but got %s", defaultVersion)
 		}
 		variantVersion := []rune(*a.properties.Variant_version)
 		if len(variantVersion) != 1 || variantVersion[0] < '0' || variantVersion[0] > '9' {
@@ -416,7 +388,7 @@
 // file for this APEX which is either from /systme/sepolicy/apex/<apexname>-file_contexts or from
 // the file_contexts property of this APEX. This is to make sure that the manifest file is correctly
 // labeled as system_file or vendor_apex_metadata_file.
-func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.OutputPath {
+func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.Path {
 	var fileContexts android.Path
 	var fileContextsDir string
 	isFileContextsModule := false
@@ -425,8 +397,10 @@
 	} else {
 		if m, t := android.SrcIsModuleWithTag(*a.properties.File_contexts); m != "" {
 			isFileContextsModule = true
-			otherModule := android.GetModuleFromPathDep(ctx, m, t)
-			fileContextsDir = ctx.OtherModuleDir(otherModule)
+			otherModule := android.GetModuleProxyFromPathDep(ctx, m, t)
+			if otherModule != nil {
+				fileContextsDir = ctx.OtherModuleDir(*otherModule)
+			}
 		}
 		fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts)
 	}
@@ -469,13 +443,13 @@
 	}
 
 	rule.Build("file_contexts."+a.Name(), "Generate file_contexts")
-	return output.OutputPath
+	return output
 }
 
 // buildInstalledFilesFile creates a build rule for the installed-files.txt file where the list of
 // files included in this APEX is shown. The text file is dist'ed so that people can see what's
 // included in the APEX without actually downloading and extracting it.
-func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath {
+func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.Path {
 	output := android.PathForModuleOut(ctx, "installed-files.txt")
 	rule := android.NewRuleBuilder(pctx, ctx)
 	rule.Command().
@@ -485,12 +459,12 @@
 		Text(" | sort -nr > ").
 		Output(output)
 	rule.Build("installed-files."+a.Name(), "Installed files")
-	return output.OutputPath
+	return output
 }
 
 // buildBundleConfig creates a build rule for the bundle config file that will control the bundle
 // creation process.
-func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.OutputPath {
+func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.Path {
 	output := android.PathForModuleOut(ctx, "bundle_config.json")
 
 	type ApkConfig struct {
@@ -535,7 +509,7 @@
 
 	android.WriteFileRule(ctx, output, string(j))
 
-	return output.OutputPath
+	return output
 }
 
 func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path {
@@ -620,7 +594,8 @@
 				}
 			} else {
 				if installSymbolFiles {
-					installedPath = ctx.InstallFile(apexDir.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
+					// store installedPath. symlinks might be created if required.
+					installedPath = apexDir.Join(ctx, fi.installDir, fi.stem())
 				}
 			}
 
@@ -817,24 +792,6 @@
 				"opt_flags":        strings.Join(optFlags, " "),
 			},
 		})
-	} else if ctx.Config().ApexTrimEnabled() && len(a.libs_to_trim(ctx)) > 0 {
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        TrimmedApexRule,
-			Implicits:   implicitInputs,
-			Output:      unsignedOutputFile,
-			Description: "apex",
-			Args: map[string]string{
-				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
-				"image_dir":        imageDir.String(),
-				"copy_commands":    strings.Join(copyCommands, " && "),
-				"manifest":         a.manifestPbOut.String(),
-				"file_contexts":    fileContexts.String(),
-				"canned_fs_config": cannedFsConfig.String(),
-				"key":              a.privateKeyFile.String(),
-				"opt_flags":        strings.Join(optFlags, " "),
-				"libs_to_trim":     strings.Join(a.libs_to_trim(ctx), ","),
-			},
-		})
 	} else {
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        apexRule,
@@ -953,17 +910,17 @@
 		args["outCommaList"] = signedOutputFile.String()
 	}
 	var validations android.Paths
-	validations = append(validations, runApexLinkerconfigValidation(ctx, unsignedOutputFile.OutputPath, imageDir.OutputPath))
+	validations = append(validations, runApexLinkerconfigValidation(ctx, unsignedOutputFile, imageDir))
 	// TODO(b/279688635) deapexer supports [ext4]
-	if !a.testApex && suffix == imageApexSuffix && ext4 == a.payloadFsType {
-		validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile.OutputPath))
+	if !a.skipValidation(apexSepolicyTests) && suffix == imageApexSuffix && ext4 == a.payloadFsType {
+		validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile))
 	}
 	if !a.testApex && len(a.properties.Unwanted_transitive_deps) > 0 {
 		validations = append(validations,
-			runApexElfCheckerUnwanted(ctx, unsignedOutputFile.OutputPath, a.properties.Unwanted_transitive_deps))
+			runApexElfCheckerUnwanted(ctx, unsignedOutputFile, a.properties.Unwanted_transitive_deps))
 	}
-	if !a.testApex && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
-		validations = append(validations, runApexHostVerifier(ctx, unsignedOutputFile.OutputPath))
+	if !a.skipValidation(hostApexVerifier) && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
+		validations = append(validations, runApexHostVerifier(ctx, a, unsignedOutputFile))
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
@@ -1061,8 +1018,9 @@
 		}
 		return ""
 	}
-	if a.overridableProperties.Package_name != "" {
-		return a.overridableProperties.Package_name
+	packageNameFromProp := a.overridableProperties.Package_name.GetOrDefault(ctx, "")
+	if packageNameFromProp != "" {
+		return packageNameFromProp
 	}
 	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
 	if overridden {
@@ -1079,7 +1037,7 @@
 	}
 
 	depInfos := android.DepNameToDepInfoMap{}
-	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		if from.Name() == to.Name() {
 			// This can happen for cc.reuseObjTag. We are not interested in tracking this.
 			// As soon as the dependency graph crosses the APEX boundary, don't go further.
@@ -1146,13 +1104,26 @@
 func (a *apexBundle) buildLintReports(ctx android.ModuleContext) {
 	depSetsBuilder := java.NewLintDepSetBuilder()
 	for _, fi := range a.filesInfo {
-		depSetsBuilder.Transitive(fi.lintDepSets)
+		if fi.lintInfo != nil {
+			depSetsBuilder.Transitive(fi.lintInfo)
+		}
 	}
 
-	a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build())
+	depSets := depSetsBuilder.Build()
+	var validations android.Paths
+
+	if a.checkStrictUpdatabilityLinting(ctx) {
+		baselines := depSets.Baseline.ToList()
+		if len(baselines) > 0 {
+			outputFile := java.VerifyStrictUpdatabilityChecks(ctx, baselines)
+			validations = append(validations, outputFile)
+		}
+	}
+
+	a.lintReports = java.BuildModuleLintReportZips(ctx, depSets, validations)
 }
 
-func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath {
+func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.Path {
 	var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
 	var executablePaths []string // this also includes dirs
 	var appSetDirs []string
@@ -1216,10 +1187,10 @@
 	cmd.Text(")").FlagWithOutput("> ", cannedFsConfig)
 	builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName()))
 
-	return cannedFsConfig.OutputPath
+	return cannedFsConfig
 }
 
-func runApexLinkerconfigValidation(ctx android.ModuleContext, apexFile android.OutputPath, imageDir android.OutputPath) android.Path {
+func runApexLinkerconfigValidation(ctx android.ModuleContext, apexFile android.Path, imageDir android.Path) android.Path {
 	timestamp := android.PathForModuleOut(ctx, "apex_linkerconfig_validation.timestamp")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexLinkerconfigValidationRule,
@@ -1236,8 +1207,8 @@
 //
 // $ deapexer list -Z {apex_file} > {file_contexts}
 // $ apex_sepolicy_tests -f {file_contexts}
-func runApexSepolicyTests(ctx android.ModuleContext, apexFile android.OutputPath) android.Path {
-	timestamp := android.PathForModuleOut(ctx, "sepolicy_tests.timestamp")
+func runApexSepolicyTests(ctx android.ModuleContext, apexFile android.Path) android.Path {
+	timestamp := android.PathForModuleOut(ctx, "apex_sepolicy_tests.timestamp")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexSepolicyTestsRule,
 		Input:  apexFile,
@@ -1246,7 +1217,7 @@
 	return timestamp
 }
 
-func runApexElfCheckerUnwanted(ctx android.ModuleContext, apexFile android.OutputPath, unwanted []string) android.Path {
+func runApexElfCheckerUnwanted(ctx android.ModuleContext, apexFile android.Path, unwanted []string) android.Path {
 	timestamp := android.PathForModuleOut(ctx, "apex_elf_unwanted.timestamp")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexElfCheckerUnwantedRule,
@@ -1260,12 +1231,15 @@
 	return timestamp
 }
 
-func runApexHostVerifier(ctx android.ModuleContext, apexFile android.OutputPath) android.Path {
+func runApexHostVerifier(ctx android.ModuleContext, a *apexBundle, apexFile android.Path) android.Path {
 	timestamp := android.PathForModuleOut(ctx, "host_apex_verifier.timestamp")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexHostVerifierRule,
 		Input:  apexFile,
 		Output: timestamp,
+		Args: map[string]string{
+			"partition_tag": a.PartitionTag(ctx.DeviceConfig()),
+		},
 	})
 	return timestamp
 }
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index 9e1ac94..f367174 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -40,14 +40,12 @@
 var _ java.ClasspathElementContext = (*testClasspathElementContext)(nil)
 
 func TestCreateClasspathElements(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		prepareForTestWithArtApex,
 		prepareForTestWithMyapex,
-		// For otherapex.
-		android.FixtureMergeMockFs(android.MockFS{
-			"system/sepolicy/apex/otherapex-file_contexts": nil,
-		}),
+		prepareForTestWithOtherapex,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo", "othersdklibrary"),
 		java.FixtureConfigureApexBootJars("myapex:bar"),
@@ -240,6 +238,7 @@
 
 	// Verify that CreateClasspathElements works when given valid input.
 	t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) {
+		t.Parallel()
 		ctx := newCtx()
 		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment})
 		expectedElements := java.ClasspathElements{
@@ -252,6 +251,7 @@
 
 	// Verify that CreateClasspathElements detects when an apex has multiple fragments.
 	t.Run("multiple fragments for same apex", func(t *testing.T) {
+		t.Parallel()
 		ctx := newCtx()
 		elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment})
 		android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs)
@@ -261,6 +261,7 @@
 
 	// Verify that CreateClasspathElements detects when a library is in multiple fragments.
 	t.Run("library from multiple fragments", func(t *testing.T) {
+		t.Parallel()
 		ctx := newCtx()
 		elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment})
 		android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs)
@@ -271,6 +272,7 @@
 	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
 	// are separated by a library from another fragment.
 	t.Run("discontiguous separated by fragment", func(t *testing.T) {
+		t.Parallel()
 		ctx := newCtx()
 		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment})
 		expectedElements := java.ClasspathElements{
@@ -285,6 +287,7 @@
 	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
 	// are separated by a standalone library.
 	t.Run("discontiguous separated by library", func(t *testing.T) {
+		t.Parallel()
 		ctx := newCtx()
 		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment})
 		expectedElements := java.ClasspathElements{
@@ -300,6 +303,7 @@
 	// indicates it is from an apex the supplied fragments list does not contain a fragment for that
 	// apex.
 	t.Run("no fragment for apex", func(t *testing.T) {
+		t.Parallel()
 		ctx := newCtx()
 		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment})
 		expectedElements := java.ClasspathElements{
diff --git a/apex/container_test.go b/apex/container_test.go
index d28b1a6..395793f 100644
--- a/apex/container_test.go
+++ b/apex/container_test.go
@@ -15,10 +15,11 @@
 package apex
 
 import (
-	"android/soong/android"
-	"android/soong/java"
 	"fmt"
 	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
 )
 
 var checkContainerMatch = func(t *testing.T, name string, container string, expected bool, actual bool) {
@@ -27,6 +28,7 @@
 }
 
 func TestApexDepsContainers(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForApexTest,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
@@ -163,6 +165,7 @@
 }
 
 func TestNonUpdatableApexDepsContainers(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForApexTest,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
@@ -277,6 +280,7 @@
 }
 
 func TestUpdatableAndNonUpdatableApexesIdenticalMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForApexTest,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
@@ -326,6 +330,7 @@
 			],
 			min_sdk_version: "30",
 			sdk_version: "current",
+			compile_dex: true,
 		}
 	`)
 
diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go
index 4feade8..b51bb36 100644
--- a/apex/dexpreopt_bootjars_test.go
+++ b/apex/dexpreopt_bootjars_test.go
@@ -168,6 +168,7 @@
 }
 
 func TestDexpreoptBootJarsWithSourceArtApex(t *testing.T) {
+	t.Parallel()
 	ruleFile := "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art"
 
 	expectedInputs := []string{
@@ -206,6 +207,7 @@
 // The only difference is that the ART profile should be deapexed from the prebuilt APEX. Other
 // inputs and outputs should be the same as above.
 func TestDexpreoptBootJarsWithPrebuiltArtApex(t *testing.T) {
+	t.Parallel()
 	ruleFile := "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art"
 
 	expectedInputs := []string{
@@ -243,6 +245,7 @@
 
 // Changes to the boot.zip structure may break the ART APK scanner.
 func TestDexpreoptBootZip(t *testing.T) {
+	t.Parallel()
 	ruleFile := "boot.zip"
 
 	ctx := android.PathContextForTesting(android.TestArchConfig("", nil, "", nil))
@@ -271,6 +274,7 @@
 // Multiple ART apexes might exist in the tree.
 // The profile should correspond to the apex selected using release build flags
 func TestDexpreoptProfileWithMultiplePrebuiltArtApexes(t *testing.T) {
+	t.Parallel()
 	ruleFile := "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art"
 	bp := `
 		// Platform.
@@ -425,6 +429,7 @@
 
 // Check that dexpreopt works with Google mainline prebuilts even in workspaces where source is missing
 func TestDexpreoptWithMainlinePrebuiltNoSource(t *testing.T) {
+	t.Parallel()
 	bp := `
 		// Platform.
 
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 9c2d899..9f9b0b4 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -36,6 +36,7 @@
 )
 
 func TestPlatformBootclasspath_Fragments(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		prepareForTestWithMyapex,
@@ -178,6 +179,7 @@
 //
 // TODO: Remove once all prebuilts use the filtered_... properties.
 func TestPlatformBootclasspath_LegacyPrebuiltFragment(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		java.FixtureConfigureApexBootJars("myapex:foo"),
@@ -243,6 +245,7 @@
 }
 
 func TestPlatformBootclasspathDependencies(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		prepareForTestWithArtApex,
@@ -409,12 +412,16 @@
 		// The fragments.
 		`com.android.art:art-bootclasspath-fragment`,
 		`myapex:my-bootclasspath-fragment`,
+
+		// Impl lib of sdk_library for transitive srcjar generation
+		`platform:foo.impl`,
 	})
 }
 
 // TestPlatformBootclasspath_AlwaysUsePrebuiltSdks verifies that the build does not fail when
 // AlwaysUsePrebuiltSdk() returns true.
 func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		prepareForTestWithMyapex,
@@ -565,6 +572,9 @@
 		// The fragments.
 		"myapex:mybootclasspath-fragment",
 		"myapex:prebuilt_mybootclasspath-fragment",
+
+		// Impl lib of sdk_library for transitive srcjar generation
+		"platform:foo.impl",
 	})
 }
 
@@ -588,6 +598,7 @@
 // platform_bootclasspath's classpaths.proto config, if the apex does not generate its own config
 // by setting generate_classpaths_proto property to false.
 func TestPlatformBootclasspath_IncludesRemainingApexJars(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		prepareForTestWithMyapex,
@@ -647,6 +658,7 @@
 }
 
 func TestBootJarNotInApex(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		PrepareForTestWithApexBuildComponents,
@@ -693,6 +705,7 @@
 }
 
 func TestBootFragmentNotInApex(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		PrepareForTestWithApexBuildComponents,
@@ -736,6 +749,7 @@
 }
 
 func TestNonBootJarInFragment(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		PrepareForTestWithApexBuildComponents,
@@ -794,6 +808,7 @@
 
 // Skip bcp_fragment content validation of source apexes if prebuilts are active.
 func TestNonBootJarInPrebuilts(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		description               string
 		selectedApexContributions string
@@ -916,6 +931,7 @@
 
 // Source and prebuilt apex provide different set of boot jars
 func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex {
 			name: "myapex",
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 9cd5688..f93eada 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -306,10 +306,6 @@
 //     extra copying of files. Contrast that with source apex modules that has to build each variant
 //     from source.
 func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) {
-
-	// Collect direct dependencies into contents.
-	contents := make(map[string]android.ApexMembership)
-
 	// Collect the list of dependencies.
 	var dependencies []android.ApexModule
 	mctx.WalkDeps(func(child, parent android.Module) bool {
@@ -347,29 +343,19 @@
 		// behavior whether there is a corresponding source module present or not.
 		depName = android.RemoveOptionalPrebuiltPrefix(depName)
 
-		// Remember if this module was added as a direct dependency.
-		direct := parent == mctx.Module()
-		contents[depName] = contents[depName].Add(direct)
-
 		// Add the module to the list of dependencies that need to have an APEX variant.
 		dependencies = append(dependencies, child.(android.ApexModule))
 
 		return true
 	})
 
-	// Create contents for the prebuilt_apex and store it away for later use.
-	apexContents := android.NewApexContents(contents)
-	android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{
-		Contents: apexContents,
-	})
+	android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{})
 
 	// Create an ApexInfo for the prebuilt_apex.
 	apexVariationName := p.ApexVariationName()
 	apexInfo := android.ApexInfo{
 		ApexVariationName: apexVariationName,
 		InApexVariants:    []string{apexVariationName},
-		InApexModules:     []string{p.BaseModuleName()}, // BaseModuleName() to avoid the prebuilt_ prefix.
-		ApexContents:      []*android.ApexContents{apexContents},
 		ForPrebuiltApex:   true,
 	}
 
@@ -386,7 +372,7 @@
 
 	inputApex android.Path
 
-	provenanceMetaDataFile android.OutputPath
+	provenanceMetaDataFile android.Path
 }
 
 type ApexFileProperties struct {
@@ -697,7 +683,7 @@
 	ctx.SetOutputFiles(android.Paths{p.outputApex}, "")
 }
 
-func (p *Prebuilt) ProvenanceMetaDataFile() android.OutputPath {
+func (p *Prebuilt) ProvenanceMetaDataFile() android.Path {
 	return p.provenanceMetaDataFile
 }
 
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index acb3649..7dbac5f 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -29,6 +29,7 @@
 )
 
 func TestSystemserverclasspathFragmentContents(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
@@ -118,6 +119,7 @@
 }
 
 func TestSystemserverclasspathFragmentNoGeneratedProto(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
@@ -171,6 +173,7 @@
 }
 
 func TestSystemServerClasspathFragmentWithContentNotInMake(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
@@ -222,6 +225,7 @@
 }
 
 func TestPrebuiltSystemserverclasspathFragmentContents(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
@@ -297,6 +301,7 @@
 }
 
 func TestSystemserverclasspathFragmentStandaloneContents(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
@@ -380,6 +385,7 @@
 }
 
 func TestPrebuiltStandaloneSystemserverclasspathFragmentContents(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
diff --git a/apex/testing.go b/apex/testing.go
index 3b200f0..63c5b69 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -16,6 +16,8 @@
 
 import "android/soong/android"
 
+const testDefaultUpdatableModuleVersion = "340090000"
+
 var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers(
 	android.FixtureRegisterWithContext(registerApexBuildComponents),
 	android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
@@ -29,4 +31,5 @@
 		// Needed by prebuilt_apex.
 		"build/soong/scripts/unpack-prebuilt-apex.sh": nil,
 	}.AddToFixture(),
+	android.PrepareForTestWithBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION", testDefaultUpdatableModuleVersion),
 )
diff --git a/apex/vndk.go b/apex/vndk.go
index 3ececc5..d88808b 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -20,6 +20,7 @@
 	"android/soong/android"
 	"android/soong/cc"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -66,7 +67,14 @@
 		vndkApexName := "com.android.vndk." + vndkVersion
 
 		if mctx.OtherModuleExists(vndkApexName) {
-			mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApexName)
+			// Reverse dependencies must exactly specify the variant they want, starting from the
+			// current module's variant. But unlike cc modules, the vndk apex doesn't have
+			// arch/image/link variations, so we explicitly remove them here.
+			mctx.AddReverseVariationDependency([]blueprint.Variation{
+				{Mutator: "arch", Variation: "common"},
+				{Mutator: "image", Variation: ""},
+				{Mutator: "link", Variation: ""},
+			}, sharedLibTag, vndkApexName)
 		}
 	} else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex {
 		if a.IsNativeBridgeSupported() {
@@ -87,7 +95,11 @@
 				// level for the primary architecture.
 				a.Disable()
 			} else {
-				mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion, mctx)...)
+				mctx.AddVariationDependencies(
+					mctx.Config().AndroidFirstDeviceTarget.Variations(),
+					prebuiltTag,
+					cc.VndkLibrariesTxtModules(vndkVersion, mctx)...,
+				)
 			}
 		}
 	}
diff --git a/bazel/Android.bp b/bazel/Android.bp
deleted file mode 100644
index f8273a8..0000000
--- a/bazel/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
-    name: "soong-bazel",
-    pkgPath: "android/soong/bazel",
-    srcs: [
-        "configurability.go",
-        "properties.go",
-        "testing.go",
-    ],
-    testSrcs: [
-        "properties_test.go",
-    ],
-    pluginFor: [
-        "soong_build",
-    ],
-    deps: [
-        "blueprint",
-    ],
-}
diff --git a/bazel/configurability.go b/bazel/configurability.go
deleted file mode 100644
index 2c9a536..0000000
--- a/bazel/configurability.go
+++ /dev/null
@@ -1,393 +0,0 @@
-// Copyright 2021 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 bazel
-
-import (
-	"fmt"
-	"math"
-	"sort"
-	"strings"
-)
-
-const (
-	// ArchType names in arch.go
-	archArm     = "arm"
-	archArm64   = "arm64"
-	archRiscv64 = "riscv64"
-	archX86     = "x86"
-	archX86_64  = "x86_64"
-
-	// OsType names in arch.go
-	OsAndroid     = "android"
-	OsDarwin      = "darwin"
-	OsLinux       = "linux_glibc"
-	osLinuxMusl   = "linux_musl"
-	osLinuxBionic = "linux_bionic"
-	OsWindows     = "windows"
-
-	// Targets in arch.go
-	osArchAndroidArm        = "android_arm"
-	OsArchAndroidArm64      = "android_arm64"
-	osArchAndroidRiscv64    = "android_riscv64"
-	osArchAndroidX86        = "android_x86"
-	osArchAndroidX86_64     = "android_x86_64"
-	osArchDarwinArm64       = "darwin_arm64"
-	osArchDarwinX86_64      = "darwin_x86_64"
-	osArchLinuxX86          = "linux_glibc_x86"
-	osArchLinuxX86_64       = "linux_glibc_x86_64"
-	osArchLinuxMuslArm      = "linux_musl_arm"
-	osArchLinuxMuslArm64    = "linux_musl_arm64"
-	osArchLinuxMuslX86      = "linux_musl_x86"
-	osArchLinuxMuslX86_64   = "linux_musl_x86_64"
-	osArchLinuxBionicArm64  = "linux_bionic_arm64"
-	osArchLinuxBionicX86_64 = "linux_bionic_x86_64"
-	osArchWindowsX86        = "windows_x86"
-	osArchWindowsX86_64     = "windows_x86_64"
-
-	// This is the string representation of the default condition wherever a
-	// configurable attribute is used in a select statement, i.e.
-	// //conditions:default for Bazel.
-	//
-	// This is consistently named "conditions_default" to mirror the Soong
-	// config variable default key in an Android.bp file, although there's no
-	// integration with Soong config variables (yet).
-	ConditionsDefaultConfigKey = "conditions_default"
-
-	ConditionsDefaultSelectKey = "//conditions:default"
-
-	productVariableBazelPackage = "//build/bazel/product_config/config_settings"
-
-	AndroidAndInApex = "android-in_apex"
-	AndroidPlatform  = "system"
-	Unbundled_app    = "unbundled_app"
-
-	InApex  = "in_apex"
-	NonApex = "non_apex"
-
-	ErrorproneDisabled = "errorprone_disabled"
-	// TODO: b/294868620 - Remove when completing the bug
-	SanitizersEnabled = "sanitizers_enabled"
-)
-
-func PowerSetWithoutEmptySet[T any](items []T) [][]T {
-	resultSize := int(math.Pow(2, float64(len(items))))
-	powerSet := make([][]T, 0, resultSize-1)
-	for i := 1; i < resultSize; i++ {
-		combination := make([]T, 0)
-		for j := 0; j < len(items); j++ {
-			if (i>>j)%2 == 1 {
-				combination = append(combination, items[j])
-			}
-		}
-		powerSet = append(powerSet, combination)
-	}
-	return powerSet
-}
-
-func createPlatformArchMap() map[string]string {
-	// Copy of archFeatures from android/arch_list.go because the bazel
-	// package can't access the android package
-	archFeatures := map[string][]string{
-		"arm": {
-			"neon",
-		},
-		"arm64": {
-			"dotprod",
-		},
-		"riscv64": {},
-		"x86": {
-			"ssse3",
-			"sse4",
-			"sse4_1",
-			"sse4_2",
-			"aes_ni",
-			"avx",
-			"avx2",
-			"avx512",
-			"popcnt",
-			"movbe",
-		},
-		"x86_64": {
-			"ssse3",
-			"sse4",
-			"sse4_1",
-			"sse4_2",
-			"aes_ni",
-			"avx",
-			"avx2",
-			"avx512",
-			"popcnt",
-		},
-	}
-	result := make(map[string]string)
-	for arch, allFeatures := range archFeatures {
-		result[arch] = "//build/bazel_common_rules/platforms/arch:" + arch
-		// Sometimes we want to select on multiple features being active, so
-		// add the power set of all possible features to the map. More details
-		// in android.ModuleBase.GetArchVariantProperties
-		for _, features := range PowerSetWithoutEmptySet(allFeatures) {
-			sort.Strings(features)
-			archFeaturesName := arch + "-" + strings.Join(features, "-")
-			result[archFeaturesName] = "//build/bazel/platforms/arch/variants:" + archFeaturesName
-		}
-	}
-	result[ConditionsDefaultConfigKey] = ConditionsDefaultSelectKey
-	return result
-}
-
-var (
-	// These are the list of OSes and architectures with a Bazel config_setting
-	// and constraint value equivalent. These exist in arch.go, but the android
-	// package depends on the bazel package, so a cyclic dependency prevents
-	// using those variables here.
-
-	// A map of architectures to the Bazel label of the constraint_value
-	// for the @platforms//cpu:cpu constraint_setting
-	platformArchMap = createPlatformArchMap()
-
-	// A map of target operating systems to the Bazel label of the
-	// constraint_value for the @platforms//os:os constraint_setting
-	platformOsMap = map[string]string{
-		OsAndroid:                  "//build/bazel_common_rules/platforms/os:android",
-		OsDarwin:                   "//build/bazel_common_rules/platforms/os:darwin",
-		OsLinux:                    "//build/bazel_common_rules/platforms/os:linux_glibc",
-		osLinuxMusl:                "//build/bazel_common_rules/platforms/os:linux_musl",
-		osLinuxBionic:              "//build/bazel_common_rules/platforms/os:linux_bionic",
-		OsWindows:                  "//build/bazel_common_rules/platforms/os:windows",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
-	}
-
-	platformOsArchMap = map[string]string{
-		osArchAndroidArm:           "//build/bazel_common_rules/platforms/os_arch:android_arm",
-		OsArchAndroidArm64:         "//build/bazel_common_rules/platforms/os_arch:android_arm64",
-		osArchAndroidRiscv64:       "//build/bazel_common_rules/platforms/os_arch:android_riscv64",
-		osArchAndroidX86:           "//build/bazel_common_rules/platforms/os_arch:android_x86",
-		osArchAndroidX86_64:        "//build/bazel_common_rules/platforms/os_arch:android_x86_64",
-		osArchDarwinArm64:          "//build/bazel_common_rules/platforms/os_arch:darwin_arm64",
-		osArchDarwinX86_64:         "//build/bazel_common_rules/platforms/os_arch:darwin_x86_64",
-		osArchLinuxX86:             "//build/bazel_common_rules/platforms/os_arch:linux_glibc_x86",
-		osArchLinuxX86_64:          "//build/bazel_common_rules/platforms/os_arch:linux_glibc_x86_64",
-		osArchLinuxMuslArm:         "//build/bazel_common_rules/platforms/os_arch:linux_musl_arm",
-		osArchLinuxMuslArm64:       "//build/bazel_common_rules/platforms/os_arch:linux_musl_arm64",
-		osArchLinuxMuslX86:         "//build/bazel_common_rules/platforms/os_arch:linux_musl_x86",
-		osArchLinuxMuslX86_64:      "//build/bazel_common_rules/platforms/os_arch:linux_musl_x86_64",
-		osArchLinuxBionicArm64:     "//build/bazel_common_rules/platforms/os_arch:linux_bionic_arm64",
-		osArchLinuxBionicX86_64:    "//build/bazel_common_rules/platforms/os_arch:linux_bionic_x86_64",
-		osArchWindowsX86:           "//build/bazel_common_rules/platforms/os_arch:windows_x86",
-		osArchWindowsX86_64:        "//build/bazel_common_rules/platforms/os_arch:windows_x86_64",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
-	}
-
-	// Map where keys are OsType names, and values are slices containing the archs
-	// that that OS supports.
-	// These definitions copied from arch.go.
-	// TODO(cparsons): Source from arch.go; this task is nontrivial, as it currently results
-	// in a cyclic dependency.
-	osToArchMap = map[string][]string{
-		OsAndroid:     {archArm, archArm64, archRiscv64, archX86, archX86_64},
-		OsLinux:       {archX86, archX86_64},
-		osLinuxMusl:   {archX86, archX86_64},
-		OsDarwin:      {archArm64, archX86_64},
-		osLinuxBionic: {archArm64, archX86_64},
-		// TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well.
-		OsWindows: {archX86, archX86_64},
-	}
-
-	osAndInApexMap = map[string]string{
-		AndroidAndInApex:           "//build/bazel/rules/apex:android-in_apex",
-		AndroidPlatform:            "//build/bazel/rules/apex:system",
-		Unbundled_app:              "//build/bazel/rules/apex:unbundled_app",
-		OsDarwin:                   "//build/bazel_common_rules/platforms/os:darwin",
-		OsLinux:                    "//build/bazel_common_rules/platforms/os:linux_glibc",
-		osLinuxMusl:                "//build/bazel_common_rules/platforms/os:linux_musl",
-		osLinuxBionic:              "//build/bazel_common_rules/platforms/os:linux_bionic",
-		OsWindows:                  "//build/bazel_common_rules/platforms/os:windows",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
-	}
-
-	inApexMap = map[string]string{
-		InApex:                     "//build/bazel/rules/apex:in_apex",
-		NonApex:                    "//build/bazel/rules/apex:non_apex",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
-	}
-
-	errorProneMap = map[string]string{
-		ErrorproneDisabled:         "//build/bazel/rules/java/errorprone:errorprone_globally_disabled",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
-	}
-
-	// TODO: b/294868620 - Remove when completing the bug
-	sanitizersEnabledMap = map[string]string{
-		SanitizersEnabled:          "//build/bazel/rules/cc:sanitizers_enabled",
-		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
-	}
-)
-
-// basic configuration types
-type configurationType int
-
-const (
-	noConfig configurationType = iota
-	arch
-	os
-	osArch
-	productVariables
-	osAndInApex
-	inApex
-	errorProneDisabled
-	// TODO: b/294868620 - Remove when completing the bug
-	sanitizersEnabled
-)
-
-func osArchString(os string, arch string) string {
-	return fmt.Sprintf("%s_%s", os, arch)
-}
-
-func (ct configurationType) String() string {
-	return map[configurationType]string{
-		noConfig:           "no_config",
-		arch:               "arch",
-		os:                 "os",
-		osArch:             "arch_os",
-		productVariables:   "product_variables",
-		osAndInApex:        "os_in_apex",
-		inApex:             "in_apex",
-		errorProneDisabled: "errorprone_disabled",
-		// TODO: b/294868620 - Remove when completing the bug
-		sanitizersEnabled: "sanitizers_enabled",
-	}[ct]
-}
-
-func (ct configurationType) validateConfig(config string) {
-	switch ct {
-	case noConfig:
-		if config != "" {
-			panic(fmt.Errorf("Cannot specify config with %s, but got %s", ct, config))
-		}
-	case arch:
-		if _, ok := platformArchMap[config]; !ok {
-			panic(fmt.Errorf("Unknown arch: %s", config))
-		}
-	case os:
-		if _, ok := platformOsMap[config]; !ok {
-			panic(fmt.Errorf("Unknown os: %s", config))
-		}
-	case osArch:
-		if _, ok := platformOsArchMap[config]; !ok {
-			panic(fmt.Errorf("Unknown os+arch: %s", config))
-		}
-	case productVariables:
-		// do nothing
-	case osAndInApex:
-		// do nothing
-		// this axis can contain additional per-apex keys
-	case inApex:
-		if _, ok := inApexMap[config]; !ok {
-			panic(fmt.Errorf("Unknown in_apex config: %s", config))
-		}
-	case errorProneDisabled:
-		if _, ok := errorProneMap[config]; !ok {
-			panic(fmt.Errorf("Unknown errorprone config: %s", config))
-		}
-	// TODO: b/294868620 - Remove when completing the bug
-	case sanitizersEnabled:
-		if _, ok := sanitizersEnabledMap[config]; !ok {
-			panic(fmt.Errorf("Unknown sanitizers_enabled config: %s", config))
-		}
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
-	}
-}
-
-// SelectKey returns the Bazel select key for a given configurationType and config string.
-func (ca ConfigurationAxis) SelectKey(config string) string {
-	ca.validateConfig(config)
-	switch ca.configurationType {
-	case noConfig:
-		panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
-	case arch:
-		return platformArchMap[config]
-	case os:
-		return platformOsMap[config]
-	case osArch:
-		return platformOsArchMap[config]
-	case productVariables:
-		if config == ConditionsDefaultConfigKey {
-			return ConditionsDefaultSelectKey
-		}
-		return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
-	case osAndInApex:
-		if ret, exists := osAndInApexMap[config]; exists {
-			return ret
-		}
-		return config
-	case inApex:
-		return inApexMap[config]
-	case errorProneDisabled:
-		return errorProneMap[config]
-	// TODO: b/294868620 - Remove when completing the bug
-	case sanitizersEnabled:
-		return sanitizersEnabledMap[config]
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType))
-	}
-}
-
-var (
-	// Indicating there is no configuration axis
-	NoConfigAxis = ConfigurationAxis{configurationType: noConfig}
-	// An axis for architecture-specific configurations
-	ArchConfigurationAxis = ConfigurationAxis{configurationType: arch}
-	// An axis for os-specific configurations
-	OsConfigurationAxis = ConfigurationAxis{configurationType: os}
-	// An axis for arch+os-specific configurations
-	OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch}
-	// An axis for os+in_apex-specific configurations
-	OsAndInApexAxis = ConfigurationAxis{configurationType: osAndInApex}
-	// An axis for in_apex-specific configurations
-	InApexAxis = ConfigurationAxis{configurationType: inApex}
-
-	ErrorProneAxis = ConfigurationAxis{configurationType: errorProneDisabled}
-
-	// TODO: b/294868620 - Remove when completing the bug
-	SanitizersEnabledAxis = ConfigurationAxis{configurationType: sanitizersEnabled}
-)
-
-// ProductVariableConfigurationAxis returns an axis for the given product variable
-func ProductVariableConfigurationAxis(archVariant bool, variable string) ConfigurationAxis {
-	return ConfigurationAxis{
-		configurationType: productVariables,
-		subType:           variable,
-		archVariant:       archVariant,
-	}
-}
-
-// ConfigurationAxis is an independent axis for configuration, there should be no overlap between
-// elements within an axis.
-type ConfigurationAxis struct {
-	configurationType
-	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
-	// distinguish between them without needing to list all 17 product variables.
-	subType string
-
-	archVariant bool
-}
-
-func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
-	if ca.configurationType == other.configurationType {
-		return ca.subType < other.subType
-	}
-	return ca.configurationType < other.configurationType
-}
diff --git a/bazel/properties.go b/bazel/properties.go
deleted file mode 100644
index 9c63bc0..0000000
--- a/bazel/properties.go
+++ /dev/null
@@ -1,1467 +0,0 @@
-// Copyright 2020 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 bazel
-
-import (
-	"fmt"
-	"path/filepath"
-	"reflect"
-	"regexp"
-	"sort"
-	"strings"
-
-	"github.com/google/blueprint"
-)
-
-// BazelTargetModuleProperties contain properties and metadata used for
-// Blueprint to BUILD file conversion.
-type BazelTargetModuleProperties struct {
-	// The Bazel rule class for this target.
-	Rule_class string `blueprint:"mutated"`
-
-	// The target label for the bzl file containing the definition of the rule class.
-	Bzl_load_location string `blueprint:"mutated"`
-}
-
-var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
-
-// Label is used to represent a Bazel compatible Label. Also stores the original
-// bp text to support string replacement.
-type Label struct {
-	// The string representation of a Bazel target label. This can be a relative
-	// or fully qualified label. These labels are used for generating BUILD
-	// files with bp2build.
-	Label string
-
-	// The original Soong/Blueprint module name that the label was derived from.
-	// This is used for replacing references to the original name with the new
-	// label, for example in genrule cmds.
-	//
-	// While there is a reversible 1:1 mapping from the module name to Bazel
-	// label with bp2build that could make computing the original module name
-	// from the label automatic, it is not the case for handcrafted targets,
-	// where modules can have a custom label mapping through the { bazel_module:
-	// { label: <label> } } property.
-	//
-	// With handcrafted labels, those modules don't go through bp2build
-	// conversion, but relies on handcrafted targets in the source tree.
-	OriginalModuleName string
-}
-
-// LabelList is used to represent a list of Bazel labels.
-type LabelList struct {
-	Includes []Label
-	Excludes []Label
-}
-
-// MakeLabelList creates a LabelList from a list Label
-func MakeLabelList(labels []Label) LabelList {
-	return LabelList{
-		Includes: labels,
-		Excludes: nil,
-	}
-}
-
-func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis {
-	keys := make([]ConfigurationAxis, 0, len(m))
-	for k := range m {
-		keys = append(keys, k)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
-	return keys
-}
-
-// MakeLabelListFromTargetNames creates a LabelList from unqualified target names
-// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets
-func MakeLabelListFromTargetNames(targetNames []string) LabelList {
-	labels := []Label{}
-	for _, name := range targetNames {
-		label := Label{Label: ":" + name}
-		labels = append(labels, label)
-	}
-	return MakeLabelList(labels)
-}
-
-func (ll *LabelList) Equals(other LabelList) bool {
-	if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
-		return false
-	}
-	for i, _ := range ll.Includes {
-		if ll.Includes[i] != other.Includes[i] {
-			return false
-		}
-	}
-	for i, _ := range ll.Excludes {
-		if ll.Excludes[i] != other.Excludes[i] {
-			return false
-		}
-	}
-	return true
-}
-
-func (ll *LabelList) IsNil() bool {
-	return ll.Includes == nil && ll.Excludes == nil
-}
-
-func (ll *LabelList) IsEmpty() bool {
-	return len(ll.Includes) == 0 && len(ll.Excludes) == 0
-}
-
-func (ll *LabelList) deepCopy() LabelList {
-	return LabelList{
-		Includes: ll.Includes[:],
-		Excludes: ll.Excludes[:],
-	}
-}
-
-// uniqueParentDirectories returns a list of the unique parent directories for
-// all files in ll.Includes.
-func (ll *LabelList) uniqueParentDirectories() []string {
-	dirMap := map[string]bool{}
-	for _, label := range ll.Includes {
-		dirMap[filepath.Dir(label.Label)] = true
-	}
-	dirs := []string{}
-	for dir := range dirMap {
-		dirs = append(dirs, dir)
-	}
-	return dirs
-}
-
-// Add inserts the label Label at the end of the LabelList.Includes.
-func (ll *LabelList) Add(label *Label) {
-	if label == nil {
-		return
-	}
-	ll.Includes = append(ll.Includes, *label)
-}
-
-// AddExclude inserts the label Label at the end of the LabelList.Excludes.
-func (ll *LabelList) AddExclude(label *Label) {
-	if label == nil {
-		return
-	}
-	ll.Excludes = append(ll.Excludes, *label)
-}
-
-// Append appends the fields of other labelList to the corresponding fields of ll.
-func (ll *LabelList) Append(other LabelList) {
-	if len(ll.Includes) > 0 || len(other.Includes) > 0 {
-		ll.Includes = append(ll.Includes, other.Includes...)
-	}
-	if len(ll.Excludes) > 0 || len(other.Excludes) > 0 {
-		ll.Excludes = append(ll.Excludes, other.Excludes...)
-	}
-}
-
-// Partition splits a LabelList into two LabelLists depending on the return value
-// of the predicate.
-// This function preserves the Includes and Excludes, but it does not provide
-// that information to the partition function.
-func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) {
-	predicated := LabelList{}
-	unpredicated := LabelList{}
-	for _, include := range ll.Includes {
-		if predicate(include) {
-			predicated.Add(&include)
-		} else {
-			unpredicated.Add(&include)
-		}
-	}
-	for _, exclude := range ll.Excludes {
-		if predicate(exclude) {
-			predicated.AddExclude(&exclude)
-		} else {
-			unpredicated.AddExclude(&exclude)
-		}
-	}
-	return predicated, unpredicated
-}
-
-// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns
-// the slice in a sorted order.
-func UniqueSortedBazelLabels(originalLabels []Label) []Label {
-	uniqueLabels := FirstUniqueBazelLabels(originalLabels)
-	sort.SliceStable(uniqueLabels, func(i, j int) bool {
-		return uniqueLabels[i].Label < uniqueLabels[j].Label
-	})
-	return uniqueLabels
-}
-
-func FirstUniqueBazelLabels(originalLabels []Label) []Label {
-	var labels []Label
-	found := make(map[string]bool, len(originalLabels))
-	for _, l := range originalLabels {
-		if _, ok := found[l.Label]; ok {
-			continue
-		}
-		labels = append(labels, l)
-		found[l.Label] = true
-	}
-	return labels
-}
-
-func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
-	var uniqueLabelList LabelList
-	uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
-	uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
-	return uniqueLabelList
-}
-
-func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
-	var uniqueLabelList LabelList
-	uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
-	uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
-	return uniqueLabelList
-}
-
-// Subtract needle from haystack
-func SubtractStrings(haystack []string, needle []string) []string {
-	// This is really a set
-	needleMap := make(map[string]bool)
-	for _, s := range needle {
-		needleMap[s] = true
-	}
-
-	var strings []string
-	for _, s := range haystack {
-		if exclude := needleMap[s]; !exclude {
-			strings = append(strings, s)
-		}
-	}
-
-	return strings
-}
-
-// Subtract needle from haystack
-func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
-	// This is really a set
-	needleMap := make(map[Label]bool)
-	for _, s := range needle {
-		needleMap[s] = true
-	}
-
-	var labels []Label
-	for _, label := range haystack {
-		if exclude := needleMap[label]; !exclude {
-			labels = append(labels, label)
-		}
-	}
-
-	return labels
-}
-
-// Appends two LabelLists, returning the combined list.
-func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
-	var result LabelList
-	result.Includes = append(a.Includes, b.Includes...)
-	result.Excludes = append(a.Excludes, b.Excludes...)
-	return result
-}
-
-// Subtract needle from haystack
-func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
-	var result LabelList
-	result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes)
-	// NOTE: Excludes are intentionally not subtracted
-	result.Excludes = haystack.Excludes
-	return result
-}
-
-// FirstUniqueBazelLabelListAttribute takes a LabelListAttribute and makes the LabelList for
-// each axis/configuration by keeping the first instance of a Label and omitting all subsequent
-// repetitions.
-func FirstUniqueBazelLabelListAttribute(attr LabelListAttribute) LabelListAttribute {
-	var result LabelListAttribute
-	result.Value = FirstUniqueBazelLabelList(attr.Value)
-	if attr.HasConfigurableValues() {
-		result.ConfigurableValues = make(configurableLabelLists)
-	}
-	for axis, configToLabels := range attr.ConfigurableValues {
-		for c, l := range configToLabels {
-			result.SetSelectValue(axis, c, FirstUniqueBazelLabelList(l))
-		}
-	}
-
-	return result
-}
-
-// SubtractBazelLabelListAttribute subtract needle from haystack for LabelList in each
-// axis/configuration.
-func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
-	var result LabelListAttribute
-	result.Value = SubtractBazelLabelList(haystack.Value, needle.Value)
-	if haystack.HasConfigurableValues() {
-		result.ConfigurableValues = make(configurableLabelLists)
-	}
-	for axis, configToLabels := range haystack.ConfigurableValues {
-		for haystackConfig, haystackLabels := range configToLabels {
-			result.SetSelectValue(axis, haystackConfig, SubtractBazelLabelList(haystackLabels, needle.SelectValue(axis, haystackConfig)))
-		}
-	}
-
-	return result
-}
-
-type Attribute interface {
-	HasConfigurableValues() bool
-}
-
-type labelSelectValues map[string]*Label
-
-type configurableLabels map[ConfigurationAxis]labelSelectValues
-
-func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
-	if cl[axis] == nil {
-		cl[axis] = make(labelSelectValues)
-	}
-	cl[axis][config] = value
-}
-
-// Represents an attribute whose value is a single label
-type LabelAttribute struct {
-	Value *Label
-
-	ConfigurableValues configurableLabels
-}
-
-func (la *LabelAttribute) axisTypes() map[configurationType]bool {
-	types := map[configurationType]bool{}
-	for k := range la.ConfigurableValues {
-		if len(la.ConfigurableValues[k]) > 0 {
-			types[k.configurationType] = true
-		}
-	}
-	return types
-}
-
-// Collapse reduces the configurable axes of the label attribute to a single axis.
-// This is necessary for final writing to bp2build, as a configurable label
-// attribute can only be comprised by a single select.
-func (la *LabelAttribute) Collapse() error {
-	axisTypes := la.axisTypes()
-	_, containsOs := axisTypes[os]
-	_, containsArch := axisTypes[arch]
-	_, containsOsArch := axisTypes[osArch]
-	_, containsProductVariables := axisTypes[productVariables]
-	if containsProductVariables {
-		if containsOs || containsArch || containsOsArch {
-			if containsArch {
-				allProductVariablesAreArchVariant := true
-				for k := range la.ConfigurableValues {
-					if k.configurationType == productVariables && !k.archVariant {
-						allProductVariablesAreArchVariant = false
-					}
-				}
-				if !allProductVariablesAreArchVariant {
-					return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
-				}
-			} else {
-				return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
-			}
-		}
-	}
-	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
-		// If a bool attribute has both os and arch configuration axes, the only
-		// way to successfully union their values is to increase the granularity
-		// of the configuration criteria to os_arch.
-		for osType, supportedArchs := range osToArchMap {
-			for _, supportedArch := range supportedArchs {
-				osArch := osArchString(osType, supportedArch)
-				if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
-					// Do nothing, as the arch_os is explicitly defined already.
-				} else {
-					archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
-					osVal := la.SelectValue(OsConfigurationAxis, osType)
-					if osVal != nil && archVal != nil {
-						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
-						// runs after os mutator.
-						la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
-					} else if osVal != nil && archVal == nil {
-						la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
-					} else if osVal == nil && archVal != nil {
-						la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
-					}
-				}
-			}
-		}
-		// All os_arch values are now set. Clear os and arch axes.
-		delete(la.ConfigurableValues, ArchConfigurationAxis)
-		delete(la.ConfigurableValues, OsConfigurationAxis)
-	}
-	return nil
-}
-
-// HasConfigurableValues returns whether there are configurable values set for this label.
-func (la LabelAttribute) HasConfigurableValues() bool {
-	for _, selectValues := range la.ConfigurableValues {
-		if len(selectValues) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// SetValue sets the base, non-configured value for the Label
-func (la *LabelAttribute) SetValue(value Label) {
-	la.SetSelectValue(NoConfigAxis, "", value)
-}
-
-// SetSelectValue set a value for a bazel select for the given axis, config and value.
-func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		la.Value = &value
-	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
-		if la.ConfigurableValues == nil {
-			la.ConfigurableValues = make(configurableLabels)
-		}
-		la.ConfigurableValues.setValueForAxis(axis, config, &value)
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SelectValue gets a value for a bazel select for the given axis and config.
-func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		return la.Value
-	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
-		return la.ConfigurableValues[axis][config]
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
-func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	return SortedConfigurationAxes(la.ConfigurableValues)
-}
-
-// MakeLabelAttribute turns a string into a LabelAttribute
-func MakeLabelAttribute(label string) *LabelAttribute {
-	return &LabelAttribute{
-		Value: &Label{
-			Label: label,
-		},
-	}
-}
-
-type configToBools map[string]bool
-
-func (ctb configToBools) setValue(config string, value *bool) {
-	if value == nil {
-		if _, ok := ctb[config]; ok {
-			delete(ctb, config)
-		}
-		return
-	}
-	ctb[config] = *value
-}
-
-type configurableBools map[ConfigurationAxis]configToBools
-
-func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
-	if cb[axis] == nil {
-		cb[axis] = make(configToBools)
-	}
-	cb[axis].setValue(config, value)
-}
-
-// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
-type BoolAttribute struct {
-	Value *bool
-
-	ConfigurableValues configurableBools
-}
-
-// HasConfigurableValues returns whether there are configurable values for this attribute.
-func (ba BoolAttribute) HasConfigurableValues() bool {
-	for _, cfgToBools := range ba.ConfigurableValues {
-		if len(cfgToBools) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// SetValue sets value for the no config axis
-func (ba *BoolAttribute) SetValue(value *bool) {
-	ba.SetSelectValue(NoConfigAxis, "", value)
-}
-
-// SetSelectValue sets value for the given axis/config.
-func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		ba.Value = value
-	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
-		if ba.ConfigurableValues == nil {
-			ba.ConfigurableValues = make(configurableBools)
-		}
-		ba.ConfigurableValues.setValueForAxis(axis, config, value)
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// ToLabelListAttribute creates and returns a LabelListAttribute from this
-// bool attribute, where each bool in this attribute corresponds to a
-// label list value in the resultant attribute.
-func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
-	getLabelList := func(boolPtr *bool) LabelList {
-		if boolPtr == nil {
-			return LabelList{nil, nil}
-		} else if *boolPtr {
-			return trueVal
-		} else {
-			return falseVal
-		}
-	}
-
-	mainVal := getLabelList(ba.Value)
-	if !ba.HasConfigurableValues() {
-		return MakeLabelListAttribute(mainVal), nil
-	}
-
-	result := LabelListAttribute{}
-	if err := ba.Collapse(); err != nil {
-		return result, err
-	}
-
-	for axis, configToBools := range ba.ConfigurableValues {
-		if len(configToBools) < 1 {
-			continue
-		}
-		for config, boolPtr := range configToBools {
-			val := getLabelList(&boolPtr)
-			if !val.Equals(mainVal) {
-				result.SetSelectValue(axis, config, val)
-			}
-		}
-		result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
-	}
-
-	return result, nil
-}
-
-// ToStringListAttribute creates a StringListAttribute from this BoolAttribute,
-// where each bool corresponds to a string list value generated by the provided
-// function.
-// TODO(b/271425661): Generalize this
-func (ba *BoolAttribute) ToStringListAttribute(valueFunc func(boolPtr *bool, axis ConfigurationAxis, config string) []string) (StringListAttribute, error) {
-	mainVal := valueFunc(ba.Value, NoConfigAxis, "")
-	if !ba.HasConfigurableValues() {
-		return MakeStringListAttribute(mainVal), nil
-	}
-
-	result := StringListAttribute{}
-	if err := ba.Collapse(); err != nil {
-		return result, err
-	}
-
-	for axis, configToBools := range ba.ConfigurableValues {
-		if len(configToBools) < 1 {
-			continue
-		}
-		for config, boolPtr := range configToBools {
-			val := valueFunc(&boolPtr, axis, config)
-			if !reflect.DeepEqual(val, mainVal) {
-				result.SetSelectValue(axis, config, val)
-			}
-		}
-		result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
-	}
-
-	return result, nil
-}
-
-// Collapse reduces the configurable axes of the boolean attribute to a single axis.
-// This is necessary for final writing to bp2build, as a configurable boolean
-// attribute can only be comprised by a single select.
-func (ba *BoolAttribute) Collapse() error {
-	axisTypes := ba.axisTypes()
-	_, containsOs := axisTypes[os]
-	_, containsArch := axisTypes[arch]
-	_, containsOsArch := axisTypes[osArch]
-	_, containsProductVariables := axisTypes[productVariables]
-	if containsProductVariables {
-		if containsOs || containsArch || containsOsArch {
-			return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
-		}
-	}
-	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
-		// If a bool attribute has both os and arch configuration axes, the only
-		// way to successfully union their values is to increase the granularity
-		// of the configuration criteria to os_arch.
-		for osType, supportedArchs := range osToArchMap {
-			for _, supportedArch := range supportedArchs {
-				osArch := osArchString(osType, supportedArch)
-				if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
-					// Do nothing, as the arch_os is explicitly defined already.
-				} else {
-					archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
-					osVal := ba.SelectValue(OsConfigurationAxis, osType)
-					if osVal != nil && archVal != nil {
-						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
-						// runs after os mutator.
-						ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
-					} else if osVal != nil && archVal == nil {
-						ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
-					} else if osVal == nil && archVal != nil {
-						ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
-					}
-				}
-			}
-		}
-		// All os_arch values are now set. Clear os and arch axes.
-		delete(ba.ConfigurableValues, ArchConfigurationAxis)
-		delete(ba.ConfigurableValues, OsConfigurationAxis)
-		// Verify post-condition; this should never fail, provided no additional
-		// axes are introduced.
-		if len(ba.ConfigurableValues) > 1 {
-			panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
-		}
-	}
-	return nil
-}
-
-func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
-	types := map[configurationType]bool{}
-	for k := range ba.ConfigurableValues {
-		if len(ba.ConfigurableValues[k]) > 0 {
-			types[k.configurationType] = true
-		}
-	}
-	return types
-}
-
-// SelectValue gets the value for the given axis/config.
-func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		return ba.Value
-	case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled:
-		if v, ok := ba.ConfigurableValues[axis][config]; ok {
-			return &v
-		} else {
-			return nil
-		}
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
-func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	return SortedConfigurationAxes(ba.ConfigurableValues)
-}
-
-// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
-type labelListSelectValues map[string]LabelList
-
-func (ll labelListSelectValues) addSelects(label labelSelectValues) {
-	for k, v := range label {
-		if label == nil {
-			continue
-		}
-		l := ll[k]
-		(&l).Add(v)
-		ll[k] = l
-	}
-}
-
-func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
-	for k, v := range other {
-		l := ll[k]
-		if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
-			l.Includes = []Label{}
-		}
-		(&l).Append(v)
-		ll[k] = l
-	}
-}
-
-// HasConfigurableValues returns whether there are configurable values within this set of selects.
-func (ll labelListSelectValues) HasConfigurableValues() bool {
-	for _, v := range ll {
-		if v.Includes != nil {
-			return true
-		}
-	}
-	return false
-}
-
-// LabelListAttribute is used to represent a list of Bazel labels as an
-// attribute.
-type LabelListAttribute struct {
-	// The non-configured attribute label list Value. Required.
-	Value LabelList
-
-	// The configured attribute label list Values. Optional
-	// a map of independent configurability axes
-	ConfigurableValues configurableLabelLists
-
-	// If true, differentiate between "nil" and "empty" list. nil means that
-	// this attribute should not be specified at all, and "empty" means that
-	// the attribute should be explicitly specified as an empty list.
-	// This mode facilitates use of attribute defaults: an empty list should
-	// override the default.
-	ForceSpecifyEmptyList bool
-
-	// If true, signal the intent to the code generator to emit all select keys,
-	// even if the Includes list for that key is empty. This mode facilitates
-	// specific select statements where an empty list for a non-default select
-	// key has a meaning.
-	EmitEmptyList bool
-
-	// If a property has struct tag "variant_prepend", this value should
-	// be set to True, so that when bp2build generates BUILD.bazel, variant
-	// properties(select ...) come before general properties.
-	Prepend bool
-}
-
-type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
-
-func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
-	if list.IsNil() {
-		if _, ok := cll[axis][config]; ok {
-			delete(cll[axis], config)
-		}
-		return
-	}
-	if cll[axis] == nil {
-		cll[axis] = make(labelListSelectValues)
-	}
-
-	cll[axis][config] = list
-}
-
-func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
-	for axis, otherSelects := range other {
-		selects := cll[axis]
-		if selects == nil {
-			selects = make(labelListSelectValues, len(otherSelects))
-		}
-		selects.appendSelects(otherSelects, forceSpecifyEmptyList)
-		cll[axis] = selects
-	}
-}
-
-func (lla *LabelListAttribute) Clone() *LabelListAttribute {
-	result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
-	return result.Append(*lla)
-}
-
-// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
-func MakeLabelListAttribute(value LabelList) LabelListAttribute {
-	return LabelListAttribute{
-		Value:              value,
-		ConfigurableValues: make(configurableLabelLists),
-	}
-}
-
-// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label.
-func MakeSingleLabelListAttribute(value Label) LabelListAttribute {
-	return MakeLabelListAttribute(MakeLabelList([]Label{value}))
-}
-
-func (lla *LabelListAttribute) SetValue(list LabelList) {
-	lla.SetSelectValue(NoConfigAxis, "", list)
-}
-
-// SetSelectValue set a value for a bazel select for the given axis, config and value.
-func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		lla.Value = list
-	case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled:
-		if lla.ConfigurableValues == nil {
-			lla.ConfigurableValues = make(configurableLabelLists)
-		}
-		lla.ConfigurableValues.setValueForAxis(axis, config, list)
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SelectValue gets a value for a bazel select for the given axis and config.
-func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		return lla.Value
-	case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled:
-		return lla.ConfigurableValues[axis][config]
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
-func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	return SortedConfigurationAxes(lla.ConfigurableValues)
-}
-
-// Append all values, including os and arch specific ones, from another
-// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
-func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
-	forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
-	if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
-		lla.Value.Includes = []Label{}
-	}
-	lla.Value.Append(other.Value)
-	if lla.ConfigurableValues == nil {
-		lla.ConfigurableValues = make(configurableLabelLists)
-	}
-	lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
-	return lla
-}
-
-// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
-// LabelList within the LabelListAttribute
-func (lla *LabelListAttribute) Add(label *LabelAttribute) {
-	if label == nil {
-		return
-	}
-
-	lla.Value.Add(label.Value)
-	if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
-		lla.ConfigurableValues = make(configurableLabelLists)
-	}
-	for axis, _ := range label.ConfigurableValues {
-		if _, exists := lla.ConfigurableValues[axis]; !exists {
-			lla.ConfigurableValues[axis] = make(labelListSelectValues)
-		}
-		lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
-	}
-}
-
-// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
-func (lla LabelListAttribute) HasConfigurableValues() bool {
-	for _, selectValues := range lla.ConfigurableValues {
-		if len(selectValues) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis
-func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool {
-	for _, values := range lla.ConfigurableValues[axis] {
-		if !values.IsNil() {
-			return true
-		}
-	}
-	return false
-}
-
-// IsEmpty returns true if the attribute has no values under any configuration.
-func (lla LabelListAttribute) IsEmpty() bool {
-	if len(lla.Value.Includes) > 0 {
-		return false
-	}
-	for axis, _ := range lla.ConfigurableValues {
-		if lla.ConfigurableValues[axis].HasConfigurableValues() {
-			return false
-		}
-	}
-	return true
-}
-
-// IsNil returns true if the attribute has not been set for any configuration.
-func (lla LabelListAttribute) IsNil() bool {
-	if lla.Value.Includes != nil {
-		return false
-	}
-	return !lla.HasConfigurableValues()
-}
-
-// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
-// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
-// be removed, e.g. if they could cause duplicate element failures.
-func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
-	val := lla.SelectValue(axis, config)
-	newList := SubtractBazelLabelList(val, labelList)
-	newList.Excludes = append(newList.Excludes, labelList.Includes...)
-	lla.SetSelectValue(axis, config, newList)
-}
-
-// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
-// the base value and included in default values as appropriate.
-func (lla *LabelListAttribute) ResolveExcludes() {
-	// If there are OsAndInApexAxis, we need to use
-	//   * includes from the OS & in APEX Axis for non-Android configs for libraries that need to be
-	//     included in non-Android OSes
-	//   * excludes from the OS Axis for non-Android configs, to exclude libraries that should _not_
-	//     be included in the non-Android OSes
-	if _, ok := lla.ConfigurableValues[OsAndInApexAxis]; ok {
-		inApexLabels := lla.ConfigurableValues[OsAndInApexAxis][ConditionsDefaultConfigKey]
-		for config, labels := range lla.ConfigurableValues[OsConfigurationAxis] {
-			// OsAndroid has already handled its excludes.
-			// We only need to copy the excludes from other arches, so if there are none, skip it.
-			if config == OsAndroid || len(labels.Excludes) == 0 {
-				continue
-			}
-			lla.ConfigurableValues[OsAndInApexAxis][config] = LabelList{
-				Includes: inApexLabels.Includes,
-				Excludes: labels.Excludes,
-			}
-		}
-	}
-
-	for axis, configToLabels := range lla.ConfigurableValues {
-		baseLabels := lla.Value.deepCopy()
-		for config, val := range configToLabels {
-			// Exclude config-specific excludes from base value
-			lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
-
-			// add base values to config specific to add labels excluded by others in this axis
-			// then remove all config-specific excludes
-			allLabels := baseLabels.deepCopy()
-			allLabels.Append(val)
-			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes})
-		}
-
-		// After going through all configs, delete the duplicates in the config
-		// values that are already in the base Value.
-		for config, val := range configToLabels {
-			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
-		}
-
-		// Now that the Value list is finalized for this axis, compare it with
-		// the original list, and union the difference with the default
-		// condition for the axis.
-		difference := SubtractBazelLabelList(baseLabels, lla.Value)
-		existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
-		existingDefaults.Append(difference)
-		lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
-
-		// if everything ends up without includes, just delete the axis
-		if !lla.ConfigurableValues[axis].HasConfigurableValues() {
-			delete(lla.ConfigurableValues, axis)
-		}
-	}
-}
-
-// Partition splits a LabelListAttribute into two LabelListAttributes depending
-// on the return value of the predicate.
-// This function preserves the Includes and Excludes, but it does not provide
-// that information to the partition function.
-func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) {
-	predicated := LabelListAttribute{}
-	unpredicated := LabelListAttribute{}
-
-	valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate)
-	predicated.SetValue(valuePartitionTrue)
-	unpredicated.SetValue(valuePartitionFalse)
-
-	for axis, selectValueLabelLists := range lla.ConfigurableValues {
-		for config, labelList := range selectValueLabelLists {
-			configPredicated, configUnpredicated := labelList.Partition(predicate)
-			predicated.SetSelectValue(axis, config, configPredicated)
-			unpredicated.SetSelectValue(axis, config, configUnpredicated)
-		}
-	}
-
-	return predicated, unpredicated
-}
-
-// OtherModuleContext is a limited context that has methods with information about other modules.
-type OtherModuleContext interface {
-	ModuleFromName(name string) (blueprint.Module, bool)
-	OtherModuleType(m blueprint.Module) string
-	OtherModuleName(m blueprint.Module) string
-	OtherModuleDir(m blueprint.Module) string
-	ModuleErrorf(fmt string, args ...interface{})
-}
-
-// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
-// label and whether it was changed.
-type LabelMapper func(OtherModuleContext, Label) (string, bool)
-
-// LabelPartition contains descriptions of a partition for labels
-type LabelPartition struct {
-	// Extensions to include in this partition
-	Extensions []string
-	// LabelMapper is a function that can map a label to a new label, and indicate whether to include
-	// the mapped label in the partition
-	LabelMapper LabelMapper
-	// Whether to store files not included in any other partition in a group of LabelPartitions
-	// Only one partition in a group of LabelPartitions can enabled Keep_remainder
-	Keep_remainder bool
-}
-
-// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the
-// partition
-type LabelPartitions map[string]LabelPartition
-
-// filter returns a pointer to a label if the label should be included in the partition or nil if
-// not.
-func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
-	if lf.LabelMapper != nil {
-		if newLabel, changed := lf.LabelMapper(ctx, label); changed {
-			return &Label{newLabel, label.OriginalModuleName}
-		}
-	}
-	for _, ext := range lf.Extensions {
-		if strings.HasSuffix(label.Label, ext) {
-			return &label
-		}
-	}
-
-	return nil
-}
-
-// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute
-type PartitionToLabelListAttribute map[string]LabelListAttribute
-
-type partitionToLabelList map[string]*LabelList
-
-func (p partitionToLabelList) appendIncludes(partition string, label Label) {
-	if _, ok := p[partition]; !ok {
-		p[partition] = &LabelList{}
-	}
-	p[partition].Includes = append(p[partition].Includes, label)
-}
-
-func (p partitionToLabelList) excludes(partition string, excludes []Label) {
-	if _, ok := p[partition]; !ok {
-		p[partition] = &LabelList{}
-	}
-	p[partition].Excludes = excludes
-}
-
-// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions
-func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute {
-	ret := PartitionToLabelListAttribute{}
-	var partitionNames []string
-	// Stored as a pointer to distinguish nil (no remainder partition) from empty string partition
-	var remainderPartition *string
-	for p, f := range partitions {
-		partitionNames = append(partitionNames, p)
-		if f.Keep_remainder {
-			if remainderPartition != nil {
-				panic("only one partition can store the remainder")
-			}
-			// If we take the address of p in a loop, we'll end up with the last value of p in
-			// remainderPartition, we want the requested partition
-			capturePartition := p
-			remainderPartition = &capturePartition
-		}
-	}
-
-	partitionLabelList := func(axis ConfigurationAxis, config string) {
-		value := lla.SelectValue(axis, config)
-		partitionToLabels := partitionToLabelList{}
-		for _, item := range value.Includes {
-			wasFiltered := false
-			var inPartition *string
-			for partition, f := range partitions {
-				filtered := f.filter(ctx, item)
-				if filtered == nil {
-					// did not match this filter, keep looking
-					continue
-				}
-				wasFiltered = true
-				partitionToLabels.appendIncludes(partition, *filtered)
-				// don't need to check other partitions if this filter used the item,
-				// continue checking if mapped to another name
-				if *filtered == item {
-					if inPartition != nil {
-						ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition)
-					}
-					capturePartition := partition
-					inPartition = &capturePartition
-				}
-			}
-
-			// if not specified in a partition, add to remainder partition if one exists
-			if !wasFiltered && remainderPartition != nil {
-				partitionToLabels.appendIncludes(*remainderPartition, item)
-			}
-		}
-
-		// ensure empty lists are maintained
-		if value.Excludes != nil {
-			for _, partition := range partitionNames {
-				partitionToLabels.excludes(partition, value.Excludes)
-			}
-		}
-
-		for partition, list := range partitionToLabels {
-			val := ret[partition]
-			(&val).SetSelectValue(axis, config, *list)
-			ret[partition] = val
-		}
-	}
-
-	partitionLabelList(NoConfigAxis, "")
-	for axis, configToList := range lla.ConfigurableValues {
-		for config, _ := range configToList {
-			partitionLabelList(axis, config)
-		}
-	}
-	return ret
-}
-
-// StringAttribute corresponds to the string Bazel attribute type with
-// support for additional metadata, like configurations.
-type StringAttribute struct {
-	// The base value of the string attribute.
-	Value *string
-
-	// The configured attribute label list Values. Optional
-	// a map of independent configurability axes
-	ConfigurableValues configurableStrings
-}
-
-type configurableStrings map[ConfigurationAxis]stringSelectValues
-
-func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) {
-	if cs[axis] == nil {
-		cs[axis] = make(stringSelectValues)
-	}
-	cs[axis][config] = str
-}
-
-type stringSelectValues map[string]*string
-
-// HasConfigurableValues returns true if the attribute contains axis-specific string values.
-func (sa StringAttribute) HasConfigurableValues() bool {
-	for _, selectValues := range sa.ConfigurableValues {
-		if len(selectValues) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// SetValue sets the base, non-configured value for the Label
-func (sa *StringAttribute) SetValue(value string) {
-	sa.SetSelectValue(NoConfigAxis, "", &value)
-}
-
-// SetSelectValue set a value for a bazel select for the given axis, config and value.
-func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		sa.Value = str
-	case arch, os, osArch, productVariables, sanitizersEnabled:
-		if sa.ConfigurableValues == nil {
-			sa.ConfigurableValues = make(configurableStrings)
-		}
-		sa.ConfigurableValues.setValueForAxis(axis, config, str)
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SelectValue gets a value for a bazel select for the given axis and config.
-func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		return sa.Value
-	case arch, os, osArch, productVariables, sanitizersEnabled:
-		if v, ok := sa.ConfigurableValues[axis][config]; ok {
-			return v
-		} else {
-			return nil
-		}
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
-func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	return SortedConfigurationAxes(sa.ConfigurableValues)
-}
-
-// Collapse reduces the configurable axes of the string attribute to a single axis.
-// This is necessary for final writing to bp2build, as a configurable string
-// attribute can only be comprised by a single select.
-func (sa *StringAttribute) Collapse() error {
-	axisTypes := sa.axisTypes()
-	_, containsOs := axisTypes[os]
-	_, containsArch := axisTypes[arch]
-	_, containsOsArch := axisTypes[osArch]
-	_, containsProductVariables := axisTypes[productVariables]
-	if containsProductVariables {
-		if containsOs || containsArch || containsOsArch {
-			return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes")
-		}
-	}
-	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
-		// If a bool attribute has both os and arch configuration axes, the only
-		// way to successfully union their values is to increase the granularity
-		// of the configuration criteria to os_arch.
-		for osType, supportedArchs := range osToArchMap {
-			for _, supportedArch := range supportedArchs {
-				osArch := osArchString(osType, supportedArch)
-				if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
-					// Do nothing, as the arch_os is explicitly defined already.
-				} else {
-					archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch)
-					osVal := sa.SelectValue(OsConfigurationAxis, osType)
-					if osVal != nil && archVal != nil {
-						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
-						// runs after os mutator.
-						sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
-					} else if osVal != nil && archVal == nil {
-						sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
-					} else if osVal == nil && archVal != nil {
-						sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
-					}
-				}
-			}
-		}
-		/// All os_arch values are now set. Clear os and arch axes.
-		delete(sa.ConfigurableValues, ArchConfigurationAxis)
-		delete(sa.ConfigurableValues, OsConfigurationAxis)
-		// Verify post-condition; this should never fail, provided no additional
-		// axes are introduced.
-		if len(sa.ConfigurableValues) > 1 {
-			panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
-		}
-	} else if containsProductVariables {
-		usedBaseValue := false
-		for a, configToProp := range sa.ConfigurableValues {
-			if a.configurationType == productVariables {
-				for c, p := range configToProp {
-					if p == nil {
-						sa.SetSelectValue(a, c, sa.Value)
-						usedBaseValue = true
-					}
-				}
-			}
-		}
-		if usedBaseValue {
-			sa.Value = nil
-		}
-	}
-	return nil
-}
-
-func (sa *StringAttribute) axisTypes() map[configurationType]bool {
-	types := map[configurationType]bool{}
-	for k := range sa.ConfigurableValues {
-		if strs := sa.ConfigurableValues[k]; len(strs) > 0 {
-			types[k.configurationType] = true
-		}
-	}
-	return types
-}
-
-// StringListAttribute corresponds to the string_list Bazel attribute type with
-// support for additional metadata, like configurations.
-type StringListAttribute struct {
-	// The base value of the string list attribute.
-	Value []string
-
-	// The configured attribute label list Values. Optional
-	// a map of independent configurability axes
-	ConfigurableValues configurableStringLists
-
-	// If a property has struct tag "variant_prepend", this value should
-	// be set to True, so that when bp2build generates BUILD.bazel, variant
-	// properties(select ...) come before general properties.
-	Prepend bool
-}
-
-// IsEmpty returns true if the attribute has no values under any configuration.
-func (sla StringListAttribute) IsEmpty() bool {
-	return len(sla.Value) == 0 && !sla.HasConfigurableValues()
-}
-
-type configurableStringLists map[ConfigurationAxis]stringListSelectValues
-
-func (csl configurableStringLists) Append(other configurableStringLists) {
-	for axis, otherSelects := range other {
-		selects := csl[axis]
-		if selects == nil {
-			selects = make(stringListSelectValues, len(otherSelects))
-		}
-		selects.appendSelects(otherSelects)
-		csl[axis] = selects
-	}
-}
-
-func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
-	if csl[axis] == nil {
-		csl[axis] = make(stringListSelectValues)
-	}
-	csl[axis][config] = list
-}
-
-type stringListSelectValues map[string][]string
-
-func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
-	for k, v := range other {
-		sl[k] = append(sl[k], v...)
-	}
-}
-
-func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
-	for _, val := range sl {
-		if len(val) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
-func MakeStringListAttribute(value []string) StringListAttribute {
-	// NOTE: These strings are not necessarily unique or sorted.
-	return StringListAttribute{
-		Value:              value,
-		ConfigurableValues: make(configurableStringLists),
-	}
-}
-
-// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
-func (sla StringListAttribute) HasConfigurableValues() bool {
-	for _, selectValues := range sla.ConfigurableValues {
-		if len(selectValues) > 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// Append appends all values, including os and arch specific ones, from another
-// StringListAttribute to this StringListAttribute
-func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
-	sla.Value = append(sla.Value, other.Value...)
-	if sla.ConfigurableValues == nil {
-		sla.ConfigurableValues = make(configurableStringLists)
-	}
-	sla.ConfigurableValues.Append(other.ConfigurableValues)
-	return sla
-}
-
-func (sla *StringListAttribute) Clone() *StringListAttribute {
-	result := &StringListAttribute{}
-	return result.Append(*sla)
-}
-
-// SetSelectValue set a value for a bazel select for the given axis, config and value.
-func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		sla.Value = list
-	case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled:
-		if sla.ConfigurableValues == nil {
-			sla.ConfigurableValues = make(configurableStringLists)
-		}
-		sla.ConfigurableValues.setValueForAxis(axis, config, list)
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SelectValue gets a value for a bazel select for the given axis and config.
-func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
-	axis.validateConfig(config)
-	switch axis.configurationType {
-	case noConfig:
-		return sla.Value
-	case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled:
-		return sla.ConfigurableValues[axis][config]
-	default:
-		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
-	}
-}
-
-// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
-func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
-	return SortedConfigurationAxes(sla.ConfigurableValues)
-}
-
-// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
-// configuration-specific values. For example, if we would convert this StringListAttribute as:
-//
-//	["a", "b", "c"] + select({
-//	   "//condition:one": ["a", "d"],
-//	   "//conditions:default": [],
-//	})
-//
-// after this function, we would convert this StringListAttribute as:
-//
-//	["a", "b", "c"] + select({
-//	   "//condition:one": ["d"],
-//	   "//conditions:default": [],
-//	})
-func (sla *StringListAttribute) DeduplicateAxesFromBase() {
-	base := sla.Value
-	for axis, configToList := range sla.ConfigurableValues {
-		for config, list := range configToList {
-			remaining := SubtractStrings(list, base)
-			if len(remaining) == 0 {
-				delete(sla.ConfigurableValues[axis], config)
-			} else {
-				sla.ConfigurableValues[axis][config] = remaining
-			}
-		}
-	}
-}
-
-// TryVariableSubstitution, replace string substitution formatting within each string in slice with
-// Starlark string.format compatible tag for productVariable.
-func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
-	if len(slice) == 0 {
-		return slice, false
-	}
-	ret := make([]string, 0, len(slice))
-	changesMade := false
-	for _, s := range slice {
-		newS, changed := TryVariableSubstitution(s, productVariable)
-		ret = append(ret, newS)
-		changesMade = changesMade || changed
-	}
-	return ret, changesMade
-}
-
-// TryVariableSubstitution, replace string substitution formatting within s with Starlark
-// string.format compatible tag for productVariable.
-func TryVariableSubstitution(s string, productVariable string) (string, bool) {
-	sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
-	return sub, s != sub
-}
-
-// StringMapAttribute is a map of strings.
-// The use case for this is storing the flag_values in a config_setting object.
-// Bazel rules do not support map attributes, and this should NOT be used in Bazel rules.
-type StringMapAttribute map[string]string
-
-// ConfigSettingAttributes stores the keys of a config_setting object.
-type ConfigSettingAttributes struct {
-	// Each key in Flag_values is a label to a custom string_setting
-	Flag_values StringMapAttribute
-	// Each element in Constraint_values is a label to a constraint_value
-	Constraint_values LabelListAttribute
-}
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
deleted file mode 100644
index 751cb8b..0000000
--- a/bazel/properties_test.go
+++ /dev/null
@@ -1,836 +0,0 @@
-// Copyright 2021 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 bazel
-
-import (
-	"reflect"
-	"strings"
-	"testing"
-
-	"github.com/google/blueprint/proptools"
-)
-
-func TestUniqueBazelLabels(t *testing.T) {
-	testCases := []struct {
-		originalLabels       []Label
-		expectedUniqueLabels []Label
-	}{
-		{
-			originalLabels: []Label{
-				{Label: "a"},
-				{Label: "b"},
-				{Label: "a"},
-				{Label: "c"},
-				// namespaces
-				{Label: "//foo:bar", OriginalModuleName: "bar"},       // when referenced from foo namespace
-				{Label: "//foo:bar", OriginalModuleName: "//foo:bar"}, // when reference from root namespace
-			},
-			expectedUniqueLabels: []Label{
-				{Label: "//foo:bar", OriginalModuleName: "bar"},
-				{Label: "a"},
-				{Label: "b"},
-				{Label: "c"},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualUniqueLabels := UniqueSortedBazelLabels(tc.originalLabels)
-		if !reflect.DeepEqual(tc.expectedUniqueLabels, actualUniqueLabels) {
-			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabels, actualUniqueLabels)
-		}
-	}
-}
-
-func TestSubtractStrings(t *testing.T) {
-	testCases := []struct {
-		haystack       []string
-		needle         []string
-		expectedResult []string
-	}{
-		{
-			haystack: []string{
-				"a",
-				"b",
-				"c",
-			},
-			needle: []string{
-				"a",
-			},
-			expectedResult: []string{
-				"b", "c",
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualResult := SubtractStrings(tc.haystack, tc.needle)
-		if !reflect.DeepEqual(tc.expectedResult, actualResult) {
-			t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult)
-		}
-	}
-}
-
-func TestSubtractBazelLabelList(t *testing.T) {
-	testCases := []struct {
-		haystack       LabelList
-		needle         LabelList
-		expectedResult LabelList
-	}{
-		{
-			haystack: LabelList{
-				Includes: []Label{
-					{Label: "a"},
-					{Label: "b"},
-					{Label: "c"},
-				},
-				Excludes: []Label{
-					{Label: "x"},
-					{Label: "y"},
-					{Label: "z"},
-				},
-			},
-			needle: LabelList{
-				Includes: []Label{
-					{Label: "a"},
-				},
-				Excludes: []Label{
-					{Label: "z"},
-				},
-			},
-			// NOTE: Excludes are intentionally not subtracted
-			expectedResult: LabelList{
-				Includes: []Label{
-					{Label: "b"},
-					{Label: "c"},
-				},
-				Excludes: []Label{
-					{Label: "x"},
-					{Label: "y"},
-					{Label: "z"},
-				},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualResult := SubtractBazelLabelList(tc.haystack, tc.needle)
-		if !reflect.DeepEqual(tc.expectedResult, actualResult) {
-			t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult)
-		}
-	}
-}
-
-func TestSubtractBazelLabelListAttribute(t *testing.T) {
-	testCases := []struct {
-		haystack LabelListAttribute
-		needle   LabelListAttribute
-		expected LabelListAttribute
-	}{
-		{
-			haystack: LabelListAttribute{
-				Value: makeLabelList(
-					[]string{"a", "b", "a", "c"},
-					[]string{"x", "x", "y", "z"},
-				),
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"arm": makeLabelList([]string{"arm_1", "arm_2"}, []string{}),
-						"x86": makeLabelList([]string{"x86_3", "x86_4", "x86_5"}, []string{"x86_5"}),
-					},
-				},
-			},
-			needle: LabelListAttribute{
-				Value: makeLabelList(
-					[]string{"d", "a"},
-					[]string{"x", "y2", "z2"},
-				),
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"arm": makeLabelList([]string{"arm_1", "arm_3"}, []string{}),
-						"x86": makeLabelList([]string{"x86_3", "x86_4"}, []string{"x86_6"}),
-					},
-				},
-			},
-			expected: LabelListAttribute{
-				Value: makeLabelList(
-					[]string{"b", "c"},
-					[]string{"x", "x", "y", "z"},
-				),
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"arm": makeLabelList([]string{"arm_2"}, []string{}),
-						"x86": makeLabelList([]string{"x86_5"}, []string{"x86_5"}),
-					},
-				},
-				ForceSpecifyEmptyList: false,
-				EmitEmptyList:         false,
-				Prepend:               false,
-			},
-		},
-	}
-	for _, tc := range testCases {
-		got := SubtractBazelLabelListAttribute(tc.haystack, tc.needle)
-		if !reflect.DeepEqual(tc.expected, got) {
-			t.Fatalf("Expected\n%v, but got\n%v", tc.expected, got)
-		}
-	}
-}
-
-func TestFirstUniqueBazelLabelList(t *testing.T) {
-	testCases := []struct {
-		originalLabelList       LabelList
-		expectedUniqueLabelList LabelList
-	}{
-		{
-			originalLabelList: LabelList{
-				Includes: []Label{
-					{Label: "a"},
-					{Label: "b"},
-					{Label: "a"},
-					{Label: "c"},
-					// namespaces
-					{Label: "//foo:bar", OriginalModuleName: "bar"},       // when referenced from foo namespace
-					{Label: "//foo:bar", OriginalModuleName: "//foo:bar"}, // when referenced from root namespace
-				},
-				Excludes: []Label{
-					{Label: "x"},
-					{Label: "x"},
-					{Label: "y"},
-					{Label: "z"},
-				},
-			},
-			expectedUniqueLabelList: LabelList{
-				Includes: []Label{
-					{Label: "a"},
-					{Label: "b"},
-					{Label: "c"},
-					{Label: "//foo:bar", OriginalModuleName: "bar"},
-				},
-				Excludes: []Label{
-					{Label: "x"},
-					{Label: "y"},
-					{Label: "z"},
-				},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualUniqueLabelList := FirstUniqueBazelLabelList(tc.originalLabelList)
-		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
-			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
-		}
-	}
-}
-
-func TestFirstUniqueBazelLabelListAttribute(t *testing.T) {
-	testCases := []struct {
-		originalLabelList       LabelListAttribute
-		expectedUniqueLabelList LabelListAttribute
-	}{
-		{
-			originalLabelList: LabelListAttribute{
-				Value: makeLabelList(
-					[]string{"a", "b", "a", "c"},
-					[]string{"x", "x", "y", "z"},
-				),
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"arm": makeLabelList([]string{"1", "2", "1"}, []string{}),
-						"x86": makeLabelList([]string{"3", "4", "4"}, []string{"5", "5"}),
-					},
-				},
-			},
-			expectedUniqueLabelList: LabelListAttribute{
-				Value: makeLabelList(
-					[]string{"a", "b", "c"},
-					[]string{"x", "y", "z"},
-				),
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"arm": makeLabelList([]string{"1", "2"}, []string{}),
-						"x86": makeLabelList([]string{"3", "4"}, []string{"5"}),
-					},
-				},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualUniqueLabelList := FirstUniqueBazelLabelListAttribute(tc.originalLabelList)
-		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
-			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
-		}
-	}
-}
-
-func TestUniqueSortedBazelLabelList(t *testing.T) {
-	testCases := []struct {
-		originalLabelList       LabelList
-		expectedUniqueLabelList LabelList
-	}{
-		{
-			originalLabelList: LabelList{
-				Includes: []Label{
-					{Label: "c"},
-					{Label: "a"},
-					{Label: "a"},
-					{Label: "b"},
-				},
-				Excludes: []Label{
-					{Label: "y"},
-					{Label: "z"},
-					{Label: "x"},
-					{Label: "x"},
-				},
-			},
-			expectedUniqueLabelList: LabelList{
-				Includes: []Label{
-					{Label: "a"},
-					{Label: "b"},
-					{Label: "c"},
-				},
-				Excludes: []Label{
-					{Label: "x"},
-					{Label: "y"},
-					{Label: "z"},
-				},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		actualUniqueLabelList := UniqueSortedBazelLabelList(tc.originalLabelList)
-		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
-			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
-		}
-	}
-}
-
-func makeLabels(labels ...string) []Label {
-	var ret []Label
-	for _, l := range labels {
-		ret = append(ret, Label{Label: l})
-	}
-	return ret
-}
-
-func makeLabelList(includes, excludes []string) LabelList {
-	return LabelList{
-		Includes: makeLabels(includes...),
-		Excludes: makeLabels(excludes...),
-	}
-}
-
-func TestResolveExcludes(t *testing.T) {
-	attr := LabelListAttribute{
-		Value: makeLabelList(
-			[]string{
-				"all_include",
-				"arm_exclude",
-				"android_exclude",
-				"product_config_exclude",
-			},
-			[]string{"all_exclude"},
-		),
-		ConfigurableValues: configurableLabelLists{
-			ArchConfigurationAxis: labelListSelectValues{
-				"arm":                      makeLabelList([]string{}, []string{"arm_exclude"}),
-				"x86":                      makeLabelList([]string{"x86_include"}, []string{}),
-				ConditionsDefaultConfigKey: makeLabelList([]string{"default_include"}, []string{}),
-			},
-			OsConfigurationAxis: labelListSelectValues{
-				"android": makeLabelList([]string{}, []string{"android_exclude"}),
-				"linux":   makeLabelList([]string{"linux_include"}, []string{}),
-			},
-			OsArchConfigurationAxis: labelListSelectValues{
-				"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
-			},
-			ProductVariableConfigurationAxis(false, "product_with_defaults"): labelListSelectValues{
-				"a":                        makeLabelList([]string{}, []string{"not_in_value"}),
-				"b":                        makeLabelList([]string{"b_val"}, []string{}),
-				"c":                        makeLabelList([]string{"c_val"}, []string{}),
-				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}),
-			},
-			ProductVariableConfigurationAxis(false, "product_only_with_excludes"): labelListSelectValues{
-				"a": makeLabelList([]string{}, []string{"product_config_exclude"}),
-			},
-		},
-	}
-
-	attr.ResolveExcludes()
-
-	expectedBaseIncludes := []Label{{Label: "all_include"}}
-	if !reflect.DeepEqual(expectedBaseIncludes, attr.Value.Includes) {
-		t.Errorf("Expected Value includes %q, got %q", attr.Value.Includes, expectedBaseIncludes)
-	}
-	var nilLabels []Label
-	expectedConfiguredIncludes := map[ConfigurationAxis]map[string][]Label{
-		ArchConfigurationAxis: {
-			"arm":                      nilLabels,
-			"x86":                      makeLabels("arm_exclude", "x86_include"),
-			ConditionsDefaultConfigKey: makeLabels("arm_exclude", "default_include"),
-		},
-		OsConfigurationAxis: {
-			"android":                  nilLabels,
-			"linux":                    makeLabels("android_exclude", "linux_include"),
-			ConditionsDefaultConfigKey: makeLabels("android_exclude"),
-		},
-		OsArchConfigurationAxis: {
-			"linux_x86":                makeLabels("linux_x86_include"),
-			ConditionsDefaultConfigKey: nilLabels,
-		},
-		ProductVariableConfigurationAxis(false, "product_with_defaults"): {
-			"a":                        nilLabels,
-			"b":                        makeLabels("b_val"),
-			"c":                        makeLabels("c_val"),
-			ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
-		},
-		ProductVariableConfigurationAxis(false, "product_only_with_excludes"): {
-			"a":                        nilLabels,
-			ConditionsDefaultConfigKey: makeLabels("product_config_exclude"),
-		},
-	}
-	for _, axis := range attr.SortedConfigurationAxes() {
-		if _, ok := expectedConfiguredIncludes[axis]; !ok {
-			t.Errorf("Found unexpected axis %s", axis)
-			continue
-		}
-		expectedForAxis := expectedConfiguredIncludes[axis]
-		gotForAxis := attr.ConfigurableValues[axis]
-		if len(expectedForAxis) != len(gotForAxis) {
-			t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis)
-		}
-		for config, value := range gotForAxis {
-			if expected, ok := expectedForAxis[config]; ok {
-				if !reflect.DeepEqual(expected, value.Includes) {
-					t.Errorf("For %s,\nexpected: %#v\ngot %#v", axis, expected, value.Includes)
-				}
-			} else {
-				t.Errorf("Got unexpected config %q for %s", config, axis)
-			}
-		}
-	}
-}
-
-func TestLabelListAttributePartition(t *testing.T) {
-	testCases := []struct {
-		name         string
-		input        LabelListAttribute
-		predicated   LabelListAttribute
-		unpredicated LabelListAttribute
-		predicate    func(label Label) bool
-	}{
-		{
-			name: "move all to predicated partition",
-			input: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			predicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			unpredicated: LabelListAttribute{},
-			predicate: func(label Label) bool {
-				return true
-			},
-		},
-		{
-			name: "move all to unpredicated partition",
-			input: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			predicated: LabelListAttribute{},
-			unpredicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			predicate: func(label Label) bool {
-				return false
-			},
-		},
-		{
-			name: "partition includes and excludes",
-			input: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			predicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "keep2"},
-				[]string{"keep1", "keep2"},
-			)),
-			unpredicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"throw1", "throw2"},
-				[]string{"throw1", "throw2"},
-			)),
-			predicate: func(label Label) bool {
-				return strings.HasPrefix(label.Label, "keep")
-			},
-		},
-		{
-			name: "partition excludes only",
-			input: MakeLabelListAttribute(makeLabelList(
-				[]string{},
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-			)),
-			predicated: MakeLabelListAttribute(makeLabelList(
-				[]string{},
-				[]string{"keep1", "keep2"},
-			)),
-			unpredicated: MakeLabelListAttribute(makeLabelList(
-				[]string{},
-				[]string{"throw1", "throw2"},
-			)),
-			predicate: func(label Label) bool {
-				return strings.HasPrefix(label.Label, "keep")
-			},
-		},
-		{
-			name: "partition includes only",
-			input: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "throw1", "keep2", "throw2"},
-				[]string{},
-			)),
-			predicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"keep1", "keep2"},
-				[]string{},
-			)),
-			unpredicated: MakeLabelListAttribute(makeLabelList(
-				[]string{"throw1", "throw2"},
-				[]string{},
-			)),
-			predicate: func(label Label) bool {
-				return strings.HasPrefix(label.Label, "keep")
-			},
-		},
-		{
-			name:         "empty partition",
-			input:        MakeLabelListAttribute(makeLabelList([]string{}, []string{})),
-			predicated:   LabelListAttribute{},
-			unpredicated: LabelListAttribute{},
-			predicate: func(label Label) bool {
-				return true
-			},
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			predicated, unpredicated := tc.input.Partition(tc.predicate)
-			if !predicated.Value.Equals(tc.predicated.Value) {
-				t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated)
-			}
-			for axis, configs := range predicated.ConfigurableValues {
-				tcConfigs, ok := tc.predicated.ConfigurableValues[axis]
-				if !ok || !reflect.DeepEqual(configs, tcConfigs) {
-					t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated)
-				}
-			}
-			if !unpredicated.Value.Equals(tc.unpredicated.Value) {
-				t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated)
-			}
-			for axis, configs := range unpredicated.ConfigurableValues {
-				tcConfigs, ok := tc.unpredicated.ConfigurableValues[axis]
-				if !ok || !reflect.DeepEqual(configs, tcConfigs) {
-					t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated)
-				}
-			}
-		})
-	}
-}
-
-// labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of
-// typ
-func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper {
-	return func(omc OtherModuleContext, label Label) (string, bool) {
-		m, ok := omc.ModuleFromName(label.Label)
-		if !ok {
-			return label.Label, false
-		}
-		mTyp := omc.OtherModuleType(m)
-		if typ == mTyp {
-			return label.Label + suffix, true
-		}
-		return label.Label, false
-	}
-}
-
-func TestPartitionLabelListAttribute(t *testing.T) {
-	testCases := []struct {
-		name           string
-		ctx            *OtherModuleTestContext
-		labelList      LabelListAttribute
-		filters        LabelPartitions
-		expected       PartitionToLabelListAttribute
-		expectedErrMsg *string
-	}{
-		{
-			name: "no configurable values",
-			ctx:  &OtherModuleTestContext{},
-			labelList: LabelListAttribute{
-				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
-			},
-			filters: LabelPartitions{
-				"A": LabelPartition{Extensions: []string{".a"}},
-				"B": LabelPartition{Extensions: []string{".b"}},
-				"C": LabelPartition{Extensions: []string{".c"}},
-			},
-			expected: PartitionToLabelListAttribute{
-				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})},
-				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
-				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
-			},
-		},
-		{
-			name: "no configurable values, remainder partition",
-			ctx:  &OtherModuleTestContext{},
-			labelList: LabelListAttribute{
-				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
-			},
-			filters: LabelPartitions{
-				"A": LabelPartition{Extensions: []string{".a"}, Keep_remainder: true},
-				"B": LabelPartition{Extensions: []string{".b"}},
-				"C": LabelPartition{Extensions: []string{".c"}},
-			},
-			expected: PartitionToLabelListAttribute{
-				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "d.d", "e.e"}, []string{})},
-				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
-				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
-			},
-		},
-		{
-			name: "no configurable values, empty partition",
-			ctx:  &OtherModuleTestContext{},
-			labelList: LabelListAttribute{
-				Value: makeLabelList([]string{"a.a", "c.c"}, []string{}),
-			},
-			filters: LabelPartitions{
-				"A": LabelPartition{Extensions: []string{".a"}},
-				"B": LabelPartition{Extensions: []string{".b"}},
-				"C": LabelPartition{Extensions: []string{".c"}},
-			},
-			expected: PartitionToLabelListAttribute{
-				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})},
-				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
-			},
-		},
-		{
-			name: "no configurable values, has map",
-			ctx: &OtherModuleTestContext{
-				Modules: []TestModuleInfo{{ModuleName: "srcs", Typ: "fg", Dir: "dir"}},
-			},
-			labelList: LabelListAttribute{
-				Value: makeLabelList([]string{"a.a", "srcs", "b.b", "c.c"}, []string{}),
-			},
-			filters: LabelPartitions{
-				"A": LabelPartition{Extensions: []string{".a"}, LabelMapper: labelAddSuffixForTypeMapper("_a", "fg")},
-				"B": LabelPartition{Extensions: []string{".b"}},
-				"C": LabelPartition{Extensions: []string{".c"}},
-			},
-			expected: PartitionToLabelListAttribute{
-				"A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "srcs_a"}, []string{})},
-				"B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})},
-				"C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})},
-			},
-		},
-		{
-			name: "configurable values, keeps empty if excludes",
-			ctx:  &OtherModuleTestContext{},
-			labelList: LabelListAttribute{
-				ConfigurableValues: configurableLabelLists{
-					ArchConfigurationAxis: labelListSelectValues{
-						"x86":    makeLabelList([]string{"a.a", "c.c"}, []string{}),
-						"arm":    makeLabelList([]string{"b.b"}, []string{}),
-						"x86_64": makeLabelList([]string{"b.b"}, []string{"d.d"}),
-					},
-				},
-			},
-			filters: LabelPartitions{
-				"A": LabelPartition{Extensions: []string{".a"}},
-				"B": LabelPartition{Extensions: []string{".b"}},
-				"C": LabelPartition{Extensions: []string{".c"}},
-			},
-			expected: PartitionToLabelListAttribute{
-				"A": LabelListAttribute{
-					ConfigurableValues: configurableLabelLists{
-						ArchConfigurationAxis: labelListSelectValues{
-							"x86":    makeLabelList([]string{"a.a"}, []string{}),
-							"x86_64": makeLabelList([]string{}, []string{"c.c"}),
-						},
-					},
-				},
-				"B": LabelListAttribute{
-					ConfigurableValues: configurableLabelLists{
-						ArchConfigurationAxis: labelListSelectValues{
-							"arm":    makeLabelList([]string{"b.b"}, []string{}),
-							"x86_64": makeLabelList([]string{"b.b"}, []string{"c.c"}),
-						},
-					},
-				},
-				"C": LabelListAttribute{
-					ConfigurableValues: configurableLabelLists{
-						ArchConfigurationAxis: labelListSelectValues{
-							"x86":    makeLabelList([]string{"c.c"}, []string{}),
-							"x86_64": makeLabelList([]string{}, []string{"c.c"}),
-						},
-					},
-				},
-			},
-		},
-		{
-			name: "error for multiple partitions same value",
-			ctx:  &OtherModuleTestContext{},
-			labelList: LabelListAttribute{
-				Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}),
-			},
-			filters: LabelPartitions{
-				"A":       LabelPartition{Extensions: []string{".a"}},
-				"other A": LabelPartition{Extensions: []string{".a"}},
-			},
-			expected:       PartitionToLabelListAttribute{},
-			expectedErrMsg: proptools.StringPtr(`"a.a" was found in multiple partitions:`),
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			got := PartitionLabelListAttribute(tc.ctx, &tc.labelList, tc.filters)
-
-			if hasErrors, expectsErr := len(tc.ctx.errors) > 0, tc.expectedErrMsg != nil; hasErrors != expectsErr {
-				t.Errorf("Unexpected error(s): %q, expected: %q", tc.ctx.errors, *tc.expectedErrMsg)
-			} else if tc.expectedErrMsg != nil {
-				found := false
-				for _, err := range tc.ctx.errors {
-					if strings.Contains(err, *tc.expectedErrMsg) {
-						found = true
-						break
-					}
-				}
-
-				if !found {
-					t.Errorf("Expected error message: %q, got %q", *tc.expectedErrMsg, tc.ctx.errors)
-				}
-				return
-			}
-
-			if len(tc.expected) != len(got) {
-				t.Errorf("Expected %d partitions, got %d partitions", len(tc.expected), len(got))
-			}
-			for partition, expectedLla := range tc.expected {
-				gotLla, ok := got[partition]
-				if !ok {
-					t.Errorf("Expected partition %q, but it was not found %v", partition, got)
-					continue
-				}
-				expectedLabelList := expectedLla.Value
-				gotLabelList := gotLla.Value
-				if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) {
-					t.Errorf("Expected no config includes %v, got %v", expectedLabelList.Includes, gotLabelList.Includes)
-				}
-				expectedAxes := expectedLla.SortedConfigurationAxes()
-				gotAxes := gotLla.SortedConfigurationAxes()
-				if !reflect.DeepEqual(expectedAxes, gotAxes) {
-					t.Errorf("Expected axes %v, got %v (%#v)", expectedAxes, gotAxes, gotLla)
-				}
-				for _, axis := range expectedLla.SortedConfigurationAxes() {
-					if _, exists := gotLla.ConfigurableValues[axis]; !exists {
-						t.Errorf("Expected %s to be a supported axis, but it was not found", axis)
-					}
-					if expected, got := expectedLla.ConfigurableValues[axis], gotLla.ConfigurableValues[axis]; len(expected) != len(got) {
-						t.Errorf("For axis %q: expected configs %v, got %v", axis, expected, got)
-					}
-					for config, expectedLabelList := range expectedLla.ConfigurableValues[axis] {
-						gotLabelList, exists := gotLla.ConfigurableValues[axis][config]
-						if !exists {
-							t.Errorf("Expected %s to be a supported config, but config was not found", config)
-							continue
-						}
-						if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) {
-							t.Errorf("Expected %s %s includes %v, got %v", axis, config, expectedLabelList.Includes, gotLabelList.Includes)
-						}
-					}
-				}
-			}
-		})
-	}
-}
-
-func TestDeduplicateAxesFromBase(t *testing.T) {
-	attr := StringListAttribute{
-		Value: []string{
-			"all_include",
-			"arm_include",
-			"android_include",
-			"linux_x86_include",
-		},
-		ConfigurableValues: configurableStringLists{
-			ArchConfigurationAxis: stringListSelectValues{
-				"arm": []string{"arm_include"},
-				"x86": []string{"x86_include"},
-			},
-			OsConfigurationAxis: stringListSelectValues{
-				"android": []string{"android_include"},
-				"linux":   []string{"linux_include"},
-			},
-			OsArchConfigurationAxis: stringListSelectValues{
-				"linux_x86": {"linux_x86_include"},
-			},
-			ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{
-				"a": []string{"not_in_value"},
-			},
-		},
-	}
-
-	attr.DeduplicateAxesFromBase()
-
-	expectedBaseIncludes := []string{
-		"all_include",
-		"arm_include",
-		"android_include",
-		"linux_x86_include",
-	}
-	if !reflect.DeepEqual(expectedBaseIncludes, attr.Value) {
-		t.Errorf("Expected Value includes %q, got %q", attr.Value, expectedBaseIncludes)
-	}
-	expectedConfiguredIncludes := configurableStringLists{
-		ArchConfigurationAxis: stringListSelectValues{
-			"x86": []string{"x86_include"},
-		},
-		OsConfigurationAxis: stringListSelectValues{
-			"linux": []string{"linux_include"},
-		},
-		OsArchConfigurationAxis: stringListSelectValues{},
-		ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{
-			"a": []string{"not_in_value"},
-		},
-	}
-	for _, axis := range attr.SortedConfigurationAxes() {
-		if _, ok := expectedConfiguredIncludes[axis]; !ok {
-			t.Errorf("Found unexpected axis %s", axis)
-			continue
-		}
-		expectedForAxis := expectedConfiguredIncludes[axis]
-		gotForAxis := attr.ConfigurableValues[axis]
-		if len(expectedForAxis) != len(gotForAxis) {
-			t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis)
-		}
-		for config, value := range gotForAxis {
-			if expected, ok := expectedForAxis[config]; ok {
-				if !reflect.DeepEqual(expected, value) {
-					t.Errorf("For %s, expected: %#v, got %#v", axis, expected, value)
-				}
-			} else {
-				t.Errorf("Got unexpected config %q for %s", config, axis)
-			}
-		}
-	}
-}
diff --git a/bazel/testing.go b/bazel/testing.go
deleted file mode 100644
index 9a43b61..0000000
--- a/bazel/testing.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2021 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 bazel
-
-import (
-	"fmt"
-
-	"github.com/google/blueprint"
-)
-
-// TestModuleInfo implements blueprint.Module interface with sufficient information to mock a subset of
-// a blueprint ModuleContext
-type TestModuleInfo struct {
-	ModuleName string
-	Typ        string
-	Dir        string
-}
-
-// Name returns name for testModuleInfo -- required to implement blueprint.Module
-func (mi TestModuleInfo) Name() string {
-	return mi.ModuleName
-}
-
-// GenerateBuildActions unused, but required to implmeent blueprint.Module
-func (mi TestModuleInfo) GenerateBuildActions(blueprint.ModuleContext) {}
-
-func (mi TestModuleInfo) equals(other TestModuleInfo) bool {
-	return mi.ModuleName == other.ModuleName && mi.Typ == other.Typ && mi.Dir == other.Dir
-}
-
-// ensure testModuleInfo implements blueprint.Module
-var _ blueprint.Module = TestModuleInfo{}
-
-// OtherModuleTestContext is a mock context that implements OtherModuleContext
-type OtherModuleTestContext struct {
-	Modules []TestModuleInfo
-	errors  []string
-}
-
-// ModuleFromName retrieves the testModuleInfo corresponding to name, if it exists
-func (omc *OtherModuleTestContext) ModuleFromName(name string) (blueprint.Module, bool) {
-	for _, m := range omc.Modules {
-		if m.ModuleName == name {
-			return m, true
-		}
-	}
-	return TestModuleInfo{}, false
-}
-
-// testModuleInfo returns the testModuleInfo corresponding to a blueprint.Module if it exists in omc
-func (omc *OtherModuleTestContext) testModuleInfo(m blueprint.Module) (TestModuleInfo, bool) {
-	mi, ok := m.(TestModuleInfo)
-	if !ok {
-		return TestModuleInfo{}, false
-	}
-	for _, other := range omc.Modules {
-		if other.equals(mi) {
-			return mi, true
-		}
-	}
-	return TestModuleInfo{}, false
-}
-
-// OtherModuleType returns type of m if it exists in omc
-func (omc *OtherModuleTestContext) OtherModuleType(m blueprint.Module) string {
-	if mi, ok := omc.testModuleInfo(m); ok {
-		return mi.Typ
-	}
-	return ""
-}
-
-// OtherModuleName returns name of m if it exists in omc
-func (omc *OtherModuleTestContext) OtherModuleName(m blueprint.Module) string {
-	if mi, ok := omc.testModuleInfo(m); ok {
-		return mi.ModuleName
-	}
-	return ""
-}
-
-// OtherModuleDir returns dir of m if it exists in omc
-func (omc *OtherModuleTestContext) OtherModuleDir(m blueprint.Module) string {
-	if mi, ok := omc.testModuleInfo(m); ok {
-		return mi.Dir
-	}
-	return ""
-}
-
-func (omc *OtherModuleTestContext) ModuleErrorf(format string, args ...interface{}) {
-	omc.errors = append(omc.errors, fmt.Sprintf(format, args...))
-}
-
-// Ensure otherModuleTestContext implements OtherModuleContext
-var _ OtherModuleContext = &OtherModuleTestContext{}
diff --git a/bin/soongdbg b/bin/soongdbg
index 98d31eb..0807291 100755
--- a/bin/soongdbg
+++ b/bin/soongdbg
@@ -393,12 +393,42 @@
                 print(f"    dep:      {d.id}")
 
 
+class StarCommand:
+    help = "Print the dependencies and reverse dependencies of a module"
+
+    def args(self, parser):
+        parser.add_argument("module", nargs="+",
+                            help="Module to print dependencies of")
+        parser.add_argument("--depth", type=int, required=True,
+                            help="max depth of dependencies")
+        print_args(parser)
+
+    def run(self, args):
+        graph = load_graph()
+        nodes = set()
+        err = False
+        for id in args.module:
+            root = graph.nodes.get(id)
+            if not root:
+                sys.stderr.write(f"error: Can't find root: {id}\n")
+                err = True
+                continue
+            get_deps(nodes, root, args.depth, False, set(args.tag))
+            nodes.remove(root) # Remove it so get_deps doesn't bail out
+            get_deps(nodes, root, args.depth, True, set(args.tag))
+        if err:
+            sys.exit(1)
+        print_nodes(args, nodes, new_module_formatter(args))
+
+
+
 COMMANDS = {
     "between": BetweenCommand(),
     "deps": DepsCommand(),
     "id": IdCommand(),
     "json": JsonCommand(),
     "query": QueryCommand(),
+    "star": StarCommand(),
 }
 
 
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
deleted file mode 100644
index ba12682..0000000
--- a/bp2build/Android.bp
+++ /dev/null
@@ -1,43 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
-    name: "soong-bp2build",
-    pkgPath: "android/soong/bp2build",
-    srcs: [
-        "androidbp_to_build_templates.go",
-        "build_conversion.go",
-        "bzl_conversion.go",
-        "configurability.go",
-        "constants.go",
-        "conversion.go",
-    ],
-    deps: [
-        "blueprint-bootstrap",
-        "soong-aidl-library",
-        "soong-aconfig",
-        "soong-android",
-        "soong-android-allowlists",
-        "soong-android-soongconfig",
-        "soong-apex",
-        "soong-bazel",
-        "soong-cc",
-        "soong-cc-config",
-        "soong-etc",
-        "soong-genrule",
-        "soong-linkerconfig",
-        "soong-python",
-        "soong-rust",
-        "soong-sh",
-        "soong-shared",
-        "soong-starlark-format",
-        "soong-ui-metrics",
-    ],
-    testSrcs: [
-        "conversion_test.go",
-    ],
-    pluginFor: [
-        "soong_build",
-    ],
-}
diff --git a/bp2build/androidbp_to_build_templates.go b/bp2build/androidbp_to_build_templates.go
deleted file mode 100644
index 9b21c32..0000000
--- a/bp2build/androidbp_to_build_templates.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2020 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 bp2build
-
-const (
-	// The default `load` preamble for every generated queryview BUILD file.
-	soongModuleLoad = `package(default_visibility = ["//visibility:public"])
-load("//build/bazel/queryview_rules:soong_module.bzl", "soong_module")
-
-`
-
-	// A macro call in the BUILD file representing a Soong module, with space
-	// for expanding more attributes.
-	soongModuleTargetTemplate = `soong_module(
-    name = "%s",
-    soong_module_name = "%s",
-    soong_module_type = "%s",
-    soong_module_variant = "%s",
-    soong_module_deps = %s,
-%s)`
-
-	ruleTargetTemplate = `%s(
-    name = "%s",
-%s)`
-
-	unnamedRuleTargetTemplate = `%s(
-%s)`
-
-	// A simple provider to mark and differentiate Soong module rule shims from
-	// regular Bazel rules. Every Soong module rule shim returns a
-	// SoongModuleInfo provider, and can only depend on rules returning
-	// SoongModuleInfo in the `soong_module_deps` attribute.
-	providersBzl = `SoongModuleInfo = provider(
-    fields = {
-        "name": "Name of module",
-        "type": "Type of module",
-        "variant": "Variant of module",
-    },
-)
-`
-
-	// The soong_module rule implementation in a .bzl file.
-	soongModuleBzl = `
-%s
-
-load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
-
-def _generic_soong_module_impl(ctx):
-    return [
-        SoongModuleInfo(
-            name = ctx.attr.soong_module_name,
-            type = ctx.attr.soong_module_type,
-            variant = ctx.attr.soong_module_variant,
-        ),
-    ]
-
-generic_soong_module = rule(
-    implementation = _generic_soong_module_impl,
-    attrs = {
-        "soong_module_name": attr.string(mandatory = True),
-        "soong_module_type": attr.string(mandatory = True),
-        "soong_module_variant": attr.string(),
-        "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
-    },
-)
-
-soong_module_rule_map = {
-%s}
-
-_SUPPORTED_TYPES = ["bool", "int", "string"]
-
-def _is_supported_type(value):
-    if type(value) in _SUPPORTED_TYPES:
-        return True
-    elif type(value) == "list":
-        supported = True
-        for v in value:
-            supported = supported and type(v) in _SUPPORTED_TYPES
-        return supported
-    else:
-        return False
-
-# soong_module is a macro that supports arbitrary kwargs, and uses soong_module_type to
-# expand to the right underlying shim.
-def soong_module(name, soong_module_type, **kwargs):
-    soong_module_rule = soong_module_rule_map.get(soong_module_type)
-
-    if soong_module_rule == None:
-        # This module type does not have an existing rule to map to, so use the
-        # generic_soong_module rule instead.
-        generic_soong_module(
-            name = name,
-            soong_module_type = soong_module_type,
-            soong_module_name = kwargs.pop("soong_module_name", ""),
-            soong_module_variant = kwargs.pop("soong_module_variant", ""),
-            soong_module_deps = kwargs.pop("soong_module_deps", []),
-        )
-    else:
-        supported_kwargs = dict()
-        for key, value in kwargs.items():
-            if _is_supported_type(value):
-                supported_kwargs[key] = value
-        soong_module_rule(
-            name = name,
-            **supported_kwargs,
-        )
-`
-
-	// A rule shim for representing a Soong module type and its properties.
-	moduleRuleShim = `
-def _%[1]s_impl(ctx):
-    return [SoongModuleInfo()]
-
-%[1]s = rule(
-    implementation = _%[1]s_impl,
-    attrs = %[2]s
-)
-`
-)
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
deleted file mode 100644
index bd56768..0000000
--- a/bp2build/build_conversion.go
+++ /dev/null
@@ -1,640 +0,0 @@
-// Copyright 2020 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 bp2build
-
-/*
-For shareable/common functionality for conversion from soong-module to build files
-for queryview/bp2build
-*/
-
-import (
-	"fmt"
-	"reflect"
-	"sort"
-	"strings"
-
-	"android/soong/android"
-	"android/soong/bazel"
-	"android/soong/starlark_fmt"
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
-)
-
-type BazelAttributes struct {
-	Attrs map[string]string
-}
-
-type BazelLoadSymbol struct {
-	// The name of the symbol in the file being loaded
-	symbol string
-	// The name the symbol wil have in this file. Can be left blank to use the same name as symbol.
-	alias string
-}
-
-type BazelLoad struct {
-	file    string
-	symbols []BazelLoadSymbol
-}
-
-type BazelTarget struct {
-	name        string
-	packageName string
-	content     string
-	ruleClass   string
-	loads       []BazelLoad
-}
-
-// Label is the fully qualified Bazel label constructed from the BazelTarget's
-// package name and target name.
-func (t BazelTarget) Label() string {
-	if t.packageName == "." {
-		return "//:" + t.name
-	} else {
-		return "//" + t.packageName + ":" + t.name
-	}
-}
-
-// PackageName returns the package of the Bazel target.
-// Defaults to root of tree.
-func (t BazelTarget) PackageName() string {
-	if t.packageName == "" {
-		return "."
-	}
-	return t.packageName
-}
-
-// BazelTargets is a typedef for a slice of BazelTarget objects.
-type BazelTargets []BazelTarget
-
-func (targets BazelTargets) packageRule() *BazelTarget {
-	for _, target := range targets {
-		if target.ruleClass == "package" {
-			return &target
-		}
-	}
-	return nil
-}
-
-// sort a list of BazelTargets in-place, by name, and by generated/handcrafted types.
-func (targets BazelTargets) sort() {
-	sort.Slice(targets, func(i, j int) bool {
-		return targets[i].name < targets[j].name
-	})
-}
-
-// String returns the string representation of BazelTargets, without load
-// statements (use LoadStatements for that), since the targets are usually not
-// adjacent to the load statements at the top of the BUILD file.
-func (targets BazelTargets) String() string {
-	var res strings.Builder
-	for i, target := range targets {
-		if target.ruleClass != "package" {
-			res.WriteString(target.content)
-		}
-		if i != len(targets)-1 {
-			res.WriteString("\n\n")
-		}
-	}
-	return res.String()
-}
-
-// LoadStatements return the string representation of the sorted and deduplicated
-// Starlark rule load statements needed by a group of BazelTargets.
-func (targets BazelTargets) LoadStatements() string {
-	// First, merge all the load statements from all the targets onto one list
-	bzlToLoadedSymbols := map[string][]BazelLoadSymbol{}
-	for _, target := range targets {
-		for _, load := range target.loads {
-		outer:
-			for _, symbol := range load.symbols {
-				alias := symbol.alias
-				if alias == "" {
-					alias = symbol.symbol
-				}
-				for _, otherSymbol := range bzlToLoadedSymbols[load.file] {
-					otherAlias := otherSymbol.alias
-					if otherAlias == "" {
-						otherAlias = otherSymbol.symbol
-					}
-					if symbol.symbol == otherSymbol.symbol && alias == otherAlias {
-						continue outer
-					} else if alias == otherAlias {
-						panic(fmt.Sprintf("Conflicting destination (%s) for loads of %s and %s", alias, symbol.symbol, otherSymbol.symbol))
-					}
-				}
-				bzlToLoadedSymbols[load.file] = append(bzlToLoadedSymbols[load.file], symbol)
-			}
-		}
-	}
-
-	var loadStatements strings.Builder
-	for i, bzl := range android.SortedKeys(bzlToLoadedSymbols) {
-		symbols := bzlToLoadedSymbols[bzl]
-		loadStatements.WriteString("load(\"")
-		loadStatements.WriteString(bzl)
-		loadStatements.WriteString("\", ")
-		sort.Slice(symbols, func(i, j int) bool {
-			if symbols[i].symbol < symbols[j].symbol {
-				return true
-			}
-			return symbols[i].alias < symbols[j].alias
-		})
-		for j, symbol := range symbols {
-			if symbol.alias != "" && symbol.alias != symbol.symbol {
-				loadStatements.WriteString(symbol.alias)
-				loadStatements.WriteString(" = ")
-			}
-			loadStatements.WriteString("\"")
-			loadStatements.WriteString(symbol.symbol)
-			loadStatements.WriteString("\"")
-			if j != len(symbols)-1 {
-				loadStatements.WriteString(", ")
-			}
-		}
-		loadStatements.WriteString(")")
-		if i != len(bzlToLoadedSymbols)-1 {
-			loadStatements.WriteString("\n")
-		}
-	}
-	return loadStatements.String()
-}
-
-type bpToBuildContext interface {
-	ModuleName(module blueprint.Module) string
-	ModuleDir(module blueprint.Module) string
-	ModuleSubDir(module blueprint.Module) string
-	ModuleType(module blueprint.Module) string
-
-	VisitAllModules(visit func(blueprint.Module))
-	VisitDirectDeps(module blueprint.Module, visit func(blueprint.Module))
-}
-
-type CodegenContext struct {
-	config             android.Config
-	context            *android.Context
-	mode               CodegenMode
-	additionalDeps     []string
-	unconvertedDepMode unconvertedDepsMode
-	topDir             string
-}
-
-func (ctx *CodegenContext) Mode() CodegenMode {
-	return ctx.mode
-}
-
-// CodegenMode is an enum to differentiate code-generation modes.
-type CodegenMode int
-
-const (
-	// QueryView - generate BUILD files with targets representing fully mutated
-	// Soong modules, representing the fully configured Soong module graph with
-	// variants and dependency edges.
-	//
-	// This mode is used for discovering and introspecting the existing Soong
-	// module graph.
-	QueryView CodegenMode = iota
-)
-
-type unconvertedDepsMode int
-
-const (
-	// Include a warning in conversion metrics about converted modules with unconverted direct deps
-	warnUnconvertedDeps unconvertedDepsMode = iota
-	// Error and fail conversion if encountering a module with unconverted direct deps
-	// Enabled by setting environment variable `BP2BUILD_ERROR_UNCONVERTED`
-	errorModulesUnconvertedDeps
-)
-
-func (mode CodegenMode) String() string {
-	switch mode {
-	case QueryView:
-		return "QueryView"
-	default:
-		return fmt.Sprintf("%d", mode)
-	}
-}
-
-// AddNinjaFileDeps adds dependencies on the specified files to be added to the ninja manifest. The
-// primary builder will be rerun whenever the specified files are modified. Allows us to fulfill the
-// PathContext interface in order to add dependencies on hand-crafted BUILD files. Note: must also
-// call AdditionalNinjaDeps and add them manually to the ninja file.
-func (ctx *CodegenContext) AddNinjaFileDeps(deps ...string) {
-	ctx.additionalDeps = append(ctx.additionalDeps, deps...)
-}
-
-// AdditionalNinjaDeps returns additional ninja deps added by CodegenContext
-func (ctx *CodegenContext) AdditionalNinjaDeps() []string {
-	return ctx.additionalDeps
-}
-
-func (ctx *CodegenContext) Config() android.Config    { return ctx.config }
-func (ctx *CodegenContext) Context() *android.Context { return ctx.context }
-
-// NewCodegenContext creates a wrapper context that conforms to PathContext for
-// writing BUILD files in the output directory.
-func NewCodegenContext(config android.Config, context *android.Context, mode CodegenMode, topDir string) *CodegenContext {
-	var unconvertedDeps unconvertedDepsMode
-	return &CodegenContext{
-		context:            context,
-		config:             config,
-		mode:               mode,
-		unconvertedDepMode: unconvertedDeps,
-		topDir:             topDir,
-	}
-}
-
-// props is an unsorted map. This function ensures that
-// the generated attributes are sorted to ensure determinism.
-func propsToAttributes(props map[string]string) string {
-	var attributes string
-	for _, propName := range android.SortedKeys(props) {
-		attributes += fmt.Sprintf("    %s = %s,\n", propName, props[propName])
-	}
-	return attributes
-}
-
-type conversionResults struct {
-	buildFileToTargets    map[string]BazelTargets
-	moduleNameToPartition map[string]string
-}
-
-func (r conversionResults) BuildDirToTargets() map[string]BazelTargets {
-	return r.buildFileToTargets
-}
-
-func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
-	ctx.Context().BeginEvent("GenerateBazelTargets")
-	defer ctx.Context().EndEvent("GenerateBazelTargets")
-	buildFileToTargets := make(map[string]BazelTargets)
-
-	dirs := make(map[string]bool)
-	moduleNameToPartition := make(map[string]string)
-
-	var errs []error
-
-	bpCtx := ctx.Context()
-	bpCtx.VisitAllModules(func(m blueprint.Module) {
-		dir := bpCtx.ModuleDir(m)
-		dirs[dir] = true
-
-		var targets []BazelTarget
-
-		switch ctx.Mode() {
-		case QueryView:
-			// Blocklist certain module types from being generated.
-			if canonicalizeModuleType(bpCtx.ModuleType(m)) == "package" {
-				// package module name contain slashes, and thus cannot
-				// be mapped cleanly to a bazel label.
-				return
-			}
-			t, err := generateSoongModuleTarget(bpCtx, m)
-			if err != nil {
-				errs = append(errs, err)
-			}
-			targets = append(targets, t)
-		default:
-			errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
-			return
-		}
-
-		for _, target := range targets {
-			targetDir := target.PackageName()
-			buildFileToTargets[targetDir] = append(buildFileToTargets[targetDir], target)
-		}
-	})
-
-	if len(errs) > 0 {
-		return conversionResults{}, errs
-	}
-
-	if generateFilegroups {
-		// Add a filegroup target that exposes all sources in the subtree of this package
-		// NOTE: This also means we generate a BUILD file for every Android.bp file (as long as it has at least one module)
-		//
-		// This works because: https://bazel.build/reference/be/functions#exports_files
-		// "As a legacy behaviour, also files mentioned as input to a rule are exported with the
-		// default visibility until the flag --incompatible_no_implicit_file_export is flipped. However, this behavior
-		// should not be relied upon and actively migrated away from."
-		//
-		// TODO(b/198619163): We should change this to export_files(glob(["**/*"])) instead, but doing that causes these errors:
-		// "Error in exports_files: generated label '//external/avb:avbtool' conflicts with existing py_binary rule"
-		// So we need to solve all the "target ... is both a rule and a file" warnings first.
-		for dir := range dirs {
-			buildFileToTargets[dir] = append(buildFileToTargets[dir], BazelTarget{
-				name:      "bp2build_all_srcs",
-				content:   `filegroup(name = "bp2build_all_srcs", srcs = glob(["**/*"]), tags = ["manual"])`,
-				ruleClass: "filegroup",
-			})
-		}
-	}
-
-	return conversionResults{
-		buildFileToTargets:    buildFileToTargets,
-		moduleNameToPartition: moduleNameToPartition,
-	}, errs
-}
-
-// Convert a module and its deps and props into a Bazel macro/rule
-// representation in the BUILD file.
-func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) (BazelTarget, error) {
-	props, err := getBuildProperties(ctx, m)
-
-	// TODO(b/163018919): DirectDeps can have duplicate (module, variant)
-	// items, if the modules are added using different DependencyTag. Figure
-	// out the implications of that.
-	depLabels := map[string]bool{}
-	if aModule, ok := m.(android.Module); ok {
-		ctx.VisitDirectDeps(aModule, func(depModule blueprint.Module) {
-			depLabels[qualifiedTargetLabel(ctx, depModule)] = true
-		})
-	}
-
-	for p := range ignoredPropNames {
-		delete(props.Attrs, p)
-	}
-	attributes := propsToAttributes(props.Attrs)
-
-	depLabelList := "[\n"
-	for depLabel := range depLabels {
-		depLabelList += fmt.Sprintf("        %q,\n", depLabel)
-	}
-	depLabelList += "    ]"
-
-	targetName := targetNameWithVariant(ctx, m)
-	return BazelTarget{
-		name:        targetName,
-		packageName: ctx.ModuleDir(m),
-		content: fmt.Sprintf(
-			soongModuleTargetTemplate,
-			targetName,
-			ctx.ModuleName(m),
-			canonicalizeModuleType(ctx.ModuleType(m)),
-			ctx.ModuleSubDir(m),
-			depLabelList,
-			attributes),
-	}, err
-}
-
-func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) (BazelAttributes, error) {
-	// TODO: this omits properties for blueprint modules (blueprint_go_binary,
-	// bootstrap_go_binary, bootstrap_go_package), which will have to be handled separately.
-	if aModule, ok := m.(android.Module); ok {
-		return extractModuleProperties(aModule.GetProperties(), false)
-	}
-
-	return BazelAttributes{}, nil
-}
-
-// Generically extract module properties and types into a map, keyed by the module property name.
-func extractModuleProperties(props []interface{}, checkForDuplicateProperties bool) (BazelAttributes, error) {
-	ret := map[string]string{}
-
-	// Iterate over this android.Module's property structs.
-	for _, properties := range props {
-		propertiesValue := reflect.ValueOf(properties)
-		// Check that propertiesValue is a pointer to the Properties struct, like
-		// *cc.BaseLinkerProperties or *java.CompilerProperties.
-		//
-		// propertiesValue can also be type-asserted to the structs to
-		// manipulate internal props, if needed.
-		if isStructPtr(propertiesValue.Type()) {
-			structValue := propertiesValue.Elem()
-			ok, err := extractStructProperties(structValue, 0)
-			if err != nil {
-				return BazelAttributes{}, err
-			}
-			for k, v := range ok {
-				if existing, exists := ret[k]; checkForDuplicateProperties && exists {
-					return BazelAttributes{}, fmt.Errorf(
-						"%s (%v) is present in properties whereas it should be consolidated into a commonAttributes",
-						k, existing)
-				}
-				ret[k] = v
-			}
-		} else {
-			return BazelAttributes{},
-				fmt.Errorf(
-					"properties must be a pointer to a struct, got %T",
-					propertiesValue.Interface())
-		}
-	}
-
-	return BazelAttributes{
-		Attrs: ret,
-	}, nil
-}
-
-func isStructPtr(t reflect.Type) bool {
-	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
-}
-
-// prettyPrint a property value into the equivalent Starlark representation
-// recursively.
-func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) (string, error) {
-	if !emitZeroValues && isZero(propertyValue) {
-		// A property value being set or unset actually matters -- Soong does set default
-		// values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
-		// https://cs.android.com/android/platform/superproject/+/main:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
-		//
-		// In Bazel-parlance, we would use "attr.<type>(default = <default
-		// value>)" to set the default value of unset attributes. In the cases
-		// where the bp2build converter didn't set the default value within the
-		// mutator when creating the BazelTargetModule, this would be a zero
-		// value. For those cases, we return an empty string so we don't
-		// unnecessarily generate empty values.
-		return "", nil
-	}
-
-	switch propertyValue.Kind() {
-	case reflect.String:
-		return fmt.Sprintf("\"%v\"", escapeString(propertyValue.String())), nil
-	case reflect.Bool:
-		return starlark_fmt.PrintBool(propertyValue.Bool()), nil
-	case reflect.Int, reflect.Uint, reflect.Int64:
-		return fmt.Sprintf("%v", propertyValue.Interface()), nil
-	case reflect.Ptr:
-		return prettyPrint(propertyValue.Elem(), indent, emitZeroValues)
-	case reflect.Slice:
-		elements := make([]string, 0, propertyValue.Len())
-		for i := 0; i < propertyValue.Len(); i++ {
-			val, err := prettyPrint(propertyValue.Index(i), indent, emitZeroValues)
-			if err != nil {
-				return "", err
-			}
-			if val != "" {
-				elements = append(elements, val)
-			}
-		}
-		return starlark_fmt.PrintList(elements, indent, func(s string) string {
-			return "%s"
-		}), nil
-
-	case reflect.Struct:
-		// Special cases where the bp2build sends additional information to the codegenerator
-		// by wrapping the attributes in a custom struct type.
-		if attr, ok := propertyValue.Interface().(bazel.Attribute); ok {
-			return prettyPrintAttribute(attr, indent)
-		} else if label, ok := propertyValue.Interface().(bazel.Label); ok {
-			return fmt.Sprintf("%q", label.Label), nil
-		}
-
-		// Sort and print the struct props by the key.
-		structProps, err := extractStructProperties(propertyValue, indent)
-
-		if err != nil {
-			return "", err
-		}
-
-		if len(structProps) == 0 {
-			return "", nil
-		}
-		return starlark_fmt.PrintDict(structProps, indent), nil
-	case reflect.Interface:
-		// TODO(b/164227191): implement pretty print for interfaces.
-		// Interfaces are used for for arch, multilib and target properties.
-		return "", nil
-	case reflect.Map:
-		if v, ok := propertyValue.Interface().(bazel.StringMapAttribute); ok {
-			return starlark_fmt.PrintStringStringDict(v, indent), nil
-		}
-		return "", fmt.Errorf("bp2build expects map of type map[string]string for field: %s", propertyValue)
-	default:
-		return "", fmt.Errorf(
-			"unexpected kind for property struct field: %s", propertyValue.Kind())
-	}
-}
-
-// Converts a reflected property struct value into a map of property names and property values,
-// which each property value correctly pretty-printed and indented at the right nest level,
-// since property structs can be nested. In Starlark, nested structs are represented as nested
-// dicts: https://docs.bazel.build/skylark/lib/dict.html
-func extractStructProperties(structValue reflect.Value, indent int) (map[string]string, error) {
-	if structValue.Kind() != reflect.Struct {
-		return map[string]string{}, fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind())
-	}
-
-	var err error
-
-	ret := map[string]string{}
-	structType := structValue.Type()
-	for i := 0; i < structValue.NumField(); i++ {
-		field := structType.Field(i)
-		if shouldSkipStructField(field) {
-			continue
-		}
-
-		fieldValue := structValue.Field(i)
-		if isZero(fieldValue) {
-			// Ignore zero-valued fields
-			continue
-		}
-
-		// if the struct is embedded (anonymous), flatten the properties into the containing struct
-		if field.Anonymous {
-			if field.Type.Kind() == reflect.Ptr {
-				fieldValue = fieldValue.Elem()
-			}
-			if fieldValue.Type().Kind() == reflect.Struct {
-				propsToMerge, err := extractStructProperties(fieldValue, indent)
-				if err != nil {
-					return map[string]string{}, err
-				}
-				for prop, value := range propsToMerge {
-					ret[prop] = value
-				}
-				continue
-			}
-		}
-
-		propertyName := proptools.PropertyNameForField(field.Name)
-		var prettyPrintedValue string
-		prettyPrintedValue, err = prettyPrint(fieldValue, indent+1, false)
-		if err != nil {
-			return map[string]string{}, fmt.Errorf(
-				"Error while parsing property: %q. %s",
-				propertyName,
-				err)
-		}
-		if prettyPrintedValue != "" {
-			ret[propertyName] = prettyPrintedValue
-		}
-	}
-
-	return ret, nil
-}
-
-func isZero(value reflect.Value) bool {
-	switch value.Kind() {
-	case reflect.Func, reflect.Map, reflect.Slice:
-		return value.IsNil()
-	case reflect.Array:
-		valueIsZero := true
-		for i := 0; i < value.Len(); i++ {
-			valueIsZero = valueIsZero && isZero(value.Index(i))
-		}
-		return valueIsZero
-	case reflect.Struct:
-		valueIsZero := true
-		for i := 0; i < value.NumField(); i++ {
-			valueIsZero = valueIsZero && isZero(value.Field(i))
-		}
-		return valueIsZero
-	case reflect.Ptr:
-		if !value.IsNil() {
-			return isZero(reflect.Indirect(value))
-		} else {
-			return true
-		}
-	// Always print bool/strings, if you want a bool/string attribute to be able to take the default value, use a
-	// pointer instead
-	case reflect.Bool, reflect.String:
-		return false
-	default:
-		if !value.IsValid() {
-			return true
-		}
-		zeroValue := reflect.Zero(value.Type())
-		result := value.Interface() == zeroValue.Interface()
-		return result
-	}
-}
-
-func escapeString(s string) string {
-	s = strings.ReplaceAll(s, "\\", "\\\\")
-
-	// b/184026959: Reverse the application of some common control sequences.
-	// These must be generated literally in the BUILD file.
-	s = strings.ReplaceAll(s, "\t", "\\t")
-	s = strings.ReplaceAll(s, "\n", "\\n")
-	s = strings.ReplaceAll(s, "\r", "\\r")
-
-	return strings.ReplaceAll(s, "\"", "\\\"")
-}
-
-func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
-	name := ""
-	if c.ModuleSubDir(logicModule) != "" {
-		// TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
-		name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
-	} else {
-		name = c.ModuleName(logicModule)
-	}
-
-	return strings.Replace(name, "//", "", 1)
-}
-
-func qualifiedTargetLabel(c bpToBuildContext, logicModule blueprint.Module) string {
-	return fmt.Sprintf("//%s:%s", c.ModuleDir(logicModule), targetNameWithVariant(c, logicModule))
-}
diff --git a/bp2build/bzl_conversion.go b/bp2build/bzl_conversion.go
deleted file mode 100644
index e774fdf..0000000
--- a/bp2build/bzl_conversion.go
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2020 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 bp2build
-
-import (
-	"android/soong/android"
-	"fmt"
-	"reflect"
-	"runtime"
-	"sort"
-	"strings"
-
-	"github.com/google/blueprint/proptools"
-)
-
-var (
-	// An allowlist of prop types that are surfaced from module props to rule
-	// attributes. (nested) dictionaries are notably absent here, because while
-	// Soong supports multi value typed and nested dictionaries, Bazel's rule
-	// attr() API supports only single-level string_dicts.
-	allowedPropTypes = map[string]bool{
-		"int":         true, // e.g. 42
-		"bool":        true, // e.g. True
-		"string_list": true, // e.g. ["a", "b"]
-		"string":      true, // e.g. "a"
-	}
-)
-
-type rule struct {
-	name  string
-	attrs string
-}
-
-type RuleShim struct {
-	// The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
-	rules []string
-
-	// The generated string content of the bzl file.
-	content string
-}
-
-// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
-// user-specified Go plugins.
-//
-// This function reuses documentation generation APIs to ensure parity between modules-as-docs
-// and modules-as-code, including the names and types of morule properties.
-func CreateRuleShims(moduleTypeFactories map[string]android.ModuleFactory) map[string]RuleShim {
-	ruleShims := map[string]RuleShim{}
-	for pkg, rules := range generateRules(moduleTypeFactories) {
-		shim := RuleShim{
-			rules: make([]string, 0, len(rules)),
-		}
-		shim.content = "load(\"//build/bazel/queryview_rules:providers.bzl\", \"SoongModuleInfo\")\n"
-
-		bzlFileName := strings.ReplaceAll(pkg, "android/soong/", "")
-		bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
-		bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
-
-		for _, r := range rules {
-			shim.content += fmt.Sprintf(moduleRuleShim, r.name, r.attrs)
-			shim.rules = append(shim.rules, r.name)
-		}
-		sort.Strings(shim.rules)
-		ruleShims[bzlFileName] = shim
-	}
-	return ruleShims
-}
-
-// Generate the content of soong_module.bzl with the rule shim load statements
-// and mapping of module_type to rule shim map for every module type in Soong.
-func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
-	var loadStmts string
-	var moduleRuleMap string
-	for _, bzlFileName := range android.SortedKeys(bzlLoads) {
-		loadStmt := "load(\"//build/bazel/queryview_rules:"
-		loadStmt += bzlFileName
-		loadStmt += ".bzl\""
-		ruleShim := bzlLoads[bzlFileName]
-		for _, rule := range ruleShim.rules {
-			loadStmt += fmt.Sprintf(", %q", rule)
-			moduleRuleMap += "    \"" + rule + "\": " + rule + ",\n"
-		}
-		loadStmt += ")\n"
-		loadStmts += loadStmt
-	}
-
-	return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
-}
-
-func generateRules(moduleTypeFactories map[string]android.ModuleFactory) map[string][]rule {
-	// TODO: add shims for bootstrap/blueprint go modules types
-
-	rules := make(map[string][]rule)
-	// TODO: allow registration of a bzl rule when registring a factory
-	for _, moduleType := range android.SortedKeys(moduleTypeFactories) {
-		factory := moduleTypeFactories[moduleType]
-		factoryName := runtime.FuncForPC(reflect.ValueOf(factory).Pointer()).Name()
-		pkg := strings.Split(factoryName, ".")[0]
-		attrs := `{
-        "soong_module_name": attr.string(mandatory = True),
-        "soong_module_variant": attr.string(),
-        "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
-`
-		attrs += getAttributes(factory)
-		attrs += "    },"
-
-		r := rule{
-			name:  canonicalizeModuleType(moduleType),
-			attrs: attrs,
-		}
-
-		rules[pkg] = append(rules[pkg], r)
-	}
-	return rules
-}
-
-type property struct {
-	name             string
-	starlarkAttrType string
-	properties       []property
-}
-
-const (
-	attributeIndent = "        "
-)
-
-func (p *property) attributeString() string {
-	if !shouldGenerateAttribute(p.name) {
-		return ""
-	}
-
-	if _, ok := allowedPropTypes[p.starlarkAttrType]; !ok {
-		// a struct -- let's just comment out sub-props
-		s := fmt.Sprintf(attributeIndent+"# %s start\n", p.name)
-		for _, nestedP := range p.properties {
-			s += "# " + nestedP.attributeString()
-		}
-		s += fmt.Sprintf(attributeIndent+"# %s end\n", p.name)
-		return s
-	}
-	return fmt.Sprintf(attributeIndent+"%q: attr.%s(),\n", p.name, p.starlarkAttrType)
-}
-
-func extractPropertyDescriptionsFromStruct(structType reflect.Type) []property {
-	properties := make([]property, 0)
-	for i := 0; i < structType.NumField(); i++ {
-		field := structType.Field(i)
-		if shouldSkipStructField(field) {
-			continue
-		}
-		subProps := extractPropertyDescriptions(field.Name, field.Type)
-		// if the struct is embedded (anonymous), flatten the properties into the containing struct
-		if field.Anonymous {
-			for _, prop := range subProps {
-				properties = append(properties, prop.properties...)
-			}
-		} else {
-			properties = append(properties, subProps...)
-		}
-	}
-	return properties
-}
-
-func extractPropertyDescriptions(name string, t reflect.Type) []property {
-	name = proptools.PropertyNameForField(name)
-
-	// TODO: handle android:paths tags, they should be changed to label types
-
-	starlarkAttrType := fmt.Sprintf("%s", t.Name())
-	props := make([]property, 0)
-
-	switch t.Kind() {
-	case reflect.Bool, reflect.String:
-		// do nothing
-	case reflect.Uint, reflect.Int, reflect.Int64:
-		starlarkAttrType = "int"
-	case reflect.Slice:
-		if t.Elem().Kind() != reflect.String {
-			// TODO: handle lists of non-strings (currently only list of Dist)
-			return []property{}
-		}
-		starlarkAttrType = "string_list"
-	case reflect.Struct:
-		props = extractPropertyDescriptionsFromStruct(t)
-	case reflect.Ptr:
-		return extractPropertyDescriptions(name, t.Elem())
-	case reflect.Interface:
-		// Interfaces are used for for arch, multilib and target properties, which are handled at runtime.
-		// These will need to be handled in a bazel-specific version of the arch mutator.
-		return []property{}
-	}
-
-	prop := property{
-		name:             name,
-		starlarkAttrType: starlarkAttrType,
-		properties:       props,
-	}
-
-	return []property{prop}
-}
-
-func getPropertyDescriptions(props []interface{}) []property {
-	// there may be duplicate properties, e.g. from defaults libraries
-	propertiesByName := make(map[string]property)
-	for _, p := range props {
-		for _, prop := range extractPropertyDescriptionsFromStruct(reflect.ValueOf(p).Elem().Type()) {
-			propertiesByName[prop.name] = prop
-		}
-	}
-
-	properties := make([]property, 0, len(propertiesByName))
-	for _, key := range android.SortedKeys(propertiesByName) {
-		properties = append(properties, propertiesByName[key])
-	}
-
-	return properties
-}
-
-func getAttributes(factory android.ModuleFactory) string {
-	attrs := ""
-	for _, p := range getPropertyDescriptions(factory().GetProperties()) {
-		attrs += p.attributeString()
-	}
-	return attrs
-}
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
deleted file mode 100644
index 3d9f0a2..0000000
--- a/bp2build/configurability.go
+++ /dev/null
@@ -1,328 +0,0 @@
-package bp2build
-
-import (
-	"fmt"
-	"reflect"
-
-	"android/soong/android"
-	"android/soong/bazel"
-	"android/soong/starlark_fmt"
-)
-
-// Configurability support for bp2build.
-
-type selects map[string]reflect.Value
-
-func getStringValue(str bazel.StringAttribute) (reflect.Value, []selects) {
-	value := reflect.ValueOf(str.Value)
-
-	if !str.HasConfigurableValues() {
-		return value, []selects{}
-	}
-
-	ret := selects{}
-	for _, axis := range str.SortedConfigurationAxes() {
-		configToStrs := str.ConfigurableValues[axis]
-		for config, strs := range configToStrs {
-			selectKey := axis.SelectKey(config)
-			ret[selectKey] = reflect.ValueOf(strs)
-		}
-	}
-
-	// if there is a select, use the base value as the conditions default value
-	if len(ret) > 0 {
-		if _, ok := ret[bazel.ConditionsDefaultSelectKey]; !ok {
-			ret[bazel.ConditionsDefaultSelectKey] = value
-			value = reflect.Zero(value.Type())
-		}
-	}
-
-	return value, []selects{ret}
-}
-
-func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects, bool) {
-	value := reflect.ValueOf(list.Value)
-	prepend := list.Prepend
-	if !list.HasConfigurableValues() {
-		return value, []selects{}, prepend
-	}
-
-	var ret []selects
-	for _, axis := range list.SortedConfigurationAxes() {
-		configToLists := list.ConfigurableValues[axis]
-		archSelects := map[string]reflect.Value{}
-		for config, labels := range configToLists {
-			selectKey := axis.SelectKey(config)
-			archSelects[selectKey] = reflect.ValueOf(labels)
-		}
-		if len(archSelects) > 0 {
-			ret = append(ret, archSelects)
-		}
-	}
-
-	return value, ret, prepend
-}
-
-func getLabelValue(label bazel.LabelAttribute) (reflect.Value, []selects) {
-	value := reflect.ValueOf(label.Value)
-	if !label.HasConfigurableValues() {
-		return value, []selects{}
-	}
-
-	ret := selects{}
-	for _, axis := range label.SortedConfigurationAxes() {
-		configToLabels := label.ConfigurableValues[axis]
-		for config, labels := range configToLabels {
-			selectKey := axis.SelectKey(config)
-			ret[selectKey] = reflect.ValueOf(labels)
-		}
-	}
-
-	// if there is a select, use the base value as the conditions default value
-	if len(ret) > 0 {
-		ret[bazel.ConditionsDefaultSelectKey] = value
-		value = reflect.Zero(value.Type())
-	}
-
-	return value, []selects{ret}
-}
-
-func getBoolValue(boolAttr bazel.BoolAttribute) (reflect.Value, []selects) {
-	value := reflect.ValueOf(boolAttr.Value)
-	if !boolAttr.HasConfigurableValues() {
-		return value, []selects{}
-	}
-
-	ret := selects{}
-	for _, axis := range boolAttr.SortedConfigurationAxes() {
-		configToBools := boolAttr.ConfigurableValues[axis]
-		for config, bools := range configToBools {
-			selectKey := axis.SelectKey(config)
-			ret[selectKey] = reflect.ValueOf(bools)
-		}
-	}
-	// if there is a select, use the base value as the conditions default value
-	if len(ret) > 0 {
-		ret[bazel.ConditionsDefaultSelectKey] = value
-		value = reflect.Zero(value.Type())
-	}
-
-	return value, []selects{ret}
-}
-func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects, bool) {
-	value := reflect.ValueOf(list.Value.Includes)
-	prepend := list.Prepend
-	var ret []selects
-	for _, axis := range list.SortedConfigurationAxes() {
-		configToLabels := list.ConfigurableValues[axis]
-		if !configToLabels.HasConfigurableValues() {
-			continue
-		}
-		archSelects := map[string]reflect.Value{}
-		defaultVal := configToLabels[bazel.ConditionsDefaultConfigKey]
-		// Skip empty list values unless ether EmitEmptyList is true, or these values differ from the default.
-		emitEmptyList := list.EmitEmptyList || len(defaultVal.Includes) > 0
-		for config, labels := range configToLabels {
-			// Omit any entries in the map which match the default value, for brevity.
-			if config != bazel.ConditionsDefaultConfigKey && labels.Equals(defaultVal) {
-				continue
-			}
-			selectKey := axis.SelectKey(config)
-			if use, value := labelListSelectValue(selectKey, labels, emitEmptyList); use {
-				archSelects[selectKey] = value
-			}
-		}
-		if len(archSelects) > 0 {
-			ret = append(ret, archSelects)
-		}
-	}
-
-	return value, ret, prepend
-}
-
-func labelListSelectValue(selectKey string, list bazel.LabelList, emitEmptyList bool) (bool, reflect.Value) {
-	if selectKey == bazel.ConditionsDefaultSelectKey || emitEmptyList || len(list.Includes) > 0 {
-		return true, reflect.ValueOf(list.Includes)
-	} else if len(list.Excludes) > 0 {
-		// if there is still an excludes -- we need to have an empty list for this select & use the
-		// value in conditions default Includes
-		return true, reflect.ValueOf([]string{})
-	}
-	return false, reflect.Zero(reflect.TypeOf([]string{}))
-}
-
-var (
-	emptyBazelList = "[]"
-	bazelNone      = "None"
-)
-
-// prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain
-// select statements.
-func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
-	var value reflect.Value
-	// configurableAttrs is the list of individual select statements to be
-	// concatenated together. These select statements should be along different
-	// axes. For example, one element may be
-	// `select({"//color:red": "one", "//color:green": "two"})`, and the second
-	// element may be `select({"//animal:cat": "three", "//animal:dog": "four"}).
-	// These selects should be sorted by axis identifier.
-	var configurableAttrs []selects
-	var prepend bool
-	var defaultSelectValue *string
-	var emitZeroValues bool
-	// If true, print the default attribute value, even if the attribute is zero.
-	shouldPrintDefault := false
-	switch list := v.(type) {
-	case bazel.StringAttribute:
-		if err := list.Collapse(); err != nil {
-			return "", err
-		}
-		value, configurableAttrs = getStringValue(list)
-		defaultSelectValue = &bazelNone
-	case bazel.StringListAttribute:
-		value, configurableAttrs, prepend = getStringListValues(list)
-		defaultSelectValue = &emptyBazelList
-	case bazel.LabelListAttribute:
-		value, configurableAttrs, prepend = getLabelListValues(list)
-		emitZeroValues = list.EmitEmptyList
-		defaultSelectValue = &emptyBazelList
-		if list.ForceSpecifyEmptyList && (!value.IsNil() || list.HasConfigurableValues()) {
-			shouldPrintDefault = true
-		}
-	case bazel.LabelAttribute:
-		if err := list.Collapse(); err != nil {
-			return "", err
-		}
-		value, configurableAttrs = getLabelValue(list)
-		defaultSelectValue = &bazelNone
-	case bazel.BoolAttribute:
-		if err := list.Collapse(); err != nil {
-			return "", err
-		}
-		value, configurableAttrs = getBoolValue(list)
-		defaultSelectValue = &bazelNone
-	default:
-		return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
-	}
-
-	var err error
-	ret := ""
-	if value.Kind() != reflect.Invalid {
-		s, err := prettyPrint(value, indent, false) // never emit zero values for the base value
-		if err != nil {
-			return ret, err
-		}
-
-		ret += s
-	}
-	// Convenience function to prepend/append selects components to an attribute value.
-	concatenateSelects := func(selectsData selects, defaultValue *string, s string, prepend bool) (string, error) {
-		selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent, emitZeroValues)
-		if err != nil {
-			return "", err
-		}
-		var left, right string
-		if prepend {
-			left, right = selectMap, s
-		} else {
-			left, right = s, selectMap
-		}
-		if left != "" && right != "" {
-			left += " + "
-		}
-		left += right
-
-		return left, nil
-	}
-
-	for _, configurableAttr := range configurableAttrs {
-		ret, err = concatenateSelects(configurableAttr, defaultSelectValue, ret, prepend)
-		if err != nil {
-			return "", err
-		}
-	}
-
-	if ret == "" && shouldPrintDefault {
-		return *defaultSelectValue, nil
-	}
-	return ret, nil
-}
-
-// prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way
-// to construct a select map for any kind of attribute type.
-func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int, emitZeroValues bool) (string, error) {
-	if selectMap == nil {
-		return "", nil
-	}
-
-	var selects string
-	for _, selectKey := range android.SortedKeys(selectMap) {
-		if selectKey == bazel.ConditionsDefaultSelectKey {
-			// Handle default condition later.
-			continue
-		}
-		value := selectMap[selectKey]
-		if isZero(value) && !emitZeroValues && isZero(selectMap[bazel.ConditionsDefaultSelectKey]) {
-			// Ignore zero values to not generate empty lists. However, always note zero values if
-			// the default value is non-zero.
-			continue
-		}
-		s, err := prettyPrintSelectEntry(value, selectKey, indent, true)
-		if err != nil {
-			return "", err
-		}
-		// s could still be an empty string, e.g. unset slices of structs with
-		// length of 0.
-		if s != "" {
-			selects += s + ",\n"
-		}
-	}
-
-	if len(selects) == 0 {
-		// If there is a default value, and there are no selects for this axis, print that without any selects.
-		if val, exists := selectMap[bazel.ConditionsDefaultSelectKey]; exists {
-			return prettyPrint(val, indent, emitZeroValues)
-		}
-		// No conditions (or all values are empty lists), so no need for a map.
-		return "", nil
-	}
-
-	// Create the map.
-	ret := "select({\n"
-	ret += selects
-
-	// Handle the default condition
-	s, err := prettyPrintSelectEntry(selectMap[bazel.ConditionsDefaultSelectKey], bazel.ConditionsDefaultSelectKey, indent, emitZeroValues)
-	if err != nil {
-		return "", err
-	}
-	if s != "" {
-		// Print the custom default value.
-		ret += s
-		ret += ",\n"
-	} else if defaultValue != nil {
-		// Print an explicit empty list (the default value) even if the value is
-		// empty, to avoid errors about not finding a configuration that matches.
-		ret += fmt.Sprintf("%s\"%s\": %s,\n", starlark_fmt.Indention(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
-	}
-
-	ret += starlark_fmt.Indention(indent)
-	ret += "})"
-
-	return ret, nil
-}
-
-// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
-// with a provided key.
-func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
-	s := starlark_fmt.Indention(indent + 1)
-	v, err := prettyPrint(value, indent+1, emitZeroValues)
-	if err != nil {
-		return "", err
-	}
-	if v == "" {
-		return "", nil
-	}
-	s += fmt.Sprintf("\"%s\": %s", key, v)
-	return s, nil
-}
diff --git a/bp2build/constants.go b/bp2build/constants.go
deleted file mode 100644
index 4870dff..0000000
--- a/bp2build/constants.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2020 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 bp2build
-
-var (
-	// When both a BUILD and BUILD.bazel file are exist in the same package, the BUILD.bazel file will
-	// be preferred for use within a Bazel build.
-
-	// The file name used for automatically generated files.
-	GeneratedBuildFileName = "BUILD.bazel"
-
-	// The file name used for hand-crafted build targets.
-	// NOTE: It is okay that this matches GeneratedBuildFileName, since we generate BUILD files in a different directory to source files
-	// FIXME: Because there are hundreds of existing BUILD.bazel files in the AOSP tree, we should pick another name here, like BUILD.android
-	HandcraftedBuildFileName = "BUILD.bazel"
-)
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
deleted file mode 100644
index 9f1aa09..0000000
--- a/bp2build/conversion.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package bp2build
-
-import (
-	"reflect"
-	"strings"
-
-	"android/soong/android"
-	"github.com/google/blueprint/proptools"
-)
-
-type BazelFile struct {
-	Dir      string
-	Basename string
-	Contents string
-}
-
-func CreateBazelFiles(ruleShims map[string]RuleShim, buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
-	var files []BazelFile
-
-	if mode == QueryView {
-		// Write top level WORKSPACE.
-		files = append(files, newFile("", "WORKSPACE", ""))
-
-		// Used to denote that the top level directory is a package.
-		files = append(files, newFile("", GeneratedBuildFileName, ""))
-
-		files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
-
-		// These files are only used for queryview.
-		files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
-
-		for bzlFileName, ruleShim := range ruleShims {
-			files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
-		}
-		files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
-	}
-
-	files = append(files, createBuildFiles(buildToTargets, mode)...)
-
-	return files
-}
-
-func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
-	files := make([]BazelFile, 0, len(buildToTargets))
-	for _, dir := range android.SortedKeys(buildToTargets) {
-		targets := buildToTargets[dir]
-		targets.sort()
-
-		var content string
-		if mode == QueryView {
-			content = soongModuleLoad
-		}
-		if content != "" {
-			// If there are load statements, add a couple of newlines.
-			content += "\n\n"
-		}
-		content += targets.String()
-		files = append(files, newFile(dir, GeneratedBuildFileName, content))
-	}
-	return files
-}
-
-func newFile(dir, basename, content string) BazelFile {
-	return BazelFile{
-		Dir:      dir,
-		Basename: basename,
-		Contents: content,
-	}
-}
-
-const (
-	bazelRulesSubDir = "build/bazel/queryview_rules"
-)
-
-var (
-	// Certain module property names are blocklisted/ignored here, for the reasons commented.
-	ignoredPropNames = map[string]bool{
-		"name":               true, // redundant, since this is explicitly generated for every target
-		"from":               true, // reserved keyword
-		"in":                 true, // reserved keyword
-		"size":               true, // reserved for tests
-		"arch":               true, // interface prop type is not supported yet.
-		"multilib":           true, // interface prop type is not supported yet.
-		"target":             true, // interface prop type is not supported yet.
-		"visibility":         true, // Bazel has native visibility semantics. Handle later.
-		"features":           true, // There is already a built-in attribute 'features' which cannot be overridden.
-		"for":                true, // reserved keyword, b/233579439
-		"versions_with_info": true, // TODO(b/245730552) struct properties not fully supported
-	}
-)
-
-func shouldGenerateAttribute(prop string) bool {
-	return !ignoredPropNames[prop]
-}
-
-func shouldSkipStructField(field reflect.StructField) bool {
-	if field.PkgPath != "" && !field.Anonymous {
-		// Skip unexported fields. Some properties are
-		// internal to Soong only, and these fields do not have PkgPath.
-		return true
-	}
-	// fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc.
-	// but cannot be set in a .bp file
-	if proptools.HasTag(field, "blueprint", "mutated") {
-		return true
-	}
-	return false
-}
-
-// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
-// testonly = True, forcing other rules that depend on _test rules to also be
-// marked as testonly = True. This semantic constraint is not present in Soong.
-// To work around, rename "*_test" rules to "*_test_".
-func canonicalizeModuleType(moduleName string) string {
-	if strings.HasSuffix(moduleName, "_test") {
-		return moduleName + "_"
-	}
-
-	return moduleName
-}
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
deleted file mode 100644
index 2f806fa..0000000
--- a/bp2build/conversion_test.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2020 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 bp2build
-
-import (
-	"sort"
-	"testing"
-)
-
-type bazelFilepath struct {
-	dir      string
-	basename string
-}
-
-func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) {
-	files := CreateBazelFiles(map[string]RuleShim{}, map[string]BazelTargets{}, QueryView)
-	expectedFilePaths := []bazelFilepath{
-		{
-			dir:      "",
-			basename: "BUILD.bazel",
-		},
-		{
-			dir:      "",
-			basename: "WORKSPACE",
-		},
-		{
-			dir:      bazelRulesSubDir,
-			basename: "BUILD.bazel",
-		},
-		{
-			dir:      bazelRulesSubDir,
-			basename: "providers.bzl",
-		},
-		{
-			dir:      bazelRulesSubDir,
-			basename: "soong_module.bzl",
-		},
-	}
-
-	// Compare number of files
-	if a, e := len(files), len(expectedFilePaths); a != e {
-		t.Errorf("Expected %d files, got %d", e, a)
-	}
-
-	// Sort the files to be deterministic
-	sort.Slice(files, func(i, j int) bool {
-		if dir1, dir2 := files[i].Dir, files[j].Dir; dir1 == dir2 {
-			return files[i].Basename < files[j].Basename
-		} else {
-			return dir1 < dir2
-		}
-	})
-
-	// Compare the file contents
-	for i := range files {
-		actualFile, expectedFile := files[i], expectedFilePaths[i]
-
-		if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename {
-			t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename)
-		} else if actualFile.Basename == "BUILD.bazel" || actualFile.Basename == "WORKSPACE" {
-			if actualFile.Contents != "" {
-				t.Errorf("Expected %s to have no content.", actualFile)
-			}
-		} else if actualFile.Contents == "" {
-			t.Errorf("Contents of %s unexpected empty.", actualFile)
-		}
-	}
-}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 8679821..3b7073e 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -109,41 +109,41 @@
 
 var _ android.ImageInterface = (*bpf)(nil)
 
-func (bpf *bpf) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+func (bpf *bpf) ImageMutatorBegin(ctx android.ImageInterfaceContext) {}
 
-func (bpf *bpf) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+func (bpf *bpf) VendorVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return proptools.Bool(bpf.properties.Vendor)
 }
 
-func (bpf *bpf) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+func (bpf *bpf) ProductVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (bpf *bpf) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+func (bpf *bpf) CoreVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return !proptools.Bool(bpf.properties.Vendor)
 }
 
-func (bpf *bpf) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (bpf *bpf) RamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (bpf *bpf) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (bpf *bpf) VendorRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (bpf *bpf) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (bpf *bpf) DebugRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (bpf *bpf) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+func (bpf *bpf) RecoveryVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (bpf *bpf) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+func (bpf *bpf) ExtraImageVariations(ctx android.ImageInterfaceContext) []string {
 	return nil
 }
 
-func (bpf *bpf) SetImageVariation(ctx android.BaseModuleContext, variation string) {
+func (bpf *bpf) SetImageVariation(ctx android.ImageInterfaceContext, variation string) {
 	bpf.properties.VendorInternal = variation == "vendor"
 }
 
diff --git a/bpf/libbpf/libbpf_prog.go b/bpf/libbpf/libbpf_prog.go
index ac61510..3b26d46 100644
--- a/bpf/libbpf/libbpf_prog.go
+++ b/bpf/libbpf/libbpf_prog.go
@@ -104,41 +104,41 @@
 
 var _ android.ImageInterface = (*libbpfProg)(nil)
 
-func (libbpf *libbpfProg) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+func (libbpf *libbpfProg) ImageMutatorBegin(ctx android.ImageInterfaceContext) {}
 
-func (libbpf *libbpfProg) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+func (libbpf *libbpfProg) VendorVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (libbpf *libbpfProg) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+func (libbpf *libbpfProg) ProductVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (libbpf *libbpfProg) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+func (libbpf *libbpfProg) CoreVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return true
 }
 
-func (libbpf *libbpfProg) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (libbpf *libbpfProg) RamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (libbpf *libbpfProg) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (libbpf *libbpfProg) VendorRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (libbpf *libbpfProg) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (libbpf *libbpfProg) DebugRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (libbpf *libbpfProg) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+func (libbpf *libbpfProg) RecoveryVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (libbpf *libbpfProg) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+func (libbpf *libbpfProg) ExtraImageVariations(ctx android.ImageInterfaceContext) []string {
 	return nil
 }
 
-func (libbpf *libbpfProg) SetImageVariation(ctx android.BaseModuleContext, variation string) {
+func (libbpf *libbpfProg) SetImageVariation(ctx android.ImageInterfaceContext, variation string) {
 }
 
 func (libbpf *libbpfProg) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -158,7 +158,8 @@
 		"-Wall",
 		"-Werror",
 		"-Wextra",
-
+		// Flag to assist with the transition to libbpf
+		"-DENABLE_LIBBPF",
 		"-isystem bionic/libc/include",
 		"-isystem bionic/libc/kernel/uapi",
 		// The architecture doesn't matter here, but asm/types.h is included by linux/types.h.
@@ -205,7 +206,7 @@
 		if strings.ContainsRune(src.Base(), '_') {
 			ctx.ModuleErrorf("invalid character '_' in source name")
 		}
-		obj := android.ObjPathWithExt(ctx, "unstripped", src, "o")
+		obj := android.ObjPathWithExt(ctx, "unstripped", src, "bpf")
 
 		ctx.Build(pctx, android.BuildParams{
 			Rule:      libbpfProgCcRule,
@@ -218,7 +219,7 @@
 			},
 		})
 
-		objStripped := android.ObjPathWithExt(ctx, "", src, "o")
+		objStripped := android.ObjPathWithExt(ctx, "", src, "bpf")
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   libbpfProgStripRule,
 			Input:  obj,
@@ -230,7 +231,7 @@
 		libbpf.objs = append(libbpf.objs, objStripped.WithoutRel())
 	}
 
-	installDir := android.PathForModuleInstall(ctx, "etc", "bpf/libbpf")
+	installDir := android.PathForModuleInstall(ctx, "etc", "bpf")
 	if len(libbpf.properties.Relative_install_path) > 0 {
 		installDir = installDir.Join(ctx, libbpf.properties.Relative_install_path)
 	}
@@ -251,7 +252,7 @@
 			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 			fmt.Fprintln(w)
 			var localModulePath string
-			localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf/libbpf"
+			localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf"
 			if len(libbpf.properties.Relative_install_path) > 0 {
 				localModulePath += "/" + libbpf.properties.Relative_install_path
 			}
diff --git a/bpf/libbpf/libbpf_prog_test.go b/bpf/libbpf/libbpf_prog_test.go
index f4f5167..2b3b378 100644
--- a/bpf/libbpf/libbpf_prog_test.go
+++ b/bpf/libbpf/libbpf_prog_test.go
@@ -41,14 +41,15 @@
 func TestLibbpfProgDataDependency(t *testing.T) {
 	bp := `
 		libbpf_prog {
-			name: "bpf.o",
+			name: "bpf.bpf",
 			srcs: ["bpf.c"],
 		}
 
 		cc_test {
 			name: "vts_test_binary_bpf_module",
+			compile_multilib: "first",
 			srcs: ["BpfTest.cpp"],
-			data: [":bpf.o"],
+			data: [":bpf.bpf"],
 			gtest: false,
 		}
 	`
@@ -59,7 +60,7 @@
 func TestLibbpfProgSourceName(t *testing.T) {
 	bp := `
 		libbpf_prog {
-			name: "bpf_invalid_name.o",
+			name: "bpf_invalid_name.bpf",
 			srcs: ["bpf_invalid_name.c"],
 		}
 	`
diff --git a/build_test.bash b/build_test.bash
deleted file mode 100755
index defdd82..0000000
--- a/build_test.bash
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/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.
-
-#
-# This file is used in our continous build infrastructure to run a variety of
-# tests related to the build system.
-#
-# Currently, it's used to build and run multiproduct_kati, so it'll attempt
-# to build ninja files for every product in the tree. I expect this to
-# evolve as we find interesting things to test or track performance for.
-#
-
-# Products that are broken or otherwise don't work with multiproduct_kati
-SKIPPED_PRODUCTS=(
-    # These products are for soong-only builds, and will fail the kati stage.
-    linux_bionic
-    mainline_sdk
-    ndk
-
-    # New architecture bringup, fails without ALLOW_MISSING_DEPENDENCIES=true
-    aosp_riscv64
-)
-
-# To track how long we took to startup.
-case $(uname -s) in
-  Darwin)
-    export TRACE_BEGIN_SOONG=`$T/prebuilts/build-tools/path/darwin-x86/date +%s%3N`
-    ;;
-  *)
-    export TRACE_BEGIN_SOONG=$(date +%s%N)
-    ;;
-esac
-
-# Remove BUILD_NUMBER so that incremental builds on build servers don't
-# re-read makefiles every time.
-unset BUILD_NUMBER
-
-export TOP=$(cd $(dirname ${BASH_SOURCE[0]})/../..; PWD= /bin/pwd)
-cd "${TOP}"
-source "${TOP}/build/soong/scripts/microfactory.bash"
-
-case $(uname) in
-  Linux)
-    if [[ -f /lib/x86_64-linux-gnu/libSegFault.so ]]; then
-      export LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so
-      export SEGFAULT_USE_ALTSTACK=1
-    fi
-    ulimit -a
-    ;;
-esac
-
-echo
-echo "Free disk space:"
-# Ignore df errors because it errors out on gvfsd file systems
-# but still displays most of the useful info we need
-df -h || true
-
-echo
-echo "Running Bazel smoke test..."
-STANDALONE_BAZEL=true "${TOP}/build/bazel/bin/bazel" --batch --max_idle_secs=1 help
-
-echo
-echo "Running Soong test..."
-soong_build_go multiproduct_kati android/soong/cmd/multiproduct_kati
-exec "$(getoutdir)/multiproduct_kati" --skip-products "$(echo "${SKIPPED_PRODUCTS[@]-}" | tr ' ' ',')" "$@"
diff --git a/cc/Android.bp b/cc/Android.bp
index 3688c8a..3b29ae8 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -7,6 +7,7 @@
     pkgPath: "android/soong/cc",
     deps: [
         "blueprint",
+        "blueprint-depset",
         "blueprint-pathtools",
         "soong",
         "soong-aconfig",
@@ -16,7 +17,6 @@
         "soong-etc",
         "soong-fuzz",
         "soong-genrule",
-        "soong-testing",
         "soong-tradefed",
     ],
     srcs: [
@@ -27,6 +27,7 @@
         "builder.go",
         "cc.go",
         "ccdeps.go",
+        "cc_preprocess_no_configuration.go",
         "check.go",
         "coverage.go",
         "gen.go",
@@ -88,6 +89,7 @@
     testSrcs: [
         "afdo_test.go",
         "binary_test.go",
+        "cc_preprocess_no_configuration_test.go",
         "cc_test.go",
         "cc_test_only_property_test.go",
         "cmake_snapshot_test.go",
@@ -102,6 +104,7 @@
         "orderfile_test.go",
         "prebuilt_test.go",
         "proto_test.go",
+        "sabi_test.go",
         "sanitize_test.go",
         "sdk_test.go",
         "test_data_test.go",
@@ -119,3 +122,31 @@
     // Used by plugins
     visibility: ["//visibility:public"],
 }
+
+phony {
+    name: "llndk_libs",
+    required: [
+        "libEGL",
+        "libGLESv1_CM",
+        "libGLESv2",
+        "libGLESv3",
+        "libRS",
+        "libandroid_net",
+        "libapexsupport",
+        "libbinder_ndk",
+        "libc",
+        "libcgrouprc",
+        "libclang_rt.asan",
+        "libdl",
+        "libft2",
+        "liblog",
+        "libm",
+        "libmediandk",
+        "libnativewindow",
+        "libselinux",
+        "libsync",
+        "libvendorsupport",
+        "libvndksupport",
+        "libvulkan",
+    ],
+}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 6966f76..8037272 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -36,7 +36,7 @@
 type AndroidMkContext interface {
 	BaseModuleName() string
 	Target() android.Target
-	subAndroidMk(*android.AndroidMkEntries, interface{})
+	subAndroidMk(android.Config, *android.AndroidMkInfo, interface{})
 	Arch() android.Arch
 	Os() android.OsType
 	Host() bool
@@ -48,112 +48,124 @@
 	InRecovery() bool
 	NotInPlatform() bool
 	InVendorOrProduct() bool
+	ArchSpecific() bool
 }
 
-type subAndroidMkProvider interface {
-	AndroidMkEntries(AndroidMkContext, *android.AndroidMkEntries)
+type subAndroidMkProviderInfoProducer interface {
+	prepareAndroidMKProviderInfo(android.Config, AndroidMkContext, *android.AndroidMkInfo)
 }
 
-func (c *Module) subAndroidMk(entries *android.AndroidMkEntries, obj interface{}) {
+type subAndroidMkFooterInfoProducer interface {
+	prepareAndroidMKFooterInfo(android.Config, AndroidMkContext, *android.AndroidMkInfo)
+}
+
+func (c *Module) subAndroidMk(config android.Config, entries *android.AndroidMkInfo, obj interface{}) {
 	if c.subAndroidMkOnce == nil {
-		c.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
+		c.subAndroidMkOnce = make(map[subAndroidMkProviderInfoProducer]bool)
 	}
-	if androidmk, ok := obj.(subAndroidMkProvider); ok {
+	if androidmk, ok := obj.(subAndroidMkProviderInfoProducer); ok {
 		if !c.subAndroidMkOnce[androidmk] {
 			c.subAndroidMkOnce[androidmk] = true
-			androidmk.AndroidMkEntries(c, entries)
+			androidmk.prepareAndroidMKProviderInfo(config, c, entries)
 		}
 	}
 }
 
-func (c *Module) AndroidMkEntries() []android.AndroidMkEntries {
+var _ android.AndroidMkProviderInfoProducer = (*Module)(nil)
+
+func (c *Module) PrepareAndroidMKProviderInfo(config android.Config) *android.AndroidMkProviderInfo {
 	if c.hideApexVariantFromMake || c.Properties.HideFromMake {
-		return []android.AndroidMkEntries{{
-			Disabled: true,
-		}}
+		return &android.AndroidMkProviderInfo{
+			PrimaryInfo: android.AndroidMkInfo{
+				Disabled: true,
+			},
+		}
 	}
 
-	entries := android.AndroidMkEntries{
-		OutputFile: c.outputFile,
-		// TODO(jiyong): add the APEXes providing shared libs to the required
-		// modules Currently, adding c.Properties.ApexesProvidingSharedLibs is
-		// causing multiple ART APEXes (com.android.art and com.android.art.debug)
-		// to be installed. And this is breaking some older devices (like marlin)
-		// where system.img is small.
-		Required:     c.Properties.AndroidMkRuntimeLibs,
-		OverrideName: c.BaseModuleName(),
-		Include:      "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
-
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				if len(c.Properties.Logtags) > 0 {
-					entries.AddStrings("LOCAL_SOONG_LOGTAGS_FILES", c.logtagsPaths.Strings()...)
-				}
-				// Note: Pass the exact value of AndroidMkSystemSharedLibs to the Make
-				// world, even if it is an empty list. In the Make world,
-				// LOCAL_SYSTEM_SHARED_LIBRARIES defaults to "none", which is expanded
-				// to the default list of system shared libs by the build system.
-				// Soong computes the exact list of system shared libs, so we have to
-				// override the default value when the list of libs is actually empty.
-				entries.SetString("LOCAL_SYSTEM_SHARED_LIBRARIES", strings.Join(c.Properties.AndroidMkSystemSharedLibs, " "))
-				if len(c.Properties.AndroidMkSharedLibs) > 0 {
-					entries.AddStrings("LOCAL_SHARED_LIBRARIES", c.Properties.AndroidMkSharedLibs...)
-				}
-				if len(c.Properties.AndroidMkRuntimeLibs) > 0 {
-					entries.AddStrings("LOCAL_RUNTIME_LIBRARIES", c.Properties.AndroidMkRuntimeLibs...)
-				}
-				entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
-				if c.InVendor() {
-					entries.SetBool("LOCAL_IN_VENDOR", true)
-				} else if c.InProduct() {
-					entries.SetBool("LOCAL_IN_PRODUCT", true)
-				}
-				if c.Properties.SdkAndPlatformVariantVisibleToMake {
-					// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
-					// dependencies to the .sdk suffix when building a module that uses the SDK.
-					entries.SetString("SOONG_SDK_VARIANT_MODULES",
-						"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
-				}
-				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", c.IsSkipInstall())
-			},
-		},
-		ExtraFooters: []android.AndroidMkExtraFootersFunc{
-			func(w io.Writer, name, prefix, moduleDir string) {
-				if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake &&
-					c.CcLibraryInterface() && c.Shared() {
-					// Using the SDK variant as a JNI library needs a copy of the .so that
-					// is not named .sdk.so so that it can be packaged into the APK with
-					// the right name.
-					fmt.Fprintln(w, "$(eval $(call copy-one-file,",
-						"$(LOCAL_BUILT_MODULE),",
-						"$(patsubst %.sdk.so,%.so,$(LOCAL_BUILT_MODULE))))")
-				}
-			},
+	providerData := android.AndroidMkProviderInfo{
+		PrimaryInfo: android.AndroidMkInfo{
+			OutputFile:   c.outputFile,
+			Required:     c.Properties.AndroidMkRuntimeLibs,
+			OverrideName: c.BaseModuleName(),
+			Include:      "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
+			EntryMap:     make(map[string][]string),
 		},
 	}
 
+	entries := &providerData.PrimaryInfo
+	if len(c.Properties.Logtags) > 0 {
+		entries.AddStrings("LOCAL_SOONG_LOGTAGS_FILES", c.logtagsPaths.Strings()...)
+	}
+	// Note: Pass the exact value of AndroidMkSystemSharedLibs to the Make
+	// world, even if it is an empty list. In the Make world,
+	// LOCAL_SYSTEM_SHARED_LIBRARIES defaults to "none", which is expanded
+	// to the default list of system shared libs by the build system.
+	// Soong computes the exact list of system shared libs, so we have to
+	// override the default value when the list of libs is actually empty.
+	entries.SetString("LOCAL_SYSTEM_SHARED_LIBRARIES", strings.Join(c.Properties.AndroidMkSystemSharedLibs, " "))
+	if len(c.Properties.AndroidMkSharedLibs) > 0 {
+		entries.AddStrings("LOCAL_SHARED_LIBRARIES", c.Properties.AndroidMkSharedLibs...)
+	}
+	if len(c.Properties.AndroidMkRuntimeLibs) > 0 {
+		entries.AddStrings("LOCAL_RUNTIME_LIBRARIES", c.Properties.AndroidMkRuntimeLibs...)
+	}
+	entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
+	if c.InVendor() {
+		entries.SetBool("LOCAL_IN_VENDOR", true)
+	} else if c.InProduct() {
+		entries.SetBool("LOCAL_IN_PRODUCT", true)
+	}
+	if c.Properties.SdkAndPlatformVariantVisibleToMake {
+		// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
+		// dependencies to the .sdk suffix when building a module that uses the SDK.
+		entries.SetString("SOONG_SDK_VARIANT_MODULES",
+			"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
+	}
+	entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", c.IsSkipInstall())
+
 	for _, feature := range c.features {
-		c.subAndroidMk(&entries, feature)
+		c.subAndroidMk(config, entries, feature)
 	}
 
-	c.subAndroidMk(&entries, c.compiler)
-	c.subAndroidMk(&entries, c.linker)
+	c.subAndroidMk(config, entries, c.compiler)
+	c.subAndroidMk(config, entries, c.linker)
 	if c.sanitize != nil {
-		c.subAndroidMk(&entries, c.sanitize)
+		c.subAndroidMk(config, entries, c.sanitize)
 	}
-	c.subAndroidMk(&entries, c.installer)
+	c.subAndroidMk(config, entries, c.installer)
 
 	entries.SubName += c.Properties.SubName
 
-	return []android.AndroidMkEntries{entries}
+	// The footer info comes at the last step, previously it was achieved by
+	// calling some extra footer function that were added earlier. Because we no
+	// longer use these extra footer functions, we need to put this step at the
+	// last one.
+	if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake &&
+		c.CcLibraryInterface() && c.Shared() {
+		// Using the SDK variant as a JNI library needs a copy of the .so that
+		// is not named .sdk.so so that it can be packaged into the APK with
+		// the right name.
+		entries.FooterStrings = []string{
+			fmt.Sprintf("%s %s %s", "$(eval $(call copy-one-file,",
+				"$(LOCAL_BUILT_MODULE),",
+				"$(patsubst %.sdk.so,%.so,$(LOCAL_BUILT_MODULE))))")}
+	}
+
+	for _, obj := range []interface{}{c.compiler, c.linker, c.sanitize, c.installer} {
+		if obj == nil {
+			continue
+		}
+		if p, ok := obj.(subAndroidMkFooterInfoProducer); ok {
+			p.prepareAndroidMKFooterInfo(config, c, entries)
+		}
+	}
+
+	return &providerData
 }
 
-func androidMkWriteExtraTestConfigs(extraTestConfigs android.Paths, entries *android.AndroidMkEntries) {
+func androidMkWriteExtraTestConfigs(extraTestConfigs android.Paths, entries *android.AndroidMkInfo) {
 	if len(extraTestConfigs) > 0 {
-		entries.ExtraEntries = append(entries.ExtraEntries,
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", extraTestConfigs.Strings()...)
-			})
+		entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", extraTestConfigs.Strings()...)
 	}
 }
 
@@ -169,7 +181,7 @@
 	return overrides
 }
 
-func (library *libraryDecorator) androidMkWriteExportedFlags(entries *android.AndroidMkEntries) {
+func (library *libraryDecorator) androidMkWriteExportedFlags(entries *android.AndroidMkInfo) {
 	var exportedFlags []string
 	var includeDirs android.Paths
 	var systemIncludeDirs android.Paths
@@ -200,7 +212,7 @@
 	}
 }
 
-func (library *libraryDecorator) androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries *android.AndroidMkEntries) {
+func (library *libraryDecorator) androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries *android.AndroidMkInfo) {
 	if !library.static() {
 		entries.AddPaths("LOCAL_ADDITIONAL_DEPENDENCIES", library.sAbiDiff)
 	}
@@ -213,23 +225,21 @@
 	}
 }
 
-func (library *libraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (library *libraryDecorator) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
 	if library.static() {
 		entries.Class = "STATIC_LIBRARIES"
 	} else if library.shared() {
 		entries.Class = "SHARED_LIBRARIES"
-		entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			entries.SetString("LOCAL_SOONG_TOC", library.toc().String())
-			if !library.buildStubs() && library.unstrippedOutputFile != nil {
-				entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", library.unstrippedOutputFile.String())
-			}
-			if len(library.Properties.Overrides) > 0 {
-				entries.SetString("LOCAL_OVERRIDES_MODULES", strings.Join(makeOverrideModuleNames(ctx, library.Properties.Overrides), " "))
-			}
-			if len(library.postInstallCmds) > 0 {
-				entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(library.postInstallCmds, "&& "))
-			}
-		})
+		entries.SetString("LOCAL_SOONG_TOC", library.toc().String())
+		if !library.buildStubs() && library.unstrippedOutputFile != nil {
+			entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", library.unstrippedOutputFile.String())
+		}
+		if len(library.Properties.Overrides) > 0 {
+			entries.SetString("LOCAL_OVERRIDES_MODULES", strings.Join(makeOverrideModuleNames(ctx, library.Properties.Overrides), " "))
+		}
+		if len(library.postInstallCmds) > 0 {
+			entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(library.postInstallCmds, "&& "))
+		}
 	} else if library.header() {
 		entries.Class = "HEADER_LIBRARIES"
 	}
@@ -238,34 +248,30 @@
 		entries.DistFiles = android.MakeDefaultDistFiles(library.distFile)
 	}
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		library.androidMkWriteExportedFlags(entries)
-		library.androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries)
+	library.androidMkWriteExportedFlags(entries)
+	library.androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries)
 
-		if entries.OutputFile.Valid() {
-			_, _, ext := android.SplitFileExt(entries.OutputFile.Path().Base())
-			entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
-		}
+	if entries.OutputFile.Valid() {
+		_, _, ext := android.SplitFileExt(entries.OutputFile.Path().Base())
+		entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
+	}
 
-		if library.coverageOutputFile.Valid() {
-			entries.SetString("LOCAL_PREBUILT_COVERAGE_ARCHIVE", library.coverageOutputFile.String())
-		}
-	})
+	if library.coverageOutputFile.Valid() {
+		entries.SetString("LOCAL_PREBUILT_COVERAGE_ARCHIVE", library.coverageOutputFile.String())
+	}
 
 	if library.shared() && !library.buildStubs() {
-		ctx.subAndroidMk(entries, library.baseInstaller)
+		ctx.subAndroidMk(config, entries, library.baseInstaller)
 	} else {
 		if library.buildStubs() && library.stubsVersion() != "" {
 			entries.SubName = "." + library.stubsVersion()
 		}
-		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			// library.makeUninstallable() depends on this to bypass HideFromMake() for
-			// static libraries.
-			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-			if library.buildStubs() {
-				entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
-			}
-		})
+		// library.makeUninstallable() depends on this to bypass HideFromMake() for
+		// static libraries.
+		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+		if library.buildStubs() {
+			entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
+		}
 	}
 	// If a library providing a stub is included in an APEX, the private APIs of the library
 	// is accessible only inside the APEX. From outside of the APEX, clients can only use the
@@ -284,124 +290,136 @@
 	}
 }
 
-func (object *objectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (object *objectLinker) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
 	entries.Class = "STATIC_LIBRARIES"
-	entries.ExtraFooters = append(entries.ExtraFooters,
-		func(w io.Writer, name, prefix, moduleDir string) {
-			out := entries.OutputFile.Path()
-			varname := fmt.Sprintf("SOONG_%sOBJECT_%s%s", prefix, name, entries.SubName)
-
-			fmt.Fprintf(w, "\n%s := %s\n", varname, out.String())
-			fmt.Fprintln(w, ".KATI_READONLY: "+varname)
-		})
 }
 
-func (test *testDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		if len(test.InstallerProperties.Test_suites) > 0 {
-			entries.AddCompatibilityTestSuites(test.InstallerProperties.Test_suites...)
+func (object *objectLinker) prepareAndroidMKFooterInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
+	out := entries.OutputFile.Path()
+	name := ctx.BaseModuleName()
+	if entries.OverrideName != "" {
+		name = entries.OverrideName
+	}
+
+	prefix := ""
+	if ctx.ArchSpecific() {
+		switch ctx.Os().Class {
+		case android.Host:
+			if ctx.Target().HostCross {
+				prefix = "HOST_CROSS_"
+			} else {
+				prefix = "HOST_"
+			}
+		case android.Device:
+			prefix = "TARGET_"
+
 		}
-	})
+
+		if ctx.Arch().ArchType != config.Targets[ctx.Os()][0].Arch.ArchType {
+			prefix = "2ND_" + prefix
+		}
+	}
+
+	varname := fmt.Sprintf("SOONG_%sOBJECT_%s%s", prefix, name, entries.SubName)
+
+	entries.FooterStrings = append(entries.FooterStrings,
+		fmt.Sprintf("\n%s := %s\n.KATI_READONLY: %s", varname, out.String(), varname))
 }
 
-func (binary *binaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	ctx.subAndroidMk(entries, binary.baseInstaller)
+func (test *testDecorator) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
+	if len(test.InstallerProperties.Test_suites) > 0 {
+		entries.AddCompatibilityTestSuites(test.InstallerProperties.Test_suites...)
+	}
+}
+
+func (binary *binaryDecorator) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
+	ctx.subAndroidMk(config, entries, binary.baseInstaller)
 
 	entries.Class = "EXECUTABLES"
 	entries.DistFiles = binary.distFiles
-	entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", binary.unstrippedOutputFile.String())
-		if len(binary.symlinks) > 0 {
-			entries.AddStrings("LOCAL_MODULE_SYMLINKS", binary.symlinks...)
-		}
+	entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", binary.unstrippedOutputFile.String())
+	if len(binary.symlinks) > 0 {
+		entries.AddStrings("LOCAL_MODULE_SYMLINKS", binary.symlinks...)
+	}
 
-		if binary.coverageOutputFile.Valid() {
-			entries.SetString("LOCAL_PREBUILT_COVERAGE_ARCHIVE", binary.coverageOutputFile.String())
-		}
+	if binary.coverageOutputFile.Valid() {
+		entries.SetString("LOCAL_PREBUILT_COVERAGE_ARCHIVE", binary.coverageOutputFile.String())
+	}
 
-		if len(binary.Properties.Overrides) > 0 {
-			entries.SetString("LOCAL_OVERRIDES_MODULES", strings.Join(makeOverrideModuleNames(ctx, binary.Properties.Overrides), " "))
-		}
-		if len(binary.postInstallCmds) > 0 {
-			entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(binary.postInstallCmds, "&& "))
-		}
-	})
+	if len(binary.Properties.Overrides) > 0 {
+		entries.SetString("LOCAL_OVERRIDES_MODULES", strings.Join(makeOverrideModuleNames(ctx, binary.Properties.Overrides), " "))
+	}
+	if len(binary.postInstallCmds) > 0 {
+		entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(binary.postInstallCmds, "&& "))
+	}
 }
 
-func (benchmark *benchmarkDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	ctx.subAndroidMk(entries, benchmark.binaryDecorator)
+func (benchmark *benchmarkDecorator) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
+	ctx.subAndroidMk(config, entries, benchmark.binaryDecorator)
 	entries.Class = "NATIVE_TESTS"
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		if len(benchmark.Properties.Test_suites) > 0 {
-			entries.AddCompatibilityTestSuites(benchmark.Properties.Test_suites...)
-		}
-		if benchmark.testConfig != nil {
-			entries.SetString("LOCAL_FULL_TEST_CONFIG", benchmark.testConfig.String())
-		}
-		entries.SetBool("LOCAL_NATIVE_BENCHMARK", true)
-		if !BoolDefault(benchmark.Properties.Auto_gen_config, true) {
-			entries.SetBool("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", true)
-		}
-	})
+	if len(benchmark.Properties.Test_suites) > 0 {
+		entries.AddCompatibilityTestSuites(benchmark.Properties.Test_suites...)
+	}
+	if benchmark.testConfig != nil {
+		entries.SetString("LOCAL_FULL_TEST_CONFIG", benchmark.testConfig.String())
+	}
+	entries.SetBool("LOCAL_NATIVE_BENCHMARK", true)
+	if !BoolDefault(benchmark.Properties.Auto_gen_config, true) {
+		entries.SetBool("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", true)
+	}
 }
 
-func (test *testBinary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	ctx.subAndroidMk(entries, test.binaryDecorator)
-	ctx.subAndroidMk(entries, test.testDecorator)
+func (test *testBinary) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
+	ctx.subAndroidMk(config, entries, test.binaryDecorator)
+	ctx.subAndroidMk(config, entries, test.testDecorator)
 
 	entries.Class = "NATIVE_TESTS"
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		if test.testConfig != nil {
-			entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
-		}
-		if !BoolDefault(test.Properties.Auto_gen_config, true) {
-			entries.SetBool("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", true)
-		}
-		entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", test.Properties.Test_mainline_modules...)
+	if test.testConfig != nil {
+		entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
+	}
+	if !BoolDefault(test.Properties.Auto_gen_config, true) {
+		entries.SetBool("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", true)
+	}
+	entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", test.Properties.Test_mainline_modules...)
 
-		entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(test.Properties.Per_testcase_directory))
-		if len(test.Properties.Data_bins) > 0 {
-			entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
-		}
+	entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(test.Properties.Per_testcase_directory))
+	if len(test.Properties.Data_bins) > 0 {
+		entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+	}
 
-		test.Properties.Test_options.CommonTestOptions.SetAndroidMkEntries(entries)
-	})
+	test.Properties.Test_options.CommonTestOptions.SetAndroidMkInfoEntries(entries)
 
 	androidMkWriteExtraTestConfigs(test.extraTestConfigs, entries)
 }
 
-func (fuzz *fuzzBinary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	ctx.subAndroidMk(entries, fuzz.binaryDecorator)
+func (fuzz *fuzzBinary) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
+	ctx.subAndroidMk(config, entries, fuzz.binaryDecorator)
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		entries.SetBool("LOCAL_IS_FUZZ_TARGET", true)
-		if fuzz.installedSharedDeps != nil {
-			// TOOD: move to install dep
-			entries.AddStrings("LOCAL_FUZZ_INSTALLED_SHARED_DEPS", fuzz.installedSharedDeps...)
-		}
-	})
+	entries.SetBool("LOCAL_IS_FUZZ_TARGET", true)
+	if fuzz.installedSharedDeps != nil {
+		// TOOD: move to install dep
+		entries.AddStrings("LOCAL_FUZZ_INSTALLED_SHARED_DEPS", fuzz.installedSharedDeps...)
+	}
 }
 
-func (test *testLibrary) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	ctx.subAndroidMk(entries, test.libraryDecorator)
-	ctx.subAndroidMk(entries, test.testDecorator)
+func (test *testLibrary) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
+	ctx.subAndroidMk(config, entries, test.libraryDecorator)
+	ctx.subAndroidMk(config, entries, test.testDecorator)
 }
 
-func (installer *baseInstaller) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (installer *baseInstaller) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
 	if installer.path == (android.InstallPath{}) {
 		return
 	}
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		path, file := filepath.Split(installer.path.String())
-		stem, suffix, _ := android.SplitFileExt(file)
-		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
-		entries.SetString("LOCAL_MODULE_PATH", path)
-		entries.SetString("LOCAL_MODULE_STEM", stem)
-	})
+	path, file := filepath.Split(installer.path.String())
+	stem, suffix, _ := android.SplitFileExt(file)
+	entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+	entries.SetString("LOCAL_MODULE_PATH", path)
+	entries.SetString("LOCAL_MODULE_STEM", stem)
 }
 
-func (c *stubDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *stubDecorator) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
 	entries.SubName = ndkLibrarySuffix + "." + c.apiLevel.String()
 	entries.Class = "SHARED_LIBRARIES"
 
@@ -410,85 +428,75 @@
 		return
 	}
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		path, file := filepath.Split(c.installPath.String())
-		stem, suffix, _ := android.SplitFileExt(file)
-		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
-		entries.SetString("LOCAL_MODULE_PATH", path)
-		entries.SetString("LOCAL_MODULE_STEM", stem)
-		entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
-		if c.parsedCoverageXmlPath.String() != "" {
-			entries.SetString("SOONG_NDK_API_XML", "$(SOONG_NDK_API_XML) "+c.parsedCoverageXmlPath.String())
-		}
-		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) // Stubs should not be installed
-	})
+	path, file := filepath.Split(c.installPath.String())
+	stem, suffix, _ := android.SplitFileExt(file)
+	entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+	entries.SetString("LOCAL_MODULE_PATH", path)
+	entries.SetString("LOCAL_MODULE_STEM", stem)
+	entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
+	if c.parsedCoverageXmlPath.String() != "" {
+		entries.SetString("SOONG_NDK_API_XML", "$(SOONG_NDK_API_XML) "+c.parsedCoverageXmlPath.String())
+	}
+	entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) // Stubs should not be installed
 }
 
-func (c *vndkPrebuiltLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *vndkPrebuiltLibraryDecorator) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
 	entries.Class = "SHARED_LIBRARIES"
 
 	entries.SubName = c.androidMkSuffix
 
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		c.libraryDecorator.androidMkWriteExportedFlags(entries)
+	c.libraryDecorator.androidMkWriteExportedFlags(entries)
 
-		// Specifying stem is to pass check_elf_files when vendor modules link against vndk prebuilt.
-		// We can't use install path because VNDKs are not installed. Instead, Srcs is directly used.
-		_, file := filepath.Split(c.properties.Srcs[0])
-		stem, suffix, ext := android.SplitFileExt(file)
-		entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
-		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
-		entries.SetString("LOCAL_MODULE_STEM", stem)
+	// Specifying stem is to pass check_elf_files when vendor modules link against vndk prebuilt.
+	// We can't use install path because VNDKs are not installed. Instead, Srcs is directly used.
+	_, file := filepath.Split(c.properties.Srcs[0])
+	stem, suffix, ext := android.SplitFileExt(file)
+	entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
+	entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+	entries.SetString("LOCAL_MODULE_STEM", stem)
 
-		if c.tocFile.Valid() {
-			entries.SetString("LOCAL_SOONG_TOC", c.tocFile.String())
-		}
+	if c.tocFile.Valid() {
+		entries.SetString("LOCAL_SOONG_TOC", c.tocFile.String())
+	}
 
-		// VNDK libraries available to vendor are not installed because
-		// they are packaged in VNDK APEX and installed by APEX packages (apex/apex.go)
-		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-	})
+	// VNDK libraries available to vendor are not installed because
+	// they are packaged in VNDK APEX and installed by APEX packages (apex/apex.go)
+	entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
 }
 
-func (p *prebuiltLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		if p.properties.Check_elf_files != nil {
-			entries.SetBool("LOCAL_CHECK_ELF_FILES", *p.properties.Check_elf_files)
-		} else {
-			// soong_cc_rust_prebuilt.mk does not include check_elf_file.mk by default
-			// because cc_library_shared and cc_binary use soong_cc_rust_prebuilt.mk as well.
-			// In order to turn on prebuilt ABI checker, set `LOCAL_CHECK_ELF_FILES` to
-			// true if `p.properties.Check_elf_files` is not specified.
-			entries.SetBool("LOCAL_CHECK_ELF_FILES", true)
-		}
-	})
+func (p *prebuiltLinker) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
+	if p.properties.Check_elf_files != nil {
+		entries.SetBool("LOCAL_CHECK_ELF_FILES", *p.properties.Check_elf_files)
+	} else {
+		// soong_cc_rust_prebuilt.mk does not include check_elf_file.mk by default
+		// because cc_library_shared and cc_binary use soong_cc_rust_prebuilt.mk as well.
+		// In order to turn on prebuilt ABI checker, set `LOCAL_CHECK_ELF_FILES` to
+		// true if `p.properties.Check_elf_files` is not specified.
+		entries.SetBool("LOCAL_CHECK_ELF_FILES", true)
+	}
 }
 
-func (p *prebuiltLibraryLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	ctx.subAndroidMk(entries, p.libraryDecorator)
+func (p *prebuiltLibraryLinker) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
+	ctx.subAndroidMk(config, entries, p.libraryDecorator)
 	if p.shared() {
-		ctx.subAndroidMk(entries, &p.prebuiltLinker)
+		ctx.subAndroidMk(config, entries, &p.prebuiltLinker)
 		androidMkWritePrebuiltOptions(p.baseLinker, entries)
 	}
 }
 
-func (p *prebuiltBinaryLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	ctx.subAndroidMk(entries, p.binaryDecorator)
-	ctx.subAndroidMk(entries, &p.prebuiltLinker)
+func (p *prebuiltBinaryLinker) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
+	ctx.subAndroidMk(config, entries, p.binaryDecorator)
+	ctx.subAndroidMk(config, entries, &p.prebuiltLinker)
 	androidMkWritePrebuiltOptions(p.baseLinker, entries)
 }
 
-func androidMkWritePrebuiltOptions(linker *baseLinker, entries *android.AndroidMkEntries) {
+func androidMkWritePrebuiltOptions(linker *baseLinker, entries *android.AndroidMkInfo) {
 	allow := linker.Properties.Allow_undefined_symbols
 	if allow != nil {
-		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			entries.SetBool("LOCAL_ALLOW_UNDEFINED_SYMBOLS", *allow)
-		})
+		entries.SetBool("LOCAL_ALLOW_UNDEFINED_SYMBOLS", *allow)
 	}
 	ignore := linker.Properties.Ignore_max_page_size
 	if ignore != nil {
-		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			entries.SetBool("LOCAL_IGNORE_MAX_PAGE_SIZE", *ignore)
-		})
+		entries.SetBool("LOCAL_IGNORE_MAX_PAGE_SIZE", *ignore)
 	}
 }
diff --git a/cc/api_level.go b/cc/api_level.go
index 69a0d3a..7dc0213 100644
--- a/cc/api_level.go
+++ b/cc/api_level.go
@@ -31,22 +31,31 @@
 	case android.Arm64, android.X86_64:
 		return android.FirstLp64Version
 	case android.Riscv64:
-		apiLevel, err := android.ApiLevelFromUser(ctx, "VanillaIceCream")
-		if err != nil {
-			panic(err)
-		}
-		return apiLevel
+		return android.FutureApiLevel
 	default:
 		panic(fmt.Errorf("Unknown arch %q", arch))
 	}
 }
 
+// Native API levels cannot be less than the MinApiLevelForArch. This function
+// sets the lower bound of the API level with the MinApiLevelForArch.
+func nativeClampedApiLevel(ctx android.BaseModuleContext,
+	apiLevel android.ApiLevel) android.ApiLevel {
+
+	min := MinApiForArch(ctx, ctx.Arch().ArchType)
+
+	if apiLevel.LessThan(min) {
+		return min
+	}
+
+	return apiLevel
+}
+
 func nativeApiLevelFromUser(ctx android.BaseModuleContext,
 	raw string) (android.ApiLevel, error) {
 
-	min := MinApiForArch(ctx, ctx.Arch().ArchType)
 	if raw == "minimum" {
-		return min, nil
+		return MinApiForArch(ctx, ctx.Arch().ArchType), nil
 	}
 
 	value, err := android.ApiLevelFromUser(ctx, raw)
@@ -54,15 +63,12 @@
 		return android.NoneApiLevel, err
 	}
 
-	if value.LessThan(min) {
-		return min, nil
-	}
-
-	return value, nil
+	return nativeClampedApiLevel(ctx, value), nil
 }
 
 func nativeApiLevelOrPanic(ctx android.BaseModuleContext,
 	raw string) android.ApiLevel {
+
 	value, err := nativeApiLevelFromUser(ctx, raw)
 	if err != nil {
 		panic(err.Error())
diff --git a/cc/binary.go b/cc/binary.go
index 2ac9a45..4b77bea 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -505,7 +505,7 @@
 	// The original path becomes a symlink to the corresponding file in the
 	// runtime APEX.
 	translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
-	if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !ctx.Host() && ctx.directlyInAnyApex() &&
+	if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !ctx.Host() && !ctx.isSdkVariant() &&
 		!translatedArch && ctx.apexVariationName() == "" && !ctx.inRamdisk() && !ctx.inRecovery() &&
 		!ctx.inVendorRamdisk() {
 
diff --git a/cc/builder.go b/cc/builder.go
index cd535c1..b98bef9 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -821,7 +821,7 @@
 		return nil
 	}
 
-	output := android.PathForModuleOut(ctx, "generated_rust_staticlib", "lib"+ctx.ModuleName()+"_rust_staticlib.a")
+	output := android.PathForModuleOut(ctx, "generated_rust_staticlib", "librustlibs.a")
 	stemFile := output.ReplaceExtension(ctx, "rs")
 	crateNames := []string{}
 
@@ -945,7 +945,7 @@
 func transformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Paths, soFile android.Path,
 	baseName string, exportedIncludeDirs []string, symbolFile android.OptionalPath,
 	excludedSymbolVersions, excludedSymbolTags, includedSymbolTags []string,
-	api string, isLlndk bool) android.Path {
+	api string) android.Path {
 
 	outputFile := android.PathForModuleOut(ctx, baseName+".lsdump")
 
@@ -966,9 +966,6 @@
 	for _, tag := range includedSymbolTags {
 		symbolFilterStr += " --include-symbol-tag " + tag
 	}
-	if isLlndk {
-		symbolFilterStr += " --symbol-tag-policy MatchTagOnly"
-	}
 	apiLevelsJson := android.GetApiLevelsJson(ctx)
 	implicits = append(implicits, apiLevelsJson)
 	symbolFilterStr += " --api-map " + apiLevelsJson.String()
diff --git a/cc/cc.go b/cc/cc.go
index e47e3fc..6b4e0dc 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -19,14 +19,15 @@
 // is handled in builder.go
 
 import (
+	"errors"
 	"fmt"
 	"io"
+	"slices"
 	"strconv"
 	"strings"
 
-	"android/soong/testing"
-
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/aidl_library"
@@ -36,6 +37,36 @@
 	"android/soong/genrule"
 )
 
+type CcMakeVarsInfo struct {
+	WarningsAllowed string
+	UsingWnoError   string
+	MissingProfile  string
+}
+
+var CcMakeVarsInfoProvider = blueprint.NewProvider[*CcMakeVarsInfo]()
+
+type CcObjectInfo struct {
+	objFiles   android.Paths
+	tidyFiles  android.Paths
+	kytheFiles android.Paths
+}
+
+var CcObjectInfoProvider = blueprint.NewProvider[CcObjectInfo]()
+
+// Common info about the cc module.
+type CcInfo struct {
+	HasStubsVariants bool
+}
+
+var CcInfoProvider = blueprint.NewProvider[CcInfo]()
+
+type LinkableInfo struct {
+	// StaticExecutable returns true if this is a binary module with "static_executable: true".
+	StaticExecutable bool
+}
+
+var LinkableInfoKey = blueprint.NewProvider[LinkableInfo]()
+
 func init() {
 	RegisterCCBuildComponents(android.InitRegistrationContext)
 
@@ -48,10 +79,10 @@
 
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.Transition("sdk", &sdkTransitionMutator{})
-		ctx.BottomUp("llndk", llndkMutator).Parallel()
+		ctx.BottomUp("llndk", llndkMutator)
 		ctx.Transition("link", &linkageTransitionMutator{})
 		ctx.Transition("version", &versionTransitionMutator{})
-		ctx.BottomUp("begin", BeginMutator).Parallel()
+		ctx.BottomUp("begin", BeginMutator)
 	})
 
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
@@ -59,10 +90,10 @@
 			san.registerMutators(ctx)
 		}
 
-		ctx.BottomUp("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
-		ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
+		ctx.BottomUp("sanitize_runtime_deps", sanitizerRuntimeDepsMutator)
+		ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator)
 
-		ctx.BottomUp("fuzz_deps", fuzzMutatorDeps)
+		ctx.Transition("fuzz", &fuzzTransitionMutator{})
 
 		ctx.Transition("coverage", &coverageTransitionMutator{})
 
@@ -72,13 +103,13 @@
 
 		ctx.Transition("lto", &ltoTransitionMutator{})
 
-		ctx.BottomUp("check_linktype", checkLinkTypeMutator).Parallel()
-		ctx.BottomUp("double_loadable", checkDoubleLoadableLibraries).Parallel()
+		ctx.BottomUp("check_linktype", checkLinkTypeMutator)
+		ctx.BottomUp("double_loadable", checkDoubleLoadableLibraries)
 	})
 
-	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+	ctx.PostApexMutators(func(ctx android.RegisterMutatorsContext) {
 		// sabi mutator needs to be run after apex mutator finishes.
-		ctx.TopDown("sabi_deps", sabiDepsMutator)
+		ctx.Transition("sabi", &sabiTransitionMutator{})
 	})
 
 	ctx.RegisterParallelSingletonType("kythe_extract_all", kytheExtractAllFactory)
@@ -119,9 +150,10 @@
 
 	ObjFiles []string
 
-	GeneratedSources []string
-	GeneratedHeaders []string
-	GeneratedDeps    []string
+	GeneratedSources            []string
+	GeneratedHeaders            []string
+	DeviceFirstGeneratedHeaders []string
+	GeneratedDeps               []string
 
 	ReexportGeneratedHeaders []string
 
@@ -137,7 +169,7 @@
 
 	// LLNDK headers for the ABI checker to check LLNDK implementation library.
 	// An LLNDK implementation is the core variant. LLNDK header libs are reexported by the vendor variant.
-	// The core variant cannot depend on the vendor variant because of the order of CreateVariations.
+	// The core variant cannot depend on the vendor variant because of the order of imageTransitionMutator.Split().
 	// Instead, the LLNDK implementation depends on the LLNDK header libs.
 	LlndkHeaderLibs []string
 }
@@ -169,7 +201,7 @@
 	RustRlibDeps []RustRlibDep
 
 	// Transitive static library dependencies of static libraries for use in ordering.
-	TranstiveStaticLibrariesForOrdering *android.DepSet[android.Path]
+	TranstiveStaticLibrariesForOrdering depset.DepSet[android.Path]
 
 	// Paths to .o files
 	Objs Objects
@@ -213,6 +245,9 @@
 	// LLNDK headers for the ABI checker to check LLNDK implementation library.
 	LlndkIncludeDirs       android.Paths
 	LlndkSystemIncludeDirs android.Paths
+
+	directImplementationDeps     android.Paths
+	transitiveImplementationDeps []depset.DepSet[android.Path]
 }
 
 // LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
@@ -313,15 +348,14 @@
 	// If true, always create an sdk variant and don't create a platform variant.
 	Sdk_variant_only *bool
 
-	AndroidMkSharedLibs       []string `blueprint:"mutated"`
-	AndroidMkStaticLibs       []string `blueprint:"mutated"`
-	AndroidMkRlibs            []string `blueprint:"mutated"`
-	AndroidMkRuntimeLibs      []string `blueprint:"mutated"`
-	AndroidMkWholeStaticLibs  []string `blueprint:"mutated"`
-	AndroidMkHeaderLibs       []string `blueprint:"mutated"`
-	HideFromMake              bool     `blueprint:"mutated"`
-	PreventInstall            bool     `blueprint:"mutated"`
-	ApexesProvidingSharedLibs []string `blueprint:"mutated"`
+	AndroidMkSharedLibs      []string `blueprint:"mutated"`
+	AndroidMkStaticLibs      []string `blueprint:"mutated"`
+	AndroidMkRlibs           []string `blueprint:"mutated"`
+	AndroidMkRuntimeLibs     []string `blueprint:"mutated"`
+	AndroidMkWholeStaticLibs []string `blueprint:"mutated"`
+	AndroidMkHeaderLibs      []string `blueprint:"mutated"`
+	HideFromMake             bool     `blueprint:"mutated"`
+	PreventInstall           bool     `blueprint:"mutated"`
 
 	// Set by DepsMutator.
 	AndroidMkSystemSharedLibs []string `blueprint:"mutated"`
@@ -389,11 +423,6 @@
 	// variant to have a ".sdk" suffix.
 	SdkAndPlatformVariantVisibleToMake bool `blueprint:"mutated"`
 
-	// List of APEXes that this module has private access to for testing purpose. The module
-	// can depend on libraries that are not exported by the APEXes and use private symbols
-	// from the exported libraries.
-	Test_for []string `android:"arch_variant"`
-
 	Target struct {
 		Platform struct {
 			// List of modules required by the core variant.
@@ -525,12 +554,12 @@
 	apexSdkVersion() android.ApiLevel
 	bootstrap() bool
 	nativeCoverage() bool
-	directlyInAnyApex() bool
 	isPreventInstall() bool
 	isCfiAssemblySupportEnabled() bool
 	getSharedFlags() *SharedFlags
 	notInPlatform() bool
 	optimizeForSize() bool
+	getOrCreateMakeVarsInfo() *CcMakeVarsInfo
 }
 
 type SharedFlags struct {
@@ -639,10 +668,6 @@
 	installInRoot() bool
 }
 
-type xref interface {
-	XrefCcFiles() android.Paths
-}
-
 type overridable interface {
 	overriddenModules() []string
 }
@@ -845,9 +870,10 @@
 	sourceProperties android.SourceProperties
 
 	// initialize before calling Init
-	hod        android.HostOrDeviceSupported
-	multilib   android.Multilib
-	testModule bool
+	hod         android.HostOrDeviceSupported
+	multilib    android.Multilib
+	testModule  bool
+	incremental bool
 
 	// Allowable SdkMemberTypes of this module type.
 	sdkMemberTypes []android.SdkMemberType
@@ -878,7 +904,7 @@
 
 	cachedToolchain config.Toolchain
 
-	subAndroidMkOnce map[subAndroidMkProvider]bool
+	subAndroidMkOnce map[subAndroidMkProviderInfoProducer]bool
 
 	// Flags used to compile this module
 	flags Flags
@@ -890,12 +916,6 @@
 	staticAnalogue *StaticLibraryInfo
 
 	makeLinkType string
-	// Kythe (source file indexer) paths for this compilation module
-	kytheFiles android.Paths
-	// Object .o file output paths for this compilation module
-	objFiles android.Paths
-	// Tidy .tidy file output paths for this compilation module
-	tidyFiles android.Paths
 
 	// For apex variants, this is set as apex.min_sdk_version
 	apexSdkVersion android.ApiLevel
@@ -913,8 +933,16 @@
 	hasSysprop      bool
 	hasWinMsg       bool
 	hasYacc         bool
+
+	makeVarsInfo *CcMakeVarsInfo
 }
 
+func (c *Module) IncrementalSupported() bool {
+	return c.incremental
+}
+
+var _ blueprint.Incremental = (*Module)(nil)
+
 func (c *Module) AddJSONData(d *map[string]interface{}) {
 	c.AndroidModuleBase().AddJSONData(d)
 	(*d)["Cc"] = map[string]interface{}{
@@ -944,7 +972,6 @@
 		"IsLlndk":                c.IsLlndk(),
 		"IsVendorPublicLibrary":  c.IsVendorPublicLibrary(),
 		"ApexSdkVersion":         c.apexSdkVersion,
-		"TestFor":                c.TestFor(),
 		"AidlSrcs":               c.hasAidl,
 		"LexSrcs":                c.hasLex,
 		"ProtoSrcs":              c.hasProto,
@@ -1455,10 +1482,6 @@
 	return isBionic(name)
 }
 
-func (c *Module) XrefCcFiles() android.Paths {
-	return c.kytheFiles
-}
-
 func (c *Module) isCfiAssemblySupportEnabled() bool {
 	return c.sanitize != nil &&
 		Bool(c.sanitize.Properties.Sanitize.Config.Cfi_assembly_support)
@@ -1549,12 +1572,11 @@
 	}
 
 	if ctx.ctx.Device() {
-		config := ctx.ctx.Config()
-		if ctx.inVendor() {
-			// If building for vendor with final API, then use the latest _stable_ API as "current".
-			if config.VendorApiLevelFrozen() && (ver == "" || ver == "current") {
-				ver = config.PlatformSdkVersion().String()
-			}
+		// When building for vendor/product, use the latest _stable_ API as "current".
+		// This is passed to clang/aidl compilers so that compiled/generated code works
+		// with the system.
+		if (ctx.inVendor() || ctx.inProduct()) && (ver == "" || ver == "current") {
+			ver = ctx.ctx.Config().PlatformSdkVersion().String()
 		}
 	}
 
@@ -1675,10 +1697,6 @@
 	return ctx.mod.nativeCoverage()
 }
 
-func (ctx *moduleContextImpl) directlyInAnyApex() bool {
-	return ctx.mod.DirectlyInAnyApex()
-}
-
 func (ctx *moduleContextImpl) isPreventInstall() bool {
 	return ctx.mod.Properties.PreventInstall
 }
@@ -1700,6 +1718,13 @@
 	return ctx.mod.NotInPlatform()
 }
 
+func (ctx *moduleContextImpl) getOrCreateMakeVarsInfo() *CcMakeVarsInfo {
+	if ctx.mod.makeVarsInfo == nil {
+		ctx.mod.makeVarsInfo = &CcMakeVarsInfo{}
+	}
+	return ctx.mod.makeVarsInfo
+}
+
 func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
 	return &Module{
 		hod:      hod,
@@ -1861,7 +1886,7 @@
 // Returns true if a stub library could be installed in multiple apexes
 func (c *Module) stubLibraryMultipleApexViolation(ctx android.ModuleContext) bool {
 	// If this is not an apex variant, no check necessary
-	if !c.InAnyApex() {
+	if info, ok := android.ModuleProvider(ctx, android.ApexInfoProvider); !ok || info.IsForPlatform() {
 		return false
 	}
 	// If this is not a stub library, no check necessary
@@ -2023,9 +2048,6 @@
 		if ctx.Failed() {
 			return
 		}
-		c.kytheFiles = objs.kytheFiles
-		c.objFiles = objs.objFiles
-		c.tidyFiles = objs.tidyFiles
 	}
 
 	if c.linker != nil {
@@ -2036,9 +2058,10 @@
 		c.outputFile = android.OptionalPathForPath(outputFile)
 
 		c.maybeUnhideFromMake()
-	}
-	if c.testModule {
-		android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+
+		android.SetProvider(ctx, ImplementationDepInfoProvider, &ImplementationDepInfo{
+			ImplementationDeps: depset.New(depset.PREORDER, deps.directImplementationDeps, deps.transitiveImplementationDeps),
+		})
 	}
 
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
@@ -2093,7 +2116,36 @@
 		c.hasYacc = b.hasSrcExt(ctx, ".y") || b.hasSrcExt(ctx, ".yy")
 	}
 
+	ccObjectInfo := CcObjectInfo{
+		kytheFiles: objs.kytheFiles,
+	}
+	if !ctx.Config().KatiEnabled() || !android.ShouldSkipAndroidMkProcessing(ctx, c) {
+		ccObjectInfo.objFiles = objs.objFiles
+		ccObjectInfo.tidyFiles = objs.tidyFiles
+	}
+	if len(ccObjectInfo.kytheFiles)+len(ccObjectInfo.objFiles)+len(ccObjectInfo.tidyFiles) > 0 {
+		android.SetProvider(ctx, CcObjectInfoProvider, ccObjectInfo)
+	}
+
+	android.SetProvider(ctx, LinkableInfoKey, LinkableInfo{
+		StaticExecutable: c.StaticExecutable(),
+	})
+
+	android.SetProvider(ctx, CcInfoProvider, CcInfo{
+		HasStubsVariants: c.HasStubsVariants(),
+	})
+
 	c.setOutputFiles(ctx)
+
+	if c.makeVarsInfo != nil {
+		android.SetProvider(ctx, CcMakeVarsInfoProvider, c.makeVarsInfo)
+	}
+}
+
+func setOutputFilesIfNotEmpty(ctx ModuleContext, files android.Paths, tag string) {
+	if len(files) > 0 {
+		ctx.SetOutputFiles(files, tag)
+	}
 }
 
 func (c *Module) setOutputFiles(ctx ModuleContext) {
@@ -2169,6 +2221,7 @@
 		// modules can be hidden from make as some are needed for resolving make side
 		// dependencies.
 		c.HideFromMake()
+		c.SkipInstall()
 	} else if !installable(c, apexInfo) {
 		c.SkipInstall()
 	}
@@ -2257,6 +2310,10 @@
 	deps.RuntimeLibs = android.LastUniqueStrings(deps.RuntimeLibs)
 	deps.LlndkHeaderLibs = android.LastUniqueStrings(deps.LlndkHeaderLibs)
 
+	if err := checkConflictingExplicitVersions(deps.SharedLibs); err != nil {
+		ctx.PropertyErrorf("shared_libs", "%s", err.Error())
+	}
+
 	for _, lib := range deps.ReexportSharedLibHeaders {
 		if !inList(lib, deps.SharedLibs) {
 			ctx.PropertyErrorf("export_shared_lib_headers", "Shared library not in shared_libs: '%s'", lib)
@@ -2284,6 +2341,26 @@
 	return deps
 }
 
+func checkConflictingExplicitVersions(libs []string) error {
+	withoutVersion := func(s string) string {
+		name, _ := StubsLibNameAndVersion(s)
+		return name
+	}
+	var errs []error
+	for i, lib := range libs {
+		libName := withoutVersion(lib)
+		libsToCompare := libs[i+1:]
+		j := slices.IndexFunc(libsToCompare, func(s string) bool {
+			return withoutVersion(s) == libName
+		})
+		if j >= 0 {
+			errs = append(errs, fmt.Errorf("duplicate shared libraries with different explicit versions: %q and %q",
+				lib, libsToCompare[j]))
+		}
+	}
+	return errors.Join(errs...)
+}
+
 func (c *Module) beginMutator(actx android.BottomUpMutatorContext) {
 	ctx := &baseModuleContext{
 		BaseModuleContext: actx,
@@ -2345,9 +2422,16 @@
 
 	if version != "" && canBeOrLinkAgainstVersionVariants(mod) {
 		// Version is explicitly specified. i.e. libFoo#30
+		if version == "impl" {
+			version = ""
+		}
 		variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
 		if tag, ok := depTag.(libraryDependencyTag); ok {
 			tag.explicitlyVersioned = true
+			// depTag is an interface that contains a concrete non-pointer struct.  That makes the local
+			// tag variable a copy of the contents of depTag, and updating it doesn't change depTag.  Reassign
+			// the modified copy to depTag.
+			depTag = tag
 		} else {
 			panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
 		}
@@ -2626,6 +2710,11 @@
 		actx.AddDependency(c, depTag, gen)
 	}
 
+	for _, gen := range deps.DeviceFirstGeneratedHeaders {
+		depTag := genHeaderDepTag
+		actx.AddVariationDependencies(ctx.Config().AndroidFirstDeviceTarget.Variations(), depTag, gen)
+	}
+
 	crtVariations := GetCrtVariations(ctx, c)
 	actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
 	for _, crt := range deps.CrtBegin {
@@ -3051,6 +3140,13 @@
 				linkFile = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
 				depFile = sharedLibraryInfo.TableOfContents
 
+				if !sharedLibraryInfo.IsStubs {
+					depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+					if info, ok := android.OtherModuleProvider(ctx, dep, ImplementationDepInfoProvider); ok {
+						depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+					}
+				}
+
 				ptr = &depPaths.SharedLibs
 				switch libDepTag.Order {
 				case earlyLibraryDependency:
@@ -3216,18 +3312,6 @@
 				c.Properties.AndroidMkHeaderLibs = append(
 					c.Properties.AndroidMkHeaderLibs, makeLibName)
 			case libDepTag.shared():
-				if lib := moduleLibraryInterface(dep); lib != nil {
-					if lib.buildStubs() && dep.(android.ApexModule).InAnyApex() {
-						// Add the dependency to the APEX(es) providing the library so that
-						// m <module> can trigger building the APEXes as well.
-						depApexInfo, _ := android.OtherModuleProvider(ctx, dep, android.ApexInfoProvider)
-						for _, an := range depApexInfo.InApexVariants {
-							c.Properties.ApexesProvidingSharedLibs = append(
-								c.Properties.ApexesProvidingSharedLibs, an)
-						}
-					}
-				}
-
 				// Note: the order of libs in this list is not important because
 				// they merely serve as Make dependencies and do not affect this lib itself.
 				c.Properties.AndroidMkSharedLibs = append(
@@ -3289,23 +3373,17 @@
 	return depPaths
 }
 
-func ShouldUseStubForApex(ctx android.ModuleContext, dep android.Module) bool {
-	depName := ctx.OtherModuleName(dep)
-	thisModule, ok := ctx.Module().(android.ApexModule)
-	if !ok {
-		panic(fmt.Errorf("Not an APEX module: %q", ctx.ModuleName()))
-	}
-
+func ShouldUseStubForApex(ctx android.ModuleContext, parent, dep android.Module) bool {
 	inVendorOrProduct := false
 	bootstrap := false
-	if linkable, ok := ctx.Module().(LinkableInterface); !ok {
-		panic(fmt.Errorf("Not a Linkable module: %q", ctx.ModuleName()))
+	if linkable, ok := parent.(LinkableInterface); !ok {
+		ctx.ModuleErrorf("Not a Linkable module: %q", ctx.ModuleName())
 	} else {
 		inVendorOrProduct = linkable.InVendorOrProduct()
 		bootstrap = linkable.Bootstrap()
 	}
 
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	apexInfo, _ := android.OtherModuleProvider(ctx, parent, android.ApexInfoProvider)
 
 	useStubs := false
 
@@ -3324,38 +3402,9 @@
 		isNotInPlatform := dep.(android.ApexModule).NotInPlatform()
 
 		useStubs = isNotInPlatform && !bootstrap
-
-		if useStubs {
-			// Another exception: if this module is a test for an APEX, then
-			// it is linked with the non-stub variant of a module in the APEX
-			// as if this is part of the APEX.
-			testFor, _ := android.ModuleProvider(ctx, android.ApexTestForInfoProvider)
-			for _, apexContents := range testFor.ApexContents {
-				if apexContents.DirectlyInApex(depName) {
-					useStubs = false
-					break
-				}
-			}
-		}
-		if useStubs {
-			// Yet another exception: If this module and the dependency are
-			// available to the same APEXes then skip stubs between their
-			// platform variants. This complements the test_for case above,
-			// which avoids the stubs on a direct APEX library dependency, by
-			// avoiding stubs for indirect test dependencies as well.
-			//
-			// TODO(b/183882457): This doesn't work if the two libraries have
-			// only partially overlapping apex_available. For that test_for
-			// modules would need to be split into APEX variants and resolved
-			// separately for each APEX they have access to.
-			if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
-				useStubs = false
-			}
-		}
 	} else {
-		// If building for APEX, use stubs when the parent is in any APEX that
-		// the child is not in.
-		useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
+		// If building for APEX, always use stubs (can be bypassed by depending on <dep>#impl)
+		useStubs = true
 	}
 
 	return useStubs
@@ -3381,7 +3430,7 @@
 
 	if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 {
 		// when to use (unspecified) stubs, use the latest one.
-		if ShouldUseStubForApex(ctx, dep) {
+		if ShouldUseStubForApex(ctx, ctx.Module(), dep) {
 			stubs := sharedLibraryStubsInfo.SharedStubLibraries
 			toUse := stubs[len(stubs)-1]
 			sharedLibraryInfo = toUse.SharedLibraryInfo
@@ -3393,19 +3442,17 @@
 
 // orderStaticModuleDeps rearranges the order of the static library dependencies of the module
 // to match the topological order of the dependency tree, including any static analogues of
-// direct shared libraries.  It returns the ordered static dependencies, and an android.DepSet
+// direct shared libraries.  It returns the ordered static dependencies, and a depset.DepSet
 // of the transitive dependencies.
-func orderStaticModuleDeps(staticDeps []StaticLibraryInfo, sharedDeps []SharedLibraryInfo) (ordered android.Paths, transitive *android.DepSet[android.Path]) {
-	transitiveStaticLibsBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL)
+func orderStaticModuleDeps(staticDeps []StaticLibraryInfo, sharedDeps []SharedLibraryInfo) (ordered android.Paths, transitive depset.DepSet[android.Path]) {
+	transitiveStaticLibsBuilder := depset.NewBuilder[android.Path](depset.TOPOLOGICAL)
 	var staticPaths android.Paths
 	for _, staticDep := range staticDeps {
 		staticPaths = append(staticPaths, staticDep.StaticLibrary)
 		transitiveStaticLibsBuilder.Transitive(staticDep.TransitiveStaticLibrariesForOrdering)
 	}
 	for _, sharedDep := range sharedDeps {
-		if sharedDep.TransitiveStaticLibrariesForOrdering != nil {
-			transitiveStaticLibsBuilder.Transitive(sharedDep.TransitiveStaticLibrariesForOrdering)
-		}
+		transitiveStaticLibsBuilder.Transitive(sharedDep.TransitiveStaticLibrariesForOrdering)
 	}
 	transitiveStaticLibs := transitiveStaticLibsBuilder.Build()
 
@@ -3654,10 +3701,6 @@
 	}
 }
 
-func (c *Module) TestFor() []string {
-	return c.Properties.Test_for
-}
-
 func (c *Module) EverInstallable() bool {
 	return c.installer != nil &&
 		// Check to see whether the module is actually ever installable.
@@ -3941,9 +3984,10 @@
 
 func (ks *kytheExtractAllSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var xrefTargets android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
-		if ccModule, ok := module.(xref); ok {
-			xrefTargets = append(xrefTargets, ccModule.XrefCcFiles()...)
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		files := android.OtherModuleProviderOrDefault(ctx, module, CcObjectInfoProvider).kytheFiles
+		if len(files) > 0 {
+			xrefTargets = append(xrefTargets, files...)
 		}
 	})
 	// TODO(asmundak): Perhaps emit a rule to output a warning if there were no xrefTargets
diff --git a/cc/cc_preprocess_no_configuration.go b/cc/cc_preprocess_no_configuration.go
new file mode 100644
index 0000000..3d4b077
--- /dev/null
+++ b/cc/cc_preprocess_no_configuration.go
@@ -0,0 +1,115 @@
+// 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 cc
+
+import (
+	"android/soong/android"
+	"slices"
+	"strings"
+)
+
+func init() {
+	RegisterCCPreprocessNoConfiguration(android.InitRegistrationContext)
+}
+
+func RegisterCCPreprocessNoConfiguration(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("cc_preprocess_no_configuration", ccPreprocessNoConfigurationFactory)
+}
+
+// cc_preprocess_no_configuration modules run the c preprocessor on a single input source file.
+// They also have "no configuration", meaning they don't have an arch or os associated with them,
+// they should be thought of as pure textual transformations of the input file. In some cases this
+// is good, in others you might want to do different transformations depending on what arch the
+// result will be compiled in, in which case you can use cc_object instead of this module.
+func ccPreprocessNoConfigurationFactory() android.Module {
+	m := &ccPreprocessNoConfiguration{}
+	m.AddProperties(&m.properties)
+	android.InitAndroidModule(m)
+	return m
+}
+
+type ccPreprocessNoConfigurationProps struct {
+	// Called Srcs for consistency with the other cc module types, but only accepts 1 input source
+	// file.
+	Srcs []string `android:"path"`
+	// The flags to pass to the c compiler. Must include -E in order to enable preprocessing-only
+	// mode.
+	Cflags []string `android:"path"`
+}
+
+type ccPreprocessNoConfiguration struct {
+	android.ModuleBase
+	properties ccPreprocessNoConfigurationProps
+}
+
+func (m *ccPreprocessNoConfiguration) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	srcs := android.PathsForModuleSrc(ctx, m.properties.Srcs)
+	if len(srcs) != 1 {
+		ctx.PropertyErrorf("Srcs", "cc_preprocess_no_configuration only accepts 1 source file, found: %v", srcs.Strings())
+		return
+	}
+	src := srcs[0]
+
+	hasE := false
+	for _, cflag := range m.properties.Cflags {
+		if cflag == "-E" {
+			hasE = true
+			break
+		} else if cflag == "-P" || strings.HasPrefix(cflag, "-D") {
+			// do nothing, allow it
+		} else {
+			ctx.PropertyErrorf("Cflags", "cc_preprocess_no_configuration only allows -D and -P flags, found: %q", cflag)
+			return
+		}
+	}
+	if !hasE {
+		ctx.PropertyErrorf("Cflags", "cc_preprocess_no_configuration must have a -E cflag")
+		return
+	}
+
+	cflags := slices.Clone(m.properties.Cflags)
+
+	// Match behavior of other cc modules:
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/soong/cc/compiler.go;l=422;drc=7297f05ee8cda422ccb32c4af4d9d715d6bac10e
+	cflags = append(cflags, "-I"+ctx.ModuleDir())
+
+	var ccCmd string
+	switch src.Ext() {
+	case ".c":
+		ccCmd = "clang"
+	case ".cpp", ".cc", ".cxx", ".mm":
+		ccCmd = "clang++"
+	default:
+		ctx.PropertyErrorf("srcs", "File %s has unknown extension. Supported extensions: .c, .cpp, .cc, .cxx, .mm", src)
+		return
+	}
+
+	ccCmd = "${config.ClangBin}/" + ccCmd
+
+	outFile := android.PathForModuleOut(ctx, src.Base())
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        cc,
+		Description: ccCmd + " " + src.Rel(),
+		Output:      outFile,
+		Input:       src,
+		Args: map[string]string{
+			"cFlags": strings.Join(cflags, " "),
+			"ccCmd":  ccCmd,
+		},
+	})
+
+	ctx.SetOutputFiles([]android.Path{outFile}, "")
+}
diff --git a/cc/cc_preprocess_no_configuration_test.go b/cc/cc_preprocess_no_configuration_test.go
new file mode 100644
index 0000000..c6eae4c
--- /dev/null
+++ b/cc/cc_preprocess_no_configuration_test.go
@@ -0,0 +1,43 @@
+// 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 cc
+
+import (
+	"android/soong/android"
+	"testing"
+)
+
+func TestCcPreprocessNoConfiguration(t *testing.T) {
+	bp := `
+	cc_preprocess_no_configuration {
+		name: "foo",
+		srcs: ["main.cc"],
+		cflags: ["-E", "-DANDROID"],
+	}
+	`
+
+	fixture := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		android.FixtureRegisterWithContext(RegisterCCPreprocessNoConfiguration),
+		android.FixtureAddTextFile("foo/bar/Android.bp", bp),
+	)
+
+	result := fixture.RunTest(t)
+
+	foo := result.ModuleForTests("foo", "")
+	actual := foo.Rule("cc").Args["cFlags"]
+	expected := "-E -DANDROID -Ifoo/bar"
+	android.AssertStringEquals(t, "cflags should be correct", expected, actual)
+}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 3f3347b..98af7b6 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -20,6 +20,7 @@
 	"reflect"
 	"regexp"
 	"runtime"
+	"slices"
 	"strings"
 	"testing"
 
@@ -39,9 +40,6 @@
 
 var prepareForCcTest = android.GroupFixturePreparers(
 	PrepareForIntegrationTestWithCc,
-	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.VendorApiLevel = StringPtr("202404")
-	}),
 )
 
 var apexVariationName = "apex28"
@@ -381,7 +379,7 @@
 	if !strings.HasSuffix(outputPath, "/main_test") {
 		t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
 	}
-	entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+	entries := android.AndroidMkInfoForTest(t, ctx, module).PrimaryInfo
 	if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:foo/bar/baz") {
 		t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
 			" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
@@ -409,7 +407,7 @@
 	ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
 	module := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon").Module()
 
-	entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+	entries := android.AndroidMkInfoForTest(t, ctx, module).PrimaryInfo
 	compatEntries := entries.EntryMap["LOCAL_COMPATIBILITY_SUITE"]
 	if len(compatEntries) != 2 {
 		t.Errorf("expected two elements in LOCAL_COMPATIBILITY_SUITE. got %d", len(compatEntries))
@@ -441,7 +439,7 @@
 	ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
 	module := ctx.ModuleForTests("main_test_lib", "android_arm_armv7-a-neon_shared").Module()
 
-	entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+	entries := android.AndroidMkInfoForTest(t, ctx, module).PrimaryInfo
 	compatEntries := entries.EntryMap["LOCAL_COMPATIBILITY_SUITE"]
 	if len(compatEntries) != 2 {
 		t.Errorf("expected two elements in LOCAL_COMPATIBILITY_SUITE. got %d", len(compatEntries))
@@ -1007,7 +1005,7 @@
 	android.AssertArrayString(t, "variants for llndk stubs", expected, actual)
 
 	params := result.ModuleForTests("libllndk", "android_vendor_arm_armv7-a-neon_shared").Description("generate stub")
-	android.AssertSame(t, "use Vendor API level for default stubs", "202404", params.Args["apiLevel"])
+	android.AssertSame(t, "use Vendor API level for default stubs", "35", params.Args["apiLevel"])
 
 	checkExportedIncludeDirs := func(module, variant string, expectedSystemDirs []string, expectedDirs ...string) {
 		t.Helper()
@@ -1430,7 +1428,7 @@
 	if !strings.HasSuffix(outputPath, "/main_test") {
 		t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
 	}
-	entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+	entries := android.AndroidMkInfoForTest(t, ctx, module).PrimaryInfo
 	if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:foo/bar/baz") {
 		t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
 			" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
@@ -1504,111 +1502,6 @@
 	}
 }
 
-func TestStubsForLibraryInMultipleApexes(t *testing.T) {
-	t.Parallel()
-	ctx := testCc(t, `
-		cc_library_shared {
-			name: "libFoo",
-			srcs: ["foo.c"],
-			stubs: {
-				symbol_file: "foo.map.txt",
-				versions: ["current"],
-			},
-			apex_available: ["bar", "a1"],
-		}
-
-		cc_library_shared {
-			name: "libBar",
-			srcs: ["bar.c"],
-			shared_libs: ["libFoo"],
-			apex_available: ["a1"],
-		}
-
-		cc_library_shared {
-			name: "libA1",
-			srcs: ["a1.c"],
-			shared_libs: ["libFoo"],
-			apex_available: ["a1"],
-		}
-
-		cc_library_shared {
-			name: "libBarA1",
-			srcs: ["bara1.c"],
-			shared_libs: ["libFoo"],
-			apex_available: ["bar", "a1"],
-		}
-
-		cc_library_shared {
-			name: "libAnyApex",
-			srcs: ["anyApex.c"],
-			shared_libs: ["libFoo"],
-			apex_available: ["//apex_available:anyapex"],
-		}
-
-		cc_library_shared {
-			name: "libBaz",
-			srcs: ["baz.c"],
-			shared_libs: ["libFoo"],
-			apex_available: ["baz"],
-		}
-
-		cc_library_shared {
-			name: "libQux",
-			srcs: ["qux.c"],
-			shared_libs: ["libFoo"],
-			apex_available: ["qux", "bar"],
-		}`)
-
-	variants := ctx.ModuleVariantsForTests("libFoo")
-	expectedVariants := []string{
-		"android_arm64_armv8-a_shared",
-		"android_arm64_armv8-a_shared_current",
-		"android_arm_armv7-a-neon_shared",
-		"android_arm_armv7-a-neon_shared_current",
-	}
-	variantsMismatch := false
-	if len(variants) != len(expectedVariants) {
-		variantsMismatch = true
-	} else {
-		for _, v := range expectedVariants {
-			if !inList(v, variants) {
-				variantsMismatch = false
-			}
-		}
-	}
-	if variantsMismatch {
-		t.Errorf("variants of libFoo expected:\n")
-		for _, v := range expectedVariants {
-			t.Errorf("%q\n", v)
-		}
-		t.Errorf(", but got:\n")
-		for _, v := range variants {
-			t.Errorf("%q\n", v)
-		}
-	}
-
-	linkAgainstFoo := []string{"libBarA1"}
-	linkAgainstFooStubs := []string{"libBar", "libA1", "libBaz", "libQux", "libAnyApex"}
-
-	libFooPath := "libFoo/android_arm64_armv8-a_shared/libFoo.so"
-	for _, lib := range linkAgainstFoo {
-		libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
-		libFlags := libLinkRule.Args["libFlags"]
-		if !strings.Contains(libFlags, libFooPath) {
-			t.Errorf("%q: %q is not found in %q", lib, libFooPath, libFlags)
-		}
-	}
-
-	libFooStubPath := "libFoo/android_arm64_armv8-a_shared_current/libFoo.so"
-	for _, lib := range linkAgainstFooStubs {
-		libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
-		libFlags := libLinkRule.Args["libFlags"]
-		if !strings.Contains(libFlags, libFooStubPath) {
-			t.Errorf("%q: %q is not found in %q", lib, libFooStubPath, libFlags)
-		}
-	}
-}
-
 func TestVersioningMacro(t *testing.T) {
 	t.Parallel()
 	for _, tc := range []struct{ moduleName, expected string }{
@@ -1903,11 +1796,11 @@
 
 	moduleName = "afl_fuzz_static_lib"
 	checkPcGuardFlag(moduleName, variant+"_static", false)
-	checkPcGuardFlag(moduleName, variant+"_static_fuzzer", true)
+	checkPcGuardFlag(moduleName, variant+"_static_fuzzer_afl", true)
 
 	moduleName = "second_static_lib"
 	checkPcGuardFlag(moduleName, variant+"_static", false)
-	checkPcGuardFlag(moduleName, variant+"_static_fuzzer", true)
+	checkPcGuardFlag(moduleName, variant+"_static_fuzzer_afl", true)
 
 	ctx.ModuleForTests("afl_fuzz_shared_lib",
 		"android_arm64_armv8-a_shared").Rule("cc")
@@ -2733,6 +2626,11 @@
 
 func TestIncludeDirectoryOrdering(t *testing.T) {
 	t.Parallel()
+
+	expectedPlatformFlags := []string{
+		"-nostdlibinc",
+	}
+
 	baseExpectedFlags := []string{
 		"${config.ArmThumbCflags}",
 		"${config.ArmCflags}",
@@ -2742,10 +2640,18 @@
 		"${config.ArmToolchainCflags}",
 		"${config.ArmArmv7ANeonCflags}",
 		"${config.ArmGenericCflags}",
+	}
+
+	expectedTargetNDKFlags := []string{
 		"-target",
 		"armv7a-linux-androideabi21",
 	}
 
+	expectedTargetPlatformFlags := []string{
+		"-target",
+		"armv7a-linux-androideabi10000",
+	}
+
 	expectedIncludes := []string{
 		"external/foo/android_arm_export_include_dirs",
 		"external/foo/lib32_export_include_dirs",
@@ -2773,6 +2679,9 @@
 		"external/foo/libarm",
 		"external/foo/lib32",
 		"external/foo/libandroid_arm",
+	}
+
+	expectedNDKSTLIncludes := []string{
 		"defaults/cc/common/ndk_libc++_shared_include_dirs",
 	}
 
@@ -2783,38 +2692,92 @@
 	cstd := []string{"-std=gnu17", "-std=conly"}
 	cppstd := []string{"-std=gnu++20", "-std=cpp", "-fno-rtti"}
 
-	lastIncludes := []string{
-		"out/soong/ndk/sysroot/usr/include",
-		"out/soong/ndk/sysroot/usr/include/arm-linux-androideabi",
+	lastNDKFlags := []string{
+		"--sysroot",
+		"out/soong/ndk/sysroot",
 	}
 
-	combineSlices := func(slices ...[]string) []string {
-		var ret []string
-		for _, s := range slices {
-			ret = append(ret, s...)
-		}
-		return ret
+	lastPlatformIncludes := []string{
+		"${config.CommonGlobalIncludes}",
 	}
 
 	testCases := []struct {
-		name     string
-		src      string
-		expected []string
+		name             string
+		src              string
+		expectedNDK      []string
+		expectedPlatform []string
 	}{
 		{
-			name:     "c",
-			src:      "foo.c",
-			expected: combineSlices(baseExpectedFlags, conly, expectedIncludes, cflags, cstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"}),
+			name: "c",
+			src:  "foo.c",
+			expectedNDK: slices.Concat(
+				baseExpectedFlags,
+				expectedTargetNDKFlags,
+				conly,
+				expectedIncludes,
+				expectedNDKSTLIncludes,
+				cflags,
+				cstd,
+				lastNDKFlags,
+				[]string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"},
+			),
+			expectedPlatform: slices.Concat(
+				expectedPlatformFlags,
+				baseExpectedFlags,
+				expectedTargetPlatformFlags,
+				conly,
+				expectedIncludes,
+				cflags,
+				cstd,
+				lastPlatformIncludes,
+				[]string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"},
+			),
 		},
 		{
-			name:     "cc",
-			src:      "foo.cc",
-			expected: combineSlices(baseExpectedFlags, cppOnly, expectedIncludes, cflags, cppstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"}),
+			name: "cc",
+			src:  "foo.cc",
+			expectedNDK: slices.Concat(
+				baseExpectedFlags,
+				expectedTargetNDKFlags,
+				cppOnly,
+				expectedIncludes,
+				expectedNDKSTLIncludes,
+				cflags,
+				cppstd,
+				lastNDKFlags,
+				[]string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"},
+			),
+			expectedPlatform: slices.Concat(
+				expectedPlatformFlags,
+				baseExpectedFlags,
+				expectedTargetPlatformFlags,
+				cppOnly,
+				expectedIncludes,
+				cflags,
+				cppstd,
+				lastPlatformIncludes,
+				[]string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"},
+			),
 		},
 		{
-			name:     "assemble",
-			src:      "foo.s",
-			expected: combineSlices(baseExpectedFlags, []string{"${config.CommonGlobalAsflags}"}, expectedIncludes, lastIncludes),
+			name: "assemble",
+			src:  "foo.s",
+			expectedNDK: slices.Concat(
+				baseExpectedFlags,
+				expectedTargetNDKFlags,
+				[]string{"${config.CommonGlobalAsflags}"},
+				expectedIncludes,
+				expectedNDKSTLIncludes,
+				lastNDKFlags,
+			),
+			expectedPlatform: slices.Concat(
+				expectedPlatformFlags,
+				baseExpectedFlags,
+				expectedTargetPlatformFlags,
+				[]string{"${config.CommonGlobalAsflags}"},
+				expectedIncludes,
+				lastPlatformIncludes,
+			),
 		},
 	}
 
@@ -2909,25 +2872,34 @@
 		`, lib, lib)
 			}
 
-			ctx := android.GroupFixturePreparers(
-				PrepareForIntegrationTestWithCc,
-				android.FixtureAddTextFile("external/foo/Android.bp", bp),
-			).RunTest(t)
-			cflags := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_sdk_static").Output("obj/external/foo/foo.o").Args["cFlags"]
+			runTest := func(t *testing.T, variant string, expected []string) {
+				ctx := android.GroupFixturePreparers(
+					PrepareForIntegrationTestWithCc,
+					android.FixtureAddTextFile("external/foo/Android.bp", bp),
+				).RunTest(t)
+				cflags := ctx.ModuleForTests("libfoo", variant).Output("obj/external/foo/foo.o").Args["cFlags"]
 
-			var includes []string
-			flags := strings.Split(cflags, " ")
-			for _, flag := range flags {
-				if strings.HasPrefix(flag, "-I") {
-					includes = append(includes, strings.TrimPrefix(flag, "-I"))
-				} else if flag == "-isystem" {
-					// skip isystem, include next
-				} else if len(flag) > 0 {
-					includes = append(includes, flag)
+				var includes []string
+				flags := strings.Split(cflags, " ")
+				for _, flag := range flags {
+					if strings.HasPrefix(flag, "-I") {
+						includes = append(includes, strings.TrimPrefix(flag, "-I"))
+					} else if flag == "-isystem" {
+						// skip isystem, include next
+					} else if len(flag) > 0 {
+						includes = append(includes, flag)
+					}
 				}
+
+				android.AssertArrayString(t, "includes", expected, includes)
 			}
 
-			android.AssertArrayString(t, "includes", tc.expected, includes)
+			t.Run("platform", func(t *testing.T) {
+				runTest(t, "android_arm_armv7-a-neon_static", tc.expectedPlatform)
+			})
+			t.Run("ndk", func(t *testing.T) {
+				runTest(t, "android_arm_armv7-a-neon_sdk_static", tc.expectedNDK)
+			})
 		})
 	}
 
@@ -3178,7 +3150,7 @@
 	testDepWithVariant("product")
 }
 
-func TestVendorSdkVersion(t *testing.T) {
+func TestVendorOrProductVariantUsesPlatformSdkVersionAsDefault(t *testing.T) {
 	t.Parallel()
 
 	bp := `
@@ -3186,31 +3158,29 @@
 			name: "libfoo",
 			srcs: ["libfoo.cc"],
 			vendor_available: true,
+			product_available: true,
 		}
 
 		cc_library {
 			name: "libbar",
 			srcs: ["libbar.cc"],
 			vendor_available: true,
+			product_available: true,
 			min_sdk_version: "29",
 		}
 	`
 
 	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-	testSdkVersionFlag := func(module, version string) {
-		flags := ctx.ModuleForTests(module, "android_vendor_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
-		android.AssertStringDoesContain(t, "min sdk version", flags, "-target aarch64-linux-android"+version)
+	testSdkVersionFlag := func(module, variant, version string) {
+		flags := ctx.ModuleForTests(module, "android_"+variant+"_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+		android.AssertStringDoesContain(t, "target SDK version", flags, "-target aarch64-linux-android"+version)
 	}
 
-	testSdkVersionFlag("libfoo", "10000")
-	testSdkVersionFlag("libbar", "29")
-
-	ctx = android.GroupFixturePreparers(
-		prepareForCcTest,
-		android.PrepareForTestWithBuildFlag("RELEASE_BOARD_API_LEVEL_FROZEN", "true"),
-	).RunTestWithBp(t, bp)
-	testSdkVersionFlag("libfoo", "30")
-	testSdkVersionFlag("libbar", "29")
+	testSdkVersionFlag("libfoo", "vendor", "30")
+	testSdkVersionFlag("libfoo", "product", "30")
+	// target SDK version can be set explicitly with min_sdk_version
+	testSdkVersionFlag("libbar", "vendor", "29")
+	testSdkVersionFlag("libbar", "product", "29")
 }
 
 func TestClangVerify(t *testing.T) {
@@ -3241,3 +3211,20 @@
 		t.Errorf("expected %q in cflags, got %q", "-Xclang -verify", cFlags_cv)
 	}
 }
+
+func TestCheckConflictingExplicitVersions(t *testing.T) {
+	PrepareForIntegrationTestWithCc.
+		ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+			`shared_libs: duplicate shared libraries with different explicit versions: "libbar" and "libbar#impl"`,
+		)).
+		RunTestWithBp(t, `
+			cc_library {
+				name: "libfoo",
+				shared_libs: ["libbar", "libbar#impl"],
+			}
+
+			cc_library {
+				name: "libbar",
+			}
+		`)
+}
diff --git a/cc/check.go b/cc/check.go
index fa1926d..8e2844f 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -40,6 +40,8 @@
 			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use native_coverage instead", flag)
 		} else if flag == "-fwhole-program-vtables" {
 			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use whole_program_vtables instead", flag)
+		} else if flag == "-gsplit-dwarf" {
+			ctx.PropertyErrorf(prop, "Bad flag: `%s`, soong cannot track dependencies to split dwarf debuginfo", flag)
 		} else if flag == "-fno-integrated-as" {
 			ctx.PropertyErrorf("Bad flag: `%s` is disallowed as it may invoke the `as` from the build host", flag)
 		} else if flag == "-Weverything" {
diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go
index 61fa46d..f553f27 100644
--- a/cc/cmake_snapshot.go
+++ b/cc/cmake_snapshot.go
@@ -484,7 +484,7 @@
 	// Packaging all make files into the zip file
 	makefilesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_makefiles.rsp")
 	zipCmd.
-		FlagWithArg("-C ", android.PathForModuleGen(ctx).OutputPath.String()).
+		FlagWithArg("-C ", android.PathForModuleGen(ctx).String()).
 		FlagWithRspFileInputList("-r ", makefilesRspFile, makefilesList)
 
 	// Packaging all prebuilts into the zip file
@@ -533,6 +533,8 @@
 		return "test"
 	case *benchmarkDecorator:
 		return "test"
+	case *objectLinker:
+		return "object"
 	}
 	panic(fmt.Sprintf("Unexpected module type: %T", m.linker))
 }
diff --git a/cc/compdb.go b/cc/compdb.go
index b33f490..4132e09 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -146,6 +146,8 @@
 		isAsm = false
 		isCpp = true
 		clangPath = cxxPath
+	case ".o":
+		return nil
 	default:
 		log.Print("Unknown file extension " + src.Ext() + " on file " + src.String())
 		isAsm = true
@@ -185,6 +187,10 @@
 	}
 	for _, src := range srcs {
 		if _, ok := builds[src.String()]; !ok {
+			args := getArguments(src, ctx, ccModule, ccPath, cxxPath)
+			if args == nil {
+				continue
+			}
 			builds[src.String()] = compDbEntry{
 				Directory: android.AbsSrcDirForExistingUseCases(),
 				Arguments: getArguments(src, ctx, ccModule, ccPath, cxxPath),
diff --git a/cc/compiler.go b/cc/compiler.go
index 396ec88..91f107c 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -78,11 +78,11 @@
 	// If possible, don't use this.  If adding paths from the current directory use
 	// local_include_dirs, if adding paths from other modules use export_include_dirs in
 	// that module.
-	Include_dirs []string `android:"arch_variant,variant_prepend"`
+	Include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of directories relative to the Blueprints file that will
 	// be added to the include path using -I
-	Local_include_dirs []string `android:"arch_variant,variant_prepend"`
+	Local_include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// Add the directory containing the Android.bp file to the list of include
 	// directories. Defaults to true.
@@ -100,6 +100,11 @@
 	// of genrule modules.
 	Generated_headers proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
+	// Same as generated_headers, but the dependencies will be added based on the first supported
+	// arch variant and the device os variant. This can be useful for creating a host tool that
+	// embeds a copy of a device tool, that it then extracts and pushes to a device at runtime.
+	Device_first_generated_headers proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
+
 	// pass -frtti instead of -fno-rtti
 	Rtti *bool `android:"arch_variant"`
 
@@ -228,9 +233,6 @@
 		Static *bool `android:"arch_variant"`
 	} `android:"arch_variant"`
 
-	// Stores the original list of source files before being cleared by library reuse
-	OriginalSrcs proptools.Configurable[[]string] `blueprint:"mutated"`
-
 	// Build and link with OpenMP
 	Openmp *bool `android:"arch_variant"`
 }
@@ -297,6 +299,7 @@
 	deps.GeneratedSources = append(deps.GeneratedSources, compiler.Properties.Generated_sources...)
 	deps.GeneratedSources = removeListFromList(deps.GeneratedSources, compiler.Properties.Exclude_generated_sources)
 	deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers.GetOrDefault(ctx, nil)...)
+	deps.DeviceFirstGeneratedHeaders = append(deps.DeviceFirstGeneratedHeaders, compiler.Properties.Device_first_generated_headers.GetOrDefault(ctx, nil)...)
 	deps.AidlLibs = append(deps.AidlLibs, compiler.Properties.Aidl.Libs...)
 
 	android.ProtoDeps(ctx, &compiler.Proto)
@@ -363,10 +366,20 @@
 	tc := ctx.toolchain()
 	modulePath := ctx.ModuleDir()
 
-	srcs := compiler.Properties.Srcs.GetOrDefault(ctx, nil)
-	exclude_srcs := compiler.Properties.Exclude_srcs.GetOrDefault(ctx, nil)
-	compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs)
-	compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...)
+	reuseObjs := false
+	if len(ctx.GetDirectDepsWithTag(reuseObjTag)) > 0 {
+		reuseObjs = true
+	}
+
+	// If a reuseObjTag dependency exists then this module is reusing the objects (generally the shared variant
+	// reusing objects from the static variant), and doesn't need to compile any sources of its own.
+	var srcs []string
+	if !reuseObjs {
+		srcs = compiler.Properties.Srcs.GetOrDefault(ctx, nil)
+		exclude_srcs := compiler.Properties.Exclude_srcs.GetOrDefault(ctx, nil)
+		compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs)
+		compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...)
+	}
 
 	cflags := compiler.Properties.Cflags.GetOrDefault(ctx, nil)
 	cppflags := compiler.Properties.Cppflags.GetOrDefault(ctx, nil)
@@ -398,13 +411,13 @@
 	}
 
 	// Include dir cflags
-	localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs)
+	localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs.GetOrDefault(ctx, nil))
 	if len(localIncludeDirs) > 0 {
 		f := includeDirsToFlags(localIncludeDirs)
 		flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
 		flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
 	}
-	rootIncludeDirs := android.PathsForSource(ctx, compiler.Properties.Include_dirs)
+	rootIncludeDirs := android.PathsForSource(ctx, compiler.Properties.Include_dirs.GetOrDefault(ctx, nil))
 	if len(rootIncludeDirs) > 0 {
 		f := includeDirsToFlags(rootIncludeDirs)
 		flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
@@ -423,32 +436,34 @@
 	}
 
 	if ctx.useSdk() {
-		// TODO: Switch to --sysroot.
 		// The NDK headers are installed to a common sysroot. While a more
 		// typical Soong approach would be to only make the headers for the
 		// library you're using available, we're trying to emulate the NDK
 		// behavior here, and the NDK always has all the NDK headers available.
 		flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
-			"-isystem "+getCurrentIncludePath(ctx).String(),
-			"-isystem "+getCurrentIncludePath(ctx).Join(ctx, config.NDKTriple(tc)).String())
+			"--sysroot "+getNdkSysrootBase(ctx).String())
+	} else if ctx.Device() {
+		flags.Global.CommonFlags = append(flags.Global.CFlags, "-nostdlibinc")
 	}
 
 	if ctx.InVendorOrProduct() {
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VNDK__")
 		if ctx.inVendor() {
 			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR__")
-
-			vendorApiLevel := ctx.Config().VendorApiLevel()
-			if vendorApiLevel == "" {
-				// TODO(b/314036847): This is a fallback for UDC targets.
-				// This must be a build failure when UDC is no longer built
-				// from this source tree.
-				vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
-			}
-			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel)
 		} else if ctx.inProduct() {
 			flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_PRODUCT__")
 		}
+
+		// Define __ANDROID_VENDOR_API__ for both product and vendor variants
+		// because they both use the same LLNDK libraries.
+		vendorApiLevel := ctx.Config().VendorApiLevel()
+		if vendorApiLevel == "" {
+			// TODO(b/314036847): This is a fallback for UDC targets.
+			// This must be a build failure when UDC is no longer built
+			// from this source tree.
+			vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
+		}
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel)
 	}
 
 	if ctx.inRecovery() {
@@ -670,10 +685,10 @@
 	if len(srcs) > 0 {
 		module := ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName()
 		if inList("-Wno-error", flags.Local.CFlags) || inList("-Wno-error", flags.Local.CppFlags) {
-			addToModuleList(ctx, modulesUsingWnoErrorKey, module)
+			ctx.getOrCreateMakeVarsInfo().UsingWnoError = module
 		} else if !inList("-Werror", flags.Local.CFlags) && !inList("-Werror", flags.Local.CppFlags) {
 			if warningsAreAllowed(ctx.ModuleDir()) {
-				addToModuleList(ctx, modulesWarningsAllowedKey, module)
+				ctx.getOrCreateMakeVarsInfo().WarningsAllowed = module
 			} else {
 				flags.Local.CFlags = append([]string{"-Werror"}, flags.Local.CFlags...)
 			}
@@ -686,7 +701,9 @@
 
 	if ctx.optimizeForSize() {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-Oz")
-		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-enable-ml-inliner=release")
+		if !ctx.Config().IsEnvFalse("THINLTO_USE_MLGO") {
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-enable-ml-inliner=release")
+		}
 	}
 
 	// Exclude directories from manual binder interface allowed list.
@@ -719,11 +736,6 @@
 			return true
 		}
 	}
-	for _, src := range compiler.Properties.OriginalSrcs.GetOrDefault(ctx, nil) {
-		if filepath.Ext(src) == ext {
-			return true
-		}
-	}
 
 	return false
 }
@@ -795,7 +807,7 @@
 type RustBindgenClangProperties struct {
 	// list of directories relative to the Blueprints file that will
 	// be added to the include path using -I
-	Local_include_dirs []string `android:"arch_variant,variant_prepend"`
+	Local_include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of static libraries that provide headers for this binding.
 	Static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
diff --git a/cc/config/global.go b/cc/config/global.go
index 9d3de6d..b19682d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -72,6 +72,9 @@
 
 		// Warnings disabled by default.
 
+		// We should encourage use of C23 features even when the whole project
+		// isn't C23-ready.
+		"-Wno-c23-extensions",
 		// Designated initializer syntax is recommended by the Google C++ style
 		// and is OK to use even if not formally supported by the chosen C++
 		// version.
@@ -172,7 +175,6 @@
 		"-Werror=address",
 		"-Werror=sequence-point",
 		"-Werror=format-security",
-		"-nostdlibinc",
 	}
 
 	commonGlobalLldflags = []string{
@@ -236,12 +238,14 @@
 	// opting into the warning.
 	noOverrideGlobalCflags = []string{
 		"-Werror=bool-operation",
+		"-Werror=dangling",
 		"-Werror=format-insufficient-args",
 		"-Werror=implicit-int-float-conversion",
 		"-Werror=int-in-bool-context",
 		"-Werror=int-to-pointer-cast",
 		"-Werror=pointer-to-int-cast",
 		"-Werror=xor-used-as-pow",
+		"-Wimplicit-int-float-conversion",
 		// http://b/161386391 for -Wno-void-pointer-to-enum-cast
 		"-Wno-void-pointer-to-enum-cast",
 		// http://b/161386391 for -Wno-void-pointer-to-int-cast
@@ -288,14 +292,13 @@
 		"-Wno-error=deprecated",          // in external/googletest/googletest
 		// Disabling until the warning is fixed in libc++abi header files b/366180429
 		"-Wno-deprecated-dynamic-exception-spec",
-		// New warnings to be fixed after clang-r475365
-		"-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",
 
 		// Allow using VLA CXX extension.
 		"-Wno-vla-cxx-extension",
+		"-Wno-cast-function-type-mismatch",
 	}
 
 	noOverride64GlobalCflags = []string{}
@@ -384,7 +387,7 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r530567"
+	ClangDefaultVersion      = "clang-r536225"
 	ClangDefaultShortVersion = "19"
 
 	// Directories with warnings from Android.bp files.
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 5aa2a7e..e7ac038 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -40,6 +40,9 @@
 			"-march=x86-64",
 		},
 
+		"alderlake": []string{
+			"-march=alderlake",
+		},
 		"broadwell": []string{
 			"-march=broadwell",
 		},
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 4b0041c..a92881d 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -42,6 +42,9 @@
 		"x86_64": []string{
 			"-march=prescott",
 		},
+		"alderlake": []string{
+			"-march=alderlake",
+		},
 		"atom": []string{
 			"-march=atom",
 		},
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 3f21bc6..8a974c0 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -57,38 +57,76 @@
 	return []interface{}{&fuzzer.Properties}
 }
 
-func fuzzMutatorDeps(mctx android.BottomUpMutatorContext) {
-	currentModule, ok := mctx.Module().(*Module)
+// fuzzTransitionMutator creates variants to propagate the FuzzFramework value down to dependencies.
+type fuzzTransitionMutator struct{}
+
+func (f *fuzzTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	return []string{""}
+}
+
+func (f *fuzzTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	m, ok := ctx.Module().(*Module)
+	if !ok {
+		return ""
+	}
+
+	if m.fuzzer == nil {
+		return ""
+	}
+
+	if m.sanitize == nil {
+		return ""
+	}
+
+	isFuzzerPointer := m.sanitize.getSanitizerBoolPtr(Fuzzer)
+	if isFuzzerPointer == nil || !*isFuzzerPointer {
+		return ""
+	}
+
+	if m.fuzzer.Properties.FuzzFramework != "" {
+		return m.fuzzer.Properties.FuzzFramework.Variant()
+	}
+
+	return sourceVariation
+}
+
+func (f *fuzzTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	m, ok := ctx.Module().(*Module)
+	if !ok {
+		return ""
+	}
+
+	if m.fuzzer == nil {
+		return ""
+	}
+
+	if m.sanitize == nil {
+		return ""
+	}
+
+	isFuzzerPointer := m.sanitize.getSanitizerBoolPtr(Fuzzer)
+	if isFuzzerPointer == nil || !*isFuzzerPointer {
+		return ""
+	}
+
+	return incomingVariation
+}
+
+func (f *fuzzTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	m, ok := ctx.Module().(*Module)
 	if !ok {
 		return
 	}
 
-	if currentModule.fuzzer == nil {
+	if m.fuzzer == nil {
 		return
 	}
 
-	mctx.WalkDeps(func(child android.Module, parent android.Module) bool {
-		c, ok := child.(*Module)
-		if !ok {
-			return false
-		}
-
-		if c.sanitize == nil {
-			return false
-		}
-
-		isFuzzerPointer := c.sanitize.getSanitizerBoolPtr(Fuzzer)
-		if isFuzzerPointer == nil || !*isFuzzerPointer {
-			return false
-		}
-
-		if c.fuzzer == nil {
-			return false
-		}
-
-		c.fuzzer.Properties.FuzzFramework = currentModule.fuzzer.Properties.FuzzFramework
-		return true
-	})
+	if variation != "" {
+		m.fuzzer.Properties.FuzzFramework = fuzz.FrameworkFromVariant(variation)
+		m.SetHideFromMake()
+		m.SetPreventInstall()
+	}
 }
 
 // cc_fuzz creates a host/device fuzzer binary. Host binaries can be found at
@@ -309,6 +347,7 @@
 
 func PackageFuzzModule(ctx android.ModuleContext, fuzzPackagedModule fuzz.FuzzPackagedModule, pctx android.PackageContext) fuzz.FuzzPackagedModule {
 	fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Corpus)
+	fuzzPackagedModule.Corpus = append(fuzzPackagedModule.Corpus, android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Device_common_corpus)...)
 
 	fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzzPackagedModule.FuzzProperties.Data)
 
diff --git a/cc/generated_cc_library.go b/cc/generated_cc_library.go
index b1084e4..709586b 100644
--- a/cc/generated_cc_library.go
+++ b/cc/generated_cc_library.go
@@ -18,7 +18,7 @@
 	"android/soong/android"
 )
 
-func GeneratedCcLibraryModuleFactory(moduleName string, callbacks Generator) android.Module {
+func GeneratedCcLibraryModuleFactory(callbacks Generator) android.Module {
 	module, _ := NewLibrary(android.HostAndDeviceSupported)
 
 	// Can be used as both a static and a shared library.
diff --git a/cc/genrule.go b/cc/genrule.go
index fe3b127..bd6c5f1 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -77,41 +77,41 @@
 
 var _ android.ImageInterface = (*GenruleExtraProperties)(nil)
 
-func (g *GenruleExtraProperties) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+func (g *GenruleExtraProperties) ImageMutatorBegin(ctx android.ImageInterfaceContext) {}
 
-func (g *GenruleExtraProperties) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+func (g *GenruleExtraProperties) VendorVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return Bool(g.Vendor_available) || Bool(g.Odm_available) || ctx.SocSpecific() || ctx.DeviceSpecific()
 }
 
-func (g *GenruleExtraProperties) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+func (g *GenruleExtraProperties) ProductVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return Bool(g.Product_available) || ctx.ProductSpecific()
 }
 
-func (g *GenruleExtraProperties) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+func (g *GenruleExtraProperties) CoreVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return !(ctx.SocSpecific() || ctx.DeviceSpecific() || ctx.ProductSpecific())
 }
 
-func (g *GenruleExtraProperties) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (g *GenruleExtraProperties) RamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return Bool(g.Ramdisk_available)
 }
 
-func (g *GenruleExtraProperties) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (g *GenruleExtraProperties) VendorRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return Bool(g.Vendor_ramdisk_available)
 }
 
-func (g *GenruleExtraProperties) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (g *GenruleExtraProperties) DebugRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	// If the build is using a snapshot, the recovery variant under AOSP directories
 	// is not needed.
 	return Bool(g.Recovery_available)
 }
 
-func (g *GenruleExtraProperties) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+func (g *GenruleExtraProperties) ExtraImageVariations(ctx android.ImageInterfaceContext) []string {
 	return nil
 }
 
-func (g *GenruleExtraProperties) SetImageVariation(ctx android.BaseModuleContext, variation string) {
+func (g *GenruleExtraProperties) SetImageVariation(ctx android.ImageInterfaceContext, variation string) {
 }
diff --git a/cc/image.go b/cc/image.go
index 7594a08..9766af3 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -177,10 +177,7 @@
 	IsSnapshotPrebuilt() bool
 
 	// SnapshotVersion returns the snapshot version for this module.
-	SnapshotVersion(mctx android.BaseModuleContext) string
-
-	// SdkVersion returns the SDK version for this module.
-	SdkVersion() string
+	SnapshotVersion(mctx android.ImageInterfaceContext) string
 
 	// ExtraVariants returns the list of extra variants this module requires.
 	ExtraVariants() []string
@@ -209,7 +206,7 @@
 
 var _ ImageMutatableModule = (*Module)(nil)
 
-func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
+func (m *Module) ImageMutatorBegin(mctx android.ImageInterfaceContext) {
 	MutateImage(mctx, m)
 }
 
@@ -273,7 +270,7 @@
 	m.Properties.VendorVariantNeeded = b
 }
 
-func (m *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
+func (m *Module) SnapshotVersion(mctx android.ImageInterfaceContext) string {
 	if snapshot, ok := m.linker.(SnapshotInterface); ok {
 		return snapshot.Version()
 	} else {
@@ -291,7 +288,7 @@
 }
 
 // MutateImage handles common image mutations for ImageMutatableModule interfaces.
-func MutateImage(mctx android.BaseModuleContext, m ImageMutatableModule) {
+func MutateImage(mctx android.ImageInterfaceContext, m ImageMutatableModule) {
 	// Validation check
 	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
 	productSpecific := mctx.ProductSpecific()
@@ -370,7 +367,7 @@
 		if m.HasProductVariant() {
 			productVariantNeeded = true
 		}
-	} else if vendorSpecific && m.SdkVersion() == "" {
+	} else if vendorSpecific {
 		// This will be available in /vendor (or /odm) only
 		vendorVariantNeeded = true
 	} else {
@@ -380,7 +377,7 @@
 		coreVariantNeeded = true
 	}
 
-	if coreVariantNeeded && productSpecific && m.SdkVersion() == "" {
+	if coreVariantNeeded && productSpecific {
 		// The module has "product_specific: true" that does not create core variant.
 		coreVariantNeeded = false
 		productVariantNeeded = true
@@ -431,35 +428,35 @@
 	}
 }
 
-func (c *Module) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+func (c *Module) VendorVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return c.Properties.VendorVariantNeeded
 }
 
-func (c *Module) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+func (c *Module) ProductVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return c.Properties.ProductVariantNeeded
 }
 
-func (c *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+func (c *Module) CoreVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return c.Properties.CoreVariantNeeded
 }
 
-func (c *Module) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (c *Module) RamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return c.Properties.RamdiskVariantNeeded
 }
 
-func (c *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (c *Module) VendorRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return c.Properties.VendorRamdiskVariantNeeded
 }
 
-func (c *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (c *Module) DebugRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+func (c *Module) RecoveryVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return c.Properties.RecoveryVariantNeeded
 }
 
-func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+func (c *Module) ExtraImageVariations(ctx android.ImageInterfaceContext) []string {
 	return c.Properties.ExtraVersionedImageVariations
 }
 
@@ -513,7 +510,7 @@
 	}
 }
 
-func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string) {
+func (c *Module) SetImageVariation(ctx android.ImageInterfaceContext, variant string) {
 	if variant == android.RamdiskVariation {
 		c.MakeAsPlatform()
 		squashRamdiskSrcs(c)
diff --git a/cc/library.go b/cc/library.go
index c4ec88c..654f66b 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -27,6 +27,7 @@
 	"android/soong/android"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
@@ -555,8 +556,7 @@
 	return flags
 }
 
-func (library *libraryDecorator) getHeaderAbiCheckerProperties(ctx android.BaseModuleContext) headerAbiCheckerProperties {
-	m := ctx.Module().(*Module)
+func (library *libraryDecorator) getHeaderAbiCheckerProperties(m *Module) headerAbiCheckerProperties {
 	variantProps := &library.Properties.Target.Platform.Header_abi_checker
 	if m.InVendor() {
 		variantProps = &library.Properties.Target.Vendor.Header_abi_checker
@@ -566,23 +566,23 @@
 	props := library.Properties.Header_abi_checker
 	err := proptools.AppendProperties(&props, variantProps, nil)
 	if err != nil {
-		ctx.ModuleErrorf("Cannot merge headerAbiCheckerProperties: %s", err.Error())
+		panic(fmt.Errorf("Cannot merge headerAbiCheckerProperties: %s", err.Error()))
 	}
 	return props
 }
 
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
 	if ctx.IsLlndk() {
-		vendorApiLevel := ctx.Config().VendorApiLevel()
-		if vendorApiLevel == "" {
-			// TODO(b/321892570): Some tests relying on old fixtures which
-			// doesn't set vendorApiLevel. Needs to fix them.
-			vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
+		// Get the matching SDK version for the vendor API level.
+		version, err := android.GetSdkVersionForVendorApiLevel(ctx.Config().VendorApiLevel())
+		if err != nil {
+			panic(err)
 		}
+
 		// This is the vendor variant of an LLNDK library, build the LLNDK stubs.
 		nativeAbiResult := parseNativeAbiDefinition(ctx,
 			String(library.Properties.Llndk.Symbol_file),
-			android.ApiLevelOrPanic(ctx, vendorApiLevel), "--llndk")
+			nativeClampedApiLevel(ctx, version), "--llndk")
 		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
 		if !Bool(library.Properties.Llndk.Unversioned) {
 			library.versionScriptPath = android.OptionalPathForPath(
@@ -669,7 +669,7 @@
 	// However, having this distinction helps guard accidental
 	// promotion or demotion of API and also helps the API review process b/191371676
 	var flag string
-	if ctx.Module().(android.ApexModule).NotInPlatform() {
+	if ctx.notInPlatform() {
 		flag = "--apex"
 	} else {
 		flag = "--systemapi"
@@ -725,7 +725,7 @@
 	setShared()
 
 	// Gets the ABI properties for vendor, product, or platform variant
-	getHeaderAbiCheckerProperties(ctx android.BaseModuleContext) headerAbiCheckerProperties
+	getHeaderAbiCheckerProperties(m *Module) headerAbiCheckerProperties
 
 	// Write LOCAL_ADDITIONAL_DEPENDENCIES for ABI diff
 	androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer)
@@ -753,6 +753,7 @@
 	hasLLNDKStubs() bool
 	hasLLNDKHeaders() bool
 	hasVendorPublicLibrary() bool
+	isLLNDKMovedToApex() bool
 }
 
 var _ libraryInterface = (*libraryDecorator)(nil)
@@ -1025,7 +1026,7 @@
 			Objects:                      library.objects,
 			WholeStaticLibsFromPrebuilts: library.wholeStaticLibsFromPrebuilts,
 
-			TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).
+			TransitiveStaticLibrariesForOrdering: depset.NewBuilder[android.Path](depset.TOPOLOGICAL).
 				Direct(outputFile).
 				Transitive(deps.TranstiveStaticLibrariesForOrdering).
 				Build(),
@@ -1190,7 +1191,7 @@
 	library.coverageOutputFile = transformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
 	library.linkSAbiDumpFiles(ctx, deps, objs, fileName, unstrippedOutputFile)
 
-	var transitiveStaticLibrariesForOrdering *android.DepSet[android.Path]
+	var transitiveStaticLibrariesForOrdering depset.DepSet[android.Path]
 	if static := ctx.GetDirectDepsWithTag(staticVariantTag); len(static) > 0 {
 		s, _ := android.OtherModuleProvider(ctx, static[0], StaticLibraryInfoProvider)
 		transitiveStaticLibrariesForOrdering = s.TransitiveStaticLibrariesForOrdering
@@ -1201,6 +1202,7 @@
 		SharedLibrary:                        unstrippedOutputFile,
 		TransitiveStaticLibrariesForOrdering: transitiveStaticLibrariesForOrdering,
 		Target:                               ctx.Target(),
+		IsStubs:                              library.buildStubs(),
 	})
 
 	addStubDependencyProviders(ctx)
@@ -1296,15 +1298,14 @@
 func (library *libraryDecorator) linkLlndkSAbiDumpFiles(ctx ModuleContext,
 	deps PathDeps, sAbiDumpFiles android.Paths, soFile android.Path, libFileName string,
 	excludeSymbolVersions, excludeSymbolTags []string,
-	vendorApiLevel string) android.Path {
-	// NDK symbols in version 34 are LLNDK symbols. Those in version 35 are not.
+	sdkVersionForVendorApiLevel string) android.Path {
 	return transformDumpToLinkedDump(ctx,
 		sAbiDumpFiles, soFile, libFileName+".llndk",
 		library.llndkIncludeDirsForAbiCheck(ctx, deps),
 		android.OptionalPathForModuleSrc(ctx, library.Properties.Llndk.Symbol_file),
 		append([]string{"*_PLATFORM", "*_PRIVATE"}, excludeSymbolVersions...),
 		append([]string{"platform-only"}, excludeSymbolTags...),
-		[]string{"llndk=" + vendorApiLevel}, "34", true /* isLlndk */)
+		[]string{"llndk"}, sdkVersionForVendorApiLevel)
 }
 
 func (library *libraryDecorator) linkApexSAbiDumpFiles(ctx ModuleContext,
@@ -1317,7 +1318,7 @@
 		android.OptionalPathForModuleSrc(ctx, library.Properties.Stubs.Symbol_file),
 		append([]string{"*_PLATFORM", "*_PRIVATE"}, excludeSymbolVersions...),
 		append([]string{"platform-only"}, excludeSymbolTags...),
-		[]string{"apex", "systemapi"}, sdkVersion, false /* isLlndk */)
+		[]string{"apex", "systemapi"}, sdkVersion)
 }
 
 func getRefAbiDumpFile(ctx android.ModuleInstallPathContext,
@@ -1372,7 +1373,7 @@
 	sourceVersion, errorMessage string) {
 
 	extraFlags := []string{"-target-version", sourceVersion}
-	headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
+	headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx.Module().(*Module))
 	if Bool(headerAbiChecker.Check_all_apis) {
 		extraFlags = append(extraFlags, "-check-all-apis")
 	} else {
@@ -1444,7 +1445,7 @@
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathDeps, objs Objects, fileName string, soFile android.Path) {
 	if library.sabi.shouldCreateSourceAbiDump() {
 		exportedIncludeDirs := library.exportedIncludeDirsForAbiCheck(ctx)
-		headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
+		headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx.Module().(*Module))
 		currSdkVersion := currRefAbiDumpSdkVersion(ctx)
 		currVendorVersion := ctx.Config().VendorApiLevel()
 
@@ -1455,22 +1456,27 @@
 			android.OptionalPathForModuleSrc(ctx, library.symbolFileForAbiCheck(ctx)),
 			headerAbiChecker.Exclude_symbol_versions,
 			headerAbiChecker.Exclude_symbol_tags,
-			[]string{} /* includeSymbolTags */, currSdkVersion, false /* isLlndk */)
+			[]string{} /* includeSymbolTags */, currSdkVersion)
 
 		var llndkDump, apexVariantDump android.Path
-		tags := classifySourceAbiDump(ctx)
+		tags := classifySourceAbiDump(ctx.Module().(*Module))
 		optInTags := []lsdumpTag{}
 		for _, tag := range tags {
 			if tag == llndkLsdumpTag && currVendorVersion != "" {
 				if llndkDump == nil {
+					sdkVersion, err := android.GetSdkVersionForVendorApiLevel(currVendorVersion)
+					if err != nil {
+						ctx.ModuleErrorf("Cannot create %s llndk dump: %s", fileName, err)
+						return
+					}
 					// TODO(b/323447559): Evaluate if replacing sAbiDumpFiles with implDump is faster
 					llndkDump = library.linkLlndkSAbiDumpFiles(ctx,
 						deps, objs.sAbiDumpFiles, soFile, fileName,
 						headerAbiChecker.Exclude_symbol_versions,
 						headerAbiChecker.Exclude_symbol_tags,
-						currVendorVersion)
+						nativeClampedApiLevel(ctx, sdkVersion).String())
 				}
-				addLsdumpPath(string(tag) + ":" + llndkDump.String())
+				addLsdumpPath(ctx.Config(), string(tag)+":"+llndkDump.String())
 			} else if tag == apexLsdumpTag {
 				if apexVariantDump == nil {
 					apexVariantDump = library.linkApexSAbiDumpFiles(ctx,
@@ -1479,12 +1485,12 @@
 						headerAbiChecker.Exclude_symbol_tags,
 						currSdkVersion)
 				}
-				addLsdumpPath(string(tag) + ":" + apexVariantDump.String())
+				addLsdumpPath(ctx.Config(), string(tag)+":"+apexVariantDump.String())
 			} else {
 				if tag.dirName() == "" {
 					optInTags = append(optInTags, tag)
 				}
-				addLsdumpPath(string(tag) + ":" + implDump.String())
+				addLsdumpPath(ctx.Config(), string(tag)+":"+implDump.String())
 			}
 		}
 
@@ -1763,21 +1769,17 @@
 
 func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) {
 	if library.shared() {
-		if library.hasStubsVariants() && !ctx.Host() && ctx.directlyInAnyApex() {
+		translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
+		if library.hasStubsVariants() && !ctx.Host() && !ctx.isSdkVariant() &&
+			InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() &&
+			!translatedArch && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() {
 			// Bionic libraries (e.g. libc.so) is installed to the bootstrap subdirectory.
 			// The original path becomes a symlink to the corresponding file in the
 			// runtime APEX.
-			translatedArch := ctx.Target().NativeBridge == android.NativeBridgeEnabled
-			if InstallToBootstrap(ctx.baseModuleName(), ctx.Config()) && !library.buildStubs() &&
-				!translatedArch && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() {
-				if ctx.Device() {
-					library.installSymlinkToRuntimeApex(ctx, file)
-				}
-				library.baseInstaller.subDir = "bootstrap"
+			if ctx.Device() {
+				library.installSymlinkToRuntimeApex(ctx, file)
 			}
-		} else if ctx.directlyInAnyApex() && ctx.IsLlndk() && !isBionic(ctx.baseModuleName()) {
-			// Skip installing LLNDK (non-bionic) libraries moved to APEX.
-			ctx.Module().HideFromMake()
+			library.baseInstaller.subDir = "bootstrap"
 		}
 
 		library.baseInstaller.install(ctx, file)
@@ -1861,6 +1863,11 @@
 	return Bool(library.Properties.Llndk.Llndk_headers)
 }
 
+// isLLNDKMovedToApex returns true if this cc_library module sets the llndk.moved_to_apex property.
+func (library *libraryDecorator) isLLNDKMovedToApex() bool {
+	return Bool(library.Properties.Llndk.Moved_to_apex)
+}
+
 // hasVendorPublicLibrary returns true if this cc_library module has a variant that will build
 // vendor public library stubs.
 func (library *libraryDecorator) hasVendorPublicLibrary() bool {
@@ -1876,7 +1883,7 @@
 }
 
 func (library *libraryDecorator) symbolFileForAbiCheck(ctx ModuleContext) *string {
-	if props := library.getHeaderAbiCheckerProperties(ctx); props.Symbol_file != nil {
+	if props := library.getHeaderAbiCheckerProperties(ctx.Module().(*Module)); props.Symbol_file != nil {
 		return props.Symbol_file
 	}
 	if library.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
@@ -2079,12 +2086,7 @@
 			sharedCompiler.StaticProperties.Static.System_shared_libs == nil &&
 			sharedCompiler.SharedProperties.Shared.System_shared_libs == nil {
 
-			// TODO: namespaces?
 			ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, reuseObjTag, ctx.ModuleName())
-			sharedCompiler.baseCompiler.Properties.OriginalSrcs =
-				sharedCompiler.baseCompiler.Properties.Srcs
-			sharedCompiler.baseCompiler.Properties.Srcs = proptools.NewConfigurable[[]string](nil, nil)
-			sharedCompiler.baseCompiler.Properties.Generated_sources = nil
 		}
 
 		// This dep is just to reference static variant from shared variant
diff --git a/cc/library_headers_test.go b/cc/library_headers_test.go
index 1924b2f..5a45767 100644
--- a/cc/library_headers_test.go
+++ b/cc/library_headers_test.go
@@ -46,7 +46,7 @@
 
 			// Test that there's a valid AndroidMk entry.
 			headers := ctx.ModuleForTests("headers", "android_arm64_armv8-a").Module()
-			e := android.AndroidMkEntriesForTest(t, ctx, headers)[0]
+			e := android.AndroidMkInfoForTest(t, ctx, headers).PrimaryInfo
 
 			// This duplicates the tests done in AndroidMkEntries.write. It would be
 			// better to test its output, but there are no test functions that capture that.
diff --git a/cc/linkable.go b/cc/linkable.go
index 1672366..1a9a9ab 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -5,6 +5,7 @@
 	"android/soong/fuzz"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 )
 
 // PlatformSanitizeable is an interface for sanitizing platform modules.
@@ -134,6 +135,10 @@
 	// IsNdk returns true if the library is in the configs known NDK list.
 	IsNdk(config android.Config) bool
 
+	// HasStubsVariants true if this module is a stub or has a sibling variant
+	// that is a stub.
+	HasStubsVariants() bool
+
 	// IsStubs returns true if the this is a stubs library.
 	IsStubs() bool
 
@@ -316,10 +321,12 @@
 	SharedLibrary android.Path
 	Target        android.Target
 
-	TableOfContents android.OptionalPath
+	TableOfContents    android.OptionalPath
+	IsStubs            bool
+	ImplementationDeps depset.DepSet[string]
 
 	// should be obtained from static analogue
-	TransitiveStaticLibrariesForOrdering *android.DepSet[android.Path]
+	TransitiveStaticLibrariesForOrdering depset.DepSet[android.Path]
 }
 
 var SharedLibraryInfoProvider = blueprint.NewProvider[SharedLibraryInfo]()
@@ -361,7 +368,7 @@
 	// This isn't the actual transitive DepSet, shared library dependencies have been
 	// converted into static library analogues.  It is only used to order the static
 	// library dependencies that were specified for the current module.
-	TransitiveStaticLibrariesForOrdering *android.DepSet[android.Path]
+	TransitiveStaticLibrariesForOrdering depset.DepSet[android.Path]
 }
 
 var StaticLibraryInfoProvider = blueprint.NewProvider[StaticLibraryInfo]()
@@ -385,3 +392,9 @@
 }
 
 var FlagExporterInfoProvider = blueprint.NewProvider[FlagExporterInfo]()
+
+var ImplementationDepInfoProvider = blueprint.NewProvider[*ImplementationDepInfo]()
+
+type ImplementationDepInfo struct {
+	ImplementationDeps depset.DepSet[android.Path]
+}
diff --git a/cc/linker.go b/cc/linker.go
index 1efacad..b96d139 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -85,7 +85,7 @@
 
 	// list of header libraries to re-export include directories from. Entries must be
 	// present in header_libs.
-	Export_header_lib_headers []string `android:"arch_variant"`
+	Export_header_lib_headers proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of generated headers to re-export include directories from. Entries must be
 	// present in generated_headers.
@@ -235,13 +235,16 @@
 	// Generate compact dynamic relocation table, default true.
 	Pack_relocations *bool `android:"arch_variant"`
 
-	// local file name to pass to the linker as --version-script
+	// local file name to pass to the linker as --version-script.  Not supported on darwin, and will fail to build
+	// if provided to the darwin variant of a module.
 	Version_script *string `android:"path,arch_variant"`
 
-	// local file name to pass to the linker as --dynamic-list
+	// local file name to pass to the linker as --dynamic-list.  Not supported on darwin, and will fail to build
+	// if provided to the darwin variant of a module.
 	Dynamic_list *string `android:"path,arch_variant"`
 
-	// local files to pass to the linker as --script
+	// local files to pass to the linker as --script.  Not supported on darwin or windows, and will fail to build
+	// if provided to the darwin or windows variant of a module.
 	Linker_scripts []string `android:"path,arch_variant"`
 
 	// list of static libs that should not be used to build this module
@@ -302,7 +305,7 @@
 	deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Shared_libs.GetOrDefault(ctx, nil)...)
 	deps.RuntimeLibs = append(deps.RuntimeLibs, linker.Properties.Runtime_libs...)
 
-	deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders, linker.Properties.Export_header_lib_headers...)
+	deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders, linker.Properties.Export_header_lib_headers.GetOrDefault(ctx, nil)...)
 	deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, linker.Properties.Export_static_lib_headers...)
 	deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, linker.Properties.Export_shared_lib_headers...)
 	deps.ReexportGeneratedHeaders = append(deps.ReexportGeneratedHeaders, linker.Properties.Export_generated_headers...)
@@ -560,7 +563,7 @@
 
 		if versionScript.Valid() {
 			if ctx.Darwin() {
-				ctx.PropertyErrorf("version_script", "Not supported on Darwin")
+				ctx.AddMissingDependencies([]string{"version_script_not_supported_on_darwin"})
 			} else {
 				flags.Local.LdFlags = append(flags.Local.LdFlags,
 					config.VersionScriptFlagPrefix+versionScript.String())
@@ -578,7 +581,7 @@
 		dynamicList := android.OptionalPathForModuleSrc(ctx, linker.Properties.Dynamic_list)
 		if dynamicList.Valid() {
 			if ctx.Darwin() {
-				ctx.PropertyErrorf("dynamic_list", "Not supported on Darwin")
+				ctx.AddMissingDependencies([]string{"dynamic_list_not_supported_on_darwin"})
 			} else {
 				flags.Local.LdFlags = append(flags.Local.LdFlags,
 					"-Wl,--dynamic-list,"+dynamicList.String())
@@ -587,13 +590,17 @@
 		}
 
 		linkerScriptPaths := android.PathsForModuleSrc(ctx, linker.Properties.Linker_scripts)
-		if len(linkerScriptPaths) > 0 && (ctx.Darwin() || ctx.Windows()) {
-			ctx.PropertyErrorf("linker_scripts", "Only supported for ELF files")
-		} else {
-			for _, linkerScriptPath := range linkerScriptPaths {
-				flags.Local.LdFlags = append(flags.Local.LdFlags,
-					"-Wl,--script,"+linkerScriptPath.String())
-				flags.LdFlagsDeps = append(flags.LdFlagsDeps, linkerScriptPath)
+		if len(linkerScriptPaths) > 0 {
+			if ctx.Darwin() {
+				ctx.AddMissingDependencies([]string{"linker_scripts_not_supported_on_darwin"})
+			} else if ctx.Windows() {
+				ctx.PropertyErrorf("linker_scripts", "Only supported for ELF files")
+			} else {
+				for _, linkerScriptPath := range linkerScriptPaths {
+					flags.Local.LdFlags = append(flags.Local.LdFlags,
+						"-Wl,--script,"+linkerScriptPath.String())
+					flags.LdFlagsDeps = append(flags.LdFlagsDeps, linkerScriptPath)
+				}
 			}
 		}
 	}
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index c7950f9..162dd54 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -57,17 +57,18 @@
 	// if true, make this module available to provide headers to other modules that set
 	// llndk.symbol_file.
 	Llndk_headers *bool
+
+	// moved_to_apex marks this module has having been distributed through an apex module.
+	Moved_to_apex *bool
 }
 
 func makeLlndkVars(ctx android.MakeVarsContext) {
-	// Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to avoid installing libraries on /system if
-	// they been moved to an apex.
+	// Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to generate the linker config.
 	movedToApexLlndkLibraries := make(map[string]bool)
 	ctx.VisitAllModules(func(module android.Module) {
 		if library := moduleLibraryInterface(module); library != nil && library.hasLLNDKStubs() {
-			// Skip bionic libs, they are handled in different manner
-			name := library.implementationModuleName(module.(*Module).BaseModuleName())
-			if module.(android.ApexModule).DirectlyInAnyApex() && !isBionic(name) {
+			if library.isLLNDKMovedToApex() {
+				name := library.implementationModuleName(module.(*Module).BaseModuleName())
 				movedToApexLlndkLibraries[name] = true
 			}
 		}
diff --git a/cc/lto.go b/cc/lto.go
index f3af7d2..4444152 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -110,7 +110,7 @@
 		var ltoLdFlags []string
 
 		// Do not perform costly LTO optimizations for Eng builds.
-		if Bool(lto.Properties.Lto_O0) || ctx.optimizeForSize() || ctx.Config().Eng() {
+		if Bool(lto.Properties.Lto_O0) || ctx.Config().Eng() {
 			ltoLdFlags = append(ltoLdFlags, "-Wl,--lto-O0")
 		}
 
diff --git a/cc/makevars.go b/cc/makevars.go
index c9352a4..4cb98e7 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -25,10 +25,7 @@
 )
 
 var (
-	modulesWarningsAllowedKey    = android.NewOnceKey("ModulesWarningsAllowed")
-	modulesUsingWnoErrorKey      = android.NewOnceKey("ModulesUsingWnoError")
-	modulesMissingProfileFileKey = android.NewOnceKey("ModulesMissingProfileFile")
-	sanitizerVariables           = map[string]string{
+	sanitizerVariables = map[string]string{
 		"ADDRESS_SANITIZER_RUNTIME_LIBRARY":   config.AddressSanitizerRuntimeLibrary(),
 		"HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(),
 		"HWADDRESS_SANITIZER_STATIC_LIBRARY":  config.HWAddressSanitizerStaticLibrary(),
@@ -50,15 +47,9 @@
 	}).(*sync.Map)
 }
 
-func makeStringOfKeys(ctx android.MakeVarsContext, key android.OnceKey) string {
-	set := getNamedMapForConfig(ctx.Config(), key)
-	keys := []string{}
-	set.Range(func(key interface{}, value interface{}) bool {
-		keys = append(keys, key.(string))
-		return true
-	})
-	sort.Strings(keys)
-	return strings.Join(keys, " ")
+func makeVarsString(items []string) string {
+	items = android.SortedUniqueStrings(items)
+	return strings.Join(items, " ")
 }
 
 func makeStringOfWarningAllowedProjects() string {
@@ -108,27 +99,33 @@
 	ctx.Strict("GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideExternalGlobalCflags}")
 
 	// Filter vendor_public_library that are exported to make
-	exportedVendorPublicLibraries := []string{}
+	var exportedVendorPublicLibraries []string
+	var warningsAllowed []string
+	var usingWnoErrors []string
+	var missingProfiles []string
 	ctx.VisitAllModules(func(module android.Module) {
+		if v, ok := android.OtherModuleProvider(ctx, module, CcMakeVarsInfoProvider); ok {
+			warningsAllowed = android.AppendIfNotZero(warningsAllowed, v.WarningsAllowed)
+			usingWnoErrors = android.AppendIfNotZero(usingWnoErrors, v.UsingWnoError)
+			missingProfiles = android.AppendIfNotZero(missingProfiles, v.MissingProfile)
+		}
 		if ccModule, ok := module.(*Module); ok {
 			baseName := ccModule.BaseModuleName()
 			if ccModule.IsVendorPublicLibrary() && module.ExportedToMake() {
-				if !inList(baseName, exportedVendorPublicLibraries) {
-					exportedVendorPublicLibraries = append(exportedVendorPublicLibraries, baseName)
-				}
+				exportedVendorPublicLibraries = append(exportedVendorPublicLibraries, baseName)
 			}
 		}
 	})
-	sort.Strings(exportedVendorPublicLibraries)
-	ctx.Strict("VENDOR_PUBLIC_LIBRARIES", strings.Join(exportedVendorPublicLibraries, " "))
+	ctx.Strict("VENDOR_PUBLIC_LIBRARIES", makeVarsString(exportedVendorPublicLibraries))
 
+	lsdumpPaths := *lsdumpPaths(ctx.Config())
 	sort.Strings(lsdumpPaths)
 	ctx.Strict("LSDUMP_PATHS", strings.Join(lsdumpPaths, " "))
 
 	ctx.Strict("ANDROID_WARNING_ALLOWED_PROJECTS", makeStringOfWarningAllowedProjects())
-	ctx.Strict("SOONG_MODULES_WARNINGS_ALLOWED", makeStringOfKeys(ctx, modulesWarningsAllowedKey))
-	ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeStringOfKeys(ctx, modulesUsingWnoErrorKey))
-	ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeStringOfKeys(ctx, modulesMissingProfileFileKey))
+	ctx.Strict("SOONG_MODULES_WARNINGS_ALLOWED", makeVarsString(warningsAllowed))
+	ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeVarsString(usingWnoErrors))
+	ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeVarsString(missingProfiles))
 
 	ctx.Strict("CLANG_COVERAGE_CONFIG_CFLAGS", strings.Join(clangCoverageCFlags, " "))
 	ctx.Strict("CLANG_COVERAGE_CONFIG_COMMFLAGS", strings.Join(clangCoverageCommonFlags, " "))
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 01551ab..2411614 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -144,11 +144,9 @@
 }
 
 func ndkLibraryVersions(ctx android.BaseModuleContext, from android.ApiLevel) []string {
-	var versions []android.ApiLevel
 	versionStrs := []string{}
-	for _, version := range ctx.Config().AllSupportedApiLevels() {
+	for _, version := range ctx.Config().FinalApiLevels() {
 		if version.GreaterThanOrEqualTo(from) {
-			versions = append(versions, version)
 			versionStrs = append(versionStrs, version.String())
 		}
 	}
@@ -330,6 +328,12 @@
 	return android.ExistentPathForSource(ctx, subpath)
 }
 
+func (this *stubDecorator) builtAbiDumpLocation(ctx ModuleContext, apiLevel android.ApiLevel) android.OutputPath {
+	return getNdkAbiDumpInstallBase(ctx).Join(ctx,
+		apiLevel.String(), ctx.Arch().ArchType.String(),
+		this.libraryName(ctx), "abi.stg")
+}
+
 // Feature flag.
 func (this *stubDecorator) canDumpAbi(ctx ModuleContext) bool {
 	if runtime.GOOS == "darwin" {
@@ -345,25 +349,24 @@
 		return false
 	}
 
-	if this.apiLevel.IsCurrent() {
-		// "current" (AKA 10000) is not tracked.
-		return false
-	}
-
 	// http://b/156513478
 	return ctx.Config().ReleaseNdkAbiMonitored()
 }
 
 // Feature flag to disable diffing against prebuilts.
-func canDiffAbi(config android.Config) bool {
+func (this *stubDecorator) canDiffAbi(config android.Config) bool {
+	if this.apiLevel.IsCurrent() {
+		// Diffs are performed from this to next, and there's nothing after
+		// current.
+		return false
+	}
+
 	return config.ReleaseNdkAbiMonitored()
 }
 
 func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
 	implementationLibrary := this.findImplementationLibrary(ctx)
-	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
-		this.apiLevel.String(), ctx.Arch().ArchType.String(),
-		this.libraryName(ctx), "abi.stg")
+	this.abiDumpPath = this.builtAbiDumpLocation(ctx, this.apiLevel)
 	this.hasAbiDump = true
 	headersList := getNdkABIHeadersFile(ctx)
 	ctx.Build(pctx, android.BuildParams{
@@ -383,7 +386,7 @@
 }
 
 func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel {
-	apiLevels := append(ctx.Config().AllSupportedApiLevels(),
+	apiLevels := append(ctx.Config().FinalApiLevels(),
 		android.FutureApiLevel)
 	for _, api := range apiLevels {
 		if api.GreaterThan(apiLevel) {
@@ -436,38 +439,49 @@
 				"non-current API level %s", this.apiLevel))
 		}
 
-		// "current" ABI is not tracked.
-		if !nextApiLevel.IsCurrent() {
-			nextAbiDiffPath := android.PathForModuleOut(ctx,
-				"abidiff_next.timestamp")
-			nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel)
+		// Preview ABI levels are not recorded in prebuilts. ABI compatibility
+		// for preview APIs is still monitored via "current" so we have early
+		// warning rather than learning about an ABI break during finalization,
+		// but is only checked against the "current" API dumps in the out
+		// directory.
+		nextAbiDiffPath := android.PathForModuleOut(ctx,
+			"abidiff_next.timestamp")
+
+		var nextAbiDump android.OptionalPath
+		if nextApiLevel.IsCurrent() {
+			nextAbiDump = android.OptionalPathForPath(
+				this.builtAbiDumpLocation(ctx, *nextApiLevel),
+			)
+		} else {
+			nextAbiDump = this.findPrebuiltAbiDump(ctx, *nextApiLevel)
+		}
+
+		if !nextAbiDump.Valid() {
 			missingNextPrebuiltError := fmt.Sprintf(
 				missingPrebuiltErrorTemplate, this.libraryName(ctx),
 				nextAbiDump.InvalidReason())
-			if !nextAbiDump.Valid() {
-				ctx.Build(pctx, android.BuildParams{
-					Rule:   android.ErrorRule,
-					Output: nextAbiDiffPath,
-					Args: map[string]string{
-						"error": missingNextPrebuiltError,
-					},
-				})
-			} else {
-				ctx.Build(pctx, android.BuildParams{
-					Rule: stgdiff,
-					Description: fmt.Sprintf(
-						"Comparing ABI to the next API level %s %s",
-						prebuiltAbiDump, nextAbiDump),
-					Output: nextAbiDiffPath,
-					Inputs: android.Paths{
-						prebuiltAbiDump.Path(), nextAbiDump.Path()},
-					Args: map[string]string{
-						"args": "--format=small --ignore=interface_addition",
-					},
-				})
-			}
-			this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath)
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.ErrorRule,
+				Output: nextAbiDiffPath,
+				Args: map[string]string{
+					"error": missingNextPrebuiltError,
+				},
+			})
+		} else {
+			ctx.Build(pctx, android.BuildParams{
+				Rule: stgdiff,
+				Description: fmt.Sprintf(
+					"Comparing ABI to the next API level %s %s",
+					prebuiltAbiDump, nextAbiDump),
+				Output: nextAbiDiffPath,
+				Inputs: android.Paths{
+					prebuiltAbiDump.Path(), nextAbiDump.Path()},
+				Args: map[string]string{
+					"args": "--format=small --ignore=interface_addition",
+				},
+			})
 		}
+		this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath)
 	}
 }
 
@@ -492,7 +506,7 @@
 	c.versionScriptPath = nativeAbiResult.versionScript
 	if c.canDumpAbi(ctx) {
 		c.dumpAbi(ctx, nativeAbiResult.symbolList)
-		if canDiffAbi(ctx.Config()) {
+		if c.canDiffAbi(ctx.Config()) {
 			c.diffAbi(ctx)
 		}
 	}
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 22f31d9..6c24b4f 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -473,16 +473,17 @@
             VERSION_35 { # introduced=35
                 global:
                     wiggle;
-                    waggle;
-                    waggle; # llndk=202404
-                    bubble; # llndk=202404
-                    duddle;
-                    duddle; # llndk=202504
+                    waggle; # llndk
             } VERSION_34;
+            VERSION_36 { # introduced=36
+                global:
+                    abc;
+                    xyz; # llndk
+            } VERSION_35;
         """))
         f = copy(self.filter)
         f.llndk = True
-        f.api = 202404
+        f.api = 35
         parser = symbolfile.SymbolFileParser(input_file, {}, f)
         versions = parser.parse()
 
@@ -497,8 +498,8 @@
         expected_src = textwrap.dedent("""\
             void foo() {}
             void bar() {}
+            void wiggle() {}
             void waggle() {}
-            void bubble() {}
         """)
         self.assertEqual(expected_src, src_file.getvalue())
 
@@ -510,8 +511,8 @@
             };
             VERSION_35 {
                 global:
+                    wiggle;
                     waggle;
-                    bubble;
             } VERSION_34;
         """)
         self.assertEqual(expected_version, version_file.getvalue())
@@ -521,15 +522,15 @@
             LIBANDROID {
                 global:
                     foo; # introduced=34
-                    bar; # introduced=35
-                    bar; # llndk=202404
-                    baz; # introduced=35
+                    bar; # introduced=35 llndk
+                    baz; # introduced=V
+                    qux; # introduced=36
             };
         """))
         f = copy(self.filter)
         f.llndk = True
-        f.api = 202404
-        parser = symbolfile.SymbolFileParser(input_file, {}, f)
+        f.api = 35
+        parser = symbolfile.SymbolFileParser(input_file, {'V': 35}, f)
         versions = parser.parse()
 
         src_file = io.StringIO()
@@ -543,6 +544,7 @@
         expected_src = textwrap.dedent("""\
             void foo() {}
             void bar() {}
+            void baz() {}
         """)
         self.assertEqual(expected_src, src_file.getvalue())
 
@@ -551,6 +553,7 @@
                 global:
                     foo;
                     bar;
+                    baz;
             };
         """)
         self.assertEqual(expected_version, version_file.getvalue())
diff --git a/cc/orderfile.go b/cc/orderfile.go
index 38b8905..6e08da7 100644
--- a/cc/orderfile.go
+++ b/cc/orderfile.go
@@ -54,10 +54,6 @@
 	})
 }
 
-func recordMissingOrderfile(ctx BaseModuleContext, missing string) {
-	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
-}
-
 type OrderfileProperties struct {
 	Orderfile struct {
 		Instrumentation *bool
@@ -117,7 +113,7 @@
 
 	// Record that this module's order file is absent
 	missing := *props.Orderfile.Order_file_path + ":" + ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName()
-	recordMissingOrderfile(ctx, missing)
+	ctx.getOrCreateMakeVarsInfo().MissingProfile = missing
 
 	return android.OptionalPath{}
 }
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 299fb51..96a07bc 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -16,8 +16,8 @@
 
 import (
 	"path/filepath"
-	"strings"
 
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -119,22 +119,6 @@
 	// Stub variants will create a stub .so file from stub .c files
 	if p.buildStubs() && objs.objFiles != nil {
 		// TODO (b/275273834): Make objs.objFiles == nil a hard error when the symbol files have been added to module sdk.
-
-		// The map.txt files of libclang_rt.* contain version information, but the checked in .so files do not.
-		// e.g. libclang_rt.* libs impl
-		// $ nm -D prebuilts/../libclang_rt.hwasan-aarch64-android.so
-		// __hwasan_init
-
-		// stubs generated from .map.txt
-		// $ nm -D out/soong/.intermediates/../<stubs>/libclang_rt.hwasan-aarch64-android.so
-		// __hwasan_init@@LIBCLANG_RT_ASAN
-
-		// Special-case libclang_rt.* libs to account for this discrepancy.
-		// TODO (spandandas): Remove this special case https://r.android.com/3236596 has been submitted, and a new set of map.txt
-		// files of libclang_rt.* libs have been generated.
-		if strings.Contains(ctx.ModuleName(), "libclang_rt.") {
-			p.versionScriptPath = android.OptionalPathForPath(nil)
-		}
 		return p.linkShared(ctx, flags, deps, objs)
 	}
 
@@ -156,7 +140,7 @@
 		}
 
 		if p.static() {
-			depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(in).Build()
+			depSet := depset.NewBuilder[android.Path](depset.TOPOLOGICAL).Direct(in).Build()
 			android.SetProvider(ctx, StaticLibraryInfoProvider, StaticLibraryInfo{
 				StaticLibrary: in,
 
@@ -220,6 +204,7 @@
 				Target:        ctx.Target(),
 
 				TableOfContents: p.tocFile,
+				IsStubs:         p.buildStubs(),
 			})
 
 			return outputFile
@@ -231,6 +216,7 @@
 		android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
 			SharedLibrary: latestStub,
 			Target:        ctx.Target(),
+			IsStubs:       true,
 		})
 
 		return latestStub
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index acbbabc..3214fb4 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -50,6 +50,7 @@
 		cc_prebuilt_library_shared {
 			name: "liba",
 			srcs: ["liba.so"],
+			prefer: true,
 		}
 
 		cc_library {
@@ -59,6 +60,7 @@
 		cc_prebuilt_library_static {
 			name: "libb",
 			srcs: ["libb.a"],
+			prefer: true,
 		}
 
 		cc_library_shared {
@@ -169,9 +171,9 @@
 		t.Errorf("crtx missing dependency on prebuilt_crtx")
 	}
 
-	entries := android.AndroidMkEntriesForTest(t, ctx, prebuiltLiba)[0]
+	entries := android.AndroidMkInfoForTest(t, ctx, prebuiltLiba).PrimaryInfo
 	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_prebuilt_library_shared", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
-	entries = android.AndroidMkEntriesForTest(t, ctx, prebuiltLibb)[0]
+	entries = android.AndroidMkInfoForTest(t, ctx, prebuiltLibb).PrimaryInfo
 	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "cc_prebuilt_library_static", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
 }
 
@@ -486,7 +488,7 @@
 		expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_arm64_armv8-a_shared").Module()
 		android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", libfoo.Name(), tc.expectedDependencyName), true, hasDep(ctx, libfoo, expectedDependency))
 		// check that LOCAL_SHARED_LIBRARIES contains libbar and not libbar.v<N>
-		entries := android.AndroidMkEntriesForTest(t, ctx, libfoo)[0]
+		entries := android.AndroidMkInfoForTest(t, ctx, libfoo).PrimaryInfo
 		android.AssertStringListContains(t, "Version should not be present in LOCAL_SHARED_LIBRARIES", entries.EntryMap["LOCAL_SHARED_LIBRARIES"], "libbar")
 
 		// check installation rules
@@ -496,7 +498,7 @@
 
 		// check LOCAL_MODULE of the selected module name
 		// the prebuilt should have the same LOCAL_MODULE when exported to make
-		entries = android.AndroidMkEntriesForTest(t, ctx, libbar)[0]
+		entries = android.AndroidMkInfoForTest(t, ctx, libbar).PrimaryInfo
 		android.AssertStringEquals(t, "unexpected LOCAL_MODULE", "libbar", entries.EntryMap["LOCAL_MODULE"][0])
 	}
 }
diff --git a/cc/sabi.go b/cc/sabi.go
index 64eab41..bc61b6c 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -22,10 +22,16 @@
 )
 
 var (
-	lsdumpPaths     []string
 	lsdumpPathsLock sync.Mutex
+	lsdumpKey       = android.NewOnceKey("lsdump")
 )
 
+func lsdumpPaths(config android.Config) *[]string {
+	return config.Once(lsdumpKey, func() any {
+		return &[]string{}
+	}).(*[]string)
+}
+
 type lsdumpTag string
 
 const (
@@ -84,8 +90,8 @@
 
 type SAbiProperties struct {
 	// Whether ABI dump should be created for this module.
-	// Set by `sabiDepsMutator` if this module is a shared library that needs ABI check, or a static
-	// library that is depended on by an ABI checked library.
+	// Set by `sabiTransitionMutator` if this module is a shared library that needs ABI check,
+	// or a static library that is depended on by an ABI checked library.
 	ShouldCreateSourceAbiDump bool `blueprint:"mutated"`
 
 	// Include directories that may contain ABI information exported by a library.
@@ -121,10 +127,9 @@
 }
 
 // Returns a slice of strings that represent the ABI dumps generated for this module.
-func classifySourceAbiDump(ctx android.BaseModuleContext) []lsdumpTag {
+func classifySourceAbiDump(m *Module) []lsdumpTag {
 	result := []lsdumpTag{}
-	m := ctx.Module().(*Module)
-	headerAbiChecker := m.library.getHeaderAbiCheckerProperties(ctx)
+	headerAbiChecker := m.library.getHeaderAbiCheckerProperties(m)
 	if headerAbiChecker.explicitlyDisabled() {
 		return result
 	}
@@ -149,24 +154,37 @@
 	return result
 }
 
-// Called from sabiDepsMutator to check whether ABI dumps should be created for this module.
+type shouldCreateAbiDumpContext interface {
+	android.ModuleProviderContext
+	Module() android.Module
+	Config() android.Config
+}
+
+var _ shouldCreateAbiDumpContext = android.ModuleContext(nil)
+var _ shouldCreateAbiDumpContext = android.OutgoingTransitionContext(nil)
+
+// Called from sabiTransitionMutator to check whether ABI dumps should be created for this module.
 // ctx should be wrapping a native library type module.
-func shouldCreateSourceAbiDumpForLibrary(ctx android.BaseModuleContext) bool {
-	// Only generate ABI dump for device modules.
-	if !ctx.Device() {
+func shouldCreateSourceAbiDumpForLibrary(ctx shouldCreateAbiDumpContext) bool {
+	m, ok := ctx.Module().(*Module)
+	if !ok {
 		return false
 	}
 
-	m := ctx.Module().(*Module)
+	// Only generate ABI dump for device modules.
+	if !m.Device() {
+		return false
+	}
 
 	// Only create ABI dump for native library module types.
 	if m.library == nil {
 		return false
 	}
 
-	// Create ABI dump for static libraries only if they are dependencies of ABI checked libraries.
+	// Don't create ABI dump for static libraries
+	// The sabi variant will be propagated to dependencies of ABI checked libraries.
 	if m.library.static() {
-		return m.sabi.shouldCreateSourceAbiDump()
+		return false
 	}
 
 	// Module is shared library type.
@@ -215,39 +233,73 @@
 			return false
 		}
 	}
-	return len(classifySourceAbiDump(ctx)) > 0
+	return len(classifySourceAbiDump(m)) > 0
 }
 
 // Mark the direct and transitive dependencies of libraries that need ABI check, so that ABI dumps
 // of their dependencies would be generated.
-func sabiDepsMutator(mctx android.TopDownMutatorContext) {
+type sabiTransitionMutator struct{}
+
+func (s *sabiTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	return []string{""}
+}
+
+func (s *sabiTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
 	// Escape hatch to not check any ABI dump.
-	if mctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
-		return
+	if ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
+		return ""
 	}
+
 	// Only create ABI dump for native shared libraries and their static library dependencies.
-	if m, ok := mctx.Module().(*Module); ok && m.sabi != nil {
-		if shouldCreateSourceAbiDumpForLibrary(mctx) {
-			// Mark this module so that .sdump / .lsdump for this library can be generated.
+	if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
+		if shouldCreateSourceAbiDumpForLibrary(ctx) {
+			if IsStaticDepTag(ctx.DepTag()) || ctx.DepTag() == reuseObjTag {
+				return "sabi"
+			}
+		} else if sourceVariation == "sabi" {
+			if IsWholeStaticLib(ctx.DepTag()) || ctx.DepTag() == reuseObjTag {
+				return "sabi"
+			}
+		}
+	}
+
+	return ""
+}
+
+func (s *sabiTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if incomingVariation == "" {
+		return ""
+	}
+
+	if incomingVariation == "sabi" {
+		if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
+			return "sabi"
+		}
+	}
+
+	return ""
+}
+
+func (s *sabiTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
+		if variation == "sabi" {
 			m.sabi.Properties.ShouldCreateSourceAbiDump = true
-			// Mark all of its static library dependencies.
-			mctx.VisitDirectDeps(func(child android.Module) {
-				depTag := mctx.OtherModuleDependencyTag(child)
-				if IsStaticDepTag(depTag) || depTag == reuseObjTag {
-					if c, ok := child.(*Module); ok && c.sabi != nil {
-						// Mark this module so that .sdump for this static library can be generated.
-						c.sabi.Properties.ShouldCreateSourceAbiDump = true
-					}
-				}
-			})
+			m.HideFromMake()
+			m.Properties.PreventInstall = true
+		} else if shouldCreateSourceAbiDumpForLibrary(ctx) {
+			// Escape hatch to not check any ABI dump.
+			if !ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
+				m.sabi.Properties.ShouldCreateSourceAbiDump = true
+			}
 		}
 	}
 }
 
 // Add an entry to the global list of lsdump. The list is exported to a Make variable by
 // `cc.makeVarsProvider`.
-func addLsdumpPath(lsdumpPath string) {
+func addLsdumpPath(config android.Config, lsdumpPath string) {
+	lsdumpPaths := lsdumpPaths(config)
 	lsdumpPathsLock.Lock()
 	defer lsdumpPathsLock.Unlock()
-	lsdumpPaths = append(lsdumpPaths, lsdumpPath)
+	*lsdumpPaths = append(*lsdumpPaths, lsdumpPath)
 }
diff --git a/cc/sabi_test.go b/cc/sabi_test.go
new file mode 100644
index 0000000..6b8cc17
--- /dev/null
+++ b/cc/sabi_test.go
@@ -0,0 +1,66 @@
+// 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 cc
+
+import (
+	"android/soong/android"
+	"testing"
+)
+
+func TestSabi(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libsabi",
+			srcs: ["sabi.cpp"],
+			static_libs: ["libdirect"],
+			header_abi_checker: {
+				enabled: true,
+				symbol_file: "libsabi.map.txt",
+                ref_dump_dirs: ["abi-dumps"],
+			},
+		}
+
+		cc_library {
+			name: "libdirect",
+			srcs: ["direct.cpp"],
+			whole_static_libs: ["libtransitive"],
+		}
+
+		cc_library {
+			name: "libtransitive",
+			srcs: ["transitive.cpp"],
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithCcDefaultModules,
+	).RunTestWithBp(t, bp)
+
+	libsabiStatic := result.ModuleForTests("libsabi", "android_arm64_armv8-a_static_sabi")
+	sabiObjSDump := libsabiStatic.Output("obj/sabi.sdump")
+
+	libDirect := result.ModuleForTests("libdirect", "android_arm64_armv8-a_static_sabi")
+	directObjSDump := libDirect.Output("obj/direct.sdump")
+
+	libTransitive := result.ModuleForTests("libtransitive", "android_arm64_armv8-a_static_sabi")
+	transitiveObjSDump := libTransitive.Output("obj/transitive.sdump")
+
+	libsabiShared := result.ModuleForTests("libsabi", "android_arm64_armv8-a_shared")
+	sabiLink := libsabiShared.Rule("sAbiLink")
+
+	android.AssertStringListContains(t, "sabi link inputs", sabiLink.Inputs.Strings(), sabiObjSDump.Output.String())
+	android.AssertStringListContains(t, "sabi link inputs", sabiLink.Inputs.Strings(), directObjSDump.Output.String())
+	android.AssertStringListContains(t, "sabi link inputs", sabiLink.Inputs.Strings(), transitiveObjSDump.Output.String())
+}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 7f52ce1..d8d8c7a 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -992,7 +992,7 @@
 	return flags
 }
 
-func (s *sanitize) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (s *sanitize) prepareAndroidMKProviderInfo(config android.Config, ctx AndroidMkContext, entries *android.AndroidMkInfo) {
 	// Add a suffix for cfi/hwasan/scs-enabled static/header libraries to allow surfacing
 	// both the sanitized and non-sanitized variants to make without a name conflict.
 	if entries.Class == "STATIC_LIBRARIES" || entries.Class == "HEADER_LIBRARIES" {
@@ -1504,9 +1504,6 @@
 
 		if Bool(sanProps.Memtag_globals) {
 			sanitizers = append(sanitizers, "memtag-globals")
-			// TODO(mitchp): For now, enable memtag-heap with memtag-globals because the linker
-			// isn't new enough (https://reviews.llvm.org/differential/changeset/?ref=4243566).
-			sanitizers = append(sanitizers, "memtag-heap")
 		}
 
 		if Bool(sanProps.Fuzzer) {
@@ -1813,7 +1810,7 @@
 type sanitizerLibrariesTxtModule struct {
 	android.ModuleBase
 
-	outputFile android.OutputPath
+	outputFile android.Path
 }
 
 var _ etc.PrebuiltEtcModule = (*sanitizerLibrariesTxtModule)(nil)
@@ -1830,10 +1827,7 @@
 
 type sanitizerLibraryDependencyTag struct {
 	blueprint.BaseDependencyTag
-}
-
-func (t sanitizerLibraryDependencyTag) AllowDisabledModuleDependency(target android.Module) bool {
-	return true
+	android.AlwaysAllowDisabledModuleDependencyTag
 }
 
 var _ android.AllowDisabledModuleDependency = (*sanitizerLibraryDependencyTag)(nil)
@@ -1899,20 +1893,23 @@
 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))
+	outputFile := android.PathForModuleOut(ctx, filename)
+	android.WriteFileRule(ctx, outputFile, txt.getSanitizerLibs(ctx))
 
 	installPath := android.PathForModuleInstall(ctx, "etc")
-	ctx.InstallFile(installPath, filename, txt.outputFile)
+	ctx.InstallFile(installPath, filename, outputFile)
 
-	ctx.SetOutputFiles(android.Paths{txt.outputFile}, "")
+	ctx.SetOutputFiles(android.Paths{outputFile}, "")
+	txt.outputFile = outputFile
 }
 
-func (txt *sanitizerLibrariesTxtModule) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(txt.outputFile),
-	}}
+func (txt *sanitizerLibrariesTxtModule) PrepareAndroidMKProviderInfo(config android.Config) *android.AndroidMkProviderInfo {
+	return &android.AndroidMkProviderInfo{
+		PrimaryInfo: android.AndroidMkInfo{
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(txt.outputFile),
+		},
+	}
 }
 
 // PrebuiltEtcModule interface
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 4553616..f2bd186 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -103,24 +103,13 @@
     @property
     def has_llndk_tags(self) -> bool:
         """Returns True if any LL-NDK tags are set."""
-        for tag in self.tags:
-            if tag == 'llndk' or tag.startswith('llndk='):
-                return True
-        return False
+        return 'llndk' in self.tags
 
     @property
     def has_platform_only_tags(self) -> bool:
         """Returns True if any platform-only tags are set."""
         return 'platform-only' in self.tags
 
-    def copy_introduced_from(self, tags: Tags) -> None:
-        """Copies introduced= or introduced-*= tags."""
-        for tag in tags:
-            if tag.startswith('introduced=') or tag.startswith('introduced-'):
-                name, _ = split_tag(tag)
-                if not any(self_tag.startswith(name + '=') for self_tag in self.tags):
-                    self.tags += (tag,)
-
 
 @dataclass
 class Symbol:
@@ -158,8 +147,6 @@
     """Returns true if this tag has an API level that may need decoding."""
     if tag.startswith('llndk-deprecated='):
         return True
-    if tag.startswith('llndk='):
-        return True
     if tag.startswith('introduced='):
         return True
     if tag.startswith('introduced-'):
@@ -245,21 +232,19 @@
         self.systemapi = systemapi
         self.ndk = ndk
 
+    def _symbol_in_arch_api(self, tags: Tags) -> bool:
+        if not symbol_in_arch(tags, self.arch):
+            return True
+        if not symbol_in_api(tags, self.arch, self.api):
+            return True
+        return False
+
     def _should_omit_tags(self, tags: Tags) -> bool:
         """Returns True if the tagged object should be omitted.
 
         This defines the rules shared between version tagging and symbol tagging.
         """
-        # LLNDK mode/tags follow the similar filtering except that API level checking
-        # is based llndk= instead of introduced=.
-        if self.llndk:
-            if tags.has_mode_tags and not tags.has_llndk_tags:
-                return True
-            if not symbol_in_arch(tags, self.arch):
-                return True
-            if not symbol_in_llndk_api(tags, self.arch, self.api):
-                return True
-            return False
+        # The apex and llndk tags will only exclude APIs from other modes. If in
         # APEX or LLNDK mode and neither tag is provided, we fall back to the
         # default behavior because all NDK symbols are implicitly available to
         # APEX and LLNDK.
@@ -268,12 +253,10 @@
                 return False
             if self.systemapi and tags.has_systemapi_tags:
                 return False
+            if self.llndk and tags.has_llndk_tags:
+                return self._symbol_in_arch_api(tags)
             return True
-        if not symbol_in_arch(tags, self.arch):
-            return True
-        if not symbol_in_api(tags, self.arch, self.api):
-            return True
-        return False
+        return self._symbol_in_arch_api(tags)
 
     def should_omit_version(self, version: Version) -> bool:
         """Returns True if the version section should be omitted.
@@ -286,10 +269,6 @@
             return True
         if version.tags.has_platform_only_tags:
             return True
-        # Include all versions when targeting LLNDK because LLNDK symbols are self-versioned.
-        # Empty version block will be handled separately.
-        if self.llndk:
-            return False
         return self._should_omit_tags(version.tags)
 
     def should_omit_symbol(self, symbol: Symbol) -> bool:
@@ -302,6 +281,7 @@
 
         return self._should_omit_tags(symbol.tags)
 
+
 def symbol_in_arch(tags: Tags, arch: Arch) -> bool:
     """Returns true if the symbol is present for the given architecture."""
     has_arch_tags = False
@@ -316,14 +296,6 @@
     # for the tagged architectures.
     return not has_arch_tags
 
-def symbol_in_llndk_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool:
-    """Returns true if the symbol is present for the given LLNDK API level."""
-    # Check llndk= first.
-    for tag in tags:
-        if tag.startswith('llndk='):
-            return api >= int(get_tag_value(tag))
-    # If not, we keep old behavior: NDK symbols in <= 34 are LLNDK symbols.
-    return symbol_in_api(tags, arch, 34)
 
 def symbol_in_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool:
     """Returns true if the symbol is present for the given API level."""
@@ -400,7 +372,6 @@
                     f'Unexpected contents at top level: {self.current_line}')
 
         self.check_no_duplicate_symbols(versions)
-        self.check_llndk_introduced(versions)
         return versions
 
     def check_no_duplicate_symbols(self, versions: Iterable[Version]) -> None:
@@ -429,31 +400,6 @@
             raise MultiplyDefinedSymbolError(
                 sorted(list(multiply_defined_symbols)))
 
-    def check_llndk_introduced(self, versions: Iterable[Version]) -> None:
-        """Raises errors when llndk= is missing for new llndk symbols."""
-        if not self.filter.llndk:
-            return
-
-        def assert_llndk_with_version(tags: Tags,  name: str) -> None:
-            has_llndk_introduced = False
-            for tag in tags:
-                if tag.startswith('llndk='):
-                    has_llndk_introduced = True
-                    break
-            if not has_llndk_introduced:
-                raise ParseError(f'{name}: missing version. `llndk=yyyymm`')
-
-        arch = self.filter.arch
-        for version in versions:
-            # llndk symbols >= introduced=35 should be tagged
-            # explicitly with llndk=yyyymm.
-            for symbol in version.symbols:
-                if not symbol.tags.has_llndk_tags:
-                    continue
-                if symbol_in_api(symbol.tags, arch, 34):
-                    continue
-                assert_llndk_with_version(symbol.tags, symbol.name)
-
     def parse_version(self) -> Version:
         """Parses a single version section and returns a Version object."""
         assert self.current_line is not None
@@ -487,9 +433,7 @@
                 else:
                     raise ParseError('Unknown visiblity label: ' + visibility)
             elif global_scope and not cpp_symbols:
-                symbol = self.parse_symbol()
-                symbol.tags.copy_introduced_from(tags)
-                symbols.append(symbol)
+                symbols.append(self.parse_symbol())
             else:
                 # We're in a hidden scope or in 'extern "C++"' block. Ignore
                 # everything.
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index 8b412b9..14bb737 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -344,45 +344,6 @@
         self.assertInclude(f_llndk, s_none)
         self.assertInclude(f_llndk, s_llndk)
 
-    def test_omit_llndk_versioned(self) -> None:
-        f_ndk = self.filter
-        f_ndk.api = 35
-
-        f_llndk = copy(f_ndk)
-        f_llndk.llndk = True
-        f_llndk.api = 202404
-
-        s = Symbol('foo', Tags())
-        s_llndk = Symbol('foo', Tags.from_strs(['llndk']))
-        s_llndk_202404 = Symbol('foo', Tags.from_strs(['llndk=202404']))
-        s_34 = Symbol('foo', Tags.from_strs(['introduced=34']))
-        s_34_llndk = Symbol('foo', Tags.from_strs(['introduced=34', 'llndk']))
-        s_35 = Symbol('foo', Tags.from_strs(['introduced=35']))
-        s_35_llndk_202404 = Symbol('foo', Tags.from_strs(['introduced=35', 'llndk=202404']))
-        s_35_llndk_202504 = Symbol('foo', Tags.from_strs(['introduced=35', 'llndk=202504']))
-
-        # When targeting NDK, omit LLNDK tags
-        self.assertInclude(f_ndk, s)
-        self.assertOmit(f_ndk, s_llndk)
-        self.assertOmit(f_ndk, s_llndk_202404)
-        self.assertInclude(f_ndk, s_34)
-        self.assertOmit(f_ndk, s_34_llndk)
-        self.assertInclude(f_ndk, s_35)
-        self.assertOmit(f_ndk, s_35_llndk_202404)
-        self.assertOmit(f_ndk, s_35_llndk_202504)
-
-        # When targeting LLNDK, old symbols without any mode tags are included as LLNDK
-        self.assertInclude(f_llndk, s)
-        # When targeting LLNDK, old symbols with #llndk are included as LLNDK
-        self.assertInclude(f_llndk, s_llndk)
-        self.assertInclude(f_llndk, s_llndk_202404)
-        self.assertInclude(f_llndk, s_34)
-        self.assertInclude(f_llndk, s_34_llndk)
-        # When targeting LLNDK, new symbols(>=35) should be tagged with llndk-introduced=.
-        self.assertOmit(f_llndk, s_35)
-        self.assertInclude(f_llndk, s_35_llndk_202404)
-        self.assertOmit(f_llndk, s_35_llndk_202504)
-
     def test_omit_apex(self) -> None:
         f_none = self.filter
         f_apex = copy(f_none)
@@ -494,8 +455,8 @@
         # should_omit_tags() can differently based on introduced API level when treating
         # LLNDK-available symbols.
         expected_symbols = [
-            Symbol('baz', Tags.from_strs(['introduced=35'])),
-            Symbol('qux', Tags.from_strs(['apex', 'llndk', 'introduced=35'])),
+            Symbol('baz', Tags()),
+            Symbol('qux', Tags.from_strs(['apex', 'llndk'])),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
@@ -643,19 +604,6 @@
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
-    def test_parse_llndk_version_is_missing(self) -> None:
-        input_file = io.StringIO(textwrap.dedent("""\
-            VERSION_1 { # introduced=35
-                foo;
-                bar; # llndk
-            };
-        """))
-        f = copy(self.filter)
-        f.llndk = True
-        parser = symbolfile.SymbolFileParser(input_file, {}, f)
-        with self.assertRaises(symbolfile.ParseError):
-            parser.parse()
-
 
 def main() -> None:
     suite = unittest.TestLoader().loadTestsFromName(__name__)
diff --git a/cc/test.go b/cc/test.go
index f5bb761..ae73886 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -15,10 +15,11 @@
 package cc
 
 import (
-	"github.com/google/blueprint/proptools"
 	"path/filepath"
 	"strconv"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/tradefed"
 )
@@ -82,6 +83,16 @@
 	// the test
 	Data []string `android:"path,arch_variant"`
 
+	// Same as data, but adds depedencies on modules using the device's os variant, and common
+	// architecture's variant. Can be useful to add device-built apps to the data of a host
+	// test.
+	Device_common_data []string `android:"path_device_common"`
+
+	// Same as data, but adds depedencies on modules using the device's os variant, and the device's
+	// first architecture's variant. Can be useful to add device-built apps to the data of a host
+	// test.
+	Device_first_data []string `android:"path_device_first"`
+
 	// list of shared library modules that should be installed alongside the test
 	Data_libs []string `android:"arch_variant"`
 
@@ -324,6 +335,8 @@
 
 func (test *testBinary) install(ctx ModuleContext, file android.Path) {
 	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
+	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_common_data)...)
+	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_first_data)...)
 
 	for _, dataSrcPath := range dataSrcPaths {
 		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
diff --git a/cc/tidy.go b/cc/tidy.go
index ec1e8a2..6481b95 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -219,15 +219,11 @@
 	subsetTidyFileGroups := make(map[string]android.Paths) // subset group name => tidy file Paths
 
 	// (1) Collect all obj/tidy files into OS-specific groups.
-	ctx.VisitAllModuleVariants(module, func(variant android.Module) {
-		if ctx.Config().KatiEnabled() && android.ShouldSkipAndroidMkProcessing(ctx, variant) {
-			return
-		}
-		if m, ok := variant.(*Module); ok {
-			osName := variant.Target().Os.Name
-			addToOSGroup(osName, m.objFiles, allObjFileGroups, subsetObjFileGroups)
-			addToOSGroup(osName, m.tidyFiles, allTidyFileGroups, subsetTidyFileGroups)
-		}
+	ctx.VisitAllModuleVariantProxies(module, func(variant android.ModuleProxy) {
+		osName := android.OtherModuleProviderOrDefault(ctx, variant, android.CommonModuleInfoKey).CompileTarget.Os.Name
+		info := android.OtherModuleProviderOrDefault(ctx, variant, CcObjectInfoProvider)
+		addToOSGroup(osName, info.objFiles, allObjFileGroups, subsetObjFileGroups)
+		addToOSGroup(osName, info.tidyFiles, allTidyFileGroups, subsetTidyFileGroups)
 	})
 
 	// (2) Add an all-OS group, with "" or "subset" name, to include all os-specific phony targets.
@@ -258,7 +254,7 @@
 
 	// Collect tidy/obj targets from the 'final' modules.
 	ctx.VisitAllModules(func(module android.Module) {
-		if module == ctx.FinalModule(module) {
+		if ctx.IsFinalModule(module) {
 			collectTidyObjModuleTargets(ctx, module, tidyModulesInDirGroup, objModulesInDirGroup)
 		}
 	})
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index e7dff40..4a2adf0 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -166,6 +166,7 @@
 			Target:        ctx.Target(),
 
 			TableOfContents: p.tocFile,
+			IsStubs:         false,
 		})
 
 		p.libraryDecorator.flagExporter.setProvider(ctx)
diff --git a/cmd/find_input_delta/find_input_delta/Android.bp b/cmd/find_input_delta/find_input_delta/Android.bp
new file mode 100644
index 0000000..93a7708
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta/Android.bp
@@ -0,0 +1,18 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "find_input_delta",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-find_input_delta-lib",
+        "soong-cmd-find_input_delta-proto",
+        "soong-cmd-find_input_delta-proto_internal",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
diff --git a/cmd/find_input_delta/find_input_delta/main.go b/cmd/find_input_delta/find_input_delta/main.go
new file mode 100644
index 0000000..a864584
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta/main.go
@@ -0,0 +1,95 @@
+// 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 main
+
+import (
+	"flag"
+	"os"
+	"strings"
+
+	fid_lib "android/soong/cmd/find_input_delta/find_input_delta_lib"
+)
+
+func main() {
+	var top string
+	var prior_state_file string
+	var new_state_file string
+	var target string
+	var inputs_file string
+	var template string
+	var inputs []string
+	var inspect bool
+	var err error
+
+	flag.StringVar(&top, "top", ".", "path to top of workspace")
+	flag.StringVar(&prior_state_file, "prior_state", "", "prior internal state file")
+	flag.StringVar(&new_state_file, "new_state", "", "new internal state file")
+	flag.StringVar(&target, "target", "", "name of ninja output file for build action")
+	flag.StringVar(&inputs_file, "inputs_file", "", "file containing list of input files")
+	flag.StringVar(&template, "template", fid_lib.DefaultTemplate, "output template for FileList")
+	flag.BoolVar(&inspect, "inspect", false, "whether to inspect file contents")
+
+	flag.Parse()
+
+	if target == "" {
+		panic("must specify --target")
+	}
+	if prior_state_file == "" {
+		prior_state_file = target + ".pc_state"
+	}
+	if new_state_file == "" {
+		new_state_file = prior_state_file + ".new"
+	}
+
+	if err = os.Chdir(top); err != nil {
+		panic(err)
+	}
+
+	inputs = flag.Args()
+	if inputs_file != "" {
+		data, err := os.ReadFile(inputs_file)
+		if err != nil {
+			panic(err)
+		}
+		inputs = append(inputs, strings.Split(string(data), "\n")...)
+	}
+
+	// Read the prior state
+	prior_state, err := fid_lib.LoadState(prior_state_file, fid_lib.OsFs)
+	if err != nil {
+		panic(err)
+	}
+	// Create the new state
+	new_state, err := fid_lib.CreateState(inputs, inspect, fid_lib.OsFs)
+	if err != nil {
+		panic(err)
+	}
+	if err = fid_lib.WriteState(new_state, new_state_file); err != nil {
+		panic(err)
+	}
+
+	file_list := *fid_lib.CompareInternalState(prior_state, new_state, target)
+
+	if err = file_list.Format(os.Stdout, template); err != nil {
+		panic(err)
+	}
+
+	metrics_file := os.Getenv("SOONG_METRICS_AGGREGATION_FILE")
+	if metrics_file != "" {
+		if err = file_list.SendMetrics(metrics_file); err != nil {
+			panic(err)
+		}
+	}
+}
diff --git a/testing/code_metadata_internal_proto/Android.bp b/cmd/find_input_delta/find_input_delta_lib/Android.bp
similarity index 63%
copy from testing/code_metadata_internal_proto/Android.bp
copy to cmd/find_input_delta/find_input_delta_lib/Android.bp
index 396e44f..ef9c65b 100644
--- a/testing/code_metadata_internal_proto/Android.bp
+++ b/cmd/find_input_delta/find_input_delta_lib/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2022 Google Inc. All rights reserved.
+// 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.
@@ -17,17 +17,20 @@
 }
 
 bootstrap_go_package {
-    name: "soong-testing-code_metadata_internal_proto",
-    pkgPath: "android/soong/testing/code_metadata_internal_proto",
+    name: "soong-cmd-find_input_delta-lib",
+    pkgPath: "android/soong/cmd/find_input_delta/find_input_delta_lib",
     deps: [
+        "golang-protobuf-encoding-prototext",
         "golang-protobuf-reflect-protoreflect",
         "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-find_input_delta-proto",
+        "soong-cmd-find_input_delta-proto_internal",
+        "android-archive-zip",
+        "blueprint-pathtools",
     ],
     srcs: [
-        "code_metadata_internal.pb.go",
-    ],
-    visibility: [
-        "//build/make/tools/metadata",
-        "//build/soong:__subpackages__",
+        "fs.go",
+        "file_list.go",
+        "internal_state.go",
     ],
 }
diff --git a/cmd/find_input_delta/find_input_delta_lib/file_list.go b/cmd/find_input_delta/find_input_delta_lib/file_list.go
new file mode 100644
index 0000000..01242a0
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/file_list.go
@@ -0,0 +1,203 @@
+// 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 find_input_delta_lib
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"slices"
+	"text/template"
+
+	fid_exp "android/soong/cmd/find_input_delta/find_input_delta_proto"
+	"google.golang.org/protobuf/encoding/protowire"
+	"google.golang.org/protobuf/proto"
+)
+
+var DefaultTemplate = `
+	{{- define "contents"}}
+		{{- range .Deletions}}-{{.}} {{end}}
+		{{- range .Additions}}+{{.}} {{end}}
+		{{- range .Changes}}+{{- .Name}} {{end}}
+		{{- range .Changes}}
+		  {{- if or .Additions .Deletions .Changes}}--file {{.Name}} {{template "contents" .}}--endfile {{end}}
+		{{- end}}
+	{{- end}}
+	{{- template "contents" .}}`
+
+type FileList struct {
+	// The name of the parent for the list of file differences.
+	// For the outermost FileList, this is the name of the ninja target.
+	// Under `Changes`, it is the name of the changed file.
+	Name string
+
+	// The added files
+	Additions []string
+
+	// The deleted files
+	Deletions []string
+
+	// The modified files
+	Changes []FileList
+
+	// Map of file_extension:counts
+	ExtCountMap map[string]*FileCounts
+
+	// Total number of added/changed/deleted files.
+	TotalDelta uint32
+}
+
+// The maximum number of files that will be recorded by name.
+var MaxFilesRecorded uint32 = 50
+
+type FileCounts struct {
+	Additions uint32
+	Deletions uint32
+	Changes   uint32
+}
+
+func FileListFactory(name string) *FileList {
+	return &FileList{
+		Name:        name,
+		ExtCountMap: make(map[string]*FileCounts),
+	}
+}
+
+func (fl *FileList) addFile(name string) {
+	fl.Additions = append(fl.Additions, name)
+	fl.TotalDelta += 1
+	ext := filepath.Ext(name)
+	if _, ok := fl.ExtCountMap[ext]; !ok {
+		fl.ExtCountMap[ext] = &FileCounts{}
+	}
+	fl.ExtCountMap[ext].Additions += 1
+}
+
+func (fl *FileList) deleteFile(name string) {
+	fl.Deletions = append(fl.Deletions, name)
+	fl.TotalDelta += 1
+	ext := filepath.Ext(name)
+	if _, ok := fl.ExtCountMap[ext]; !ok {
+		fl.ExtCountMap[ext] = &FileCounts{}
+	}
+	fl.ExtCountMap[ext].Deletions += 1
+}
+
+func (fl *FileList) changeFile(name string, ch *FileList) {
+	fl.Changes = append(fl.Changes, *ch)
+	fl.TotalDelta += 1
+	ext := filepath.Ext(name)
+	if _, ok := fl.ExtCountMap[ext]; !ok {
+		fl.ExtCountMap[ext] = &FileCounts{}
+	}
+	fl.ExtCountMap[ext].Changes += 1
+}
+
+func (fl FileList) ToProto() (*fid_exp.FileList, error) {
+	var count uint32
+	return fl.toProto(&count)
+}
+
+func (fl FileList) toProto(count *uint32) (*fid_exp.FileList, error) {
+	ret := &fid_exp.FileList{
+		Name: proto.String(fl.Name),
+	}
+	for _, a := range fl.Additions {
+		if *count >= MaxFilesRecorded {
+			break
+		}
+		ret.Additions = append(ret.Additions, a)
+		*count += 1
+	}
+	for _, ch := range fl.Changes {
+		if *count >= MaxFilesRecorded {
+			break
+		} else {
+			// Pre-increment to limit what the call adds.
+			*count += 1
+			change, err := ch.toProto(count)
+			if err != nil {
+				return nil, err
+			}
+			ret.Changes = append(ret.Changes, change)
+		}
+	}
+	for _, d := range fl.Deletions {
+		if *count >= MaxFilesRecorded {
+			break
+		}
+		ret.Deletions = append(ret.Deletions, d)
+	}
+	ret.TotalDelta = proto.Uint32(*count)
+	exts := []string{}
+	for k := range fl.ExtCountMap {
+		exts = append(exts, k)
+	}
+	slices.Sort(exts)
+	for _, k := range exts {
+		v := fl.ExtCountMap[k]
+		ret.Counts = append(ret.Counts, &fid_exp.FileCount{
+			Extension:     proto.String(k),
+			Additions:     proto.Uint32(v.Additions),
+			Deletions:     proto.Uint32(v.Deletions),
+			Modifications: proto.Uint32(v.Changes),
+		})
+	}
+	return ret, nil
+}
+
+func (fl FileList) SendMetrics(path string) error {
+	if path == "" {
+		return fmt.Errorf("No path given")
+	}
+	message, err := fl.ToProto()
+	if err != nil {
+		return err
+	}
+
+	// Marshal the message wrapped in SoongCombinedMetrics.
+	data := protowire.AppendVarint(
+		[]byte{},
+		protowire.EncodeTag(
+			protowire.Number(fid_exp.FieldNumbers_FIELD_NUMBERS_FILE_LIST),
+			protowire.BytesType))
+	size := uint64(proto.Size(message))
+	data = protowire.AppendVarint(data, size)
+	data, err = proto.MarshalOptions{UseCachedSize: true}.MarshalAppend(data, message)
+	if err != nil {
+		return err
+	}
+
+	out, err := os.Create(path)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		if err := out.Close(); err != nil {
+			fmt.Fprintf(os.Stderr, "Failed to close %s: %v\n", path, err)
+		}
+	}()
+	_, err = out.Write(data)
+	return err
+}
+
+func (fl FileList) Format(wr io.Writer, format string) error {
+	tmpl, err := template.New("filelist").Parse(format)
+	if err != nil {
+		return err
+	}
+	return tmpl.Execute(wr, fl)
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/file_list_test.go b/cmd/find_input_delta/find_input_delta_lib/file_list_test.go
new file mode 100644
index 0000000..2459f1e
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/file_list_test.go
@@ -0,0 +1,131 @@
+// 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 find_input_delta_lib
+
+import (
+	"bytes"
+	"slices"
+	"testing"
+
+	// For Assert*.
+	"android/soong/android"
+)
+
+func (fl *FileList) Equal(other *FileList) bool {
+	if fl.Name != other.Name {
+		return false
+	}
+	if !slices.Equal(fl.Additions, other.Additions) {
+		return false
+	}
+	if !slices.Equal(fl.Deletions, other.Deletions) {
+		return false
+	}
+	if len(fl.Changes) != len(other.Changes) {
+		return false
+	}
+	for idx, ch := range fl.Changes {
+		if !ch.Equal(&other.Changes[idx]) {
+			return false
+		}
+	}
+	return true
+}
+
+func TestFormat(t *testing.T) {
+	testCases := []struct {
+		Name     string
+		Template string
+		Input    FileList
+		Expected string
+		Err      error
+	}{
+		{
+			Name:     "no contents",
+			Template: DefaultTemplate,
+			Input: FileList{
+				Name:      "target",
+				Additions: []string{"add1", "add2"},
+				Deletions: []string{"del1", "del2"},
+				Changes: []FileList{
+					FileList{Name: "mod1"},
+					FileList{Name: "mod2"},
+				},
+			},
+			Expected: "-del1 -del2 +add1 +add2 +mod1 +mod2 ",
+			Err:      nil,
+		},
+		{
+			Name:     "adds",
+			Template: DefaultTemplate,
+			Input: FileList{
+				Name:      "target",
+				Additions: []string{"add1", "add2"},
+			},
+			Expected: "+add1 +add2 ",
+			Err:      nil,
+		},
+		{
+			Name:     "deletes",
+			Template: DefaultTemplate,
+			Input: FileList{
+				Name:      "target",
+				Deletions: []string{"del1", "del2"},
+			},
+			Expected: "-del1 -del2 ",
+			Err:      nil,
+		},
+		{
+			Name:     "changes",
+			Template: DefaultTemplate,
+			Input: FileList{
+				Name: "target",
+				Changes: []FileList{
+					FileList{Name: "mod1"},
+					FileList{Name: "mod2"},
+				},
+			},
+			Expected: "+mod1 +mod2 ",
+			Err:      nil,
+		},
+		{
+			Name:     "with contents",
+			Template: DefaultTemplate,
+			Input: FileList{
+				Name:      "target",
+				Additions: []string{"add1", "add2"},
+				Deletions: []string{"del1", "del2"},
+				Changes: []FileList{
+					FileList{
+						Name: "mod1",
+					},
+					FileList{
+						Name:      "mod2",
+						Additions: []string{"a1"},
+						Deletions: []string{"d1"},
+					},
+				},
+			},
+			Expected: "-del1 -del2 +add1 +add2 +mod1 +mod2 --file mod2 -d1 +a1 --endfile ",
+			Err:      nil,
+		},
+	}
+	for _, tc := range testCases {
+		buf := bytes.NewBuffer([]byte{})
+		err := tc.Input.Format(buf, tc.Template)
+		android.AssertSame(t, tc.Name, tc.Err, err)
+		android.AssertSame(t, tc.Name, tc.Expected, buf.String())
+	}
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/fs.go b/cmd/find_input_delta/find_input_delta_lib/fs.go
new file mode 100644
index 0000000..09a8aa6
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/fs.go
@@ -0,0 +1,37 @@
+// 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 find_input_delta_lib
+
+import (
+	"io/fs"
+	"os"
+)
+
+// OsFs provides a minimal implementation so that we can use testing/fstest for
+// unit tests.
+var OsFs fileSystem = osFS{}
+
+type fileSystem interface {
+	Open(path string) (fs.File, error)
+	Stat(path string) (os.FileInfo, error)
+	ReadFile(path string) ([]byte, error)
+}
+
+// osFS implements fileSystem using the local disk.
+type osFS struct{}
+
+func (osFS) Open(path string) (fs.File, error)     { return os.Open(path) }
+func (osFS) Stat(path string) (os.FileInfo, error) { return os.Stat(path) }
+func (osFS) ReadFile(path string) ([]byte, error)  { return os.ReadFile(path) }
diff --git a/cmd/find_input_delta/find_input_delta_lib/internal_state.go b/cmd/find_input_delta/find_input_delta_lib/internal_state.go
new file mode 100644
index 0000000..2b8c395
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/internal_state.go
@@ -0,0 +1,145 @@
+// 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 find_input_delta_lib
+
+import (
+	"errors"
+	"fmt"
+	"io/fs"
+	"regexp"
+	"slices"
+
+	fid_proto "android/soong/cmd/find_input_delta/find_input_delta_proto_internal"
+	"android/soong/third_party/zip"
+	"github.com/google/blueprint/pathtools"
+	"google.golang.org/protobuf/proto"
+)
+
+// Load the internal state from a file.
+// If the file does not exist, an empty state is returned.
+func LoadState(filename string, fsys fs.ReadFileFS) (*fid_proto.PartialCompileInputs, error) {
+	var message = &fid_proto.PartialCompileInputs{}
+	data, err := fsys.ReadFile(filename)
+	if err != nil && !errors.Is(err, fs.ErrNotExist) {
+		return message, err
+	}
+	proto.Unmarshal(data, message)
+	return message, nil
+}
+
+type StatReadFileFS interface {
+	fs.StatFS
+	fs.ReadFileFS
+}
+
+// Create the internal state by examining the inputs.
+func CreateState(inputs []string, inspect_contents bool, fsys StatReadFileFS) (*fid_proto.PartialCompileInputs, error) {
+	ret := &fid_proto.PartialCompileInputs{}
+	slices.Sort(inputs)
+	for _, input := range inputs {
+		stat, err := fs.Stat(fsys, input)
+		if err != nil {
+			return ret, err
+		}
+		pci := &fid_proto.PartialCompileInput{
+			Name:      proto.String(input),
+			MtimeNsec: proto.Int64(stat.ModTime().UnixNano()),
+			// If we ever have an easy hash, assign it here.
+		}
+		if inspect_contents {
+			// NOTE: When we find it useful, we can parallelize the file inspection for speed.
+			contents, err := InspectFileContents(input)
+			if err != nil {
+				return ret, err
+			}
+			if contents != nil {
+				pci.Contents = contents
+			}
+		}
+		ret.InputFiles = append(ret.InputFiles, pci)
+	}
+	return ret, nil
+}
+
+// We ignore any suffix digit caused by sharding.
+var InspectExtsZipRegexp = regexp.MustCompile("\\.(jar|apex|apk)[0-9]*$")
+
+// Inspect the file and extract the state of the elements in the archive.
+// If this is not an archive of some sort, nil is returned.
+func InspectFileContents(name string) ([]*fid_proto.PartialCompileInput, error) {
+	if InspectExtsZipRegexp.Match([]byte(name)) {
+		return inspectZipFileContents(name)
+	}
+	return nil, nil
+}
+
+func inspectZipFileContents(name string) ([]*fid_proto.PartialCompileInput, error) {
+	rc, err := zip.OpenReader(name)
+	if err != nil {
+		return nil, err
+	}
+	ret := []*fid_proto.PartialCompileInput{}
+	for _, v := range rc.File {
+		pci := &fid_proto.PartialCompileInput{
+			Name:      proto.String(v.Name),
+			MtimeNsec: proto.Int64(v.ModTime().UnixNano()),
+			Hash:      proto.String(fmt.Sprintf("%08x", v.CRC32)),
+		}
+		ret = append(ret, pci)
+		// We do not support nested inspection.
+	}
+	return ret, nil
+}
+
+func WriteState(s *fid_proto.PartialCompileInputs, path string) error {
+	data, err := proto.Marshal(s)
+	if err != nil {
+		return err
+	}
+	return pathtools.WriteFileIfChanged(path, data, 0644)
+}
+
+func CompareInternalState(prior, other *fid_proto.PartialCompileInputs, target string) *FileList {
+	return CompareInputFiles(prior.GetInputFiles(), other.GetInputFiles(), target)
+}
+
+func CompareInputFiles(prior, other []*fid_proto.PartialCompileInput, name string) *FileList {
+	fl := FileListFactory(name)
+	PriorMap := make(map[string]*fid_proto.PartialCompileInput, len(prior))
+	// We know that the lists are properly sorted, so we can simply compare them.
+	for _, v := range prior {
+		PriorMap[v.GetName()] = v
+	}
+	otherMap := make(map[string]*fid_proto.PartialCompileInput, len(other))
+	for _, v := range other {
+		name = v.GetName()
+		otherMap[name] = v
+		if _, ok := PriorMap[name]; !ok {
+			// Added file
+			fl.addFile(name)
+		} else if !proto.Equal(PriorMap[name], v) {
+			// Changed file
+			fl.changeFile(name, CompareInputFiles(PriorMap[name].GetContents(), v.GetContents(), name))
+		}
+	}
+	for _, v := range prior {
+		name := v.GetName()
+		if _, ok := otherMap[name]; !ok {
+			// Deleted file
+			fl.deleteFile(name)
+		}
+	}
+	return fl
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/internal_state_test.go b/cmd/find_input_delta/find_input_delta_lib/internal_state_test.go
new file mode 100644
index 0000000..c168d5a
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/internal_state_test.go
@@ -0,0 +1,283 @@
+// 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 find_input_delta_lib
+
+import (
+	"errors"
+	"io/fs"
+	"testing"
+	"testing/fstest"
+	"time"
+
+	// For Assert*.
+	"android/soong/android"
+
+	fid_proto "android/soong/cmd/find_input_delta/find_input_delta_proto_internal"
+	"google.golang.org/protobuf/proto"
+)
+
+// Various state files
+
+func marshalProto(t *testing.T, message proto.Message) []byte {
+	data, err := proto.Marshal(message)
+	if err != nil {
+		t.Errorf("%v", err)
+	}
+	return data
+}
+
+func protoFile(name string, mtime_nsec int64, hash string, contents []*fid_proto.PartialCompileInput) (pci *fid_proto.PartialCompileInput) {
+	pci = &fid_proto.PartialCompileInput{
+		Name: proto.String(name),
+	}
+	if mtime_nsec != 0 {
+		pci.MtimeNsec = proto.Int64(mtime_nsec)
+	}
+	if len(hash) > 0 {
+		pci.Hash = proto.String(hash)
+	}
+	if contents != nil {
+		pci.Contents = contents
+	}
+	return
+}
+
+func TestLoadState(t *testing.T) {
+	testCases := []struct {
+		Name     string
+		Filename string
+		Mapfs    fs.ReadFileFS
+		Expected *fid_proto.PartialCompileInputs
+		Err      error
+	}{
+		{
+			Name:     "missing file",
+			Filename: "missing",
+			Mapfs:    fstest.MapFS{},
+			Expected: &fid_proto.PartialCompileInputs{},
+			Err:      nil,
+		},
+		{
+			Name:     "bad file",
+			Filename: ".",
+			Mapfs:    OsFs,
+			Expected: &fid_proto.PartialCompileInputs{},
+			Err:      errors.New("read failed"),
+		},
+		{
+			Name:     "file with mtime",
+			Filename: "state.old",
+			Mapfs: fstest.MapFS{
+				"state.old": &fstest.MapFile{
+					Data: marshalProto(t, &fid_proto.PartialCompileInputs{
+						InputFiles: []*fid_proto.PartialCompileInput{
+							protoFile("input1", 100, "", nil),
+						},
+					}),
+				},
+			},
+			Expected: &fid_proto.PartialCompileInputs{
+				InputFiles: []*fid_proto.PartialCompileInput{
+					protoFile("input1", 100, "", nil),
+				},
+			},
+			Err: nil,
+		},
+		{
+			Name:     "file with mtime and hash",
+			Filename: "state.old",
+			Mapfs: fstest.MapFS{
+				"state.old": &fstest.MapFile{
+					Data: marshalProto(t, &fid_proto.PartialCompileInputs{
+						InputFiles: []*fid_proto.PartialCompileInput{
+							protoFile("input1", 100, "crc:crc_value", nil),
+						},
+					}),
+				},
+			},
+			Expected: &fid_proto.PartialCompileInputs{
+				InputFiles: []*fid_proto.PartialCompileInput{
+					protoFile("input1", 100, "crc:crc_value", nil),
+				},
+			},
+			Err: nil,
+		},
+	}
+	for _, tc := range testCases {
+		actual, err := LoadState(tc.Filename, tc.Mapfs)
+		if tc.Err == nil {
+			android.AssertSame(t, tc.Name, tc.Err, err)
+		} else if err == nil {
+			t.Errorf("%s: expected error, did not get one", tc.Name)
+		}
+		if !proto.Equal(tc.Expected, actual) {
+			t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
+		}
+	}
+}
+
+func TestCreateState(t *testing.T) {
+	testCases := []struct {
+		Name     string
+		Inputs   []string
+		Inspect  bool
+		Mapfs    StatReadFileFS
+		Expected *fid_proto.PartialCompileInputs
+		Err      error
+	}{
+		{
+			Name:     "no inputs",
+			Inputs:   []string{},
+			Mapfs:    fstest.MapFS{},
+			Expected: &fid_proto.PartialCompileInputs{},
+			Err:      nil,
+		},
+		{
+			Name:   "files found",
+			Inputs: []string{"baz", "foo", "bar"},
+			Mapfs: fstest.MapFS{
+				"foo": &fstest.MapFile{ModTime: time.Unix(0, 100).UTC()},
+				"baz": &fstest.MapFile{ModTime: time.Unix(0, 300).UTC()},
+				"bar": &fstest.MapFile{ModTime: time.Unix(0, 200).UTC()},
+			},
+			Expected: &fid_proto.PartialCompileInputs{
+				InputFiles: []*fid_proto.PartialCompileInput{
+					// Files are always sorted.
+					protoFile("bar", 200, "", nil),
+					protoFile("baz", 300, "", nil),
+					protoFile("foo", 100, "", nil),
+				},
+			},
+			Err: nil,
+		},
+	}
+	for _, tc := range testCases {
+		actual, err := CreateState(tc.Inputs, tc.Inspect, tc.Mapfs)
+		if tc.Err == nil {
+			android.AssertSame(t, tc.Name, tc.Err, err)
+		} else if err == nil {
+			t.Errorf("%s: expected error, did not get one", tc.Name)
+		}
+		if !proto.Equal(tc.Expected, actual) {
+			t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
+		}
+	}
+}
+
+func TestCompareInternalState(t *testing.T) {
+	testCases := []struct {
+		Name     string
+		Target   string
+		Prior    *fid_proto.PartialCompileInputs
+		New      *fid_proto.PartialCompileInputs
+		Expected *FileList
+	}{
+		{
+			Name:   "prior is empty",
+			Target: "foo",
+			Prior:  &fid_proto.PartialCompileInputs{},
+			New: &fid_proto.PartialCompileInputs{
+				InputFiles: []*fid_proto.PartialCompileInput{
+					protoFile("file1", 100, "", nil),
+				},
+			},
+			Expected: &FileList{
+				Name:      "foo",
+				Additions: []string{"file1"},
+			},
+		},
+		{
+			Name:   "one each add modify delete",
+			Target: "foo",
+			Prior: &fid_proto.PartialCompileInputs{
+				InputFiles: []*fid_proto.PartialCompileInput{
+					protoFile("file0", 100, "", nil),
+					protoFile("file1", 100, "", nil),
+					protoFile("file2", 200, "", nil),
+				},
+			},
+			New: &fid_proto.PartialCompileInputs{
+				InputFiles: []*fid_proto.PartialCompileInput{
+					protoFile("file0", 100, "", nil),
+					protoFile("file1", 200, "", nil),
+					protoFile("file3", 300, "", nil),
+				},
+			},
+			Expected: &FileList{
+				Name:      "foo",
+				Additions: []string{"file3"},
+				Changes:   []FileList{FileList{Name: "file1"}},
+				Deletions: []string{"file2"},
+			},
+		},
+		{
+			Name:   "interior one each add modify delete",
+			Target: "bar",
+			Prior: &fid_proto.PartialCompileInputs{
+				InputFiles: []*fid_proto.PartialCompileInput{
+					protoFile("file1", 405, "", []*fid_proto.PartialCompileInput{
+						protoFile("innerC", 400, "crc32:11111111", nil),
+						protoFile("innerD", 400, "crc32:44444444", nil),
+					}),
+				},
+			},
+			New: &fid_proto.PartialCompileInputs{
+				InputFiles: []*fid_proto.PartialCompileInput{
+					protoFile("file1", 505, "", []*fid_proto.PartialCompileInput{
+						protoFile("innerA", 400, "crc32:55555555", nil),
+						protoFile("innerC", 500, "crc32:66666666", nil),
+					}),
+				},
+			},
+			Expected: &FileList{
+				Name: "bar",
+				Changes: []FileList{FileList{
+					Name:      "file1",
+					Additions: []string{"innerA"},
+					Changes:   []FileList{FileList{Name: "innerC"}},
+					Deletions: []string{"innerD"},
+				}},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		actual := CompareInternalState(tc.Prior, tc.New, tc.Target)
+		if !tc.Expected.Equal(actual) {
+			t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
+		}
+	}
+}
+
+func TestCompareInspectExtsZipRegexp(t *testing.T) {
+	testCases := []struct {
+		Name     string
+		Expected bool
+	}{
+		{Name: ".jar", Expected: true},
+		{Name: ".jar5", Expected: true},
+		{Name: ".apex", Expected: true},
+		{Name: ".apex9", Expected: true},
+		{Name: ".apexx", Expected: false},
+		{Name: ".apk", Expected: true},
+		{Name: ".apk3", Expected: true},
+		{Name: ".go", Expected: false},
+	}
+	for _, tc := range testCases {
+		actual := InspectExtsZipRegexp.Match([]byte(tc.Name))
+		if tc.Expected != actual {
+			t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
+		}
+	}
+}
diff --git a/testing/code_metadata_internal_proto/Android.bp b/cmd/find_input_delta/find_input_delta_proto/Android.bp
similarity index 70%
copy from testing/code_metadata_internal_proto/Android.bp
copy to cmd/find_input_delta/find_input_delta_proto/Android.bp
index 396e44f..1a05b9e 100644
--- a/testing/code_metadata_internal_proto/Android.bp
+++ b/cmd/find_input_delta/find_input_delta_proto/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2022 Google Inc. All rights reserved.
+// 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.
@@ -17,17 +17,13 @@
 }
 
 bootstrap_go_package {
-    name: "soong-testing-code_metadata_internal_proto",
-    pkgPath: "android/soong/testing/code_metadata_internal_proto",
+    name: "soong-cmd-find_input_delta-proto",
+    pkgPath: "android/soong/cmd/find_input_delta/find_input_delta_proto",
     deps: [
         "golang-protobuf-reflect-protoreflect",
         "golang-protobuf-runtime-protoimpl",
     ],
     srcs: [
-        "code_metadata_internal.pb.go",
-    ],
-    visibility: [
-        "//build/make/tools/metadata",
-        "//build/soong:__subpackages__",
+        "file_list.pb.go",
     ],
 }
diff --git a/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go b/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go
new file mode 100644
index 0000000..745de2d
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go
@@ -0,0 +1,385 @@
+//
+// 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.33.0
+// 	protoc        v3.21.12
+// source: file_list.proto
+
+package find_input_delta_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 FieldNumbers int32
+
+const (
+	FieldNumbers_FIELD_NUMBERS_UNSPECIFIED FieldNumbers = 0
+	FieldNumbers_FIELD_NUMBERS_FILE_LIST   FieldNumbers = 1
+)
+
+// Enum value maps for FieldNumbers.
+var (
+	FieldNumbers_name = map[int32]string{
+		0: "FIELD_NUMBERS_UNSPECIFIED",
+		1: "FIELD_NUMBERS_FILE_LIST",
+	}
+	FieldNumbers_value = map[string]int32{
+		"FIELD_NUMBERS_UNSPECIFIED": 0,
+		"FIELD_NUMBERS_FILE_LIST":   1,
+	}
+)
+
+func (x FieldNumbers) Enum() *FieldNumbers {
+	p := new(FieldNumbers)
+	*p = x
+	return p
+}
+
+func (x FieldNumbers) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (FieldNumbers) Descriptor() protoreflect.EnumDescriptor {
+	return file_file_list_proto_enumTypes[0].Descriptor()
+}
+
+func (FieldNumbers) Type() protoreflect.EnumType {
+	return &file_file_list_proto_enumTypes[0]
+}
+
+func (x FieldNumbers) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *FieldNumbers) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+	if err != nil {
+		return err
+	}
+	*x = FieldNumbers(num)
+	return nil
+}
+
+// Deprecated: Use FieldNumbers.Descriptor instead.
+func (FieldNumbers) EnumDescriptor() ([]byte, []int) {
+	return file_file_list_proto_rawDescGZIP(), []int{0}
+}
+
+type FileList struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the file.
+	// In the outermost message, this is the name of the Ninja target.
+	// When used in `changes`, this is the name of the changed file.
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// The added files.
+	Additions []string `protobuf:"bytes,2,rep,name=additions" json:"additions,omitempty"`
+	// The changed files.
+	Changes []*FileList `protobuf:"bytes,3,rep,name=changes" json:"changes,omitempty"`
+	// The deleted files.
+	Deletions []string `protobuf:"bytes,4,rep,name=deletions" json:"deletions,omitempty"`
+	// Count of files added/changed/deleted.
+	TotalDelta *uint32 `protobuf:"varint,5,opt,name=total_delta,json=totalDelta" json:"total_delta,omitempty"`
+	// Counts by extension.
+	Counts []*FileCount `protobuf:"bytes,6,rep,name=counts" json:"counts,omitempty"`
+}
+
+func (x *FileList) Reset() {
+	*x = FileList{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_file_list_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FileList) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FileList) ProtoMessage() {}
+
+func (x *FileList) ProtoReflect() protoreflect.Message {
+	mi := &file_file_list_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 FileList.ProtoReflect.Descriptor instead.
+func (*FileList) Descriptor() ([]byte, []int) {
+	return file_file_list_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *FileList) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *FileList) GetAdditions() []string {
+	if x != nil {
+		return x.Additions
+	}
+	return nil
+}
+
+func (x *FileList) GetChanges() []*FileList {
+	if x != nil {
+		return x.Changes
+	}
+	return nil
+}
+
+func (x *FileList) GetDeletions() []string {
+	if x != nil {
+		return x.Deletions
+	}
+	return nil
+}
+
+func (x *FileList) GetTotalDelta() uint32 {
+	if x != nil && x.TotalDelta != nil {
+		return *x.TotalDelta
+	}
+	return 0
+}
+
+func (x *FileList) GetCounts() []*FileCount {
+	if x != nil {
+		return x.Counts
+	}
+	return nil
+}
+
+type FileCount struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The file extension
+	Extension *string `protobuf:"bytes,1,opt,name=extension" json:"extension,omitempty"`
+	// Number of added files with this extension.
+	Additions *uint32 `protobuf:"varint,2,opt,name=additions" json:"additions,omitempty"`
+	// Number of modified files with this extension.
+	Modifications *uint32 `protobuf:"varint,3,opt,name=modifications" json:"modifications,omitempty"`
+	// Number of deleted files with this extension.
+	Deletions *uint32 `protobuf:"varint,4,opt,name=deletions" json:"deletions,omitempty"`
+}
+
+func (x *FileCount) Reset() {
+	*x = FileCount{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_file_list_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FileCount) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FileCount) ProtoMessage() {}
+
+func (x *FileCount) ProtoReflect() protoreflect.Message {
+	mi := &file_file_list_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 FileCount.ProtoReflect.Descriptor instead.
+func (*FileCount) Descriptor() ([]byte, []int) {
+	return file_file_list_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *FileCount) GetExtension() string {
+	if x != nil && x.Extension != nil {
+		return *x.Extension
+	}
+	return ""
+}
+
+func (x *FileCount) GetAdditions() uint32 {
+	if x != nil && x.Additions != nil {
+		return *x.Additions
+	}
+	return 0
+}
+
+func (x *FileCount) GetModifications() uint32 {
+	if x != nil && x.Modifications != nil {
+		return *x.Modifications
+	}
+	return 0
+}
+
+func (x *FileCount) GetDeletions() uint32 {
+	if x != nil && x.Deletions != nil {
+		return *x.Deletions
+	}
+	return 0
+}
+
+var File_file_list_proto protoreflect.FileDescriptor
+
+var file_file_list_proto_rawDesc = []byte{
+	0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x12, 0x1e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x5f,
+	0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x22, 0x82, 0x02, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 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, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x12, 0x42, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x28, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64,
+	0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x63, 0x68, 0x61,
+	0x6e, 0x67, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x6c, 0x74,
+	0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x65,
+	0x6c, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x06, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69,
+	0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x06,
+	0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0x8b, 0x01, 0x0a, 0x09, 0x46, 0x69, 0x6c, 0x65, 0x43,
+	0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
+	0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+	0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x2a, 0x4a, 0x0a, 0x0c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x75, 0x6d,
+	0x62, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x4e, 0x55,
+	0x4d, 0x42, 0x45, 0x52, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
+	0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x4e, 0x55, 0x4d,
+	0x42, 0x45, 0x52, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x01,
+	0x42, 0x3b, 0x5a, 0x39, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e,
+	0x67, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74,
+	0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75,
+	0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_file_list_proto_rawDescOnce sync.Once
+	file_file_list_proto_rawDescData = file_file_list_proto_rawDesc
+)
+
+func file_file_list_proto_rawDescGZIP() []byte {
+	file_file_list_proto_rawDescOnce.Do(func() {
+		file_file_list_proto_rawDescData = protoimpl.X.CompressGZIP(file_file_list_proto_rawDescData)
+	})
+	return file_file_list_proto_rawDescData
+}
+
+var file_file_list_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_file_list_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_file_list_proto_goTypes = []interface{}{
+	(FieldNumbers)(0), // 0: android.find_input_delta_proto.FieldNumbers
+	(*FileList)(nil),  // 1: android.find_input_delta_proto.FileList
+	(*FileCount)(nil), // 2: android.find_input_delta_proto.FileCount
+}
+var file_file_list_proto_depIdxs = []int32{
+	1, // 0: android.find_input_delta_proto.FileList.changes:type_name -> android.find_input_delta_proto.FileList
+	2, // 1: android.find_input_delta_proto.FileList.counts:type_name -> android.find_input_delta_proto.FileCount
+	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_file_list_proto_init() }
+func file_file_list_proto_init() {
+	if File_file_list_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_file_list_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FileList); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_file_list_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FileCount); 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_file_list_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_file_list_proto_goTypes,
+		DependencyIndexes: file_file_list_proto_depIdxs,
+		EnumInfos:         file_file_list_proto_enumTypes,
+		MessageInfos:      file_file_list_proto_msgTypes,
+	}.Build()
+	File_file_list_proto = out.File
+	file_file_list_proto_rawDesc = nil
+	file_file_list_proto_goTypes = nil
+	file_file_list_proto_depIdxs = nil
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto/file_list.proto b/cmd/find_input_delta/find_input_delta_proto/file_list.proto
new file mode 100644
index 0000000..7180358
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/file_list.proto
@@ -0,0 +1,59 @@
+//
+// 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.find_input_delta_proto;
+option go_package = "android/soong/cmd/find_input_delta/find_input_delta_proto";
+
+enum FieldNumbers {
+  FIELD_NUMBERS_UNSPECIFIED = 0;
+  FIELD_NUMBERS_FILE_LIST = 1;
+}
+
+message FileList {
+  // The name of the file.
+  // In the outermost message, this is the name of the Ninja target.
+  // When used in `changes`, this is the name of the changed file.
+  optional string name = 1;
+
+  // The added files.
+  repeated string additions = 2;
+
+  // The changed files.
+  repeated FileList changes = 3;
+
+  // The deleted files.
+  repeated string deletions = 4;
+
+  // Count of files added/changed/deleted.
+  optional uint32 total_delta = 5;
+
+  // Counts by extension.
+  repeated FileCount counts = 6;
+}
+
+message FileCount {
+  // The file extension
+  optional string extension = 1;
+
+  // Number of added files with this extension.
+  optional uint32 additions = 2;
+
+  // Number of modified files with this extension.
+  optional uint32 modifications = 3;
+
+  // Number of deleted files with this extension.
+  optional uint32 deletions = 4;
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto/regen.sh b/cmd/find_input_delta/find_input_delta_proto/regen.sh
new file mode 100644
index 0000000..d773659
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. file_list.proto
diff --git a/testing/code_metadata_internal_proto/Android.bp b/cmd/find_input_delta/find_input_delta_proto_internal/Android.bp
similarity index 70%
rename from testing/code_metadata_internal_proto/Android.bp
rename to cmd/find_input_delta/find_input_delta_proto_internal/Android.bp
index 396e44f..00ba9ff 100644
--- a/testing/code_metadata_internal_proto/Android.bp
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2022 Google Inc. All rights reserved.
+// 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.
@@ -17,17 +17,13 @@
 }
 
 bootstrap_go_package {
-    name: "soong-testing-code_metadata_internal_proto",
-    pkgPath: "android/soong/testing/code_metadata_internal_proto",
+    name: "soong-cmd-find_input_delta-proto_internal",
+    pkgPath: "android/soong/cmd/find_input_delta/find_input_delta_proto_internal",
     deps: [
         "golang-protobuf-reflect-protoreflect",
         "golang-protobuf-runtime-protoimpl",
     ],
     srcs: [
-        "code_metadata_internal.pb.go",
-    ],
-    visibility: [
-        "//build/make/tools/metadata",
-        "//build/soong:__subpackages__",
+        "internal_state.pb.go",
     ],
 }
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.pb.go b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.pb.go
new file mode 100644
index 0000000..c5b048b
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.pb.go
@@ -0,0 +1,272 @@
+//
+// 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.33.0
+// 	protoc        v3.21.12
+// source: internal_state.proto
+
+package find_input_delta_proto_internal
+
+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)
+)
+
+// The state of all inputs.
+type PartialCompileInputs struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The status of each file.
+	InputFiles []*PartialCompileInput `protobuf:"bytes,1,rep,name=input_files,json=inputFiles" json:"input_files,omitempty"`
+}
+
+func (x *PartialCompileInputs) Reset() {
+	*x = PartialCompileInputs{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_state_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PartialCompileInputs) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PartialCompileInputs) ProtoMessage() {}
+
+func (x *PartialCompileInputs) ProtoReflect() protoreflect.Message {
+	mi := &file_internal_state_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 PartialCompileInputs.ProtoReflect.Descriptor instead.
+func (*PartialCompileInputs) Descriptor() ([]byte, []int) {
+	return file_internal_state_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *PartialCompileInputs) GetInputFiles() []*PartialCompileInput {
+	if x != nil {
+		return x.InputFiles
+	}
+	return nil
+}
+
+// The state of one input.
+type PartialCompileInput struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the file.
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// The timestamp of the file in (Unix) nanoseconds.
+	MtimeNsec *int64 `protobuf:"varint,2,opt,name=mtime_nsec,json=mtimeNsec" json:"mtime_nsec,omitempty"`
+	// The hash of the file.  For crc32 hashes, this will be 8 hex digits.
+	Hash *string `protobuf:"bytes,3,opt,name=hash" json:"hash,omitempty"`
+	// Contents of the file, if the file was inspected (such as jar files, etc).
+	Contents []*PartialCompileInput `protobuf:"bytes,4,rep,name=contents" json:"contents,omitempty"`
+}
+
+func (x *PartialCompileInput) Reset() {
+	*x = PartialCompileInput{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_state_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PartialCompileInput) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PartialCompileInput) ProtoMessage() {}
+
+func (x *PartialCompileInput) ProtoReflect() protoreflect.Message {
+	mi := &file_internal_state_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 PartialCompileInput.ProtoReflect.Descriptor instead.
+func (*PartialCompileInput) Descriptor() ([]byte, []int) {
+	return file_internal_state_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *PartialCompileInput) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *PartialCompileInput) GetMtimeNsec() int64 {
+	if x != nil && x.MtimeNsec != nil {
+		return *x.MtimeNsec
+	}
+	return 0
+}
+
+func (x *PartialCompileInput) GetHash() string {
+	if x != nil && x.Hash != nil {
+		return *x.Hash
+	}
+	return ""
+}
+
+func (x *PartialCompileInput) GetContents() []*PartialCompileInput {
+	if x != nil {
+		return x.Contents
+	}
+	return nil
+}
+
+var File_internal_state_proto protoreflect.FileDescriptor
+
+var file_internal_state_proto_rawDesc = []byte{
+	0x0a, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x27, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+	0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
+	0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22,
+	0x75, 0x0a, 0x14, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c,
+	0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x5d, 0x0a, 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74,
+	0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75,
+	0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x69, 0x6e,
+	0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x43, 0x6f,
+	0x6d, 0x70, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x0a, 0x69, 0x6e, 0x70, 0x75,
+	0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0xb6, 0x01, 0x0a, 0x13, 0x50, 0x61, 0x72, 0x74, 0x69,
+	0x61, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x65, 0x63,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x74, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x65,
+	0x63, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x58, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+	0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c,
+	0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
+	0x6c, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65,
+	0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42,
+	0x40, 0x5a, 0x3e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74,
+	0x61, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c,
+	0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
+	0x6c,
+}
+
+var (
+	file_internal_state_proto_rawDescOnce sync.Once
+	file_internal_state_proto_rawDescData = file_internal_state_proto_rawDesc
+)
+
+func file_internal_state_proto_rawDescGZIP() []byte {
+	file_internal_state_proto_rawDescOnce.Do(func() {
+		file_internal_state_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_state_proto_rawDescData)
+	})
+	return file_internal_state_proto_rawDescData
+}
+
+var file_internal_state_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_internal_state_proto_goTypes = []interface{}{
+	(*PartialCompileInputs)(nil), // 0: android.find_input_delta_proto_internal.PartialCompileInputs
+	(*PartialCompileInput)(nil),  // 1: android.find_input_delta_proto_internal.PartialCompileInput
+}
+var file_internal_state_proto_depIdxs = []int32{
+	1, // 0: android.find_input_delta_proto_internal.PartialCompileInputs.input_files:type_name -> android.find_input_delta_proto_internal.PartialCompileInput
+	1, // 1: android.find_input_delta_proto_internal.PartialCompileInput.contents:type_name -> android.find_input_delta_proto_internal.PartialCompileInput
+	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_internal_state_proto_init() }
+func file_internal_state_proto_init() {
+	if File_internal_state_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_internal_state_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PartialCompileInputs); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_state_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PartialCompileInput); 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_internal_state_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_internal_state_proto_goTypes,
+		DependencyIndexes: file_internal_state_proto_depIdxs,
+		MessageInfos:      file_internal_state_proto_msgTypes,
+	}.Build()
+	File_internal_state_proto = out.File
+	file_internal_state_proto_rawDesc = nil
+	file_internal_state_proto_goTypes = nil
+	file_internal_state_proto_depIdxs = nil
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.proto b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.proto
new file mode 100644
index 0000000..54c90cc
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.proto
@@ -0,0 +1,39 @@
+//
+// 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.find_input_delta_proto_internal;
+option go_package = "android/soong/find_input_delta/find_input_delta_proto_internal";
+
+// The state of all inputs.
+message PartialCompileInputs {
+  // The status of each file.
+  repeated PartialCompileInput input_files = 1;
+}
+
+// The state of one input.
+message PartialCompileInput {
+  // The name of the file.
+  optional string name = 1;
+
+  // The timestamp of the file in (Unix) nanoseconds.
+  optional int64 mtime_nsec = 2;
+
+  // The hash of the file.  For crc32 hashes, this will be 8 hex digits.
+  optional string hash = 3;
+
+  // Contents of the file, if the file was inspected (such as jar files, etc).
+  repeated PartialCompileInput contents = 4;
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh b/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh
new file mode 100644
index 0000000..cbaf7d0
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:.  internal_state.proto
diff --git a/cmd/multiproduct_kati/Android.bp b/cmd/multiproduct_kati/Android.bp
deleted file mode 100644
index 20ca2a3..0000000
--- a/cmd/multiproduct_kati/Android.bp
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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.
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-blueprint_go_binary {
-    name: "multiproduct_kati",
-    deps: [
-        "soong-ui-logger",
-        "soong-ui-signal",
-        "soong-ui-terminal",
-        "soong-ui-tracer",
-        "soong-zip",
-    ],
-    srcs: [
-        "main.go",
-    ],
-    testSrcs: [
-        "main_test.go",
-    ],
-    linux: {
-        srcs: [
-            "main_linux.go",
-        ],
-    },
-    darwin: {
-        srcs: [
-            "main_darwin.go",
-        ],
-    },
-}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
deleted file mode 100644
index c3b0381..0000000
--- a/cmd/multiproduct_kati/main.go
+++ /dev/null
@@ -1,598 +0,0 @@
-// 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.
-
-package main
-
-import (
-	"bufio"
-	"context"
-	"flag"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"log"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"regexp"
-	"runtime"
-	"strings"
-	"sync"
-	"syscall"
-	"time"
-
-	"android/soong/ui/logger"
-	"android/soong/ui/signal"
-	"android/soong/ui/status"
-	"android/soong/ui/terminal"
-	"android/soong/ui/tracer"
-	"android/soong/zip"
-)
-
-var numJobs = flag.Int("j", 0, "number of parallel jobs [0=autodetect]")
-
-var keepArtifacts = flag.Bool("keep", false, "keep archives of artifacts")
-var incremental = flag.Bool("incremental", false, "run in incremental mode (saving intermediates)")
-
-var outDir = flag.String("out", "", "path to store output directories (defaults to tmpdir under $OUT when empty)")
-var alternateResultDir = flag.Bool("dist", false, "write select results to $DIST_DIR (or <out>/dist when empty)")
-
-var bazelMode = flag.Bool("bazel-mode", false, "use bazel for analysis of certain modules")
-var bazelModeStaging = flag.Bool("bazel-mode-staging", false, "use bazel for analysis of certain near-ready modules")
-
-var onlyConfig = flag.Bool("only-config", false, "Only run product config (not Soong or Kati)")
-var onlySoong = flag.Bool("only-soong", false, "Only run product config and Soong (not Kati)")
-
-var buildVariant = flag.String("variant", "eng", "build variant to use")
-
-var shardCount = flag.Int("shard-count", 1, "split the products into multiple shards (to spread the build onto multiple machines, etc)")
-var shard = flag.Int("shard", 1, "1-indexed shard to execute")
-
-var skipProducts multipleStringArg
-var includeProducts multipleStringArg
-
-func init() {
-	flag.Var(&skipProducts, "skip-products", "comma-separated list of products to skip (known failures, etc)")
-	flag.Var(&includeProducts, "products", "comma-separated list of products to build")
-}
-
-// multipleStringArg is a flag.Value that takes comma separated lists and converts them to a
-// []string.  The argument can be passed multiple times to append more values.
-type multipleStringArg []string
-
-func (m *multipleStringArg) String() string {
-	return strings.Join(*m, `, `)
-}
-
-func (m *multipleStringArg) Set(s string) error {
-	*m = append(*m, strings.Split(s, ",")...)
-	return nil
-}
-
-const errorLeadingLines = 20
-const errorTrailingLines = 20
-
-func errMsgFromLog(filename string) string {
-	if filename == "" {
-		return ""
-	}
-
-	data, err := ioutil.ReadFile(filename)
-	if err != nil {
-		return ""
-	}
-
-	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
-	if len(lines) > errorLeadingLines+errorTrailingLines+1 {
-		lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
-			len(lines)-errorLeadingLines-errorTrailingLines)
-
-		lines = append(lines[:errorLeadingLines+1],
-			lines[len(lines)-errorTrailingLines:]...)
-	}
-	var buf strings.Builder
-	for _, line := range lines {
-		buf.WriteString("> ")
-		buf.WriteString(line)
-		buf.WriteString("\n")
-	}
-	return buf.String()
-}
-
-// TODO(b/70370883): This tool uses a lot of open files -- over the default
-// soft limit of 1024 on some systems. So bump up to the hard limit until I fix
-// the algorithm.
-func setMaxFiles(log logger.Logger) {
-	var limits syscall.Rlimit
-
-	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
-	if err != nil {
-		log.Println("Failed to get file limit:", err)
-		return
-	}
-
-	log.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
-	if limits.Cur == limits.Max {
-		return
-	}
-
-	limits.Cur = limits.Max
-	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
-	if err != nil {
-		log.Println("Failed to increase file limit:", err)
-	}
-}
-
-func inList(str string, list []string) bool {
-	for _, other := range list {
-		if str == other {
-			return true
-		}
-	}
-	return false
-}
-
-func copyFile(from, to string) error {
-	fromFile, err := os.Open(from)
-	if err != nil {
-		return err
-	}
-	defer fromFile.Close()
-
-	toFile, err := os.Create(to)
-	if err != nil {
-		return err
-	}
-	defer toFile.Close()
-
-	_, err = io.Copy(toFile, fromFile)
-	return err
-}
-
-type mpContext struct {
-	Logger logger.Logger
-	Status status.ToolStatus
-
-	SoongUi     string
-	MainOutDir  string
-	MainLogsDir string
-}
-
-func findNamedProducts(soongUi string, log logger.Logger) []string {
-	cmd := exec.Command(soongUi, "--dumpvars-mode", "--vars=all_named_products")
-	output, err := cmd.Output()
-	if err != nil {
-		log.Fatalf("Cannot determine named products: %v", err)
-	}
-
-	rx := regexp.MustCompile(`^all_named_products='(.*)'$`)
-	match := rx.FindStringSubmatch(strings.TrimSpace(string(output)))
-	return strings.Fields(match[1])
-}
-
-// ensureEmptyFileExists ensures that the containing directory exists, and the
-// specified file exists. If it doesn't exist, it will write an empty file.
-func ensureEmptyFileExists(file string, log logger.Logger) {
-	if _, err := os.Stat(file); os.IsNotExist(err) {
-		f, err := os.Create(file)
-		if err != nil {
-			log.Fatalf("Error creating %s: %q\n", file, err)
-		}
-		f.Close()
-	} else if err != nil {
-		log.Fatalf("Error checking %s: %q\n", file, err)
-	}
-}
-
-func outDirBase() string {
-	outDirBase := os.Getenv("OUT_DIR")
-	if outDirBase == "" {
-		return "out"
-	} else {
-		return outDirBase
-	}
-}
-
-func distDir(outDir string) string {
-	if distDir := os.Getenv("DIST_DIR"); distDir != "" {
-		return filepath.Clean(distDir)
-	} else {
-		return filepath.Join(outDir, "dist")
-	}
-}
-
-func forceAnsiOutput() bool {
-	value := os.Getenv("SOONG_UI_ANSI_OUTPUT")
-	return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true"
-}
-
-func getBazelArg() string {
-	count := 0
-	str := ""
-	if *bazelMode {
-		count++
-		str = "--bazel-mode"
-	}
-	if *bazelModeStaging {
-		count++
-		str = "--bazel-mode-staging"
-	}
-
-	if count > 1 {
-		// Can't set more than one
-		fmt.Errorf("Only one bazel mode is permitted to be set.")
-		os.Exit(1)
-	}
-
-	return str
-}
-
-func main() {
-	stdio := terminal.StdioImpl{}
-
-	output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false,
-		forceAnsiOutput())
-	log := logger.New(output)
-	defer log.Cleanup()
-
-	for _, v := range os.Environ() {
-		log.Println("Environment: " + v)
-	}
-
-	log.Printf("Argv: %v\n", os.Args)
-
-	flag.Parse()
-
-	_, cancel := context.WithCancel(context.Background())
-	defer cancel()
-
-	trace := tracer.New(log)
-	defer trace.Close()
-
-	stat := &status.Status{}
-	defer stat.Finish()
-	stat.AddOutput(output)
-
-	var failures failureCount
-	stat.AddOutput(&failures)
-
-	signal.SetupSignals(log, cancel, func() {
-		trace.Close()
-		log.Cleanup()
-		stat.Finish()
-	})
-
-	soongUi := "build/soong/soong_ui.bash"
-
-	var outputDir string
-	if *outDir != "" {
-		outputDir = *outDir
-	} else {
-		name := "multiproduct"
-		if !*incremental {
-			name += "-" + time.Now().Format("20060102150405")
-		}
-		outputDir = filepath.Join(outDirBase(), name)
-	}
-
-	log.Println("Output directory:", outputDir)
-
-	// The ninja_build file is used by our buildbots to understand that the output
-	// can be parsed as ninja output.
-	if err := os.MkdirAll(outputDir, 0777); err != nil {
-		log.Fatalf("Failed to create output directory: %v", err)
-	}
-	ensureEmptyFileExists(filepath.Join(outputDir, "ninja_build"), log)
-
-	logsDir := filepath.Join(outputDir, "logs")
-	os.MkdirAll(logsDir, 0777)
-
-	var configLogsDir string
-	if *alternateResultDir {
-		configLogsDir = filepath.Join(distDir(outDirBase()), "logs")
-	} else {
-		configLogsDir = outputDir
-	}
-
-	log.Println("Logs dir: " + configLogsDir)
-
-	os.MkdirAll(configLogsDir, 0777)
-	log.SetOutput(filepath.Join(configLogsDir, "soong.log"))
-	trace.SetOutput(filepath.Join(configLogsDir, "build.trace"))
-
-	var jobs = *numJobs
-	if jobs < 1 {
-		jobs = runtime.NumCPU() / 4
-
-		ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
-		if ramJobs := ramGb / 40; ramGb > 0 && jobs > ramJobs {
-			jobs = ramJobs
-		}
-
-		if jobs < 1 {
-			jobs = 1
-		}
-	}
-	log.Verbosef("Using %d parallel jobs", jobs)
-
-	setMaxFiles(log)
-
-	allProducts := findNamedProducts(soongUi, log)
-	var productsList []string
-
-	if len(includeProducts) > 0 {
-		var missingProducts []string
-		for _, product := range includeProducts {
-			if inList(product, allProducts) {
-				productsList = append(productsList, product)
-			} else {
-				missingProducts = append(missingProducts, product)
-			}
-		}
-		if len(missingProducts) > 0 {
-			log.Fatalf("Products don't exist: %s\n", missingProducts)
-		}
-	} else {
-		productsList = allProducts
-	}
-
-	finalProductsList := make([]string, 0, len(productsList))
-	skipProduct := func(p string) bool {
-		for _, s := range skipProducts {
-			if p == s {
-				return true
-			}
-		}
-		return false
-	}
-	for _, product := range productsList {
-		if !skipProduct(product) {
-			finalProductsList = append(finalProductsList, product)
-		} else {
-			log.Verbose("Skipping: ", product)
-		}
-	}
-
-	if *shard < 1 {
-		log.Fatalf("--shard value must be >= 1, not %d\n", *shard)
-	} else if *shardCount < 1 {
-		log.Fatalf("--shard-count value must be >= 1, not %d\n", *shardCount)
-	} else if *shard > *shardCount {
-		log.Fatalf("--shard (%d) must not be greater than --shard-count (%d)\n", *shard,
-			*shardCount)
-	} else if *shardCount > 1 {
-		finalProductsList = splitList(finalProductsList, *shardCount)[*shard-1]
-	}
-
-	log.Verbose("Got product list: ", finalProductsList)
-
-	s := stat.StartTool()
-	s.SetTotalActions(len(finalProductsList))
-
-	mpCtx := &mpContext{
-		Logger:      log,
-		Status:      s,
-		SoongUi:     soongUi,
-		MainOutDir:  outputDir,
-		MainLogsDir: logsDir,
-	}
-
-	products := make(chan string, len(productsList))
-	go func() {
-		defer close(products)
-		for _, product := range finalProductsList {
-			products <- product
-		}
-	}()
-
-	var wg sync.WaitGroup
-	for i := 0; i < jobs; i++ {
-		wg.Add(1)
-		// To smooth out the spikes in memory usage, skew the
-		// initial starting time of the jobs by a small amount.
-		time.Sleep(15 * time.Second)
-		go func() {
-			defer wg.Done()
-			for {
-				select {
-				case product := <-products:
-					if product == "" {
-						return
-					}
-					runSoongUiForProduct(mpCtx, product)
-				}
-			}
-		}()
-	}
-	wg.Wait()
-
-	if *alternateResultDir {
-		args := zip.ZipArgs{
-			FileArgs: []zip.FileArg{
-				{GlobDir: logsDir, SourcePrefixToStrip: logsDir},
-			},
-			OutputFilePath:   filepath.Join(distDir(outDirBase()), "logs.zip"),
-			NumParallelJobs:  runtime.NumCPU(),
-			CompressionLevel: 5,
-		}
-		log.Printf("Logs zip: %v\n", args.OutputFilePath)
-		if err := zip.Zip(args); err != nil {
-			log.Fatalf("Error zipping logs: %v", err)
-		}
-	}
-
-	s.Finish()
-
-	if failures.count == 1 {
-		log.Fatal("1 failure")
-	} else if failures.count > 1 {
-		log.Fatalf("%d failures %q", failures.count, failures.fails)
-	} else {
-		fmt.Fprintln(output, "Success")
-	}
-}
-
-func cleanupAfterProduct(outDir, productZip string) {
-	if *keepArtifacts {
-		args := zip.ZipArgs{
-			FileArgs: []zip.FileArg{
-				{
-					GlobDir:             outDir,
-					SourcePrefixToStrip: outDir,
-				},
-			},
-			OutputFilePath:   productZip,
-			NumParallelJobs:  runtime.NumCPU(),
-			CompressionLevel: 5,
-		}
-		if err := zip.Zip(args); err != nil {
-			log.Fatalf("Error zipping artifacts: %v", err)
-		}
-	}
-	if !*incremental {
-		os.RemoveAll(outDir)
-	}
-}
-
-func runSoongUiForProduct(mpctx *mpContext, product string) {
-	outDir := filepath.Join(mpctx.MainOutDir, product)
-	logsDir := filepath.Join(mpctx.MainLogsDir, product)
-	productZip := filepath.Join(mpctx.MainOutDir, product+".zip")
-	consoleLogPath := filepath.Join(logsDir, "std.log")
-
-	if err := os.MkdirAll(outDir, 0777); err != nil {
-		mpctx.Logger.Fatalf("Error creating out directory: %v", err)
-	}
-	if err := os.MkdirAll(logsDir, 0777); err != nil {
-		mpctx.Logger.Fatalf("Error creating log directory: %v", err)
-	}
-
-	consoleLogFile, err := os.Create(consoleLogPath)
-	if err != nil {
-		mpctx.Logger.Fatalf("Error creating console log file: %v", err)
-	}
-	defer consoleLogFile.Close()
-
-	consoleLogWriter := bufio.NewWriter(consoleLogFile)
-	defer consoleLogWriter.Flush()
-
-	args := []string{"--make-mode", "--skip-soong-tests", "--skip-ninja"}
-
-	if !*keepArtifacts {
-		args = append(args, "--empty-ninja-file")
-	}
-
-	if *onlyConfig {
-		args = append(args, "--config-only")
-	} else if *onlySoong {
-		args = append(args, "--soong-only")
-	}
-
-	bazelStr := getBazelArg()
-	if bazelStr != "" {
-		args = append(args, bazelStr)
-	}
-
-	cmd := exec.Command(mpctx.SoongUi, args...)
-	cmd.Stdout = consoleLogWriter
-	cmd.Stderr = consoleLogWriter
-	cmd.Env = append(os.Environ(),
-		"OUT_DIR="+outDir,
-		"TARGET_PRODUCT="+product,
-		"TARGET_BUILD_VARIANT="+*buildVariant,
-		"TARGET_BUILD_TYPE=release",
-		"TARGET_BUILD_APPS=",
-		"TARGET_BUILD_UNBUNDLED=",
-		"USE_RBE=false") // Disabling RBE saves ~10 secs per product
-
-	if *alternateResultDir {
-		cmd.Env = append(cmd.Env,
-			"DIST_DIR="+filepath.Join(distDir(outDirBase()), "products/"+product))
-	}
-
-	action := &status.Action{
-		Description: product,
-		Outputs:     []string{product},
-	}
-
-	mpctx.Status.StartAction(action)
-	defer cleanupAfterProduct(outDir, productZip)
-
-	before := time.Now()
-	err = cmd.Run()
-
-	if !*onlyConfig && !*onlySoong {
-		katiBuildNinjaFile := filepath.Join(outDir, "build-"+product+".ninja")
-		if after, err := os.Stat(katiBuildNinjaFile); err == nil && after.ModTime().After(before) {
-			err := copyFile(consoleLogPath, filepath.Join(filepath.Dir(consoleLogPath), "std_full.log"))
-			if err != nil {
-				log.Fatalf("Error copying log file: %s", err)
-			}
-		}
-	}
-	var errOutput string
-	if err == nil {
-		errOutput = ""
-	} else {
-		errOutput = errMsgFromLog(consoleLogPath)
-	}
-
-	mpctx.Status.FinishAction(status.ActionResult{
-		Action: action,
-		Error:  err,
-		Output: errOutput,
-	})
-}
-
-type failureCount struct {
-	count int
-	fails []string
-}
-
-func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
-
-func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
-	if result.Error != nil {
-		f.count += 1
-		f.fails = append(f.fails, result.Action.Description)
-	}
-}
-
-func (f *failureCount) Message(level status.MsgLevel, message string) {
-	if level >= status.ErrorLvl {
-		f.count += 1
-	}
-}
-
-func (f *failureCount) Flush() {}
-
-func (f *failureCount) Write(p []byte) (int, error) {
-	// discard writes
-	return len(p), nil
-}
-
-func splitList(list []string, shardCount int) (ret [][]string) {
-	each := len(list) / shardCount
-	extra := len(list) % shardCount
-	for i := 0; i < shardCount; i++ {
-		count := each
-		if extra > 0 {
-			count += 1
-			extra -= 1
-		}
-		ret = append(ret, list[:count])
-		list = list[count:]
-	}
-	return
-}
diff --git a/cmd/multiproduct_kati/main_darwin.go b/cmd/multiproduct_kati/main_darwin.go
deleted file mode 100644
index 3d1b12a..0000000
--- a/cmd/multiproduct_kati/main_darwin.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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.
-
-package main
-
-func detectTotalRAM() uint64 {
-	// unimplemented stub on darwin
-	return 0
-}
diff --git a/cmd/multiproduct_kati/main_linux.go b/cmd/multiproduct_kati/main_linux.go
deleted file mode 100644
index db74496..0000000
--- a/cmd/multiproduct_kati/main_linux.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.
-
-package main
-
-import (
-	"syscall"
-)
-
-func detectTotalRAM() uint64 {
-	var info syscall.Sysinfo_t
-	err := syscall.Sysinfo(&info)
-	if err != nil {
-		panic(err)
-	}
-	return info.Totalram * uint64(info.Unit)
-}
diff --git a/cmd/multiproduct_kati/main_test.go b/cmd/multiproduct_kati/main_test.go
deleted file mode 100644
index 263a124..0000000
--- a/cmd/multiproduct_kati/main_test.go
+++ /dev/null
@@ -1,93 +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 main
-
-import (
-	"fmt"
-	"reflect"
-	"testing"
-)
-
-func TestSplitList(t *testing.T) {
-	testcases := []struct {
-		inputCount int
-		shardCount int
-		want       [][]string
-	}{
-		{
-			inputCount: 1,
-			shardCount: 1,
-			want:       [][]string{{"1"}},
-		},
-		{
-			inputCount: 1,
-			shardCount: 2,
-			want:       [][]string{{"1"}, {}},
-		},
-		{
-			inputCount: 4,
-			shardCount: 2,
-			want:       [][]string{{"1", "2"}, {"3", "4"}},
-		},
-		{
-			inputCount: 19,
-			shardCount: 10,
-			want: [][]string{
-				{"1", "2"},
-				{"3", "4"},
-				{"5", "6"},
-				{"7", "8"},
-				{"9", "10"},
-				{"11", "12"},
-				{"13", "14"},
-				{"15", "16"},
-				{"17", "18"},
-				{"19"},
-			},
-		},
-		{
-			inputCount: 15,
-			shardCount: 10,
-			want: [][]string{
-				{"1", "2"},
-				{"3", "4"},
-				{"5", "6"},
-				{"7", "8"},
-				{"9", "10"},
-				{"11"},
-				{"12"},
-				{"13"},
-				{"14"},
-				{"15"},
-			},
-		},
-	}
-
-	for _, tc := range testcases {
-		t.Run(fmt.Sprintf("%d/%d", tc.inputCount, tc.shardCount), func(t *testing.T) {
-			input := []string{}
-			for i := 1; i <= tc.inputCount; i++ {
-				input = append(input, fmt.Sprintf("%d", i))
-			}
-
-			got := splitList(input, tc.shardCount)
-
-			if !reflect.DeepEqual(got, tc.want) {
-				t.Errorf("unexpected result for splitList([]string{...%d...}, %d):\nwant: %v\n got: %v\n",
-					tc.inputCount, tc.shardCount, tc.want, got)
-			}
-		})
-	}
-}
diff --git a/cmd/release_config/build_flag/main.go b/cmd/release_config/build_flag/main.go
index 5d183ee..5d103cc 100644
--- a/cmd/release_config/build_flag/main.go
+++ b/cmd/release_config/build_flag/main.go
@@ -90,6 +90,9 @@
 	if !ok {
 		return "", fmt.Errorf("%s not found in %s", name, config.Name)
 	}
+	if fa.Redacted {
+		return "==REDACTED==", nil
+	}
 	return rc_lib.MarshalValue(fa.Value), nil
 }
 
@@ -329,7 +332,7 @@
 		return err
 	}
 	updatedFiles = append(updatedFiles, flagPath)
-	fmt.Printf("Added/Updated: %s\n", strings.Join(updatedFiles, " "))
+	fmt.Printf("\033[1mAdded/Updated: %s\033[0m\n", strings.Join(updatedFiles, " "))
 	return nil
 }
 
diff --git a/cmd/release_config/release_config_contributions/Android.bp b/cmd/release_config/release_config_contributions/Android.bp
new file mode 100644
index 0000000..6882ea2
--- /dev/null
+++ b/cmd/release_config/release_config_contributions/Android.bp
@@ -0,0 +1,32 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "release-config-contributions",
+    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-release_config_contributions",
+    pkgPath: "android/soong/cmd/release_config/release_config_contributions",
+    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/release_config_contributions/main.go b/cmd/release_config/release_config_contributions/main.go
new file mode 100644
index 0000000..a954cf1
--- /dev/null
+++ b/cmd/release_config/release_config_contributions/main.go
@@ -0,0 +1,130 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+	"slices"
+	"strings"
+
+	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 release config directories to process.
+	dirs rc_lib.StringList
+
+	// Disable warning messages
+	quiet bool
+
+	// Panic on errors.
+	debug bool
+}
+
+func sortDirectories(dirList []string) {
+	order := func(dir string) int {
+		switch {
+		// These three are always in this order.
+		case dir == "build/release":
+			return 1
+		case dir == "vendor/google_shared/build/release":
+			return 2
+		case dir == "vendor/google/release":
+			return 3
+		// Keep their subdirs in the same order.
+		case strings.HasPrefix(dir, "build/release/"):
+			return 21
+		case strings.HasPrefix(dir, "vendor/google_shared/build/release/"):
+			return 22
+		case strings.HasPrefix(dir, "vendor/google/release/"):
+			return 23
+		// Everything else sorts by directory path.
+		default:
+			return 99
+		}
+	}
+
+	slices.SortFunc(dirList, func(a, b string) int {
+		aOrder, bOrder := order(a), order(b)
+		if aOrder != bOrder {
+			return aOrder - bOrder
+		}
+		return strings.Compare(a, b)
+	})
+}
+
+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.dirs, "dir", "path to a release config contribution directory. May be repeated")
+	flag.StringVar(&flags.format, "format", "pb", "output file format")
+	flag.StringVar(&flags.output, "output", "release_config_contributions.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)
+	}
+
+	contributingDirsMap := make(map[string][]string)
+	for _, dir := range flags.dirs {
+		contributions, err := rc_lib.EnumerateReleaseConfigs(dir)
+		if err != nil {
+			errorExit(err)
+		}
+		for _, name := range contributions {
+			contributingDirsMap[name] = append(contributingDirsMap[name], dir)
+		}
+	}
+
+	releaseConfigNames := []string{}
+	for name := range contributingDirsMap {
+		releaseConfigNames = append(releaseConfigNames, name)
+	}
+	slices.Sort(releaseConfigNames)
+
+	message := &rc_proto.ReleaseConfigContributionsArtifacts{
+		ReleaseConfigContributionsArtifactList: []*rc_proto.ReleaseConfigContributionsArtifact{},
+	}
+	for _, name := range releaseConfigNames {
+		dirs := contributingDirsMap[name]
+		sortDirectories(dirs)
+		message.ReleaseConfigContributionsArtifactList = append(
+			message.ReleaseConfigContributionsArtifactList,
+			&rc_proto.ReleaseConfigContributionsArtifact{
+				Name:                    &name,
+				ContributingDirectories: dirs,
+			})
+	}
+
+	err = rc_lib.WriteFormattedMessage(flags.output, flags.format, message)
+	if err != nil {
+		errorExit(err)
+	}
+}
diff --git a/cmd/release_config/release_config_lib/flag_artifact.go b/cmd/release_config/release_config_lib/flag_artifact.go
index 51c02d2..cb13fdc 100644
--- a/cmd/release_config/release_config_lib/flag_artifact.go
+++ b/cmd/release_config/release_config_lib/flag_artifact.go
@@ -84,8 +84,10 @@
 
 func (fas *FlagArtifacts) SortedFlagNames() []string {
 	var names []string
-	for k, _ := range *fas {
-		names = append(names, k)
+	for k, v := range *fas {
+		if !v.Redacted {
+			names = append(names, k)
+		}
 	}
 	slices.Sort(names)
 	return names
@@ -99,6 +101,9 @@
 	if namespace := fa.FlagDeclaration.GetNamespace(); namespace != "" {
 		ret.Namespace = proto.String(namespace)
 	}
+	if bugs := fa.FlagDeclaration.GetBugs(); bugs != nil {
+		ret.Bugs = bugs
+	}
 	if description := fa.FlagDeclaration.GetDescription(); description != "" {
 		ret.Description = proto.String(description)
 	}
@@ -180,15 +185,20 @@
 //	error: any error encountered
 func (fa *FlagArtifact) UpdateValue(flagValue FlagValue) error {
 	name := *flagValue.proto.Name
-	fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value})
-	if flagValue.proto.GetRedacted() {
-		fa.Redacted = true
+	redacted := flagValue.proto.GetRedacted()
+	if redacted {
+		fa.Redact()
+		flagValue.proto.Value = fa.Value
 		fmt.Printf("Redacting flag %s in %s\n", name, flagValue.path)
-		return nil
+	} else {
+		// If we are assigning a value, then the flag is no longer redacted.
+		fa.Redacted = false
 	}
+	fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value})
 	if fa.Value.GetObsolete() {
 		return fmt.Errorf("Attempting to set obsolete flag %s. Trace=%v", name, fa.Traces)
 	}
+
 	var newValue *rc_proto.Value
 	switch val := flagValue.proto.Value.Val.(type) {
 	case *rc_proto.Value_StringValue:
@@ -210,6 +220,11 @@
 	return nil
 }
 
+func (fa *FlagArtifact) Redact() {
+	fa.Redacted = true
+	fa.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{StringValue: "*REDACTED*"}}
+}
+
 // Marshal the FlagArtifact into a flag_artifact message.
 func (fa *FlagArtifact) Marshal() (*rc_proto.FlagArtifact, error) {
 	if fa.Redacted {
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
index ee71336..719ddc0 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -21,7 +21,6 @@
 	"path/filepath"
 	"regexp"
 	"slices"
-	"sort"
 	"strings"
 
 	rc_proto "android/soong/cmd/release_config/release_config_proto"
@@ -86,6 +85,26 @@
 	// 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
+
+	// What type of release config is this?  This should never be
+	// ReleaseConfigType_CONFIG_TYPE_UNSPECIFIED.
+	ReleaseConfigType rc_proto.ReleaseConfigType
+}
+
+// If true, this is a proper release config that can be used in "lunch".
+func (config *ReleaseConfig) isConfigListable() bool {
+	switch config.ReleaseConfigType {
+	case rc_proto.ReleaseConfigType_RELEASE_CONFIG:
+		return true
+	}
+
+	return false
+}
+
+// If true, this ReleaseConfigType may only inherit from a ReleaseConfig of the
+// same ReleaseConfigType.
+var ReleaseConfigInheritanceDenyMap = map[rc_proto.ReleaseConfigType]bool{
+	rc_proto.ReleaseConfigType_BUILD_VARIANT: true,
 }
 
 func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) {
@@ -98,6 +117,10 @@
 }
 
 func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error {
+	if config.ReleaseConfigType != iConfig.ReleaseConfigType && ReleaseConfigInheritanceDenyMap[config.ReleaseConfigType] {
+		return fmt.Errorf("Release config %s (type '%s') cannot inherit from %s (type '%s')",
+			config.Name, config.ReleaseConfigType, iConfig.Name, iConfig.ReleaseConfigType)
+	}
 	for f := range iConfig.FilesUsedMap {
 		config.FilesUsedMap[f] = true
 	}
@@ -107,6 +130,9 @@
 		if !ok {
 			return fmt.Errorf("Could not inherit flag %s from %s", name, iConfig.Name)
 		}
+		if fa.Redacted {
+			myFa.Redact()
+		}
 		if name == "RELEASE_ACONFIG_VALUE_SETS" {
 			// If there is a value assigned, add the trace.
 			if len(fa.Value.GetStringValue()) > 0 {
@@ -152,10 +178,10 @@
 	contributionsToApply := []*ReleaseConfigContribution{}
 	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")
-	if err == nil && !isRoot {
-		config.InheritNames = append([]string{"root"}, config.InheritNames...)
+	if config.ReleaseConfigType == rc_proto.ReleaseConfigType_RELEASE_CONFIG {
+		if _, err = configs.GetReleaseConfigStrict("root"); err == nil {
+			config.InheritNames = append([]string{"root"}, config.InheritNames...)
+		}
 	}
 	for _, inherit := range config.InheritNames {
 		if _, ok := myInheritsSet[inherit]; ok {
@@ -166,6 +192,13 @@
 		}
 		myInherits = append(myInherits, inherit)
 		myInheritsSet[inherit] = true
+		// TODO: there are some configs that rely on vgsbr being
+		// present on branches where it isn't. Once the broken configs
+		// are fixed, we can be more strict.  In the meantime, they
+		// will wind up inheriting `trunk_stable` instead of the
+		// non-existent (alias) that they reference today.  Once fixed,
+		// this becomes:
+		//    iConfig, err := configs.GetReleaseConfigStrict(inherit)
 		iConfig, err := configs.GetReleaseConfig(inherit)
 		if err != nil {
 			return err
@@ -261,11 +294,38 @@
 			if err := fa.UpdateValue(*value); err != nil {
 				return err
 			}
-			if fa.Redacted {
-				delete(config.FlagArtifacts, name)
+		}
+	}
+
+	if config.ReleaseConfigType == rc_proto.ReleaseConfigType_RELEASE_CONFIG {
+		inheritBuildVariant := func() error {
+			build_variant := os.Getenv("TARGET_BUILD_VARIANT")
+			if build_variant == "" || config.Name == build_variant {
+				return nil
+			}
+			variant, err := configs.GetReleaseConfigStrict(build_variant)
+			if err != nil {
+				// Failure to find the build-variant release config is
+				// not an error.
+				return nil
+			}
+			if variant.ReleaseConfigType != rc_proto.ReleaseConfigType_BUILD_VARIANT {
+				return nil
+			}
+			if err = variant.GenerateReleaseConfig(configs); err != nil {
+				return err
+			}
+			return config.InheritConfig(variant)
+		}
+
+		useVariant, ok := config.FlagArtifacts["RELEASE_BUILD_USE_VARIANT_FLAGS"]
+		if ok && MarshalValue(useVariant.Value) != "" {
+			if err = inheritBuildVariant(); err != nil {
+				return err
 			}
 		}
 	}
+
 	// Now remove any duplicates from the actual value of RELEASE_ACONFIG_VALUE_SETS
 	myAconfigValueSets := []string{}
 	myAconfigValueSetsMap := map[string]bool{}
@@ -313,6 +373,10 @@
 		if err != nil {
 			return err
 		}
+		// Redacted flags return nil when rendered.
+		if artifact == nil {
+			continue
+		}
 		for _, container := range v.FlagDeclaration.Containers {
 			if _, ok := config.PartitionBuildFlags[container]; !ok {
 				config.PartitionBuildFlags[container] = &rc_proto.FlagArtifacts{}
@@ -325,12 +389,7 @@
 		OtherNames: config.OtherNames,
 		Flags: func() []*rc_proto.FlagArtifact {
 			ret := []*rc_proto.FlagArtifact{}
-			flagNames := []string{}
-			for k := range config.FlagArtifacts {
-				flagNames = append(flagNames, k)
-			}
-			sort.Strings(flagNames)
-			for _, flagName := range flagNames {
+			for _, flagName := range config.FlagArtifacts.SortedFlagNames() {
 				flag := config.FlagArtifacts[flagName]
 				ret = append(ret, &rc_proto.FlagArtifact{
 					FlagDeclaration: flag.FlagDeclaration,
@@ -340,11 +399,12 @@
 			}
 			return ret
 		}(),
-		AconfigValueSets: myAconfigValueSets,
-		Inherits:         myInherits,
-		Directories:      directories,
-		ValueDirectories: valueDirectories,
-		PriorStages:      SortedMapKeys(config.PriorStagesMap),
+		AconfigValueSets:  myAconfigValueSets,
+		Inherits:          myInherits,
+		Directories:       directories,
+		ValueDirectories:  valueDirectories,
+		PriorStages:       SortedMapKeys(config.PriorStagesMap),
+		ReleaseConfigType: config.ReleaseConfigType.Enum(),
 	}
 
 	config.compileInProgress = false
@@ -365,7 +425,7 @@
 		}
 	}
 	for _, rcName := range extraAconfigReleaseConfigs {
-		rc, err := configs.GetReleaseConfig(rcName)
+		rc, err := configs.GetReleaseConfigStrict(rcName)
 		if 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 97eb8f1..4f621c7 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -248,6 +248,18 @@
 	return configs.configDirs[index], nil
 }
 
+// Return the (unsorted) release configs contributed to by `dir`.
+func EnumerateReleaseConfigs(dir string) ([]string, error) {
+	var ret []string
+	err := WalkTextprotoFiles(dir, "release_configs", func(path string, d fs.DirEntry, err error) error {
+		// Strip off the trailing `.textproto` from the name.
+		name := filepath.Base(path)
+		ret = append(ret, name[:len(name)-10])
+		return err
+	})
+	return ret, err
+}
+
 func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex int) error {
 	if _, err := os.Stat(path); err != nil {
 		return fmt.Errorf("%s does not exist\n", path)
@@ -349,10 +361,22 @@
 		if fmt.Sprintf("%s.textproto", name) != filepath.Base(path) {
 			return fmt.Errorf("%s incorrectly declares release config %s", path, name)
 		}
+		releaseConfigType := releaseConfigContribution.proto.ReleaseConfigType
+		if releaseConfigType == nil {
+			if name == "root" {
+				releaseConfigType = rc_proto.ReleaseConfigType_EXPLICIT_INHERITANCE_CONFIG.Enum()
+			} else {
+				releaseConfigType = rc_proto.ReleaseConfigType_RELEASE_CONFIG.Enum()
+			}
+		}
 		if _, ok := configs.ReleaseConfigs[name]; !ok {
 			configs.ReleaseConfigs[name] = ReleaseConfigFactory(name, ConfigDirIndex)
+			configs.ReleaseConfigs[name].ReleaseConfigType = *releaseConfigType
 		}
 		config := configs.ReleaseConfigs[name]
+		if config.ReleaseConfigType != *releaseConfigType {
+			return fmt.Errorf("%s mismatching ReleaseConfigType value %s", path, *releaseConfigType)
+		}
 		config.FilesUsedMap[path] = true
 		inheritNames := make(map[string]bool)
 		for _, inh := range config.InheritNames {
@@ -398,6 +422,14 @@
 }
 
 func (configs *ReleaseConfigs) GetReleaseConfig(name string) (*ReleaseConfig, error) {
+	return configs.getReleaseConfig(name, configs.allowMissing)
+}
+
+func (configs *ReleaseConfigs) GetReleaseConfigStrict(name string) (*ReleaseConfig, error) {
+	return configs.getReleaseConfig(name, false)
+}
+
+func (configs *ReleaseConfigs) getReleaseConfig(name string, allow_missing bool) (*ReleaseConfig, error) {
 	trace := []string{name}
 	for target, ok := configs.Aliases[name]; ok; target, ok = configs.Aliases[name] {
 		name = *target
@@ -406,7 +438,7 @@
 	if config, ok := configs.ReleaseConfigs[name]; ok {
 		return config, nil
 	}
-	if configs.allowMissing {
+	if allow_missing {
 		if config, ok := configs.ReleaseConfigs["trunk_staging"]; ok {
 			return config, nil
 		}
@@ -417,8 +449,10 @@
 func (configs *ReleaseConfigs) GetAllReleaseNames() []string {
 	var allReleaseNames []string
 	for _, v := range configs.ReleaseConfigs {
-		allReleaseNames = append(allReleaseNames, v.Name)
-		allReleaseNames = append(allReleaseNames, v.OtherNames...)
+		if v.isConfigListable() {
+			allReleaseNames = append(allReleaseNames, v.Name)
+			allReleaseNames = append(allReleaseNames, v.OtherNames...)
+		}
 	}
 	slices.Sort(allReleaseNames)
 	return allReleaseNames
@@ -455,7 +489,7 @@
 		dirName := filepath.Dir(contrib.path)
 		for k, names := range contrib.FlagValueDirs {
 			for _, rcName := range names {
-				if config, err := configs.GetReleaseConfig(rcName); err == nil {
+				if config, err := configs.getReleaseConfig(rcName, false); err == nil {
 					rcPath := filepath.Join(dirName, "release_configs", fmt.Sprintf("%s.textproto", config.Name))
 					if _, err := os.Stat(rcPath); err != nil {
 						errors = append(errors, fmt.Sprintf("%s exists but %s does not contribute to %s",
diff --git a/cmd/release_config/release_config_proto/Android.bp b/cmd/release_config/release_config_proto/Android.bp
index c34d203..c6869b1 100644
--- a/cmd/release_config/release_config_proto/Android.bp
+++ b/cmd/release_config/release_config_proto/Android.bp
@@ -28,5 +28,6 @@
         "build_flags_declarations.pb.go",
         "build_flags_src.pb.go",
         "build_flags_out.pb.go",
+        "release_configs_contributions.pb.go",
     ],
 }
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
index 7db945a..1df246c 100644
--- a/cmd/release_config/release_config_proto/build_flags_declarations.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_declarations.pb.go
@@ -48,6 +48,8 @@
 	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"`
+	// The bug number associated with the flag.
+	Bugs []string `protobuf:"bytes,4,rep,name=bugs" json:"bugs,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.
@@ -110,6 +112,13 @@
 	return ""
 }
 
+func (x *FlagDeclarationArtifact) GetBugs() []string {
+	if x != nil {
+		return x.Bugs
+	}
+	return nil
+}
+
 func (x *FlagDeclarationArtifact) GetDeclarationPath() string {
 	if x != nil && x.DeclarationPath != nil {
 		return *x.DeclarationPath
@@ -187,37 +196,38 @@
 	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, 0x8c, 0x02, 0x0a, 0x17, 0x46, 0x6c, 0x61,
+	0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x02, 0x0a, 0x17, 0x46, 0x6c, 0x61,
 	0x67, 0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 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, 0x57, 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, 0x96, 0x01, 0x0a, 0x18, 0x46, 0x6c, 0x61, 0x67,
-	0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x74, 0x69, 0x66,
-	0x61, 0x63, 0x74, 0x73, 0x12, 0x7a, 0x0a, 0x1e, 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, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 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, 0x46, 0x6c, 0x61, 0x67,
-	0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x74, 0x69, 0x66,
-	0x61, 0x63, 0x74, 0x52, 0x1b, 0x66, 0x6c, 0x61, 0x67, 0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61,
-	0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74,
-	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,
+	0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x75, 0x67, 0x73,
+	0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x62, 0x75, 0x67, 0x73, 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, 0x57, 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, 0x06, 0x08,
+	0xcf, 0x01, 0x10, 0xd0, 0x01, 0x22, 0x96, 0x01, 0x0a, 0x18, 0x46, 0x6c, 0x61, 0x67, 0x44, 0x65,
+	0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63,
+	0x74, 0x73, 0x12, 0x7a, 0x0a, 0x1e, 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, 0x5f,
+	0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 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, 0x46, 0x6c, 0x61, 0x67, 0x44, 0x65,
+	0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63,
+	0x74, 0x52, 0x1b, 0x66, 0x6c, 0x61, 0x67, 0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 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_declarations.proto b/cmd/release_config/release_config_proto/build_flags_declarations.proto
index d755e02..ccdccfb 100644
--- a/cmd/release_config/release_config_proto/build_flags_declarations.proto
+++ b/cmd/release_config/release_config_proto/build_flags_declarations.proto
@@ -51,8 +51,8 @@
   // Text description of the flag's purpose.
   optional string description = 3;
 
-  // reserve this for bug, if needed.
-  reserved 4;
+  // The bug number associated with the flag.
+  repeated string bugs = 4;
 
   // Where the flag was declared.
   optional string declaration_path = 5;
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 60ba2e1..ec07415 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
@@ -235,6 +235,8 @@
 	// config.  The listed directories contain at least a `release_config` message
 	// for this release config.
 	ValueDirectories []string `protobuf:"bytes,8,rep,name=value_directories,json=valueDirectories" json:"value_directories,omitempty"`
+	// The ReleaseConfigType of this release config.
+	ReleaseConfigType *ReleaseConfigType `protobuf:"varint,9,opt,name=release_config_type,json=releaseConfigType,enum=android.release_config_proto.ReleaseConfigType" json:"release_config_type,omitempty"`
 }
 
 func (x *ReleaseConfigArtifact) Reset() {
@@ -325,6 +327,13 @@
 	return nil
 }
 
+func (x *ReleaseConfigArtifact) GetReleaseConfigType() ReleaseConfigType {
+	if x != nil && x.ReleaseConfigType != nil {
+		return *x.ReleaseConfigType
+	}
+	return ReleaseConfigType_CONFIG_TYPE_UNSPECIFIED
+}
+
 type ReleaseConfigsArtifact struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -425,7 +434,7 @@
 	0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
 	0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x6c, 0x61, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66,
 	0x61, 0x63, 0x74, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x0e, 0x66, 0x6c, 0x61, 0x67,
-	0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22, 0xda, 0x02, 0x0a, 0x15, 0x52,
+	0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22, 0xbb, 0x03, 0x0a, 0x15, 0x52,
 	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x41, 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,
@@ -446,41 +455,47 @@
 	0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x76, 0x61,
 	0x6c, 0x75, 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18,
 	0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x69, 0x72, 0x65,
-	0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x52, 0x0e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72,
-	0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22, 0xde, 0x03, 0x0a, 0x16, 0x52, 0x65, 0x6c, 0x65,
-	0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61,
-	0x63, 0x74, 0x12, 0x5a, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x61, 0x6e, 0x64,
+	0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x5f, 0x0a, 0x13, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09,
+	0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 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, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x11, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61,
+	0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22, 0xde, 0x03, 0x0a, 0x16, 0x52, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66,
+	0x61, 0x63, 0x74, 0x12, 0x5a, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 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, 0x52, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
+	0x52, 0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
+	0x67, 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, 0x33,
+	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, 0x52, 0x65,
+	0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x41, 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, 0x85, 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, 0x4e, 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, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73,
-	0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52,
-	0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x67,
-	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, 0x33, 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, 0x52, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x41, 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, 0x85, 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, 0x4e, 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, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
-	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x41, 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,
-	0x77, 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, 0x44,
-	0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 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, 0x52, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 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,
+	0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x41, 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, 0x77, 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,
+	0x44, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e,
+	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, 0x52, 0x65,
+	0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 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 (
@@ -505,7 +520,8 @@
 	nil,                            // 5: android.release_config_proto.ReleaseConfigsArtifact.ReleaseConfigMapsMapEntry
 	(*Value)(nil),                  // 6: android.release_config_proto.Value
 	(*FlagDeclaration)(nil),        // 7: android.release_config_proto.FlagDeclaration
-	(*ReleaseConfigMap)(nil),       // 8: android.release_config_proto.ReleaseConfigMap
+	(ReleaseConfigType)(0),         // 8: android.release_config_proto.ReleaseConfigType
+	(*ReleaseConfigMap)(nil),       // 9: android.release_config_proto.ReleaseConfigMap
 }
 var file_build_flags_out_proto_depIdxs = []int32{
 	6,  // 0: android.release_config_proto.Tracepoint.value:type_name -> android.release_config_proto.Value
@@ -514,15 +530,16 @@
 	0,  // 3: android.release_config_proto.FlagArtifact.traces:type_name -> android.release_config_proto.Tracepoint
 	1,  // 4: android.release_config_proto.FlagArtifacts.flags:type_name -> android.release_config_proto.FlagArtifact
 	1,  // 5: android.release_config_proto.ReleaseConfigArtifact.flags:type_name -> android.release_config_proto.FlagArtifact
-	3,  // 6: android.release_config_proto.ReleaseConfigsArtifact.release_config:type_name -> android.release_config_proto.ReleaseConfigArtifact
-	3,  // 7: android.release_config_proto.ReleaseConfigsArtifact.other_release_configs:type_name -> android.release_config_proto.ReleaseConfigArtifact
-	5,  // 8: android.release_config_proto.ReleaseConfigsArtifact.release_config_maps_map:type_name -> android.release_config_proto.ReleaseConfigsArtifact.ReleaseConfigMapsMapEntry
-	8,  // 9: android.release_config_proto.ReleaseConfigsArtifact.ReleaseConfigMapsMapEntry.value:type_name -> android.release_config_proto.ReleaseConfigMap
-	10, // [10:10] is the sub-list for method output_type
-	10, // [10:10] is the sub-list for method input_type
-	10, // [10:10] is the sub-list for extension type_name
-	10, // [10:10] is the sub-list for extension extendee
-	0,  // [0:10] is the sub-list for field type_name
+	8,  // 6: android.release_config_proto.ReleaseConfigArtifact.release_config_type:type_name -> android.release_config_proto.ReleaseConfigType
+	3,  // 7: android.release_config_proto.ReleaseConfigsArtifact.release_config:type_name -> android.release_config_proto.ReleaseConfigArtifact
+	3,  // 8: android.release_config_proto.ReleaseConfigsArtifact.other_release_configs:type_name -> android.release_config_proto.ReleaseConfigArtifact
+	5,  // 9: android.release_config_proto.ReleaseConfigsArtifact.release_config_maps_map:type_name -> android.release_config_proto.ReleaseConfigsArtifact.ReleaseConfigMapsMapEntry
+	9,  // 10: android.release_config_proto.ReleaseConfigsArtifact.ReleaseConfigMapsMapEntry.value:type_name -> android.release_config_proto.ReleaseConfigMap
+	11, // [11:11] is the sub-list for method output_type
+	11, // [11:11] is the sub-list for method input_type
+	11, // [11:11] is the sub-list for extension type_name
+	11, // [11:11] is the sub-list for extension extendee
+	0,  // [0:11] is the sub-list for field type_name
 }
 
 func init() { file_build_flags_out_proto_init() }
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 2ab62d1..55c848e 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.proto
+++ b/cmd/release_config/release_config_proto/build_flags_out.proto
@@ -96,6 +96,9 @@
   // config.  The listed directories contain at least a `release_config` message
   // for this release config.
   repeated string value_directories = 8;
+
+  // The ReleaseConfigType of this release config.
+  optional ReleaseConfigType release_config_type = 9;
 }
 
 message ReleaseConfigsArtifact {
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 d784dee..c48c323 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
@@ -35,6 +35,76 @@
 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
+type ReleaseConfigType int32
+
+const (
+	// This is treated as `RELEASE_CONFIG`.
+	ReleaseConfigType_CONFIG_TYPE_UNSPECIFIED ReleaseConfigType = 0
+	// This is a normal release config.  This is the only ReleaseConfigType with
+	// implicit inheritance.
+	ReleaseConfigType_RELEASE_CONFIG ReleaseConfigType = 1
+	// Same as RELEASE_CONFIG, except no implicit inheritance happens.
+	// This is the "root" release config.
+	ReleaseConfigType_EXPLICIT_INHERITANCE_CONFIG ReleaseConfigType = 2
+	// This is a release config applied based on the TARGET_BUILD_VARIANT
+	// environment variable, if the build flag RELEASE_BUILD_USE_VARIANT_FLAGS is
+	// enabled.
+	ReleaseConfigType_BUILD_VARIANT ReleaseConfigType = 3
+)
+
+// Enum value maps for ReleaseConfigType.
+var (
+	ReleaseConfigType_name = map[int32]string{
+		0: "CONFIG_TYPE_UNSPECIFIED",
+		1: "RELEASE_CONFIG",
+		2: "EXPLICIT_INHERITANCE_CONFIG",
+		3: "BUILD_VARIANT",
+	}
+	ReleaseConfigType_value = map[string]int32{
+		"CONFIG_TYPE_UNSPECIFIED":     0,
+		"RELEASE_CONFIG":              1,
+		"EXPLICIT_INHERITANCE_CONFIG": 2,
+		"BUILD_VARIANT":               3,
+	}
+)
+
+func (x ReleaseConfigType) Enum() *ReleaseConfigType {
+	p := new(ReleaseConfigType)
+	*p = x
+	return p
+}
+
+func (x ReleaseConfigType) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (ReleaseConfigType) Descriptor() protoreflect.EnumDescriptor {
+	return file_build_flags_src_proto_enumTypes[0].Descriptor()
+}
+
+func (ReleaseConfigType) Type() protoreflect.EnumType {
+	return &file_build_flags_src_proto_enumTypes[0]
+}
+
+func (x ReleaseConfigType) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *ReleaseConfigType) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+	if err != nil {
+		return err
+	}
+	*x = ReleaseConfigType(num)
+	return nil
+}
+
+// Deprecated: Use ReleaseConfigType.Descriptor instead.
+func (ReleaseConfigType) EnumDescriptor() ([]byte, []int) {
+	return file_build_flags_src_proto_rawDescGZIP(), []int{0}
+}
+
 type Value struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -159,6 +229,8 @@
 	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"`
+	// The bug number associated with the flag.
+	Bugs []string `protobuf:"bytes,4,rep,name=bugs" json:"bugs,omitempty"`
 	// Value for the flag
 	Value *Value `protobuf:"bytes,201,opt,name=value" json:"value,omitempty"`
 	// Workflow for this flag.
@@ -221,6 +293,13 @@
 	return ""
 }
 
+func (x *FlagDeclaration) GetBugs() []string {
+	if x != nil {
+		return x.Bugs
+	}
+	return nil
+}
+
 func (x *FlagDeclaration) GetValue() *Value {
 	if x != nil {
 		return x.Value
@@ -329,6 +408,8 @@
 	// 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"`
+	// The ReleaseConfigType of this release config.
+	ReleaseConfigType *ReleaseConfigType `protobuf:"varint,6,opt,name=release_config_type,json=releaseConfigType,enum=android.release_config_proto.ReleaseConfigType" json:"release_config_type,omitempty"`
 }
 
 func (x *ReleaseConfig) Reset() {
@@ -398,6 +479,13 @@
 	return nil
 }
 
+func (x *ReleaseConfig) GetReleaseConfigType() ReleaseConfigType {
+	if x != nil && x.ReleaseConfigType != nil {
+		return *x.ReleaseConfigType
+	}
+	return ReleaseConfigType_CONFIG_TYPE_UNSPECIFIED
+}
+
 // Any aliases.  These are used for continuous integration builder config.
 type ReleaseAlias struct {
 	state         protoimpl.MessageState
@@ -541,62 +629,76 @@
 	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, 0x95, 0x02, 0x0a, 0x0f, 0x46, 0x6c, 0x61, 0x67,
+	0x42, 0x05, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x22, 0xa3, 0x02, 0x0a, 0x0f, 0x46, 0x6c, 0x61, 0x67,
 	0x44, 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, 0x56,
-	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,
+	0x12, 0x0a, 0x04, 0x62, 0x75, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x62,
+	0x75, 0x67, 0x73, 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, 0x56, 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, 0x57, 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, 0x06, 0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01, 0x22, 0x78, 0x0a,
+	0x09, 0x46, 0x6c, 0x61, 0x67, 0x56, 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, 0x57, 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,
-	0x78, 0x0a, 0x09, 0x46, 0x6c, 0x61, 0x67, 0x56, 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,
-	0x56, 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, 0xbe, 0x01, 0x0a, 0x0d, 0x52, 0x65,
-	0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 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, 0x3a, 0x0a, 0x0c, 0x52, 0x65,
-	0x6c, 0x65, 0x61, 0x73, 0x65, 0x41, 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, 0xa9, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6c, 0x65, 0x61,
-	0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x12, 0x44, 0x0a, 0x07, 0x61,
-	0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 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, 0x52, 0x65, 0x6c, 0x65,
-	0x61, 0x73, 0x65, 0x41, 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,
+	0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 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, 0x9f, 0x02, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x65,
+	0x61, 0x73, 0x65, 0x43, 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, 0x12, 0x5f, 0x0a, 0x13, 0x72, 0x65, 0x6c, 0x65,
+	0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18,
+	0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 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, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x11, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x0a, 0x0c, 0x52, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x41, 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, 0xa9, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x12, 0x44, 0x0a, 0x07, 0x61, 0x6c,
+	0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 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, 0x52, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x41, 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, 0x78, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47,
+	0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
+	0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x5f, 0x43,
+	0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x58, 0x50, 0x4c, 0x49,
+	0x43, 0x49, 0x54, 0x5f, 0x49, 0x4e, 0x48, 0x45, 0x52, 0x49, 0x54, 0x41, 0x4e, 0x43, 0x45, 0x5f,
+	0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x42, 0x55, 0x49, 0x4c,
+	0x44, 0x5f, 0x56, 0x41, 0x52, 0x49, 0x41, 0x4e, 0x54, 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 (
@@ -611,26 +713,29 @@
 	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{}{
-	(*Value)(nil),            // 0: android.release_config_proto.Value
-	(*FlagDeclaration)(nil),  // 1: android.release_config_proto.FlagDeclaration
-	(*FlagValue)(nil),        // 2: android.release_config_proto.FlagValue
-	(*ReleaseConfig)(nil),    // 3: android.release_config_proto.ReleaseConfig
-	(*ReleaseAlias)(nil),     // 4: android.release_config_proto.ReleaseAlias
-	(*ReleaseConfigMap)(nil), // 5: android.release_config_proto.ReleaseConfigMap
-	(Workflow)(0),            // 6: android.release_config_proto.Workflow
+	(ReleaseConfigType)(0),   // 0: android.release_config_proto.ReleaseConfigType
+	(*Value)(nil),            // 1: android.release_config_proto.Value
+	(*FlagDeclaration)(nil),  // 2: android.release_config_proto.FlagDeclaration
+	(*FlagValue)(nil),        // 3: android.release_config_proto.FlagValue
+	(*ReleaseConfig)(nil),    // 4: android.release_config_proto.ReleaseConfig
+	(*ReleaseAlias)(nil),     // 5: android.release_config_proto.ReleaseAlias
+	(*ReleaseConfigMap)(nil), // 6: android.release_config_proto.ReleaseConfigMap
+	(Workflow)(0),            // 7: android.release_config_proto.Workflow
 }
 var file_build_flags_src_proto_depIdxs = []int32{
-	0, // 0: android.release_config_proto.FlagDeclaration.value:type_name -> android.release_config_proto.Value
-	6, // 1: android.release_config_proto.FlagDeclaration.workflow:type_name -> android.release_config_proto.Workflow
-	0, // 2: android.release_config_proto.FlagValue.value:type_name -> android.release_config_proto.Value
-	4, // 3: android.release_config_proto.ReleaseConfigMap.aliases:type_name -> android.release_config_proto.ReleaseAlias
-	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
-	4, // [4:4] is the sub-list for extension extendee
-	0, // [0:4] is the sub-list for field type_name
+	1, // 0: android.release_config_proto.FlagDeclaration.value:type_name -> android.release_config_proto.Value
+	7, // 1: android.release_config_proto.FlagDeclaration.workflow:type_name -> android.release_config_proto.Workflow
+	1, // 2: android.release_config_proto.FlagValue.value:type_name -> android.release_config_proto.Value
+	0, // 3: android.release_config_proto.ReleaseConfig.release_config_type:type_name -> android.release_config_proto.ReleaseConfigType
+	5, // 4: android.release_config_proto.ReleaseConfigMap.aliases:type_name -> android.release_config_proto.ReleaseAlias
+	5, // [5:5] is the sub-list for method output_type
+	5, // [5:5] is the sub-list for method input_type
+	5, // [5:5] is the sub-list for extension type_name
+	5, // [5:5] is the sub-list for extension extendee
+	0, // [0:5] is the sub-list for field type_name
 }
 
 func init() { file_build_flags_src_proto_init() }
@@ -724,13 +829,14 @@
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_build_flags_src_proto_rawDesc,
-			NumEnums:      0,
+			NumEnums:      1,
 			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 e1925bc..e42ac31 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.proto
+++ b/cmd/release_config/release_config_proto/build_flags_src.proto
@@ -67,8 +67,8 @@
   // Text description of the flag's purpose.
   optional string description = 3;
 
-  // reserve this for bug, if needed.
-  reserved 4;
+  // The bug number associated with the flag.
+  repeated string bugs = 4;
 
   // Value for the flag
   optional Value value = 201;
@@ -98,6 +98,24 @@
   optional bool redacted = 202;
 }
 
+enum ReleaseConfigType {
+  // This is treated as `RELEASE_CONFIG`.
+  CONFIG_TYPE_UNSPECIFIED = 0;
+
+  // This is a normal release config.  This is the only ReleaseConfigType with
+  // implicit inheritance.
+  RELEASE_CONFIG = 1;
+
+  // Same as RELEASE_CONFIG, except no implicit inheritance happens.
+  // This is the "root" release config.
+  EXPLICIT_INHERITANCE_CONFIG = 2;
+
+  // This is a release config applied based on the TARGET_BUILD_VARIANT
+  // environment variable, if the build flag RELEASE_BUILD_USE_VARIANT_FLAGS is
+  // enabled.
+  BUILD_VARIANT = 3;
+}
+
 // This replaces $(call declare-release-config).
 message ReleaseConfig {
   // The name of the release config.
@@ -117,6 +135,9 @@
   // 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;
+
+  // The ReleaseConfigType of this release config.
+  optional ReleaseConfigType release_config_type = 6;
 }
 
 // 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
old mode 100644
new mode 100755
index 23e3115..a92bfc0
--- 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 build_flags_common.proto build_flags_declarations.proto
+aprotoc --go_out=paths=source_relative:. build_flags_src.proto build_flags_out.proto build_flags_common.proto build_flags_declarations.proto release_configs_contributions.proto
diff --git a/cmd/release_config/release_config_proto/release_configs_contributions.pb.go b/cmd/release_config/release_config_proto/release_configs_contributions.pb.go
new file mode 100644
index 0000000..54854f1
--- /dev/null
+++ b/cmd/release_config/release_config_proto/release_configs_contributions.pb.go
@@ -0,0 +1,252 @@
+//
+// Copyright (C) 2025 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.33.0
+// 	protoc        v3.21.12
+// source: release_configs_contributions.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 ReleaseConfigContributionsArtifact struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the release config.
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// The release config contribution directories that may contribute to this
+	// release config.
+	ContributingDirectories []string `protobuf:"bytes,2,rep,name=contributing_directories,json=contributingDirectories" json:"contributing_directories,omitempty"`
+}
+
+func (x *ReleaseConfigContributionsArtifact) Reset() {
+	*x = ReleaseConfigContributionsArtifact{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_release_configs_contributions_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ReleaseConfigContributionsArtifact) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReleaseConfigContributionsArtifact) ProtoMessage() {}
+
+func (x *ReleaseConfigContributionsArtifact) ProtoReflect() protoreflect.Message {
+	mi := &file_release_configs_contributions_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 ReleaseConfigContributionsArtifact.ProtoReflect.Descriptor instead.
+func (*ReleaseConfigContributionsArtifact) Descriptor() ([]byte, []int) {
+	return file_release_configs_contributions_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *ReleaseConfigContributionsArtifact) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *ReleaseConfigContributionsArtifact) GetContributingDirectories() []string {
+	if x != nil {
+		return x.ContributingDirectories
+	}
+	return nil
+}
+
+type ReleaseConfigContributionsArtifacts struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The artifacts
+	ReleaseConfigContributionsArtifactList []*ReleaseConfigContributionsArtifact `protobuf:"bytes,1,rep,name=release_config_contributions_artifact_list,json=releaseConfigContributionsArtifactList" json:"release_config_contributions_artifact_list,omitempty"`
+}
+
+func (x *ReleaseConfigContributionsArtifacts) Reset() {
+	*x = ReleaseConfigContributionsArtifacts{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_release_configs_contributions_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ReleaseConfigContributionsArtifacts) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReleaseConfigContributionsArtifacts) ProtoMessage() {}
+
+func (x *ReleaseConfigContributionsArtifacts) ProtoReflect() protoreflect.Message {
+	mi := &file_release_configs_contributions_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 ReleaseConfigContributionsArtifacts.ProtoReflect.Descriptor instead.
+func (*ReleaseConfigContributionsArtifacts) Descriptor() ([]byte, []int) {
+	return file_release_configs_contributions_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *ReleaseConfigContributionsArtifacts) GetReleaseConfigContributionsArtifactList() []*ReleaseConfigContributionsArtifact {
+	if x != nil {
+		return x.ReleaseConfigContributionsArtifactList
+	}
+	return nil
+}
+
+var File_release_configs_contributions_proto protoreflect.FileDescriptor
+
+var file_release_configs_contributions_proto_rawDesc = []byte{
+	0x0a, 0x23, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 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, 0x22, 0x73, 0x0a, 0x22, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x41, 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, 0x39, 0x0a,
+	0x18, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69,
+	0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x17, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x72,
+	0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x22, 0xc4, 0x01, 0x0a, 0x23, 0x52, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69,
+	0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73,
+	0x12, 0x9c, 0x01, 0x0a, 0x2a, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 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, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x41,
+	0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x26, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 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_release_configs_contributions_proto_rawDescOnce sync.Once
+	file_release_configs_contributions_proto_rawDescData = file_release_configs_contributions_proto_rawDesc
+)
+
+func file_release_configs_contributions_proto_rawDescGZIP() []byte {
+	file_release_configs_contributions_proto_rawDescOnce.Do(func() {
+		file_release_configs_contributions_proto_rawDescData = protoimpl.X.CompressGZIP(file_release_configs_contributions_proto_rawDescData)
+	})
+	return file_release_configs_contributions_proto_rawDescData
+}
+
+var file_release_configs_contributions_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_release_configs_contributions_proto_goTypes = []interface{}{
+	(*ReleaseConfigContributionsArtifact)(nil),  // 0: android.release_config_proto.ReleaseConfigContributionsArtifact
+	(*ReleaseConfigContributionsArtifacts)(nil), // 1: android.release_config_proto.ReleaseConfigContributionsArtifacts
+}
+var file_release_configs_contributions_proto_depIdxs = []int32{
+	0, // 0: android.release_config_proto.ReleaseConfigContributionsArtifacts.release_config_contributions_artifact_list:type_name -> android.release_config_proto.ReleaseConfigContributionsArtifact
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_release_configs_contributions_proto_init() }
+func file_release_configs_contributions_proto_init() {
+	if File_release_configs_contributions_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_release_configs_contributions_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ReleaseConfigContributionsArtifact); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_release_configs_contributions_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ReleaseConfigContributionsArtifacts); 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_release_configs_contributions_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_release_configs_contributions_proto_goTypes,
+		DependencyIndexes: file_release_configs_contributions_proto_depIdxs,
+		MessageInfos:      file_release_configs_contributions_proto_msgTypes,
+	}.Build()
+	File_release_configs_contributions_proto = out.File
+	file_release_configs_contributions_proto_rawDesc = nil
+	file_release_configs_contributions_proto_goTypes = nil
+	file_release_configs_contributions_proto_depIdxs = nil
+}
diff --git a/cmd/release_config/release_config_proto/release_configs_contributions.proto b/cmd/release_config/release_config_proto/release_configs_contributions.proto
new file mode 100644
index 0000000..bc7aeda
--- /dev/null
+++ b/cmd/release_config/release_config_proto/release_configs_contributions.proto
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2025 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";
+
+message ReleaseConfigContributionsArtifact {
+  // The name of the release config.
+  optional string name = 1;
+
+  // The release config contribution directories that may contribute to this
+  // release config.
+  repeated string contributing_directories = 2;
+}
+
+message ReleaseConfigContributionsArtifacts {
+  // The artifacts
+  repeated ReleaseConfigContributionsArtifact release_config_contributions_artifact_list = 1;
+}
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 72af3e0..d8f563b 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -26,13 +26,11 @@
         "soong",
         "soong-android",
         "soong-provenance",
-        "soong-bp2build",
         "soong-ui-metrics_proto",
     ],
     srcs: [
         "main.go",
         "writedocs.go",
-        "queryview.go",
     ],
     primaryBuilder: true,
 }
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 577c6cc..6642023 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -26,7 +26,6 @@
 
 	"android/soong/android"
 	"android/soong/android/allowlists"
-	"android/soong/bp2build"
 	"android/soong/shared"
 
 	"github.com/google/blueprint"
@@ -78,7 +77,6 @@
 	flag.StringVar(&cmdlineArgs.ModuleGraphFile, "module_graph_file", "", "JSON module graph file to output")
 	flag.StringVar(&cmdlineArgs.ModuleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules")
 	flag.StringVar(&cmdlineArgs.DocFile, "soong_docs", "", "build documentation file to output")
-	flag.StringVar(&cmdlineArgs.BazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
 	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
 	flag.StringVar(&cmdlineArgs.SoongVariables, "soong_variables", "soong.variables", "the file contains all build variables")
 	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
@@ -121,16 +119,6 @@
 	return false
 }
 
-// Run the code-generation phase to convert BazelTargetModules to BUILD files.
-func runQueryView(queryviewDir, queryviewMarker string, ctx *android.Context) {
-	ctx.EventHandler.Begin("queryview")
-	defer ctx.EventHandler.End("queryview")
-	codegenContext := bp2build.NewCodegenContext(ctx.Config(), ctx, bp2build.QueryView, topDir)
-	err := createBazelWorkspace(codegenContext, shared.JoinPath(topDir, queryviewDir), false)
-	maybeQuit(err, "")
-	touch(shared.JoinPath(topDir, queryviewMarker))
-}
-
 func writeNinjaHint(ctx *android.Context) error {
 	ctx.BeginEvent("ninja_hint")
 	defer ctx.EndEvent("ninja_hint")
@@ -283,7 +271,7 @@
 	switch ctx.Config().BuildMode {
 	case android.GenerateModuleGraph:
 		stopBefore = bootstrap.StopBeforeWriteNinja
-	case android.GenerateQueryView, android.GenerateDocFile:
+	case android.GenerateDocFile:
 		stopBefore = bootstrap.StopBeforePrepareBuildActions
 	default:
 		stopBefore = bootstrap.DoEverything
@@ -294,10 +282,6 @@
 
 	// Convert the Soong module graph into Bazel BUILD files.
 	switch ctx.Config().BuildMode {
-	case android.GenerateQueryView:
-		queryviewMarkerFile := cmdlineArgs.BazelQueryViewDir + ".marker"
-		runQueryView(cmdlineArgs.BazelQueryViewDir, queryviewMarkerFile, ctx)
-		return queryviewMarkerFile, ninjaDeps
 	case android.GenerateModuleGraph:
 		writeJsonModuleGraphAndActions(ctx, cmdlineArgs)
 		return cmdlineArgs.ModuleGraphFile, ninjaDeps
@@ -375,6 +359,7 @@
 	ctx.Register()
 	finalOutputFile, ninjaDeps := runSoongOnlyBuild(ctx)
 
+	ninjaDeps = append(ninjaDeps, configuration.ProductVariablesFileName)
 	ninjaDeps = append(ninjaDeps, usedEnvFile)
 	if shared.IsDebugging() {
 		// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
deleted file mode 100644
index eafd67a..0000000
--- a/cmd/soong_build/queryview.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2020 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 main
-
-import (
-	"io/fs"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-
-	"android/soong/android"
-	"android/soong/bp2build"
-)
-
-// A helper function to generate a Read-only Bazel workspace in outDir
-func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string, generateFilegroups bool) error {
-	os.RemoveAll(outDir)
-	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
-
-	res, err := bp2build.GenerateBazelTargets(ctx, generateFilegroups)
-	if err != nil {
-		panic(err)
-	}
-
-	filesToWrite := bp2build.CreateBazelFiles(ruleShims, res.BuildDirToTargets(), ctx.Mode())
-	bazelRcFiles, err2 := CopyBazelRcFiles()
-	if err2 != nil {
-		return err2
-	}
-	filesToWrite = append(filesToWrite, bazelRcFiles...)
-	for _, f := range filesToWrite {
-		if err := writeReadOnlyFile(outDir, f); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// CopyBazelRcFiles creates BazelFiles for all the bazelrc files under
-// build/bazel. They're needed because the rc files are still read when running
-// queryview, so they have to be in the queryview workspace.
-func CopyBazelRcFiles() ([]bp2build.BazelFile, error) {
-	result := make([]bp2build.BazelFile, 0)
-	err := filepath.WalkDir(filepath.Join(topDir, "build/bazel"), func(path string, info fs.DirEntry, err error) error {
-		if filepath.Ext(path) == ".bazelrc" {
-			contents, err := os.ReadFile(path)
-			if err != nil {
-				return err
-			}
-			path, err = filepath.Rel(topDir, path)
-			if err != nil {
-				return err
-			}
-			result = append(result, bp2build.BazelFile{
-				Dir:      filepath.Dir(path),
-				Basename: filepath.Base(path),
-				Contents: string(contents),
-			})
-		}
-		return nil
-	})
-	return result, err
-}
-
-// The auto-conversion directory should be read-only, sufficient for bazel query. The files
-// are not intended to be edited by end users.
-func writeReadOnlyFile(dir string, f bp2build.BazelFile) error {
-	dir = filepath.Join(dir, f.Dir)
-	if err := createDirectoryIfNonexistent(dir); err != nil {
-		return err
-	}
-	pathToFile := filepath.Join(dir, f.Basename)
-
-	// 0444 is read-only
-	err := ioutil.WriteFile(pathToFile, []byte(f.Contents), 0444)
-
-	return err
-}
-
-func writeReadWriteFile(dir string, f bp2build.BazelFile) error {
-	dir = filepath.Join(dir, f.Dir)
-	if err := createDirectoryIfNonexistent(dir); err != nil {
-		return err
-	}
-	pathToFile := filepath.Join(dir, f.Basename)
-
-	// 0644 is read-write
-	err := ioutil.WriteFile(pathToFile, []byte(f.Contents), 0644)
-
-	return err
-}
-
-func createDirectoryIfNonexistent(dir string) error {
-	if _, err := os.Stat(dir); os.IsNotExist(err) {
-		return os.MkdirAll(dir, os.ModePerm)
-	} else {
-		return err
-	}
-}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 2d3156a..c7134d7 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -251,11 +251,10 @@
 	buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
 	rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
 	soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
-	bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
 	soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
 
 	//Delete the stale metrics files
-	staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile}
+	staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, soongBuildMetricsFile}
 	if err := deleteStaleMetrics(staleFileSlice); err != nil {
 		log.Fatalln(err)
 	}
diff --git a/cmd/symbols_map/symbols_map_proto/symbols_map.proto b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
index 693fe3e..a76d171 100644
--- a/cmd/symbols_map/symbols_map_proto/symbols_map.proto
+++ b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
@@ -37,6 +37,21 @@
 
   // type is the type of the mapping, either ELF or R8.
   optional Type type = 3;
+
+  // LocationType is the place where to look for the file with the given
+  // identifier.
+  Enum LocationType {
+    // ZIP denotes the file with the given identifier is in the distribuited
+    // symbols.zip or proguard_dict.zip files, or the local disc.
+    ZIP = 0;
+    // AB denotes the file with the given identifier is in the AB artifacts but
+    // not in a symbols.zip or proguard_dict.zip.
+    AB = 1;
+  }
+
+  // location_type is the Location Type that dictates where to search for the
+  // file with the given identifier. Defaults to ZIP if not present.
+  optional LocationType location_type = 4;
 }
 
 message Mappings {
diff --git a/compliance/Android.bp b/compliance/Android.bp
index 08736b4..6662970 100644
--- a/compliance/Android.bp
+++ b/compliance/Android.bp
@@ -34,6 +34,7 @@
     name: "notice_xml_system",
     partition_name: "system",
     visibility: [
-        "//device/google/cuttlefish/system_image",
+        "//build/make/target/product/generic",
+        "//build/make/target/product/gsi",
     ],
 }
diff --git a/compliance/notice.go b/compliance/notice.go
index 4fc83ab..edd1b34 100644
--- a/compliance/notice.go
+++ b/compliance/notice.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
@@ -62,8 +63,7 @@
 
 	props noticeXmlProperties
 
-	outputFile  android.OutputPath
-	installPath android.InstallPath
+	outputFile android.OutputPath
 }
 
 type noticeXmlProperties struct {
@@ -86,10 +86,8 @@
 
 	nx.outputFile = output.OutputPath
 
-	if android.Bool(ctx.Config().ProductVariables().UseSoongSystemImage) {
-		nx.installPath = android.PathForModuleInPartitionInstall(ctx, nx.props.Partition_name, "etc")
-		ctx.InstallFile(nx.installPath, "NOTICE.xml.gz", nx.outputFile)
-	}
+	installPath := android.PathForModuleInPartitionInstall(ctx, nx.props.Partition_name, "etc")
+	ctx.PackageFile(installPath, "NOTICE.xml.gz", nx.outputFile)
 }
 
 func (nx *NoticeXmlModule) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/compliance/notice_test.go b/compliance/notice_test.go
index 6187e53..e8578ec 100644
--- a/compliance/notice_test.go
+++ b/compliance/notice_test.go
@@ -35,4 +35,4 @@
 
 	m := result.Module("notice_xml_system", "android_arm64_armv8-a").(*NoticeXmlModule)
 	android.AssertStringEquals(t, "output file", "NOTICE.xml.gz", m.outputFile.Base())
-}
\ No newline at end of file
+}
diff --git a/cuj/Android.bp b/cuj/Android.bp
deleted file mode 100644
index f9cfdc1..0000000
--- a/cuj/Android.bp
+++ /dev/null
@@ -1,17 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-blueprint_go_binary {
-    name: "cuj_tests",
-    deps: [
-        "soong-ui-build",
-        "soong-ui-logger",
-        "soong-ui-signal",
-        "soong-ui-terminal",
-        "soong-ui-tracer",
-    ],
-    srcs: [
-        "cuj.go",
-    ],
-}
diff --git a/cuj/cuj.go b/cuj/cuj.go
deleted file mode 100644
index de6f10d..0000000
--- a/cuj/cuj.go
+++ /dev/null
@@ -1,239 +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.
-
-// This executable runs a series of build commands to test and benchmark some critical user journeys.
-package main
-
-import (
-	"context"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strconv"
-	"strings"
-	"time"
-
-	"android/soong/ui/build"
-	"android/soong/ui/logger"
-	"android/soong/ui/metrics"
-	"android/soong/ui/signal"
-	"android/soong/ui/status"
-	"android/soong/ui/terminal"
-	"android/soong/ui/tracer"
-)
-
-type Test struct {
-	name   string
-	args   []string
-	before func() error
-
-	results TestResults
-}
-
-type TestResults struct {
-	metrics *metrics.Metrics
-	err     error
-}
-
-// Run runs a single build command.  It emulates the "m" command line by calling into Soong UI directly.
-func (t *Test) Run(logsDir string) {
-	output := terminal.NewStatusOutput(os.Stdout, "", false, false, false)
-
-	log := logger.New(output)
-	defer log.Cleanup()
-
-	ctx, cancel := context.WithCancel(context.Background())
-	defer cancel()
-
-	trace := tracer.New(log)
-	defer trace.Close()
-
-	met := metrics.New()
-
-	stat := &status.Status{}
-	defer stat.Finish()
-	stat.AddOutput(output)
-	stat.AddOutput(trace.StatusTracer())
-
-	signal.SetupSignals(log, cancel, func() {
-		trace.Close()
-		log.Cleanup()
-		stat.Finish()
-	})
-
-	buildCtx := build.Context{ContextImpl: &build.ContextImpl{
-		Context: ctx,
-		Logger:  log,
-		Metrics: met,
-		Tracer:  trace,
-		Writer:  output,
-		Status:  stat,
-	}}
-
-	defer logger.Recover(func(err error) {
-		t.results.err = err
-	})
-
-	config := build.NewConfig(buildCtx, t.args...)
-	build.SetupOutDir(buildCtx, config)
-
-	os.MkdirAll(logsDir, 0777)
-	log.SetOutput(filepath.Join(logsDir, "soong.log"))
-	trace.SetOutput(filepath.Join(logsDir, "build.trace"))
-	stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log")))
-	stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log")))
-	stat.AddOutput(status.NewProtoErrorLog(log, filepath.Join(logsDir, "build_error")))
-	stat.AddOutput(status.NewCriticalPathLogger(log, nil))
-
-	defer met.Dump(filepath.Join(logsDir, "soong_metrics"))
-
-	if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
-		if !strings.HasSuffix(start, "N") {
-			if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
-				log.Verbosef("Took %dms to start up.",
-					time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
-				buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
-			}
-		}
-
-		if executable, err := os.Executable(); err == nil {
-			trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
-		}
-	}
-
-	f := build.NewSourceFinder(buildCtx, config)
-	defer f.Shutdown()
-	build.FindSources(buildCtx, config, f)
-
-	build.Build(buildCtx, config)
-
-	t.results.metrics = met
-}
-
-// Touch the Intent.java file to cause a rebuild of the frameworks to monitor the
-// incremental build speed as mentioned b/152046247. Intent.java file was chosen
-// as it is a key component of the framework and is often modified.
-func touchIntentFile() error {
-	const intentFileName = "frameworks/base/core/java/android/content/Intent.java"
-	currentTime := time.Now().Local()
-	return os.Chtimes(intentFileName, currentTime, currentTime)
-}
-
-func main() {
-	outDir := os.Getenv("OUT_DIR")
-	if outDir == "" {
-		outDir = "out"
-	}
-
-	cujDir := filepath.Join(outDir, "cuj_tests")
-
-	wd, _ := os.Getwd()
-	os.Setenv("TOP", wd)
-	// Use a subdirectory for the out directory for the tests to keep them isolated.
-	os.Setenv("OUT_DIR", filepath.Join(cujDir, "out"))
-
-	// Each of these tests is run in sequence without resetting the output tree.  The state of the output tree will
-	// affect each successive test.  To maintain the validity of the benchmarks across changes, care must be taken
-	// to avoid changing the state of the tree when a test is run.  This is most easily accomplished by adding tests
-	// at the end.
-	tests := []Test{
-		{
-			// Reset the out directory to get reproducible results.
-			name: "clean",
-			args: []string{"clean"},
-		},
-		{
-			// Parse the build files.
-			name: "nothing",
-			args: []string{"nothing"},
-		},
-		{
-			// Parse the build files again to monitor issues like globs rerunning.
-			name: "nothing_rebuild",
-			args: []string{"nothing"},
-		},
-		{
-			// Parse the build files again, this should always be very short.
-			name: "nothing_rebuild_twice",
-			args: []string{"nothing"},
-		},
-		{
-			// Build the framework as a common developer task and one that keeps getting longer.
-			name: "framework",
-			args: []string{"framework"},
-		},
-		{
-			// Build the framework again to make sure it doesn't rebuild anything.
-			name: "framework_rebuild",
-			args: []string{"framework"},
-		},
-		{
-			// Build the framework again to make sure it doesn't rebuild anything even if it did the second time.
-			name: "framework_rebuild_twice",
-			args: []string{"framework"},
-		},
-		{
-			// Scenario major_inc_build (b/152046247): tracking build speed of major incremental build.
-			name: "major_inc_build_droid",
-			args: []string{"droid"},
-		},
-		{
-			name:   "major_inc_build_framework_minus_apex_after_droid_build",
-			args:   []string{"framework-minus-apex"},
-			before: touchIntentFile,
-		},
-		{
-			name:   "major_inc_build_framework_after_droid_build",
-			args:   []string{"framework"},
-			before: touchIntentFile,
-		},
-		{
-			name:   "major_inc_build_sync_after_droid_build",
-			args:   []string{"sync"},
-			before: touchIntentFile,
-		},
-		{
-			name:   "major_inc_build_droid_rebuild",
-			args:   []string{"droid"},
-			before: touchIntentFile,
-		},
-		{
-			name:   "major_inc_build_update_api_after_droid_rebuild",
-			args:   []string{"update-api"},
-			before: touchIntentFile,
-		},
-	}
-
-	cujMetrics := metrics.NewCriticalUserJourneysMetrics()
-	defer cujMetrics.Dump(filepath.Join(cujDir, "logs", "cuj_metrics.pb"))
-
-	for i, t := range tests {
-		logsSubDir := fmt.Sprintf("%02d_%s", i, t.name)
-		logsDir := filepath.Join(cujDir, "logs", logsSubDir)
-		if t.before != nil {
-			if err := t.before(); err != nil {
-				fmt.Printf("error running before function on test %q: %v\n", t.name, err)
-				break
-			}
-		}
-		t.Run(logsDir)
-		if t.results.err != nil {
-			fmt.Printf("error running test %q: %s\n", t.name, t.results.err)
-			break
-		}
-		if t.results.metrics != nil {
-			cujMetrics.Add(t.name, t.results.metrics)
-		}
-	}
-}
diff --git a/cuj/run_cuj_tests.sh b/cuj/run_cuj_tests.sh
deleted file mode 100755
index a746bd5..0000000
--- a/cuj/run_cuj_tests.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash -e
-
-readonly UNAME="$(uname)"
-case "$UNAME" in
-Linux)
-    readonly OS='linux'
-    ;;
-Darwin)
-    readonly OS='darwin'
-    ;;
-*)
-    echo "Unsupported OS '$UNAME'"
-    exit 1
-    ;;
-esac
-
-readonly ANDROID_TOP="$(cd $(dirname $0)/../../..; pwd)"
-cd "$ANDROID_TOP"
-
-export OUT_DIR="${OUT_DIR:-out}"
-
-build/soong/soong_ui.bash --make-mode "${OUT_DIR}/host/${OS}-x86/bin/cuj_tests"
-
-"${OUT_DIR}/host/${OS}-x86/bin/cuj_tests" || true
-
-if [ -n "${DIST_DIR}" ]; then
-  cp -r "${OUT_DIR}/cuj_tests/logs" "${DIST_DIR}"
-fi
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index fe6317c..e3804e5 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -191,6 +191,10 @@
 	ForceCreateAppImage bool
 
 	PresignedPrebuilt bool
+
+	// ApexPartition is the partition in which the dexpreopt files of apex system server jars (if any) are installed.
+	// This is a noop unless the module is apex system server jar.
+	ApexPartition string
 }
 
 type globalSoongConfigSingleton struct{}
@@ -458,6 +462,12 @@
 	return target.IsReplacedByPrebuilt()
 }
 
+func (d dex2oatDependencyTag) AllowDisabledModuleDependencyProxy(
+	ctx android.OtherModuleProviderContext, target android.ModuleProxy) bool {
+	return android.OtherModuleProviderOrDefault(
+		ctx, target, android.CommonModuleInfoKey).ReplacedByPrebuilt
+}
+
 // Dex2oatDepTag represents the dependency onto the dex2oatd module. It is added to any module that
 // needs dexpreopting and so it makes no sense for it to be checked for visibility or included in
 // the apex.
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 5616483..7a39fa1 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -219,9 +219,9 @@
 }
 
 // Returns the location to the odex file for the dex file at `path`.
-func ToOdexPath(path string, arch android.ArchType) string {
+func ToOdexPath(path string, arch android.ArchType, partition string) string {
 	if strings.HasPrefix(path, "/apex/") {
-		return filepath.Join("/system/framework/oat", arch.String(),
+		return filepath.Join(partition, "framework/oat", arch.String(),
 			strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
 	}
 
@@ -245,7 +245,7 @@
 
 	odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
 	odexSymbolsPath := odexPath.ReplaceExtension(ctx, "symbols.odex")
-	odexInstallPath := ToOdexPath(module.DexLocation, arch)
+	odexInstallPath := ToOdexPath(module.DexLocation, arch, module.ApexPartition)
 	if odexOnSystemOther(module, global) {
 		odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath)
 	}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 6f7d3bb..7b0f51f 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -42,12 +42,14 @@
 }
 
 func testApexModuleConfig(ctx android.PathContext, name, apexName string) *ModuleConfig {
-	return createTestModuleConfig(
+	ret := createTestModuleConfig(
 		name,
 		fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, name),
 		android.PathForOutput(ctx, fmt.Sprintf("%s/dexpreopt/%s.jar", name, name)),
 		android.PathForOutput(ctx, fmt.Sprintf("%s/aligned/%s.jar", name, name)),
 		android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
+	ret.ApexPartition = "/system"
+	return ret
 }
 
 func testPlatformSystemServerModuleConfig(ctx android.PathContext, name string) *ModuleConfig {
@@ -221,6 +223,49 @@
 	DexpreoptRunningInSoong = oldDexpreoptRunningInSoong
 }
 
+// Same as `TestDexPreoptApexSystemServerJars`, but the apex jar is in /system_ext
+func TestDexPreoptApexSystemServerJarsSystemExt(t *testing.T) {
+	// modify the global variable for test
+	var oldDexpreoptRunningInSoong = DexpreoptRunningInSoong
+	DexpreoptRunningInSoong = true
+
+	// test begin
+	config := android.TestConfig("out", nil, "", nil)
+	ctx := android.BuilderContextForTesting(config)
+	globalSoong := globalSoongConfigForTests(ctx)
+	global := GlobalConfigForTests(ctx)
+	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+	module.ApexPartition = "/system_ext"
+	productPackages := android.PathForTesting("product_packages.txt")
+
+	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
+		[]string{"com.android.apex1:service-A"})
+
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	wantInstalls := android.RuleBuilderInstalls{
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.odex"), "/system_ext/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.odex"},
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system_ext/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.vdex"},
+	}
+
+	android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
+
+	android.AssertStringListContains(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
+
+	// rule with apex sscp cp as false
+	rule, err = GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	android.AssertStringListDoesNotContain(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
+
+	// cleanup the global variable for test
+	DexpreoptRunningInSoong = oldDexpreoptRunningInSoong
+}
+
 func TestDexPreoptStandaloneSystemServerJars(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
diff --git a/docs/perf.md b/docs/perf.md
index 5b53c8d..446ba9d 100644
--- a/docs/perf.md
+++ b/docs/perf.md
@@ -61,8 +61,7 @@
 
 saves CPU profile for each Soong invocation in /tmp/foo._step_ file, where
 _step_ is Soong execution step. The main step is `build`. The others as
-`bp2build_files`, `bp2build_workspace`, `modulegraph`, `queryview`,
-`api_bp2build`, `soong_docs` (not all of them necessarily run during the build).
+`soong_docs` (not all of them necessarily run during the build).
 The profiles can be inspected with `go tool pprof` from the command line or
 with _Run>Open Profiler Snapshot_ in IntelliJ IDEA.
 
diff --git a/docs/selects.md b/docs/selects.md
new file mode 100644
index 0000000..4f436ac
--- /dev/null
+++ b/docs/selects.md
@@ -0,0 +1,170 @@
+# Select statements
+
+## Introduction
+
+Soong currently has the arch, target, product_variables, and soong config variable properties that all support changing the values of soong properties based on some condition. These are confusing for users, and particularly the soong config variable properties require a lot of boilerplate that is annoying to write.
+
+In addition, these properties are all implemented using reflection on property structs, and can combine in ways that the original authors did not expect. For example, soong config variables can be combined with arch/target by saying that they operate on "arch.arm.enabled" instead of just "enabled". But product variables do not have the same abilities.
+
+The goal here is to combine all these different configuration mechanisms into one, to reduce complexity and boilerplate both in Android.bp files and in soong code.
+
+## Usage
+
+The soong select statements take their name and inspiration from [bazel select statements](https://bazel.build/docs/configurable-attributes).
+
+### Syntax
+
+#### Basic
+
+The basic syntax for select statements looks like:
+
+```
+my_module_type {
+  name: "my_module",
+  some_string_property: select(arch(), {
+    "arm": "foo",
+    "x86": "bar",
+    default: "baz",
+  }),
+}
+```
+
+That is, `select(` followed by a variable function, then a map of values of the variable to values to set the property to.
+
+Arguments can be passed to the "functions" that look up axes:
+
+```
+select(soong_config_variable("my_namespace", "my_variable"), {
+  "value1": "foo",
+  default: "bar",
+})
+```
+
+
+The list of functions that can currently be selected on:
+ - `arch()`
+ - `os()`
+ - `soong_config_variable(namespace, variable)`
+ - `release_flag(flag)`
+
+The functions are [defined here](https://cs.android.com/android/platform/superproject/main/+/main:build/soong/android/module.go;l=2144;drc=3f01580c04bfe37c920e247015cce93cff2451c0), and it should be easy to add more.
+
+#### Multivariable
+
+To handle multivariable selects, multiple axes can be specified within parenthesis, to look like tuple destructuring. All of the variables being selected must match the corresponding value from the branch in order for the branch to be chosen.
+
+```
+select((arch(), os()), {
+  ("arm", "linux"): "foo",
+  (default, "windows"): "bar",
+  (default, default): "baz",
+})
+```
+
+#### Unset
+
+You can have unset branches of selects using the "unset" keyword, which will act as if the property was not assigned to. This is only really useful if you’re using defaults modules.
+
+```
+cc_binary {
+  name: "my_binary",
+  enabled: select(os(), {
+    "darwin": false,
+    default: unset,
+  }),
+}
+```
+
+#### Appending
+
+You can append select statements to both scalar values and other select statements:
+
+```
+my_module_type {
+  name: "my_module",
+  // string_property will be set to something like penguin-four, apple-two, etc.
+  string_property: select(os(), {
+    "linux_glibc": "penguin",
+    "darwin": "apple",
+    default: "unknown",
+  }) + "-" + select(soong_config_variable("ANDROID", "favorite_vehicle"), {
+    "car": "four",
+    "tricycle": "three",
+    "bike": "two",
+     default: "unknown",
+  })
+}
+```
+
+
+You can also append a select with a value with another select that may not have a value, because some of its branches are "unset". If an unset branch was selected it will not append anything to the other select.
+
+#### Binding the selected value to a Blueprint variable and the "any" keyword
+
+In case you want to allow a selected value to have an unbounded number of possible values, you can bind its value to a blueprint variable and use it within the expression for that select branch.
+
+```
+my_module_type {
+  name: "my_module",
+  my_string_property: select(soong_config_variable("my_namespace", "my_variable"), {
+    "some_value": "baz",
+    any @ my_var: "foo" + my_var,
+    default: "bar",
+  }),
+}
+```
+
+The syntax is `any @ my_variable_name: <expression using my_variable_name>`. `any` is currently the only pattern that can be bound to a variable, but we may add more in the future. `any` is equivalent to `default` except it will not match undefined select conditions.
+
+#### Errors
+
+If a select statement does not have a "default" branch, and none of the other branches match the variable being selected on, it’s a compile-time error. This may be useful for enforcing a variable is 1 of only a few values.
+
+```
+# in product config:
+$(call soong_config_set,ANDROID,my_variable,foo)
+
+// in an Android.bp:
+my_module_type {
+  name: "my_module",
+  // Will error out with: soong_config_variable("ANDROID", "my_variable") had value "foo", which was not handled by the select
+  enabled: select(soong_config_variable("ANDROID", "my_variable"), {
+    "bar": true,
+    "baz": false,
+  }),
+}
+```
+
+### Changes to property structs to support selects
+
+Currently, the way configurable properties work is that there is secretly another property struct that has the `target`, `arch`, etc. properties, and then when the arch mutator (or other relevant mutator) runs, it copies the values of these properties onto the regular property structs. There’s nothing stopping you from accessing your properties from a mutator that runs before the one that updates the properties based on configurable values. This is a potential source of bugs, and we want to make sure that select statements don’t have the same pitfall. For that reason, you have to read property’s values through a getter which can do this check. This requires changing the code on a property-by-property basis to support selects.
+
+To make a property support selects, it must be of type [proptools.Configurable[T]](https://cs.android.com/android/platform/superproject/main/+/main:build/blueprint/proptools/configurable.go;l=341;drc=a52b058cccd2caa778d0f97077adcd4ef7ffb68a). T is the old type of the property. Currently we support bool, string, and []string. Configurable has a `Get(evaluator)` method to get the value of the property. The evaluator can be a ModuleContext, or if you’re in a situation where you only have a very limited context and a module, (such as in a singleton) you can use [ModuleBase.ConfigurableEvaluator](https://cs.android.com/android/platform/superproject/main/+/main:build/soong/android/module.go;l=2133;drc=e19f741052cce097da940d9083d3f29e668de5cb).
+
+`proptools.Configurable[T]` will handle unset properties for you, so you don’t need to make it a pointer type. However, there is a not-widely-known feature of property structs, where normally, properties are appended when squashing defaults. But if the property was a pointer property, later defaults replace earlier values instead of appending. With selects, to maintain this behavior, add the `android:"replace_instead_of_append"` struct tag. The "append" behavior for boolean values is to boolean OR them together, which is rarely what you want, so most boolean properties are pointers today.
+
+Old:
+```
+type commonProperties struct {
+  Enabled *bool `android:"arch_variant"`
+}
+
+func (m *ModuleBase) Enabled() *bool {
+  return m.commonProperties.Enabled
+}
+```
+
+New:
+```
+type commonProperties struct {
+  Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
+}
+
+func (m *ModuleBase) Enabled(ctx ConfigAndErrorContext) *bool {
+  return m.commonProperties.Enabled.Get(m.ConfigurableEvaluator(ctx))
+}
+```
+
+The `android:"arch_variant"` tag is kept to support the old `target:` and `arch:` properties with this property, but if all their usages in bp files were replaced by selects, then that tag could be removed.
+
+The enabled property underwent this migration in https://r.android.com/3066188
diff --git a/etc/Android.bp b/etc/Android.bp
index 580c54f..e92437e 100644
--- a/etc/Android.bp
+++ b/etc/Android.bp
@@ -11,6 +11,8 @@
         "soong-android",
     ],
     srcs: [
+        "adb_keys.go",
+        "avbpubkey.go",
         "install_symlink.go",
         "otacerts_zip.go",
         "prebuilt_etc.go",
diff --git a/etc/adb_keys.go b/etc/adb_keys.go
new file mode 100644
index 0000000..cfcb1d5
--- /dev/null
+++ b/etc/adb_keys.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 etc
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("adb_keys", AdbKeysModuleFactory)
+}
+
+type AdbKeysModule struct {
+	android.ModuleBase
+	outputPath  android.Path
+	installPath android.InstallPath
+}
+
+func AdbKeysModuleFactory() android.Module {
+	module := &AdbKeysModule{}
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
+func (m *AdbKeysModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	productVariables := ctx.Config().ProductVariables()
+
+	if !m.ProductSpecific() {
+		ctx.ModuleErrorf("adb_keys module type must set product_specific to true")
+	}
+
+	if !(android.Bool(productVariables.Debuggable) && len(android.String(productVariables.AdbKeys)) > 0) {
+		m.SkipInstall()
+		return
+	}
+
+	outputPath := android.PathForModuleOut(ctx, "adb_keys")
+	input := android.ExistentPathForSource(ctx, android.String(productVariables.AdbKeys))
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Output: outputPath,
+		Input:  input.Path(),
+	})
+	m.installPath = android.PathForModuleInstall(ctx, "etc/security")
+	ctx.InstallFile(m.installPath, "adb_keys", outputPath)
+	m.outputPath = outputPath
+}
+
+func (m *AdbKeysModule) AndroidMkEntries() []android.AndroidMkEntries {
+	if m.IsSkipInstall() {
+		return []android.AndroidMkEntries{}
+	}
+
+	return []android.AndroidMkEntries{
+		{
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(m.outputPath),
+		}}
+}
diff --git a/etc/avbpubkey.go b/etc/avbpubkey.go
new file mode 100644
index 0000000..3f998d4
--- /dev/null
+++ b/etc/avbpubkey.go
@@ -0,0 +1,84 @@
+// 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 etc
+
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	android.RegisterModuleType("avbpubkey", AvbpubkeyModuleFactory)
+	pctx.HostBinToolVariable("avbtool", "avbtool")
+}
+
+type avbpubkeyProperty struct {
+	Private_key *string `android:"path"`
+}
+
+type AvbpubkeyModule struct {
+	android.ModuleBase
+
+	properties avbpubkeyProperty
+
+	outputPath  android.WritablePath
+	installPath android.InstallPath
+}
+
+func AvbpubkeyModuleFactory() android.Module {
+	module := &AvbpubkeyModule{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
+var avbPubKeyRule = pctx.AndroidStaticRule("avbpubkey",
+	blueprint.RuleParams{
+		Command: `${avbtool} extract_public_key --key ${in} --output ${out}.tmp` +
+			` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+		CommandDeps: []string{"${avbtool}"},
+		Description: "Extracting system_other avb key",
+	})
+
+func (m *AvbpubkeyModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if !m.ProductSpecific() {
+		ctx.ModuleErrorf("avbpubkey module type must set product_specific to true")
+	}
+
+	m.outputPath = android.PathForModuleOut(ctx, ctx.ModuleName(), "system_other.avbpubkey")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   avbPubKeyRule,
+		Input:  android.PathForModuleSrc(ctx, proptools.String(m.properties.Private_key)),
+		Output: m.outputPath,
+	})
+
+	m.installPath = android.PathForModuleInstall(ctx, "etc/security/avb")
+	ctx.InstallFile(m.installPath, "system_other.avbpubkey", m.outputPath)
+}
+
+func (m *AvbpubkeyModule) AndroidMkEntries() []android.AndroidMkEntries {
+	if m.IsSkipInstall() {
+		return []android.AndroidMkEntries{}
+	}
+
+	return []android.AndroidMkEntries{
+		{
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(m.outputPath),
+		}}
+}
diff --git a/etc/install_symlink.go b/etc/install_symlink.go
index 2182b86..aa33445 100644
--- a/etc/install_symlink.go
+++ b/etc/install_symlink.go
@@ -26,6 +26,7 @@
 
 func RegisterInstallSymlinkBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("install_symlink", InstallSymlinkFactory)
+	ctx.RegisterModuleType("install_symlink_host", InstallSymlinkHostFactory)
 }
 
 // install_symlink can be used to install an symlink with an arbitrary target to an arbitrary path
@@ -37,6 +38,14 @@
 	return module
 }
 
+// install_symlink can be used to install an symlink to an arbitrary path on the host.
+func InstallSymlinkHostFactory() android.Module {
+	module := &InstallSymlink{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon)
+	return module
+}
+
 type InstallSymlinkProperties struct {
 	// Where to install this symlink, relative to the partition it's installed on.
 	// Which partition it's installed on can be controlled by the vendor, system_ext, ramdisk, etc.
diff --git a/etc/install_symlink_test.go b/etc/install_symlink_test.go
index d7165e5..c97d97c 100644
--- a/etc/install_symlink_test.go
+++ b/etc/install_symlink_test.go
@@ -133,3 +133,39 @@
 		}
 	`)
 }
+
+var prepareForInstallSymlinkHostTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithAndroidBuildComponents,
+	android.FixtureRegisterWithContext(RegisterInstallSymlinkBuildComponents),
+)
+
+func TestInstallSymlinkHostBasic(t *testing.T) {
+	result := prepareForInstallSymlinkHostTest.RunTestWithBp(t, `
+		install_symlink_host {
+			name: "foo",
+			installed_location: "bin/foo",
+			symlink_target: "aa/bb/cc",
+		}
+	`)
+
+	buildOS := result.Config.BuildOS.String()
+	foo := result.ModuleForTests("foo", buildOS+"_common").Module()
+
+	androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, foo)
+	if len(androidMkEntries) != 1 {
+		t.Fatalf("expected 1 androidmkentry, got %d", len(androidMkEntries))
+	}
+
+	symlinks := androidMkEntries[0].EntryMap["LOCAL_SOONG_INSTALL_SYMLINKS"]
+	if len(symlinks) != 1 {
+		t.Fatalf("Expected 1 symlink, got %d", len(symlinks))
+	}
+
+	if !strings.HasSuffix(symlinks[0], "bin/foo") {
+		t.Fatalf("Expected symlink install path to end in bin/foo, got: %s", symlinks[0])
+	}
+
+	if !strings.Contains(symlinks[0], "host") {
+		t.Fatalf("Expected symlink install path to contain `host`, got: %s", symlinks[0])
+	}
+}
diff --git a/etc/otacerts_zip.go b/etc/otacerts_zip.go
index b6f175a..8220cea 100644
--- a/etc/otacerts_zip.go
+++ b/etc/otacerts_zip.go
@@ -44,7 +44,7 @@
 	android.ModuleBase
 
 	properties otacertsZipProperties
-	outputPath android.OutputPath
+	outputPath android.Path
 }
 
 // otacerts_zip collects key files defined in PRODUCT_DEFAULT_DEV_CERTIFICATE
@@ -61,41 +61,41 @@
 
 var _ android.ImageInterface = (*otacertsZipModule)(nil)
 
-func (m *otacertsZipModule) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+func (m *otacertsZipModule) ImageMutatorBegin(ctx android.ImageInterfaceContext) {}
 
-func (m *otacertsZipModule) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+func (m *otacertsZipModule) VendorVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (m *otacertsZipModule) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+func (m *otacertsZipModule) ProductVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (m *otacertsZipModule) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+func (m *otacertsZipModule) CoreVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return !m.ModuleBase.InstallInRecovery()
 }
 
-func (m *otacertsZipModule) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (m *otacertsZipModule) RamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (m *otacertsZipModule) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (m *otacertsZipModule) VendorRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (m *otacertsZipModule) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (m *otacertsZipModule) DebugRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (m *otacertsZipModule) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+func (m *otacertsZipModule) RecoveryVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return proptools.Bool(m.properties.Recovery_available) || m.ModuleBase.InstallInRecovery()
 }
 
-func (m *otacertsZipModule) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+func (m *otacertsZipModule) ExtraImageVariations(ctx android.ImageInterfaceContext) []string {
 	return nil
 }
 
-func (m *otacertsZipModule) SetImageVariation(ctx android.BaseModuleContext, variation string) {
+func (m *otacertsZipModule) SetImageVariation(ctx android.ImageInterfaceContext, variation string) {
 }
 
 func (m *otacertsZipModule) InRecovery() bool {
@@ -117,11 +117,11 @@
 	// Read .x509.pem files listed  in PRODUCT_EXTRA_OTA_KEYS or PRODUCT_EXTRA_RECOVERY_KEYS.
 	extras := ctx.Config().ExtraOtaKeys(ctx, m.InRecovery())
 	srcPaths := append([]android.SourcePath{pem}, extras...)
-	m.outputPath = android.PathForModuleOut(ctx, m.outputFileName()).OutputPath
+	outputPath := android.PathForModuleOut(ctx, m.outputFileName())
 
 	rule := android.NewRuleBuilder(pctx, ctx)
 	cmd := rule.Command().BuiltTool("soong_zip").
-		FlagWithOutput("-o ", m.outputPath).
+		FlagWithOutput("-o ", outputPath).
 		Flag("-j ").
 		Flag("-symlinks=false ")
 	for _, src := range srcPaths {
@@ -130,7 +130,8 @@
 	rule.Build(ctx.ModuleName(), "Generating the otacerts zip file")
 
 	installPath := android.PathForModuleInstall(ctx, "etc", proptools.String(m.properties.Relative_install_path))
-	ctx.InstallFile(installPath, m.outputFileName(), m.outputPath)
+	ctx.InstallFile(installPath, m.outputFileName(), outputPath)
+	m.outputPath = outputPath
 }
 
 func (m *otacertsZipModule) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index fc6d1f7..2bcbde1 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -59,12 +59,32 @@
 	ctx.RegisterModuleType("prebuilt_usr_keylayout", PrebuiltUserKeyLayoutFactory)
 	ctx.RegisterModuleType("prebuilt_usr_keychars", PrebuiltUserKeyCharsFactory)
 	ctx.RegisterModuleType("prebuilt_usr_idc", PrebuiltUserIdcFactory)
+	ctx.RegisterModuleType("prebuilt_usr_srec", PrebuiltUserSrecFactory)
 	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)
 	ctx.RegisterModuleType("prebuilt_renderscript_bitcode", PrebuiltRenderScriptBitcodeFactory)
+	ctx.RegisterModuleType("prebuilt_media", PrebuiltMediaFactory)
+	ctx.RegisterModuleType("prebuilt_voicepack", PrebuiltVoicepackFactory)
+	ctx.RegisterModuleType("prebuilt_bin", PrebuiltBinaryFactory)
+	ctx.RegisterModuleType("prebuilt_wallpaper", PrebuiltWallpaperFactory)
+	ctx.RegisterModuleType("prebuilt_priv_app", PrebuiltPrivAppFactory)
+	ctx.RegisterModuleType("prebuilt_rfs", PrebuiltRfsFactory)
+	ctx.RegisterModuleType("prebuilt_framework", PrebuiltFrameworkFactory)
+	ctx.RegisterModuleType("prebuilt_res", PrebuiltResFactory)
+	ctx.RegisterModuleType("prebuilt_wlc_upt", PrebuiltWlcUptFactory)
+	ctx.RegisterModuleType("prebuilt_odm", PrebuiltOdmFactory)
+	ctx.RegisterModuleType("prebuilt_vendor_dlkm", PrebuiltVendorDlkmFactory)
+	ctx.RegisterModuleType("prebuilt_bt_firmware", PrebuiltBtFirmwareFactory)
+	ctx.RegisterModuleType("prebuilt_tvservice", PrebuiltTvServiceFactory)
+	ctx.RegisterModuleType("prebuilt_optee", PrebuiltOpteeFactory)
+	ctx.RegisterModuleType("prebuilt_tvconfig", PrebuiltTvConfigFactory)
+	ctx.RegisterModuleType("prebuilt_vendor", PrebuiltVendorFactory)
+	ctx.RegisterModuleType("prebuilt_sbin", PrebuiltSbinFactory)
+	ctx.RegisterModuleType("prebuilt_system", PrebuiltSystemFactory)
+	ctx.RegisterModuleType("prebuilt_first_stage_ramdisk", PrebuiltFirstStageRamdiskFactory)
 
 	ctx.RegisterModuleType("prebuilt_defaults", defaultsFactory)
 
@@ -72,15 +92,22 @@
 
 var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterPrebuiltEtcBuildComponents)
 
-type prebuiltEtcProperties struct {
+type PrebuiltEtcProperties struct {
 	// Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
 	// Mutually exclusive with srcs.
 	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.
+	// Mutually exclusive with src. When used, filename_from_src is set to true unless dsts is also
+	// set. May use globs in filenames.
 	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
+	// Destination files of this prebuilt. Requires srcs to be used and causes srcs not to implicitly
+	// set filename_from_src. This can be used to install each source file to a different directory
+	// and/or change filenames when files are installed. Must be exactly one entry per source file,
+	// which means care must be taken if srcs has globs.
+	Dsts 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).
 	Filename *string `android:"arch_variant"`
@@ -114,6 +141,9 @@
 
 	// Install symlinks to the installed file.
 	Symlinks []string `android:"arch_variant"`
+
+	// Install to partition oem when set to true.
+	Oem_specific *bool `android:"arch_variant"`
 }
 
 type prebuiltSubdirProperties struct {
@@ -149,7 +179,7 @@
 	android.ModuleBase
 	android.DefaultableModuleBase
 
-	properties prebuiltEtcProperties
+	properties PrebuiltEtcProperties
 
 	// rootProperties is used to return the value of the InstallInRoot() method. Currently, only
 	// prebuilt_avb and prebuilt_root modules use this.
@@ -158,7 +188,7 @@
 	subdirProperties prebuiltSubdirProperties
 
 	sourceFilePaths android.Paths
-	outputFilePaths android.OutputPaths
+	outputFilePaths android.WritablePaths
 	// The base install location, e.g. "etc" for prebuilt_etc, "usr/share" for prebuilt_usr_share.
 	installDirBase               string
 	installDirBase64             string
@@ -166,7 +196,7 @@
 	// The base install location when soc_specific property is set to true, e.g. "firmware" for
 	// prebuilt_firmware.
 	socInstallDirBase      string
-	installDirPath         android.InstallPath
+	installDirPaths        []android.InstallPath
 	additionalDependencies *android.Paths
 
 	usedSrcsProperty bool
@@ -229,30 +259,30 @@
 
 var _ android.ImageInterface = (*PrebuiltEtc)(nil)
 
-func (p *PrebuiltEtc) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+func (p *PrebuiltEtc) ImageMutatorBegin(ctx android.ImageInterfaceContext) {}
 
-func (p *PrebuiltEtc) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+func (p *PrebuiltEtc) VendorVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (p *PrebuiltEtc) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+func (p *PrebuiltEtc) ProductVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (p *PrebuiltEtc) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+func (p *PrebuiltEtc) CoreVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return !p.ModuleBase.InstallInRecovery() && !p.ModuleBase.InstallInRamdisk() &&
 		!p.ModuleBase.InstallInVendorRamdisk() && !p.ModuleBase.InstallInDebugRamdisk()
 }
 
-func (p *PrebuiltEtc) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (p *PrebuiltEtc) RamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return proptools.Bool(p.properties.Ramdisk_available) || p.ModuleBase.InstallInRamdisk()
 }
 
-func (p *PrebuiltEtc) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (p *PrebuiltEtc) VendorRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return proptools.Bool(p.properties.Vendor_ramdisk_available) || p.ModuleBase.InstallInVendorRamdisk()
 }
 
-func (p *PrebuiltEtc) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (p *PrebuiltEtc) DebugRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return proptools.Bool(p.properties.Debug_ramdisk_available) || p.ModuleBase.InstallInDebugRamdisk()
 }
 
@@ -260,15 +290,15 @@
 	return proptools.Bool(p.rootProperties.Install_in_root)
 }
 
-func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return proptools.Bool(p.properties.Recovery_available) || p.ModuleBase.InstallInRecovery()
 }
 
-func (p *PrebuiltEtc) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+func (p *PrebuiltEtc) ExtraImageVariations(ctx android.ImageInterfaceContext) []string {
 	return nil
 }
 
-func (p *PrebuiltEtc) SetImageVariation(ctx android.BaseModuleContext, variation string) {
+func (p *PrebuiltEtc) SetImageVariation(ctx android.ImageInterfaceContext, variation string) {
 }
 
 func (p *PrebuiltEtc) SourceFilePath(ctx android.ModuleContext) android.Path {
@@ -279,7 +309,10 @@
 }
 
 func (p *PrebuiltEtc) InstallDirPath() android.InstallPath {
-	return p.installDirPath
+	if len(p.installDirPaths) != 1 {
+		panic(fmt.Errorf("InstallDirPath not available on multi-source prebuilt %q", p.Name()))
+	}
+	return p.installDirPaths[0]
 }
 
 // This allows other derivative modules (e.g. prebuilt_etc_xml) to perform
@@ -288,7 +321,7 @@
 	p.additionalDependencies = &paths
 }
 
-func (p *PrebuiltEtc) OutputFile() android.OutputPath {
+func (p *PrebuiltEtc) OutputFile() android.Path {
 	if p.usedSrcsProperty {
 		panic(fmt.Errorf("OutputFile not available on multi-source prebuilt %q", p.Name()))
 	}
@@ -338,12 +371,20 @@
 	if srcProperty.IsPresent() && len(srcsProperty) > 0 {
 		ctx.PropertyErrorf("src", "src is set. Cannot set srcs")
 	}
+	dstsProperty := p.properties.Dsts.GetOrDefault(ctx, nil)
+	if len(dstsProperty) > 0 && len(srcsProperty) == 0 {
+		ctx.PropertyErrorf("dsts", "dsts is set. Must use srcs")
+	}
 
 	// Check that `sub_dir` and `relative_install_path` are not set at the same time.
 	if p.subdirProperties.Sub_dir != nil && p.subdirProperties.Relative_install_path != nil {
 		ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
 	}
-	p.installDirPath = android.PathForModuleInstall(ctx, p.installBaseDir(ctx), p.SubDir())
+	baseInstallDirPath := android.PathForModuleInstall(ctx, p.installBaseDir(ctx), p.SubDir())
+	// TODO(b/377304441)
+	if android.Bool(p.properties.Oem_specific) {
+		baseInstallDirPath = android.PathForModuleInPartitionInstall(ctx, ctx.DeviceConfig().OemPath(), p.installBaseDir(ctx), p.SubDir())
+	}
 
 	filename := proptools.String(p.properties.Filename)
 	filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
@@ -373,16 +414,17 @@
 			ctx.PropertyErrorf("filename", "filename cannot contain separator '/'")
 			return
 		}
-		p.outputFilePaths = android.OutputPaths{android.PathForModuleOut(ctx, filename).OutputPath}
+		p.outputFilePaths = android.WritablePaths{android.PathForModuleOut(ctx, filename)}
 
 		ip := installProperties{
 			filename:       filename,
 			sourceFilePath: p.sourceFilePaths[0],
 			outputFilePath: p.outputFilePaths[0],
-			installDirPath: p.installDirPath,
+			installDirPath: baseInstallDirPath,
 			symlinks:       p.properties.Symlinks,
 		}
 		installs = append(installs, ip)
+		p.installDirPaths = append(p.installDirPaths, baseInstallDirPath)
 	} else if len(srcsProperty) > 0 {
 		p.usedSrcsProperty = true
 		if filename != "" {
@@ -392,20 +434,39 @@
 			ctx.PropertyErrorf("symlinks", "symlinks cannot be set when using srcs")
 		}
 		if p.properties.Filename_from_src != nil {
-			ctx.PropertyErrorf("filename_from_src", "filename_from_src is implicitly set to true when using srcs")
+			if len(dstsProperty) > 0 {
+				ctx.PropertyErrorf("filename_from_src", "dsts is set. Cannot set filename_from_src")
+			} else {
+				ctx.PropertyErrorf("filename_from_src", "filename_from_src is implicitly set to true when using srcs")
+			}
 		}
 		p.sourceFilePaths = android.PathsForModuleSrc(ctx, srcsProperty)
-		for _, src := range p.sourceFilePaths {
-			filename := src.Base()
-			output := android.PathForModuleOut(ctx, filename).OutputPath
+		if len(dstsProperty) > 0 && len(p.sourceFilePaths) != len(dstsProperty) {
+			ctx.PropertyErrorf("dsts", "Must have one entry in dsts per source file")
+		}
+		for i, src := range p.sourceFilePaths {
+			var filename string
+			var installDirPath android.InstallPath
+
+			if len(dstsProperty) > 0 {
+				var dstdir string
+
+				dstdir, filename = filepath.Split(dstsProperty[i])
+				installDirPath = baseInstallDirPath.Join(ctx, dstdir)
+			} else {
+				filename = src.Base()
+				installDirPath = baseInstallDirPath
+			}
+			output := android.PathForModuleOut(ctx, filename)
 			ip := installProperties{
 				filename:       filename,
 				sourceFilePath: src,
 				outputFilePath: output,
-				installDirPath: p.installDirPath,
+				installDirPath: installDirPath,
 			}
 			p.outputFilePaths = append(p.outputFilePaths, output)
 			installs = append(installs, ip)
+			p.installDirPaths = append(p.installDirPaths, installDirPath)
 		}
 	} else if ctx.Config().AllowMissingDependencies() {
 		// If no srcs was set and AllowMissingDependencies is enabled then
@@ -416,14 +477,15 @@
 		if filename == "" {
 			filename = ctx.ModuleName()
 		}
-		p.outputFilePaths = android.OutputPaths{android.PathForModuleOut(ctx, filename).OutputPath}
+		p.outputFilePaths = android.WritablePaths{android.PathForModuleOut(ctx, filename)}
 		ip := installProperties{
 			filename:       filename,
 			sourceFilePath: p.sourceFilePaths[0],
 			outputFilePath: p.outputFilePaths[0],
-			installDirPath: p.installDirPath,
+			installDirPath: baseInstallDirPath,
 		}
 		installs = append(installs, ip)
+		p.installDirPaths = append(p.installDirPaths, baseInstallDirPath)
 	} else {
 		ctx.PropertyErrorf("src", "missing prebuilt source file")
 		return
@@ -443,7 +505,7 @@
 type installProperties struct {
 	filename       string
 	sourceFilePath android.Path
-	outputFilePath android.OutputPath
+	outputFilePath android.WritablePath
 	installDirPath android.InstallPath
 	symlinks       []string
 }
@@ -493,7 +555,7 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
-				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
+				entries.SetString("LOCAL_MODULE_PATH", p.installDirPaths[0].String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePaths[0].Base())
 				if len(p.properties.Symlinks) > 0 {
 					entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
@@ -515,6 +577,7 @@
 	p.installDirBase = dirBase
 	p.AddProperties(&p.properties)
 	p.AddProperties(&p.subdirProperties)
+	p.AddProperties(&p.rootProperties)
 }
 
 func InitPrebuiltRootModule(p *PrebuiltEtc) {
@@ -549,7 +612,7 @@
 
 	module.AddProperties(props...)
 	module.AddProperties(
-		&prebuiltEtcProperties{},
+		&PrebuiltEtcProperties{},
 		&prebuiltSubdirProperties{},
 	)
 
@@ -681,12 +744,23 @@
 	return module
 }
 
+// prebuilt_usr_srec is for a prebuilt artifact that is installed in
+// <partition>/usr/srec/<sub_dir> directory.
+func PrebuiltUserSrecFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "usr/srec")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+	return module
+}
+
 // prebuilt_font installs a font in <partition>/fonts directory.
 func PrebuiltFontFactory() android.Module {
 	module := &PrebuiltEtc{}
 	InitPrebuiltEtcModule(module, "fonts")
 	// This module is device-only
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	return module
 }
@@ -753,3 +827,193 @@
 	android.InitDefaultableModule(module)
 	return module
 }
+
+// prebuilt_media installs media files in <partition>/media directory.
+func PrebuiltMediaFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "media")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_voicepack installs voice pack files in <partition>/tts directory.
+func PrebuiltVoicepackFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "tts")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_bin installs files in <partition>/bin directory.
+func PrebuiltBinaryFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "bin")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_wallpaper installs image files in <partition>/wallpaper directory.
+func PrebuiltWallpaperFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "wallpaper")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_priv_app installs files in <partition>/priv-app directory.
+func PrebuiltPrivAppFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "priv-app")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_rfs installs files in <partition>/rfs directory.
+func PrebuiltRfsFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "rfs")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_framework installs files in <partition>/framework directory.
+func PrebuiltFrameworkFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "framework")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_res installs files in <partition>/res directory.
+func PrebuiltResFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "res")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_wlc_upt installs files in <partition>/wlc_upt directory.
+func PrebuiltWlcUptFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "wlc_upt")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_odm installs files in <partition>/odm directory.
+func PrebuiltOdmFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "odm")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_vendor_dlkm installs files in <partition>/vendor_dlkm directory.
+func PrebuiltVendorDlkmFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "vendor_dlkm")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_bt_firmware installs files in <partition>/bt_firmware directory.
+func PrebuiltBtFirmwareFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "bt_firmware")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_tvservice installs files in <partition>/tvservice directory.
+func PrebuiltTvServiceFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "tvservice")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_optee installs files in <partition>/optee directory.
+func PrebuiltOpteeFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "optee")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_tvconfig installs files in <partition>/tvconfig directory.
+func PrebuiltTvConfigFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "tvconfig")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_vendor installs files in <partition>/vendor directory.
+func PrebuiltVendorFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "vendor")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_sbin installs files in <partition>/sbin directory.
+func PrebuiltSbinFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "sbin")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_system installs files in <partition>/system directory.
+func PrebuiltSystemFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "system")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_first_stage_ramdisk installs files in <partition>/first_stage_ramdisk directory.
+func PrebuiltFirstStageRamdiskFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "first_stage_ramdisk")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index e739afe..0fd04d8 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -119,6 +119,113 @@
 	android.AssertStringEquals(t, "output file path", "foo.conf", p.outputFilePaths[2].Base())
 }
 
+func TestPrebuiltEtcDsts(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_etc {
+			name: "foo",
+			srcs: ["foo.conf", "bar.conf"],
+			dsts: ["foodir/foo.conf", "bardir/extradir/different.name"],
+		}
+	`)
+
+	p := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+	android.AssertStringEquals(t, "output file path", "foo.conf", p.outputFilePaths[0].Base())
+	android.AssertStringEquals(t, "output file path", "different.name", p.outputFilePaths[1].Base())
+
+	expectedPaths := [...]string{
+		"out/soong/target/product/test_device/system/etc/foodir",
+		"out/soong/target/product/test_device/system/etc/bardir/extradir",
+	}
+	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[0], p.installDirPaths[0])
+	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[1], p.installDirPaths[1])
+}
+
+func TestPrebuiltEtcDstsPlusRelativeInstallPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_etc {
+			name: "foo",
+			srcs: ["foo.conf", "bar.conf"],
+			dsts: ["foodir/foo.conf", "bardir/extradir/different.name"],
+			relative_install_path: "somewhere",
+		}
+	`)
+
+	p := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+	android.AssertStringEquals(t, "output file path", "foo.conf", p.outputFilePaths[0].Base())
+	android.AssertStringEquals(t, "output file path", "different.name", p.outputFilePaths[1].Base())
+
+	expectedPaths := [...]string{
+		"out/soong/target/product/test_device/system/etc/somewhere/foodir",
+		"out/soong/target/product/test_device/system/etc/somewhere/bardir/extradir",
+	}
+	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[0], p.installDirPaths[0])
+	android.AssertPathRelativeToTopEquals(t, "install dir", expectedPaths[1], p.installDirPaths[1])
+}
+
+func TestPrebuiltEtcDstsSrcGlob(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_etc {
+			name: "foo",
+			srcs: ["*.conf"],
+			dsts: ["a.conf", "b.conf", "c.conf"],
+		}
+	`)
+
+	p := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+	android.AssertStringEquals(t, "output file path", "a.conf", p.outputFilePaths[0].Base())
+	android.AssertStringEquals(t, "output file path", "b.conf", p.outputFilePaths[1].Base())
+	android.AssertStringEquals(t, "output file path", "c.conf", p.outputFilePaths[2].Base())
+}
+
+func TestPrebuiltEtcDstsSrcGlobDstsTooShort(t *testing.T) {
+	prepareForPrebuiltEtcTest.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("Must have one entry in dsts per source file")).
+		RunTestWithBp(t, `
+			prebuilt_etc {
+				name: "foo",
+				srcs: ["*.conf"],
+				dsts: ["a.conf", "b.conf"],
+			}
+		`)
+}
+
+func TestPrebuiltEtcDstsSrcGlobDstsTooLong(t *testing.T) {
+	prepareForPrebuiltEtcTest.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("Must have one entry in dsts per source file")).
+		RunTestWithBp(t, `
+			prebuilt_etc {
+				name: "foo",
+				srcs: ["*.conf"],
+				dsts: ["a.conf", "b.conf", "c.conf", "d.conf"],
+			}
+		`)
+}
+
+func TestPrebuiltEtcCannotDstsWithSrc(t *testing.T) {
+	prepareForPrebuiltEtcTest.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("dsts is set. Must use srcs")).
+		RunTestWithBp(t, `
+			prebuilt_etc {
+				name: "foo.conf",
+				src: "foo.conf",
+				dsts: ["a.conf"],
+			}
+		`)
+}
+
+func TestPrebuiltEtcCannotDstsWithFilenameFromSrc(t *testing.T) {
+	prepareForPrebuiltEtcTest.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("dsts is set. Cannot set filename_from_src")).
+		RunTestWithBp(t, `
+			prebuilt_etc {
+				name: "foo.conf",
+				srcs: ["foo.conf"],
+				dsts: ["a.conf"],
+				filename_from_src: true,
+			}
+		`)
+}
+
 func TestPrebuiltEtcAndroidMk(t *testing.T) {
 	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc {
@@ -165,7 +272,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/etc/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltEtcCannotSetRelativeInstallPathAndSubDir(t *testing.T) {
@@ -231,7 +338,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltRootInstallDirPathValidate(t *testing.T) {
@@ -256,7 +363,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/root/avb"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltAvdInstallDirPathValidate(t *testing.T) {
@@ -280,7 +387,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/usr/share/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltUserShareHostInstallDirPath(t *testing.T) {
@@ -295,7 +402,7 @@
 	buildOS := result.Config.BuildOS.String()
 	p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc)
 	expected := filepath.Join("out/soong/host", result.Config.PrebuiltOS(), "usr", "share", "bar")
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltPrebuiltUserHyphenDataInstallDirPath(t *testing.T) {
@@ -309,7 +416,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/usr/hyphen-data/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltPrebuiltUserKeyLayoutInstallDirPath(t *testing.T) {
@@ -323,7 +430,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/usr/keylayout/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltPrebuiltUserKeyCharsInstallDirPath(t *testing.T) {
@@ -337,7 +444,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/usr/keychars/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltPrebuiltUserIdcInstallDirPath(t *testing.T) {
@@ -351,7 +458,7 @@
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/usr/idc/bar"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltFontInstallDirPath(t *testing.T) {
@@ -362,9 +469,9 @@
 		}
 	`)
 
-	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+	p := result.Module("foo.conf", "android_common").(*PrebuiltEtc)
 	expected := "out/soong/target/product/test_device/system/fonts"
-	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltOverlayInstallDirPath(t *testing.T) {
@@ -377,7 +484,7 @@
 
 	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)
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
 }
 
 func TestPrebuiltFirmwareDirPath(t *testing.T) {
@@ -409,7 +516,7 @@
 		t.Run(tt.description, func(t *testing.T) {
 			result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
 			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
+			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPaths[0])
 		})
 	}
 }
@@ -443,7 +550,7 @@
 		t.Run(tt.description, func(t *testing.T) {
 			result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
 			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
+			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPaths[0])
 		})
 	}
 }
@@ -477,7 +584,22 @@
 		t.Run(tt.description, func(t *testing.T) {
 			result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
 			p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
+			android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPaths[0])
 		})
 	}
 }
+
+func TestPrebuiltMediaAutoDirPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_media {
+			name: "foo",
+			src: "Alarm_Beep_01.ogg",
+			product_specific: true,
+			relative_install_path: "alarms"
+		}
+	`)
+
+	p := result.Module("foo", "android_common").(*PrebuiltEtc)
+	expected := "out/soong/target/product/test_device/product/media/alarms"
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPaths[0])
+}
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index a08f7cf..127faa7 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -16,13 +16,16 @@
     ],
     srcs: [
         "aconfig_files.go",
+        "android_device.go",
         "avb_add_hash_footer.go",
         "avb_gen_vbmeta_image.go",
         "bootimg.go",
+        "bootconfig.go",
         "filesystem.go",
         "fsverity_metadata.go",
         "logical_partition.go",
         "raw_binary.go",
+        "super_image.go",
         "system_image.go",
         "vbmeta.go",
         "testing.go",
diff --git a/filesystem/aconfig_files.go b/filesystem/aconfig_files.go
index 5c047bc..c80ae03 100644
--- a/filesystem/aconfig_files.go
+++ b/filesystem/aconfig_files.go
@@ -16,7 +16,6 @@
 
 import (
 	"android/soong/android"
-	"strings"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -26,59 +25,41 @@
 		return
 	}
 
-	aconfigFlagsBuilderPath := android.PathForModuleOut(ctx, "aconfig_flags_builder.sh")
-	aconfigToolPath := ctx.Config().HostToolPath(ctx, "aconfig")
-	cmd := builder.Command().Tool(aconfigFlagsBuilderPath).Implicit(aconfigToolPath)
-
-	var caches []string
+	var caches []android.Path
 	for _, ps := range specs {
-		cmd.Implicits(ps.GetAconfigPaths())
-		caches = append(caches, ps.GetAconfigPaths().Strings()...)
+		caches = append(caches, ps.GetAconfigPaths()...)
 	}
-	caches = android.SortedUniqueStrings(caches)
-
-	var sbCaches strings.Builder
-	for _, cache := range caches {
-		sbCaches.WriteString("  --cache ")
-		sbCaches.WriteString(cache)
-		sbCaches.WriteString(" \\\n")
-	}
-	sbCaches.WriteRune('\n')
-
-	var sb strings.Builder
-	sb.WriteString("set -e\n")
+	caches = android.SortedUniquePaths(caches)
 
 	installAconfigFlagsPath := dir.Join(ctx, "etc", "aconfig_flags.pb")
-	sb.WriteString(aconfigToolPath.String())
-	sb.WriteString(" dump-cache --dedup --format protobuf --out ")
-	sb.WriteString(installAconfigFlagsPath.String())
-	sb.WriteString(" \\\n")
-	sb.WriteString(sbCaches.String())
-	cmd.ImplicitOutput(installAconfigFlagsPath)
+	cmd := builder.Command().
+		BuiltTool("aconfig").
+		Text(" dump-cache --dedup --format protobuf --out").
+		Output(installAconfigFlagsPath).
+		Textf("--filter container:%s", f.PartitionType())
+	for _, cache := range caches {
+		cmd.FlagWithInput("--cache ", cache)
+	}
 	f.appendToEntry(ctx, installAconfigFlagsPath)
 
 	installAconfigStorageDir := dir.Join(ctx, "etc", "aconfig")
-	sb.WriteString("mkdir -p ")
-	sb.WriteString(installAconfigStorageDir.String())
-	sb.WriteRune('\n')
+	builder.Command().Text("mkdir -p").Text(installAconfigStorageDir.String())
 
 	generatePartitionAconfigStorageFile := func(fileType, fileName string) {
 		outputPath := installAconfigStorageDir.Join(ctx, fileName)
-		sb.WriteString(aconfigToolPath.String())
-		sb.WriteString(" create-storage --container ")
-		sb.WriteString(f.PartitionType())
-		sb.WriteString(" --file ")
-		sb.WriteString(fileType)
-		sb.WriteString(" --out ")
-		sb.WriteString(outputPath.String())
-		sb.WriteString(" \\\n")
-		sb.WriteString(sbCaches.String())
-		cmd.ImplicitOutput(outputPath)
+		builder.Command().
+			BuiltTool("aconfig").
+			FlagWithArg("create-storage --container ", f.PartitionType()).
+			FlagWithArg("--file ", fileType).
+			FlagWithOutput("--out ", outputPath).
+			FlagWithArg("--cache ", installAconfigFlagsPath.String())
 		f.appendToEntry(ctx, outputPath)
 	}
-	generatePartitionAconfigStorageFile("package_map", "package.map")
-	generatePartitionAconfigStorageFile("flag_map", "flag.map")
-	generatePartitionAconfigStorageFile("flag_val", "flag.val")
 
-	android.WriteExecutableFileRuleVerbatim(ctx, aconfigFlagsBuilderPath, sb.String())
+	if ctx.Config().ReleaseCreateAconfigStorageFile() {
+		generatePartitionAconfigStorageFile("package_map", "package.map")
+		generatePartitionAconfigStorageFile("flag_map", "flag.map")
+		generatePartitionAconfigStorageFile("flag_val", "flag.val")
+		generatePartitionAconfigStorageFile("flag_info", "flag.info")
+	}
 }
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
new file mode 100644
index 0000000..ab1b96e
--- /dev/null
+++ b/filesystem/android_device.go
@@ -0,0 +1,83 @@
+// 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.
+
+package filesystem
+
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+type PartitionNameProperties struct {
+	// Name of the Boot_partition_name partition filesystem module
+	Boot_partition_name *string
+	// Name of the System partition filesystem module
+	System_partition_name *string
+	// Name of the System_ext partition filesystem module
+	System_ext_partition_name *string
+	// Name of the Product partition filesystem module
+	Product_partition_name *string
+	// Name of the Vendor partition filesystem module
+	Vendor_partition_name *string
+	// Name of the Odm partition filesystem module
+	Odm_partition_name *string
+	// The vbmeta partition and its "chained" partitions
+	Vbmeta_partitions []string
+	// Name of the Userdata partition filesystem module
+	Userdata_partition_name *string
+}
+
+type androidDevice struct {
+	android.ModuleBase
+
+	partitionProps PartitionNameProperties
+}
+
+func AndroidDeviceFactory() android.Module {
+	module := &androidDevice{}
+	module.AddProperties(&module.partitionProps)
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+type partitionDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var filesystemDepTag partitionDepTagType
+
+func (a *androidDevice) DepsMutator(ctx android.BottomUpMutatorContext) {
+	addDependencyIfDefined := func(dep *string) {
+		if dep != nil {
+			ctx.AddDependency(ctx.Module(), filesystemDepTag, proptools.String(dep))
+		}
+	}
+
+	addDependencyIfDefined(a.partitionProps.Boot_partition_name)
+	addDependencyIfDefined(a.partitionProps.System_partition_name)
+	addDependencyIfDefined(a.partitionProps.System_ext_partition_name)
+	addDependencyIfDefined(a.partitionProps.Product_partition_name)
+	addDependencyIfDefined(a.partitionProps.Vendor_partition_name)
+	addDependencyIfDefined(a.partitionProps.Odm_partition_name)
+	addDependencyIfDefined(a.partitionProps.Userdata_partition_name)
+	for _, vbmetaPartition := range a.partitionProps.Vbmeta_partitions {
+		ctx.AddDependency(ctx.Module(), filesystemDepTag, vbmetaPartition)
+	}
+}
+
+func (a *androidDevice) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+
+}
diff --git a/filesystem/avb_add_hash_footer.go b/filesystem/avb_add_hash_footer.go
index 469f1fb..9d4ba3e 100644
--- a/filesystem/avb_add_hash_footer.go
+++ b/filesystem/avb_add_hash_footer.go
@@ -29,7 +29,7 @@
 
 	properties avbAddHashFooterProperties
 
-	output     android.OutputPath
+	output     android.Path
 	installDir android.InstallPath
 }
 
@@ -97,8 +97,8 @@
 		return
 	}
 	input := android.PathForModuleSrc(ctx, proptools.String(a.properties.Src))
-	a.output = android.PathForModuleOut(ctx, a.installFileName()).OutputPath
-	builder.Command().Text("cp").Input(input).Output(a.output)
+	output := android.PathForModuleOut(ctx, a.installFileName())
+	builder.Command().Text("cp").Input(input).Output(output)
 
 	cmd := builder.Command().BuiltTool("avbtool").Text("add_hash_footer")
 
@@ -141,12 +141,13 @@
 		cmd.Flag(fmt.Sprintf(" --rollback_index %d", rollbackIndex))
 	}
 
-	cmd.FlagWithOutput("--image ", a.output)
+	cmd.FlagWithOutput("--image ", output)
 
 	builder.Build("avbAddHashFooter", fmt.Sprintf("avbAddHashFooter %s", ctx.ModuleName()))
 
 	a.installDir = android.PathForModuleInstall(ctx, "etc")
-	ctx.InstallFile(a.installDir, a.installFileName(), a.output)
+	ctx.InstallFile(a.installDir, a.installFileName(), output)
+	a.output = output
 }
 
 func addAvbProp(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, prop avbProp) {
diff --git a/filesystem/avb_gen_vbmeta_image.go b/filesystem/avb_gen_vbmeta_image.go
index a7fd782..0669a2c 100644
--- a/filesystem/avb_gen_vbmeta_image.go
+++ b/filesystem/avb_gen_vbmeta_image.go
@@ -28,7 +28,7 @@
 
 	properties avbGenVbmetaImageProperties
 
-	output     android.OutputPath
+	output     android.Path
 	installDir android.InstallPath
 }
 
@@ -78,11 +78,12 @@
 	}
 	cmd.FlagWithArg("--salt ", proptools.String(a.properties.Salt))
 
-	a.output = android.PathForModuleOut(ctx, a.installFileName()).OutputPath
-	cmd.FlagWithOutput("--output_vbmeta_image ", a.output)
+	output := android.PathForModuleOut(ctx, a.installFileName())
+	cmd.FlagWithOutput("--output_vbmeta_image ", output)
 	builder.Build("avbGenVbmetaImage", fmt.Sprintf("avbGenVbmetaImage %s", ctx.ModuleName()))
 
-	ctx.SetOutputFiles([]android.Path{a.output}, "")
+	ctx.SetOutputFiles([]android.Path{output}, "")
+	a.output = output
 }
 
 var _ android.AndroidMkEntriesProvider = (*avbGenVbmetaImage)(nil)
diff --git a/filesystem/bootconfig.go b/filesystem/bootconfig.go
new file mode 100644
index 0000000..b125824
--- /dev/null
+++ b/filesystem/bootconfig.go
@@ -0,0 +1,80 @@
+// 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 filesystem
+
+import (
+	"android/soong/android"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	android.RegisterModuleType("bootconfig", BootconfigModuleFactory)
+	pctx.Import("android/soong/android")
+}
+
+type bootconfigProperty struct {
+	// List of bootconfig parameters that will be written as a line separated list in the output
+	// file.
+	Boot_config []string
+	// Path to the file that contains the list of bootconfig parameters. This will be appended
+	// to the output file, after the entries in boot_config.
+	Boot_config_file *string `android:"path"`
+}
+
+type BootconfigModule struct {
+	android.ModuleBase
+
+	properties bootconfigProperty
+}
+
+// bootconfig module generates the `vendor-bootconfig.img` file, which lists the bootconfig
+// parameters and can be passed as a `--vendor_bootconfig` value in mkbootimg invocation.
+func BootconfigModuleFactory() android.Module {
+	module := &BootconfigModule{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+func (m *BootconfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	bootConfig := m.properties.Boot_config
+	bootConfigFileStr := proptools.String(m.properties.Boot_config_file)
+	if len(bootConfig) == 0 && len(bootConfigFileStr) == 0 {
+		return
+	}
+
+	var bootConfigFile android.Path
+	if len(bootConfigFileStr) > 0 {
+		bootConfigFile = android.PathForModuleSrc(ctx, bootConfigFileStr)
+	}
+
+	outputPath := android.PathForModuleOut(ctx, ctx.ModuleName(), "vendor-bootconfig.img")
+	bootConfigOutput := android.PathForModuleOut(ctx, ctx.ModuleName(), "bootconfig.txt")
+	android.WriteFileRule(ctx, bootConfigOutput, strings.Join(bootConfig, "\n"))
+
+	bcFiles := android.Paths{bootConfigOutput}
+	if bootConfigFile != nil {
+		bcFiles = append(bcFiles, bootConfigFile)
+	}
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.Cat,
+		Description: "concatenate bootconfig parameters",
+		Inputs:      bcFiles,
+		Output:      outputPath,
+	})
+	ctx.SetOutputFiles(android.Paths{outputPath}, "")
+}
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index e796ab9..0ffec26 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -26,19 +26,21 @@
 )
 
 func init() {
-	android.RegisterModuleType("bootimg", bootimgFactory)
+	android.RegisterModuleType("bootimg", BootimgFactory)
 }
 
 type bootimg struct {
 	android.ModuleBase
 
-	properties bootimgProperties
+	properties BootimgProperties
 
-	output     android.OutputPath
+	output     android.Path
 	installDir android.InstallPath
+
+	bootImageType bootImageType
 }
 
-type bootimgProperties struct {
+type BootimgProperties struct {
 	// Set the name of the output. Defaults to <module_name>.img.
 	Stem *string
 
@@ -56,9 +58,13 @@
 	// https://source.android.com/devices/bootloader/boot-image-header
 	Header_version *string
 
-	// Determines if this image is for the vendor_boot partition. Default is false. Refer to
-	// https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
-	Vendor_boot *bool
+	// Determines the specific type of boot image this module is building. Can be boot,
+	// vendor_boot or init_boot. Defaults to boot.
+	// Refer to https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
+	// for vendor_boot.
+	// Refer to https://source.android.com/docs/core/architecture/partitions/generic-boot for
+	// init_boot.
+	Boot_image_type *string
 
 	// Optional kernel commandline arguments
 	Cmdline []string `android:"arch_variant"`
@@ -67,22 +73,87 @@
 	// and `header_version` is greater than or equal to 4.
 	Bootconfig *string `android:"arch_variant,path"`
 
+	// The size of the partition on the device. It will be a build error if this built partition
+	// image exceeds this size.
+	Partition_size *int64
+
 	// When set to true, sign the image with avbtool. Default is false.
 	Use_avb *bool
 
+	// This can either be "default", or "make_legacy". "make_legacy" will sign the boot image
+	// like how build/make/core/Makefile does, to get bit-for-bit backwards compatibility. But
+	// we may want to reconsider if it's necessary to have two modes in the future. The default
+	// is "default"
+	Avb_mode *string
+
 	// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
 	Partition_name *string
 
 	// Path to the private key that avbtool will use to sign this filesystem image.
 	// TODO(jiyong): allow apex_key to be specified here
-	Avb_private_key *string `android:"path"`
+	Avb_private_key *string `android:"path_device_first"`
 
 	// Hash and signing algorithm for avbtool. Default is SHA256_RSA4096.
 	Avb_algorithm *string
+
+	// The index used to prevent rollback of the image on device.
+	Avb_rollback_index *int64
+
+	// The security patch passed to as the com.android.build.<type>.security_patch avb property.
+	// Replacement for the make variables BOOT_SECURITY_PATCH / INIT_BOOT_SECURITY_PATCH.
+	Security_patch *string
+}
+
+type bootImageType int
+
+const (
+	unsupported bootImageType = iota
+	boot
+	vendorBoot
+	initBoot
+)
+
+func toBootImageType(ctx android.ModuleContext, bootImageType string) bootImageType {
+	switch bootImageType {
+	case "boot":
+		return boot
+	case "vendor_boot":
+		return vendorBoot
+	case "init_boot":
+		return initBoot
+	default:
+		ctx.ModuleErrorf("Unknown boot_image_type %s. Must be one of \"boot\", \"vendor_boot\", or \"init_boot\"", bootImageType)
+	}
+	return unsupported
+}
+
+func (b bootImageType) String() string {
+	switch b {
+	case boot:
+		return "boot"
+	case vendorBoot:
+		return "vendor_boot"
+	case initBoot:
+		return "init_boot"
+	default:
+		panic("unknown boot image type")
+	}
+}
+
+func (b bootImageType) isBoot() bool {
+	return b == boot
+}
+
+func (b bootImageType) isVendorBoot() bool {
+	return b == vendorBoot
+}
+
+func (b bootImageType) isInitBoot() bool {
+	return b == initBoot
 }
 
 // bootimg is the image for the boot partition. It consists of header, kernel, ramdisk, and dtb.
-func bootimgFactory() android.Module {
+func BootimgFactory() android.Module {
 	module := &bootimg{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
@@ -112,38 +183,63 @@
 }
 
 func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	vendor := proptools.Bool(b.properties.Vendor_boot)
-	unsignedOutput := b.buildBootImage(ctx, vendor)
+	b.bootImageType = toBootImageType(ctx, proptools.StringDefault(b.properties.Boot_image_type, "boot"))
+	if b.bootImageType == unsupported {
+		return
+	}
 
+	kernelProp := proptools.String(b.properties.Kernel_prebuilt)
+	if b.bootImageType.isVendorBoot() && kernelProp != "" {
+		ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel")
+		return
+	}
+	if b.bootImageType.isBoot() && kernelProp == "" {
+		ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel")
+		return
+	}
+	var kernel android.Path
+	if kernelProp != "" {
+		kernel = android.PathForModuleSrc(ctx, kernelProp)
+	}
+
+	unsignedOutput := b.buildBootImage(ctx, kernel)
+
+	output := unsignedOutput
 	if proptools.Bool(b.properties.Use_avb) {
-		b.output = b.signImage(ctx, unsignedOutput)
-	} else {
-		b.output = unsignedOutput
+		// This bootimg module supports 2 modes of avb signing. It is not clear to this author
+		// why there are differences, but one of them is to match the behavior of make-built boot
+		// images.
+		switch proptools.StringDefault(b.properties.Avb_mode, "default") {
+		case "default":
+			output = b.signImage(ctx, unsignedOutput)
+		case "make_legacy":
+			output = b.addAvbFooter(ctx, unsignedOutput, kernel)
+		default:
+			ctx.PropertyErrorf("avb_mode", `Unknown value for avb_mode, expected "default" or "make_legacy", got: %q`, *b.properties.Avb_mode)
+		}
 	}
 
 	b.installDir = android.PathForModuleInstall(ctx, "etc")
-	ctx.InstallFile(b.installDir, b.installFileName(), b.output)
+	ctx.InstallFile(b.installDir, b.installFileName(), output)
 
-	ctx.SetOutputFiles([]android.Path{b.output}, "")
+	ctx.SetOutputFiles([]android.Path{output}, "")
+	b.output = output
 }
 
-func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android.OutputPath {
-	output := android.PathForModuleOut(ctx, "unsigned", b.installFileName()).OutputPath
+func (b *bootimg) buildBootImage(ctx android.ModuleContext, kernel android.Path) android.Path {
+	output := android.PathForModuleOut(ctx, "unsigned", b.installFileName())
 
 	builder := android.NewRuleBuilder(pctx, ctx)
 	cmd := builder.Command().BuiltTool("mkbootimg")
 
-	kernel := proptools.String(b.properties.Kernel_prebuilt)
-	if vendor && kernel != "" {
-		ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel")
-		return output
+	if kernel != nil {
+		cmd.FlagWithInput("--kernel ", kernel)
 	}
-	if !vendor && kernel == "" {
-		ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel")
-		return output
-	}
-	if kernel != "" {
-		cmd.FlagWithInput("--kernel ", android.PathForModuleSrc(ctx, kernel))
+
+	// These arguments are passed for boot.img and init_boot.img generation
+	if b.bootImageType.isBoot() || b.bootImageType.isInitBoot() {
+		cmd.FlagWithArg("--os_version ", ctx.Config().PlatformVersionLastStable())
+		cmd.FlagWithArg("--os_patch_level ", ctx.Config().PlatformSecurityPatch())
 	}
 
 	dtbName := proptools.String(b.properties.Dtb_prebuilt)
@@ -155,7 +251,7 @@
 	cmdline := strings.Join(b.properties.Cmdline, " ")
 	if cmdline != "" {
 		flag := "--cmdline "
-		if vendor {
+		if b.bootImageType.isVendorBoot() {
 			flag = "--vendor_cmdline "
 		}
 		cmd.FlagWithArg(flag, proptools.ShellEscapeIncludingSpaces(cmdline))
@@ -182,7 +278,7 @@
 		ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
 		if filesystem, ok := ramdisk.(*filesystem); ok {
 			flag := "--ramdisk "
-			if vendor {
+			if b.bootImageType.isVendorBoot() {
 				flag = "--vendor_ramdisk "
 			}
 			cmd.FlagWithInput(flag, filesystem.OutputPath())
@@ -194,7 +290,7 @@
 
 	bootconfig := proptools.String(b.properties.Bootconfig)
 	if bootconfig != "" {
-		if !vendor {
+		if !b.bootImageType.isVendorBoot() {
 			ctx.PropertyErrorf("bootconfig", "requires vendor_boot: true")
 			return output
 		}
@@ -205,20 +301,85 @@
 		cmd.FlagWithInput("--vendor_bootconfig ", android.PathForModuleSrc(ctx, bootconfig))
 	}
 
+	// Output flag for boot.img and init_boot.img
 	flag := "--output "
-	if vendor {
+	if b.bootImageType.isVendorBoot() {
 		flag = "--vendor_boot "
 	}
 	cmd.FlagWithOutput(flag, output)
 
+	if b.properties.Partition_size != nil {
+		assertMaxImageSize(builder, output, *b.properties.Partition_size, proptools.Bool(b.properties.Use_avb))
+	}
+
 	builder.Build("build_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName()))
 	return output
 }
 
-func (b *bootimg) signImage(ctx android.ModuleContext, unsignedImage android.OutputPath) android.OutputPath {
+func (b *bootimg) addAvbFooter(ctx android.ModuleContext, unsignedImage android.Path, kernel android.Path) android.Path {
+	output := android.PathForModuleOut(ctx, b.installFileName())
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Text("cp").Input(unsignedImage).Output(output)
+	cmd := builder.Command().BuiltTool("avbtool").
+		Text("add_hash_footer").
+		FlagWithInput("--image ", output)
+
+	if b.properties.Partition_size != nil {
+		cmd.FlagWithArg("--partition_size ", strconv.FormatInt(*b.properties.Partition_size, 10))
+	} else {
+		cmd.Flag("--dynamic_partition_size")
+	}
+
+	// If you don't provide a salt, avbtool will use random bytes for the salt.
+	// This is bad for determinism (cached builds and diff tests are affected), so instead,
+	// we try to provide a salt. The requirements for a salt are not very clear, one aspect of it
+	// is that if it's unpredictable, attackers trying to change the contents of a partition need
+	// to find a new hash collision every release, because the salt changed.
+	if kernel != nil {
+		cmd.Textf(`--salt $(sha256sum "%s" | cut -d " " -f 1)`, kernel.String())
+		cmd.Implicit(kernel)
+	} else {
+		cmd.Textf(`--salt $(sha256sum "%s" "%s" | cut -d " " -f 1 | tr -d '\n')`, ctx.Config().BuildNumberFile(ctx), ctx.Config().Getenv("BUILD_DATETIME_FILE"))
+		cmd.OrderOnly(ctx.Config().BuildNumberFile(ctx))
+	}
+
+	cmd.FlagWithArg("--partition_name ", b.bootImageType.String())
+
+	if b.properties.Avb_algorithm != nil {
+		cmd.FlagWithArg("--algorithm ", proptools.NinjaAndShellEscape(*b.properties.Avb_algorithm))
+	}
+
+	if b.properties.Avb_private_key != nil {
+		key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key))
+		cmd.FlagWithInput("--key ", key)
+	}
+
+	if !b.bootImageType.isVendorBoot() {
+		cmd.FlagWithArg("--prop ", proptools.NinjaAndShellEscape(fmt.Sprintf(
+			"com.android.build.%s.os_version:%s", b.bootImageType.String(), ctx.Config().PlatformVersionLastStable())))
+	}
+
+	fingerprintFile := ctx.Config().BuildFingerprintFile(ctx)
+	cmd.FlagWithArg("--prop ", fmt.Sprintf("com.android.build.%s.fingerprint:$(cat %s)", b.bootImageType.String(), fingerprintFile.String()))
+	cmd.OrderOnly(fingerprintFile)
+
+	if b.properties.Security_patch != nil {
+		cmd.FlagWithArg("--prop ", proptools.NinjaAndShellEscape(fmt.Sprintf(
+			"com.android.build.%s.security_patch:%s", b.bootImageType.String(), *b.properties.Security_patch)))
+	}
+
+	if b.properties.Avb_rollback_index != nil {
+		cmd.FlagWithArg("--rollback_index ", strconv.FormatInt(*b.properties.Avb_rollback_index, 10))
+	}
+
+	builder.Build("add_avb_footer", fmt.Sprintf("Adding avb footer to %s", b.BaseModuleName()))
+	return output
+}
+
+func (b *bootimg) signImage(ctx android.ModuleContext, unsignedImage android.Path) android.Path {
 	propFile, toolDeps := b.buildPropFile(ctx)
 
-	output := android.PathForModuleOut(ctx, b.installFileName()).OutputPath
+	output := android.PathForModuleOut(ctx, b.installFileName())
 	builder := android.NewRuleBuilder(pctx, ctx)
 	builder.Command().Text("cp").Input(unsignedImage).Output(output)
 	builder.Command().BuiltTool("verity_utils").
@@ -239,7 +400,7 @@
 	return sha1sum(input)
 }
 
-func (b *bootimg) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
+func (b *bootimg) buildPropFile(ctx android.ModuleContext) (android.Path, android.Paths) {
 	var sb strings.Builder
 	var deps android.Paths
 	addStr := func(name string, value string) {
@@ -261,7 +422,7 @@
 	addStr("partition_name", partitionName)
 	addStr("avb_salt", b.salt())
 
-	propFile = android.PathForModuleOut(ctx, "prop").OutputPath
+	propFile := android.PathForModuleOut(ctx, "prop")
 	android.WriteFileRule(ctx, propFile, sb.String())
 	return propFile, deps
 }
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 0353992..b9cb076 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -25,6 +25,8 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/java"
+	"android/soong/linkerconfig"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -32,46 +34,71 @@
 
 func init() {
 	registerBuildComponents(android.InitRegistrationContext)
+	registerMutators(android.InitRegistrationContext)
 }
 
 func registerBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("android_filesystem", filesystemFactory)
+	ctx.RegisterModuleType("android_filesystem", FilesystemFactory)
 	ctx.RegisterModuleType("android_filesystem_defaults", filesystemDefaultsFactory)
-	ctx.RegisterModuleType("android_system_image", systemImageFactory)
+	ctx.RegisterModuleType("android_system_image", SystemImageFactory)
 	ctx.RegisterModuleType("avb_add_hash_footer", avbAddHashFooterFactory)
 	ctx.RegisterModuleType("avb_add_hash_footer_defaults", avbAddHashFooterDefaultsFactory)
 	ctx.RegisterModuleType("avb_gen_vbmeta_image", avbGenVbmetaImageFactory)
 	ctx.RegisterModuleType("avb_gen_vbmeta_image_defaults", avbGenVbmetaImageDefaultsFactory)
 }
 
+func registerMutators(ctx android.RegistrationContext) {
+	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("add_autogenerated_rro_deps", addAutogeneratedRroDeps)
+	})
+}
+
+// Remember to add referenced files to implicits!
+var textFileProcessorRule = pctx.AndroidStaticRule("text_file_processing", blueprint.RuleParams{
+	Command:     "build/soong/scripts/text_file_processor.py $in $out",
+	CommandDeps: []string{"build/soong/scripts/text_file_processor.py"},
+})
+
 type filesystem struct {
 	android.ModuleBase
 	android.PackagingBase
 	android.DefaultableModuleBase
 
-	properties filesystemProperties
+	properties FilesystemProperties
 
-	// Function that builds extra files under the root directory and returns the files
-	buildExtraFiles func(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths
-
-	// Function that filters PackagingSpec in PackagingBase.GatherPackagingSpecs()
-	filterPackagingSpec func(spec android.PackagingSpec) bool
-
-	output     android.OutputPath
+	output     android.Path
 	installDir android.InstallPath
 
-	fileListFile android.OutputPath
+	fileListFile android.Path
 
 	// Keeps the entries installed from this filesystem
 	entries []string
+
+	filesystemBuilder filesystemBuilder
 }
 
-type symlinkDefinition struct {
+type filesystemBuilder interface {
+	BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath)
+	// Function that filters PackagingSpec in PackagingBase.GatherPackagingSpecs()
+	FilterPackagingSpec(spec android.PackagingSpec) bool
+	// Function that modifies PackagingSpec in PackagingBase.GatherPackagingSpecs() to customize.
+	// For example, GSI system.img contains system_ext and product artifacts and their
+	// relPathInPackage need to be rebased to system/system_ext and system/system_product.
+	ModifyPackagingSpec(spec *android.PackagingSpec)
+
+	// Function to check if the filesystem should not use `vintf_fragments` property,
+	// but use `vintf_fragment` module type instead
+	ShouldUseVintfFragmentModuleOnly() bool
+}
+
+var _ filesystemBuilder = (*filesystem)(nil)
+
+type SymlinkDefinition struct {
 	Target *string
 	Name   *string
 }
 
-type filesystemProperties struct {
+type FilesystemProperties struct {
 	// When set to true, sign the image with avbtool. Default is false.
 	Use_avb *bool
 
@@ -86,13 +113,16 @@
 	// avbtool. Default used by avbtool is sha1.
 	Avb_hash_algorithm *string
 
+	// Whether or not to use forward-error-correction codes when signing with AVB. Defaults to true.
+	Use_fec *bool
+
 	// The index used to prevent rollback of the image. Only used if use_avb is true.
 	Rollback_index *int64
 
 	// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
 	Partition_name *string
 
-	// Type of the filesystem. Currently, ext4, cpio, and compressed_cpio are supported. Default
+	// Type of the filesystem. Currently, ext4, erofs, cpio, and compressed_cpio are supported. Default
 	// is ext4.
 	Type *string
 
@@ -100,9 +130,13 @@
 	// checks, and will be used in the future for API surface checks.
 	Partition_type *string
 
-	// file_contexts file to make image. Currently, only ext4 is supported.
+	// file_contexts file to make image. Currently, only ext4 is supported. These file contexts
+	// will be compiled with sefcontext_compile
 	File_contexts *string `android:"path"`
 
+	// The selinux file contexts, after having already run them through sefcontext_compile
+	Precompiled_file_contexts *string `android:"path"`
+
 	// Base directory relative to root, to which deps are installed, e.g. "system". Default is "."
 	// (root).
 	Base_dir *string
@@ -111,7 +145,7 @@
 	Dirs proptools.Configurable[[]string]
 
 	// Symbolic links to be created under root with "ln -sf <target> <name>".
-	Symlinks []symlinkDefinition
+	Symlinks []SymlinkDefinition
 
 	// Seconds since unix epoch to override timestamps of file entries
 	Fake_timestamp *string
@@ -137,6 +171,61 @@
 	Gen_aconfig_flags_pb *bool
 
 	Fsverity fsverityProperties
+
+	// If this property is set to true, the filesystem will call ctx.UncheckedModule(), causing
+	// it to not be built on checkbuilds. Used for the automatic migration from make to soong
+	// build modules, where we want to emit some not-yet-working filesystems and we don't want them
+	// to be built.
+	Unchecked_module *bool `blueprint:"mutated"`
+
+	Erofs ErofsProperties
+
+	F2fs F2fsProperties
+
+	Linker_config LinkerConfigProperties
+
+	// Determines if the module is auto-generated from Soong or not. If the module is
+	// auto-generated, its deps are exempted from visibility enforcement.
+	Is_auto_generated *bool
+
+	// Path to the dev nodes description file. This is only needed for building the ramdisk
+	// partition and should not be explicitly specified.
+	Dev_nodes_description_file *string `android:"path" blueprint:"mutated"`
+
+	// Additional dependencies used for building android products
+	Android_filesystem_deps AndroidFilesystemDeps
+}
+
+type AndroidFilesystemDeps struct {
+	System     *string
+	System_ext *string
+}
+
+// Additional properties required to generate erofs FS partitions.
+type ErofsProperties struct {
+	// Compressor and Compression level passed to mkfs.erofs. e.g. (lz4hc,9)
+	// Please see external/erofs-utils/README for complete documentation.
+	Compressor *string
+
+	// Used as --compress-hints for mkfs.erofs
+	Compress_hints *string `android:"path"`
+
+	Sparse *bool
+}
+
+// Additional properties required to generate f2fs FS partitions.
+type F2fsProperties struct {
+	Sparse *bool
+}
+
+type LinkerConfigProperties struct {
+
+	// Build a linker.config.pb file
+	Gen_linker_config *bool
+
+	// List of files (in .json format) that will be converted to a linker config file (in .pb format).
+	// The linker config file be installed in the filesystem at /etc/linker.config.pb
+	Linker_config_srcs []string `android:"path"`
 }
 
 // android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -144,9 +233,9 @@
 // modules in the filesystem image are built for the target device (i.e. Android, not Linux host).
 // The modules are placed in the filesystem image just like they are installed to the ordinary
 // partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
-func filesystemFactory() android.Module {
+func FilesystemFactory() android.Module {
 	module := &filesystem{}
-	module.filterPackagingSpec = module.filterInstallablePackagingSpec
+	module.filesystemBuilder = module
 	initFilesystemModule(module, module)
 	return module
 }
@@ -155,43 +244,110 @@
 	module.AddProperties(&filesystemModule.properties)
 	android.InitPackageModule(filesystemModule)
 	filesystemModule.PackagingBase.DepsCollectFirstTargetOnly = true
+	filesystemModule.PackagingBase.AllowHighPriorityDeps = true
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		filesystemModule.setDevNodesDescriptionProp()
+	})
 }
 
-var dependencyTag = struct {
+type depTag struct {
 	blueprint.BaseDependencyTag
 	android.PackagingItemAlwaysDepTag
-}{}
+}
+
+var dependencyTag = depTag{}
+
+type depTagWithVisibilityEnforcementBypass struct {
+	depTag
+}
+
+type interPartitionDepTag struct {
+	blueprint.BaseDependencyTag
+}
+
+var interPartitionDependencyTag = interPartitionDepTag{}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = (*depTagWithVisibilityEnforcementBypass)(nil)
+
+func (t depTagWithVisibilityEnforcementBypass) ExcludeFromVisibilityEnforcement() {}
+
+var dependencyTagWithVisibilityEnforcementBypass = depTagWithVisibilityEnforcementBypass{}
+
+// ramdiskDevNodesDescription is the name of the filegroup module that provides the file that
+// contains the description of dev nodes added to the CPIO archive for the ramdisk partition.
+const ramdiskDevNodesDescription = "ramdisk_node_list"
+
+func (f *filesystem) setDevNodesDescriptionProp() {
+	if proptools.String(f.properties.Partition_name) == "ramdisk" {
+		f.properties.Dev_nodes_description_file = proptools.StringPtr(":" + ramdiskDevNodesDescription)
+	}
+}
 
 func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
-	f.AddDeps(ctx, dependencyTag)
+	if proptools.Bool(f.properties.Is_auto_generated) {
+		f.AddDeps(ctx, dependencyTagWithVisibilityEnforcementBypass)
+	} else {
+		f.AddDeps(ctx, dependencyTag)
+	}
+	if f.properties.Android_filesystem_deps.System != nil {
+		ctx.AddDependency(ctx.Module(), interPartitionDependencyTag, proptools.String(f.properties.Android_filesystem_deps.System))
+	}
+	if f.properties.Android_filesystem_deps.System_ext != nil {
+		ctx.AddDependency(ctx.Module(), interPartitionDependencyTag, proptools.String(f.properties.Android_filesystem_deps.System_ext))
+	}
 }
 
 type fsType int
 
 const (
 	ext4Type fsType = iota
+	erofsType
+	f2fsType
 	compressedCpioType
 	cpioType // uncompressed
 	unknown
 )
 
-func (f *filesystem) fsType(ctx android.ModuleContext) fsType {
-	typeStr := proptools.StringDefault(f.properties.Type, "ext4")
+func (fs fsType) IsUnknown() bool {
+	return fs == unknown
+}
+
+type FilesystemInfo struct {
+	// A text file containing the list of paths installed on the partition.
+	FileListFile android.Path
+}
+
+var FilesystemProvider = blueprint.NewProvider[FilesystemInfo]()
+
+func GetFsTypeFromString(ctx android.EarlyModuleContext, typeStr string) fsType {
 	switch typeStr {
 	case "ext4":
 		return ext4Type
+	case "erofs":
+		return erofsType
+	case "f2fs":
+		return f2fsType
 	case "compressed_cpio":
 		return compressedCpioType
 	case "cpio":
 		return cpioType
 	default:
-		ctx.PropertyErrorf("type", "%q not supported", typeStr)
 		return unknown
 	}
 }
 
+func (f *filesystem) fsType(ctx android.ModuleContext) fsType {
+	typeStr := proptools.StringDefault(f.properties.Type, "ext4")
+	fsType := GetFsTypeFromString(ctx, typeStr)
+	if fsType == unknown {
+		ctx.PropertyErrorf("type", "%q not supported", typeStr)
+	}
+	return fsType
+}
+
 func (f *filesystem) installFileName() string {
 	return f.BaseModuleName() + ".img"
 }
@@ -200,18 +356,43 @@
 	return proptools.StringDefault(f.properties.Partition_name, f.Name())
 }
 
-func (f *filesystem) filterInstallablePackagingSpec(ps android.PackagingSpec) bool {
+func (f *filesystem) FilterPackagingSpec(ps android.PackagingSpec) bool {
 	// Filesystem module respects the installation semantic. A PackagingSpec from a module with
 	// IsSkipInstall() is skipped.
-	return !ps.SkipInstall()
+	if ps.SkipInstall() {
+		return false
+	}
+	if proptools.Bool(f.properties.Is_auto_generated) { // TODO (spandandas): Remove this.
+		pt := f.PartitionType()
+		return ps.Partition() == pt || strings.HasPrefix(ps.Partition(), pt+"/")
+	}
+	return true
+}
+
+func (f *filesystem) ModifyPackagingSpec(ps *android.PackagingSpec) {
+	// Sometimes, android.modulePartition() returns a path with >1 path components.
+	// This makes the partition field of packagingSpecs have multiple components, like
+	// "system/product". Right now, the filesystem module doesn't look at the partition field
+	// when deciding what path to install the file under, only the RelPathInPackage field, so
+	// we move the later path components from partition to relPathInPackage. This should probably
+	// be revisited in the future.
+	prefix := f.PartitionType() + "/"
+	if strings.HasPrefix(ps.Partition(), prefix) {
+		subPartition := strings.TrimPrefix(ps.Partition(), prefix)
+		ps.SetPartition(f.PartitionType())
+		ps.SetRelPathInPackage(filepath.Join(subPartition, ps.RelPathInPackage()))
+	}
 }
 
 var pctx = android.NewPackageContext("android/soong/filesystem")
 
 func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	validatePartitionType(ctx, f)
+	if f.filesystemBuilder.ShouldUseVintfFragmentModuleOnly() {
+		f.validateVintfFragments(ctx)
+	}
 	switch f.fsType(ctx) {
-	case ext4Type:
+	case ext4Type, erofsType, f2fsType:
 		f.output = f.buildImageUsingBuildImage(ctx)
 	case compressedCpioType:
 		f.output = f.buildCpioImage(ctx, true)
@@ -225,12 +406,58 @@
 	ctx.InstallFile(f.installDir, f.installFileName(), f.output)
 	ctx.SetOutputFiles([]android.Path{f.output}, "")
 
-	f.fileListFile = android.PathForModuleOut(ctx, "fileList").OutputPath
-	android.WriteFileRule(ctx, f.fileListFile, f.installedFilesList())
+	fileListFile := android.PathForModuleOut(ctx, "fileList")
+	android.WriteFileRule(ctx, fileListFile, f.installedFilesList())
+
+	android.SetProvider(ctx, FilesystemProvider, FilesystemInfo{
+		FileListFile: fileListFile,
+	})
+	f.fileListFile = fileListFile
+
+	if proptools.Bool(f.properties.Unchecked_module) {
+		ctx.UncheckedModule()
+	}
 }
 
-func (f *filesystem) appendToEntry(ctx android.ModuleContext, installedFile android.OutputPath) {
-	partitionBaseDir := android.PathForModuleOut(ctx, "root", f.partitionName()).String() + "/"
+func (f *filesystem) validateVintfFragments(ctx android.ModuleContext) {
+	visitedModule := map[string]bool{}
+	packagingSpecs := f.gatherFilteredPackagingSpecs(ctx)
+
+	moduleInFileSystem := func(mod android.Module) bool {
+		for _, ps := range android.OtherModuleProviderOrDefault(
+			ctx, mod, android.InstallFilesProvider).PackagingSpecs {
+			if _, ok := packagingSpecs[ps.RelPathInPackage()]; ok {
+				return true
+			}
+		}
+		return false
+	}
+
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		if visitedModule[child.Name()] {
+			return false
+		}
+		if !moduleInFileSystem(child) {
+			visitedModule[child.Name()] = true
+			return true
+		}
+		if vintfFragments := child.VintfFragments(ctx); vintfFragments != nil {
+			ctx.PropertyErrorf(
+				"vintf_fragments",
+				"Module %s is referenced by soong-defined filesystem %s with property vintf_fragments(%s) in use."+
+					" Use vintf_fragment_modules property instead.",
+				child.Name(),
+				f.BaseModuleName(),
+				strings.Join(vintfFragments, ", "),
+			)
+		}
+		visitedModule[child.Name()] = true
+		return true
+	})
+}
+
+func (f *filesystem) appendToEntry(ctx android.ModuleContext, installedFile android.Path) {
+	partitionBaseDir := android.PathForModuleOut(ctx, "root", proptools.String(f.properties.Base_dir)).String() + "/"
 
 	relPath, inTargetPartition := strings.CutPrefix(installedFile.String(), partitionBaseDir)
 	if inTargetPartition {
@@ -291,25 +518,6 @@
 		builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String())
 		f.appendToEntry(ctx, dst)
 	}
-
-	// create extra files if there's any
-	if f.buildExtraFiles != nil {
-		rootForExtraFiles := android.PathForModuleGen(ctx, "root-extra").OutputPath
-		extraFiles := f.buildExtraFiles(ctx, rootForExtraFiles)
-		for _, extraFile := range extraFiles {
-			rel, err := filepath.Rel(rootForExtraFiles.String(), extraFile.String())
-			if err != nil || strings.HasPrefix(rel, "..") {
-				ctx.ModuleErrorf("can't make %q relative to %q", extraFile, rootForExtraFiles)
-			}
-			f.appendToEntry(ctx, rootDir.Join(ctx, rel))
-		}
-		if len(extraFiles) > 0 {
-			builder.Command().BuiltTool("merge_directories").
-				Implicits(extraFiles.Paths()).
-				Text(rootDir.String()).
-				Text(rootForExtraFiles.String())
-		}
-	}
 }
 
 func (f *filesystem) copyPackagingSpecs(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir, rebasedDir android.WritablePath) []string {
@@ -339,7 +547,7 @@
 	builder.Command().Textf("cp -prf %s/* %s", rebasedDir, installPath)
 }
 
-func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath {
+func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.Path {
 	rootDir := android.PathForModuleOut(ctx, "root").OutputPath
 	rebasedDir := rootDir
 	if f.properties.Base_dir != nil {
@@ -356,6 +564,7 @@
 	f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
 	f.buildEventLogtagsFile(ctx, builder, rebasedDir)
 	f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir)
+	f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir)
 	f.copyFilesToProductOut(ctx, builder, rebasedDir)
 
 	// run host_init_verifier
@@ -367,11 +576,21 @@
 		FlagWithArg("--out_system=", rootDir.String()+"/system")
 
 	propFile, toolDeps := f.buildPropFile(ctx)
-	output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
-	builder.Command().BuiltTool("build_image").
+
+	// Most of the time, if build_image were to call a host tool, it accepts the path to the
+	// host tool in a field in the prop file. However, it doesn't have that option for fec, which
+	// it expects to just be on the PATH. Add fec to the PATH.
+	fec := ctx.Config().HostToolPath(ctx, "fec")
+	pathToolDirs := []string{filepath.Dir(fec.String())}
+
+	output := android.PathForModuleOut(ctx, f.installFileName())
+	builder.Command().
+		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
+		BuiltTool("build_image").
 		Text(rootDir.String()). // input directory
 		Input(propFile).
 		Implicits(toolDeps).
+		Implicit(fec).
 		Output(output).
 		Text(rootDir.String()) // directory where to find fs_config_files|dirs
 
@@ -381,14 +600,14 @@
 	return output
 }
 
-func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.OutputPath {
+func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.Path {
 	builder := android.NewRuleBuilder(pctx, ctx)
 	fcBin := android.PathForModuleOut(ctx, "file_contexts.bin")
 	builder.Command().BuiltTool("sefcontext_compile").
 		FlagWithOutput("-o ", fcBin).
 		Input(android.PathForModuleSrc(ctx, proptools.String(f.properties.File_contexts)))
 	builder.Build("build_filesystem_file_contexts", fmt.Sprintf("Creating filesystem file contexts for %s", f.BaseModuleName()))
-	return fcBin.OutputPath
+	return fcBin
 }
 
 // Calculates avb_salt from entry list (sorted) for deterministic output.
@@ -396,7 +615,7 @@
 	return sha1sum(f.entries)
 }
 
-func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
+func (f *filesystem) buildPropFile(ctx android.ModuleContext) (android.Path, android.Paths) {
 	var deps android.Paths
 	var propFileString strings.Builder
 	addStr := func(name string, value string) {
@@ -413,9 +632,13 @@
 	// Type string that build_image.py accepts.
 	fsTypeStr := func(t fsType) string {
 		switch t {
-		// TODO(jiyong): add more types like f2fs, erofs, etc.
+		// TODO(372522486): add more types like f2fs, erofs, etc.
 		case ext4Type:
 			return "ext4"
+		case erofsType:
+			return "erofs"
+		case f2fsType:
+			return "f2fs"
 		}
 		panic(fmt.Errorf("unsupported fs type %v", t))
 	}
@@ -434,10 +657,15 @@
 		addPath("avb_avbtool", ctx.Config().HostToolPath(ctx, "avbtool"))
 		algorithm := proptools.StringDefault(f.properties.Avb_algorithm, "SHA256_RSA4096")
 		addStr("avb_algorithm", algorithm)
-		key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key))
-		addPath("avb_key_path", key)
+		if f.properties.Avb_private_key != nil {
+			key := android.PathForModuleSrc(ctx, *f.properties.Avb_private_key)
+			addPath("avb_key_path", key)
+		}
 		addStr("partition_name", f.partitionName())
-		avb_add_hashtree_footer_args := "--do_not_generate_fec"
+		avb_add_hashtree_footer_args := ""
+		if !proptools.BoolDefault(f.properties.Use_fec, true) {
+			avb_add_hashtree_footer_args += " --do_not_generate_fec"
+		}
 		if hashAlgorithm := proptools.String(f.properties.Avb_hash_algorithm); hashAlgorithm != "" {
 			avb_add_hashtree_footer_args += " --hash_algorithm " + hashAlgorithm
 		}
@@ -448,15 +676,22 @@
 			}
 			avb_add_hashtree_footer_args += " --rollback_index " + strconv.Itoa(rollbackIndex)
 		}
-		securityPatchKey := "com.android.build." + f.partitionName() + ".security_patch"
-		securityPatchValue := ctx.Config().PlatformSecurityPatch()
-		avb_add_hashtree_footer_args += " --prop " + securityPatchKey + ":" + securityPatchValue
+		avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.os_version:%s", f.partitionName(), ctx.Config().PlatformVersionLastStable())
+		avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.fingerprint:{CONTENTS_OF:%s}", f.partitionName(), ctx.Config().BuildFingerprintFile(ctx))
+		avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.security_patch:%s", f.partitionName(), ctx.Config().PlatformSecurityPatch())
 		addStr("avb_add_hashtree_footer_args", avb_add_hashtree_footer_args)
 		addStr("avb_salt", f.salt())
 	}
 
-	if proptools.String(f.properties.File_contexts) != "" {
+	if f.properties.File_contexts != nil && f.properties.Precompiled_file_contexts != nil {
+		ctx.ModuleErrorf("file_contexts and precompiled_file_contexts cannot both be set")
+	} else if f.properties.File_contexts != nil {
 		addPath("selinux_fc", f.buildFileContexts(ctx))
+	} else if f.properties.Precompiled_file_contexts != nil {
+		src := android.PathForModuleSrc(ctx, *f.properties.Precompiled_file_contexts)
+		if src != nil {
+			addPath("selinux_fc", src)
+		}
 	}
 	if timestamp := proptools.String(f.properties.Fake_timestamp); timestamp != "" {
 		addStr("timestamp", timestamp)
@@ -465,12 +700,68 @@
 		addStr("uuid", uuid)
 		addStr("hash_seed", uuid)
 	}
-	propFile = android.PathForModuleOut(ctx, "prop").OutputPath
-	android.WriteFileRuleVerbatim(ctx, propFile, propFileString.String())
+
+	// TODO(b/381120092): This should only be added if none of the size-related properties are set,
+	// but currently soong built partitions don't have size properties. Make code:
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2262;drc=39cd33701c9278db0e7e481a090605f428d5b12d
+	// Make uses system_disable_sparse but disable_sparse has the same effect, and we shouldn't need
+	// to qualify it because each partition gets its own property file built.
+	addStr("disable_sparse", "true")
+
+	fst := f.fsType(ctx)
+	switch fst {
+	case erofsType:
+		// Add erofs properties
+		addStr("erofs_default_compressor", proptools.StringDefault(f.properties.Erofs.Compressor, "lz4hc,9"))
+		if f.properties.Erofs.Compress_hints != nil {
+			src := android.PathForModuleSrc(ctx, *f.properties.Erofs.Compress_hints)
+			addPath("erofs_default_compress_hints", src)
+		}
+		if proptools.BoolDefault(f.properties.Erofs.Sparse, true) {
+			// https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2292;bpv=1;bpt=0;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b
+			addStr("erofs_sparse_flag", "-s")
+		}
+	case f2fsType:
+		if proptools.BoolDefault(f.properties.F2fs.Sparse, true) {
+			// https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2294;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0
+			addStr("f2fs_sparse_flag", "-S")
+		}
+	}
+	f.checkFsTypePropertyError(ctx, fst, fsTypeStr(fst))
+
+	propFilePreProcessing := android.PathForModuleOut(ctx, "prop_pre_processing")
+	android.WriteFileRuleVerbatim(ctx, propFilePreProcessing, propFileString.String())
+	propFile := android.PathForModuleOut(ctx, "prop")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:     textFileProcessorRule,
+		Input:    propFilePreProcessing,
+		Output:   propFile,
+		Implicit: ctx.Config().BuildFingerprintFile(ctx),
+	})
 	return propFile, deps
 }
 
-func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) android.OutputPath {
+// This method checks if there is any property set for the fstype(s) other than
+// the current fstype.
+func (f *filesystem) checkFsTypePropertyError(ctx android.ModuleContext, t fsType, fs string) {
+	raiseError := func(otherFsType, currentFsType string) {
+		errMsg := fmt.Sprintf("%s is non-empty, but FS type is %s\n. Please delete %s properties if this partition should use %s\n", otherFsType, currentFsType, otherFsType, currentFsType)
+		ctx.PropertyErrorf(otherFsType, errMsg)
+	}
+
+	if t != erofsType {
+		if f.properties.Erofs.Compressor != nil || f.properties.Erofs.Compress_hints != nil || f.properties.Erofs.Sparse != nil {
+			raiseError("erofs", fs)
+		}
+	}
+	if t != f2fsType {
+		if f.properties.F2fs.Sparse != nil {
+			raiseError("f2fs", fs)
+		}
+	}
+}
+
+func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) android.Path {
 	if proptools.Bool(f.properties.Use_avb) {
 		ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+
 			"Consider adding this to bootimg module and signing the entire boot image.")
@@ -499,12 +790,16 @@
 	f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
 	f.buildEventLogtagsFile(ctx, builder, rebasedDir)
 	f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir)
+	f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir)
 	f.copyFilesToProductOut(ctx, builder, rebasedDir)
 
-	output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
+	output := android.PathForModuleOut(ctx, f.installFileName())
 	cmd := builder.Command().
 		BuiltTool("mkbootfs").
 		Text(rootDir.String()) // input directory
+	if nodeList := f.properties.Dev_nodes_description_file; nodeList != nil {
+		cmd.FlagWithInput("-n ", android.PathForModuleSrc(ctx, proptools.String(nodeList)))
+	}
 	if compressed {
 		cmd.Text("|").
 			BuiltTool("lz4").
@@ -534,6 +829,9 @@
 	"vendor_dlkm",
 	"odm_dlkm",
 	"system_dlkm",
+	"ramdisk",
+	"vendor_ramdisk",
+	"recovery",
 }
 
 func (f *filesystem) addMakeBuiltFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.Path) {
@@ -590,6 +888,22 @@
 	f.appendToEntry(ctx, eventLogtagsPath)
 }
 
+func (f *filesystem) BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
+	if !proptools.Bool(f.properties.Linker_config.Gen_linker_config) {
+		return
+	}
+
+	provideModules, _ := f.getLibsForLinkerConfig(ctx)
+	output := rebasedDir.Join(ctx, "etc", "linker.config.pb")
+	linkerconfig.BuildLinkerConfig(ctx, builder, android.PathsForModuleSrc(ctx, f.properties.Linker_config.Linker_config_srcs), provideModules, nil, output)
+
+	f.appendToEntry(ctx, output)
+}
+
+func (f *filesystem) ShouldUseVintfFragmentModuleOnly() bool {
+	return false
+}
+
 type partition interface {
 	PartitionType() string
 }
@@ -645,7 +959,7 @@
 // Note that "apex" module installs its contents to "apex"(fake partition) as well
 // for symbol lookup by imitating "activated" paths.
 func (f *filesystem) gatherFilteredPackagingSpecs(ctx android.ModuleContext) map[string]android.PackagingSpec {
-	specs := f.PackagingBase.GatherPackagingSpecsWithFilter(ctx, f.filterPackagingSpec)
+	specs := f.PackagingBase.GatherPackagingSpecsWithFilterAndModifier(ctx, f.filesystemBuilder.FilterPackagingSpec, f.filesystemBuilder.ModifyPackagingSpec)
 	return specs
 }
 
@@ -671,13 +985,7 @@
 	android.ModuleBase
 	android.DefaultsModuleBase
 
-	properties filesystemDefaultsProperties
-}
-
-type filesystemDefaultsProperties struct {
-	// Identifies which partition this is for //visibility:any_system_image (and others) visibility
-	// checks, and will be used in the future for API surface checks.
-	Partition_type *string
+	properties FilesystemProperties
 }
 
 // android_filesystem_defaults is a default module for android_filesystem and android_system_image
@@ -698,3 +1006,98 @@
 func (f *filesystemDefaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	validatePartitionType(ctx, f)
 }
+
+// getLibsForLinkerConfig returns
+// 1. A list of libraries installed in this filesystem
+// 2. A list of dep libraries _not_ installed in this filesystem
+//
+// `linkerconfig.BuildLinkerConfig` will convert these two to a linker.config.pb for the filesystem
+// (1) will be added to --provideLibs if they are C libraries with a stable interface (has stubs)
+// (2) will be added to --requireLibs if they are C libraries with a stable interface (has stubs)
+func (f *filesystem) getLibsForLinkerConfig(ctx android.ModuleContext) ([]android.Module, []android.Module) {
+	// we need "Module"s for packaging items
+	modulesInPackageByModule := make(map[android.Module]bool)
+	modulesInPackageByName := make(map[string]bool)
+
+	deps := f.gatherFilteredPackagingSpecs(ctx)
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		for _, ps := range android.OtherModuleProviderOrDefault(
+			ctx, child, android.InstallFilesProvider).PackagingSpecs {
+			if _, ok := deps[ps.RelPathInPackage()]; ok && ps.Partition() == f.PartitionType() {
+				modulesInPackageByModule[child] = true
+				modulesInPackageByName[child.Name()] = true
+				return true
+			}
+		}
+		return true
+	})
+
+	provideModules := make([]android.Module, 0, len(modulesInPackageByModule))
+	for mod := range modulesInPackageByModule {
+		provideModules = append(provideModules, mod)
+	}
+
+	var requireModules []android.Module
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		_, parentInPackage := modulesInPackageByModule[parent]
+		_, childInPackageName := modulesInPackageByName[child.Name()]
+
+		// When parent is in the package, and child (or its variant) is not, this can be from an interface.
+		if parentInPackage && !childInPackageName {
+			requireModules = append(requireModules, child)
+		}
+		return true
+	})
+
+	return provideModules, requireModules
+}
+
+// Checks that the given file doesn't exceed the given size, and will also print a warning
+// if it's nearing the maximum size. Equivalent to assert-max-image-size in make:
+// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/definitions.mk;l=3455;drc=993c4de29a02a6accd60ceaaee153307e1a18d10
+func assertMaxImageSize(builder *android.RuleBuilder, image android.Path, maxSize int64, addAvbLater bool) {
+	if addAvbLater {
+		// The value 69632 is derived from MAX_VBMETA_SIZE + MAX_FOOTER_SIZE in avbtool.
+		// Logic copied from make:
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=228;drc=a6a0007ef24e16c0b79f439beac4a118416717e6
+		maxSize -= 69632
+	}
+	cmd := builder.Command()
+	cmd.Textf(`file="%s"; maxsize="%d";`+
+		`total=$(stat -c "%%s" "$file" | tr -d '\n');`+
+		`if [ "$total" -gt "$maxsize" ]; then `+
+		`  echo "error: $file too large ($total > $maxsize)";`+
+		`  false;`+
+		`elif [ "$total" -gt $((maxsize - 32768)) ]; then `+
+		`  echo "WARNING: $file approaching size limit ($total now; limit $maxsize)";`+
+		`fi`,
+		image, maxSize)
+	cmd.Implicit(image)
+}
+
+// addAutogeneratedRroDeps walks the transitive closure of vendor and product partitions.
+// It visits apps installed in system and system_ext partitions, and adds the autogenerated
+// RRO modules to its own deps.
+func addAutogeneratedRroDeps(ctx android.BottomUpMutatorContext) {
+	f, ok := ctx.Module().(*filesystem)
+	if !ok {
+		return
+	}
+	thisPartition := f.PartitionType()
+	if thisPartition != "vendor" && thisPartition != "product" {
+		return
+	}
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		depTag := ctx.OtherModuleDependencyTag(child)
+		if parent.Name() == f.Name() && depTag != interPartitionDependencyTag {
+			return false // This is a module listed in deps of vendor/product filesystem
+		}
+		if vendorOverlay := java.AutogeneratedRroModuleName(ctx, child.Name(), "vendor"); ctx.OtherModuleExists(vendorOverlay) && thisPartition == "vendor" {
+			ctx.AddFarVariationDependencies(nil, dependencyTagWithVisibilityEnforcementBypass, vendorOverlay)
+		}
+		if productOverlay := java.AutogeneratedRroModuleName(ctx, child.Name(), "product"); ctx.OtherModuleExists(productOverlay) && thisPartition == "product" {
+			ctx.AddFarVariationDependencies(nil, dependencyTagWithVisibilityEnforcementBypass, productOverlay)
+		}
+		return true
+	})
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 8c0d111..0ed3870 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"os"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -156,11 +157,15 @@
 	result := fixture.RunTestWithBp(t, `
 		android_system_image {
 			name: "myfilesystem",
+			base_dir: "system",
 			deps: [
 				"libfoo",
 				"libbar",
 			],
-			linker_config_src: "linker.config.json",
+			linker_config: {
+				gen_linker_config: true,
+				linker_config_srcs: ["linker.config.json"],
+			},
 		}
 
 		cc_library {
@@ -176,12 +181,16 @@
 	`)
 
 	module := result.ModuleForTests("myfilesystem", "android_common")
-	output := module.Output("system/etc/linker.config.pb")
+	output := module.Output("out/soong/.intermediates/myfilesystem/android_common/root/system/etc/linker.config.pb")
+
+	fullCommand := output.RuleParams.Command
+	startIndex := strings.Index(fullCommand, "conv_linker_config")
+	linkerConfigCommand := fullCommand[startIndex:]
 
 	android.AssertStringDoesContain(t, "linker.config.pb should have libfoo",
-		output.RuleParams.Command, "libfoo.so")
+		linkerConfigCommand, "libfoo.so")
 	android.AssertStringDoesNotContain(t, "linker.config.pb should not have libbar",
-		output.RuleParams.Command, "libbar.so")
+		linkerConfigCommand, "libbar.so")
 }
 
 func registerComponent(ctx android.RegistrationContext) {
@@ -223,7 +232,10 @@
 					deps: ["foo"],
 				},
 			},
-			linker_config_src: "linker.config.json",
+			linker_config: {
+				gen_linker_config: true,
+				linker_config_srcs: ["linker.config.json"],
+			},
 		}
 		component {
 			name: "foo",
@@ -232,7 +244,7 @@
 	`)
 
 	module := result.ModuleForTests("myfilesystem", "android_common").Module().(*systemImage)
-	android.AssertDeepEquals(t, "entries should have foo only", []string{"components/foo"}, module.entries)
+	android.AssertDeepEquals(t, "entries should have foo and not bar", []string{"components/foo", "etc/linker.config.pb"}, module.entries)
 }
 
 func TestAvbGenVbmetaImage(t *testing.T) {
@@ -318,7 +330,10 @@
 			deps: [
 				"libfoo",
 			],
-			linker_config_src: "linker.config.json",
+			linker_config: {
+				gen_linker_config: true,
+				linker_config_srcs: ["linker.config.json"],
+			},
 		}
 
 		cc_library {
@@ -559,3 +574,217 @@
 		}
 	}
 }
+
+func TestErofsPartition(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem {
+			name: "erofs_partition",
+			type: "erofs",
+			erofs: {
+				compressor: "lz4hc,9",
+				compress_hints: "compress_hints.txt",
+			},
+			deps: ["binfoo"],
+		}
+
+		cc_binary {
+			name: "binfoo",
+		}
+	`)
+
+	partition := result.ModuleForTests("erofs_partition", "android_common")
+	buildImageConfig := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("prop_pre_processing"))
+	android.AssertStringDoesContain(t, "erofs fs type", buildImageConfig, "fs_type=erofs")
+	android.AssertStringDoesContain(t, "erofs fs type compress algorithm", buildImageConfig, "erofs_default_compressor=lz4hc,9")
+	android.AssertStringDoesContain(t, "erofs fs type compress hint", buildImageConfig, "erofs_default_compress_hints=compress_hints.txt")
+	android.AssertStringDoesContain(t, "erofs fs type sparse", buildImageConfig, "erofs_sparse_flag=-s")
+}
+
+func TestF2fsPartition(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem {
+			name: "f2fs_partition",
+			type: "f2fs",
+		}
+	`)
+
+	partition := result.ModuleForTests("f2fs_partition", "android_common")
+	buildImageConfig := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("prop_pre_processing"))
+	android.AssertStringDoesContain(t, "f2fs fs type", buildImageConfig, "fs_type=f2fs")
+	android.AssertStringDoesContain(t, "f2fs fs type sparse", buildImageConfig, "f2fs_sparse_flag=-S")
+}
+
+func TestFsTypesPropertyError(t *testing.T) {
+	fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		"erofs: erofs is non-empty, but FS type is f2fs\n. Please delete erofs properties if this partition should use f2fs\n")).
+		RunTestWithBp(t, `
+		android_filesystem {
+			name: "f2fs_partition",
+			type: "f2fs",
+			erofs: {
+				compressor: "lz4hc,9",
+				compress_hints: "compress_hints.txt",
+			},
+		}
+	`)
+}
+
+// If a system_ext/ module depends on system/ module, the dependency should *not*
+// be installed in system_ext/
+func TestDoNotPackageCrossPartitionDependencies(t *testing.T) {
+	t.Skip() // TODO (spandandas): Re-enable this
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem {
+			name: "myfilesystem",
+			deps: ["binfoo"],
+			partition_type: "system_ext",
+		}
+
+		cc_binary {
+			name: "binfoo",
+			shared_libs: ["libfoo"],
+			system_ext_specific: true,
+		}
+		cc_library_shared {
+			name: "libfoo", // installed in system/
+		}
+	`)
+
+	partition := result.ModuleForTests("myfilesystem", "android_common")
+	fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList"))
+	android.AssertDeepEquals(t, "filesystem with dependencies on different partition", "bin/binfoo\n", fileList)
+}
+
+// If a cc_library is listed in `deps`, and it has a shared and static variant, then the shared variant
+// should be installed.
+func TestUseSharedVariationOfNativeLib(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem {
+			name: "myfilesystem",
+			deps: ["libfoo"],
+		}
+		// cc_library will create a static and shared variant.
+		cc_library {
+			name: "libfoo",
+		}
+	`)
+
+	partition := result.ModuleForTests("myfilesystem", "android_common")
+	fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList"))
+	android.AssertDeepEquals(t, "cc_library listed in deps",
+		"lib64/bootstrap/libc.so\nlib64/bootstrap/libdl.so\nlib64/bootstrap/libm.so\nlib64/libc++.so\nlib64/libc.so\nlib64/libdl.so\nlib64/libfoo.so\nlib64/libm.so\n",
+		fileList)
+}
+
+// binfoo1 overrides binbar. transitive deps of binbar should not be installed.
+func TestDoNotInstallTransitiveDepOfOverriddenModule(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+android_filesystem {
+    name: "myfilesystem",
+    deps: ["binfoo1", "libfoo2", "binbar"],
+}
+cc_binary {
+    name: "binfoo1",
+    shared_libs: ["libfoo"],
+    overrides: ["binbar"],
+}
+cc_library {
+    name: "libfoo",
+}
+cc_library {
+    name: "libfoo2",
+    overrides: ["libfoo"],
+}
+// binbar gets overridden by binfoo1
+// therefore, libbar should not be installed
+cc_binary {
+    name: "binbar",
+    shared_libs: ["libbar"]
+}
+cc_library {
+    name: "libbar",
+}
+	`)
+
+	partition := result.ModuleForTests("myfilesystem", "android_common")
+	fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList"))
+	android.AssertDeepEquals(t, "Shared library dep of overridden binary should not be installed",
+		"bin/binfoo1\nlib64/bootstrap/libc.so\nlib64/bootstrap/libdl.so\nlib64/bootstrap/libm.so\nlib64/libc++.so\nlib64/libc.so\nlib64/libdl.so\nlib64/libfoo2.so\nlib64/libm.so\n",
+		fileList)
+}
+
+func TestInstallLinkerConfigFile(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+android_filesystem {
+    name: "myfilesystem",
+    deps: ["libfoo_has_no_stubs", "libfoo_has_stubs"],
+    linker_config: {
+        gen_linker_config: true,
+        linker_config_srcs: ["linker.config.json"],
+    },
+    partition_type: "vendor",
+}
+cc_library {
+    name: "libfoo_has_no_stubs",
+    vendor: true,
+}
+cc_library {
+    name: "libfoo_has_stubs",
+    stubs: {symbol_file: "libfoo.map.txt"},
+    vendor: true,
+}
+	`)
+
+	linkerConfigCmd := result.ModuleForTests("myfilesystem", "android_common").Rule("build_filesystem_image").RuleParams.Command
+	android.AssertStringDoesContain(t, "Could not find linker.config.json file in cmd", linkerConfigCmd, "conv_linker_config proto --force -s linker.config.json")
+	android.AssertStringDoesContain(t, "Could not find stub in `provideLibs`", linkerConfigCmd, "--key provideLibs --value libfoo_has_stubs.so")
+}
+
+// override_android_* modules implicitly override their base module.
+// If both of these are listed in `deps`, the base module should not be installed.
+func TestOverrideModulesInDeps(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem {
+			name: "myfilesystem",
+			deps: ["myapp", "myoverrideapp"],
+		}
+
+		android_app {
+			name: "myapp",
+			platform_apis: true,
+		}
+		override_android_app {
+			name: "myoverrideapp",
+			base: "myapp",
+		}
+	`)
+
+	partition := result.ModuleForTests("myfilesystem", "android_common")
+	fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList"))
+	android.AssertStringEquals(t, "filesystem with override app", "app/myoverrideapp/myoverrideapp.apk\n", fileList)
+}
+
+func TestRamdiskPartitionSetsDevNodes(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		fixture,
+		android.FixtureMergeMockFs(android.MockFS{
+			"ramdisk_node_list": nil,
+		}),
+	).RunTestWithBp(t, `
+		android_filesystem {
+			name: "ramdisk_filesystem",
+			partition_name: "ramdisk",
+		}
+		filegroup {
+			name: "ramdisk_node_list",
+			srcs: ["ramdisk_node_list"],
+		}
+	`)
+
+	android.AssertBoolEquals(
+		t,
+		"Generated ramdisk image expected to depend on \"ramdisk_node_list\" module",
+		true,
+		java.CheckModuleHasDependency(t, result.TestContext, "ramdisk_filesystem", "android_common", "ramdisk_node_list"),
+	)
+}
diff --git a/filesystem/fsverity_metadata.go b/filesystem/fsverity_metadata.go
index d7bb654..ef46067 100644
--- a/filesystem/fsverity_metadata.go
+++ b/filesystem/fsverity_metadata.go
@@ -15,6 +15,7 @@
 package filesystem
 
 import (
+	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -32,7 +33,7 @@
 	Libs []string `android:"path"`
 }
 
-func (f *filesystem) writeManifestGeneratorListFile(ctx android.ModuleContext, outputPath android.OutputPath, matchedSpecs []android.PackagingSpec, rebasedDir android.OutputPath) {
+func (f *filesystem) writeManifestGeneratorListFile(ctx android.ModuleContext, outputPath android.WritablePath, matchedSpecs []android.PackagingSpec, rebasedDir android.OutputPath) {
 	var buf strings.Builder
 	for _, spec := range matchedSpecs {
 		buf.WriteString(rebasedDir.Join(ctx, spec.RelPathInPackage()).String())
@@ -65,115 +66,85 @@
 		return
 	}
 
-	fsverityBuilderPath := android.PathForModuleOut(ctx, "fsverity_builder.sh")
-	metadataGeneratorPath := ctx.Config().HostToolPath(ctx, "fsverity_metadata_generator")
 	fsverityPath := ctx.Config().HostToolPath(ctx, "fsverity")
 
-	cmd := builder.Command().Tool(fsverityBuilderPath)
-
 	// STEP 1: generate .fsv_meta
 	var sb strings.Builder
 	sb.WriteString("set -e\n")
-	cmd.Implicit(metadataGeneratorPath).Implicit(fsverityPath)
 	for _, spec := range matchedSpecs {
 		// srcPath is copied by CopySpecsToDir()
 		srcPath := rebasedDir.Join(ctx, spec.RelPathInPackage())
 		destPath := rebasedDir.Join(ctx, spec.RelPathInPackage()+".fsv_meta")
-		sb.WriteString(metadataGeneratorPath.String())
-		sb.WriteString(" --fsverity-path ")
-		sb.WriteString(fsverityPath.String())
-		sb.WriteString(" --signature none --hash-alg sha256 --output ")
-		sb.WriteString(destPath.String())
-		sb.WriteRune(' ')
-		sb.WriteString(srcPath.String())
-		sb.WriteRune('\n')
+		builder.Command().
+			BuiltTool("fsverity_metadata_generator").
+			FlagWithInput("--fsverity-path ", fsverityPath).
+			FlagWithArg("--signature ", "none").
+			FlagWithArg("--hash-alg ", "sha256").
+			FlagWithArg("--output ", destPath.String()).
+			Text(srcPath.String())
 		f.appendToEntry(ctx, destPath)
 	}
 
 	// STEP 2: generate signed BuildManifest.apk
 	// STEP 2-1: generate build_manifest.pb
+	manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity_manifest.list")
+	f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath, matchedSpecs, rebasedDir)
 	assetsPath := android.PathForModuleOut(ctx, "fsverity_manifest/assets")
 	manifestPbPath := assetsPath.Join(ctx, "build_manifest.pb")
-	manifestGeneratorPath := ctx.Config().HostToolPath(ctx, "fsverity_manifest_generator")
-	cmd.Implicit(manifestGeneratorPath)
-	sb.WriteString("rm -rf ")
-	sb.WriteString(assetsPath.String())
-	sb.WriteString(" && mkdir -p ")
-	sb.WriteString(assetsPath.String())
-	sb.WriteRune('\n')
-	sb.WriteString(manifestGeneratorPath.String())
-	sb.WriteString(" --fsverity-path ")
-	sb.WriteString(fsverityPath.String())
-	sb.WriteString(" --base-dir ")
-	sb.WriteString(rootDir.String())
-	sb.WriteString(" --output ")
-	sb.WriteString(manifestPbPath.String())
-	sb.WriteRune(' ')
-	f.appendToEntry(ctx, manifestPbPath)
+	builder.Command().Text("rm -rf " + assetsPath.String())
+	builder.Command().Text("mkdir -p " + assetsPath.String())
+	builder.Command().
+		BuiltTool("fsverity_manifest_generator").
+		FlagWithInput("--fsverity-path ", fsverityPath).
+		FlagWithArg("--base-dir ", rootDir.String()).
+		FlagWithArg("--output ", manifestPbPath.String()).
+		FlagWithInput("@", manifestGeneratorListPath)
 
-	manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity_manifest.list")
-	f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath.OutputPath, matchedSpecs, rebasedDir)
-	sb.WriteRune('@')
-	sb.WriteString(manifestGeneratorListPath.String())
-	sb.WriteRune('\n')
-	cmd.Implicit(manifestGeneratorListPath)
-	f.appendToEntry(ctx, manifestGeneratorListPath.OutputPath)
+	f.appendToEntry(ctx, manifestPbPath)
+	f.appendToEntry(ctx, manifestGeneratorListPath)
 
 	// STEP 2-2: generate BuildManifest.apk (unsigned)
-	aapt2Path := ctx.Config().HostToolPath(ctx, "aapt2")
-	apkPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", "BuildManifest.apk")
-	idsigPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", "BuildManifest.apk.idsig")
+	apkNameSuffix := ""
+	if f.PartitionType() == "system_ext" {
+		//https://source.corp.google.com/h/googleplex-android/platform/build/+/e392d2b486c2d4187b20a72b1c67cc737ecbcca5:core/Makefile;l=3410;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0
+		apkNameSuffix = "SystemExt"
+	}
+	apkPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk", apkNameSuffix))
+	idsigPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk.idsig", apkNameSuffix))
 	manifestTemplatePath := android.PathForSource(ctx, "system/security/fsverity/AndroidManifest.xml")
 	libs := android.PathsForModuleSrc(ctx, f.properties.Fsverity.Libs)
-	cmd.Implicit(aapt2Path)
-	cmd.Implicit(manifestTemplatePath)
-	cmd.Implicits(libs)
-	cmd.ImplicitOutput(apkPath)
 
-	sb.WriteString(aapt2Path.String())
-	sb.WriteString(" link -o ")
-	sb.WriteString(apkPath.String())
-	sb.WriteString(" -A ")
-	sb.WriteString(assetsPath.String())
-	for _, lib := range libs {
-		sb.WriteString(" -I ")
-		sb.WriteString(lib.String())
-	}
 	minSdkVersion := ctx.Config().PlatformSdkCodename()
 	if minSdkVersion == "REL" {
 		minSdkVersion = ctx.Config().PlatformSdkVersion().String()
 	}
-	sb.WriteString(" --min-sdk-version ")
-	sb.WriteString(minSdkVersion)
-	sb.WriteString(" --version-code ")
-	sb.WriteString(ctx.Config().PlatformSdkVersion().String())
-	sb.WriteString(" --version-name ")
-	sb.WriteString(ctx.Config().AppsDefaultVersionName())
-	sb.WriteString(" --manifest ")
-	sb.WriteString(manifestTemplatePath.String())
-	sb.WriteString(" --rename-manifest-package com.android.security.fsverity_metadata.")
-	sb.WriteString(f.partitionName())
-	sb.WriteRune('\n')
+
+	unsignedApkCommand := builder.Command().
+		BuiltTool("aapt2").
+		Text("link").
+		FlagWithOutput("-o ", apkPath).
+		FlagWithArg("-A ", assetsPath.String())
+	for _, lib := range libs {
+		unsignedApkCommand.FlagWithInput("-I ", lib)
+	}
+	unsignedApkCommand.
+		FlagWithArg("--min-sdk-version ", minSdkVersion).
+		FlagWithArg("--version-code ", ctx.Config().PlatformSdkVersion().String()).
+		FlagWithArg("--version-name ", ctx.Config().AppsDefaultVersionName()).
+		FlagWithInput("--manifest ", manifestTemplatePath).
+		Text(" --rename-manifest-package com.android.security.fsverity_metadata." + f.partitionName())
 
 	f.appendToEntry(ctx, apkPath)
 
 	// STEP 2-3: sign BuildManifest.apk
-	apksignerPath := ctx.Config().HostToolPath(ctx, "apksigner")
 	pemPath, keyPath := ctx.Config().DefaultAppCertificate(ctx)
-	cmd.Implicit(apksignerPath)
-	cmd.Implicit(pemPath)
-	cmd.Implicit(keyPath)
-	cmd.ImplicitOutput(idsigPath)
-	sb.WriteString(apksignerPath.String())
-	sb.WriteString(" sign --in ")
-	sb.WriteString(apkPath.String())
-	sb.WriteString(" --cert ")
-	sb.WriteString(pemPath.String())
-	sb.WriteString(" --key ")
-	sb.WriteString(keyPath.String())
-	sb.WriteRune('\n')
+	builder.Command().
+		BuiltTool("apksigner").
+		Text("sign").
+		FlagWithArg("--in ", apkPath.String()).
+		FlagWithInput("--cert ", pemPath).
+		FlagWithInput("--key ", keyPath).
+		ImplicitOutput(idsigPath)
 
 	f.appendToEntry(ctx, idsigPath)
-
-	android.WriteExecutableFileRuleVerbatim(ctx, fsverityBuilderPath, sb.String())
 }
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index 988a57b..d0888a9 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -32,7 +32,7 @@
 
 	properties logicalPartitionProperties
 
-	output     android.OutputPath
+	output     android.Path
 	installDir android.InstallPath
 }
 
@@ -95,11 +95,14 @@
 	builder := android.NewRuleBuilder(pctx, ctx)
 
 	// Sparse the filesystem images and calculate their sizes
-	sparseImages := make(map[string]android.OutputPath)
-	sparseImageSizes := make(map[string]android.OutputPath)
+	sparseImages := make(map[string]android.Path)
+	sparseImageSizes := make(map[string]android.Path)
 
 	sparsePartitions := func(partitions []partitionProperties) {
 		for _, part := range partitions {
+			if part.Filesystem == nil {
+				continue
+			}
 			sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder)
 			pName := proptools.String(part.Name)
 			sparseImages[pName] = sparseImg
@@ -185,31 +188,29 @@
 		addPartitionsToGroup(group.Partitions, gName)
 	}
 
-	l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath
-	cmd.FlagWithOutput("--output=", l.output)
+	output := android.PathForModuleOut(ctx, l.installFileName())
+	cmd.FlagWithOutput("--output=", output)
 
 	builder.Build("build_logical_partition", fmt.Sprintf("Creating %s", l.BaseModuleName()))
 
 	l.installDir = android.PathForModuleInstall(ctx, "etc")
-	ctx.InstallFile(l.installDir, l.installFileName(), l.output)
+	ctx.InstallFile(l.installDir, l.installFileName(), output)
 
-	ctx.SetOutputFiles([]android.Path{l.output}, "")
+	ctx.SetOutputFiles([]android.Path{output}, "")
+	l.output = output
 }
 
 // Add a rule that converts the filesystem for the given partition to the given rule builder. The
 // path to the sparse file and the text file having the size of the partition are returned.
-func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (sparseImg android.OutputPath, sizeTxt android.OutputPath) {
-	if p.Filesystem == nil {
-		return
-	}
-	img := android.PathForModuleSrc(ctx, proptools.String(p.Filesystem))
+func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (android.Path, android.Path) {
+	img := android.PathForModuleSrc(ctx, *p.Filesystem)
 	name := proptools.String(p.Name)
-	sparseImg = android.PathForModuleOut(ctx, name+".img").OutputPath
 
+	sparseImg := android.PathForModuleOut(ctx, name+".img")
 	builder.Temporary(sparseImg)
 	builder.Command().BuiltTool("img2simg").Input(img).Output(sparseImg)
 
-	sizeTxt = android.PathForModuleOut(ctx, name+"-size.txt").OutputPath
+	sizeTxt := android.PathForModuleOut(ctx, name+"-size.txt")
 	builder.Temporary(sizeTxt)
 	builder.Command().BuiltTool("sparse_img").Flag("--get_partition_size").Input(sparseImg).
 		Text("| ").Text("tr").FlagWithArg("-d ", "'\n'").
diff --git a/filesystem/raw_binary.go b/filesystem/raw_binary.go
index ad36c29..707fba0 100644
--- a/filesystem/raw_binary.go
+++ b/filesystem/raw_binary.go
@@ -42,7 +42,7 @@
 
 	properties rawBinaryProperties
 
-	output     android.OutputPath
+	output     android.Path
 	installDir android.InstallPath
 }
 
@@ -71,7 +71,7 @@
 
 func (r *rawBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	inputFile := android.PathForModuleSrc(ctx, proptools.String(r.properties.Src))
-	outputFile := android.PathForModuleOut(ctx, r.installFileName()).OutputPath
+	outputFile := android.PathForModuleOut(ctx, r.installFileName())
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        toRawBinary,
@@ -83,11 +83,11 @@
 		},
 	})
 
-	r.output = outputFile
 	r.installDir = android.PathForModuleInstall(ctx, "etc")
-	ctx.InstallFile(r.installDir, r.installFileName(), r.output)
+	ctx.InstallFile(r.installDir, r.installFileName(), outputFile)
 
-	ctx.SetOutputFiles([]android.Path{r.output}, "")
+	ctx.SetOutputFiles([]android.Path{outputFile}, "")
+	r.output = outputFile
 }
 
 var _ android.AndroidMkEntriesProvider = (*rawBinary)(nil)
diff --git a/filesystem/super_image.go b/filesystem/super_image.go
new file mode 100644
index 0000000..0f8f614
--- /dev/null
+++ b/filesystem/super_image.go
@@ -0,0 +1,236 @@
+// 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.
+
+package filesystem
+
+import (
+	"fmt"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	android.RegisterModuleType("super_image", SuperImageFactory)
+}
+
+type superImage struct {
+	android.ModuleBase
+
+	properties     SuperImageProperties
+	partitionProps SuperImagePartitionNameProperties
+
+	installDir android.InstallPath
+}
+
+type SuperImageProperties struct {
+	// the size of the super partition
+	Size *int64
+	// the block device where metadata for dynamic partitions is stored
+	Metadata_device *string
+	// the super partition block device list
+	Block_devices []string
+	// whether A/B updater is used
+	Ab_update *bool
+	// whether dynamic partitions is enabled on devices that were launched without this support
+	Retrofit *bool
+	// whether virtual A/B seamless update is enabled
+	Virtual_ab *bool
+	// whether retrofitting virtual A/B seamless update is enabled
+	Virtual_ab_retrofit *bool
+	// whether the output is a sparse image
+	Sparse *bool
+	// information about how partitions within the super partition are grouped together
+	Partition_groups []PartitionGroupsInfo
+	// whether dynamic partitions is used
+	Use_dynamic_partitions *bool
+}
+
+type PartitionGroupsInfo struct {
+	Name          string
+	GroupSize     string
+	PartitionList []string
+}
+
+type SuperImagePartitionNameProperties struct {
+	// Name of the System partition filesystem module
+	System_partition *string
+	// Name of the System_ext partition filesystem module
+	System_ext_partition *string
+	// Name of the System_dlkm partition filesystem module
+	System_dlkm_partition *string
+	// Name of the System_other partition filesystem module
+	System_other_partition *string
+	// Name of the Product partition filesystem module
+	Product_partition *string
+	// Name of the Vendor partition filesystem module
+	Vendor_partition *string
+	// Name of the Vendor_dlkm partition filesystem module
+	Vendor_dlkm_partition *string
+	// Name of the Odm partition filesystem module
+	Odm_partition *string
+	// Name of the Odm_dlkm partition filesystem module
+	Odm_dlkm_partition *string
+}
+
+func SuperImageFactory() android.Module {
+	module := &superImage{}
+	module.AddProperties(&module.properties, &module.partitionProps)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+type superImageDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var superImageDepTag superImageDepTagType
+
+func (s *superImage) DepsMutator(ctx android.BottomUpMutatorContext) {
+	addDependencyIfDefined := func(dep *string) {
+		if dep != nil {
+			ctx.AddDependency(ctx.Module(), superImageDepTag, proptools.String(dep))
+		}
+	}
+
+	addDependencyIfDefined(s.partitionProps.System_partition)
+	addDependencyIfDefined(s.partitionProps.System_ext_partition)
+	addDependencyIfDefined(s.partitionProps.System_dlkm_partition)
+	addDependencyIfDefined(s.partitionProps.System_other_partition)
+	addDependencyIfDefined(s.partitionProps.Product_partition)
+	addDependencyIfDefined(s.partitionProps.Vendor_partition)
+	addDependencyIfDefined(s.partitionProps.Vendor_dlkm_partition)
+	addDependencyIfDefined(s.partitionProps.Odm_partition)
+	addDependencyIfDefined(s.partitionProps.Odm_dlkm_partition)
+}
+
+func (s *superImage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	miscInfo, deps := s.buildMiscInfo(ctx)
+	builder := android.NewRuleBuilder(pctx, ctx)
+	output := android.PathForModuleOut(ctx, s.installFileName())
+	lpMake := ctx.Config().HostToolPath(ctx, "lpmake")
+	lpMakeDir := filepath.Dir(lpMake.String())
+	deps = append(deps, lpMake)
+	builder.Command().Textf("PATH=%s:\\$PATH", lpMakeDir).
+		BuiltTool("build_super_image").
+		Text("-v").
+		Input(miscInfo).
+		Implicits(deps).
+		Output(output)
+	builder.Build("build_super_image", fmt.Sprintf("Creating super image %s", s.BaseModuleName()))
+	ctx.SetOutputFiles([]android.Path{output}, "")
+}
+
+func (s *superImage) installFileName() string {
+	return s.BaseModuleName() + ".img"
+}
+
+func (s *superImage) buildMiscInfo(ctx android.ModuleContext) (android.Path, android.Paths) {
+	var miscInfoString strings.Builder
+	addStr := func(name string, value string) {
+		miscInfoString.WriteString(name)
+		miscInfoString.WriteRune('=')
+		miscInfoString.WriteString(value)
+		miscInfoString.WriteRune('\n')
+	}
+
+	addStr("use_dynamic_partitions", strconv.FormatBool(proptools.Bool(s.properties.Use_dynamic_partitions)))
+	addStr("dynamic_partition_retrofit", strconv.FormatBool(proptools.Bool(s.properties.Retrofit)))
+	addStr("lpmake", "lpmake")
+	addStr("super_metadata_device", proptools.String(s.properties.Metadata_device))
+	if len(s.properties.Block_devices) > 0 {
+		addStr("super_block_devices", strings.Join(s.properties.Block_devices, " "))
+	}
+	addStr("super_super_device_size", strconv.Itoa(proptools.Int(s.properties.Size)))
+	var groups, partitionList []string
+	for _, groupInfo := range s.properties.Partition_groups {
+		groups = append(groups, groupInfo.Name)
+		partitionList = append(partitionList, groupInfo.PartitionList...)
+		addStr("super_"+groupInfo.Name+"_group_size", groupInfo.GroupSize)
+		addStr("super_"+groupInfo.Name+"_partition_list", strings.Join(groupInfo.PartitionList, " "))
+	}
+	addStr("super_partition_groups", strings.Join(groups, " "))
+	addStr("dynamic_partition_list", strings.Join(partitionList, " "))
+
+	addStr("virtual_ab", strconv.FormatBool(proptools.Bool(s.properties.Virtual_ab)))
+	addStr("virtual_ab_retrofit", strconv.FormatBool(proptools.Bool(s.properties.Virtual_ab_retrofit)))
+	addStr("ab_update", strconv.FormatBool(proptools.Bool(s.properties.Ab_update)))
+	addStr("build_non_sparse_super_partition", strconv.FormatBool(!proptools.Bool(s.properties.Sparse)))
+
+	partitionToImagePath := make(map[string]string)
+	nameToPartition := make(map[string]string)
+	var systemOtherPartitionNameNeeded string
+	addEntryToPartitionToName := func(p string, s *string) {
+		if proptools.String(s) != "" {
+			nameToPartition[*s] = p
+		}
+	}
+
+	// Build partitionToImagePath, because system partition may need system_other
+	// partition image path
+	for _, p := range partitionList {
+		if _, ok := nameToPartition[p]; ok {
+			continue
+		}
+		switch p {
+		case "system":
+			addEntryToPartitionToName(p, s.partitionProps.System_partition)
+			systemOtherPartitionNameNeeded = proptools.String(s.partitionProps.System_other_partition)
+		case "system_dlkm":
+			addEntryToPartitionToName(p, s.partitionProps.System_dlkm_partition)
+		case "system_ext":
+			addEntryToPartitionToName(p, s.partitionProps.System_ext_partition)
+		case "product":
+			addEntryToPartitionToName(p, s.partitionProps.Product_partition)
+		case "vendor":
+			addEntryToPartitionToName(p, s.partitionProps.Vendor_partition)
+		case "vendor_dlkm":
+			addEntryToPartitionToName(p, s.partitionProps.Vendor_dlkm_partition)
+		case "odm":
+			addEntryToPartitionToName(p, s.partitionProps.Odm_partition)
+		case "odm_dlkm":
+			addEntryToPartitionToName(p, s.partitionProps.Odm_dlkm_partition)
+		default:
+			ctx.ModuleErrorf("current partition %s not a super image supported partition", p)
+		}
+	}
+
+	var deps android.Paths
+	ctx.VisitDirectDeps(func(m android.Module) {
+		if p, ok := nameToPartition[m.Name()]; ok {
+			if output, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider); ok {
+				partitionToImagePath[p] = output.DefaultOutputFiles[0].String()
+				deps = append(deps, output.DefaultOutputFiles[0])
+			}
+		} else if systemOtherPartitionNameNeeded != "" && m.Name() == systemOtherPartitionNameNeeded {
+			if output, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider); ok {
+				partitionToImagePath["system_other"] = output.DefaultOutputFiles[0].String()
+				// TODO: add system_other to deps after it can be generated
+				// deps = append(deps, output.DefaultOutputFiles[0])
+			}
+		}
+	})
+
+	for _, p := range android.SortedKeys(partitionToImagePath) {
+		addStr(p+"_image", partitionToImagePath[p])
+	}
+
+	miscInfo := android.PathForModuleOut(ctx, "misc_info.txt")
+	android.WriteFileRule(ctx, miscInfo, miscInfoString.String())
+	return miscInfo, deps
+}
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 63cb627..60a5133 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -17,88 +17,53 @@
 import (
 	"android/soong/android"
 	"android/soong/linkerconfig"
+
+	"strings"
+
+	"github.com/google/blueprint/proptools"
 )
 
 type systemImage struct {
 	filesystem
-
-	properties systemImageProperties
 }
 
-type systemImageProperties struct {
-	// Path to the input linker config json file.
-	Linker_config_src *string `android:"path"`
-}
+var _ filesystemBuilder = (*systemImage)(nil)
 
 // android_system_image is a specialization of android_filesystem for the 'system' partition.
 // Currently, the only difference is the inclusion of linker.config.pb file which specifies
 // the provided and the required libraries to and from APEXes.
-func systemImageFactory() android.Module {
+func SystemImageFactory() android.Module {
 	module := &systemImage{}
-	module.AddProperties(&module.properties)
-	module.filesystem.buildExtraFiles = module.buildExtraFiles
-	module.filesystem.filterPackagingSpec = module.filterPackagingSpec
+	module.filesystemBuilder = module
 	initFilesystemModule(module, &module.filesystem)
 	return module
 }
 
-func (s *systemImage) buildExtraFiles(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths {
-	if s.filesystem.properties.Partition_type != nil {
-		ctx.PropertyErrorf("partition_type", "partition_type must be unset on an android_system_image module. It is assumed to be 'system'.")
-	}
-	lc := s.buildLinkerConfigFile(ctx, root)
-	// Add more files if needed
-	return []android.OutputPath{lc}
+func (s systemImage) FsProps() FilesystemProperties {
+	return s.filesystem.properties
 }
 
-func (s *systemImage) buildLinkerConfigFile(ctx android.ModuleContext, root android.OutputPath) android.OutputPath {
-	input := android.PathForModuleSrc(ctx, android.String(s.properties.Linker_config_src))
-	output := root.Join(ctx, "system", "etc", "linker.config.pb")
-
-	// we need "Module"s for packaging items
-	modulesInPackageByModule := make(map[android.Module]bool)
-	modulesInPackageByName := make(map[string]bool)
-
-	deps := s.gatherFilteredPackagingSpecs(ctx)
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		for _, ps := range android.OtherModuleProviderOrDefault(
-			ctx, child, android.InstallFilesProvider).PackagingSpecs {
-			if _, ok := deps[ps.RelPathInPackage()]; ok {
-				modulesInPackageByModule[child] = true
-				modulesInPackageByName[child.Name()] = true
-				return true
-			}
-		}
-		return true
-	})
-
-	provideModules := make([]android.Module, 0, len(modulesInPackageByModule))
-	for mod := range modulesInPackageByModule {
-		provideModules = append(provideModules, mod)
+func (s *systemImage) BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
+	if !proptools.Bool(s.filesystem.properties.Linker_config.Gen_linker_config) {
+		return
 	}
 
-	var requireModules []android.Module
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		_, parentInPackage := modulesInPackageByModule[parent]
-		_, childInPackageName := modulesInPackageByName[child.Name()]
+	provideModules, requireModules := s.getLibsForLinkerConfig(ctx)
+	output := rebasedDir.Join(ctx, "etc", "linker.config.pb")
+	linkerconfig.BuildLinkerConfig(ctx, builder, android.PathsForModuleSrc(ctx, s.filesystem.properties.Linker_config.Linker_config_srcs), provideModules, requireModules, output)
 
-		// When parent is in the package, and child (or its variant) is not, this can be from an interface.
-		if parentInPackage && !childInPackageName {
-			requireModules = append(requireModules, child)
-		}
-		return true
-	})
-
-	builder := android.NewRuleBuilder(pctx, ctx)
-	linkerconfig.BuildLinkerConfig(ctx, builder, input, provideModules, requireModules, output)
-	builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String())
-	return output
+	s.appendToEntry(ctx, output)
 }
 
 // Filter the result of GatherPackagingSpecs to discard items targeting outside "system" / "root"
 // partition.  Note that "apex" module installs its contents to "apex"(fake partition) as well
 // for symbol lookup by imitating "activated" paths.
-func (s *systemImage) filterPackagingSpec(ps android.PackagingSpec) bool {
-	return s.filesystem.filterInstallablePackagingSpec(ps) &&
-		(ps.Partition() == "system" || ps.Partition() == "root")
+func (s *systemImage) FilterPackagingSpec(ps android.PackagingSpec) bool {
+	return !ps.SkipInstall() &&
+		(ps.Partition() == "system" || ps.Partition() == "root" ||
+			strings.HasPrefix(ps.Partition(), "system/"))
+}
+
+func (s *systemImage) ShouldUseVintfFragmentModuleOnly() bool {
+	return true
 }
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 1d64796..6a47859 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -25,19 +25,30 @@
 )
 
 func init() {
-	android.RegisterModuleType("vbmeta", vbmetaFactory)
+	android.RegisterModuleType("vbmeta", VbmetaFactory)
+	pctx.HostBinToolVariable("avbtool", "avbtool")
 }
 
+var (
+	extractPublicKeyRule = pctx.AndroidStaticRule("avb_extract_public_key",
+		blueprint.RuleParams{
+			Command: `${avbtool} extract_public_key --key $in --output $out`,
+			CommandDeps: []string{
+				"${avbtool}",
+			},
+		})
+)
+
 type vbmeta struct {
 	android.ModuleBase
 
-	properties vbmetaProperties
+	properties VbmetaProperties
 
-	output     android.OutputPath
+	output     android.Path
 	installDir android.InstallPath
 }
 
-type vbmetaProperties struct {
+type VbmetaProperties struct {
 	// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
 	Partition_name *string
 
@@ -50,9 +61,8 @@
 	// Algorithm that avbtool will use to sign this vbmeta image. Default is SHA256_RSA4096.
 	Algorithm *string
 
-	// File whose content will provide the rollback index. If unspecified, the rollback index
-	// is from PLATFORM_SECURITY_PATCH
-	Rollback_index_file *string `android:"path"`
+	// The rollback index. If unspecified, the rollback index is from PLATFORM_SECURITY_PATCH
+	Rollback_index *int64
 
 	// Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0.
 	Rollback_index_location *int64
@@ -61,8 +71,15 @@
 	// have to be signed (use_avb: true).
 	Partitions proptools.Configurable[[]string]
 
-	// List of chained partitions that this vbmeta deletages the verification.
-	Chained_partitions []chainedPartitionProperties
+	// Metadata about the chained partitions that this vbmeta delegates the verification.
+	// This is an alternative to chained_partitions, using chained_partitions instead is simpler
+	// in most cases. However, this property allows building this vbmeta partition without
+	// its chained partitions existing in this build.
+	Chained_partition_metadata []ChainedPartitionProperties
+
+	// List of chained partitions that this vbmeta delegates the verification. They are the
+	// names of other vbmeta modules.
+	Chained_partitions []string
 
 	// List of key-value pair of avb properties
 	Avb_properties []avbProperty
@@ -76,7 +93,7 @@
 	Value *string
 }
 
-type chainedPartitionProperties struct {
+type ChainedPartitionProperties struct {
 	// Name of the chained partition
 	Name *string
 
@@ -94,23 +111,42 @@
 	Private_key *string `android:"path"`
 }
 
+type vbmetaPartitionInfo struct {
+	// Name of the partition
+	Name string
+
+	// Rollback index location, non-negative int
+	RollbackIndexLocation int
+
+	// The path to the public key of the private key used to sign this partition. Derived from
+	// the private key.
+	PublicKey android.Path
+}
+
+var vbmetaPartitionProvider = blueprint.NewProvider[vbmetaPartitionInfo]()
+
 // vbmeta is the partition image that has the verification information for other partitions.
-func vbmetaFactory() android.Module {
+func VbmetaFactory() android.Module {
 	module := &vbmeta{}
 	module.AddProperties(&module.properties)
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	return module
 }
 
 type vbmetaDep struct {
 	blueprint.BaseDependencyTag
-	kind string
 }
 
-var vbmetaPartitionDep = vbmetaDep{kind: "partition"}
+type chainedPartitionDep struct {
+	blueprint.BaseDependencyTag
+}
+
+var vbmetaPartitionDep = vbmetaDep{}
+var vbmetaChainedPartitionDep = chainedPartitionDep{}
 
 func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions.GetOrDefault(ctx, nil)...)
+	ctx.AddDependency(ctx.Module(), vbmetaChainedPartitionDep, v.properties.Chained_partitions...)
 }
 
 func (v *vbmeta) installFileName() string {
@@ -125,10 +161,6 @@
 const vbmetaMaxSize = 64 * 1024
 
 func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	extractedPublicKeys := v.extractPublicKeys(ctx)
-
-	v.output = android.PathForModuleOut(ctx, v.installFileName()).OutputPath
-
 	builder := android.NewRuleBuilder(pctx, ctx)
 	cmd := builder.Command().BuiltTool("avbtool").Text("make_vbmeta_image")
 
@@ -176,97 +208,114 @@
 		cmd.FlagWithInput("--include_descriptors_from_image ", signedImage)
 	}
 
-	for i, cp := range v.properties.Chained_partitions {
-		name := proptools.String(cp.Name)
+	seenRils := make(map[int]bool)
+	for _, cp := range ctx.GetDirectDepsWithTag(vbmetaChainedPartitionDep) {
+		info, ok := android.OtherModuleProvider(ctx, cp, vbmetaPartitionProvider)
+		if !ok {
+			ctx.PropertyErrorf("chained_partitions", "Expected all modules in chained_partitions to provide vbmetaPartitionProvider, but %s did not", cp.Name())
+			continue
+		}
+		if info.Name == "" {
+			ctx.PropertyErrorf("chained_partitions", "name must be specified")
+			continue
+		}
+
+		ril := info.RollbackIndexLocation
+		if ril < 0 {
+			ctx.PropertyErrorf("chained_partitions", "rollback index location must be 0, 1, 2, ...")
+			continue
+		} else if seenRils[ril] {
+			ctx.PropertyErrorf("chained_partitions", "Multiple chained partitions with the same rollback index location %d", ril)
+			continue
+		}
+		seenRils[ril] = true
+
+		publicKey := info.PublicKey
+		cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", info.Name, ril, publicKey.String()))
+		cmd.Implicit(publicKey)
+	}
+	for _, cpm := range v.properties.Chained_partition_metadata {
+		name := proptools.String(cpm.Name)
 		if name == "" {
 			ctx.PropertyErrorf("chained_partitions", "name must be specified")
 			continue
 		}
 
-		ril := proptools.IntDefault(cp.Rollback_index_location, i+1)
+		ril := proptools.IntDefault(cpm.Rollback_index_location, -1)
 		if ril < 0 {
-			ctx.PropertyErrorf("chained_partitions", "must be 0, 1, 2, ...")
+			ctx.PropertyErrorf("chained_partition_metadata", "rollback index location must be 0, 1, 2, ...")
+			continue
+		} else if seenRils[ril] {
+			ctx.PropertyErrorf("chained_partition_metadata", "Multiple chained partitions with the same rollback index location %d", ril)
+			continue
+		}
+		seenRils[ril] = true
+
+		var publicKey android.Path
+		if cpm.Public_key != nil {
+			publicKey = android.PathForModuleSrc(ctx, *cpm.Public_key)
+		} else if cpm.Private_key != nil {
+			privateKey := android.PathForModuleSrc(ctx, *cpm.Private_key)
+			extractedPublicKey := android.PathForModuleOut(ctx, "chained_metadata", name+".avbpubkey")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   extractPublicKeyRule,
+				Input:  privateKey,
+				Output: extractedPublicKey,
+			})
+			publicKey = extractedPublicKey
+		} else {
+			ctx.PropertyErrorf("public_key", "Either public_key or private_key must be specified")
 			continue
 		}
 
-		var publicKey android.Path
-		if cp.Public_key != nil {
-			publicKey = android.PathForModuleSrc(ctx, proptools.String(cp.Public_key))
-		} else {
-			publicKey = extractedPublicKeys[name]
-		}
 		cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", name, ril, publicKey.String()))
 		cmd.Implicit(publicKey)
 	}
 
-	cmd.FlagWithOutput("--output ", v.output)
+	output := android.PathForModuleOut(ctx, v.installFileName())
+	cmd.FlagWithOutput("--output ", output)
 
 	// libavb expects to be able to read the maximum vbmeta size, so we must provide a partition
 	// which matches this or the read will fail.
 	builder.Command().Text("truncate").
 		FlagWithArg("-s ", strconv.Itoa(vbmetaMaxSize)).
-		Output(v.output)
+		Output(output)
 
 	builder.Build("vbmeta", fmt.Sprintf("vbmeta %s", ctx.ModuleName()))
 
 	v.installDir = android.PathForModuleInstall(ctx, "etc")
-	ctx.InstallFile(v.installDir, v.installFileName(), v.output)
+	ctx.InstallFile(v.installDir, v.installFileName(), output)
 
-	ctx.SetOutputFiles([]android.Path{v.output}, "")
-	android.SetProvider(ctx, android.AndroidMkInfoProvider, v.prepareAndroidMKProviderInfo())
+	extractedPublicKey := android.PathForModuleOut(ctx, v.partitionName()+".avbpubkey")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   extractPublicKeyRule,
+		Input:  key,
+		Output: extractedPublicKey,
+	})
+
+	android.SetProvider(ctx, vbmetaPartitionProvider, vbmetaPartitionInfo{
+		Name:                  v.partitionName(),
+		RollbackIndexLocation: ril,
+		PublicKey:             extractedPublicKey,
+	})
+
+	ctx.SetOutputFiles([]android.Path{output}, "")
+	v.output = output
 }
 
 // Returns the embedded shell command that prints the rollback index
 func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string {
-	var cmd string
-	if v.properties.Rollback_index_file != nil {
-		f := android.PathForModuleSrc(ctx, proptools.String(v.properties.Rollback_index_file))
-		cmd = "cat " + f.String()
+	if v.properties.Rollback_index != nil {
+		return fmt.Sprintf("%d", *v.properties.Rollback_index)
 	} else {
-		cmd = "date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s"
+		// Take the first line and remove the newline char
+		return "$(date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s | head -1 | tr -d '\n'" + ")"
 	}
-	// Take the first line and remove the newline char
-	return "$(" + cmd + " | head -1 | tr -d '\n'" + ")"
 }
 
-// Extract public keys from chained_partitions.private_key. The keys are indexed with the partition
-// name.
-func (v *vbmeta) extractPublicKeys(ctx android.ModuleContext) map[string]android.OutputPath {
-	result := make(map[string]android.OutputPath)
+var _ android.AndroidMkProviderInfoProducer = (*vbmeta)(nil)
 
-	builder := android.NewRuleBuilder(pctx, ctx)
-	for _, cp := range v.properties.Chained_partitions {
-		if cp.Private_key == nil {
-			continue
-		}
-
-		name := proptools.String(cp.Name)
-		if name == "" {
-			ctx.PropertyErrorf("chained_partitions", "name must be specified")
-			continue
-		}
-
-		if _, ok := result[name]; ok {
-			ctx.PropertyErrorf("chained_partitions", "name %q is duplicated", name)
-			continue
-		}
-
-		privateKeyFile := android.PathForModuleSrc(ctx, proptools.String(cp.Private_key))
-		publicKeyFile := android.PathForModuleOut(ctx, name+".avbpubkey").OutputPath
-
-		builder.Command().
-			BuiltTool("avbtool").
-			Text("extract_public_key").
-			FlagWithInput("--key ", privateKeyFile).
-			FlagWithOutput("--output ", publicKeyFile)
-
-		result[name] = publicKeyFile
-	}
-	builder.Build("vbmeta_extract_public_key", fmt.Sprintf("Extract public keys for %s", ctx.ModuleName()))
-	return result
-}
-
-func (v *vbmeta) prepareAndroidMKProviderInfo() *android.AndroidMkProviderInfo {
+func (v *vbmeta) PrepareAndroidMKProviderInfo(config android.Config) *android.AndroidMkProviderInfo {
 	providerData := android.AndroidMkProviderInfo{
 		PrimaryInfo: android.AndroidMkInfo{
 			Class:      "ETC",
diff --git a/fsgen/Android.bp b/fsgen/Android.bp
new file mode 100644
index 0000000..365d954
--- /dev/null
+++ b/fsgen/Android.bp
@@ -0,0 +1,33 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-fsgen",
+    pkgPath: "android/soong/fsgen",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-filesystem",
+        "soong-kernel",
+    ],
+    srcs: [
+        "boot_imgs.go",
+        "filesystem_creator.go",
+        "fsgen_mutators.go",
+        "prebuilt_etc_modules_gen.go",
+        "super_img.go",
+        "util.go",
+        "vbmeta_partitions.go",
+    ],
+    testSrcs: [
+        "filesystem_creator_test.go",
+    ],
+    pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
+}
+
+soong_filesystem_creator {
+    name: "soong_filesystem_creator",
+}
diff --git a/fsgen/boot_imgs.go b/fsgen/boot_imgs.go
new file mode 100644
index 0000000..4e80720
--- /dev/null
+++ b/fsgen/boot_imgs.go
@@ -0,0 +1,317 @@
+package fsgen
+
+import (
+	"android/soong/android"
+	"android/soong/filesystem"
+	"fmt"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+func createBootImage(ctx android.LoadHookContext, dtbImg dtbImg) bool {
+	partitionVariables := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+
+	if partitionVariables.TargetKernelPath == "" {
+		// There are potentially code paths that don't set TARGET_KERNEL_PATH
+		return false
+	}
+
+	kernelDir := filepath.Dir(partitionVariables.TargetKernelPath)
+	kernelBase := filepath.Base(partitionVariables.TargetKernelPath)
+	kernelFilegroupName := generatedModuleName(ctx.Config(), "kernel")
+
+	ctx.CreateModuleInDirectory(
+		android.FileGroupFactory,
+		kernelDir,
+		&struct {
+			Name       *string
+			Srcs       []string
+			Visibility []string
+		}{
+			Name:       proptools.StringPtr(kernelFilegroupName),
+			Srcs:       []string{kernelBase},
+			Visibility: []string{"//visibility:public"},
+		},
+	)
+
+	var partitionSize *int64
+	if partitionVariables.BoardBootimagePartitionSize != "" {
+		// Base of zero will allow base 10 or base 16 if starting with 0x
+		parsed, err := strconv.ParseInt(partitionVariables.BoardBootimagePartitionSize, 0, 64)
+		if err != nil {
+			panic(fmt.Sprintf("BOARD_BOOTIMAGE_PARTITION_SIZE must be an int, got %s", partitionVariables.BoardBootimagePartitionSize))
+		}
+		partitionSize = &parsed
+	}
+
+	var securityPatch *string
+	if partitionVariables.BootSecurityPatch != "" {
+		securityPatch = &partitionVariables.BootSecurityPatch
+	}
+
+	avbInfo := getAvbInfo(ctx.Config(), "boot")
+
+	bootImageName := generatedModuleNameForPartition(ctx.Config(), "boot")
+
+	var dtbPrebuilt *string
+	if dtbImg.include && dtbImg.imgType == "boot" {
+		dtbPrebuilt = proptools.StringPtr(":" + dtbImg.name)
+	}
+
+	var cmdline []string
+	if !buildingVendorBootImage(partitionVariables) {
+		cmdline = partitionVariables.InternalKernelCmdline
+	}
+
+	ctx.CreateModule(
+		filesystem.BootimgFactory,
+		&filesystem.BootimgProperties{
+			Kernel_prebuilt:    proptools.StringPtr(":" + kernelFilegroupName),
+			Header_version:     proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
+			Partition_size:     partitionSize,
+			Use_avb:            avbInfo.avbEnable,
+			Avb_mode:           avbInfo.avbMode,
+			Avb_private_key:    avbInfo.avbkeyFilegroup,
+			Avb_rollback_index: avbInfo.avbRollbackIndex,
+			Avb_algorithm:      avbInfo.avbAlgorithm,
+			Security_patch:     securityPatch,
+			Dtb_prebuilt:       dtbPrebuilt,
+			Cmdline:            cmdline,
+		},
+		&struct {
+			Name *string
+		}{
+			Name: proptools.StringPtr(bootImageName),
+		},
+	)
+	return true
+}
+
+func createVendorBootImage(ctx android.LoadHookContext, dtbImg dtbImg) bool {
+	partitionVariables := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+
+	bootImageName := generatedModuleNameForPartition(ctx.Config(), "vendor_boot")
+
+	avbInfo := getAvbInfo(ctx.Config(), "vendor_boot")
+
+	var dtbPrebuilt *string
+	if dtbImg.include && dtbImg.imgType == "vendor_boot" {
+		dtbPrebuilt = proptools.StringPtr(":" + dtbImg.name)
+	}
+
+	cmdline := partitionVariables.InternalKernelCmdline
+
+	var vendorBootConfigImg *string
+	if name, ok := createVendorBootConfigImg(ctx); ok {
+		vendorBootConfigImg = proptools.StringPtr(":" + name)
+	}
+
+	ctx.CreateModule(
+		filesystem.BootimgFactory,
+		&filesystem.BootimgProperties{
+			Boot_image_type:    proptools.StringPtr("vendor_boot"),
+			Ramdisk_module:     proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_ramdisk")),
+			Header_version:     proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
+			Use_avb:            avbInfo.avbEnable,
+			Avb_mode:           avbInfo.avbMode,
+			Avb_private_key:    avbInfo.avbkeyFilegroup,
+			Avb_rollback_index: avbInfo.avbRollbackIndex,
+			Avb_algorithm:      avbInfo.avbAlgorithm,
+			Dtb_prebuilt:       dtbPrebuilt,
+			Cmdline:            cmdline,
+			Bootconfig:         vendorBootConfigImg,
+		},
+		&struct {
+			Name *string
+		}{
+			Name: proptools.StringPtr(bootImageName),
+		},
+	)
+	return true
+}
+
+func createInitBootImage(ctx android.LoadHookContext) bool {
+	partitionVariables := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+
+	bootImageName := generatedModuleNameForPartition(ctx.Config(), "init_boot")
+
+	var securityPatch *string
+	if partitionVariables.InitBootSecurityPatch != "" {
+		securityPatch = &partitionVariables.InitBootSecurityPatch
+	} else if partitionVariables.BootSecurityPatch != "" {
+		securityPatch = &partitionVariables.BootSecurityPatch
+	}
+
+	var partitionSize *int64
+	if partitionVariables.BoardInitBootimagePartitionSize != "" {
+		// Base of zero will allow base 10 or base 16 if starting with 0x
+		parsed, err := strconv.ParseInt(partitionVariables.BoardInitBootimagePartitionSize, 0, 64)
+		if err != nil {
+			panic(fmt.Sprintf("BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE must be an int, got %s", partitionVariables.BoardInitBootimagePartitionSize))
+		}
+		partitionSize = &parsed
+	}
+
+	avbInfo := getAvbInfo(ctx.Config(), "init_boot")
+
+	ctx.CreateModule(
+		filesystem.BootimgFactory,
+		&filesystem.BootimgProperties{
+			Boot_image_type:    proptools.StringPtr("init_boot"),
+			Ramdisk_module:     proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "ramdisk")),
+			Header_version:     proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
+			Security_patch:     securityPatch,
+			Partition_size:     partitionSize,
+			Use_avb:            avbInfo.avbEnable,
+			Avb_mode:           avbInfo.avbMode,
+			Avb_private_key:    avbInfo.avbkeyFilegroup,
+			Avb_rollback_index: avbInfo.avbRollbackIndex,
+			Avb_algorithm:      avbInfo.avbAlgorithm,
+		},
+		&struct {
+			Name *string
+		}{
+			Name: proptools.StringPtr(bootImageName),
+		},
+	)
+	return true
+}
+
+// Returns the equivalent of the BUILDING_BOOT_IMAGE variable in make. Derived from this logic:
+// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/board_config.mk;l=458;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
+func buildingBootImage(partitionVars android.PartitionVariables) bool {
+	if partitionVars.BoardUsesRecoveryAsBoot {
+		return false
+	}
+
+	if partitionVars.ProductBuildBootImage {
+		return true
+	}
+
+	if len(partitionVars.BoardPrebuiltBootimage) > 0 {
+		return false
+	}
+
+	if len(partitionVars.BoardBootimagePartitionSize) > 0 {
+		return true
+	}
+
+	// TODO: return true if BOARD_KERNEL_BINARIES is set and has a *_BOOTIMAGE_PARTITION_SIZE
+	// variable. However, I don't think BOARD_KERNEL_BINARIES is ever set in practice.
+
+	return false
+}
+
+// Returns the equivalent of the BUILDING_VENDOR_BOOT_IMAGE variable in make. Derived from this logic:
+// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/board_config.mk;l=518;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
+func buildingVendorBootImage(partitionVars android.PartitionVariables) bool {
+	if v, exists := boardBootHeaderVersion(partitionVars); exists && v >= 3 {
+		x := partitionVars.ProductBuildVendorBootImage
+		if x == "" || x == "true" {
+			return true
+		}
+	}
+
+	return false
+}
+
+// Derived from: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/board_config.mk;l=480;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
+func buildingInitBootImage(partitionVars android.PartitionVariables) bool {
+	if !partitionVars.ProductBuildInitBootImage {
+		if partitionVars.BoardUsesRecoveryAsBoot || len(partitionVars.BoardPrebuiltInitBootimage) > 0 {
+			return false
+		} else if len(partitionVars.BoardInitBootimagePartitionSize) > 0 {
+			return true
+		}
+	} else {
+		if partitionVars.BoardUsesRecoveryAsBoot {
+			panic("PRODUCT_BUILD_INIT_BOOT_IMAGE is true, but so is BOARD_USES_RECOVERY_AS_BOOT. Use only one option.")
+		}
+		return true
+	}
+	return false
+}
+
+func boardBootHeaderVersion(partitionVars android.PartitionVariables) (int, bool) {
+	if len(partitionVars.BoardBootHeaderVersion) == 0 {
+		return 0, false
+	}
+	v, err := strconv.ParseInt(partitionVars.BoardBootHeaderVersion, 10, 32)
+	if err != nil {
+		panic(fmt.Sprintf("BOARD_BOOT_HEADER_VERSION must be an int, got: %q", partitionVars.BoardBootHeaderVersion))
+	}
+	return int(v), true
+}
+
+type dtbImg struct {
+	// whether to include the dtb image in boot image
+	include bool
+
+	// name of the generated dtb image filegroup name
+	name string
+
+	// type of the boot image that the dtb image argument should be specified
+	imgType string
+}
+
+func createDtbImgFilegroup(ctx android.LoadHookContext) dtbImg {
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	if !partitionVars.BoardIncludeDtbInBootimg {
+		return dtbImg{include: false}
+	}
+	for _, copyFilePair := range partitionVars.ProductCopyFiles {
+		srcDestList := strings.Split(copyFilePair, ":")
+		if len(srcDestList) < 2 {
+			ctx.ModuleErrorf("PRODUCT_COPY_FILES must follow the format \"src:dest\", got: %s", copyFilePair)
+		}
+		if srcDestList[1] == "dtb.img" {
+			moduleName := generatedModuleName(ctx.Config(), "dtb_img_filegroup")
+			ctx.CreateModuleInDirectory(
+				android.FileGroupFactory,
+				filepath.Dir(srcDestList[0]),
+				&struct {
+					Name *string
+					Srcs []string
+				}{
+					Name: proptools.StringPtr(moduleName),
+					Srcs: []string{filepath.Base(srcDestList[1])},
+				},
+			)
+			imgType := "vendor_boot"
+			if !buildingVendorBootImage(partitionVars) {
+				imgType = "boot"
+			}
+			return dtbImg{include: true, name: moduleName, imgType: imgType}
+		}
+	}
+	return dtbImg{include: false}
+}
+
+func createVendorBootConfigImg(ctx android.LoadHookContext) (string, bool) {
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	bootconfig := partitionVars.InternalBootconfig
+	bootconfigFile := partitionVars.InternalBootconfigFile
+	if len(bootconfig) == 0 && len(bootconfigFile) == 0 {
+		return "", false
+	}
+
+	vendorBootconfigImgModuleName := generatedModuleName(ctx.Config(), "vendor_bootconfig_image")
+
+	ctx.CreateModule(
+		filesystem.BootconfigModuleFactory,
+		&struct {
+			Name             *string
+			Boot_config      []string
+			Boot_config_file *string
+		}{
+			Name:             proptools.StringPtr(vendorBootconfigImgModuleName),
+			Boot_config:      bootconfig,
+			Boot_config_file: proptools.StringPtr(bootconfigFile),
+		},
+	)
+
+	return vendorBootconfigImgModuleName, true
+}
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
new file mode 100644
index 0000000..1378513
--- /dev/null
+++ b/fsgen/filesystem_creator.go
@@ -0,0 +1,1061 @@
+// 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.
+
+package fsgen
+
+import (
+	"crypto/sha256"
+	"fmt"
+	"path/filepath"
+	"slices"
+	"strconv"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/filesystem"
+	"android/soong/kernel"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/parser"
+	"github.com/google/blueprint/proptools"
+)
+
+var pctx = android.NewPackageContext("android/soong/fsgen")
+
+func init() {
+	registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("soong_filesystem_creator", filesystemCreatorFactory)
+	ctx.PreDepsMutators(RegisterCollectFileSystemDepsMutators)
+}
+
+type filesystemCreatorProps struct {
+	Generated_partition_types   []string `blueprint:"mutated"`
+	Unsupported_partition_types []string `blueprint:"mutated"`
+
+	Vbmeta_module_names    []string `blueprint:"mutated"`
+	Vbmeta_partition_names []string `blueprint:"mutated"`
+
+	Boot_image        string `blueprint:"mutated" android:"path_device_first"`
+	Vendor_boot_image string `blueprint:"mutated" android:"path_device_first"`
+	Init_boot_image   string `blueprint:"mutated" android:"path_device_first"`
+	Super_image       string `blueprint:"mutated" android:"path_device_first"`
+}
+
+type filesystemCreator struct {
+	android.ModuleBase
+
+	properties filesystemCreatorProps
+}
+
+func filesystemCreatorFactory() android.Module {
+	module := &filesystemCreator{}
+
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	module.AddProperties(&module.properties)
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		generatedPrebuiltEtcModuleNames := createPrebuiltEtcModules(ctx)
+		avbpubkeyGenerated := createAvbpubkeyModule(ctx)
+		createFsGenState(ctx, generatedPrebuiltEtcModuleNames, avbpubkeyGenerated)
+		module.createAvbKeyFilegroups(ctx)
+		module.createMiscFilegroups(ctx)
+		module.createInternalModules(ctx)
+	})
+
+	return module
+}
+
+func generatedPartitions(ctx android.LoadHookContext) []string {
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	generatedPartitions := []string{"system"}
+	if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
+		generatedPartitions = append(generatedPartitions, "system_ext")
+	}
+	if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
+		generatedPartitions = append(generatedPartitions, "vendor")
+	}
+	if ctx.DeviceConfig().BuildingProductImage() && ctx.DeviceConfig().ProductPath() == "product" {
+		generatedPartitions = append(generatedPartitions, "product")
+	}
+	if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
+		generatedPartitions = append(generatedPartitions, "odm")
+	}
+	if ctx.DeviceConfig().BuildingUserdataImage() && ctx.DeviceConfig().UserdataPath() == "data" {
+		generatedPartitions = append(generatedPartitions, "userdata")
+	}
+	if partitionVars.BuildingSystemDlkmImage {
+		generatedPartitions = append(generatedPartitions, "system_dlkm")
+	}
+	if partitionVars.BuildingVendorDlkmImage {
+		generatedPartitions = append(generatedPartitions, "vendor_dlkm")
+	}
+	if partitionVars.BuildingOdmDlkmImage {
+		generatedPartitions = append(generatedPartitions, "odm_dlkm")
+	}
+	if partitionVars.BuildingRamdiskImage {
+		generatedPartitions = append(generatedPartitions, "ramdisk")
+	}
+	if buildingVendorBootImage(partitionVars) {
+		generatedPartitions = append(generatedPartitions, "vendor_ramdisk")
+	}
+	if ctx.DeviceConfig().BuildingRecoveryImage() && ctx.DeviceConfig().RecoveryPath() == "recovery" {
+		generatedPartitions = append(generatedPartitions, "recovery")
+	}
+	return generatedPartitions
+}
+
+func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
+	soongGeneratedPartitions := generatedPartitions(ctx)
+	finalSoongGeneratedPartitions := make([]string, 0, len(soongGeneratedPartitions))
+	for _, partitionType := range soongGeneratedPartitions {
+		if f.createPartition(ctx, partitionType) {
+			f.properties.Generated_partition_types = append(f.properties.Generated_partition_types, partitionType)
+			finalSoongGeneratedPartitions = append(finalSoongGeneratedPartitions, partitionType)
+		} else {
+			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType)
+		}
+	}
+
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	dtbImg := createDtbImgFilegroup(ctx)
+
+	if buildingBootImage(partitionVars) {
+		if createBootImage(ctx, dtbImg) {
+			f.properties.Boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "boot")
+		} else {
+			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "boot")
+		}
+	}
+	if buildingVendorBootImage(partitionVars) {
+		if createVendorBootImage(ctx, dtbImg) {
+			f.properties.Vendor_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "vendor_boot")
+		} else {
+			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_boot")
+		}
+	}
+	if buildingInitBootImage(partitionVars) {
+		if createInitBootImage(ctx) {
+			f.properties.Init_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "init_boot")
+		} else {
+			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "init_boot")
+		}
+	}
+
+	for _, x := range createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) {
+		f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName)
+		f.properties.Vbmeta_partition_names = append(f.properties.Vbmeta_partition_names, x.partitionName)
+	}
+
+	if buildingSuperImage(partitionVars) {
+		createSuperImage(ctx, finalSoongGeneratedPartitions, partitionVars)
+		f.properties.Super_image = ":" + generatedModuleName(ctx.Config(), "super")
+	}
+
+	ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = finalSoongGeneratedPartitions
+	f.createDeviceModule(ctx, finalSoongGeneratedPartitions, f.properties.Vbmeta_module_names)
+}
+
+func generatedModuleName(cfg android.Config, suffix string) string {
+	prefix := "soong"
+	if cfg.HasDeviceProduct() {
+		prefix = cfg.DeviceProduct()
+	}
+	return fmt.Sprintf("%s_generated_%s", prefix, suffix)
+}
+
+func generatedModuleNameForPartition(cfg android.Config, partitionType string) string {
+	return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
+}
+
+func (f *filesystemCreator) createDeviceModule(
+	ctx android.LoadHookContext,
+	generatedPartitionTypes []string,
+	vbmetaPartitions []string,
+) {
+	baseProps := &struct {
+		Name *string
+	}{
+		Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "device")),
+	}
+
+	// Currently, only the system and system_ext partition module is created.
+	partitionProps := &filesystem.PartitionNameProperties{}
+	if android.InList("system", generatedPartitionTypes) {
+		partitionProps.System_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
+	}
+	if android.InList("system_ext", generatedPartitionTypes) {
+		partitionProps.System_ext_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
+	}
+	if android.InList("vendor", generatedPartitionTypes) {
+		partitionProps.Vendor_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
+	}
+	if android.InList("product", generatedPartitionTypes) {
+		partitionProps.Product_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
+	}
+	if android.InList("odm", generatedPartitionTypes) {
+		partitionProps.Odm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
+	}
+	if android.InList("userdata", f.properties.Generated_partition_types) {
+		partitionProps.Userdata_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "userdata"))
+	}
+	partitionProps.Vbmeta_partitions = vbmetaPartitions
+
+	ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps)
+}
+
+func partitionSpecificFsProps(ctx android.EarlyModuleContext, fsProps *filesystem.FilesystemProperties, partitionVars android.PartitionVariables, partitionType string) {
+	switch partitionType {
+	case "system":
+		fsProps.Build_logtags = proptools.BoolPtr(true)
+		// https://source.corp.google.com/h/googleplex-android/platform/build//639d79f5012a6542ab1f733b0697db45761ab0f3:core/packaging/flags.mk;l=21;drc=5ba8a8b77507f93aa48cc61c5ba3f31a4d0cbf37;bpv=1;bpt=0
+		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
+		// Identical to that of the aosp_shared_system_image
+		if partitionVars.ProductFsverityGenerateMetadata {
+			fsProps.Fsverity.Inputs = []string{
+				"etc/boot-image.prof",
+				"etc/dirty-image-objects",
+				"etc/preloaded-classes",
+				"etc/classpaths/*.pb",
+				"framework/*",
+				"framework/*/*",     // framework/{arch}
+				"framework/oat/*/*", // framework/oat/{arch}
+			}
+			fsProps.Fsverity.Libs = []string{":framework-res{.export-package.apk}"}
+		}
+		// Most of the symlinks and directories listed here originate from create_root_structure.mk,
+		// but the handwritten generic system image also recreates them:
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/target/product/generic/Android.bp;l=33;drc=db08311f1b6ef6cb0a4fbcc6263b89849360ce04
+		// TODO(b/377734331): only generate the symlinks if the relevant partitions exist
+		fsProps.Symlinks = []filesystem.SymlinkDefinition{
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/system/bin/init"),
+				Name:   proptools.StringPtr("init"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/system/etc"),
+				Name:   proptools.StringPtr("etc"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/system/bin"),
+				Name:   proptools.StringPtr("bin"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/data/user_de/0/com.android.shell/files/bugreports"),
+				Name:   proptools.StringPtr("bugreports"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/sys/kernel/debug"),
+				Name:   proptools.StringPtr("d"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/storage/self/primary"),
+				Name:   proptools.StringPtr("sdcard"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/product/etc/security/adb_keys"),
+				Name:   proptools.StringPtr("adb_keys"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/odm/app"),
+				Name:   proptools.StringPtr("odm/app"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/odm/bin"),
+				Name:   proptools.StringPtr("odm/bin"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/odm/etc"),
+				Name:   proptools.StringPtr("odm/etc"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/odm/firmware"),
+				Name:   proptools.StringPtr("odm/firmware"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/odm/framework"),
+				Name:   proptools.StringPtr("odm/framework"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/odm/lib"),
+				Name:   proptools.StringPtr("odm/lib"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/odm/lib64"),
+				Name:   proptools.StringPtr("odm/lib64"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/odm/overlay"),
+				Name:   proptools.StringPtr("odm/overlay"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/odm/priv-app"),
+				Name:   proptools.StringPtr("odm/priv-app"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/odm/usr"),
+				Name:   proptools.StringPtr("odm/usr"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/product"),
+				Name:   proptools.StringPtr("system/product"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/system_ext"),
+				Name:   proptools.StringPtr("system/system_ext"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor"),
+				Name:   proptools.StringPtr("system/vendor"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/system_dlkm/lib/modules"),
+				Name:   proptools.StringPtr("system/lib/modules"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/data/cache"),
+				Name:   proptools.StringPtr("cache"),
+			},
+			// For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+			// both devices with and without /odm_dlkm partition. Those symlinks are for
+			// devices without /odm_dlkm partition. For devices with /odm_dlkm
+			// partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.
+			// Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
+			// via /odm/lib/modules directly. All of this also applies to the vendor_dlkm symlink
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/odm/odm_dlkm/etc"),
+				Name:   proptools.StringPtr("odm_dlkm/etc"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor/vendor_dlkm/etc"),
+				Name:   proptools.StringPtr("vendor_dlkm/etc"),
+			},
+		}
+		fsProps.Base_dir = proptools.StringPtr("system")
+		fsProps.Dirs = proptools.NewSimpleConfigurable([]string{
+			// From generic_rootdirs in build/make/target/product/generic/Android.bp
+			"acct",
+			"apex",
+			"bootstrap-apex",
+			"config",
+			"data",
+			"data_mirror",
+			"debug_ramdisk",
+			"dev",
+			"linkerconfig",
+			"metadata",
+			"mnt",
+			"odm",
+			"odm_dlkm",
+			"oem",
+			"postinstall",
+			"proc",
+			"second_stage_resources",
+			"storage",
+			"sys",
+			"system",
+			"system_dlkm",
+			"tmp",
+			"vendor",
+			"vendor_dlkm",
+
+			// from android_rootdirs in build/make/target/product/generic/Android.bp
+			"system_ext",
+			"product",
+		})
+	case "system_ext":
+		if partitionVars.ProductFsverityGenerateMetadata {
+			fsProps.Fsverity.Inputs = []string{
+				"framework/*",
+				"framework/*/*",     // framework/{arch}
+				"framework/oat/*/*", // framework/oat/{arch}
+			}
+			fsProps.Fsverity.Libs = []string{":framework-res{.export-package.apk}"}
+		}
+	case "product":
+		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
+		fsProps.Android_filesystem_deps.System = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
+		if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
+			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
+		}
+	case "vendor":
+		fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
+		fsProps.Symlinks = []filesystem.SymlinkDefinition{
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/odm"),
+				Name:   proptools.StringPtr("odm"),
+			},
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/vendor_dlkm/lib/modules"),
+				Name:   proptools.StringPtr("lib/modules"),
+			},
+		}
+		fsProps.Android_filesystem_deps.System = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
+		if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
+			fsProps.Android_filesystem_deps.System_ext = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
+		}
+	case "odm":
+		fsProps.Symlinks = []filesystem.SymlinkDefinition{
+			filesystem.SymlinkDefinition{
+				Target: proptools.StringPtr("/odm_dlkm/lib/modules"),
+				Name:   proptools.StringPtr("lib/modules"),
+			},
+		}
+	case "userdata":
+		fsProps.Base_dir = proptools.StringPtr("data")
+	case "ramdisk":
+		// Following the logic in https://cs.android.com/android/platform/superproject/main/+/c3c5063df32748a8806ce5da5dd0db158eab9ad9:build/make/core/Makefile;l=1307
+		fsProps.Dirs = android.NewSimpleConfigurable([]string{
+			"debug_ramdisk",
+			"dev",
+			"metadata",
+			"mnt",
+			"proc",
+			"second_stage_resources",
+			"sys",
+		})
+		if partitionVars.BoardUsesGenericKernelImage {
+			fsProps.Dirs.AppendSimpleValue([]string{
+				"first_stage_ramdisk/debug_ramdisk",
+				"first_stage_ramdisk/dev",
+				"first_stage_ramdisk/metadata",
+				"first_stage_ramdisk/mnt",
+				"first_stage_ramdisk/proc",
+				"first_stage_ramdisk/second_stage_resources",
+				"first_stage_ramdisk/sys",
+			})
+		}
+	case "recovery":
+		// Following https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2826;drc=ad7cfb56010cb22c3aa0e70cf71c804352553526
+		fsProps.Dirs = android.NewSimpleConfigurable([]string{
+			"sdcard",
+			"tmp",
+		})
+		fsProps.Symlinks = []filesystem.SymlinkDefinition{
+			{
+				Target: proptools.StringPtr("/system/bin/init"),
+				Name:   proptools.StringPtr("init"),
+			},
+			{
+				Target: proptools.StringPtr("prop.default"),
+				Name:   proptools.StringPtr("default.prop"),
+			},
+		}
+		fsProps.Base_dir = proptools.StringPtr("recovery")
+	}
+}
+
+var (
+	dlkmPartitions = []string{
+		"system_dlkm",
+		"vendor_dlkm",
+		"odm_dlkm",
+	}
+)
+
+// Creates a soong module to build the given partition. Returns false if we can't support building
+// it.
+func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partitionType string) bool {
+	baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
+
+	fsProps, supported := generateFsProps(ctx, partitionType)
+	if !supported {
+		return false
+	}
+
+	if partitionType == "vendor" || partitionType == "product" || partitionType == "system" {
+		fsProps.Linker_config.Gen_linker_config = proptools.BoolPtr(true)
+		if partitionType != "system" {
+			fsProps.Linker_config.Linker_config_srcs = f.createLinkerConfigSourceFilegroups(ctx, partitionType)
+		}
+	}
+
+	if android.InList(partitionType, append(dlkmPartitions, "vendor_ramdisk")) {
+		f.createPrebuiltKernelModules(ctx, partitionType)
+	}
+
+	var module android.Module
+	if partitionType == "system" {
+		module = ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps)
+	} else {
+		// Explicitly set the partition.
+		fsProps.Partition_type = proptools.StringPtr(partitionType)
+		module = ctx.CreateModule(filesystem.FilesystemFactory, baseProps, fsProps)
+	}
+	module.HideFromMake()
+	if partitionType == "vendor" {
+		f.createVendorBuildProp(ctx)
+	}
+	return true
+}
+
+// Creates filegroups for the files specified in BOARD_(partition_)AVB_KEY_PATH
+func (f *filesystemCreator) createAvbKeyFilegroups(ctx android.LoadHookContext) {
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	var files []string
+
+	if len(partitionVars.BoardAvbKeyPath) > 0 {
+		files = append(files, partitionVars.BoardAvbKeyPath)
+	}
+	for _, partition := range android.SortedKeys(partitionVars.PartitionQualifiedVariables) {
+		specificPartitionVars := partitionVars.PartitionQualifiedVariables[partition]
+		if len(specificPartitionVars.BoardAvbKeyPath) > 0 {
+			files = append(files, specificPartitionVars.BoardAvbKeyPath)
+		}
+	}
+
+	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+	for _, file := range files {
+		if _, ok := fsGenState.avbKeyFilegroups[file]; ok {
+			continue
+		}
+		if file == "external/avb/test/data/testkey_rsa4096.pem" {
+			// There already exists a checked-in filegroup for this commonly-used key, just use that
+			fsGenState.avbKeyFilegroups[file] = "avb_testkey_rsa4096"
+			continue
+		}
+		dir := filepath.Dir(file)
+		base := filepath.Base(file)
+		name := fmt.Sprintf("avb_key_%x", strings.ReplaceAll(file, "/", "_"))
+		ctx.CreateModuleInDirectory(
+			android.FileGroupFactory,
+			dir,
+			&struct {
+				Name       *string
+				Srcs       []string
+				Visibility []string
+			}{
+				Name:       proptools.StringPtr(name),
+				Srcs:       []string{base},
+				Visibility: []string{"//visibility:public"},
+			},
+		)
+		fsGenState.avbKeyFilegroups[file] = name
+	}
+}
+
+// Creates filegroups for miscellaneous other files
+func (f *filesystemCreator) createMiscFilegroups(ctx android.LoadHookContext) {
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+
+	if partitionVars.BoardErofsCompressorHints != "" {
+		dir := filepath.Dir(partitionVars.BoardErofsCompressorHints)
+		base := filepath.Base(partitionVars.BoardErofsCompressorHints)
+		ctx.CreateModuleInDirectory(
+			android.FileGroupFactory,
+			dir,
+			&struct {
+				Name       *string
+				Srcs       []string
+				Visibility []string
+			}{
+				Name:       proptools.StringPtr("soong_generated_board_erofs_compress_hints_filegroup"),
+				Srcs:       []string{base},
+				Visibility: []string{"//visibility:public"},
+			},
+		)
+	}
+}
+
+// createPrebuiltKernelModules creates `prebuilt_kernel_modules`. These modules will be added to deps of the
+// autogenerated *_dlkm filsystem modules. Each _dlkm partition should have a single prebuilt_kernel_modules dependency.
+// This ensures that the depmod artifacts (modules.* installed in /lib/modules/) are generated with a complete view.
+func (f *filesystemCreator) createPrebuiltKernelModules(ctx android.LoadHookContext, partitionType string) {
+	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+	name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules", partitionType))
+	props := &struct {
+		Name                 *string
+		Srcs                 []string
+		System_deps          []string
+		System_dlkm_specific *bool
+		Vendor_dlkm_specific *bool
+		Odm_dlkm_specific    *bool
+		Vendor_ramdisk       *bool
+		Load_by_default      *bool
+		Blocklist_file       *string
+		Options_file         *string
+	}{
+		Name: proptools.StringPtr(name),
+	}
+	switch partitionType {
+	case "system_dlkm":
+		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules).Strings()
+		props.System_dlkm_specific = proptools.BoolPtr(true)
+		if len(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelLoadModules) == 0 {
+			// Create empty modules.load file for system
+			// https://source.corp.google.com/h/googleplex-android/platform/build/+/ef55daac9954896161b26db4f3ef1781b5a5694c:core/Makefile;l=695-700;drc=549fe2a5162548bd8b47867d35f907eb22332023;bpv=1;bpt=0
+			props.Load_by_default = proptools.BoolPtr(false)
+		}
+		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelBlocklistFile; blocklistFile != "" {
+			props.Blocklist_file = proptools.StringPtr(blocklistFile)
+		}
+	case "vendor_dlkm":
+		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorKernelModules).Strings()
+		if len(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules) > 0 {
+			props.System_deps = []string{":" + generatedModuleName(ctx.Config(), "system_dlkm-kernel-modules") + "{.modules}"}
+		}
+		props.Vendor_dlkm_specific = proptools.BoolPtr(true)
+		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorKernelBlocklistFile; blocklistFile != "" {
+			props.Blocklist_file = proptools.StringPtr(blocklistFile)
+		}
+	case "odm_dlkm":
+		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.OdmKernelModules).Strings()
+		props.Odm_dlkm_specific = proptools.BoolPtr(true)
+		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.OdmKernelBlocklistFile; blocklistFile != "" {
+			props.Blocklist_file = proptools.StringPtr(blocklistFile)
+		}
+	case "vendor_ramdisk":
+		props.Srcs = android.ExistentPathsForSources(ctx, ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelModules).Strings()
+		props.Vendor_ramdisk = proptools.BoolPtr(true)
+		if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelBlocklistFile; blocklistFile != "" {
+			props.Blocklist_file = proptools.StringPtr(blocklistFile)
+		}
+		if optionsFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorRamdiskKernelOptionsFile; optionsFile != "" {
+			props.Options_file = proptools.StringPtr(optionsFile)
+		}
+
+	default:
+		ctx.ModuleErrorf("DLKM is not supported for %s\n", partitionType)
+	}
+
+	if len(props.Srcs) == 0 {
+		return // do not generate `prebuilt_kernel_modules` if there are no sources
+	}
+
+	kernelModule := ctx.CreateModuleInDirectory(
+		kernel.PrebuiltKernelModulesFactory,
+		".", // create in root directory for now
+		props,
+	)
+	kernelModule.HideFromMake()
+	// Add to deps
+	(*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
+}
+
+// Create a build_prop and android_info module. This will be used to create /vendor/build.prop
+func (f *filesystemCreator) createVendorBuildProp(ctx android.LoadHookContext) {
+	// Create a android_info for vendor
+	// The board info files might be in a directory outside the root soong namespace, so create
+	// the module in "."
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	androidInfoProps := &struct {
+		Name                  *string
+		Board_info_files      []string
+		Bootloader_board_name *string
+	}{
+		Name:             proptools.StringPtr(generatedModuleName(ctx.Config(), "android-info.prop")),
+		Board_info_files: partitionVars.BoardInfoFiles,
+	}
+	if len(androidInfoProps.Board_info_files) == 0 {
+		androidInfoProps.Bootloader_board_name = proptools.StringPtr(partitionVars.BootLoaderBoardName)
+	}
+	androidInfoProp := ctx.CreateModuleInDirectory(
+		android.AndroidInfoFactory,
+		".",
+		androidInfoProps,
+	)
+	androidInfoProp.HideFromMake()
+	// Create a build prop for vendor
+	vendorBuildProps := &struct {
+		Name           *string
+		Vendor         *bool
+		Stem           *string
+		Product_config *string
+		Android_info   *string
+	}{
+		Name:           proptools.StringPtr(generatedModuleName(ctx.Config(), "vendor-build.prop")),
+		Vendor:         proptools.BoolPtr(true),
+		Stem:           proptools.StringPtr("build.prop"),
+		Product_config: proptools.StringPtr(":product_config"),
+		Android_info:   proptools.StringPtr(":" + androidInfoProp.Name()),
+	}
+	vendorBuildProp := ctx.CreateModule(
+		android.BuildPropFactory,
+		vendorBuildProps,
+	)
+	vendorBuildProp.HideFromMake()
+}
+
+// createLinkerConfigSourceFilegroups creates filegroup modules to generate linker.config.pb for the following partitions
+// 1. vendor: Using PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS (space separated file list)
+// 1. product: Using PRODUCT_PRODUCT_LINKER_CONFIG_FRAGMENTS (space separated file list)
+// It creates a filegroup for each file in the fragment list
+// The filegroup modules are then added to `linker_config_srcs` of the autogenerated vendor `android_filesystem`.
+func (f *filesystemCreator) createLinkerConfigSourceFilegroups(ctx android.LoadHookContext, partitionType string) []string {
+	ret := []string{}
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	var linkerConfigSrcs []string
+	if partitionType == "vendor" {
+		linkerConfigSrcs = android.FirstUniqueStrings(partitionVars.VendorLinkerConfigSrcs)
+	} else if partitionType == "product" {
+		linkerConfigSrcs = android.FirstUniqueStrings(partitionVars.ProductLinkerConfigSrcs)
+	} else {
+		ctx.ModuleErrorf("linker.config.pb is only supported for vendor and product partitions. For system partition, use `android_system_image`")
+	}
+
+	if len(linkerConfigSrcs) > 0 {
+		// Create a filegroup, and add `:<filegroup_name>` to ret.
+		for index, linkerConfigSrc := range linkerConfigSrcs {
+			dir := filepath.Dir(linkerConfigSrc)
+			base := filepath.Base(linkerConfigSrc)
+			fgName := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-linker-config-src%s", partitionType, strconv.Itoa(index)))
+			srcs := []string{base}
+			fgProps := &struct {
+				Name *string
+				Srcs proptools.Configurable[[]string]
+			}{
+				Name: proptools.StringPtr(fgName),
+				Srcs: proptools.NewSimpleConfigurable(srcs),
+			}
+			ctx.CreateModuleInDirectory(
+				android.FileGroupFactory,
+				dir,
+				fgProps,
+			)
+			ret = append(ret, ":"+fgName)
+		}
+	}
+	return ret
+}
+
+type filesystemBaseProperty struct {
+	Name             *string
+	Compile_multilib *string
+	Visibility       []string
+}
+
+func generateBaseProps(namePtr *string) *filesystemBaseProperty {
+	return &filesystemBaseProperty{
+		Name:             namePtr,
+		Compile_multilib: proptools.StringPtr("both"),
+		// The vbmeta modules are currently in the root directory and depend on the partitions
+		Visibility: []string{"//.", "//build/soong:__subpackages__"},
+	}
+}
+
+func generateFsProps(ctx android.EarlyModuleContext, partitionType string) (*filesystem.FilesystemProperties, bool) {
+	fsProps := &filesystem.FilesystemProperties{}
+
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	var avbInfo avbInfo
+	var fsType string
+	if strings.Contains(partitionType, "ramdisk") {
+		fsType = "compressed_cpio"
+	} else {
+		specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
+		fsType = specificPartitionVars.BoardFileSystemType
+		avbInfo = getAvbInfo(ctx.Config(), partitionType)
+		if fsType == "" {
+			fsType = "ext4" //default
+		}
+	}
+
+	fsProps.Type = proptools.StringPtr(fsType)
+	if filesystem.GetFsTypeFromString(ctx, *fsProps.Type).IsUnknown() {
+		// Currently the android_filesystem module type only supports a handful of FS types like ext4, erofs
+		return nil, false
+	}
+
+	if *fsProps.Type == "erofs" {
+		if partitionVars.BoardErofsCompressor != "" {
+			fsProps.Erofs.Compressor = proptools.StringPtr(partitionVars.BoardErofsCompressor)
+		}
+		if partitionVars.BoardErofsCompressorHints != "" {
+			fsProps.Erofs.Compress_hints = proptools.StringPtr(":soong_generated_board_erofs_compress_hints_filegroup")
+		}
+	}
+
+	// Don't build this module on checkbuilds, the soong-built partitions are still in-progress
+	// and sometimes don't build.
+	fsProps.Unchecked_module = proptools.BoolPtr(true)
+
+	// BOARD_AVB_ENABLE
+	fsProps.Use_avb = avbInfo.avbEnable
+	// BOARD_AVB_KEY_PATH
+	fsProps.Avb_private_key = avbInfo.avbkeyFilegroup
+	// BOARD_AVB_ALGORITHM
+	fsProps.Avb_algorithm = avbInfo.avbAlgorithm
+	// BOARD_AVB_SYSTEM_ROLLBACK_INDEX
+	fsProps.Rollback_index = avbInfo.avbRollbackIndex
+	fsProps.Avb_hash_algorithm = avbInfo.avbHashAlgorithm
+
+	fsProps.Partition_name = proptools.StringPtr(partitionType)
+
+	switch partitionType {
+	// The partitions that support file_contexts came from here:
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2270;drc=ad7cfb56010cb22c3aa0e70cf71c804352553526
+	case "system", "userdata", "cache", "vendor", "product", "system_ext", "odm", "vendor_dlkm", "odm_dlkm", "system_dlkm", "oem":
+		fsProps.Precompiled_file_contexts = proptools.StringPtr(":file_contexts_bin_gen")
+	}
+
+	fsProps.Is_auto_generated = proptools.BoolPtr(true)
+
+	partitionSpecificFsProps(ctx, fsProps, partitionVars, partitionType)
+
+	return fsProps, true
+}
+
+type avbInfo struct {
+	avbEnable        *bool
+	avbKeyPath       *string
+	avbkeyFilegroup  *string
+	avbAlgorithm     *string
+	avbRollbackIndex *int64
+	avbMode          *string
+	avbHashAlgorithm *string
+}
+
+func getAvbInfo(config android.Config, partitionType string) avbInfo {
+	partitionVars := config.ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
+	var result avbInfo
+	boardAvbEnable := partitionVars.BoardAvbEnable
+	if boardAvbEnable {
+		result.avbEnable = proptools.BoolPtr(true)
+		// There are "global" and "specific" copies of a lot of these variables. Sometimes they
+		// choose the specific and then fall back to the global one if it's not set, other times
+		// the global one actually only applies to the vbmeta partition.
+		if partitionType == "vbmeta" {
+			if partitionVars.BoardAvbKeyPath != "" {
+				result.avbKeyPath = proptools.StringPtr(partitionVars.BoardAvbKeyPath)
+			}
+			if partitionVars.BoardAvbRollbackIndex != "" {
+				parsed, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 64)
+				if err != nil {
+					panic(fmt.Sprintf("Rollback index must be an int, got %s", partitionVars.BoardAvbRollbackIndex))
+				}
+				result.avbRollbackIndex = &parsed
+			}
+		}
+		if specificPartitionVars.BoardAvbKeyPath != "" {
+			result.avbKeyPath = proptools.StringPtr(specificPartitionVars.BoardAvbKeyPath)
+		}
+		if specificPartitionVars.BoardAvbAlgorithm != "" {
+			result.avbAlgorithm = proptools.StringPtr(specificPartitionVars.BoardAvbAlgorithm)
+		} else if partitionVars.BoardAvbAlgorithm != "" {
+			result.avbAlgorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm)
+		}
+		if specificPartitionVars.BoardAvbRollbackIndex != "" {
+			parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64)
+			if err != nil {
+				panic(fmt.Sprintf("Rollback index must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndex))
+			}
+			result.avbRollbackIndex = &parsed
+		}
+		if specificPartitionVars.BoardAvbRollbackIndex != "" {
+			parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64)
+			if err != nil {
+				panic(fmt.Sprintf("Rollback index must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndex))
+			}
+			result.avbRollbackIndex = &parsed
+		}
+
+		// Make allows you to pass arbitrary arguments to avbtool via this variable, but in practice
+		// it's only used for --hash_algorithm. The soong module has a dedicated property for the
+		// hashtree algorithm, and doesn't allow custom arguments, so just extract the hashtree
+		// algorithm out of the arbitrary arguments.
+		addHashtreeFooterArgs := strings.Split(specificPartitionVars.BoardAvbAddHashtreeFooterArgs, " ")
+		if i := slices.Index(addHashtreeFooterArgs, "--hash_algorithm"); i >= 0 {
+			result.avbHashAlgorithm = &addHashtreeFooterArgs[i+1]
+		}
+
+		result.avbMode = proptools.StringPtr("make_legacy")
+	}
+	if result.avbKeyPath != nil {
+		fsGenState := config.Get(fsGenStateOnceKey).(*FsGenState)
+		filegroup := fsGenState.avbKeyFilegroups[*result.avbKeyPath]
+		result.avbkeyFilegroup = proptools.StringPtr(":" + filegroup)
+	}
+	return result
+}
+
+func (f *filesystemCreator) createFileListDiffTest(ctx android.ModuleContext, partitionType string) android.Path {
+	partitionModuleName := generatedModuleNameForPartition(ctx.Config(), partitionType)
+	systemImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag)
+	filesystemInfo, ok := android.OtherModuleProvider(ctx, systemImage, filesystem.FilesystemProvider)
+	if !ok {
+		ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName)
+	}
+	makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType))
+	diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", partitionModuleName))
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().BuiltTool("file_list_diff").
+		Input(makeFileList).
+		Input(filesystemInfo.FileListFile).
+		Text(partitionModuleName)
+	builder.Command().Text("touch").Output(diffTestResultFile)
+	builder.Build(partitionModuleName+" diff test", partitionModuleName+" diff test")
+	return diffTestResultFile
+}
+
+func createFailingCommand(ctx android.ModuleContext, message string) android.Path {
+	hasher := sha256.New()
+	hasher.Write([]byte(message))
+	filename := fmt.Sprintf("failing_command_%x.txt", hasher.Sum(nil))
+	file := android.PathForModuleOut(ctx, filename)
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Textf("echo %s", proptools.NinjaAndShellEscape(message))
+	builder.Command().Text("exit 1 #").Output(file)
+	builder.Build("failing command "+filename, "failing command "+filename)
+	return file
+}
+
+func createVbmetaDiff(ctx android.ModuleContext, vbmetaModuleName string, vbmetaPartitionName string) android.Path {
+	vbmetaModule := ctx.GetDirectDepWithTag(vbmetaModuleName, generatedVbmetaPartitionDepTag)
+	outputFilesProvider, ok := android.OtherModuleProvider(ctx, vbmetaModule, android.OutputFilesProvider)
+	if !ok {
+		ctx.ModuleErrorf("Expected module %s to provide OutputFiles", vbmetaModule)
+	}
+	if len(outputFilesProvider.DefaultOutputFiles) != 1 {
+		ctx.ModuleErrorf("Expected 1 output file from module %s", vbmetaModule)
+	}
+	soongVbMetaFile := outputFilesProvider.DefaultOutputFiles[0]
+	makeVbmetaFile := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/%s.img", ctx.Config().DeviceName(), vbmetaPartitionName))
+
+	diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", vbmetaModuleName))
+	createDiffTest(ctx, diffTestResultFile, soongVbMetaFile, makeVbmetaFile)
+	return diffTestResultFile
+}
+
+func createDiffTest(ctx android.ModuleContext, diffTestResultFile android.WritablePath, file1 android.Path, file2 android.Path) {
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Text("diff").
+		Input(file1).
+		Input(file2)
+	builder.Command().Text("touch").Output(diffTestResultFile)
+	builder.Build("diff test "+diffTestResultFile.String(), "diff test")
+}
+
+type systemImageDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var generatedFilesystemDepTag systemImageDepTagType
+var generatedVbmetaPartitionDepTag systemImageDepTagType
+
+func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
+	for _, partitionType := range f.properties.Generated_partition_types {
+		ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, generatedModuleNameForPartition(ctx.Config(), partitionType))
+	}
+	for _, vbmetaModule := range f.properties.Vbmeta_module_names {
+		ctx.AddDependency(ctx.Module(), generatedVbmetaPartitionDepTag, vbmetaModule)
+	}
+}
+
+func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if ctx.ModuleDir() != "build/soong/fsgen" {
+		ctx.ModuleErrorf("There can only be one soong_filesystem_creator in build/soong/fsgen")
+	}
+	f.HideFromMake()
+
+	var content strings.Builder
+	generatedBp := android.PathForModuleOut(ctx, "soong_generated_product_config.bp")
+	for _, partition := range ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions {
+		content.WriteString(generateBpContent(ctx, partition))
+		content.WriteString("\n")
+	}
+	android.WriteFileRule(ctx, generatedBp, content.String())
+
+	ctx.Phony("product_config_to_bp", generatedBp)
+
+	var diffTestFiles []android.Path
+	for _, partitionType := range f.properties.Generated_partition_types {
+		diffTestFile := f.createFileListDiffTest(ctx, partitionType)
+		diffTestFiles = append(diffTestFiles, diffTestFile)
+		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
+	}
+	for _, partitionType := range f.properties.Unsupported_partition_types {
+		diffTestFile := createFailingCommand(ctx, fmt.Sprintf("Couldn't build %s partition", partitionType))
+		diffTestFiles = append(diffTestFiles, diffTestFile)
+		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
+	}
+	for i, vbmetaModule := range f.properties.Vbmeta_module_names {
+		diffTestFile := createVbmetaDiff(ctx, vbmetaModule, f.properties.Vbmeta_partition_names[i])
+		diffTestFiles = append(diffTestFiles, diffTestFile)
+		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", f.properties.Vbmeta_partition_names[i]), diffTestFile)
+	}
+	if f.properties.Boot_image != "" {
+		diffTestFile := android.PathForModuleOut(ctx, "boot_diff_test.txt")
+		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Boot_image)
+		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/boot.img", ctx.Config().DeviceName()))
+		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
+		diffTestFiles = append(diffTestFiles, diffTestFile)
+		ctx.Phony("soong_generated_boot_filesystem_test", diffTestFile)
+	}
+	if f.properties.Vendor_boot_image != "" {
+		diffTestFile := android.PathForModuleOut(ctx, "vendor_boot_diff_test.txt")
+		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Vendor_boot_image)
+		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/vendor_boot.img", ctx.Config().DeviceName()))
+		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
+		diffTestFiles = append(diffTestFiles, diffTestFile)
+		ctx.Phony("soong_generated_vendor_boot_filesystem_test", diffTestFile)
+	}
+	if f.properties.Init_boot_image != "" {
+		diffTestFile := android.PathForModuleOut(ctx, "init_boot_diff_test.txt")
+		soongBootImg := android.PathForModuleSrc(ctx, f.properties.Init_boot_image)
+		makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/init_boot.img", ctx.Config().DeviceName()))
+		createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
+		diffTestFiles = append(diffTestFiles, diffTestFile)
+		ctx.Phony("soong_generated_init_boot_filesystem_test", diffTestFile)
+	}
+	if f.properties.Super_image != "" {
+		diffTestFile := android.PathForModuleOut(ctx, "super_diff_test.txt")
+		soongSuperImg := android.PathForModuleSrc(ctx, f.properties.Super_image)
+		makeSuperImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/super.img", ctx.Config().DeviceName()))
+		createDiffTest(ctx, diffTestFile, soongSuperImg, makeSuperImage)
+		diffTestFiles = append(diffTestFiles, diffTestFile)
+		ctx.Phony("soong_generated_super_filesystem_test", diffTestFile)
+	}
+	ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
+}
+
+func generateBpContent(ctx android.EarlyModuleContext, partitionType string) string {
+	fsProps, fsTypeSupported := generateFsProps(ctx, partitionType)
+	if !fsTypeSupported {
+		return ""
+	}
+
+	baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
+	fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+	deps := fsGenState.fsDeps[partitionType]
+	highPriorityDeps := fsGenState.generatedPrebuiltEtcModuleNames
+	depProps := generateDepStruct(*deps, highPriorityDeps)
+
+	result, err := proptools.RepackProperties([]interface{}{baseProps, fsProps, depProps})
+	if err != nil {
+		ctx.ModuleErrorf("%s", err.Error())
+		return ""
+	}
+
+	moduleType := "android_filesystem"
+	if partitionType == "system" {
+		moduleType = "android_system_image"
+	}
+
+	file := &parser.File{
+		Defs: []parser.Definition{
+			&parser.Module{
+				Type: moduleType,
+				Map:  *result,
+			},
+		},
+	}
+	bytes, err := parser.Print(file)
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+	}
+	return strings.TrimSpace(string(bytes))
+}
diff --git a/fsgen/filesystem_creator_test.go b/fsgen/filesystem_creator_test.go
new file mode 100644
index 0000000..5657608
--- /dev/null
+++ b/fsgen/filesystem_creator_test.go
@@ -0,0 +1,383 @@
+// 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 fsgen
+
+import (
+	"android/soong/android"
+	"android/soong/etc"
+	"android/soong/filesystem"
+	"android/soong/java"
+	"testing"
+
+	"github.com/google/blueprint/proptools"
+)
+
+var prepareForTestWithFsgenBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents)
+
+func TestFileSystemCreatorSystemImageProps(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		android.PrepareForTestWithAndroidBuildComponents,
+		android.PrepareForTestWithAllowMissingDependencies,
+		filesystem.PrepareForTestWithFilesystemBuildComponents,
+		prepareForTestWithFsgenBuildComponents,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.BoardAvbEnable = true
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.PartitionQualifiedVariables =
+				map[string]android.PartitionQualifiedVariablesType{
+					"system": {
+						BoardAvbKeyPath:       "external/avb/test/data/testkey_rsa4096.pem",
+						BoardAvbAlgorithm:     "SHA256_RSA4096",
+						BoardAvbRollbackIndex: "0",
+						BoardFileSystemType:   "ext4",
+					},
+				}
+		}),
+		android.FixtureMergeMockFs(android.MockFS{
+			"external/avb/test/data/testkey_rsa4096.pem": nil,
+			"external/avb/test/Android.bp": []byte(`
+			filegroup {
+				name: "avb_testkey_rsa4096",
+				srcs: ["data/testkey_rsa4096.pem"],
+			}
+			`),
+			"build/soong/fsgen/Android.bp": []byte(`
+			soong_filesystem_creator {
+				name: "foo",
+			}
+			`),
+		}),
+	).RunTest(t)
+
+	fooSystem := result.ModuleForTests("test_product_generated_system_image", "android_common").Module().(interface {
+		FsProps() filesystem.FilesystemProperties
+	})
+	android.AssertBoolEquals(
+		t,
+		"Property expected to match the product variable 'BOARD_AVB_ENABLE'",
+		true,
+		proptools.Bool(fooSystem.FsProps().Use_avb),
+	)
+	android.AssertStringEquals(
+		t,
+		"Property the avb_private_key property to be set to the existing filegroup",
+		":avb_testkey_rsa4096",
+		proptools.String(fooSystem.FsProps().Avb_private_key),
+	)
+	android.AssertStringEquals(
+		t,
+		"Property expected to match the product variable 'BOARD_AVB_ALGORITHM'",
+		"SHA256_RSA4096",
+		proptools.String(fooSystem.FsProps().Avb_algorithm),
+	)
+	android.AssertIntEquals(
+		t,
+		"Property expected to match the product variable 'BOARD_AVB_SYSTEM_ROLLBACK_INDEX'",
+		0,
+		proptools.Int(fooSystem.FsProps().Rollback_index),
+	)
+	android.AssertStringEquals(
+		t,
+		"Property expected to match the product variable 'BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE'",
+		"ext4",
+		proptools.String(fooSystem.FsProps().Type),
+	)
+}
+
+func TestFileSystemCreatorSetPartitionDeps(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		android.PrepareForTestWithAndroidBuildComponents,
+		android.PrepareForTestWithAllowMissingDependencies,
+		filesystem.PrepareForTestWithFilesystemBuildComponents,
+		prepareForTestWithFsgenBuildComponents,
+		java.PrepareForTestWithJavaBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.ProductPackages = []string{"bar", "baz"}
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.PartitionQualifiedVariables =
+				map[string]android.PartitionQualifiedVariablesType{
+					"system": {
+						BoardFileSystemType: "ext4",
+					},
+				}
+		}),
+		android.FixtureMergeMockFs(android.MockFS{
+			"external/avb/test/data/testkey_rsa4096.pem": nil,
+			"build/soong/fsgen/Android.bp": []byte(`
+			soong_filesystem_creator {
+				name: "foo",
+			}
+			`),
+		}),
+	).RunTestWithBp(t, `
+	java_library {
+		name: "bar",
+		srcs: ["A.java"],
+	}
+	java_library {
+		name: "baz",
+		srcs: ["A.java"],
+		product_specific: true,
+	}
+	`)
+
+	android.AssertBoolEquals(
+		t,
+		"Generated system image expected to depend on system partition installed \"bar\"",
+		true,
+		java.CheckModuleHasDependency(t, result.TestContext, "test_product_generated_system_image", "android_common", "bar"),
+	)
+	android.AssertBoolEquals(
+		t,
+		"Generated system image expected to not depend on product partition installed \"baz\"",
+		false,
+		java.CheckModuleHasDependency(t, result.TestContext, "test_product_generated_system_image", "android_common", "baz"),
+	)
+}
+
+func TestFileSystemCreatorDepsWithNamespace(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		android.PrepareForTestWithAndroidBuildComponents,
+		android.PrepareForTestWithAllowMissingDependencies,
+		android.PrepareForTestWithNamespace,
+		android.PrepareForTestWithArchMutator,
+		filesystem.PrepareForTestWithFilesystemBuildComponents,
+		prepareForTestWithFsgenBuildComponents,
+		java.PrepareForTestWithJavaBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.ProductPackages = []string{"bar"}
+			config.TestProductVariables.NamespacesToExport = []string{"a/b"}
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.PartitionQualifiedVariables =
+				map[string]android.PartitionQualifiedVariablesType{
+					"system": {
+						BoardFileSystemType: "ext4",
+					},
+				}
+		}),
+		android.PrepareForNativeBridgeEnabled,
+		android.FixtureMergeMockFs(android.MockFS{
+			"external/avb/test/data/testkey_rsa4096.pem": nil,
+			"build/soong/fsgen/Android.bp": []byte(`
+			soong_filesystem_creator {
+				name: "foo",
+			}
+			`),
+			"a/b/Android.bp": []byte(`
+			soong_namespace{
+			}
+			java_library {
+				name: "bar",
+				srcs: ["A.java"],
+				compile_multilib: "64",
+			}
+			`),
+			"c/d/Android.bp": []byte(`
+			soong_namespace{
+			}
+			java_library {
+				name: "bar",
+				srcs: ["A.java"],
+			}
+			`),
+		}),
+	).RunTest(t)
+
+	var packagingProps android.PackagingProperties
+	for _, prop := range result.ModuleForTests("test_product_generated_system_image", "android_common").Module().GetProperties() {
+		if packagingPropStruct, ok := prop.(*android.PackagingProperties); ok {
+			packagingProps = *packagingPropStruct
+		}
+	}
+	moduleDeps := packagingProps.Multilib.Lib64.Deps
+
+	eval := result.ModuleForTests("test_product_generated_system_image", "android_common").Module().ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringListContains(
+		t,
+		"Generated system image expected to depend on \"bar\" defined in \"a/b\" namespace",
+		moduleDeps.GetOrDefault(eval, nil),
+		"//a/b:bar",
+	)
+	android.AssertStringListDoesNotContain(
+		t,
+		"Generated system image expected to not depend on \"bar\" defined in \"c/d\" namespace",
+		moduleDeps.GetOrDefault(eval, nil),
+		"//c/d:bar",
+	)
+}
+
+func TestRemoveOverriddenModulesFromDeps(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		android.PrepareForTestWithAndroidBuildComponents,
+		android.PrepareForTestWithAllowMissingDependencies,
+		prepareForTestWithFsgenBuildComponents,
+		java.PrepareForTestWithJavaBuildComponents,
+		android.FixtureMergeMockFs(android.MockFS{
+			"external/avb/test/data/testkey_rsa4096.pem": nil,
+			"build/soong/fsgen/Android.bp": []byte(`
+			soong_filesystem_creator {
+				name: "foo",
+			}
+			`),
+		}),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.ProductPackages = []string{"libfoo", "libbar"}
+		}),
+	).RunTestWithBp(t, `
+java_library {
+	name: "libfoo",
+}
+java_library {
+	name: "libbar",
+	required: ["libbaz"],
+}
+java_library {
+	name: "libbaz",
+	overrides: ["libfoo"], // overrides libfoo
+}
+	`)
+	resolvedSystemDeps := result.TestContext.Config().Get(fsGenStateOnceKey).(*FsGenState).fsDeps["system"]
+	_, libFooInDeps := (*resolvedSystemDeps)["libfoo"]
+	android.AssertBoolEquals(t, "libfoo should not appear in deps because it has been overridden by libbaz. The latter is a required dep of libbar, which is listed in PRODUCT_PACKAGES", false, libFooInDeps)
+}
+
+func TestPrebuiltEtcModuleGen(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		android.PrepareForTestWithAndroidBuildComponents,
+		android.PrepareForTestWithAllowMissingDependencies,
+		filesystem.PrepareForTestWithFilesystemBuildComponents,
+		prepareForTestWithFsgenBuildComponents,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.ProductCopyFiles = []string{
+				"frameworks/base/config/preloaded-classes:system/etc/preloaded-classes",
+				"frameworks/base/data/keyboards/Vendor_0079_Product_0011.kl:system/usr/keylayout/subdir/Vendor_0079_Product_0011.kl",
+				"frameworks/base/data/keyboards/Vendor_0079_Product_18d4.kl:system/usr/keylayout/subdir/Vendor_0079_Product_18d4.kl",
+				"some/non/existing/file.txt:system/etc/file.txt",
+				"device/sample/etc/apns-full-conf.xml:product/etc/apns-conf.xml:google",
+				"device/sample/etc/apns-full-conf.xml:product/etc/apns-conf-2.xml",
+			}
+			config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.PartitionQualifiedVariables =
+				map[string]android.PartitionQualifiedVariablesType{
+					"system": {
+						BoardFileSystemType: "ext4",
+					},
+				}
+		}),
+		android.FixtureMergeMockFs(android.MockFS{
+			"external/avb/test/data/testkey_rsa4096.pem": nil,
+			"build/soong/fsgen/Android.bp": []byte(`
+			soong_filesystem_creator {
+				name: "foo",
+			}
+			`),
+			"frameworks/base/config/preloaded-classes":                   nil,
+			"frameworks/base/data/keyboards/Vendor_0079_Product_0011.kl": nil,
+			"frameworks/base/data/keyboards/Vendor_0079_Product_18d4.kl": nil,
+			"device/sample/etc/apns-full-conf.xml":                       nil,
+		}),
+	).RunTest(t)
+
+	checkModuleProp := func(m android.Module, matcher func(actual interface{}) bool) bool {
+		for _, prop := range m.GetProperties() {
+
+			if matcher(prop) {
+				return true
+			}
+		}
+		return false
+	}
+
+	// check generated prebuilt_* module type install path and install partition
+	generatedModule := result.ModuleForTests("system-frameworks_base_config-etc-0", "android_arm64_armv8-a").Module()
+	etcModule, _ := generatedModule.(*etc.PrebuiltEtc)
+	android.AssertStringEquals(
+		t,
+		"module expected to have etc install path",
+		"etc",
+		etcModule.BaseDir(),
+	)
+	android.AssertBoolEquals(
+		t,
+		"module expected to be installed in system partition",
+		true,
+		!generatedModule.InstallInProduct() &&
+			!generatedModule.InstallInVendor() &&
+			!generatedModule.InstallInSystemExt(),
+	)
+
+	// check generated prebuilt_* module specifies correct relative_install_path property
+	generatedModule = result.ModuleForTests("system-frameworks_base_data_keyboards-usr_keylayout_subdir-0", "android_arm64_armv8-a").Module()
+	etcModule, _ = generatedModule.(*etc.PrebuiltEtc)
+	android.AssertStringEquals(
+		t,
+		"module expected to set correct relative_install_path properties",
+		"subdir",
+		etcModule.SubDir(),
+	)
+
+	// check that prebuilt_* module is not generated for non existing source file
+	android.AssertPanicMessageContains(
+		t,
+		"prebuilt_* module not generated for non existing source file",
+		"failed to find module \"system-some_non_existing-etc-0\"",
+		func() { result.ModuleForTests("system-some_non_existing-etc-0", "android_arm64_armv8-a") },
+	)
+
+	// check that duplicate src file can exist in PRODUCT_COPY_FILES and generates separate modules
+	generatedModule0 := result.ModuleForTests("product-device_sample_etc-etc-0", "android_arm64_armv8-a").Module()
+	generatedModule1 := result.ModuleForTests("product-device_sample_etc-etc-1", "android_arm64_armv8-a").Module()
+
+	// check that generated prebuilt_* module sets correct srcs and dsts property
+	eval := generatedModule0.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertBoolEquals(
+		t,
+		"module expected to set correct srcs and dsts properties",
+		true,
+		checkModuleProp(generatedModule0, func(actual interface{}) bool {
+			if p, ok := actual.(*etc.PrebuiltEtcProperties); ok {
+				srcs := p.Srcs.GetOrDefault(eval, nil)
+				dsts := p.Dsts.GetOrDefault(eval, nil)
+				return len(srcs) == 1 &&
+					srcs[0] == "apns-full-conf.xml" &&
+					len(dsts) == 1 &&
+					dsts[0] == "apns-conf.xml"
+			}
+			return false
+		}),
+	)
+
+	// check that generated prebuilt_* module sets correct srcs and dsts property
+	eval = generatedModule1.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertBoolEquals(
+		t,
+		"module expected to set correct srcs and dsts properties",
+		true,
+		checkModuleProp(generatedModule1, func(actual interface{}) bool {
+			if p, ok := actual.(*etc.PrebuiltEtcProperties); ok {
+				srcs := p.Srcs.GetOrDefault(eval, nil)
+				dsts := p.Dsts.GetOrDefault(eval, nil)
+				return len(srcs) == 1 &&
+					srcs[0] == "apns-full-conf.xml" &&
+					len(dsts) == 1 &&
+					dsts[0] == "apns-conf-2.xml"
+			}
+			return false
+		}),
+	)
+}
diff --git a/fsgen/fsgen_mutators.go b/fsgen/fsgen_mutators.go
new file mode 100644
index 0000000..f0a54db
--- /dev/null
+++ b/fsgen/fsgen_mutators.go
@@ -0,0 +1,372 @@
+// 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.
+
+package fsgen
+
+import (
+	"fmt"
+	"slices"
+	"strings"
+	"sync"
+
+	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
+)
+
+func RegisterCollectFileSystemDepsMutators(ctx android.RegisterMutatorsContext) {
+	ctx.BottomUp("fs_collect_deps", collectDepsMutator).MutatesGlobalState()
+	ctx.BottomUp("fs_set_deps", setDepsMutator)
+}
+
+var fsGenStateOnceKey = android.NewOnceKey("FsGenState")
+var fsGenRemoveOverridesOnceKey = android.NewOnceKey("FsGenRemoveOverrides")
+
+// Map of partition module name to its partition that may be generated by Soong.
+// Note that it is not guaranteed that all modules returned by this function are successfully
+// created.
+func getAllSoongGeneratedPartitionNames(config android.Config, partitions []string) map[string]string {
+	ret := map[string]string{}
+	for _, partition := range partitions {
+		ret[generatedModuleNameForPartition(config, partition)] = partition
+	}
+	return ret
+}
+
+type depCandidateProps struct {
+	Namespace string
+	Multilib  string
+	Arch      []android.ArchType
+}
+
+// Map of module name to depCandidateProps
+type multilibDeps map[string]*depCandidateProps
+
+// Information necessary to generate the filesystem modules, including details about their
+// dependencies
+type FsGenState struct {
+	// List of modules in `PRODUCT_PACKAGES` and `PRODUCT_PACKAGES_DEBUG`
+	depCandidates []string
+	// Map of names of partition to the information of modules to be added as deps
+	fsDeps map[string]*multilibDeps
+	// List of name of partitions to be generated by the filesystem_creator module
+	soongGeneratedPartitions []string
+	// Mutex to protect the fsDeps
+	fsDepsMutex sync.Mutex
+	// Map of _all_ soong module names to their corresponding installation properties
+	moduleToInstallationProps map[string]installationProperties
+	// List of prebuilt_* modules that are autogenerated.
+	generatedPrebuiltEtcModuleNames []string
+	// Mapping from a path to an avb key to the name of a filegroup module that contains it
+	avbKeyFilegroups map[string]string
+}
+
+type installationProperties struct {
+	Required  []string
+	Overrides []string
+}
+
+func defaultDepCandidateProps(config android.Config) *depCandidateProps {
+	return &depCandidateProps{
+		Namespace: ".",
+		Arch:      []android.ArchType{config.BuildArch},
+	}
+}
+
+func createFsGenState(ctx android.LoadHookContext, generatedPrebuiltEtcModuleNames []string, avbpubkeyGenerated bool) *FsGenState {
+	return ctx.Config().Once(fsGenStateOnceKey, func() interface{} {
+		partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+		candidates := android.FirstUniqueStrings(android.Concat(partitionVars.ProductPackages, partitionVars.ProductPackagesDebug))
+		candidates = android.Concat(candidates, generatedPrebuiltEtcModuleNames)
+
+		fsGenState := FsGenState{
+			depCandidates: candidates,
+			fsDeps: map[string]*multilibDeps{
+				// These additional deps are added according to the cuttlefish system image bp.
+				"system": {
+					// keep-sorted start
+					"com.android.apex.cts.shim.v1_prebuilt":     defaultDepCandidateProps(ctx.Config()),
+					"dex_bootjars":                              defaultDepCandidateProps(ctx.Config()),
+					"framework_compatibility_matrix.device.xml": defaultDepCandidateProps(ctx.Config()),
+					"init.environ.rc-soong":                     defaultDepCandidateProps(ctx.Config()),
+					"libcompiler_rt":                            defaultDepCandidateProps(ctx.Config()),
+					"libdmabufheap":                             defaultDepCandidateProps(ctx.Config()),
+					"libgsi":                                    defaultDepCandidateProps(ctx.Config()),
+					"llndk.libraries.txt":                       defaultDepCandidateProps(ctx.Config()),
+					"logpersist.start":                          defaultDepCandidateProps(ctx.Config()),
+					"update_engine_sideload":                    defaultDepCandidateProps(ctx.Config()),
+					// keep-sorted end
+				},
+				"vendor": {
+					"fs_config_files_vendor":                               defaultDepCandidateProps(ctx.Config()),
+					"fs_config_dirs_vendor":                                defaultDepCandidateProps(ctx.Config()),
+					generatedModuleName(ctx.Config(), "vendor-build.prop"): defaultDepCandidateProps(ctx.Config()),
+				},
+				"odm": {
+					// fs_config_* files are automatically installed for all products with odm partitions.
+					// https://cs.android.com/android/_/android/platform/build/+/e4849e87ab660b59a6501b3928693db065ee873b:tools/fs_config/Android.mk;l=34;drc=8d6481b92c4b4e9b9f31a61545b6862090fcc14b;bpv=1;bpt=0
+					"fs_config_files_odm": defaultDepCandidateProps(ctx.Config()),
+					"fs_config_dirs_odm":  defaultDepCandidateProps(ctx.Config()),
+				},
+				"product": {},
+				"system_ext": {
+					// VNDK apexes are automatically included.
+					// This hardcoded list will need to be updated if `PRODUCT_EXTRA_VNDK_VERSIONS` is updated.
+					// https://cs.android.com/android/_/android/platform/build/+/adba533072b00c53ac0f198c550a3cbd7a00e4cd:core/main.mk;l=984;bpv=1;bpt=0;drc=174db7b179592cf07cbfd2adb0119486fda911e7
+					"com.android.vndk.v30": defaultDepCandidateProps(ctx.Config()),
+					"com.android.vndk.v31": defaultDepCandidateProps(ctx.Config()),
+					"com.android.vndk.v32": defaultDepCandidateProps(ctx.Config()),
+					"com.android.vndk.v33": defaultDepCandidateProps(ctx.Config()),
+					"com.android.vndk.v34": defaultDepCandidateProps(ctx.Config()),
+				},
+				"userdata": {},
+				"system_dlkm": {
+					// these are phony required deps of the phony fs_config_dirs_nonsystem
+					"fs_config_dirs_system_dlkm":  defaultDepCandidateProps(ctx.Config()),
+					"fs_config_files_system_dlkm": defaultDepCandidateProps(ctx.Config()),
+					// build props are automatically added to `ALL_DEFAULT_INSTALLED_MODULES`
+					"system_dlkm-build.prop": defaultDepCandidateProps(ctx.Config()),
+				},
+				"vendor_dlkm": {
+					"fs_config_dirs_vendor_dlkm":  defaultDepCandidateProps(ctx.Config()),
+					"fs_config_files_vendor_dlkm": defaultDepCandidateProps(ctx.Config()),
+					"vendor_dlkm-build.prop":      defaultDepCandidateProps(ctx.Config()),
+				},
+				"odm_dlkm": {
+					"fs_config_dirs_odm_dlkm":  defaultDepCandidateProps(ctx.Config()),
+					"fs_config_files_odm_dlkm": defaultDepCandidateProps(ctx.Config()),
+					"odm_dlkm-build.prop":      defaultDepCandidateProps(ctx.Config()),
+				},
+				"ramdisk":        {},
+				"vendor_ramdisk": {},
+				"recovery":       {},
+			},
+			fsDepsMutex:                     sync.Mutex{},
+			moduleToInstallationProps:       map[string]installationProperties{},
+			generatedPrebuiltEtcModuleNames: generatedPrebuiltEtcModuleNames,
+			avbKeyFilegroups:                map[string]string{},
+		}
+
+		if avbpubkeyGenerated {
+			(*fsGenState.fsDeps["product"])["system_other_avbpubkey"] = defaultDepCandidateProps(ctx.Config())
+		}
+
+		// Add common resources `prebuilt_res` module as dep of recovery partition
+		(*fsGenState.fsDeps["recovery"])[fmt.Sprintf("recovery-resources-common-%s", getDpi(ctx))] = defaultDepCandidateProps(ctx.Config())
+
+		return &fsGenState
+	}).(*FsGenState)
+}
+
+func checkDepModuleInMultipleNamespaces(mctx android.BottomUpMutatorContext, foundDeps multilibDeps, module string, partitionName string) {
+	otherNamespace := mctx.Namespace().Path
+	if val, found := foundDeps[module]; found && otherNamespace != "." && !android.InList(val.Namespace, []string{".", otherNamespace}) {
+		mctx.ModuleErrorf("found in multiple namespaces(%s and %s) when including in %s partition", val.Namespace, otherNamespace, partitionName)
+	}
+}
+
+func appendDepIfAppropriate(mctx android.BottomUpMutatorContext, deps *multilibDeps, installPartition string) {
+	moduleName := mctx.ModuleName()
+	checkDepModuleInMultipleNamespaces(mctx, *deps, moduleName, installPartition)
+	if _, ok := (*deps)[moduleName]; ok {
+		// Prefer the namespace-specific module over the platform module
+		if mctx.Namespace().Path != "." {
+			(*deps)[moduleName].Namespace = mctx.Namespace().Path
+		}
+		(*deps)[moduleName].Arch = append((*deps)[moduleName].Arch, mctx.Module().Target().Arch.ArchType)
+	} else {
+		multilib, _ := mctx.Module().DecodeMultilib(mctx)
+		(*deps)[moduleName] = &depCandidateProps{
+			Namespace: mctx.Namespace().Path,
+			Multilib:  multilib,
+			Arch:      []android.ArchType{mctx.Module().Target().Arch.ArchType},
+		}
+	}
+}
+
+func collectDepsMutator(mctx android.BottomUpMutatorContext) {
+	m := mctx.Module()
+	if m.Target().Os.Class != android.Device {
+		return
+	}
+	fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+
+	fsGenState.fsDepsMutex.Lock()
+	defer fsGenState.fsDepsMutex.Unlock()
+
+	if slices.Contains(fsGenState.depCandidates, mctx.ModuleName()) {
+		installPartition := m.PartitionTag(mctx.DeviceConfig())
+		// Only add the module as dependency when:
+		// - its enabled
+		// - its namespace is included in PRODUCT_SOONG_NAMESPACES
+		if m.Enabled(mctx) && m.ExportedToMake() {
+			appendDepIfAppropriate(mctx, fsGenState.fsDeps[installPartition], installPartition)
+		}
+	}
+	// store the map of module to (required,overrides) even if the module is not in PRODUCT_PACKAGES.
+	// the module might be installed transitively.
+	if m.Enabled(mctx) && m.ExportedToMake() {
+		fsGenState.moduleToInstallationProps[m.Name()] = installationProperties{
+			Required:  m.RequiredModuleNames(mctx),
+			Overrides: m.Overrides(),
+		}
+	}
+}
+
+type depsStruct struct {
+	Deps []string
+}
+
+type multilibDepsStruct struct {
+	Common   depsStruct
+	Lib32    depsStruct
+	Lib64    depsStruct
+	Both     depsStruct
+	Prefer32 depsStruct
+}
+
+type packagingPropsStruct struct {
+	High_priority_deps []string
+	Deps               []string
+	Multilib           multilibDepsStruct
+}
+
+func fullyQualifiedModuleName(moduleName, namespace string) string {
+	if namespace == "." {
+		return moduleName
+	}
+	return fmt.Sprintf("//%s:%s", namespace, moduleName)
+}
+
+func getBitness(archTypes []android.ArchType) (ret []string) {
+	for _, archType := range archTypes {
+		if archType.Multilib == "" {
+			ret = append(ret, android.COMMON_VARIANT)
+		} else {
+			ret = append(ret, archType.Bitness())
+		}
+	}
+	return ret
+}
+
+func setDepsMutator(mctx android.BottomUpMutatorContext) {
+	removeOverriddenDeps(mctx)
+	fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+	fsDeps := fsGenState.fsDeps
+	soongGeneratedPartitionMap := getAllSoongGeneratedPartitionNames(mctx.Config(), fsGenState.soongGeneratedPartitions)
+	m := mctx.Module()
+	if partition, ok := soongGeneratedPartitionMap[m.Name()]; ok {
+		depsStruct := generateDepStruct(*fsDeps[partition], fsGenState.generatedPrebuiltEtcModuleNames)
+		if err := proptools.AppendMatchingProperties(m.GetProperties(), depsStruct, nil); err != nil {
+			mctx.ModuleErrorf(err.Error())
+		}
+	}
+}
+
+// removeOverriddenDeps collects PRODUCT_PACKAGES and (transitive) required deps.
+// it then removes any modules which appear in `overrides` of the above list.
+func removeOverriddenDeps(mctx android.BottomUpMutatorContext) {
+	mctx.Config().Once(fsGenRemoveOverridesOnceKey, func() interface{} {
+		fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+		fsDeps := fsGenState.fsDeps
+		overridden := map[string]bool{}
+		allDeps := []string{}
+
+		// Step 1: Initialization: Append PRODUCT_PACKAGES to the queue
+		for _, fsDep := range fsDeps {
+			for depName, _ := range *fsDep {
+				allDeps = append(allDeps, depName)
+			}
+		}
+
+		// Step 2: Process the queue, and add required modules to the queue.
+		i := 0
+		for {
+			if i == len(allDeps) {
+				break
+			}
+			depName := allDeps[i]
+			for _, overrides := range fsGenState.moduleToInstallationProps[depName].Overrides {
+				overridden[overrides] = true
+			}
+			// add required dep to the queue.
+			allDeps = append(allDeps, fsGenState.moduleToInstallationProps[depName].Required...)
+			i += 1
+		}
+
+		// Step 3: Delete all the overridden modules.
+		for overridden, _ := range overridden {
+			for partition, _ := range fsDeps {
+				delete(*fsDeps[partition], overridden)
+			}
+		}
+		return nil
+	})
+}
+
+var HighPriorityDeps = []string{}
+
+func isHighPriorityDep(depName string) bool {
+	for _, highPriorityDeps := range HighPriorityDeps {
+		if strings.HasPrefix(depName, highPriorityDeps) {
+			return true
+		}
+	}
+	return false
+}
+
+func generateDepStruct(deps map[string]*depCandidateProps, highPriorityDeps []string) *packagingPropsStruct {
+	depsStruct := packagingPropsStruct{}
+	for depName, depProps := range deps {
+		bitness := getBitness(depProps.Arch)
+		fullyQualifiedDepName := fullyQualifiedModuleName(depName, depProps.Namespace)
+		if android.InList(depName, highPriorityDeps) {
+			depsStruct.High_priority_deps = append(depsStruct.High_priority_deps, fullyQualifiedDepName)
+		} else if android.InList("32", bitness) && android.InList("64", bitness) {
+			// If both 32 and 64 bit variants are enabled for this module
+			switch depProps.Multilib {
+			case string(android.MultilibBoth):
+				depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
+			case string(android.MultilibCommon), string(android.MultilibFirst):
+				depsStruct.Deps = append(depsStruct.Deps, fullyQualifiedDepName)
+			case "32":
+				depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
+			case "64", "darwin_universal":
+				depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
+			case "prefer32", "first_prefer32":
+				depsStruct.Multilib.Prefer32.Deps = append(depsStruct.Multilib.Prefer32.Deps, fullyQualifiedDepName)
+			default:
+				depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
+			}
+		} else if android.InList("64", bitness) {
+			// If only 64 bit variant is enabled
+			depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
+		} else if android.InList("32", bitness) {
+			// If only 32 bit variant is enabled
+			depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
+		} else {
+			// If only common variant is enabled
+			depsStruct.Multilib.Common.Deps = append(depsStruct.Multilib.Common.Deps, fullyQualifiedDepName)
+		}
+	}
+	depsStruct.Deps = android.SortedUniqueStrings(depsStruct.Deps)
+	depsStruct.Multilib.Lib32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib32.Deps)
+	depsStruct.Multilib.Lib64.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib64.Deps)
+	depsStruct.Multilib.Prefer32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Prefer32.Deps)
+	depsStruct.Multilib.Both.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Both.Deps)
+	depsStruct.Multilib.Common.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Common.Deps)
+	depsStruct.High_priority_deps = android.SortedUniqueStrings(depsStruct.High_priority_deps)
+
+	return &depsStruct
+}
diff --git a/fsgen/prebuilt_etc_modules_gen.go b/fsgen/prebuilt_etc_modules_gen.go
new file mode 100644
index 0000000..e028b1d
--- /dev/null
+++ b/fsgen/prebuilt_etc_modules_gen.go
@@ -0,0 +1,412 @@
+// 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.
+
+package fsgen
+
+import (
+	"android/soong/android"
+	"android/soong/etc"
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+type srcBaseFileInstallBaseFileTuple struct {
+	srcBaseFile     string
+	installBaseFile string
+}
+
+// prebuilt src files grouped by the install partitions.
+// Each groups are a mapping of the relative install path to the name of the files
+type prebuiltSrcGroupByInstallPartition struct {
+	system     map[string][]srcBaseFileInstallBaseFileTuple
+	system_ext map[string][]srcBaseFileInstallBaseFileTuple
+	product    map[string][]srcBaseFileInstallBaseFileTuple
+	vendor     map[string][]srcBaseFileInstallBaseFileTuple
+	recovery   map[string][]srcBaseFileInstallBaseFileTuple
+}
+
+func newPrebuiltSrcGroupByInstallPartition() *prebuiltSrcGroupByInstallPartition {
+	return &prebuiltSrcGroupByInstallPartition{
+		system:     map[string][]srcBaseFileInstallBaseFileTuple{},
+		system_ext: map[string][]srcBaseFileInstallBaseFileTuple{},
+		product:    map[string][]srcBaseFileInstallBaseFileTuple{},
+		vendor:     map[string][]srcBaseFileInstallBaseFileTuple{},
+		recovery:   map[string][]srcBaseFileInstallBaseFileTuple{},
+	}
+}
+
+func isSubdirectory(parent, child string) bool {
+	rel, err := filepath.Rel(parent, child)
+	if err != nil {
+		return false
+	}
+	return !strings.HasPrefix(rel, "..")
+}
+
+func appendIfCorrectInstallPartition(partitionToInstallPathList []partitionToInstallPath, destPath, srcPath string, srcGroup *prebuiltSrcGroupByInstallPartition) {
+	for _, part := range partitionToInstallPathList {
+		partition := part.name
+		installPath := part.installPath
+
+		if isSubdirectory(installPath, destPath) {
+			relativeInstallPath, _ := filepath.Rel(installPath, destPath)
+			relativeInstallDir := filepath.Dir(relativeInstallPath)
+			var srcMap map[string][]srcBaseFileInstallBaseFileTuple
+			switch partition {
+			case "system":
+				srcMap = srcGroup.system
+			case "system_ext":
+				srcMap = srcGroup.system_ext
+			case "product":
+				srcMap = srcGroup.product
+			case "vendor":
+				srcMap = srcGroup.vendor
+			case "recovery":
+				srcMap = srcGroup.recovery
+			}
+			if srcMap != nil {
+				srcMap[relativeInstallDir] = append(srcMap[relativeInstallDir], srcBaseFileInstallBaseFileTuple{
+					srcBaseFile:     filepath.Base(srcPath),
+					installBaseFile: filepath.Base(destPath),
+				})
+			}
+			return
+		}
+	}
+}
+
+// Create a map of source files to the list of destination files from PRODUCT_COPY_FILES entries.
+// Note that the value of the map is a list of string, given that a single source file can be
+// copied to multiple files.
+// This function also checks the existence of the source files, and validates that there is no
+// multiple source files copying to the same dest file.
+func uniqueExistingProductCopyFileMap(ctx android.LoadHookContext) map[string][]string {
+	seen := make(map[string]bool)
+	filtered := make(map[string][]string)
+
+	for _, copyFilePair := range ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ProductCopyFiles {
+		srcDestList := strings.Split(copyFilePair, ":")
+		if len(srcDestList) < 2 {
+			ctx.ModuleErrorf("PRODUCT_COPY_FILES must follow the format \"src:dest\", got: %s", copyFilePair)
+		}
+		src, dest := srcDestList[0], srcDestList[1]
+
+		// Some downstream branches use absolute path as entries in PRODUCT_COPY_FILES.
+		// Convert them to relative path from top and check if they do not escape the tree root.
+		relSrc := android.ToRelativeSourcePath(ctx, src)
+
+		if _, ok := seen[dest]; !ok {
+			if optionalPath := android.ExistentPathForSource(ctx, relSrc); optionalPath.Valid() {
+				seen[dest] = true
+				filtered[relSrc] = append(filtered[relSrc], dest)
+			}
+		}
+	}
+
+	return filtered
+}
+
+type partitionToInstallPath struct {
+	name        string
+	installPath string
+}
+
+func processProductCopyFiles(ctx android.LoadHookContext) map[string]*prebuiltSrcGroupByInstallPartition {
+	// Filter out duplicate dest entries and non existing src entries
+	productCopyFileMap := uniqueExistingProductCopyFileMap(ctx)
+
+	// System is intentionally added at the last to consider the scenarios where
+	// non-system partitions are installed as part of the system partition
+	partitionToInstallPathList := []partitionToInstallPath{
+		{name: "recovery", installPath: "recovery/root"},
+		{name: "vendor", installPath: ctx.DeviceConfig().VendorPath()},
+		{name: "product", installPath: ctx.DeviceConfig().ProductPath()},
+		{name: "system_ext", installPath: ctx.DeviceConfig().SystemExtPath()},
+		{name: "system", installPath: "system"},
+	}
+
+	groupedSources := map[string]*prebuiltSrcGroupByInstallPartition{}
+	for _, src := range android.SortedKeys(productCopyFileMap) {
+		destFiles := productCopyFileMap[src]
+		srcFileDir := filepath.Dir(src)
+		if _, ok := groupedSources[srcFileDir]; !ok {
+			groupedSources[srcFileDir] = newPrebuiltSrcGroupByInstallPartition()
+		}
+		for _, dest := range destFiles {
+			appendIfCorrectInstallPartition(partitionToInstallPathList, dest, filepath.Base(src), groupedSources[srcFileDir])
+		}
+	}
+
+	return groupedSources
+}
+
+type prebuiltModuleProperties struct {
+	Name *string
+
+	Soc_specific        *bool
+	Product_specific    *bool
+	System_ext_specific *bool
+	Recovery            *bool
+	Ramdisk             *bool
+
+	Srcs []string
+	Dsts []string
+
+	No_full_install *bool
+
+	NamespaceExportedToMake bool
+
+	Visibility []string
+}
+
+// Split relative_install_path to a separate struct, because it is not supported for every
+// modules listed in [etcInstallPathToFactoryMap]
+type prebuiltSubdirProperties struct {
+	// If the base file name of the src and dst all match, dsts property does not need to be
+	// set, and only relative_install_path can be set.
+	Relative_install_path *string
+}
+
+// Split install_in_root to a separate struct as it is part of rootProperties instead of
+// properties
+type prebuiltInstallInRootProperties struct {
+	Install_in_root *bool
+}
+
+var (
+	etcInstallPathToFactoryList = map[string]android.ModuleFactory{
+		"":                    etc.PrebuiltRootFactory,
+		"avb":                 etc.PrebuiltAvbFactory,
+		"bin":                 etc.PrebuiltBinaryFactory,
+		"bt_firmware":         etc.PrebuiltBtFirmwareFactory,
+		"cacerts":             etc.PrebuiltEtcCaCertsFactory,
+		"dsp":                 etc.PrebuiltDSPFactory,
+		"etc":                 etc.PrebuiltEtcFactory,
+		"etc/dsp":             etc.PrebuiltDSPFactory,
+		"etc/firmware":        etc.PrebuiltFirmwareFactory,
+		"firmware":            etc.PrebuiltFirmwareFactory,
+		"first_stage_ramdisk": etc.PrebuiltFirstStageRamdiskFactory,
+		"fonts":               etc.PrebuiltFontFactory,
+		"framework":           etc.PrebuiltFrameworkFactory,
+		"lib":                 etc.PrebuiltRenderScriptBitcodeFactory,
+		"lib64":               etc.PrebuiltRenderScriptBitcodeFactory,
+		"lib/rfsa":            etc.PrebuiltRFSAFactory,
+		"media":               etc.PrebuiltMediaFactory,
+		"odm":                 etc.PrebuiltOdmFactory,
+		"optee":               etc.PrebuiltOpteeFactory,
+		"overlay":             etc.PrebuiltOverlayFactory,
+		"priv-app":            etc.PrebuiltPrivAppFactory,
+		"sbin":                etc.PrebuiltSbinFactory,
+		"system":              etc.PrebuiltSystemFactory,
+		"res":                 etc.PrebuiltResFactory,
+		"rfs":                 etc.PrebuiltRfsFactory,
+		"tts":                 etc.PrebuiltVoicepackFactory,
+		"tvconfig":            etc.PrebuiltTvConfigFactory,
+		"tvservice":           etc.PrebuiltTvServiceFactory,
+		"usr/share":           etc.PrebuiltUserShareFactory,
+		"usr/hyphen-data":     etc.PrebuiltUserHyphenDataFactory,
+		"usr/keylayout":       etc.PrebuiltUserKeyLayoutFactory,
+		"usr/keychars":        etc.PrebuiltUserKeyCharsFactory,
+		"usr/srec":            etc.PrebuiltUserSrecFactory,
+		"usr/idc":             etc.PrebuiltUserIdcFactory,
+		"vendor":              etc.PrebuiltVendorFactory,
+		"vendor_dlkm":         etc.PrebuiltVendorDlkmFactory,
+		"wallpaper":           etc.PrebuiltWallpaperFactory,
+		"wlc_upt":             etc.PrebuiltWlcUptFactory,
+	}
+)
+
+func generatedPrebuiltEtcModuleName(partition, srcDir, destDir string, count int) string {
+	// generated module name follows the pattern:
+	// <install partition>-<src file path>-<relative install path from partition root>-<number>
+	// Note that all path separators are replaced with "_" in the name
+	moduleName := partition
+	if !android.InList(srcDir, []string{"", "."}) {
+		moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(srcDir, string(filepath.Separator), "_"))
+	}
+	if !android.InList(destDir, []string{"", "."}) {
+		moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(destDir, string(filepath.Separator), "_"))
+	}
+	moduleName += fmt.Sprintf("-%d", count)
+
+	return moduleName
+}
+
+func groupDestFilesBySrc(destFiles []srcBaseFileInstallBaseFileTuple) (ret map[string][]srcBaseFileInstallBaseFileTuple, maxLen int) {
+	ret = map[string][]srcBaseFileInstallBaseFileTuple{}
+	maxLen = 0
+	for _, tuple := range destFiles {
+		if _, ok := ret[tuple.srcBaseFile]; !ok {
+			ret[tuple.srcBaseFile] = []srcBaseFileInstallBaseFileTuple{}
+		}
+		ret[tuple.srcBaseFile] = append(ret[tuple.srcBaseFile], tuple)
+		maxLen = max(maxLen, len(ret[tuple.srcBaseFile]))
+	}
+	return ret, maxLen
+}
+
+func prebuiltEtcModuleProps(ctx android.LoadHookContext, moduleName, partition, destDir string) prebuiltModuleProperties {
+	moduleProps := prebuiltModuleProperties{}
+	moduleProps.Name = proptools.StringPtr(moduleName)
+
+	// Set partition specific properties
+	switch partition {
+	case "system_ext":
+		moduleProps.System_ext_specific = proptools.BoolPtr(true)
+	case "product":
+		moduleProps.Product_specific = proptools.BoolPtr(true)
+	case "vendor":
+		moduleProps.Soc_specific = proptools.BoolPtr(true)
+	case "recovery":
+		// To match the logic in modulePartition() in android/paths.go
+		if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() && strings.HasPrefix(destDir, "first_stage_ramdisk") {
+			moduleProps.Ramdisk = proptools.BoolPtr(true)
+		} else {
+			moduleProps.Recovery = proptools.BoolPtr(true)
+		}
+	}
+
+	moduleProps.No_full_install = proptools.BoolPtr(true)
+	moduleProps.NamespaceExportedToMake = true
+	moduleProps.Visibility = []string{"//visibility:public"}
+
+	return moduleProps
+}
+
+func createPrebuiltEtcModulesInDirectory(ctx android.LoadHookContext, partition, srcDir, destDir string, destFiles []srcBaseFileInstallBaseFileTuple) (moduleNames []string) {
+	groupedDestFiles, maxLen := groupDestFilesBySrc(destFiles)
+
+	// Find out the most appropriate module type to generate
+	var etcInstallPathKey string
+	for _, etcInstallPath := range android.SortedKeys(etcInstallPathToFactoryList) {
+		// Do not break when found but iterate until the end to find a module with more
+		// specific install path
+		if strings.HasPrefix(destDir, etcInstallPath) {
+			etcInstallPathKey = etcInstallPath
+		}
+	}
+	relDestDirFromInstallDirBase, _ := filepath.Rel(etcInstallPathKey, destDir)
+
+	for fileIndex := range maxLen {
+		srcTuple := []srcBaseFileInstallBaseFileTuple{}
+		for _, srcFile := range android.SortedKeys(groupedDestFiles) {
+			groupedDestFile := groupedDestFiles[srcFile]
+			if len(groupedDestFile) > fileIndex {
+				srcTuple = append(srcTuple, groupedDestFile[fileIndex])
+			}
+		}
+
+		moduleName := generatedPrebuiltEtcModuleName(partition, srcDir, destDir, fileIndex)
+		moduleProps := prebuiltEtcModuleProps(ctx, moduleName, partition, destDir)
+		modulePropsPtr := &moduleProps
+		propsList := []interface{}{modulePropsPtr}
+
+		allCopyFileNamesUnchanged := true
+		var srcBaseFiles, installBaseFiles []string
+		for _, tuple := range srcTuple {
+			if tuple.srcBaseFile != tuple.installBaseFile {
+				allCopyFileNamesUnchanged = false
+			}
+			srcBaseFiles = append(srcBaseFiles, tuple.srcBaseFile)
+			installBaseFiles = append(installBaseFiles, tuple.installBaseFile)
+		}
+
+		// Recovery partition-installed modules are installed to `recovery/root/system` by
+		// default (See modulePartition() in android/paths.go). If the destination file
+		// directory is not `recovery/root/system/...`, it should set install_in_root to true
+		// to prevent being installed in `recovery/root/system`.
+		if partition == "recovery" && !strings.HasPrefix(destDir, "system") {
+			propsList = append(propsList, &prebuiltInstallInRootProperties{
+				Install_in_root: proptools.BoolPtr(true),
+			})
+		}
+
+		// Set appropriate srcs, dsts, and releative_install_path based on
+		// the source and install file names
+		if allCopyFileNamesUnchanged {
+			modulePropsPtr.Srcs = srcBaseFiles
+
+			// Specify relative_install_path if it is not installed in the root directory of the
+			// partition
+			if !android.InList(relDestDirFromInstallDirBase, []string{"", "."}) {
+				propsList = append(propsList, &prebuiltSubdirProperties{
+					Relative_install_path: proptools.StringPtr(relDestDirFromInstallDirBase),
+				})
+			}
+		} else {
+			modulePropsPtr.Srcs = srcBaseFiles
+			dsts := []string{}
+			for _, installBaseFile := range installBaseFiles {
+				dsts = append(dsts, filepath.Join(relDestDirFromInstallDirBase, installBaseFile))
+			}
+			modulePropsPtr.Dsts = dsts
+		}
+
+		ctx.CreateModuleInDirectory(etcInstallPathToFactoryList[etcInstallPathKey], srcDir, propsList...)
+		moduleNames = append(moduleNames, moduleName)
+	}
+
+	return moduleNames
+}
+
+func createPrebuiltEtcModulesForPartition(ctx android.LoadHookContext, partition, srcDir string, destDirFilesMap map[string][]srcBaseFileInstallBaseFileTuple) (ret []string) {
+	for _, destDir := range android.SortedKeys(destDirFilesMap) {
+		ret = append(ret, createPrebuiltEtcModulesInDirectory(ctx, partition, srcDir, destDir, destDirFilesMap[destDir])...)
+	}
+	return ret
+}
+
+// Creates prebuilt_* modules based on the install paths and returns the list of generated
+// module names
+func createPrebuiltEtcModules(ctx android.LoadHookContext) (ret []string) {
+	groupedSources := processProductCopyFiles(ctx)
+	for _, srcDir := range android.SortedKeys(groupedSources) {
+		groupedSource := groupedSources[srcDir]
+		ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "system", srcDir, groupedSource.system)...)
+		ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "system_ext", srcDir, groupedSource.system_ext)...)
+		ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "product", srcDir, groupedSource.product)...)
+		ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "vendor", srcDir, groupedSource.vendor)...)
+		ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "recovery", srcDir, groupedSource.recovery)...)
+	}
+
+	return ret
+}
+
+func createAvbpubkeyModule(ctx android.LoadHookContext) bool {
+	avbKeyPath := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BoardAvbKeyPath
+	if avbKeyPath == "" {
+		return false
+	}
+	ctx.CreateModuleInDirectory(
+		etc.AvbpubkeyModuleFactory,
+		".",
+		&struct {
+			Name             *string
+			Product_specific *bool
+			Private_key      *string
+			No_full_install  *bool
+			Visibility       []string
+		}{
+			Name:             proptools.StringPtr("system_other_avbpubkey"),
+			Product_specific: proptools.BoolPtr(true),
+			Private_key:      proptools.StringPtr(avbKeyPath),
+			No_full_install:  proptools.BoolPtr(true),
+			Visibility:       []string{"//visibility:public"},
+		},
+	)
+	return true
+}
diff --git a/fsgen/super_img.go b/fsgen/super_img.go
new file mode 100644
index 0000000..8ee3bf2
--- /dev/null
+++ b/fsgen/super_img.go
@@ -0,0 +1,91 @@
+// 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.
+
+package fsgen
+
+import (
+	"strconv"
+
+	"android/soong/android"
+	"android/soong/filesystem"
+	"github.com/google/blueprint/proptools"
+)
+
+func buildingSuperImage(partitionVars android.PartitionVariables) bool {
+	return partitionVars.ProductBuildSuperPartition
+}
+
+func createSuperImage(ctx android.LoadHookContext, partitions []string, partitionVars android.PartitionVariables) {
+	baseProps := &struct {
+		Name *string
+	}{
+		Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "super")),
+	}
+
+	superImageProps := &filesystem.SuperImageProperties{
+		Metadata_device:        proptools.StringPtr(partitionVars.BoardSuperPartitionMetadataDevice),
+		Block_devices:          partitionVars.BoardSuperPartitionBlockDevices,
+		Ab_update:              proptools.BoolPtr(partitionVars.AbOtaUpdater),
+		Retrofit:               proptools.BoolPtr(partitionVars.ProductRetrofitDynamicPartitions),
+		Virtual_ab:             proptools.BoolPtr(partitionVars.ProductVirtualAbOta),
+		Virtual_ab_retrofit:    proptools.BoolPtr(partitionVars.ProductVirtualAbOtaRetrofit),
+		Use_dynamic_partitions: proptools.BoolPtr(partitionVars.ProductUseDynamicPartitions),
+	}
+	size, _ := strconv.ParseInt(partitionVars.BoardSuperPartitionSize, 10, 64)
+	superImageProps.Size = proptools.Int64Ptr(size)
+	sparse := !partitionVars.TargetUserimagesSparseExtDisabled && !partitionVars.TargetUserimagesSparseF2fsDisabled
+	superImageProps.Sparse = proptools.BoolPtr(sparse)
+
+	var partitionGroupsInfo []filesystem.PartitionGroupsInfo
+	for _, groupName := range android.SortedKeys(partitionVars.BoardSuperPartitionGroups) {
+		info := filesystem.PartitionGroupsInfo{
+			Name:          groupName,
+			GroupSize:     partitionVars.BoardSuperPartitionGroups[groupName].GroupSize,
+			PartitionList: partitionVars.BoardSuperPartitionGroups[groupName].PartitionList,
+		}
+		partitionGroupsInfo = append(partitionGroupsInfo, info)
+	}
+	superImageProps.Partition_groups = partitionGroupsInfo
+
+	partitionNameProps := &filesystem.SuperImagePartitionNameProperties{}
+	if android.InList("system", partitions) {
+		partitionNameProps.System_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
+	}
+	if android.InList("system_ext", partitions) {
+		partitionNameProps.System_ext_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
+	}
+	if android.InList("system_dlkm", partitions) {
+		partitionNameProps.System_dlkm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_dlkm"))
+	}
+	if android.InList("system_other", partitions) {
+		partitionNameProps.System_other_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_other"))
+	}
+	if android.InList("product", partitions) {
+		partitionNameProps.Product_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
+	}
+	if android.InList("vendor", partitions) {
+		partitionNameProps.Vendor_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
+	}
+	if android.InList("vendor_dlkm", partitions) {
+		partitionNameProps.Vendor_dlkm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_dlkm"))
+	}
+	if android.InList("odm", partitions) {
+		partitionNameProps.Odm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
+	}
+	if android.InList("odm_dlkm", partitions) {
+		partitionNameProps.Odm_dlkm_partition = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm_dlkm"))
+	}
+
+	ctx.CreateModule(filesystem.SuperImageFactory, baseProps, superImageProps, partitionNameProps)
+}
diff --git a/fsgen/util.go b/fsgen/util.go
new file mode 100644
index 0000000..9ab3ad8
--- /dev/null
+++ b/fsgen/util.go
@@ -0,0 +1,60 @@
+// 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.
+
+package fsgen
+
+import (
+	"android/soong/android"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// Returns the appropriate dpi for recovery common resources selection. Replicates the logic in
+// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2536;drc=a6af369e71ded123734523ea640b97b70a557cb9
+func getDpi(ctx android.LoadHookContext) string {
+	recoveryDensity := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.TargetScreenDensity
+	if len(recoveryDensity) == 0 {
+		aaptPreferredConfig := ctx.Config().ProductAAPTPreferredConfig()
+		if len(aaptPreferredConfig) > 0 {
+			recoveryDensity = aaptPreferredConfig
+		} else {
+			recoveryDensity = "mdpi"
+		}
+	}
+	if !android.InList(recoveryDensity, []string{"xxxhdpi", "xxhdpi", "xhdpi", "hdpi", "mdpi"}) {
+		recoveryDensity = strings.TrimSuffix(recoveryDensity, "dpi")
+		dpiInt, err := strconv.ParseInt(recoveryDensity, 10, 64)
+		if err != nil {
+			panic(fmt.Sprintf("Error in parsing recoveryDensity: %s", err.Error()))
+		}
+		if dpiInt >= 560 {
+			recoveryDensity = "xxxhdpi"
+		} else if dpiInt >= 400 {
+			recoveryDensity = "xxhdpi"
+		} else if dpiInt >= 280 {
+			recoveryDensity = "xhdpi"
+		} else if dpiInt >= 200 {
+			recoveryDensity = "hdpi"
+		} else {
+			recoveryDensity = "mdpi"
+		}
+	}
+
+	if p := android.ExistentPathForSource(ctx, fmt.Sprintf("bootable/recovery/res-%s", recoveryDensity)); !p.Valid() {
+		recoveryDensity = "xhdpi"
+	}
+
+	return recoveryDensity
+}
diff --git a/fsgen/vbmeta_partitions.go b/fsgen/vbmeta_partitions.go
new file mode 100644
index 0000000..11c5759
--- /dev/null
+++ b/fsgen/vbmeta_partitions.go
@@ -0,0 +1,188 @@
+// 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.
+
+package fsgen
+
+import (
+	"android/soong/android"
+	"android/soong/filesystem"
+	"slices"
+	"strconv"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+type vbmetaModuleInfo struct {
+	// The name of the generated vbmeta module
+	moduleName string
+	// The name of the module that avb understands. This is the name passed to --chain_partition,
+	// and also the basename of the output file. (the output file is called partitionName + ".img")
+	partitionName string
+}
+
+// Creates the vbmeta partition and the chained vbmeta partitions. Returns the list of module names
+// that the function created. May return nil if the product isn't using avb.
+//
+// AVB is Android Verified Boot: https://source.android.com/docs/security/features/verifiedboot
+// It works by signing all the partitions, but then also including an extra metadata paritition
+// called vbmeta that depends on all the other signed partitions. This creates a requirement
+// that you update all those partitions and the vbmeta partition together, so in order to relax
+// that requirement products can set up "chained" vbmeta partitions, where a chained partition
+// like vbmeta_system might contain the avb metadata for just a few products. In cuttlefish
+// vbmeta_system contains metadata about product, system, and system_ext. Using chained partitions,
+// that group of partitions can be updated independently from the other signed partitions.
+func createVbmetaPartitions(ctx android.LoadHookContext, generatedPartitionTypes []string) []vbmetaModuleInfo {
+	partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+	// Some products seem to have BuildingVbmetaImage as true even when BoardAvbEnable is false
+	if !partitionVars.BuildingVbmetaImage || !partitionVars.BoardAvbEnable {
+		return nil
+	}
+
+	var result []vbmetaModuleInfo
+
+	var chainedPartitions []string
+	var partitionTypesHandledByChainedPartitions []string
+	for _, chainedName := range android.SortedKeys(partitionVars.ChainedVbmetaPartitions) {
+		props := partitionVars.ChainedVbmetaPartitions[chainedName]
+		chainedName = "vbmeta_" + chainedName
+		if len(props.Partitions) == 0 {
+			continue
+		}
+		if len(props.Key) == 0 {
+			ctx.ModuleErrorf("No key found for chained avb partition %q", chainedName)
+			continue
+		}
+		if len(props.Algorithm) == 0 {
+			ctx.ModuleErrorf("No algorithm found for chained avb partition %q", chainedName)
+			continue
+		}
+		if len(props.RollbackIndex) == 0 {
+			ctx.ModuleErrorf("No rollback index found for chained avb partition %q", chainedName)
+			continue
+		}
+		ril, err := strconv.ParseInt(props.RollbackIndexLocation, 10, 32)
+		if err != nil {
+			ctx.ModuleErrorf("Rollback index location must be an int, got %q", props.RollbackIndexLocation)
+			continue
+		}
+		// The default is to use the PlatformSecurityPatch, and a lot of product config files
+		// just set it to the platform security patch, so detect that and don't set the property
+		// in soong.
+		var rollbackIndex *int64
+		if props.RollbackIndex != ctx.Config().PlatformSecurityPatch() {
+			i, err := strconv.ParseInt(props.RollbackIndex, 10, 32)
+			if err != nil {
+				ctx.ModuleErrorf("Rollback index must be an int, got %q", props.RollbackIndex)
+				continue
+			}
+			rollbackIndex = &i
+		}
+
+		var partitionModules []string
+		for _, partition := range props.Partitions {
+			partitionTypesHandledByChainedPartitions = append(partitionTypesHandledByChainedPartitions, partition)
+			if !slices.Contains(generatedPartitionTypes, partition) {
+				// The partition is probably unsupported.
+				continue
+			}
+			partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partition))
+		}
+
+		name := generatedModuleName(ctx.Config(), chainedName)
+		ctx.CreateModuleInDirectory(
+			filesystem.VbmetaFactory,
+			".", // Create in the root directory for now so its easy to get the key
+			&filesystem.VbmetaProperties{
+				Partition_name:          proptools.StringPtr(chainedName),
+				Stem:                    proptools.StringPtr(chainedName + ".img"),
+				Private_key:             proptools.StringPtr(props.Key),
+				Algorithm:               &props.Algorithm,
+				Rollback_index:          rollbackIndex,
+				Rollback_index_location: &ril,
+				Partitions:              proptools.NewSimpleConfigurable(partitionModules),
+			}, &struct {
+				Name *string
+			}{
+				Name: &name,
+			},
+		).HideFromMake()
+
+		chainedPartitions = append(chainedPartitions, name)
+
+		result = append(result, vbmetaModuleInfo{
+			moduleName:    name,
+			partitionName: chainedName,
+		})
+	}
+
+	vbmetaModuleName := generatedModuleName(ctx.Config(), "vbmeta")
+
+	var algorithm *string
+	var ri *int64
+	var key *string
+	if len(partitionVars.BoardAvbKeyPath) == 0 {
+		// Match make's defaults: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4568;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
+		key = proptools.StringPtr("external/avb/test/data/testkey_rsa4096.pem")
+		algorithm = proptools.StringPtr("SHA256_RSA4096")
+	} else {
+		key = proptools.StringPtr(partitionVars.BoardAvbKeyPath)
+		algorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm)
+	}
+	if len(partitionVars.BoardAvbRollbackIndex) > 0 {
+		parsedRi, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 32)
+		if err != nil {
+			ctx.ModuleErrorf("Rollback index location must be an int, got %q", partitionVars.BoardAvbRollbackIndex)
+		}
+		ri = &parsedRi
+	}
+
+	var partitionModules []string
+	for _, partitionType := range generatedPartitionTypes {
+		if slices.Contains(partitionTypesHandledByChainedPartitions, partitionType) {
+			// Already handled by a chained vbmeta partition
+			continue
+		}
+		if strings.Contains(partitionType, "ramdisk") || strings.Contains(partitionType, "boot") {
+			// ramdisk is never signed with avb information
+			// boot partitions just have the avb footer, and don't have a corresponding vbmeta
+			// partition.
+			continue
+		}
+		partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partitionType))
+	}
+
+	ctx.CreateModuleInDirectory(
+		filesystem.VbmetaFactory,
+		".", // Create in the root directory for now so its easy to get the key
+		&filesystem.VbmetaProperties{
+			Stem:               proptools.StringPtr("vbmeta.img"),
+			Algorithm:          algorithm,
+			Private_key:        key,
+			Rollback_index:     ri,
+			Chained_partitions: chainedPartitions,
+			Partitions:         proptools.NewSimpleConfigurable(partitionModules),
+		}, &struct {
+			Name *string
+		}{
+			Name: &vbmetaModuleName,
+		},
+	).HideFromMake()
+
+	result = append(result, vbmetaModuleInfo{
+		moduleName:    vbmetaModuleName,
+		partitionName: "vbmeta",
+	})
+	return result
+}
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index a059837..aa393a2 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -44,6 +44,32 @@
 	UnknownFramework Framework = "unknownframework"
 )
 
+func (f Framework) Variant() string {
+	switch f {
+	case AFL:
+		return "afl"
+	case LibFuzzer:
+		return "libfuzzer"
+	case Jazzer:
+		return "jazzer"
+	default:
+		panic(fmt.Errorf("unknown fuzzer %q when getting variant", f))
+	}
+}
+
+func FrameworkFromVariant(v string) Framework {
+	switch v {
+	case "afl":
+		return AFL
+	case "libfuzzer":
+		return LibFuzzer
+	case "jazzer":
+		return Jazzer
+	default:
+		panic(fmt.Errorf("unknown variant %q when getting fuzzer", v))
+	}
+}
+
 var BoolDefault = proptools.BoolDefault
 
 type FuzzModule struct {
@@ -385,6 +411,11 @@
 	// Optional list of seed files to be installed to the fuzz target's output
 	// directory.
 	Corpus []string `android:"path"`
+
+	// Same as corpus, but adds dependencies on module references using the device's os variant
+	// and the common arch variant.
+	Device_common_corpus []string `android:"path_device_common"`
+
 	// Optional list of data files to be installed to the fuzz target's output
 	// directory. Directory structure relative to the module is preserved.
 	Data []string `android:"path"`
diff --git a/genrule/Android.bp b/genrule/Android.bp
index f4197e6..49df480 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -25,3 +25,53 @@
     // Used by plugins
     visibility: ["//visibility:public"],
 }
+
+genrule {
+    name: "nsjail_genrule_test_input",
+    cmd: "echo nsjail_genrule_test_input > $(out)",
+    out: ["nsjail_genrule_test_input.txt"],
+}
+
+// Pseudo-test that's run on checkbuilds to verify consistent directory
+// structure for genrules using sbox or nsjail.
+genrule_defaults {
+    name: "nsjail_genrule_test_gen_defaults",
+    // verify both relative paths and its contents
+    cmd: "(echo $(out) $(genDir) && sha256sum " +
+        "$(location get_clang_version) " +
+        "$(location py3-cmd) " +
+        "$(location genrule.go) " +
+        "$(location :nsjail_genrule_test_input) " +
+        "$(locations *.go)) | sed 's@\\./@@g' > $(out)",
+    tools: [
+        "get_clang_version", // random tool
+        "py3-cmd", // random prebuilt tool
+    ],
+    tool_files: ["genrule.go"], // random local file
+    srcs: [
+        ":nsjail_genrule_test_input", // random OutputFileProducer
+        "*.go", // random glob
+    ],
+    out: ["nsjail_genrule_test.txt"],
+}
+
+genrule {
+    name: "nsjail_genrule_test_gen_without_nsjail",
+    defaults: ["nsjail_genrule_test_gen_defaults"],
+}
+
+genrule {
+    name: "nsjail_genrule_test_gen_with_nsjail",
+    defaults: ["nsjail_genrule_test_gen_defaults"],
+    use_nsjail: true,
+}
+
+genrule {
+    name: "nsjail_genrule_test",
+    srcs: [
+        ":nsjail_genrule_test_gen_without_nsjail",
+        ":nsjail_genrule_test_gen_with_nsjail",
+    ],
+    cmd: "diff $(in) > $(out)",
+    out: ["nsjail_genrule_test"],
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index a48038b..ac62b8d 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -21,6 +21,7 @@
 import (
 	"fmt"
 	"io"
+	"path/filepath"
 	"strconv"
 	"strings"
 
@@ -62,7 +63,7 @@
 	ctx.RegisterModuleType("genrule", GenRuleFactory)
 
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
+		ctx.BottomUp("genrule_tool_deps", toolDepsMutator)
 	})
 }
 
@@ -111,6 +112,12 @@
 	return target.IsReplacedByPrebuilt()
 }
 
+func (t hostToolDependencyTag) AllowDisabledModuleDependencyProxy(
+	ctx android.OtherModuleProviderContext, target android.ModuleProxy) bool {
+	return android.OtherModuleProviderOrDefault(
+		ctx, target, android.CommonModuleInfoKey).ReplacedByPrebuilt
+}
+
 var _ android.AllowDisabledModuleDependency = (*hostToolDependencyTag)(nil)
 
 type generatorProperties struct {
@@ -138,8 +145,19 @@
 	Export_include_dirs []string
 
 	// list of input files
-	Srcs         proptools.Configurable[[]string] `android:"path,arch_variant"`
-	ResolvedSrcs []string                         `blueprint:"mutated"`
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
+
+	// Same as srcs, but will add dependencies on modules via a device os variation and the device's
+	// first supported arch's variation. Can be used to add a dependency from a host genrule to
+	// a device module.
+	Device_first_srcs proptools.Configurable[[]string] `android:"path_device_first"`
+
+	// Same as srcs, but will add dependencies on modules via a device os variation and the common
+	// arch variation. Can be used to add a dependency from a host genrule to a device module.
+	Device_common_srcs proptools.Configurable[[]string] `android:"path_device_common"`
+
+	// Same as srcs, but will add dependencies on modules via a common_os os variation.
+	Common_os_srcs proptools.Configurable[[]string] `android:"path_common_os"`
 
 	// input files to exclude
 	Exclude_srcs []string `android:"path,arch_variant"`
@@ -210,6 +228,11 @@
 	// For gensrsc sharding.
 	shard  int
 	shards int
+
+	// For nsjail tasks
+	useNsjail  bool
+	dirSrcs    android.DirectoryPaths
+	keepGendir bool
 }
 
 func (g *Module) GeneratedSourceFiles() android.Paths {
@@ -279,7 +302,15 @@
 // approach zero; there should be no genrule action registration done directly
 // by Soong logic in the mixed-build case.
 func (g *Module) generateCommonBuildActions(ctx android.ModuleContext) {
-	g.subName = ctx.ModuleSubDir()
+	// Add the variant as a suffix to the make modules to create, so that the make modules
+	// don't conflict because make doesn't know about variants. However, this causes issues with
+	// tracking required dependencies as the required property in soong is passed straight to make
+	// without accounting for these suffixes. To make it a little easier to work with, don't use
+	// a suffix for android_common variants so that java_genrules look like regular 1-variant
+	// genrules to make.
+	if ctx.ModuleSubDir() != "android_common" {
+		g.subName = ctx.ModuleSubDir()
+	}
 
 	if len(g.properties.Export_include_dirs) > 0 {
 		for _, dir := range g.properties.Export_include_dirs {
@@ -312,23 +343,18 @@
 	var packagedTools []android.PackagingSpec
 	if len(g.properties.Tools) > 0 {
 		seenTools := make(map[string]bool)
-
-		ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
-			switch tag := ctx.OtherModuleDependencyTag(module).(type) {
+		ctx.VisitDirectDepsProxyAllowDisabled(func(proxy android.ModuleProxy) {
+			switch tag := ctx.OtherModuleDependencyTag(proxy).(type) {
 			case hostToolDependencyTag:
+				// Necessary to retrieve any prebuilt replacement for the tool, since
+				// toolDepsMutator runs too late for the prebuilt mutators to have
+				// replaced the dependency.
+				module := android.PrebuiltGetPreferred(ctx, proxy)
 				tool := ctx.OtherModuleName(module)
-				if m, ok := module.(android.Module); ok {
-					// Necessary to retrieve any prebuilt replacement for the tool, since
-					// toolDepsMutator runs too late for the prebuilt mutators to have
-					// replaced the dependency.
-					module = android.PrebuiltGetPreferred(ctx, m)
-				}
-
-				switch t := module.(type) {
-				case android.HostToolProvider:
+				if h, ok := android.OtherModuleProvider(ctx, module, android.HostToolProviderKey); ok {
 					// A HostToolProvider provides the path to a tool, which will be copied
 					// into the sandbox.
-					if !t.(android.Module).Enabled(ctx) {
+					if !android.OtherModuleProviderOrDefault(ctx, module, android.CommonModuleInfoKey).Enabled {
 						if ctx.Config().AllowMissingDependencies() {
 							ctx.AddMissingDependencies([]string{tool})
 						} else {
@@ -336,13 +362,13 @@
 						}
 						return
 					}
-					path := t.HostToolPath()
+					path := h.HostToolPath
 					if !path.Valid() {
 						ctx.ModuleErrorf("host tool %q missing output file", tool)
 						return
 					}
 					if specs := android.OtherModuleProviderOrDefault(
-						ctx, t, android.InstallFilesProvider).TransitivePackagingSpecs.ToList(); specs != nil {
+						ctx, module, android.InstallFilesProvider).TransitivePackagingSpecs.ToList(); specs != nil {
 						// If the HostToolProvider has PackgingSpecs, which are definitions of the
 						// required relative locations of the tool and its dependencies, use those
 						// instead.  They will be copied to those relative locations in the sbox
@@ -364,7 +390,7 @@
 						tools = append(tools, path.Path())
 						addLocationLabel(tag.label, toolLocation{android.Paths{path.Path()}})
 					}
-				default:
+				} else {
 					ctx.ModuleErrorf("%q is not a host tool provider", tool)
 					return
 				}
@@ -424,8 +450,11 @@
 		}
 		return srcFiles
 	}
-	g.properties.ResolvedSrcs = g.properties.Srcs.GetOrDefault(ctx, nil)
-	srcFiles := addLabelsForInputs("srcs", g.properties.ResolvedSrcs, g.properties.Exclude_srcs)
+	srcs := g.properties.Srcs.GetOrDefault(ctx, nil)
+	srcFiles := addLabelsForInputs("srcs", srcs, g.properties.Exclude_srcs)
+	srcFiles = append(srcFiles, addLabelsForInputs("device_first_srcs", g.properties.Device_first_srcs.GetOrDefault(ctx, nil), nil)...)
+	srcFiles = append(srcFiles, addLabelsForInputs("device_common_srcs", g.properties.Device_common_srcs.GetOrDefault(ctx, nil), nil)...)
+	srcFiles = append(srcFiles, addLabelsForInputs("common_os_srcs", g.properties.Common_os_srcs.GetOrDefault(ctx, nil), nil)...)
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcFiles.Strings()})
 
 	var copyFrom android.Paths
@@ -454,21 +483,29 @@
 
 		// Pick a unique path outside the task.genDir for the sbox manifest textproto,
 		// a unique rule name, and the user-visible description.
-		manifestName := "genrule.sbox.textproto"
+		var rule *android.RuleBuilder
 		desc := "generate"
 		name := "generator"
-		if task.shards > 0 {
-			manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto"
-			desc += " " + strconv.Itoa(task.shard)
-			name += strconv.Itoa(task.shard)
-		} else if len(task.out) == 1 {
-			desc += " " + task.out[0].Base()
+		if task.useNsjail {
+			rule = android.NewRuleBuilder(pctx, ctx).Nsjail(task.genDir, android.PathForModuleOut(ctx, "nsjail_build_sandbox"))
+			if task.keepGendir {
+				rule.NsjailKeepGendir()
+			}
+		} else {
+			manifestName := "genrule.sbox.textproto"
+			if task.shards > 0 {
+				manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto"
+				desc += " " + strconv.Itoa(task.shard)
+				name += strconv.Itoa(task.shard)
+			} else if len(task.out) == 1 {
+				desc += " " + task.out[0].Base()
+			}
+
+			manifestPath := android.PathForModuleOut(ctx, manifestName)
+
+			// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
+			rule = getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath))
 		}
-
-		manifestPath := android.PathForModuleOut(ctx, manifestName)
-
-		// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
-		rule := getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath))
 		if Bool(g.properties.Write_if_changed) {
 			rule.Restat()
 		}
@@ -569,6 +606,18 @@
 			cmd.OrderOnly(ctx.Config().BuildNumberFile(ctx))
 		}
 
+		if task.useNsjail {
+			for _, input := range task.dirSrcs {
+				cmd.ImplicitDirectory(input)
+				// TODO(b/375551969): remove glob
+				if paths, err := ctx.GlobWithDeps(filepath.Join(input.String(), "**/*"), nil); err == nil {
+					rule.NsjailImplicits(android.PathsForSource(ctx, paths))
+				} else {
+					ctx.PropertyErrorf("dir_srcs", "can't glob %q", input.String())
+				}
+			}
+		}
+
 		// Create the rule to run the genrule command inside sbox.
 		rule.Build(name, desc)
 
@@ -627,6 +676,12 @@
 	}
 
 	g.setOutputFiles(ctx)
+
+	if ctx.Os() == android.Windows {
+		// Make doesn't support windows:
+		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/module_arch_supported.mk;l=66;drc=f264690860bb6ee7762784d6b7201aae057ba6f2
+		g.HideFromMake()
+	}
 }
 
 func (g *Module) setOutputFiles(ctx android.ModuleContext) {
@@ -643,7 +698,7 @@
 // Collect information for opening IDE project files in java/jdeps.go.
 func (g *Module) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
 	dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...)
-	for _, src := range g.properties.ResolvedSrcs {
+	for _, src := range g.properties.Srcs.GetOrDefault(ctx, nil) {
 		if strings.HasPrefix(src, ":") {
 			src = strings.Trim(src, ":")
 			dpInfo.Deps = append(dpInfo.Deps, src)
@@ -696,16 +751,22 @@
 
 type noopImageInterface struct{}
 
-func (x noopImageInterface) ImageMutatorBegin(android.BaseModuleContext)                 {}
-func (x noopImageInterface) VendorVariantNeeded(android.BaseModuleContext) bool          { return false }
-func (x noopImageInterface) ProductVariantNeeded(android.BaseModuleContext) bool         { return false }
-func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool            { return false }
-func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool         { return false }
-func (x noopImageInterface) VendorRamdiskVariantNeeded(android.BaseModuleContext) bool   { return false }
-func (x noopImageInterface) DebugRamdiskVariantNeeded(android.BaseModuleContext) bool    { return false }
-func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool        { return false }
-func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
-func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string) {
+func (x noopImageInterface) ImageMutatorBegin(android.ImageInterfaceContext)         {}
+func (x noopImageInterface) VendorVariantNeeded(android.ImageInterfaceContext) bool  { return false }
+func (x noopImageInterface) ProductVariantNeeded(android.ImageInterfaceContext) bool { return false }
+func (x noopImageInterface) CoreVariantNeeded(android.ImageInterfaceContext) bool    { return false }
+func (x noopImageInterface) RamdiskVariantNeeded(android.ImageInterfaceContext) bool { return false }
+func (x noopImageInterface) VendorRamdiskVariantNeeded(android.ImageInterfaceContext) bool {
+	return false
+}
+func (x noopImageInterface) DebugRamdiskVariantNeeded(android.ImageInterfaceContext) bool {
+	return false
+}
+func (x noopImageInterface) RecoveryVariantNeeded(android.ImageInterfaceContext) bool { return false }
+func (x noopImageInterface) ExtraImageVariations(ctx android.ImageInterfaceContext) []string {
+	return nil
+}
+func (x noopImageInterface) SetImageVariation(ctx android.ImageInterfaceContext, variation string) {
 }
 
 func NewGenSrcs() *Module {
@@ -832,15 +893,32 @@
 	properties := &genRuleProperties{}
 
 	taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
+		useNsjail := Bool(properties.Use_nsjail)
+
+		dirSrcs := android.DirectoryPathsForModuleSrc(ctx, properties.Dir_srcs)
+		if len(dirSrcs) > 0 && !useNsjail {
+			ctx.PropertyErrorf("dir_srcs", "can't use dir_srcs if use_nsjail is false")
+			return nil
+		}
+
+		keepGendir := Bool(properties.Keep_gendir)
+		if keepGendir && !useNsjail {
+			ctx.PropertyErrorf("keep_gendir", "can't use keep_gendir if use_nsjail is false")
+			return nil
+		}
+
 		outs := make(android.WritablePaths, len(properties.Out))
 		for i, out := range properties.Out {
 			outs[i] = android.PathForModuleGen(ctx, out)
 		}
 		return []generateTask{{
-			in:     srcFiles,
-			out:    outs,
-			genDir: android.PathForModuleGen(ctx),
-			cmd:    rawCommand,
+			in:         srcFiles,
+			out:        outs,
+			genDir:     android.PathForModuleGen(ctx),
+			cmd:        rawCommand,
+			useNsjail:  useNsjail,
+			dirSrcs:    dirSrcs,
+			keepGendir: keepGendir,
 		}}
 	}
 
@@ -855,6 +933,16 @@
 }
 
 type genRuleProperties struct {
+	Use_nsjail *bool
+
+	// List of input directories. Can be set only when use_nsjail is true. Currently, usage of
+	// dir_srcs is limited only to Trusty build.
+	Dir_srcs []string `android:"path"`
+
+	// If set to true, $(genDir) is not truncated. Useful when this genrule can be incrementally
+	// built. Can be set only when use_nsjail is true.
+	Keep_gendir *bool
+
 	// names of the output files that will be generated
 	Out []string `android:"arch_variant"`
 }
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 9278f15..f190750 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -24,6 +24,7 @@
 
 	"android/soong/android"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -694,8 +695,12 @@
 	expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
 	android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
 
+	srcsFileProvider, ok := android.OtherModuleProvider(result.TestContext, gen, blueprint.SrcsFileProviderKey)
+	if !ok {
+		t.Fatal("Expected genrule to have a SrcsFileProviderData, but did not")
+	}
 	expectedSrcs := []string{"in1"}
-	android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.ResolvedSrcs)
+	android.AssertDeepEquals(t, "srcs", expectedSrcs, srcsFileProvider.SrcPaths)
 }
 
 func TestGenruleAllowMissingDependencies(t *testing.T) {
diff --git a/go.mod b/go.mod
index aa43066..57b59c0 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module android/soong
 
-go 1.22
+go 1.23
 
 require (
 	github.com/google/blueprint v0.0.0
diff --git a/go.work b/go.work
index 46a135b..e538915 100644
--- a/go.work
+++ b/go.work
@@ -1,4 +1,4 @@
-go 1.22
+go 1.23
 
 use (
 	.
diff --git a/golang/golang.go b/golang/golang.go
index 618a085..d33f5e0 100644
--- a/golang/golang.go
+++ b/golang/golang.go
@@ -22,6 +22,7 @@
 
 import (
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 )
@@ -73,7 +74,7 @@
 func goBinaryModuleFactory() android.Module {
 	module := &GoBinary{}
 	module.AddProperties(module.Properties()...)
-	android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibFirst)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
 	return module
 }
 
diff --git a/golang/golang_test.go b/golang/golang_test.go
index b512144..0a4baed 100644
--- a/golang/golang_test.go
+++ b/golang/golang_test.go
@@ -16,9 +16,10 @@
 
 import (
 	"android/soong/android"
-	"github.com/google/blueprint/bootstrap"
-	"path/filepath"
+	"regexp"
 	"testing"
+
+	"github.com/google/blueprint/bootstrap"
 )
 
 func TestGolang(t *testing.T) {
@@ -39,13 +40,19 @@
 		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 			RegisterGoModuleTypes(ctx)
 			ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-				ctx.BottomUpBlueprint("bootstrap_deps", bootstrap.BootstrapDeps)
+				ctx.BottomUpBlueprint("bootstrap_deps", bootstrap.BootstrapDeps).UsesReverseDependencies()
 			})
 		}),
 	).RunTestWithBp(t, bp)
 
 	bin := result.ModuleForTests("gobin", result.Config.BuildOSTarget.String())
 
-	expected := filepath.Join("out/soong/host", result.Config.PrebuiltOS(), "bin/go/gobin/obj/gobin")
-	android.AssertPathsRelativeToTopEquals(t, "output files", []string{expected}, bin.OutputFiles(result.TestContext, t, ""))
+	expected := "^out/soong/host/" + result.Config.PrebuiltOS() + "/bin/go/gobin/?[^/]*/obj/gobin$"
+	actual := android.PathsRelativeToTop(bin.OutputFiles(result.TestContext, t, ""))
+	if len(actual) != 1 {
+		t.Fatalf("Expected 1 output file, got %v", actual)
+	}
+	if match, err := regexp.Match(expected, []byte(actual[0])); err != nil || !match {
+		t.Fatalf("Expected output file to match %q, but got %q", expected, actual[0])
+	}
 }
diff --git a/java/Android.bp b/java/Android.bp
index 926a294..885e682 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -7,6 +7,7 @@
     pkgPath: "android/soong/java",
     deps: [
         "blueprint",
+        "blueprint-depset",
         "blueprint-pathtools",
         "soong",
         "soong-aconfig",
@@ -15,7 +16,6 @@
         "soong-dexpreopt",
         "soong-genrule",
         "soong-java-config",
-        "soong-testing",
         "soong-provenance",
         "soong-python",
         "soong-remoteexec",
@@ -72,7 +72,7 @@
         "rro.go",
         "sdk.go",
         "sdk_library.go",
-        "sdk_library_external.go",
+        "sdk_library_internal.go",
         "support_libraries.go",
         "system_modules.go",
         "systemserver_classpath_fragment.go",
@@ -86,7 +86,6 @@
         "app_import_test.go",
         "app_set_test.go",
         "app_test.go",
-        "code_metadata_test.go",
         "container_test.go",
         "bootclasspath_fragment_test.go",
         "device_host_converter_test.go",
@@ -117,7 +116,6 @@
         "sdk_version_test.go",
         "system_modules_test.go",
         "systemserver_classpath_fragment_test.go",
-        "test_spec_test.go",
     ],
     pluginFor: ["soong_build"],
     visibility: ["//visibility:public"],
diff --git a/java/aapt2.go b/java/aapt2.go
index 61cf373..bae4d1e 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -15,7 +15,9 @@
 package java
 
 import (
+	"fmt"
 	"path/filepath"
+	"regexp"
 	"sort"
 	"strconv"
 	"strings"
@@ -31,19 +33,35 @@
 	return strings.HasPrefix(lastDir, "values")
 }
 
+func isFlagsPath(subDir string) bool {
+	re := regexp.MustCompile(`flag\(!?([a-zA-Z_-]+\.)*[a-zA-Z0-9_-]+\)`)
+	lastDir := filepath.Base(subDir)
+	return re.MatchString(lastDir)
+}
+
 // Convert input resource file path to output file path.
 // values-[config]/<file>.xml -> values-[config]_<file>.arsc.flat;
+// flag(fully.qualified.flag_name)/values-[config]/<file>.xml -> /values-[config]_<file>.(fully.qualified.flag_name).arsc.flat;
 // For other resource file, just replace the last "/" with "_" and add .flat extension.
 func pathToAapt2Path(ctx android.ModuleContext, res android.Path) android.WritablePath {
 
-	name := res.Base()
+	extension := filepath.Ext(res.Base())
+	name := strings.TrimSuffix(res.Base(), extension)
 	if isPathValueResource(res) {
-		name = strings.TrimSuffix(name, ".xml") + ".arsc"
+		extension = ".arsc"
 	}
 	subDir := filepath.Dir(res.String())
 	subDir, lastDir := filepath.Split(subDir)
-	name = lastDir + "_" + name + ".flat"
-	return android.PathForModuleOut(ctx, "aapt2", subDir, name)
+	if isFlagsPath(subDir) {
+		var flag string
+		subDir, flag = filepath.Split(filepath.Dir(subDir))
+		flag = strings.TrimPrefix(flag, "flag")
+		name = fmt.Sprintf("%s_%s.%s%s.flat", lastDir, name, flag, extension)
+	} else {
+		name = fmt.Sprintf("%s_%s%s.flat", lastDir, name, extension)
+	}
+	out := android.PathForModuleOut(ctx, "aapt2", subDir, name)
+	return out
 }
 
 // pathsToAapt2Paths Calls pathToAapt2Path on each entry of the given Paths, i.e. []Path.
diff --git a/java/aar.go b/java/aar.go
index 368ad33..7322b09 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -25,14 +25,15 @@
 	"android/soong/dexpreopt"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 )
 
 type AndroidLibraryDependency interface {
 	ExportPackage() android.Path
-	ResourcesNodeDepSet() *android.DepSet[*resourcesNode]
-	RRODirsDepSet() *android.DepSet[rroDir]
-	ManifestsDepSet() *android.DepSet[android.Path]
+	ResourcesNodeDepSet() depset.DepSet[*resourcesNode]
+	RRODirsDepSet() depset.DepSet[rroDir]
+	ManifestsDepSet() depset.DepSet[android.Path]
 	SetRROEnforcedForDependent(enforce bool)
 	IsRROEnforced(ctx android.BaseModuleContext) bool
 }
@@ -45,7 +46,7 @@
 	ctx.RegisterModuleType("android_library_import", AARImportFactory)
 	ctx.RegisterModuleType("android_library", AndroidLibraryFactory)
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator)
+		ctx.Transition("propagate_rro_enforcement", &propagateRROEnforcementTransitionMutator{})
 	})
 }
 
@@ -76,7 +77,7 @@
 	// list of directories relative to the Blueprints file containing
 	// Android resources.  Defaults to ["res"] if a directory called res exists.
 	// Set to [] to disable the default.
-	Resource_dirs []string `android:"path"`
+	Resource_dirs proptools.Configurable[[]string] `android:"path"`
 
 	// list of zip files containing Android resources.
 	Resource_zips []string `android:"path"`
@@ -136,9 +137,9 @@
 
 	aaptProperties aaptProperties
 
-	resourcesNodesDepSet *android.DepSet[*resourcesNode]
-	rroDirsDepSet        *android.DepSet[rroDir]
-	manifestsDepSet      *android.DepSet[android.Path]
+	resourcesNodesDepSet depset.DepSet[*resourcesNode]
+	rroDirsDepSet        depset.DepSet[rroDir]
+	manifestsDepSet      depset.DepSet[android.Path]
 
 	manifestValues struct {
 		applicationId string
@@ -151,15 +152,67 @@
 	path   android.Path
 }
 
-// Propagate RRO enforcement flag to static lib dependencies transitively.
-func propagateRROEnforcementMutator(ctx android.TopDownMutatorContext) {
+// Propagate RRO enforcement flag to static lib dependencies transitively.  If EnforceRROGlobally is set then
+// all modules will use the "" variant.  If specific modules have RRO enforced, then modules (usually apps) with
+// RRO enabled will use the "" variation for themselves, but use the "rro" variant of direct and transitive static
+// android_library dependencies.
+type propagateRROEnforcementTransitionMutator struct{}
+
+func (p propagateRROEnforcementTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	// Never split modules, apps with or without RRO enabled use the "" variant, static android_library dependencies
+	// will use create the "rro" variant from incoming tranisitons.
+	return []string{""}
+}
+
+func (p propagateRROEnforcementTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	// Non-static dependencies are not involved in RRO and always use the empty variant.
+	if ctx.DepTag() != staticLibTag {
+		return ""
+	}
+
 	m := ctx.Module()
-	if d, ok := m.(AndroidLibraryDependency); ok && d.IsRROEnforced(ctx) {
-		ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) {
-			if a, ok := d.(AndroidLibraryDependency); ok {
-				a.SetRROEnforcedForDependent(true)
-			}
-		})
+	if _, ok := m.(AndroidLibraryDependency); ok {
+		// If RRO is enforced globally don't bother using "rro" variants, the empty variant will have RRO enabled.
+		if ctx.Config().EnforceRROGlobally() {
+			return ""
+		}
+
+		// If RRO is enabled for this module use the "rro" variants of static dependencies.  IncomingTransition will
+		// rewrite this back to "" if the dependency is not an android_library.
+		if ctx.Config().EnforceRROForModule(ctx.Module().Name()) {
+			return "rro"
+		}
+	}
+
+	return sourceVariation
+}
+
+func (p propagateRROEnforcementTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	// Propagate the "rro" variant to android_library modules, but use the empty variant for everything else.
+	if incomingVariation == "rro" {
+		m := ctx.Module()
+		if _, ok := m.(AndroidLibraryDependency); ok {
+			return "rro"
+		}
+		return ""
+	}
+
+	return ""
+}
+
+func (p propagateRROEnforcementTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	m := ctx.Module()
+	if d, ok := m.(AndroidLibraryDependency); ok {
+		if variation == "rro" {
+			// This is the "rro" variant of a module that has both variants, mark this one as RRO enabled and
+			// hide it from make to avoid collisions with the non-RRO empty variant.
+			d.SetRROEnforcedForDependent(true)
+			m.HideFromMake()
+		} else if ctx.Config().EnforceRROGlobally() {
+			// RRO is enabled globally, mark it enabled for this module, but there is only one variant so no
+			// need to hide it from make.
+			d.SetRROEnforcedForDependent(true)
+		}
 	}
 }
 
@@ -180,15 +233,15 @@
 func (a *aapt) ExportPackage() android.Path {
 	return a.exportPackage
 }
-func (a *aapt) ResourcesNodeDepSet() *android.DepSet[*resourcesNode] {
+func (a *aapt) ResourcesNodeDepSet() depset.DepSet[*resourcesNode] {
 	return a.resourcesNodesDepSet
 }
 
-func (a *aapt) RRODirsDepSet() *android.DepSet[rroDir] {
+func (a *aapt) RRODirsDepSet() depset.DepSet[rroDir] {
 	return a.rroDirsDepSet
 }
 
-func (a *aapt) ManifestsDepSet() *android.DepSet[android.Path] {
+func (a *aapt) ManifestsDepSet() depset.DepSet[android.Path] {
 	return a.manifestsDepSet
 }
 
@@ -204,7 +257,7 @@
 }
 
 func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext android.SdkContext,
-	manifestPath android.Path) (compileFlags, linkFlags []string, linkDeps android.Paths,
+	manifestPath android.Path, doNotIncludeAssetDirImplicitly bool) (compileFlags, linkFlags []string, linkDeps android.Paths,
 	resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) {
 
 	hasVersionCode := android.PrefixInList(a.aaptProperties.Aaptflags, "--version-code")
@@ -221,8 +274,13 @@
 		Paths:       a.aaptProperties.Assets,
 		IncludeDirs: false,
 	})
-	assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets")
-	resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res")
+	var assetDirs android.Paths
+	if doNotIncludeAssetDirImplicitly {
+		assetDirs = android.PathsForModuleSrc(ctx, a.aaptProperties.Asset_dirs)
+	} else {
+		assetDirs = android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets")
+	}
+	resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs.GetOrDefault(ctx, nil), "res")
 	resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips)
 
 	// Glob directories into lists of paths
@@ -367,6 +425,25 @@
 	extraLinkFlags                 []string
 	aconfigTextFiles               android.Paths
 	usesLibrary                    *usesLibrary
+	// If rroDirs is provided, it will be used to generate package-res.apk
+	rroDirs *android.Paths
+	// If manifestForAapt is not nil, it will be used for aapt instead of the default source manifest.
+	manifestForAapt android.Path
+}
+
+func filterRRO(rroDirsDepSet depset.DepSet[rroDir], filter overlayType) android.Paths {
+	var paths android.Paths
+	seen := make(map[android.Path]bool)
+	for _, d := range rroDirsDepSet.ToList() {
+		if d.overlayType == filter {
+			if seen[d.path] {
+				continue
+			}
+			seen[d.path] = true
+			paths = append(paths, d.path)
+		}
+	}
+	return paths
 }
 
 func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptions) {
@@ -378,10 +455,15 @@
 	opts.classLoaderContexts = opts.classLoaderContexts.ExcludeLibs(opts.excludedLibs)
 
 	// App manifest file
-	manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
-	manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
+	var manifestFilePath android.Path
+	if opts.manifestForAapt != nil {
+		manifestFilePath = opts.manifestForAapt
+	} else {
+		manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
+		manifestFilePath = android.PathForModuleSrc(ctx, manifestFile)
+	}
 
-	manifestPath := ManifestFixer(ctx, manifestSrcPath, ManifestFixerParams{
+	manifestPath := ManifestFixer(ctx, manifestFilePath, ManifestFixerParams{
 		SdkContext:                     opts.sdkContext,
 		ClassLoaderContexts:            opts.classLoaderContexts,
 		IsLibrary:                      a.isLibrary,
@@ -420,7 +502,12 @@
 		a.mergedManifestFile = manifestPath
 	}
 
-	compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, opts.sdkContext, manifestPath)
+	// do not include assets in autogenerated RRO.
+	compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, opts.sdkContext, manifestPath, opts.rroDirs != nil)
+
+	a.rroDirsDepSet = depset.NewBuilder[rroDir](depset.TOPOLOGICAL).
+		Direct(rroDirs...).
+		Transitive(staticRRODirsDepSet).Build()
 
 	linkFlags = append(linkFlags, libFlags...)
 	linkDeps = append(linkDeps, sharedExportPackages...)
@@ -434,9 +521,9 @@
 	}
 
 	linkFlags = append(linkFlags, "--no-static-lib-packages")
-	if a.isLibrary && a.useResourceProcessorBusyBox(ctx) {
-		// When building an android_library using ResourceProcessorBusyBox pass --merge-only to skip resource
-		// references validation until the final app link step when all static libraries are present.
+	if a.isLibrary {
+		// Pass --merge-only to skip resource references validation until the final
+		// app link step when when all static libraries are present.
 		linkFlags = append(linkFlags, "--merge-only")
 	}
 
@@ -510,9 +597,16 @@
 		}
 	}
 
-	for _, dir := range overlayDirs {
-		compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files,
-			compileFlags, a.filterProduct(), opts.aconfigTextFiles).Paths()...)
+	var compiledRro, compiledRroOverlay android.Paths
+	if opts.rroDirs != nil {
+		compiledRro, compiledRroOverlay = a.compileResInDir(ctx, *opts.rroDirs, compileFlags, opts.aconfigTextFiles)
+	} else {
+		// RRO enforcement is done based on module name. Compile the overlayDirs only if rroDirs is nil.
+		// This ensures that the autogenerated RROs do not compile the overlay dirs twice.
+		for _, dir := range overlayDirs {
+			compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files,
+				compileFlags, a.filterProduct(), opts.aconfigTextFiles).Paths()...)
+		}
 	}
 
 	var splitPackages android.WritablePaths
@@ -541,10 +635,20 @@
 	if !a.isLibrary {
 		transitiveAssets = android.ReverseSliceInPlace(staticDeps.assets())
 	}
-	aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt,
-		linkFlags, linkDeps, compiledRes, compiledOverlay, transitiveAssets, splitPackages,
-		opts.aconfigTextFiles)
-	ctx.CheckbuildFile(packageRes)
+	if opts.rroDirs == nil { // link resources and overlay
+		aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt,
+			linkFlags, linkDeps, compiledRes, compiledOverlay, transitiveAssets, splitPackages,
+			opts.aconfigTextFiles)
+		ctx.CheckbuildFile(packageRes)
+	} else { // link autogenerated rro
+		if len(compiledRro) == 0 {
+			return
+		}
+		aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt,
+			linkFlags, linkDeps, compiledRro, compiledRroOverlay, nil, nil,
+			opts.aconfigTextFiles)
+		ctx.CheckbuildFile(packageRes)
+	}
 
 	// Extract assets from the resource package output so that they can be used later in aapt2link
 	// for modules that depend on this one.
@@ -590,7 +694,7 @@
 	a.extraAaptPackagesFile = extraPackages
 	a.rTxt = rTxt
 	a.splits = splits
-	a.resourcesNodesDepSet = android.NewDepSetBuilder[*resourcesNode](android.TOPOLOGICAL).
+	a.resourcesNodesDepSet = depset.NewBuilder[*resourcesNode](depset.TOPOLOGICAL).
 		Direct(&resourcesNode{
 			resPackage:          a.exportPackage,
 			manifest:            a.manifestPath,
@@ -602,15 +706,46 @@
 			usedResourceProcessor: a.useResourceProcessorBusyBox(ctx),
 		}).
 		Transitive(staticResourcesNodesDepSet).Build()
-	a.rroDirsDepSet = android.NewDepSetBuilder[rroDir](android.TOPOLOGICAL).
-		Direct(rroDirs...).
-		Transitive(staticRRODirsDepSet).Build()
-	a.manifestsDepSet = android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).
+	a.manifestsDepSet = depset.NewBuilder[android.Path](depset.TOPOLOGICAL).
 		Direct(a.manifestPath).
 		DirectSlice(additionalManifests).
 		Transitive(staticManifestsDepSet).Build()
 }
 
+// comileResInDir finds the resource files in dirs by globbing and then compiles them using aapt2
+// returns the file paths of compiled resources
+// dirs[0] is used as compileRes
+// dirs[1:] is used as compileOverlay
+func (a *aapt) compileResInDir(ctx android.ModuleContext, dirs android.Paths, compileFlags []string, aconfig android.Paths) (android.Paths, android.Paths) {
+	filesInDir := func(dir android.Path) android.Paths {
+		files, err := ctx.GlobWithDeps(filepath.Join(dir.String(), "**/*"), androidResourceIgnoreFilenames)
+		if err != nil {
+			ctx.ModuleErrorf("failed to glob overlay resource dir %q: %s", dir, err.Error())
+			return nil
+		}
+		var filePaths android.Paths
+		for _, file := range files {
+			if strings.HasSuffix(file, "/") {
+				continue // ignore directories
+			}
+			filePaths = append(filePaths, android.PathForSource(ctx, file))
+		}
+		return filePaths
+	}
+
+	var compiledRes, compiledOverlay android.Paths
+	if len(dirs) == 0 {
+		return nil, nil
+	}
+	compiledRes = append(compiledRes, aapt2Compile(ctx, dirs[0], filesInDir(dirs[0]), compileFlags, a.filterProduct(), aconfig).Paths()...)
+	if len(dirs) > 0 {
+		for _, dir := range dirs[1:] {
+			compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir, filesInDir(dir), compileFlags, a.filterProduct(), aconfig).Paths()...)
+		}
+	}
+	return compiledRes, compiledOverlay
+}
+
 var resourceProcessorBusyBox = pctx.AndroidStaticRule("resourceProcessorBusyBox",
 	blueprint.RuleParams{
 		Command: "${config.JavaCmd} -cp ${config.ResourceProcessorBusyBox} " +
@@ -724,8 +859,8 @@
 // aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
 func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext,
 	classLoaderContexts dexpreopt.ClassLoaderContextMap, usesLibrary *usesLibrary) (
-	staticResourcesNodes, sharedResourcesNodes *android.DepSet[*resourcesNode], staticRRODirs *android.DepSet[rroDir],
-	staticManifests *android.DepSet[android.Path], sharedLibs android.Paths, flags []string) {
+	staticResourcesNodes, sharedResourcesNodes depset.DepSet[*resourcesNode], staticRRODirs depset.DepSet[rroDir],
+	staticManifests depset.DepSet[android.Path], sharedLibs android.Paths, flags []string) {
 
 	if classLoaderContexts == nil {
 		// Not all callers need to compute class loader context, those who don't just pass nil.
@@ -738,10 +873,10 @@
 		sharedLibs = append(sharedLibs, sdkDep.jars...)
 	}
 
-	var staticResourcesNodeDepSets []*android.DepSet[*resourcesNode]
-	var sharedResourcesNodeDepSets []*android.DepSet[*resourcesNode]
-	rroDirsDepSetBuilder := android.NewDepSetBuilder[rroDir](android.TOPOLOGICAL)
-	manifestsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL)
+	var staticResourcesNodeDepSets []depset.DepSet[*resourcesNode]
+	var sharedResourcesNodeDepSets []depset.DepSet[*resourcesNode]
+	rroDirsDepSetBuilder := depset.NewBuilder[rroDir](depset.TOPOLOGICAL)
+	manifestsDepSetBuilder := depset.NewBuilder[android.Path](depset.TOPOLOGICAL)
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		depTag := ctx.OtherModuleDependencyTag(module)
@@ -755,7 +890,7 @@
 		switch depTag {
 		case instrumentationForTag:
 			// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
-		case sdkLibTag, libTag:
+		case sdkLibTag, libTag, rroDepTag:
 			if exportPackage != nil {
 				sharedResourcesNodeDepSets = append(sharedResourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
 				sharedLibs = append(sharedLibs, exportPackage)
@@ -785,9 +920,9 @@
 	// dependencies) the highest priority dependency is listed first, but for resources the highest priority
 	// dependency has to be listed last.  This is also inconsistent with the way manifests from the same
 	// transitive dependencies are merged.
-	staticResourcesNodes = android.NewDepSet(android.TOPOLOGICAL, nil,
+	staticResourcesNodes = depset.New(depset.TOPOLOGICAL, nil,
 		android.ReverseSliceInPlace(staticResourcesNodeDepSets))
-	sharedResourcesNodes = android.NewDepSet(android.TOPOLOGICAL, nil,
+	sharedResourcesNodes = depset.New(depset.TOPOLOGICAL, nil,
 		android.ReverseSliceInPlace(sharedResourcesNodeDepSets))
 
 	staticRRODirs = rroDirsDepSetBuilder.Build()
@@ -1015,8 +1150,8 @@
 	rTxt                               android.Path
 	rJar                               android.Path
 
-	resourcesNodesDepSet *android.DepSet[*resourcesNode]
-	manifestsDepSet      *android.DepSet[android.Path]
+	resourcesNodesDepSet depset.DepSet[*resourcesNode]
+	manifestsDepSet      depset.DepSet[android.Path]
 
 	hideApexVariantFromMake bool
 
@@ -1062,15 +1197,15 @@
 func (a *AARImport) ExportPackage() android.Path {
 	return a.exportPackage
 }
-func (a *AARImport) ResourcesNodeDepSet() *android.DepSet[*resourcesNode] {
+func (a *AARImport) ResourcesNodeDepSet() depset.DepSet[*resourcesNode] {
 	return a.resourcesNodesDepSet
 }
 
-func (a *AARImport) RRODirsDepSet() *android.DepSet[rroDir] {
-	return android.NewDepSet[rroDir](android.TOPOLOGICAL, nil, nil)
+func (a *AARImport) RRODirsDepSet() depset.DepSet[rroDir] {
+	return depset.New[rroDir](depset.TOPOLOGICAL, nil, nil)
 }
 
-func (a *AARImport) ManifestsDepSet() *android.DepSet[android.Path] {
+func (a *AARImport) ManifestsDepSet() depset.DepSet[android.Path] {
 	return a.manifestsDepSet
 }
 
@@ -1187,13 +1322,13 @@
 	proguardFlags := extractedAARDir.Join(ctx, "proguard.txt")
 	transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx)
 	android.SetProvider(ctx, ProguardSpecInfoProvider, ProguardSpecInfo{
-		ProguardFlagsFiles: android.NewDepSet[android.Path](
-			android.POSTORDER,
+		ProguardFlagsFiles: depset.New[android.Path](
+			depset.POSTORDER,
 			android.Paths{proguardFlags},
 			transitiveProguardFlags,
 		),
-		UnconditionallyExportedProguardFlags: android.NewDepSet[android.Path](
-			android.POSTORDER,
+		UnconditionallyExportedProguardFlags: depset.New[android.Path](
+			depset.POSTORDER,
 			nil,
 			transitiveUnconditionalExportedFlags,
 		),
@@ -1274,7 +1409,7 @@
 	aapt2ExtractExtraPackages(ctx, extraAaptPackagesFile, a.rJar)
 	a.extraAaptPackagesFile = extraAaptPackagesFile
 
-	resourcesNodesDepSetBuilder := android.NewDepSetBuilder[*resourcesNode](android.TOPOLOGICAL)
+	resourcesNodesDepSetBuilder := depset.NewBuilder[*resourcesNode](depset.TOPOLOGICAL)
 	resourcesNodesDepSetBuilder.Direct(&resourcesNode{
 		resPackage: a.exportPackage,
 		manifest:   a.manifest,
@@ -1287,7 +1422,7 @@
 	resourcesNodesDepSetBuilder.Transitive(staticResourcesNodesDepSet)
 	a.resourcesNodesDepSet = resourcesNodesDepSetBuilder.Build()
 
-	manifestDepSetBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(a.manifest)
+	manifestDepSetBuilder := depset.NewBuilder[android.Path](depset.TOPOLOGICAL).Direct(a.manifest)
 	manifestDepSetBuilder.Transitive(staticManifestsDepSet)
 	a.manifestsDepSet = manifestDepSetBuilder.Build()
 
@@ -1306,9 +1441,9 @@
 	var staticJars android.Paths
 	var staticHeaderJars android.Paths
 	var staticResourceJars android.Paths
-	var transitiveStaticLibsHeaderJars []*android.DepSet[android.Path]
-	var transitiveStaticLibsImplementationJars []*android.DepSet[android.Path]
-	var transitiveStaticLibsResourceJars []*android.DepSet[android.Path]
+	var transitiveStaticLibsHeaderJars []depset.DepSet[android.Path]
+	var transitiveStaticLibsImplementationJars []depset.DepSet[android.Path]
+	var transitiveStaticLibsResourceJars []depset.DepSet[android.Path]
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
@@ -1318,24 +1453,18 @@
 				staticJars = append(staticJars, dep.ImplementationJars...)
 				staticHeaderJars = append(staticHeaderJars, dep.HeaderJars...)
 				staticResourceJars = append(staticResourceJars, dep.ResourceJars...)
-				if dep.TransitiveStaticLibsHeaderJars != nil {
-					transitiveStaticLibsHeaderJars = append(transitiveStaticLibsHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-				}
-				if dep.TransitiveStaticLibsImplementationJars != nil {
-					transitiveStaticLibsImplementationJars = append(transitiveStaticLibsImplementationJars, dep.TransitiveStaticLibsImplementationJars)
-				}
-				if dep.TransitiveStaticLibsResourceJars != nil {
-					transitiveStaticLibsResourceJars = append(transitiveStaticLibsResourceJars, dep.TransitiveStaticLibsResourceJars)
-				}
+				transitiveStaticLibsHeaderJars = append(transitiveStaticLibsHeaderJars, dep.TransitiveStaticLibsHeaderJars)
+				transitiveStaticLibsImplementationJars = append(transitiveStaticLibsImplementationJars, dep.TransitiveStaticLibsImplementationJars)
+				transitiveStaticLibsResourceJars = append(transitiveStaticLibsResourceJars, dep.TransitiveStaticLibsResourceJars)
 			}
 		}
 		addCLCFromDep(ctx, module, a.classLoaderContexts)
 		addMissingOptionalUsesLibsFromDep(ctx, module, &a.usesLibrary)
 	})
 
-	completeStaticLibsHeaderJars := android.NewDepSet(android.PREORDER, android.Paths{classpathFile}, transitiveStaticLibsHeaderJars)
-	completeStaticLibsImplementationJars := android.NewDepSet(android.PREORDER, android.Paths{classpathFile}, transitiveStaticLibsImplementationJars)
-	completeStaticLibsResourceJars := android.NewDepSet(android.PREORDER, nil, transitiveStaticLibsResourceJars)
+	completeStaticLibsHeaderJars := depset.New(depset.PREORDER, android.Paths{classpathFile}, transitiveStaticLibsHeaderJars)
+	completeStaticLibsImplementationJars := depset.New(depset.PREORDER, android.Paths{classpathFile}, transitiveStaticLibsImplementationJars)
+	completeStaticLibsResourceJars := depset.New(depset.PREORDER, nil, transitiveStaticLibsResourceJars)
 
 	var implementationJarFile android.Path
 	var combineJars android.Paths
diff --git a/java/androidmk.go b/java/androidmk.go
index b050445..eb33589 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -281,49 +281,24 @@
 		return nil
 	}
 
-	if !binary.isWrapperVariant {
-		return []android.AndroidMkEntries{android.AndroidMkEntries{
-			Class:      "JAVA_LIBRARIES",
-			OutputFile: android.OptionalPathForPath(binary.outputFile),
-			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-					entries.SetPath("LOCAL_SOONG_HEADER_JAR", binary.headerJarFile)
-					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", binary.implementationAndResourcesJar)
-					if binary.dexJarFile.IsSet() {
-						entries.SetPath("LOCAL_SOONG_DEX_JAR", binary.dexJarFile.Path())
-					}
-					if len(binary.dexpreopter.builtInstalled) > 0 {
-						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled)
-					}
-				},
+	return []android.AndroidMkEntries{{
+		Class:      "JAVA_LIBRARIES",
+		OutputFile: android.OptionalPathForPath(binary.outputFile),
+		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetPath("LOCAL_SOONG_HEADER_JAR", binary.headerJarFile)
+				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", binary.implementationAndResourcesJar)
+				if binary.dexJarFile.IsSet() {
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", binary.dexJarFile.Path())
+				}
+				if len(binary.dexpreopter.builtInstalled) > 0 {
+					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled)
+				}
+				entries.AddStrings("LOCAL_REQUIRED_MODULES", binary.androidMkNamesOfJniLibs...)
 			},
-			ExtraFooters: []android.AndroidMkExtraFootersFunc{
-				func(w io.Writer, name, prefix, moduleDir string) {
-					fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
-				},
-			},
-		}}
-	} else {
-		outputFile := binary.wrapperFile
-
-		return []android.AndroidMkEntries{android.AndroidMkEntries{
-			Class:      "EXECUTABLES",
-			OutputFile: android.OptionalPathForPath(outputFile),
-			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-					entries.SetBool("LOCAL_STRIP_MODULE", false)
-				},
-			},
-			ExtraFooters: []android.AndroidMkExtraFootersFunc{
-				func(w io.Writer, name, prefix, moduleDir string) {
-					// Ensure that the wrapper script timestamp is always updated when the jar is updated
-					fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): $(jar_installed_module)")
-					fmt.Fprintln(w, "jar_installed_module :=")
-				},
-			},
-		}}
-	}
+		},
+	}}
 }
 
 func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries {
@@ -332,15 +307,11 @@
 			Disabled: true,
 		}}
 	}
-	var required []string
-	if proptools.Bool(app.appProperties.Generate_product_characteristics_rro) {
-		required = []string{app.productCharacteristicsRROPackageName()}
-	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "APPS",
 		OutputFile: android.OptionalPathForPath(app.outputFile),
 		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
-		Required:   required,
+		Required:   app.requiredModuleNames,
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				// App module names can be overridden.
@@ -375,31 +346,6 @@
 					entries.SetBoolIfTrue("LOCAL_NO_STANDARD_LIBRARIES", true)
 				}
 
-				filterRRO := func(filter overlayType) android.Paths {
-					var paths android.Paths
-					seen := make(map[android.Path]bool)
-					for _, d := range app.rroDirsDepSet.ToList() {
-						if d.overlayType == filter {
-							if seen[d.path] {
-								continue
-							}
-							seen[d.path] = true
-							paths = append(paths, d.path)
-						}
-					}
-					// Reverse the order, Soong stores rroDirs in aapt2 order (low to high priority), but Make
-					// expects it in LOCAL_RESOURCE_DIRS order (high to low priority).
-					return android.ReversePaths(paths)
-				}
-				deviceRRODirs := filterRRO(device)
-				if len(deviceRRODirs) > 0 {
-					entries.AddStrings("LOCAL_SOONG_DEVICE_RRO_DIRS", deviceRRODirs.Strings()...)
-				}
-				productRRODirs := filterRRO(product)
-				if len(productRRODirs) > 0 {
-					entries.AddStrings("LOCAL_SOONG_PRODUCT_RRO_DIRS", productRRODirs.Strings()...)
-				}
-
 				entries.SetBoolIfTrue("LOCAL_EXPORT_PACKAGE_RESOURCES", Bool(app.appProperties.Export_package_resources))
 
 				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", app.manifestPath)
@@ -415,7 +361,7 @@
 				} else {
 					var names []string
 					for _, jniLib := range app.jniLibs {
-						names = append(names, jniLib.name)
+						names = append(names, jniLib.name+":"+jniLib.target.Arch.ArchType.Bitness())
 					}
 					entries.AddStrings("LOCAL_REQUIRED_MODULES", names...)
 				}
@@ -450,6 +396,24 @@
 	}
 }
 
+func (a *AutogenRuntimeResourceOverlay) AndroidMkEntries() []android.AndroidMkEntries {
+	if a.IsHideFromMake() || a.outputFile == nil {
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
+			Disabled: true,
+		}}
+	}
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "APPS",
+		OutputFile: android.OptionalPathForPath(a.outputFile),
+		Include:    "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_CERTIFICATE", a.certificate.AndroidMkString())
+			},
+		},
+	}}
+}
+
 func (a *AndroidApp) getOverriddenPackages() []string {
 	var overridden []string
 	if len(a.overridableAppProperties.Overrides) > 0 {
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 243a279..1d98b18 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -286,7 +286,7 @@
 	}{
 		{
 			name:     "app",
-			expected: []string{"libjni"},
+			expected: []string{"libjni:64"},
 		},
 		{
 			name:     "app_embedded",
diff --git a/java/app.go b/java/app.go
index 13f1568..64ecc55 100644
--- a/java/app.go
+++ b/java/app.go
@@ -22,15 +22,13 @@
 	"path/filepath"
 	"strings"
 
-	"android/soong/testing"
-
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/dexpreopt"
-	"android/soong/genrule"
 	"android/soong/tradefed"
 )
 
@@ -63,6 +61,19 @@
 	ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
 }
 
+type AppInfo struct {
+	// Updatable is set to the value of the updatable property
+	Updatable bool
+
+	// TestHelperApp is true if the module is a android_test_helper_app
+	TestHelperApp bool
+
+	// EmbeddedJNILibs is the list of paths to JNI libraries that were embedded in the APK.
+	EmbeddedJNILibs android.Paths
+}
+
+var AppInfoProvider = blueprint.NewProvider[*AppInfo]()
+
 // AndroidManifest.xml merging
 // package splits
 
@@ -83,7 +94,7 @@
 	Package_splits []string
 
 	// list of native libraries that will be provided in or alongside the resulting jar
-	Jni_libs []string `android:"arch_variant"`
+	Jni_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// if true, use JNI libraries that link against platform APIs even if this module sets
 	// sdk_version.
@@ -153,7 +164,7 @@
 type overridableAppProperties struct {
 	// The name of a certificate in the default certificate directory, blank to use the default product certificate,
 	// or an android_app_certificate module name in the form ":module".
-	Certificate *string
+	Certificate proptools.Configurable[string] `android:"replace_instead_of_append"`
 
 	// Name of the signing certificate lineage file or filegroup module.
 	Lineage *string `android:"path"`
@@ -162,7 +173,7 @@
 	RotationMinSdkVersion *string
 
 	// the package name of this app. The package name in the manifest file is used if one was not given.
-	Package_name *string
+	Package_name proptools.Configurable[string]
 
 	// the logging parent of this app.
 	Logging_parent *string
@@ -212,13 +223,15 @@
 	javaApiUsedByOutputFile android.ModuleOutPath
 
 	privAppAllowlist android.OptionalPath
+
+	requiredModuleNames []string
 }
 
 func (a *AndroidApp) IsInstallable() bool {
 	return Bool(a.properties.Installable)
 }
 
-func (a *AndroidApp) ResourcesNodeDepSet() *android.DepSet[*resourcesNode] {
+func (a *AndroidApp) ResourcesNodeDepSet() depset.DepSet[*resourcesNode] {
 	return a.aapt.resourcesNodesDepSet
 }
 
@@ -311,7 +324,7 @@
 		} else {
 			tag = jniInstallTag
 		}
-		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
+		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs.GetOrDefault(ctx, nil)...)
 	}
 	for _, aconfig_declaration := range a.aaptProperties.Flags_packages {
 		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration)
@@ -376,7 +389,8 @@
 	checkMinSdkVersionMts(ctx, a.MinSdkVersion(ctx))
 	applicationId := a.appTestHelperAppProperties.Manifest_values.ApplicationId
 	if applicationId != nil {
-		if a.overridableAppProperties.Package_name != nil {
+		packageName := a.overridableAppProperties.Package_name.Get(ctx)
+		if packageName.IsPresent() {
 			ctx.PropertyErrorf("manifest_values.applicationId", "property is not supported when property package_name is set.")
 		}
 		a.aapt.manifestValues.applicationId = *applicationId
@@ -385,7 +399,10 @@
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
 		TestOnly: true,
 	})
-
+	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
+		Updatable:     Bool(a.appProperties.Updatable),
+		TestHelperApp: true,
+	})
 }
 
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -393,6 +410,37 @@
 	a.checkEmbedJnis(ctx)
 	a.generateAndroidBuildActions(ctx)
 	a.generateJavaUsedByApex(ctx)
+
+	var embeddedJniLibs []android.Path
+
+	if a.embeddedJniLibs {
+		for _, jni := range a.jniLibs {
+			embeddedJniLibs = append(embeddedJniLibs, jni.path)
+		}
+	}
+	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
+		Updatable:       Bool(a.appProperties.Updatable),
+		TestHelperApp:   false,
+		EmbeddedJNILibs: embeddedJniLibs,
+	})
+
+	a.requiredModuleNames = a.getRequiredModuleNames(ctx)
+}
+
+func (a *AndroidApp) getRequiredModuleNames(ctx android.ModuleContext) []string {
+	var required []string
+	if proptools.Bool(a.appProperties.Generate_product_characteristics_rro) {
+		required = []string{a.productCharacteristicsRROPackageName()}
+	}
+	// Install the vendor overlay variant if this app is installed.
+	if len(filterRRO(a.rroDirsDepSet, device)) > 0 {
+		required = append(required, AutogeneratedRroModuleName(ctx, ctx.Module().Name(), "vendor"))
+	}
+	// Install the product overlay variant if this app is installed.
+	if len(filterRRO(a.rroDirsDepSet, product)) > 0 {
+		required = append(required, AutogeneratedRroModuleName(ctx, ctx.Module().Name(), "product"))
+	}
+	return required
 }
 
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
@@ -428,7 +476,7 @@
 func (a *AndroidApp) checkEmbedJnis(ctx android.BaseModuleContext) {
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	apkInApex := !apexInfo.IsForPlatform()
-	hasJnis := len(a.appProperties.Jni_libs) > 0
+	hasJnis := len(a.appProperties.Jni_libs.GetOrDefault(ctx, nil)) > 0
 
 	if apkInApex && hasJnis && !Bool(a.appProperties.Use_embedded_native_libs) {
 		ctx.ModuleErrorf("APK in APEX should have use_embedded_native_libs: true")
@@ -569,10 +617,11 @@
 	}
 
 	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
-	if overridden || a.overridableAppProperties.Package_name != nil {
+	packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+	if overridden || packageNameProp.IsPresent() {
 		// The product override variable has a priority over the package_name property.
 		if !overridden {
-			manifestPackageName = *a.overridableAppProperties.Package_name
+			manifestPackageName = packageNameProp.Get()
 		}
 		aaptLinkFlags = append(aaptLinkFlags, generateAaptRenamePackageFlags(manifestPackageName, a.renameResourcesPackage())...)
 		a.overriddenManifestPackageName = manifestPackageName
@@ -586,7 +635,7 @@
 		if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" {
 			a.aapt.defaultManifestVersion = override
 		} else {
-			a.aapt.defaultManifestVersion = android.DefaultUpdatableModuleVersion
+			a.aapt.defaultManifestVersion = ctx.Config().ReleaseDefaultUpdatableModuleVersion()
 		}
 	}
 
@@ -663,7 +712,7 @@
 		a.dexProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
 	}
 	a.dexpreopter.uncompressedDex = *a.dexProperties.Uncompress_dex
-	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
+	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries(ctx)
 	a.dexpreopter.classLoaderContexts = a.classLoaderContexts
 	a.dexpreopter.manifestFile = a.mergedManifestFile
 	a.dexpreopter.preventInstall = a.appProperties.PreventInstall
@@ -815,13 +864,14 @@
 		return android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist)
 	}
 
-	if a.overridableAppProperties.Package_name == nil {
+	packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+	if packageNameProp.IsEmpty() {
 		ctx.PropertyErrorf("privapp_allowlist", "package_name must be set to use privapp_allowlist")
 	}
 
-	packageName := *a.overridableAppProperties.Package_name
+	packageName := packageNameProp.Get()
 	fileName := "privapp_allowlist_" + packageName + ".xml"
-	outPath := android.PathForModuleOut(ctx, fileName).OutputPath
+	outPath := android.PathForModuleOut(ctx, fileName)
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   modifyAllowlist,
 		Input:  android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist),
@@ -830,7 +880,7 @@
 			"packageName": packageName,
 		},
 	})
-	return &outPath
+	return outPath
 }
 
 func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
@@ -894,10 +944,10 @@
 	// Process all building blocks, from AAPT to certificates.
 	a.aaptBuildActions(ctx)
 	// The decision to enforce <uses-library> checks is made before adding implicit SDK libraries.
-	a.usesLibrary.freezeEnforceUsesLibraries()
+	a.usesLibrary.freezeEnforceUsesLibraries(ctx)
 
 	// Check that the <uses-library> list is coherent with the manifest.
-	if a.usesLibrary.enforceUsesLibraries() {
+	if a.usesLibrary.enforceUsesLibraries(ctx) {
 		manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(
 			ctx, a.mergedManifestFile, &a.classLoaderContexts)
 		apkDeps = append(apkDeps, manifestCheckFile)
@@ -1058,12 +1108,23 @@
 			app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
 	}
 	jniLib, prebuiltJniPackages := collectJniDeps(ctx, shouldCollectRecursiveNativeDeps,
-		checkNativeSdkVersion, func(dep cc.LinkableInterface) bool {
-			return !dep.IsNdk(ctx.Config()) && !dep.IsStubs()
+		checkNativeSdkVersion, func(parent, child android.Module) bool {
+			apkInApex := ctx.Module().(android.ApexModule).NotInPlatform()
+			childLinkable, _ := child.(cc.LinkableInterface)
+			parentLinkable, _ := parent.(cc.LinkableInterface)
+			useStubsOfDep := childLinkable.IsStubs()
+			if apkInApex && parentLinkable != nil {
+				// APK-in-APEX
+				// If the parent is a linkable interface, use stubs if the dependency edge crosses an apex boundary.
+				useStubsOfDep = useStubsOfDep || (childLinkable.HasStubsVariants() && cc.ShouldUseStubForApex(ctx, parent, child))
+			}
+			return !childLinkable.IsNdk(ctx.Config()) && !useStubsOfDep
 		})
 
 	var certificates []Certificate
 
+	var directImplementationDeps android.Paths
+	var transitiveImplementationDeps []depset.DepSet[android.Path]
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -1075,14 +1136,25 @@
 				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
 			}
 		}
+
+		if IsJniDepTag(tag) {
+			directImplementationDeps = append(directImplementationDeps, android.OutputFileForModule(ctx, module, ""))
+			if info, ok := android.OtherModuleProvider(ctx, module, cc.ImplementationDepInfoProvider); ok {
+				transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps)
+			}
+		}
 	})
+	android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{
+		ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps),
+	})
+
 	return jniLib, prebuiltJniPackages, certificates
 }
 
 func collectJniDeps(ctx android.ModuleContext,
 	shouldCollectRecursiveNativeDeps bool,
 	checkNativeSdkVersion bool,
-	filter func(cc.LinkableInterface) bool) ([]jniLib, android.Paths) {
+	filter func(parent, child android.Module) bool) ([]jniLib, android.Paths) {
 	var jniLibs []jniLib
 	var prebuiltJniPackages android.Paths
 	seenModulePaths := make(map[string]bool)
@@ -1093,7 +1165,7 @@
 
 		if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) {
 			if dep, ok := module.(cc.LinkableInterface); ok {
-				if filter != nil && !filter(dep) {
+				if filter != nil && !filter(parent, module) {
 					return false
 				}
 
@@ -1141,7 +1213,7 @@
 	return jniLibs, prebuiltJniPackages
 }
 
-func (a *AndroidApp) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
+func (a *AndroidApp) WalkPayloadDeps(ctx android.BaseModuleContext, do android.PayloadDepsCallback) {
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		isExternal := !a.DepIsInSameApex(ctx, child)
 		if am, ok := child.(android.ApexModule); ok {
@@ -1159,7 +1231,7 @@
 	}
 
 	depsInfo := android.DepNameToDepInfoMap{}
-	a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
 		depName := to.Name()
 
 		// Skip dependencies that are only available to APEXes; they are developed with updatability
@@ -1212,16 +1284,12 @@
 	return Bool(a.appProperties.Updatable)
 }
 
-func (a *AndroidApp) SetUpdatable(val bool) {
-	a.appProperties.Updatable = &val
-}
-
 func (a *AndroidApp) getCertString(ctx android.BaseModuleContext) string {
 	certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName())
 	if overridden {
 		return ":" + certificate
 	}
-	return String(a.overridableAppProperties.Certificate)
+	return a.overridableAppProperties.Certificate.GetOrDefault(ctx, "")
 }
 
 func (a *AndroidApp) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
@@ -1323,20 +1391,22 @@
 			Srcs:  []string{":" + a.Name() + "{.apk}"},
 			Cmd:   proptools.StringPtr("$(location characteristics_rro_generator) $$($(location aapt2) dump packagename $(in)) $(out)"),
 		}
-		ctx.CreateModule(genrule.GenRuleFactory, &rroManifestProperties)
+		ctx.CreateModule(GenRuleFactory, &rroManifestProperties)
 
 		rroProperties := struct {
 			Name           *string
 			Filter_product *string
 			Aaptflags      []string
 			Manifest       *string
-			Resource_dirs  []string
+			Resource_dirs  proptools.Configurable[[]string]
+			Flags_packages []string
 		}{
 			Name:           proptools.StringPtr(rroPackageName),
 			Filter_product: proptools.StringPtr(characteristics),
 			Aaptflags:      []string{"--auto-add-overlay"},
 			Manifest:       proptools.StringPtr(":" + rroManifestName),
 			Resource_dirs:  a.aaptProperties.Resource_dirs,
+			Flags_packages: a.aaptProperties.Flags_packages,
 		}
 		if !Bool(a.aaptProperties.Aapt_include_all_resources) {
 			for _, aaptConfig := range ctx.Config().ProductAAPTConfig() {
@@ -1344,11 +1414,82 @@
 			}
 		}
 		ctx.CreateModule(RuntimeResourceOverlayFactory, &rroProperties)
+
+	})
+
+	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
+		createInternalRuntimeOverlays(ctx, module.ModuleBase)
 	})
 
 	return module
 }
 
+func AutogeneratedRroModuleName(ctx android.EarlyModuleContext, moduleName, partition string) string {
+	return fmt.Sprintf("%s__%s__auto_generated_rro_%s", moduleName, ctx.Config().DeviceProduct(), partition)
+}
+
+type createModuleContext interface {
+	android.EarlyModuleContext
+	CreateModule(android.ModuleFactory, ...interface{}) android.Module
+}
+
+func createInternalRuntimeOverlays(ctx createModuleContext, a android.ModuleBase) {
+	if !ctx.Config().HasDeviceProduct() {
+		return
+	}
+	// vendor
+	vendorOverlayProps := struct {
+		Name                *string
+		Base                *string
+		Vendor              *bool
+		Product_specific    *bool
+		System_ext_specific *bool
+		Manifest            *string
+		Sdk_version         *string
+		Compile_multilib    *string
+		Enabled             proptools.Configurable[bool]
+	}{
+		Name:                proptools.StringPtr(AutogeneratedRroModuleName(ctx, a.Name(), "vendor")),
+		Base:                proptools.StringPtr(a.Name()),
+		Vendor:              proptools.BoolPtr(true),
+		Product_specific:    proptools.BoolPtr(false),
+		System_ext_specific: proptools.BoolPtr(false),
+		Manifest:            proptools.StringPtr(":" + a.Name() + "{.manifest.xml}"),
+		Sdk_version:         proptools.StringPtr("current"),
+		Compile_multilib:    proptools.StringPtr("first"),
+		Enabled:             a.EnabledProperty().Clone(),
+	}
+	ctx.CreateModule(AutogenRuntimeResourceOverlayFactory, &vendorOverlayProps)
+
+	// product
+	productOverlayProps := struct {
+		Name                *string
+		Base                *string
+		Vendor              *bool
+		Proprietary         *bool
+		Soc_specific        *bool
+		Product_specific    *bool
+		System_ext_specific *bool
+		Manifest            *string
+		Sdk_version         *string
+		Compile_multilib    *string
+		Enabled             proptools.Configurable[bool]
+	}{
+		Name:                proptools.StringPtr(AutogeneratedRroModuleName(ctx, a.Name(), "product")),
+		Base:                proptools.StringPtr(a.Name()),
+		Vendor:              proptools.BoolPtr(false),
+		Proprietary:         proptools.BoolPtr(false),
+		Soc_specific:        proptools.BoolPtr(false),
+		Product_specific:    proptools.BoolPtr(true),
+		System_ext_specific: proptools.BoolPtr(false),
+		Manifest:            proptools.StringPtr(":" + a.Name() + "{.manifest.xml}"),
+		Sdk_version:         proptools.StringPtr("current"),
+		Compile_multilib:    proptools.StringPtr("first"),
+		Enabled:             a.EnabledProperty().Clone(),
+	}
+	ctx.CreateModule(AutogenRuntimeResourceOverlayFactory, &productOverlayProps)
+}
+
 // A dictionary of values to be overridden in the manifest.
 type Manifest_values struct {
 	// Overrides the value of package_name in the manifest
@@ -1411,7 +1552,8 @@
 	}
 	applicationId := a.appTestProperties.Manifest_values.ApplicationId
 	if applicationId != nil {
-		if a.overridableAppProperties.Package_name != nil {
+		packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+		if packageNameProp.IsPresent() {
 			ctx.PropertyErrorf("manifest_values.applicationId", "property is not supported when property package_name is set.")
 		}
 		a.aapt.manifestValues.applicationId = *applicationId
@@ -1423,13 +1565,17 @@
 	}
 
 	testConfig := tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config,
-		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config, configs)
+		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites,
+		a.testProperties.Auto_gen_config, configs, a.testProperties.Test_options.Test_runner_options)
 	a.testConfig = a.FixTestConfig(ctx, testConfig)
 	a.extraTestConfigs = android.PathsForModuleSrc(ctx, a.testProperties.Test_options.Extra_test_configs)
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
-	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_common_data)...)
+	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_first_data)...)
+	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_first_prefer32_data)...)
+
 	android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
-		InstalledFiles:          a.data,
+		TestcaseRelDataFiles:    testcaseRel(a.data),
 		OutputFile:              a.OutputFile(),
 		TestConfig:              a.testConfig,
 		HostRequiredModuleNames: a.HostRequiredModuleNames(),
@@ -1437,6 +1583,8 @@
 		IsHost:                  false,
 		LocalCertificate:        a.certificate.AndroidMkString(),
 		IsUnitTest:              Bool(a.testProperties.Test_options.Unit_test),
+		MkInclude:               "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
+		MkAppClass:              "APPS",
 	})
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
 		TestOnly:       true,
@@ -1445,6 +1593,14 @@
 
 }
 
+func testcaseRel(paths android.Paths) []string {
+	relPaths := []string{}
+	for _, p := range paths {
+		relPaths = append(relPaths, p.Rel())
+	}
+	return relPaths
+}
+
 func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig android.Path) android.Path {
 	if testConfig == nil {
 		return nil
@@ -1461,10 +1617,11 @@
 		command.FlagWithArg("--test-file-name ", a.installApkName+".apk")
 	}
 
-	if a.overridableAppProperties.Package_name != nil {
+	packageNameProp := a.overridableAppProperties.Package_name.Get(ctx)
+	if packageNameProp.IsPresent() {
 		fixNeeded = true
 		command.FlagWithInput("--manifest ", a.manifestPath).
-			FlagWithArg("--package-name ", *a.overridableAppProperties.Package_name)
+			FlagWithArg("--package-name ", packageNameProp.Get())
 	}
 
 	if a.appTestProperties.Mainline_package_name != nil {
@@ -1643,6 +1800,10 @@
 
 	android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	android.InitOverrideModule(m)
+	android.AddLoadHookWithPriority(m, func(ctx android.LoadHookContext) {
+		createInternalRuntimeOverlays(ctx, m.ModuleBase)
+	}, 1) // Run after soong config load hoook
+
 	return m
 }
 
@@ -1674,11 +1835,11 @@
 
 type UsesLibraryProperties struct {
 	// A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file.
-	Uses_libs []string
+	Uses_libs proptools.Configurable[[]string]
 
 	// A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file with
 	// required=false.
-	Optional_uses_libs []string
+	Optional_uses_libs proptools.Configurable[[]string]
 
 	// If true, the list of uses_libs and optional_uses_libs modules must match the AndroidManifest.xml file.  Defaults
 	// to true if either uses_libs or optional_uses_libs is set.  Will unconditionally default to true in the future.
@@ -1726,7 +1887,7 @@
 
 func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, addCompatDeps bool) {
 	if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() {
-		ctx.AddVariationDependencies(nil, usesLibReqTag, u.usesLibraryProperties.Uses_libs...)
+		ctx.AddVariationDependencies(nil, usesLibReqTag, u.usesLibraryProperties.Uses_libs.GetOrDefault(ctx, nil)...)
 		presentOptionalUsesLibs := u.presentOptionalUsesLibs(ctx)
 		ctx.AddVariationDependencies(nil, usesLibOptTag, presentOptionalUsesLibs...)
 		// Only add these extra dependencies if the module is an app that depends on framework
@@ -1739,17 +1900,17 @@
 			ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
 			ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
 		}
-		_, diff, _ := android.ListSetDifference(u.usesLibraryProperties.Optional_uses_libs, presentOptionalUsesLibs)
+		_, diff, _ := android.ListSetDifference(u.usesLibraryProperties.Optional_uses_libs.GetOrDefault(ctx, nil), presentOptionalUsesLibs)
 		u.usesLibraryProperties.Missing_optional_uses_libs = diff
 	} else {
-		ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.usesLibraryProperties.Uses_libs...)
+		ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.usesLibraryProperties.Uses_libs.GetOrDefault(ctx, nil)...)
 		ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.presentOptionalUsesLibs(ctx)...)
 	}
 }
 
 // presentOptionalUsesLibs returns optional_uses_libs after filtering out libraries that don't exist in the source tree.
 func (u *usesLibrary) presentOptionalUsesLibs(ctx android.BaseModuleContext) []string {
-	optionalUsesLibs := android.FilterListPred(u.usesLibraryProperties.Optional_uses_libs, func(s string) bool {
+	optionalUsesLibs := android.FilterListPred(u.usesLibraryProperties.Optional_uses_libs.GetOrDefault(ctx, nil), func(s string) bool {
 		exists := ctx.OtherModuleExists(s)
 		if !exists && !android.InList(ctx.ModuleName(), ctx.Config().BuildWarningBadOptionalUsesLibsAllowlist()) {
 			fmt.Printf("Warning: Module '%s' depends on non-existing optional_uses_libs '%s'\n", ctx.ModuleName(), s)
@@ -1787,16 +1948,15 @@
 			}
 		}
 
-		// Skip java_sdk_library dependencies that provide stubs, but not an implementation.
-		// This will be restricted to optional_uses_libs
-		if sdklib, ok := m.(SdkLibraryDependency); ok {
-			if tag == usesLibOptTag && sdklib.DexJarBuildPath(ctx).PathOrNil() == nil {
-				u.shouldDisableDexpreopt = true
-				return
-			}
-		}
-
 		if lib, ok := m.(UsesLibraryDependency); ok {
+			if _, ok := android.OtherModuleProvider(ctx, m, SdkLibraryInfoProvider); ok {
+				// Skip java_sdk_library dependencies that provide stubs, but not an implementation.
+				// This will be restricted to optional_uses_libs
+				if tag == usesLibOptTag && lib.DexJarBuildPath(ctx).PathOrNil() == nil {
+					u.shouldDisableDexpreopt = true
+					return
+				}
+			}
 			libName := dep
 			if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
 				libName = *ulib.ProvidesUsesLib()
@@ -1816,15 +1976,15 @@
 // enforceUsesLibraries returns true of <uses-library> tags should be checked against uses_libs and optional_uses_libs
 // properties.  Defaults to true if either of uses_libs or optional_uses_libs is specified.  Will default to true
 // unconditionally in the future.
-func (u *usesLibrary) enforceUsesLibraries() bool {
-	defaultEnforceUsesLibs := len(u.usesLibraryProperties.Uses_libs) > 0 ||
-		len(u.usesLibraryProperties.Optional_uses_libs) > 0
+func (u *usesLibrary) enforceUsesLibraries(ctx android.ModuleContext) bool {
+	defaultEnforceUsesLibs := len(u.usesLibraryProperties.Uses_libs.GetOrDefault(ctx, nil)) > 0 ||
+		len(u.usesLibraryProperties.Optional_uses_libs.GetOrDefault(ctx, nil)) > 0
 	return BoolDefault(u.usesLibraryProperties.Enforce_uses_libs, u.enforce || defaultEnforceUsesLibs)
 }
 
 // Freeze the value of `enforce_uses_libs` based on the current values of `uses_libs` and `optional_uses_libs`.
-func (u *usesLibrary) freezeEnforceUsesLibraries() {
-	enforce := u.enforceUsesLibraries()
+func (u *usesLibrary) freezeEnforceUsesLibraries(ctx android.ModuleContext) {
+	enforce := u.enforceUsesLibraries(ctx)
 	u.usesLibraryProperties.Enforce_uses_libs = &enforce
 }
 
diff --git a/java/app_import.go b/java/app_import.go
index 045a89a..8951c7d 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -61,6 +61,9 @@
 func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
 	ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
+	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("disable_prebuilts_without_apk", disablePrebuiltsWithoutApkMutator)
+	})
 }
 
 type AndroidAppImport struct {
@@ -85,16 +88,16 @@
 
 	hideApexVariantFromMake bool
 
-	provenanceMetaDataFile android.OutputPath
+	provenanceMetaDataFile android.Path
 }
 
 type AndroidAppImportProperties struct {
 	// A prebuilt apk to import
-	Apk *string `android:"path"`
+	Apk proptools.Configurable[string] `android:"path,replace_instead_of_append"`
 
 	// The name of a certificate in the default certificate directory or an android_app_certificate
 	// module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
-	Certificate *string
+	Certificate proptools.Configurable[string] `android:"replace_instead_of_append"`
 
 	// Names of extra android_app_certificate modules to sign the apk with in the form ":module".
 	Additional_certificates []string
@@ -193,13 +196,6 @@
 			}
 		}
 	}
-
-	if String(a.properties.Apk) == "" {
-		// Disable this module since the apk property is still empty after processing all matching
-		// variants. This likely means there is no matching variant, and the default variant doesn't
-		// have an apk property value either.
-		a.Disable()
-	}
 }
 
 func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
@@ -219,8 +215,32 @@
 	}
 }
 
+// disablePrebuiltsWithoutApkMutator is a pre-arch mutator that disables AndroidAppImport or
+// AndroidTestImport modules that don't have an apk set. We need this separate mutator instead
+// of doing it in processVariants because processVariants is a defaultable hook, and configurable
+// properties can only be evaluated after the defaults (and eventually, base configurabtion)
+// mutators.
+func disablePrebuiltsWithoutApkMutator(ctx android.BottomUpMutatorContext) {
+	switch a := ctx.Module().(type) {
+	case *AndroidAppImport:
+		if a.properties.Apk.GetOrDefault(ctx, "") == "" {
+			// Disable this module since the apk property is still empty after processing all
+			// matching variants. This likely means there is no matching variant, and the default
+			// variant doesn't have an apk property value either.
+			a.Disable()
+		}
+	case *AndroidTestImport:
+		if a.properties.Apk.GetOrDefault(ctx, "") == "" {
+			// Disable this module since the apk property is still empty after processing all
+			// matching variants. This likely means there is no matching variant, and the default
+			// variant doesn't have an apk property value either.
+			a.Disable()
+		}
+	}
+}
+
 func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
-	cert := android.SrcIsModule(String(a.properties.Certificate))
+	cert := android.SrcIsModule(a.properties.Certificate.GetOrDefault(ctx, ""))
 	if cert != "" {
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
@@ -239,7 +259,7 @@
 }
 
 func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
-	ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
+	ctx android.ModuleContext, inputPath android.Path, outputPath android.WritablePath) {
 	// Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
 	// with them may invalidate pre-existing signature data.
 	if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || Bool(a.properties.Preprocessed)) {
@@ -303,7 +323,7 @@
 	}
 
 	numCertPropsSet := 0
-	if String(a.properties.Certificate) != "" {
+	if a.properties.Certificate.GetOrDefault(ctx, "") != "" {
 		numCertPropsSet++
 	}
 	if Bool(a.properties.Presigned) {
@@ -325,7 +345,7 @@
 
 	// Uncompress JNI libraries in the apk
 	jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
-	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
+	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed)
 
 	var pathFragments []string
 	relInstallPath := String(a.properties.Relative_install_path)
@@ -344,13 +364,13 @@
 	a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
 	a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
 
-	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
+	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries(ctx)
 	a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 	if a.usesLibrary.shouldDisableDexpreopt {
 		a.dexpreopter.disableDexpreopt()
 	}
 
-	if a.usesLibrary.enforceUsesLibraries() {
+	if a.usesLibrary.enforceUsesLibraries(ctx) {
 		a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk, &a.dexpreopter.classLoaderContexts)
 	}
 
@@ -386,7 +406,7 @@
 		// If the certificate property is empty at this point, default_dev_cert must be set to true.
 		// Which makes processMainCert's behavior for the empty cert string WAI.
 		_, _, certificates := collectAppDeps(ctx, a, false, false)
-		a.certificate, certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
+		a.certificate, certificates = processMainCert(a.ModuleBase, a.properties.Certificate.GetOrDefault(ctx, ""), certificates, ctx)
 		signed := android.PathForModuleOut(ctx, "signed", apkFilename)
 		var lineageFile android.Path
 		if lineage := String(a.properties.Lineage); lineage != "" {
@@ -409,7 +429,7 @@
 
 	if apexInfo.IsForPlatform() {
 		a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
-		artifactPath := android.PathForModuleSrc(ctx, *a.properties.Apk)
+		artifactPath := android.PathForModuleSrc(ctx, a.properties.Apk.GetOrDefault(ctx, ""))
 		a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath)
 	}
 
@@ -473,7 +493,7 @@
 	return a.certificate
 }
 
-func (a *AndroidAppImport) ProvenanceMetaDataFile() android.OutputPath {
+func (a *AndroidAppImport) ProvenanceMetaDataFile() android.Path {
 	return a.provenanceMetaDataFile
 }
 
@@ -533,10 +553,6 @@
 	return android.SdkSpecPrivate.ApiLevel
 }
 
-func (a *AndroidAppImport) LintDepSets() LintDepSets {
-	return LintDepSets{}
-}
-
 var _ android.ApexModule = (*AndroidAppImport)(nil)
 
 // Implements android.ApexModule
@@ -637,7 +653,7 @@
 	android.InitApexModule(module)
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
-	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+	android.InitConfigurablePrebuiltModuleString(module, &module.properties.Apk, "Apk")
 
 	module.usesLibrary.enforce = true
 
@@ -690,7 +706,7 @@
 	android.InitApexModule(module)
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
-	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+	android.InitConfigurablePrebuiltModuleString(module, &module.properties.Apk, "Apk")
 
 	return module
 }
diff --git a/java/app_test.go b/java/app_test.go
index 5ec0ad4..e9422a6 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -533,9 +533,9 @@
 	`)
 	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
 	android.AssertStringDoesContain(t,
-		"com.android.foo: expected manifest fixer to set override-placeholder-version to android.DefaultUpdatableModuleVersion",
+		"com.android.foo: expected manifest fixer to set override-placeholder-version to RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION",
 		foo.BuildParams.Args["args"],
-		fmt.Sprintf("--override-placeholder-version %s", android.DefaultUpdatableModuleVersion),
+		fmt.Sprintf("--override-placeholder-version %s", testDefaultUpdatableModuleVersion),
 	)
 }
 
@@ -1422,26 +1422,31 @@
 }
 
 func TestAndroidResourceOverlays(t *testing.T) {
+	type moduleAndVariant struct {
+		module  string
+		variant string
+	}
+
 	testCases := []struct {
 		name                       string
 		enforceRROTargets          []string
 		enforceRROExcludedOverlays []string
-		resourceFiles              map[string][]string
-		overlayFiles               map[string][]string
-		rroDirs                    map[string][]string
+		resourceFiles              map[moduleAndVariant][]string
+		overlayFiles               map[moduleAndVariant][]string
+		rroDirs                    map[moduleAndVariant][]string
 	}{
 		{
 			name:                       "no RRO",
 			enforceRROTargets:          nil,
 			enforceRROExcludedOverlays: nil,
-			resourceFiles: map[string][]string{
-				"foo":  nil,
-				"bar":  {"bar/res/res/values/strings.xml"},
-				"lib":  nil,
-				"lib2": {"lib2/res/res/values/strings.xml"},
+			resourceFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}:  nil,
+				{"bar", "android_common"}:  {"bar/res/res/values/strings.xml"},
+				{"lib", "android_common"}:  nil,
+				{"lib2", "android_common"}: {"lib2/res/res/values/strings.xml"},
 			},
-			overlayFiles: map[string][]string{
-				"foo": {
+			overlayFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: {
 					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"out/soong/.intermediates/lib/android_common/package-res.apk",
 					"out/soong/.intermediates/lib3/android_common/package-res.apk",
@@ -1450,57 +1455,65 @@
 					"device/vendor/blah/overlay/foo/res/values/strings.xml",
 					"product/vendor/blah/overlay/foo/res/values/strings.xml",
 				},
-				"bar": {
+				{"bar", "android_common"}: {
 					"device/vendor/blah/static_overlay/bar/res/values/strings.xml",
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
-				"lib": {
+				{"lib", "android_common"}: {
 					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 					"device/vendor/blah/overlay/lib/res/values/strings.xml",
 				},
 			},
-			rroDirs: map[string][]string{
-				"foo": nil,
-				"bar": nil,
+			rroDirs: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: nil,
+				{"bar", "android_common"}: nil,
 			},
 		},
 		{
 			name:                       "enforce RRO on foo",
 			enforceRROTargets:          []string{"foo"},
 			enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"},
-			resourceFiles: map[string][]string{
-				"foo":  nil,
-				"bar":  {"bar/res/res/values/strings.xml"},
-				"lib":  nil,
-				"lib2": {"lib2/res/res/values/strings.xml"},
+			resourceFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}:      nil,
+				{"bar", "android_common"}:      {"bar/res/res/values/strings.xml"},
+				{"lib", "android_common"}:      nil,
+				{"lib", "android_common_rro"}:  nil,
+				{"lib2", "android_common"}:     {"lib2/res/res/values/strings.xml"},
+				{"lib2", "android_common_rro"}: {"lib2/res/res/values/strings.xml"},
 			},
-			overlayFiles: map[string][]string{
-				"foo": {
-					"out/soong/.intermediates/lib2/android_common/package-res.apk",
-					"out/soong/.intermediates/lib/android_common/package-res.apk",
-					"out/soong/.intermediates/lib3/android_common/package-res.apk",
+			overlayFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: {
+					"out/soong/.intermediates/lib2/android_common_rro/package-res.apk",
+					"out/soong/.intermediates/lib/android_common_rro/package-res.apk",
+					"out/soong/.intermediates/lib3/android_common_rro/package-res.apk",
 					"foo/res/res/values/strings.xml",
 					"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
 				},
-				"bar": {
+				{"bar", "android_common"}: {
 					"device/vendor/blah/static_overlay/bar/res/values/strings.xml",
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
-				"lib": {
+				{"lib", "android_common"}: {
 					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
+					"device/vendor/blah/overlay/lib/res/values/strings.xml",
+				},
+				{"lib", "android_common_rro"}: {
+					"out/soong/.intermediates/lib2/android_common_rro/package-res.apk",
+					"lib/res/res/values/strings.xml",
 				},
 			},
 
-			rroDirs: map[string][]string{
-				"foo": {
+			rroDirs: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: {
 					"device:device/vendor/blah/overlay/foo/res",
 					"product:product/vendor/blah/overlay/foo/res",
 					"device:device/vendor/blah/overlay/lib/res",
 				},
-				"bar": nil,
-				"lib": {"device:device/vendor/blah/overlay/lib/res"},
+				{"bar", "android_common"}:     nil,
+				{"lib", "android_common"}:     nil,
+				{"lib", "android_common_rro"}: {"device:device/vendor/blah/overlay/lib/res"},
 			},
 		},
 		{
@@ -1511,35 +1524,35 @@
 				"device/vendor/blah/static_overlay/foo",
 				"device/vendor/blah/static_overlay/bar/res",
 			},
-			resourceFiles: map[string][]string{
-				"foo":  nil,
-				"bar":  {"bar/res/res/values/strings.xml"},
-				"lib":  nil,
-				"lib2": {"lib2/res/res/values/strings.xml"},
+			resourceFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}:  nil,
+				{"bar", "android_common"}:  {"bar/res/res/values/strings.xml"},
+				{"lib", "android_common"}:  nil,
+				{"lib2", "android_common"}: {"lib2/res/res/values/strings.xml"},
 			},
-			overlayFiles: map[string][]string{
-				"foo": {
+			overlayFiles: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: {
 					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"out/soong/.intermediates/lib/android_common/package-res.apk",
 					"out/soong/.intermediates/lib3/android_common/package-res.apk",
 					"foo/res/res/values/strings.xml",
 					"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
 				},
-				"bar": {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
-				"lib": {
+				{"bar", "android_common"}: {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
+				{"lib", "android_common"}: {
 					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 				},
 			},
-			rroDirs: map[string][]string{
-				"foo": {
+			rroDirs: map[moduleAndVariant][]string{
+				{"foo", "android_common"}: {
 					"device:device/vendor/blah/overlay/foo/res",
 					"product:product/vendor/blah/overlay/foo/res",
 					// Lib dep comes after the direct deps
 					"device:device/vendor/blah/overlay/lib/res",
 				},
-				"bar": {"device:device/vendor/blah/overlay/bar/res"},
-				"lib": {"device:device/vendor/blah/overlay/lib/res"},
+				{"bar", "android_common"}: {"device:device/vendor/blah/overlay/bar/res"},
+				{"lib", "android_common"}: {"device:device/vendor/blah/overlay/lib/res"},
 			},
 		},
 	}
@@ -1624,19 +1637,19 @@
 				for _, o := range list {
 					res := module.MaybeOutput(o)
 					if res.Rule != nil {
-						// If the overlay is compiled as part of this module (i.e. a .arsc.flat file),
+						// If the overlay is compiled as part of this moduleAndVariant (i.e. a .arsc.flat file),
 						// verify the inputs to the .arsc.flat rule.
 						files = append(files, res.Inputs.Strings()...)
 					} else {
-						// Otherwise, verify the full path to the output of the other module
+						// Otherwise, verify the full path to the output of the other moduleAndVariant
 						files = append(files, o)
 					}
 				}
 				return files
 			}
 
-			getResources := func(moduleName string) (resourceFiles, overlayFiles, rroDirs []string) {
-				module := result.ModuleForTests(moduleName, "android_common")
+			getResources := func(moduleName, variantName string) (resourceFiles, overlayFiles, rroDirs []string) {
+				module := result.ModuleForTests(moduleName, variantName)
 				resourceList := module.MaybeOutput("aapt2/res.list")
 				if resourceList.Rule != nil {
 					resourceFiles = resourceListToFiles(module, android.PathsRelativeToTop(resourceList.Inputs))
@@ -1661,21 +1674,33 @@
 				return resourceFiles, overlayFiles, rroDirs
 			}
 
-			modules := []string{"foo", "bar", "lib", "lib2"}
-			for _, module := range modules {
-				resourceFiles, overlayFiles, rroDirs := getResources(module)
+			modules := []moduleAndVariant{
+				{"foo", "android_common"},
+				{"foo", "android_common_rro"},
+				{"bar", "android_common"},
+				{"bar", "android_common_rro"},
+				{"lib", "android_common"},
+				{"lib", "android_common_rro"},
+				{"lib2", "android_common"},
+				{"lib2", "android_common_rro"},
+			}
+			for _, moduleAndVariant := range modules {
+				if _, exists := testCase.resourceFiles[moduleAndVariant]; !exists {
+					continue
+				}
+				resourceFiles, overlayFiles, rroDirs := getResources(moduleAndVariant.module, moduleAndVariant.variant)
 
-				if !reflect.DeepEqual(resourceFiles, testCase.resourceFiles[module]) {
+				if !reflect.DeepEqual(resourceFiles, testCase.resourceFiles[moduleAndVariant]) {
 					t.Errorf("expected %s resource files:\n  %#v\n got:\n  %#v",
-						module, testCase.resourceFiles[module], resourceFiles)
+						moduleAndVariant, testCase.resourceFiles[moduleAndVariant], resourceFiles)
 				}
-				if !reflect.DeepEqual(overlayFiles, testCase.overlayFiles[module]) {
+				if !reflect.DeepEqual(overlayFiles, testCase.overlayFiles[moduleAndVariant]) {
 					t.Errorf("expected %s overlay files:\n  %#v\n got:\n  %#v",
-						module, testCase.overlayFiles[module], overlayFiles)
+						moduleAndVariant, testCase.overlayFiles[moduleAndVariant], overlayFiles)
 				}
-				if !reflect.DeepEqual(rroDirs, testCase.rroDirs[module]) {
+				if !reflect.DeepEqual(rroDirs, testCase.rroDirs[moduleAndVariant]) {
 					t.Errorf("expected %s rroDirs:  %#v\n got:\n  %#v",
-						module, testCase.rroDirs[module], rroDirs)
+						moduleAndVariant, testCase.rroDirs[moduleAndVariant], rroDirs)
 				}
 			}
 		})
@@ -4657,3 +4682,195 @@
 		"--override-placeholder-version",
 	)
 }
+
+func TestResourcesWithFlagDirectories(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.FixtureMergeMockFs(android.MockFS{
+			"res/flag(test.package.flag1)/values/bools.xml":                          nil,
+			"res/flag(!test.package.flag2)/values/bools.xml":                         nil,
+			"res/flag(test.package.flag1)/values-config/strings_google_services.xml": nil,
+			"res/flags(test.package.flag1)/values/strings.xml":                       nil,
+		}),
+	).RunTestWithBp(t, `
+		android_library {
+			name: "foo",
+			srcs: ["a.java"],
+			use_resource_processor: true,
+			resource_dirs: [
+				"res",
+			],
+		}
+	`)
+	fooModule := result.ModuleForTests("foo", "android_common")
+	compileOutputPaths := fooModule.Rule("aapt2Compile").Outputs.Strings()
+
+	android.AssertStringListContains(
+		t,
+		"Expected to generate flag path",
+		compileOutputPaths,
+		"out/soong/.intermediates/foo/android_common/aapt2/res/values_bools.(test.package.flag1).arsc.flat",
+	)
+	android.AssertStringListContains(
+		t,
+		"Expected to generate flag path with ! prefix in name",
+		compileOutputPaths,
+		"out/soong/.intermediates/foo/android_common/aapt2/res/values_bools.(!test.package.flag2).arsc.flat",
+	)
+	android.AssertStringListContains(
+		t,
+		"Expected to generate flag path with configs",
+		compileOutputPaths,
+		"out/soong/.intermediates/foo/android_common/aapt2/res/values-config_strings_google_services.(test.package.flag1).arsc.flat",
+	)
+	android.AssertStringListDoesNotContain(
+		t,
+		"Expected to not generate flag path with non-flag(flag_name) pattern",
+		compileOutputPaths,
+		"out/soong/.intermediates/foo/android_common/aapt2/res/values_strings.(test.package.flag1).arsc.flat",
+	)
+}
+
+func TestAutogeneratedStaticRro(t *testing.T) {
+	t.Parallel()
+	bp := `
+android_app {
+	name: "foo",
+	srcs: ["foo.java"],
+	platform_apis: true,
+}
+override_android_app {
+	name: "override_foo",
+	base: "foo",
+}
+`
+	testCases := []struct {
+		desc               string
+		preparer           android.FixturePreparer
+		overlayApkExpected bool
+	}{
+		{
+			desc:               "No DEVICE_PACKAGE_OVERLAYS, no overlay .apk file",
+			overlayApkExpected: false,
+		},
+		{
+			desc:               "DEVICE_PACKAGE_OVERLAYS exists, but the directory is empty",
+			overlayApkExpected: false,
+			preparer: android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.DeviceResourceOverlays = []string{"device/company/test_product"}
+			}),
+		},
+		{
+			desc:               "DEVICE_PACKAGE_OVERLAYS exists, directory is non-empty, but does not contain a matching resource dir",
+			overlayApkExpected: false,
+			preparer: android.GroupFixturePreparers(
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.DeviceResourceOverlays = []string{"device/company/test_product"}
+				}),
+				android.MockFS{
+					"res/foo.xml": nil,
+					"device/company/test_product/different_res/foo.xml": nil, // different dir
+				}.AddToFixture(),
+			),
+		},
+		{
+			desc:               "DEVICE_PACKAGE_OVERLAYS and the directory contain a matching resource dir",
+			overlayApkExpected: true,
+			preparer: android.GroupFixturePreparers(
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.DeviceResourceOverlays = []string{"device/company/test_product"}
+				}),
+				android.MockFS{
+					"res/foo.xml": nil,
+					"device/company/test_product/res/foo.xml": nil,
+				}.AddToFixture(),
+			),
+		},
+	}
+	for _, tc := range testCases {
+		result := android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.EnforceRROTargets = []string{"*"}
+			}),
+			android.OptionalFixturePreparer(tc.preparer),
+		).RunTestWithBp(t, bp)
+		vendorOverlayApk := result.ModuleForTests("foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("foo__test_product__auto_generated_rro_vendor.apk")
+		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, vendorOverlayApk.Rule != nil)
+		overrideVendorOverlayApk := result.ModuleForTests("override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("override_foo__test_product__auto_generated_rro_vendor.apk")
+		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.Rule != nil)
+	}
+}
+
+func TestNoAutogeneratedStaticRroForDisabledOverrideApps(t *testing.T) {
+	t.Parallel()
+	bp := `
+soong_config_module_type {
+	name: "my_custom_override_android_app",
+	module_type: "override_android_app",
+	config_namespace: "my_namespace",
+	value_variables: ["my_app_enabled"],
+	properties: ["enabled"],
+}
+soong_config_bool_variable {
+	name: "my_app_enabled",
+}
+android_app {
+	name: "foo",
+	srcs: ["foo.java"],
+	platform_apis: true,
+}
+my_custom_override_android_app {
+	name: "override_foo",
+	base: "foo",
+	soong_config_variables: {
+		my_app_enabled: {
+			enabled: true,
+			conditions_default: {
+				enabled: false
+			},
+		},
+	}
+}
+`
+	testCases := []struct {
+		desc               string
+		preparer           android.FixturePreparer
+		overlayApkExpected bool
+	}{
+		{
+			desc:               "my_app_enabled is empty",
+			overlayApkExpected: false,
+		},
+		{
+			desc:               "my_app_enabled is true",
+			overlayApkExpected: true,
+			preparer: android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.VendorVars = map[string]map[string]string{
+					"my_namespace": {
+						"my_app_enabled": "true",
+					},
+				}
+			}),
+		},
+	}
+	for _, tc := range testCases {
+		result := android.GroupFixturePreparers(
+			PrepareForTestWithJavaDefaultModules,
+			android.PrepareForTestWithSoongConfigModuleBuildComponents,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.EnforceRROTargets = []string{"*"}
+			}),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.DeviceResourceOverlays = []string{"device/company/test_product"}
+			}),
+			android.MockFS{
+				"res/foo.xml": nil,
+				"device/company/test_product/res/foo.xml": nil,
+			}.AddToFixture(),
+			android.OptionalFixturePreparer(tc.preparer),
+		).RunTestWithBp(t, bp)
+		overrideVendorOverlayApk := result.ModuleForTests("override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").Module().(*AutogenRuntimeResourceOverlay)
+		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.exportPackage != nil)
+	}
+}
diff --git a/java/base.go b/java/base.go
index b64eb5b..c0ac4ab 100644
--- a/java/base.go
+++ b/java/base.go
@@ -24,6 +24,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 
@@ -66,11 +67,20 @@
 	Exclude_java_resource_dirs []string `android:"arch_variant"`
 
 	// list of files to use as Java resources
-	Java_resources []string `android:"path,arch_variant"`
+	Java_resources proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	// list of files that should be excluded from java_resources and java_resource_dirs
 	Exclude_java_resources []string `android:"path,arch_variant"`
 
+	// Same as java_resources, but modules added here will use the device variant. Can be useful
+	// for making a host test that tests the contents of a device built app.
+	Device_common_java_resources proptools.Configurable[[]string] `android:"path_device_common"`
+
+	// Same as java_resources, but modules added here will use the device's os variant and the
+	// device's first architecture variant. Can be useful for making a host test that tests the
+	// contents of a native device built app.
+	Device_first_java_resources proptools.Configurable[[]string] `android:"path_device_first"`
+
 	// list of module-specific flags that will be used for javac compiles
 	Javacflags []string `android:"arch_variant"`
 
@@ -83,9 +93,6 @@
 	// list of java libraries that will be compiled into the resulting jar
 	Static_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
-	// list of java libraries that should not be used to build this module
-	Exclude_static_libs []string `android:"arch_variant"`
-
 	// manifest file to be included in resulting jar
 	Manifest *string `android:"path"`
 
@@ -116,6 +123,9 @@
 	// List of modules to use as annotation processors
 	Plugins []string
 
+	// List of modules to use as kotlin plugin
+	Kotlin_plugins []string
+
 	// List of modules to export to libraries that directly depend on this library as annotation
 	// processors.  Note that if the plugins set generates_api: true this will disable the turbine
 	// optimization on modules that depend on this module, which will reduce parallelism and cause
@@ -221,11 +231,15 @@
 	// the stubs via static libs.
 	Is_stubs_module *bool
 
-	// If true, enable the "Ravenizer" tool on the output jar.
-	// "Ravenizer" is a tool for Ravenwood tests, but it can also be enabled on other kinds
-	// of java targets.
 	Ravenizer struct {
+		// If true, enable the "Ravenizer" tool on the output jar.
+		// "Ravenizer" is a tool for Ravenwood tests, but it can also be enabled on other kinds
+		// of java targets.
 		Enabled *bool
+
+		// If true, the "Ravenizer" tool will remove all Mockito and DexMaker
+		// classes from the output jar.
+		Strip_mockito *bool
 	}
 
 	// Contributing api surface of the stub module. Is not visible to bp modules, and should
@@ -470,7 +484,7 @@
 	srcJarDeps android.Paths
 
 	// the source files of this module and all its static dependencies
-	transitiveSrcFiles *android.DepSet[android.Path]
+	transitiveSrcFiles depset.DepSet[android.Path]
 
 	// jar file containing implementation classes and resources including static library
 	// dependencies
@@ -598,21 +612,24 @@
 	return proptools.Bool(j.properties.Is_stubs_module)
 }
 
-func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
-	sdkVersion := j.SdkVersion(ctx)
-	if sdkVersion.Stable() {
-		return nil
-	}
-	if sdkVersion.Kind == android.SdkCorePlatform {
-		if useLegacyCorePlatformApi(ctx, j.BaseModuleName()) {
-			return fmt.Errorf("non stable SDK %v - uses legacy core platform", sdkVersion)
-		} else {
-			// Treat stable core platform as stable.
+func CheckStableSdkVersion(ctx android.BaseModuleContext, module android.ModuleProxy) error {
+	if info, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+		if info.SdkVersion.Stable() {
 			return nil
 		}
-	} else {
-		return fmt.Errorf("non stable SDK %v", sdkVersion)
+		if info.SdkVersion.Kind == android.SdkCorePlatform {
+			if useLegacyCorePlatformApi(ctx, android.OtherModuleProviderOrDefault(ctx, module, android.CommonModuleInfoKey).BaseModuleName) {
+				return fmt.Errorf("non stable SDK %v - uses legacy core platform", info.SdkVersion)
+			} else {
+				// Treat stable core platform as stable.
+				return nil
+			}
+		} else {
+			return fmt.Errorf("non stable SDK %v", info.SdkVersion)
+		}
 	}
+
+	return nil
 }
 
 // checkSdkVersions enforces restrictions around SDK dependencies.
@@ -700,17 +717,14 @@
 
 // helper method for java modules to set OutputFilesProvider
 func setOutputFiles(ctx android.ModuleContext, m Module) {
-	ctx.SetOutputFiles(append(android.Paths{m.outputFile}, m.extraOutputFiles...), "")
-	ctx.SetOutputFiles(android.Paths{m.outputFile}, android.DefaultDistTag)
-	ctx.SetOutputFiles(android.Paths{m.implementationAndResourcesJar}, ".jar")
-	ctx.SetOutputFiles(android.Paths{m.headerJarFile}, ".hjar")
+	ctx.SetOutputFiles(append(android.PathsIfNonNil(m.outputFile), m.extraOutputFiles...), "")
+	ctx.SetOutputFiles(android.PathsIfNonNil(m.outputFile), android.DefaultDistTag)
+	ctx.SetOutputFiles(android.PathsIfNonNil(m.implementationAndResourcesJar), ".jar")
+	ctx.SetOutputFiles(android.PathsIfNonNil(m.headerJarFile), ".hjar")
 	if m.dexer.proguardDictionary.Valid() {
 		ctx.SetOutputFiles(android.Paths{m.dexer.proguardDictionary.Path()}, ".proguard_map")
 	}
 	ctx.SetOutputFiles(m.properties.Generated_srcjars, ".generated_srcjars")
-	if m.linter.outputs.xml != nil {
-		ctx.SetOutputFiles(android.Paths{m.linter.outputs.xml}, ".lint")
-	}
 }
 
 func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
@@ -755,7 +769,8 @@
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	isJacocoAgent := ctx.ModuleName() == "jacocoagent"
 
-	if j.DirectlyInAnyApex() && !isJacocoAgent && !apexInfo.IsForPlatform() {
+	compileDex := Bool(j.dexProperties.Compile_dex) || Bool(j.properties.Installable)
+	if compileDex && !isJacocoAgent && !apexInfo.IsForPlatform() {
 		if !inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
 			return true
 		} else if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
@@ -830,7 +845,7 @@
 }
 
 func (j *Module) staticLibs(ctx android.BaseModuleContext) []string {
-	return android.RemoveListFromList(j.properties.Static_libs.GetOrDefault(ctx, nil), j.properties.Exclude_static_libs)
+	return j.properties.Static_libs.GetOrDefault(ctx, nil)
 }
 
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
@@ -854,33 +869,6 @@
 	// Add dependency on libraries that provide additional hidden api annotations.
 	ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...)
 
-	if ctx.Config().EnforceInterPartitionJavaSdkLibrary() {
-		// Require java_sdk_library at inter-partition java dependency to ensure stable
-		// interface between partitions. If inter-partition java_library dependency is detected,
-		// raise build error because java_library doesn't have a stable interface.
-		//
-		// Inputs:
-		//    PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY
-		//      if true, enable enforcement
-		//    PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST
-		//      exception list of java_library names to allow inter-partition dependency
-		for idx := range j.properties.Libs {
-			if libDeps[idx] == nil {
-				continue
-			}
-
-			if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok {
-				// java_sdk_library is always allowed at inter-partition dependency.
-				// So, skip check.
-				if _, ok := javaDep.(*SdkLibrary); ok {
-					continue
-				}
-
-				j.checkPartitionsForJavaDependency(ctx, "libs", javaDep)
-			}
-		}
-	}
-
 	// For library dependencies that are component libraries (like stubs), add the implementation
 	// as a dependency (dexpreopt needs to be against the implementation library, not stubs).
 	for _, dep := range libDeps {
@@ -891,7 +879,7 @@
 					// explicitly listed in the optional_uses_libs property.
 					tag := usesLibReqTag
 					if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) ||
-						android.InList(*lib, j.usesLibrary.usesLibraryProperties.Optional_uses_libs) {
+						android.InList(*lib, j.usesLibrary.usesLibraryProperties.Optional_uses_libs.GetOrDefault(ctx, nil)) {
 						tag = usesLibOptTag
 					}
 					ctx.AddVariationDependencies(nil, tag, *lib)
@@ -901,6 +889,7 @@
 	}
 
 	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), pluginTag, j.properties.Plugins...)
+	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag, j.properties.Kotlin_plugins...)
 	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), errorpronePluginTag, j.properties.Errorprone.Extra_check_modules...)
 	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), exportedPluginTag, j.properties.Exported_plugins...)
 
@@ -933,7 +922,7 @@
 
 	if j.useCompose(ctx) {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag,
-			"androidx.compose.compiler_compiler-hosted")
+			"androidx.compose.compiler_compiler-hosted-plugin")
 	}
 }
 
@@ -1167,8 +1156,9 @@
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
-	if re := proptools.Bool(j.properties.Ravenizer.Enabled); re {
-		j.ravenizer.enabled = re
+	// Only override the original value if explicitly set
+	if j.properties.Ravenizer.Enabled != nil {
+		j.ravenizer.enabled = *j.properties.Ravenizer.Enabled
 	}
 
 	deps := j.collectDeps(ctx)
@@ -1304,7 +1294,7 @@
 		android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
 			HeaderJars:                          android.PathsIfNonNil(j.headerJarFile),
 			LocalHeaderJars:                     localHeaderJars,
-			TransitiveStaticLibsHeaderJars:      android.NewDepSet(android.PREORDER, localHeaderJars, transitiveStaticLibsHeaderJars),
+			TransitiveStaticLibsHeaderJars:      depset.New(depset.PREORDER, localHeaderJars, transitiveStaticLibsHeaderJars),
 			TransitiveLibsHeaderJarsForR8:       j.transitiveLibsHeaderJarsForR8,
 			TransitiveStaticLibsHeaderJarsForR8: j.transitiveStaticLibsHeaderJarsForR8,
 			AidlIncludeDirs:                     j.exportAidlIncludeDirs,
@@ -1313,6 +1303,7 @@
 			ExportedPluginDisableTurbine:        j.exportedDisableTurbine,
 			StubsLinkType:                       j.stubsLinkType,
 			AconfigIntermediateCacheOutputPaths: deps.aconfigProtoFiles,
+			SdkVersion:                          j.SdkVersion(ctx),
 		})
 
 		j.outputFile = j.headerJarFile
@@ -1509,7 +1500,11 @@
 
 	dirArgs, dirDeps := ResourceDirsToJarArgs(ctx, j.properties.Java_resource_dirs,
 		j.properties.Exclude_java_resource_dirs, j.properties.Exclude_java_resources)
-	fileArgs, fileDeps := ResourceFilesToJarArgs(ctx, j.properties.Java_resources, j.properties.Exclude_java_resources)
+	fileArgs, fileDeps := ResourceFilesToJarArgs(ctx, j.properties.Java_resources.GetOrDefault(ctx, nil), j.properties.Exclude_java_resources)
+	fileArgs2, fileDeps2 := ResourceFilesToJarArgs(ctx, j.properties.Device_common_java_resources.GetOrDefault(ctx, nil), nil)
+	fileArgs3, fileDeps3 := ResourceFilesToJarArgs(ctx, j.properties.Device_first_java_resources.GetOrDefault(ctx, nil), nil)
+	fileArgs = slices.Concat(fileArgs, fileArgs2, fileArgs3)
+	fileDeps = slices.Concat(fileDeps, fileDeps2, fileDeps3)
 	extraArgs, extraDeps := resourcePathsToJarArgs(j.extraResources), j.extraResources
 
 	var resArgs []string
@@ -1563,7 +1558,7 @@
 		localResourceJars = append(localResourceJars, servicesJar)
 	}
 
-	completeStaticLibsResourceJars := android.NewDepSet(android.PREORDER, localResourceJars, deps.transitiveStaticLibsResourceJars)
+	completeStaticLibsResourceJars := depset.New(depset.PREORDER, localResourceJars, deps.transitiveStaticLibsResourceJars)
 
 	var combinedResourceJar android.Path
 	var resourceJars android.Paths
@@ -1590,7 +1585,7 @@
 	// classes.jar. If there is only one input jar this step will be skipped.
 	var outputFile android.Path
 
-	completeStaticLibsImplementationJars := android.NewDepSet(android.PREORDER, localImplementationJars, deps.transitiveStaticLibsImplementationJars)
+	completeStaticLibsImplementationJars := depset.New(depset.PREORDER, localImplementationJars, deps.transitiveStaticLibsImplementationJars)
 
 	var jars android.Paths
 	if ctx.Config().UseTransitiveJarsInClasspath() {
@@ -1620,7 +1615,7 @@
 				Input:  jars[0],
 				Output: copiedJar,
 			})
-			completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, android.Paths{copiedJar}, nil)
+			completeStaticLibsImplementationJars = depset.New(depset.PREORDER, android.Paths{copiedJar}, nil)
 			outputFile = copiedJar
 		} else {
 			outputFile = jars[0]
@@ -1636,7 +1631,7 @@
 	jarjarFile, jarjarred := j.jarjarIfNecessary(ctx, outputFile, jarName, "")
 	if jarjarred {
 		localImplementationJars = android.Paths{jarjarFile}
-		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, localImplementationJars, nil)
+		completeStaticLibsImplementationJars = depset.New(depset.PREORDER, localImplementationJars, nil)
 	}
 	outputFile = jarjarFile
 
@@ -1646,7 +1641,7 @@
 		combinedResourceJar = resourceJarJarFile
 		if jarjarred {
 			localResourceJars = android.Paths{resourceJarJarFile}
-			completeStaticLibsResourceJars = android.NewDepSet(android.PREORDER, localResourceJars, nil)
+			completeStaticLibsResourceJars = depset.New(depset.PREORDER, localResourceJars, nil)
 		}
 	}
 
@@ -1656,16 +1651,23 @@
 
 	if j.ravenizer.enabled {
 		ravenizerInput := outputFile
-		ravenizerOutput := android.PathForModuleOut(ctx, "ravenizer", jarName)
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        ravenizer,
-			Description: "ravenizer",
-			Input:       ravenizerInput,
-			Output:      ravenizerOutput,
-		})
+		ravenizerOutput := android.PathForModuleOut(ctx, "ravenizer", "", jarName)
+		ravenizerArgs := ""
+		if proptools.Bool(j.properties.Ravenizer.Strip_mockito) {
+			ravenizerArgs = "--strip-mockito"
+		}
+		TransformRavenizer(ctx, ravenizerOutput, ravenizerInput, ravenizerArgs)
 		outputFile = ravenizerOutput
 		localImplementationJars = android.Paths{ravenizerOutput}
-		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, localImplementationJars, nil)
+		completeStaticLibsImplementationJars = depset.New(depset.PREORDER, localImplementationJars, nil)
+		if combinedResourceJar != nil {
+			ravenizerInput = combinedResourceJar
+			ravenizerOutput = android.PathForModuleOut(ctx, "ravenizer", "resources", jarName)
+			TransformRavenizer(ctx, ravenizerOutput, ravenizerInput, ravenizerArgs)
+			combinedResourceJar = ravenizerOutput
+			localResourceJars = android.Paths{ravenizerOutput}
+			completeStaticLibsResourceJars = depset.New(depset.PREORDER, localResourceJars, nil)
+		}
 	}
 
 	if j.shouldApiMapper() {
@@ -1679,7 +1681,7 @@
 		})
 		outputFile = apiMapperFile
 		localImplementationJars = android.Paths{apiMapperFile}
-		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, localImplementationJars, nil)
+		completeStaticLibsImplementationJars = depset.New(depset.PREORDER, localImplementationJars, nil)
 	}
 
 	// Check package restrictions if necessary.
@@ -1702,7 +1704,7 @@
 		})
 		outputFile = packageCheckOutputFile
 		localImplementationJars = android.Paths{packageCheckOutputFile}
-		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, localImplementationJars, nil)
+		completeStaticLibsImplementationJars = depset.New(depset.PREORDER, localImplementationJars, nil)
 
 		// Check packages and create a timestamp file when complete.
 		CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages)
@@ -1736,8 +1738,17 @@
 		return
 	}
 
-	if j.shouldInstrument(ctx) {
-		outputFile = j.instrument(ctx, flags, outputFile, jarName, specs)
+	completeStaticLibsImplementationJarsToCombine := completeStaticLibsImplementationJars
+
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+
+	// Enable dex compilation for the APEX variants, unless it is disabled explicitly
+	compileDex := Bool(j.dexProperties.Compile_dex) || Bool(j.properties.Installable)
+
+	if j.shouldInstrument(ctx) && (!ctx.Device() || compileDex) {
+		instrumentedOutputFile := j.instrument(ctx, flags, outputFile, jarName, specs)
+		completeStaticLibsImplementationJarsToCombine = depset.New(depset.PREORDER, android.Paths{instrumentedOutputFile}, nil)
+		outputFile = instrumentedOutputFile
 	}
 
 	// merge implementation jar with resources if necessary
@@ -1745,7 +1756,7 @@
 	if ctx.Config().UseTransitiveJarsInClasspath() {
 		resourceJars := completeStaticLibsResourceJars.ToList()
 		if len(resourceJars) > 0 {
-			implementationAndResourcesJarsToCombine = append(resourceJars, completeStaticLibsImplementationJars.ToList()...)
+			implementationAndResourcesJarsToCombine = append(resourceJars, completeStaticLibsImplementationJarsToCombine.ToList()...)
 			implementationAndResourcesJarsToCombine = append(implementationAndResourcesJarsToCombine, extraDepCombinedJars...)
 		}
 	} else {
@@ -1763,19 +1774,7 @@
 
 	j.implementationAndResourcesJar = outputFile
 
-	// Enable dex compilation for the APEX variants, unless it is disabled explicitly
-	compileDex := j.dexProperties.Compile_dex
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
-	if j.DirectlyInAnyApex() && !apexInfo.IsForPlatform() {
-		if compileDex == nil {
-			compileDex = proptools.BoolPtr(true)
-		}
-		if j.deviceProperties.Hostdex == nil {
-			j.deviceProperties.Hostdex = proptools.BoolPtr(true)
-		}
-	}
-
-	if ctx.Device() && (Bool(j.properties.Installable) || Bool(compileDex)) {
+	if ctx.Device() && compileDex {
 		if j.hasCode(ctx) {
 			if j.shouldInstrumentStatic(ctx) {
 				j.dexer.extraProguardFlagsFiles = append(j.dexer.extraProguardFlagsFiles,
@@ -1915,7 +1914,7 @@
 		RepackagedHeaderJars: android.PathsIfNonNil(repackagedHeaderJarFile),
 
 		LocalHeaderJars:                        localHeaderJars,
-		TransitiveStaticLibsHeaderJars:         android.NewDepSet(android.PREORDER, localHeaderJars, transitiveStaticLibsHeaderJars),
+		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, localHeaderJars, transitiveStaticLibsHeaderJars),
 		TransitiveStaticLibsImplementationJars: completeStaticLibsImplementationJars,
 		TransitiveStaticLibsResourceJars:       completeStaticLibsResourceJars,
 
@@ -1934,6 +1933,7 @@
 		JacocoReportClassesFile:             j.jacocoReportClassesFile,
 		StubsLinkType:                       j.stubsLinkType,
 		AconfigIntermediateCacheOutputPaths: j.aconfigCacheFiles,
+		SdkVersion:                          j.SdkVersion(ctx),
 	})
 
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
@@ -1944,17 +1944,15 @@
 	return android.InList("androidx.compose.runtime_runtime", j.staticLibs(ctx))
 }
 
-func collectDepProguardSpecInfo(ctx android.ModuleContext) (transitiveProguardFlags, transitiveUnconditionalExportedFlags []*android.DepSet[android.Path]) {
+func collectDepProguardSpecInfo(ctx android.ModuleContext) (transitiveProguardFlags, transitiveUnconditionalExportedFlags []depset.DepSet[android.Path]) {
 	ctx.VisitDirectDeps(func(m android.Module) {
 		depProguardInfo, _ := android.OtherModuleProvider(ctx, m, ProguardSpecInfoProvider)
 		depTag := ctx.OtherModuleDependencyTag(m)
 
-		if depProguardInfo.UnconditionallyExportedProguardFlags != nil {
-			transitiveUnconditionalExportedFlags = append(transitiveUnconditionalExportedFlags, depProguardInfo.UnconditionallyExportedProguardFlags)
-			transitiveProguardFlags = append(transitiveProguardFlags, depProguardInfo.UnconditionallyExportedProguardFlags)
-		}
+		transitiveUnconditionalExportedFlags = append(transitiveUnconditionalExportedFlags, depProguardInfo.UnconditionallyExportedProguardFlags)
+		transitiveProguardFlags = append(transitiveProguardFlags, depProguardInfo.UnconditionallyExportedProguardFlags)
 
-		if depTag == staticLibTag && depProguardInfo.ProguardFlagsFiles != nil {
+		if depTag == staticLibTag {
 			transitiveProguardFlags = append(transitiveProguardFlags, depProguardInfo.ProguardFlagsFiles)
 		}
 	})
@@ -1976,13 +1974,13 @@
 
 	return ProguardSpecInfo{
 		Export_proguard_flags_files: exportUnconditionally,
-		ProguardFlagsFiles: android.NewDepSet[android.Path](
-			android.POSTORDER,
+		ProguardFlagsFiles: depset.New[android.Path](
+			depset.POSTORDER,
 			proguardFlagsForThisModule,
 			transitiveProguardFlags,
 		),
-		UnconditionallyExportedProguardFlags: android.NewDepSet[android.Path](
-			android.POSTORDER,
+		UnconditionallyExportedProguardFlags: depset.New[android.Path](
+			depset.POSTORDER,
 			directUnconditionalExportedFlags,
 			transitiveUnconditionalExportedFlags,
 		),
@@ -2074,7 +2072,7 @@
 	// one input jar this step will be skipped.
 	var jars android.Paths
 	if ctx.Config().UseTransitiveJarsInClasspath() {
-		depSet := android.NewDepSet(android.PREORDER, localHeaderJars, deps.transitiveStaticLibsHeaderJars)
+		depSet := depset.New(depset.PREORDER, localHeaderJars, deps.transitiveStaticLibsHeaderJars)
 		jars = depSet.ToList()
 	} else {
 		jars = append(slices.Clone(localHeaderJars), deps.staticHeaderJars...)
@@ -2104,9 +2102,9 @@
 
 type providesTransitiveHeaderJarsForR8 struct {
 	// set of header jars for all transitive libs deps
-	transitiveLibsHeaderJarsForR8 *android.DepSet[android.Path]
+	transitiveLibsHeaderJarsForR8 depset.DepSet[android.Path]
 	// set of header jars for all transitive static libs deps
-	transitiveStaticLibsHeaderJarsForR8 *android.DepSet[android.Path]
+	transitiveStaticLibsHeaderJarsForR8 depset.DepSet[android.Path]
 }
 
 // collectTransitiveHeaderJarsForR8 visits direct dependencies and collects all transitive libs and static_libs
@@ -2116,8 +2114,8 @@
 func (j *providesTransitiveHeaderJarsForR8) collectTransitiveHeaderJarsForR8(ctx android.ModuleContext) {
 	directLibs := android.Paths{}
 	directStaticLibs := android.Paths{}
-	transitiveLibs := []*android.DepSet[android.Path]{}
-	transitiveStaticLibs := []*android.DepSet[android.Path]{}
+	transitiveLibs := []depset.DepSet[android.Path]{}
+	transitiveStaticLibs := []depset.DepSet[android.Path]{}
 	ctx.VisitDirectDeps(func(module android.Module) {
 		// don't add deps of the prebuilt version of the same library
 		if ctx.ModuleName() == android.RemoveOptionalPrebuiltPrefix(module.Name()) {
@@ -2136,17 +2134,12 @@
 				return
 			}
 
-			if dep.TransitiveLibsHeaderJarsForR8 != nil {
-				transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJarsForR8)
-			}
-			if dep.TransitiveStaticLibsHeaderJarsForR8 != nil {
-				transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJarsForR8)
-			}
-
+			transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJarsForR8)
+			transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJarsForR8)
 		}
 	})
-	j.transitiveLibsHeaderJarsForR8 = android.NewDepSet(android.POSTORDER, directLibs, transitiveLibs)
-	j.transitiveStaticLibsHeaderJarsForR8 = android.NewDepSet(android.POSTORDER, directStaticLibs, transitiveStaticLibs)
+	j.transitiveLibsHeaderJarsForR8 = depset.New(depset.POSTORDER, directLibs, transitiveLibs)
+	j.transitiveStaticLibsHeaderJarsForR8 = depset.New(depset.POSTORDER, directStaticLibs, transitiveStaticLibs)
 }
 
 func (j *Module) HeaderJars() android.Paths {
@@ -2189,16 +2182,14 @@
 
 // Collect information for opening IDE project files in java/jdeps.go.
 func (j *Module) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	// jarjar rules will repackage the sources. To prevent misleading results, IdeInfo should contain the
-	// repackaged jar instead of the input sources.
 	if j.expandJarjarRules != nil {
 		dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
+		// Add the header jar so that the rdeps can be resolved to the repackaged classes.
 		dpInfo.Jars = append(dpInfo.Jars, j.headerJarFile.String())
-	} else {
-		dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
-		dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...)
-		dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...)
 	}
+	dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
+	dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...)
+	dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...)
 	dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...)
 	dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...)
 	dpInfo.Static_libs = append(dpInfo.Static_libs, j.staticLibs(ctx)...)
@@ -2248,19 +2239,17 @@
 }
 
 func (j *Module) collectTransitiveSrcFiles(ctx android.ModuleContext, mine android.Paths) {
-	var fromDeps []*android.DepSet[android.Path]
+	var fromDeps []depset.DepSet[android.Path]
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if tag == staticLibTag {
 			if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
-				if depInfo.TransitiveSrcFiles != nil {
-					fromDeps = append(fromDeps, depInfo.TransitiveSrcFiles)
-				}
+				fromDeps = append(fromDeps, depInfo.TransitiveSrcFiles)
 			}
 		}
 	})
 
-	j.transitiveSrcFiles = android.NewDepSet(android.POSTORDER, mine, fromDeps)
+	j.transitiveSrcFiles = depset.New(depset.POSTORDER, mine, fromDeps)
 }
 
 func (j *Module) IsInstallable() bool {
@@ -2338,7 +2327,10 @@
 		"stable.core.platform.api.stubs",
 		"stub-annotations", "private-stub-annotations-jar",
 		"core-lambda-stubs",
-		"core-generated-annotation-stubs":
+		"core-generated-annotation-stubs",
+		// jacocoagent only uses core APIs, but has to specify a non-core sdk_version so it can use
+		// a prebuilt SDK to avoid circular dependencies when it statically included in the bootclasspath.
+		"jacocoagent":
 		return javaCore, true
 	case android.SdkPublic.DefaultJavaLibraryName():
 		return javaSdk, true
@@ -2394,12 +2386,12 @@
 
 	j.collectTransitiveHeaderJarsForR8(ctx)
 
-	var transitiveBootClasspathHeaderJars []*android.DepSet[android.Path]
-	var transitiveClasspathHeaderJars []*android.DepSet[android.Path]
-	var transitiveJava9ClasspathHeaderJars []*android.DepSet[android.Path]
-	var transitiveStaticJarsHeaderLibs []*android.DepSet[android.Path]
-	var transitiveStaticJarsImplementationLibs []*android.DepSet[android.Path]
-	var transitiveStaticJarsResourceLibs []*android.DepSet[android.Path]
+	var transitiveBootClasspathHeaderJars []depset.DepSet[android.Path]
+	var transitiveClasspathHeaderJars []depset.DepSet[android.Path]
+	var transitiveJava9ClasspathHeaderJars []depset.DepSet[android.Path]
+	var transitiveStaticJarsHeaderLibs []depset.DepSet[android.Path]
+	var transitiveStaticJarsImplementationLibs []depset.DepSet[android.Path]
+	var transitiveStaticJarsResourceLibs []depset.DepSet[android.Path]
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
@@ -2414,10 +2406,9 @@
 			return
 		}
 
-		if _, ok := module.(SdkLibraryDependency); ok {
+		if sdkInfo, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
 			switch tag {
 			case sdkLibTag, libTag, staticLibTag:
-				sdkInfo, _ := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider)
 				generatingLibsString := android.PrettyConcat(
 					getGeneratingLibs(ctx, j.SdkVersion(ctx), module.Name(), sdkInfo), true, "or")
 				ctx.ModuleErrorf("cannot depend directly on java_sdk_library %q; try depending on %s instead", module.Name(), generatingLibsString)
@@ -2434,9 +2425,7 @@
 			switch tag {
 			case bootClasspathTag:
 				deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...)
-				if dep.TransitiveStaticLibsHeaderJars != nil {
-					transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-				}
+				transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 			case sdkLibTag, libTag, instrumentationForTag:
 				if _, ok := module.(*Plugin); ok {
 					ctx.ModuleErrorf("a java_plugin (%s) cannot be used as a libs dependency", otherName)
@@ -2451,14 +2440,10 @@
 				addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...)
 				deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine
 
-				if dep.TransitiveStaticLibsHeaderJars != nil {
-					transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-				}
+				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 			case java9LibTag:
 				deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...)
-				if dep.TransitiveStaticLibsHeaderJars != nil {
-					transitiveJava9ClasspathHeaderJars = append(transitiveJava9ClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-				}
+				transitiveJava9ClasspathHeaderJars = append(transitiveJava9ClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 			case staticLibTag:
 				if _, ok := module.(*Plugin); ok {
 					ctx.ModuleErrorf("a java_plugin (%s) cannot be used as a static_libs dependency", otherName)
@@ -2475,16 +2460,10 @@
 				deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine
 				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...)
 
-				if dep.TransitiveStaticLibsHeaderJars != nil {
-					transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-					transitiveStaticJarsHeaderLibs = append(transitiveStaticJarsHeaderLibs, dep.TransitiveStaticLibsHeaderJars)
-				}
-				if dep.TransitiveStaticLibsImplementationJars != nil {
-					transitiveStaticJarsImplementationLibs = append(transitiveStaticJarsImplementationLibs, dep.TransitiveStaticLibsImplementationJars)
-				}
-				if dep.TransitiveStaticLibsResourceJars != nil {
-					transitiveStaticJarsResourceLibs = append(transitiveStaticJarsResourceLibs, dep.TransitiveStaticLibsResourceJars)
-				}
+				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
+				transitiveStaticJarsHeaderLibs = append(transitiveStaticJarsHeaderLibs, dep.TransitiveStaticLibsHeaderJars)
+				transitiveStaticJarsImplementationLibs = append(transitiveStaticJarsImplementationLibs, dep.TransitiveStaticLibsImplementationJars)
+				transitiveStaticJarsResourceLibs = append(transitiveStaticJarsResourceLibs, dep.TransitiveStaticLibsResourceJars)
 			case pluginTag:
 				if plugin, ok := module.(*Plugin); ok {
 					if plugin.pluginProperties.Processor_class != nil {
@@ -2519,7 +2498,11 @@
 					ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
 				}
 			case kotlinPluginTag:
-				deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...)
+				if _, ok := module.(*KotlinPlugin); ok {
+					deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...)
+				} else {
+					ctx.PropertyErrorf("kotlin_plugins", "%q is not a kotlin_plugin module", otherName)
+				}
 			case syspropPublicStubDepTag:
 				// This is a sysprop implementation library, forward the JavaInfoProvider from
 				// the corresponding sysprop public stub library as SyspropPublicStubInfoProvider.
@@ -2534,14 +2517,14 @@
 				deps.classpath = append(deps.classpath, dep.Srcs()...)
 				deps.dexClasspath = append(deps.classpath, dep.Srcs()...)
 				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars,
-					android.NewDepSet(android.PREORDER, dep.Srcs(), nil))
+					depset.New(depset.PREORDER, dep.Srcs(), nil))
 			case staticLibTag:
 				checkProducesJars(ctx, dep)
 				deps.classpath = append(deps.classpath, dep.Srcs()...)
 				deps.staticJars = append(deps.staticJars, dep.Srcs()...)
 				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...)
 
-				depHeaderJars := android.NewDepSet(android.PREORDER, dep.Srcs(), nil)
+				depHeaderJars := depset.New(depset.PREORDER, dep.Srcs(), nil)
 				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, depHeaderJars)
 				transitiveStaticJarsHeaderLibs = append(transitiveStaticJarsHeaderLibs, depHeaderJars)
 				transitiveStaticJarsImplementationLibs = append(transitiveStaticJarsImplementationLibs, depHeaderJars)
@@ -2558,10 +2541,8 @@
 				// then add its libs to the bootclasspath.
 				if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok {
 					deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars...)
-					if sm.TransitiveStaticLibsHeaderJars != nil {
-						transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars,
-							sm.TransitiveStaticLibsHeaderJars)
-					}
+					transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars,
+						sm.TransitiveStaticLibsHeaderJars)
 				} else {
 					ctx.PropertyErrorf("boot classpath dependency %q does not provide SystemModulesProvider",
 						ctx.OtherModuleName(module))
@@ -2597,11 +2578,11 @@
 	deps.transitiveStaticLibsResourceJars = transitiveStaticJarsResourceLibs
 
 	if ctx.Config().UseTransitiveJarsInClasspath() {
-		depSet := android.NewDepSet(android.PREORDER, nil, transitiveClasspathHeaderJars)
+		depSet := depset.New(depset.PREORDER, nil, transitiveClasspathHeaderJars)
 		deps.classpath = depSet.ToList()
-		depSet = android.NewDepSet(android.PREORDER, nil, transitiveBootClasspathHeaderJars)
+		depSet = depset.New(depset.PREORDER, nil, transitiveBootClasspathHeaderJars)
 		deps.bootClasspath = depSet.ToList()
-		depSet = android.NewDepSet(android.PREORDER, nil, transitiveJava9ClasspathHeaderJars)
+		depSet = depset.New(depset.PREORDER, nil, transitiveJava9ClasspathHeaderJars)
 		deps.java9Classpath = depSet.ToList()
 	}
 
@@ -2726,7 +2707,7 @@
 	module := ctx.Module()
 	moduleName := module.Name()
 
-	ctx.VisitDirectDepsIgnoreBlueprint(func(m android.Module) {
+	ctx.VisitDirectDeps(func(m android.Module) {
 		tag := ctx.OtherModuleDependencyTag(m)
 		// This logic mirrors that in (*Module).collectDeps above.  There are several places
 		// where we explicitly return RenameUseExclude, even though it is the default, to
@@ -2749,7 +2730,7 @@
 			if IsJniDepTag(tag) || tag == certificateTag || tag == proguardRaiseTag {
 				return RenameUseExclude, "tags"
 			}
-			if _, ok := m.(SdkLibraryDependency); ok {
+			if _, ok := android.OtherModuleProvider(ctx, m, SdkLibraryInfoProvider); ok {
 				switch tag {
 				case sdkLibTag, libTag:
 					return RenameUseExclude, "sdklibdep" // matches collectDeps()
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 029f6f6..3413cf3 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -29,7 +29,7 @@
 
 func registerBootclasspathBuildComponents(ctx android.RegistrationContext) {
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator).Parallel()
+		ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator)
 	})
 }
 
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 4fcd40b..375a1aa 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -23,7 +23,6 @@
 
 	"android/soong/android"
 	"android/soong/dexpreopt"
-	"android/soong/testing"
 
 	"github.com/google/blueprint/proptools"
 
@@ -84,10 +83,6 @@
 	return true
 }
 
-// Contents of bootclasspath fragments in an apex are considered to be directly in the apex, as if
-// they were listed in java_libs.
-func (b bootclasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {}
-
 // Contents of bootclasspath fragments require files from prebuilt apex files.
 func (b bootclasspathFragmentContentDependencyTag) RequiresFilesFromPrebuiltApex() {}
 
@@ -97,7 +92,6 @@
 var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContentDepTag
 var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
 var _ android.SdkMemberDependencyTag = bootclasspathFragmentContentDepTag
-var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag
 var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag
 
 func IsBootclasspathFragmentContentDepTag(tag blueprint.DependencyTag) bool {
@@ -112,7 +106,7 @@
 	// property.
 	//
 	// The order of this list matters as it is the order that is used in the bootclasspath.
-	Contents []string
+	Contents proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// The properties for specifying the API stubs provided by this fragment.
 	BootclasspathAPIProperties
@@ -296,8 +290,8 @@
 	return m
 }
 
-func (m *BootclasspathFragmentModule) bootclasspathFragmentPropertyCheck(ctx android.EarlyModuleContext) {
-	contents := m.properties.Contents
+func (m *BootclasspathFragmentModule) bootclasspathFragmentPropertyCheck(ctx android.ModuleContext) {
+	contents := m.properties.Contents.GetOrDefault(ctx, nil)
 	if len(contents) == 0 {
 		ctx.PropertyErrorf("contents", "required property is missing")
 		return
@@ -435,7 +429,7 @@
 	module := ctx.Module()
 	_, isSourceModule := module.(*BootclasspathFragmentModule)
 
-	for _, name := range b.properties.Contents {
+	for _, name := range b.properties.Contents.GetOrDefault(ctx, nil) {
 		// A bootclasspath_fragment must depend only on other source modules, while the
 		// prebuilt_bootclasspath_fragment must only depend on other prebuilt modules.
 		//
@@ -521,10 +515,9 @@
 	// be output to Make but it does not really matter which variant is output. The default/platform
 	// variant is the first (ctx.PrimaryModule()) and is usually hidden from make so this just picks
 	// the last variant (ctx.FinalModule()).
-	if ctx.Module() != ctx.FinalModule() {
+	if !ctx.IsFinalModule(ctx.Module()) {
 		b.HideFromMake()
 	}
-	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 // getProfileProviderApex returns the name of the apex that provides a boot image profile, or an
@@ -590,7 +583,7 @@
 		return global.ArtApexJars
 	}
 
-	possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, b.properties.Contents, bootclasspathFragmentContentDepTag)
+	possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, b.properties.Contents.GetOrDefault(ctx, nil), bootclasspathFragmentContentDepTag)
 	jars, unknown := global.ApexBootJars.Filter(possibleUpdatableModules)
 
 	// TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths
@@ -602,7 +595,7 @@
 	} else if android.InList("test_framework-apexd", possibleUpdatableModules) {
 		jars = jars.Append("com.android.apex.test_package", "test_framework-apexd")
 	} else if global.ApexBootJars.Len() != 0 {
-		unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents)
+		unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents.GetOrDefault(ctx, nil))
 		_, unknown = android.RemoveFromList("core-icu4j", unknown)
 		// This module only exists in car products.
 		// So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS.
@@ -849,7 +842,7 @@
 
 // Collect information for opening IDE project files in java/jdeps.go.
 func (b *BootclasspathFragmentModule) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...)
+	dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents.GetOrDefault(ctx, nil)...)
 }
 
 type bootclasspathFragmentMemberType struct {
@@ -925,7 +918,7 @@
 	module := variant.(*BootclasspathFragmentModule)
 
 	b.Image_name = module.properties.Image_name
-	b.Contents = module.properties.Contents
+	b.Contents = module.properties.Contents.GetOrDefault(ctx.SdkModuleContext(), nil)
 
 	// Get the hidden API information from the module.
 	mctx := ctx.SdkModuleContext()
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 60f1a50..3aa1258 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -191,7 +191,8 @@
 
 	checkContents := func(t *testing.T, result *android.TestResult, expected ...string) {
 		module := result.Module("myfragment", "android_common").(*BootclasspathFragmentModule)
-		android.AssertArrayString(t, "contents property", expected, module.properties.Contents)
+		eval := module.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+		android.AssertArrayString(t, "contents property", expected, module.properties.Contents.GetOrDefault(eval, nil))
 	}
 
 	preparer := android.GroupFixturePreparers(
diff --git a/java/builder.go b/java/builder.go
index 81b0feb..895ddb6 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -260,10 +260,10 @@
 
 	ravenizer = pctx.AndroidStaticRule("ravenizer",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && ${ravenizer} --in-jar $in --out-jar $out",
+			Command:     "rm -f $out && ${ravenizer} --in-jar $in --out-jar $out $ravenizerArgs",
 			CommandDeps: []string{"${ravenizer}"},
 		},
-	)
+		"ravenizerArgs")
 
 	apimapper = pctx.AndroidStaticRule("apimapper",
 		blueprint.RuleParams{
@@ -301,7 +301,7 @@
 
 	gatherReleasedFlaggedApisRule = pctx.AndroidStaticRule("gatherReleasedFlaggedApisRule",
 		blueprint.RuleParams{
-			Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}' ` +
+			Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}' ` +
 				`--out ${out} ` +
 				`${flags_path} ` +
 				`${filter_args} `,
@@ -703,6 +703,7 @@
 	// Remove any module-info.class files that may have come from prebuilt jars, they cause problems
 	// for downstream tools like desugar.
 	jarArgs = append(jarArgs, "-stripFile module-info.class")
+	jarArgs = append(jarArgs, "-stripFile META-INF/versions/*/module-info.class")
 
 	if stripDirEntries {
 		jarArgs = append(jarArgs, "-D")
@@ -782,12 +783,15 @@
 }
 
 func TransformRavenizer(ctx android.ModuleContext, outputFile android.WritablePath,
-	inputFile android.Path) {
+	inputFile android.Path, ravenizerArgs string) {
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        ravenizer,
 		Description: "ravenizer",
 		Output:      outputFile,
 		Input:       inputFile,
+		Args: map[string]string{
+			"ravenizerArgs": ravenizerArgs,
+		},
 	})
 }
 
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index 18a5dae..88a8fb8 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -18,9 +18,10 @@
 
 import (
 	"fmt"
+	"strings"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
-	"strings"
 
 	"android/soong/android"
 )
@@ -103,7 +104,11 @@
 func gatherPossibleApexModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string {
 	set := map[string]struct{}{}
 	for _, name := range contents {
-		dep, _ := ctx.GetDirectDepWithTag(name, tag).(android.Module)
+		dep := ctx.GetDirectDepWithTag(name, tag)
+		if dep == nil && ctx.Config().AllowMissingDependencies() {
+			// Ignore apex boot jars from dexpreopt if it does not exist, and missing deps are allowed.
+			continue
+		}
 		set[ModuleStemForDeapexing(dep)] = struct{}{}
 		if m, ok := dep.(ModuleWithStem); ok {
 			set[m.Stem()] = struct{}{}
diff --git a/java/code_metadata_test.go b/java/code_metadata_test.go
deleted file mode 100644
index 9dc9a22..0000000
--- a/java/code_metadata_test.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package java
-
-import (
-	"strings"
-	"testing"
-
-	"android/soong/android"
-	soongTesting "android/soong/testing"
-	"android/soong/testing/code_metadata_internal_proto"
-
-	"google.golang.org/protobuf/proto"
-)
-
-func TestCodeMetadata(t *testing.T) {
-	bp := `code_metadata {
-		name: "module-name",
-		teamId: "12345",
-		code: [
-			"foo",
-		]
-	}
-
-	java_sdk_library {
-		name: "foo",
-		srcs: ["a.java"],
-	}`
-	result := runCodeMetadataTest(t, android.FixtureExpectsNoErrors, bp)
-
-	module := result.ModuleForTests("module-name", "")
-
-	// Check that the provider has the right contents
-	data, _ := android.OtherModuleProvider(result, module.Module(), soongTesting.CodeMetadataProviderKey)
-	if !strings.HasSuffix(
-		data.IntermediatePath.String(), "/intermediateCodeMetadata.pb",
-	) {
-		t.Errorf(
-			"Missing intermediates path in provider: %s",
-			data.IntermediatePath.String(),
-		)
-	}
-
-	metadata := android.ContentFromFileRuleForTests(t, result.TestContext,
-		module.Output(data.IntermediatePath.String()))
-
-	metadataList := make([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership, 0, 2)
-	teamId := "12345"
-	bpFilePath := "Android.bp"
-	targetName := "foo"
-	srcFile := []string{"a.java"}
-	expectedMetadataProto := code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
-		TrendyTeamId: &teamId,
-		TargetName:   &targetName,
-		Path:         &bpFilePath,
-		SourceFiles:  srcFile,
-	}
-	metadataList = append(metadataList, &expectedMetadataProto)
-
-	CodeMetadataMetadata := code_metadata_internal_proto.CodeMetadataInternal{TargetOwnershipList: metadataList}
-	protoData, _ := proto.Marshal(&CodeMetadataMetadata)
-	expectedMetadata := string(protoData)
-
-	if metadata != expectedMetadata {
-		t.Errorf(
-			"Retrieved metadata: %s is not equal to expectedMetadata: %s", metadata,
-			expectedMetadata,
-		)
-	}
-
-	// Tests for all_test_spec singleton.
-	singleton := result.SingletonForTests("all_code_metadata")
-	rule := singleton.Rule("all_code_metadata_rule")
-	prebuiltOs := result.Config.PrebuiltOS()
-	expectedCmd := "out/soong/host/" + prebuiltOs + "/bin/metadata -rule code_metadata -inputFile out/soong/all_code_metadata_paths.rsp -outputFile out/soong/ownership/all_code_metadata.pb"
-	expectedOutputFile := "out/soong/ownership/all_code_metadata.pb"
-	expectedInputFile := "out/soong/.intermediates/module-name/intermediateCodeMetadata.pb"
-	if !strings.Contains(
-		strings.TrimSpace(rule.Output.String()),
-		expectedOutputFile,
-	) {
-		t.Errorf(
-			"Retrieved singletonOutputFile: %s is not equal to expectedSingletonOutputFile: %s",
-			rule.Output.String(), expectedOutputFile,
-		)
-	}
-
-	if !strings.Contains(
-		strings.TrimSpace(rule.Inputs[0].String()),
-		expectedInputFile,
-	) {
-		t.Errorf(
-			"Retrieved singletonInputFile: %s is not equal to expectedSingletonInputFile: %s",
-			rule.Inputs[0].String(), expectedInputFile,
-		)
-	}
-
-	if !strings.Contains(
-		strings.TrimSpace(rule.RuleParams.Command),
-		expectedCmd,
-	) {
-		t.Errorf(
-			"Retrieved cmd: %s doesn't contain expectedCmd: %s",
-			rule.RuleParams.Command, expectedCmd,
-		)
-	}
-}
-func runCodeMetadataTest(
-	t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
-) *android.TestResult {
-	return android.GroupFixturePreparers(
-		soongTesting.PrepareForTestWithTestingBuildComponents, prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("foo"),
-	).
-		ExtendWithErrorHandler(errorHandler).
-		RunTestWithBp(t, bp)
-}
diff --git a/java/config/config.go b/java/config/config.go
index 4c1c723..87703d8 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -97,11 +97,19 @@
 		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
 	}, dexerJavaVmFlagsList...), " "))
-	pctx.StaticVariable("R8Flags", strings.Join(append([]string{
-		"-JXmx4096M",
-		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
-		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
-	}, dexerJavaVmFlagsList...), " "))
+
+	pctx.VariableFunc("R8Flags", func(ctx android.PackageVarContext) string {
+		r8flags := append([]string{
+			"-JXmx4096M",
+			"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
+			"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
+		}, dexerJavaVmFlagsList...)
+		if r8DumpDir := ctx.Config().Getenv("R8_DUMP_DIRECTORY"); r8DumpDir != "" {
+			r8flags = append(r8flags, "-JDcom.android.tools.r8.dumpinputtodirectory="+r8DumpDir)
+		}
+		return strings.Join(r8flags, " ")
+
+	})
 
 	pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
 		`-Xmaxerrs 9999999`,
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index 1cca7ad..8c808e4 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -41,10 +41,50 @@
     is_stubs_module: true,
 }
 
+soong_config_module_type {
+    name: "core_current_stubs_soong_config_defaults",
+    module_type: "java_defaults",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "release_hidden_api_exportable_stubs",
+    ],
+    properties: [
+        "dist.targets",
+        "dist.dest",
+    ],
+}
+
+core_current_stubs_soong_config_defaults {
+    name: "core_current_stubs_everything_soong_config_defaults",
+    soong_config_variables: {
+        release_hidden_api_exportable_stubs: {
+            conditions_default: {
+                dist: {
+                    targets: dist_targets,
+                    dest: "core.current.stubs.jar",
+                },
+            },
+        },
+    },
+}
+
+core_current_stubs_soong_config_defaults {
+    name: "core_current_stubs_exportable_soong_config_defaults",
+    soong_config_variables: {
+        release_hidden_api_exportable_stubs: {
+            dist: {
+                targets: dist_targets,
+                dest: "core.current.stubs.jar",
+            },
+        },
+    },
+}
+
 java_library {
     name: "core.current.stubs",
     defaults: [
         "core.current.stubs.defaults",
+        "core_current_stubs_everything_soong_config_defaults",
     ],
     static_libs: [
         "art.module.public.api.stubs",
@@ -76,16 +116,13 @@
     name: "core.current.stubs.exportable",
     defaults: [
         "core.current.stubs.defaults",
+        "core_current_stubs_exportable_soong_config_defaults",
     ],
     static_libs: [
         "art.module.public.api.stubs.exportable",
         "conscrypt.module.public.api.stubs.exportable",
         "i18n.module.public.api.stubs.exportable",
     ],
-    dist: {
-        targets: dist_targets,
-        dest: "core.current.stubs.jar",
-    },
 }
 
 // Distributed with the SDK for turning into system modules to compile apps
@@ -95,10 +132,10 @@
 // prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk
 // directory.
 java_library {
-    name: "core-current-stubs-for-system-modules",
+    name: "core-current-stubs-for-system-modules-exportable",
     visibility: ["//development/sdk"],
     static_libs: [
-        "core.current.stubs",
+        "core.current.stubs.exportable",
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
@@ -118,6 +155,19 @@
     ],
 }
 
+java_library {
+    name: "core-current-stubs-for-system-modules",
+    visibility: ["//development/sdk"],
+    static_libs: [
+        "core.current.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+}
+
 // Defaults module to strip out android annotations
 java_defaults {
     name: "system-modules-no-annotations",
diff --git a/java/core-libraries/jarjar-strip-annotations-rules.txt b/java/core-libraries/jarjar-strip-annotations-rules.txt
index a1c261b..c74eaca 100644
--- a/java/core-libraries/jarjar-strip-annotations-rules.txt
+++ b/java/core-libraries/jarjar-strip-annotations-rules.txt
@@ -2,3 +2,4 @@
 strip-annotation android.annotation.Nullable
 strip-annotation androidx.annotation.RecentlyNonNull
 strip-annotation androidx.annotation.RecentlyNullable
+strip-annotation android.annotation.FlaggedApi
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 3f4e3cd..bfacea6 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -20,6 +20,8 @@
 
 	"android/soong/android"
 	"android/soong/dexpreopt"
+
+	"github.com/google/blueprint/depset"
 )
 
 type DeviceHostConverter struct {
@@ -96,9 +98,9 @@
 		ctx.PropertyErrorf("libs", "at least one dependency is required")
 	}
 
-	var transitiveHeaderJars []*android.DepSet[android.Path]
-	var transitiveImplementationJars []*android.DepSet[android.Path]
-	var transitiveResourceJars []*android.DepSet[android.Path]
+	var transitiveHeaderJars []depset.DepSet[android.Path]
+	var transitiveImplementationJars []depset.DepSet[android.Path]
+	var transitiveResourceJars []depset.DepSet[android.Path]
 
 	ctx.VisitDirectDepsWithTag(deviceHostConverterDepTag, func(m android.Module) {
 		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
@@ -110,15 +112,9 @@
 			d.srcJarArgs = append(d.srcJarArgs, dep.SrcJarArgs...)
 			d.srcJarDeps = append(d.srcJarDeps, dep.SrcJarDeps...)
 
-			if dep.TransitiveStaticLibsHeaderJars != nil {
-				transitiveHeaderJars = append(transitiveHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-			}
-			if dep.TransitiveStaticLibsImplementationJars != nil {
-				transitiveImplementationJars = append(transitiveImplementationJars, dep.TransitiveStaticLibsImplementationJars)
-			}
-			if dep.TransitiveStaticLibsResourceJars != nil {
-				transitiveResourceJars = append(transitiveResourceJars, dep.TransitiveStaticLibsResourceJars)
-			}
+			transitiveHeaderJars = append(transitiveHeaderJars, dep.TransitiveStaticLibsHeaderJars)
+			transitiveImplementationJars = append(transitiveImplementationJars, dep.TransitiveStaticLibsImplementationJars)
+			transitiveResourceJars = append(transitiveResourceJars, dep.TransitiveStaticLibsResourceJars)
 		} else {
 			ctx.PropertyErrorf("libs", "module %q cannot be used as a dependency", ctx.OtherModuleName(m))
 		}
@@ -147,9 +143,9 @@
 	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
 		HeaderJars:                             d.headerJars,
 		LocalHeaderJars:                        d.headerJars,
-		TransitiveStaticLibsHeaderJars:         android.NewDepSet(android.PREORDER, nil, transitiveHeaderJars),
-		TransitiveStaticLibsImplementationJars: android.NewDepSet(android.PREORDER, nil, transitiveImplementationJars),
-		TransitiveStaticLibsResourceJars:       android.NewDepSet(android.PREORDER, nil, transitiveResourceJars),
+		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, nil, transitiveHeaderJars),
+		TransitiveStaticLibsImplementationJars: depset.New(depset.PREORDER, nil, transitiveImplementationJars),
+		TransitiveStaticLibsResourceJars:       depset.New(depset.PREORDER, nil, transitiveResourceJars),
 		ImplementationAndResourcesJars:         d.implementationAndResourceJars,
 		ImplementationJars:                     d.implementationJars,
 		ResourceJars:                           d.resourceJars,
diff --git a/java/dex.go b/java/dex.go
index e0e642c..2b3c931 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -133,7 +133,7 @@
 			`$d8Template${config.D8Cmd} ${config.D8Flags} $d8Flags --output $outDir --no-dex-input-jar $in && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
-			`rm -f "$outDir/classes*.dex" "$outDir/classes.dex.jar"`,
+			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar"`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
 			"${config.SoongZipCmd}",
@@ -172,7 +172,7 @@
 			`rm -rf ${outUsageDir} && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
-			`rm -f "$outDir/classes*.dex" "$outDir/classes.dex.jar"`,
+			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar"`,
 		Depfile: "${out}.d",
 		Deps:    blueprint.DepsGCC,
 		CommandDeps: []string{
@@ -220,14 +220,21 @@
 		deps = append(deps, f)
 	}
 
-	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" {
+	var requestReleaseMode bool
+	requestReleaseMode, flags = android.RemoveFromList("--release", flags)
+
+	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" || ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
 		flags = append(flags, "--debug")
+		requestReleaseMode = false
 	}
 
-	if ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
-		flags = append(flags,
-			"--debug",
-			"--verbose")
+	// Don't strip out debug information for eng builds, unless the target
+	// explicitly provided the `--release` build flag. This allows certain
+	// test targets to remain optimized as part of eng test_suites builds.
+	if requestReleaseMode {
+		flags = append(flags, "--release")
+	} else if ctx.Config().Eng() {
+		flags = append(flags, "--debug")
 	}
 
 	// Supplying the platform build flag disables various features like API modeling and desugaring.
@@ -245,14 +252,11 @@
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
-	if !Bool(d.dexProperties.No_dex_container) && effectiveVersion.FinalOrFutureInt() >= 36 {
+	if !Bool(d.dexProperties.No_dex_container) && effectiveVersion.FinalOrFutureInt() >= 36 && ctx.Config().UseDexV41() {
 		// W is 36, but we have not bumped the SDK version yet, so check for both.
 		if ctx.Config().PlatformSdkVersion().FinalInt() >= 36 ||
-			ctx.Config().PlatformSdkCodename() == "Wear" {
-			// TODO(b/329465418): Skip this module since it causes issue with app DRM
-			if ctx.ModuleName() != "framework-minus-apex" {
-				flags = append([]string{"-JDcom.android.tools.r8.dexContainerExperiment"}, flags...)
-			}
+			ctx.Config().PlatformSdkCodename() == "Baklava" {
+			flags = append([]string{"-JDcom.android.tools.r8.dexContainerExperiment"}, flags...)
 		}
 	}
 
@@ -288,7 +292,7 @@
 	return d8Flags, d8Deps, artProfileOutput
 }
 
-func (d *dexer) r8Flags(ctx android.ModuleContext, dexParams *compileDexParams) (r8Flags []string, r8Deps android.Paths, artProfileOutput *android.OutputPath) {
+func (d *dexer) r8Flags(ctx android.ModuleContext, dexParams *compileDexParams, debugMode bool) (r8Flags []string, r8Deps android.Paths, artProfileOutput *android.OutputPath) {
 	flags := dexParams.flags
 	opt := d.dexProperties.Optimize
 
@@ -317,20 +321,16 @@
 	r8Deps = append(r8Deps, flags.dexClasspath...)
 
 	transitiveStaticLibsLookupMap := map[android.Path]bool{}
-	if d.transitiveStaticLibsHeaderJarsForR8 != nil {
-		for _, jar := range d.transitiveStaticLibsHeaderJarsForR8.ToList() {
-			transitiveStaticLibsLookupMap[jar] = true
-		}
+	for _, jar := range d.transitiveStaticLibsHeaderJarsForR8.ToList() {
+		transitiveStaticLibsLookupMap[jar] = true
 	}
 	transitiveHeaderJars := android.Paths{}
-	if d.transitiveLibsHeaderJarsForR8 != nil {
-		for _, jar := range d.transitiveLibsHeaderJarsForR8.ToList() {
-			if _, ok := transitiveStaticLibsLookupMap[jar]; ok {
-				// don't include a lib if it is already packaged in the current JAR as a static lib
-				continue
-			}
-			transitiveHeaderJars = append(transitiveHeaderJars, jar)
+	for _, jar := range d.transitiveLibsHeaderJarsForR8.ToList() {
+		if _, ok := transitiveStaticLibsLookupMap[jar]; ok {
+			// don't include a lib if it is already packaged in the current JAR as a static lib
+			continue
 		}
+		transitiveHeaderJars = append(transitiveHeaderJars, jar)
 	}
 	transitiveClasspath := classpath(transitiveHeaderJars)
 	r8Flags = append(r8Flags, transitiveClasspath.FormJavaClassPath("-libraryjars"))
@@ -360,7 +360,9 @@
 		r8Flags = append(r8Flags, "--force-proguard-compatibility")
 	}
 
-	if Bool(opt.Optimize) || Bool(opt.Obfuscate) {
+	// Avoid unnecessary stack frame noise by only injecting source map ids for non-debug
+	// optimized or obfuscated targets.
+	if (Bool(opt.Optimize) || Bool(opt.Obfuscate)) && !debugMode {
 		// TODO(b/213833843): Allow configuration of the prefix via a build variable.
 		var sourceFilePrefix = "go/retraceme "
 		var sourceFileTemplate = "\"" + sourceFilePrefix + "%MAP_ID\""
@@ -384,11 +386,6 @@
 	// TODO(ccross): if this is an instrumentation test of an obfuscated app, use the
 	// dictionary of the app and move the app from libraryjars to injars.
 
-	// Don't strip out debug information for eng builds.
-	if ctx.Config().Eng() {
-		r8Flags = append(r8Flags, "--debug")
-	}
-
 	// TODO(b/180878971): missing classes should be added to the relevant builds.
 	// TODO(b/229727645): do not use true as default for Android platform builds.
 	if proptools.BoolDefault(opt.Ignore_warnings, true) {
@@ -402,6 +399,10 @@
 		r8Flags = append(r8Flags, "--resource-output", d.resourcesOutput.Path().String())
 		if d.dexProperties.optimizedResourceShrinkingEnabled(ctx) {
 			r8Flags = append(r8Flags, "--optimized-resource-shrinking")
+			if Bool(d.dexProperties.Optimize.Optimized_shrink_resources) {
+				// Explicitly opted into optimized shrinking, no need for keeping R$id entries
+				r8Flags = append(r8Flags, "--force-optimized-resource-shrinking")
+			}
 		}
 	}
 
@@ -426,17 +427,18 @@
 // 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)
+	if dexParams.artProfileInput == nil {
+		return nil, nil, 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
 
 }
@@ -480,7 +482,8 @@
 			proguardUsageZip,
 			proguardConfiguration,
 		}
-		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams)
+		debugMode := android.InList("--debug", commonFlags)
+		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams, debugMode)
 		rule := r8
 		args := map[string]string{
 			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
diff --git a/java/dex_test.go b/java/dex_test.go
index 4862d06..8bc28e6 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -713,3 +713,97 @@
 	}
 }`)
 }
+
+func TestDebugReleaseFlags(t *testing.T) {
+	bp := `
+		android_app {
+			name: "app",
+			srcs: ["foo.java"],
+			platform_apis: true,
+			dxflags: ["%s"]
+		}
+	`
+
+	testcases := []struct {
+		name          string
+		envVar        string
+		isEng         bool
+		dxFlags       string
+		expectedFlags string
+	}{
+		{
+			name:          "app_no_optimize_dx",
+			envVar:        "NO_OPTIMIZE_DX",
+			expectedFlags: "--debug",
+		},
+		{
+			name:    "app_release_no_optimize_dx",
+			envVar:  "NO_OPTIMIZE_DX",
+			dxFlags: "--release",
+			// Global env vars override explicit dxflags.
+			expectedFlags: "--debug",
+		},
+		{
+			name:          "app_generate_dex_debug",
+			envVar:        "GENERATE_DEX_DEBUG",
+			expectedFlags: "--debug",
+		},
+		{
+			name:    "app_release_generate_dex_debug",
+			envVar:  "GENERATE_DEX_DEBUG",
+			dxFlags: "--release",
+			// Global env vars override explicit dxflags.
+			expectedFlags: "--debug",
+		},
+		{
+			name:          "app_eng",
+			isEng:         true,
+			expectedFlags: "--debug",
+		},
+		{
+			name:    "app_release_eng",
+			isEng:   true,
+			dxFlags: "--release",
+			// Eng mode does *not* override explicit dxflags.
+			expectedFlags: "--release",
+		},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			fixturePreparer := PrepareForTestWithJavaDefaultModules
+			fixturePreparer = android.GroupFixturePreparers(
+				fixturePreparer,
+				android.FixtureModifyProductVariables(
+					func(variables android.FixtureProductVariables) {
+						variables.Eng = proptools.BoolPtr(tc.isEng)
+					},
+				),
+			)
+			if tc.envVar != "" {
+				fixturePreparer = android.GroupFixturePreparers(
+					fixturePreparer,
+					android.FixtureMergeEnv(map[string]string{
+						tc.envVar: "true",
+					}),
+				)
+			}
+			result := fixturePreparer.RunTestWithBp(t, fmt.Sprintf(bp, tc.dxFlags))
+
+			appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
+			android.AssertStringDoesContain(t, "expected flag in R8 flags",
+				appR8.Args["r8Flags"], tc.expectedFlags)
+
+			var unexpectedFlags string
+			if tc.expectedFlags == "--debug" {
+				unexpectedFlags = "--release"
+			} else if tc.expectedFlags == "--release" {
+				unexpectedFlags = "--debug"
+			}
+			if unexpectedFlags != "" {
+				android.AssertStringDoesNotContain(t, "unexpected flag in R8 flags",
+					appR8.Args["r8Flags"], unexpectedFlags)
+			}
+		})
+	}
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 63a8634..5928446 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -494,6 +494,12 @@
 		PresignedPrebuilt: d.isPresignedPrebuilt,
 	}
 
+	if ctx.Config().InstallApexSystemServerDexpreoptSamePartition() {
+		dexpreoptConfig.ApexPartition = android.PathForModuleInstall(ctx).Partition()
+	} else {
+		dexpreoptConfig.ApexPartition = "system"
+	}
+
 	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "dexpreopt.config")
 	dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
 	ctx.CheckbuildFile(d.configPath)
@@ -556,7 +562,7 @@
 	// TODO(b/346662300): Let dexpreopter generate the installPath for dexpreopt files instead of
 	// using the dex location to generate the installPath.
 	if isApexSystemServerJar {
-		dexpreoptPartition = "system"
+		dexpreoptPartition = dexpreoptConfig.ApexPartition
 	}
 	for _, install := range dexpreoptRule.Installs() {
 		// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index a2e4734..8c60d23 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -463,8 +463,9 @@
 
 func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
 	ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
+	ctx.RegisterModuleType("art_boot_images", artBootImagesFactory)
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("dex_bootjars_deps", DexpreoptBootJarsMutator).Parallel()
+		ctx.BottomUp("dex_bootjars_deps", DexpreoptBootJarsMutator)
 	})
 }
 
@@ -601,6 +602,7 @@
 	d.defaultBootImage = defaultBootImageConfig(ctx)
 	d.otherImages = make([]*bootImageConfig, 0, len(imageConfigs)-1)
 	var profileInstalls android.RuleBuilderInstalls
+	var artBootImageHostInstalls android.RuleBuilderInstalls
 	for _, name := range getImageNames() {
 		config := imageConfigs[name]
 		if config != d.defaultBootImage {
@@ -616,6 +618,18 @@
 			d.bootFrameworkProfile = bootProfile
 			profileInstalls = append(profileInstalls, installs...)
 		}
+		// Gather the install files of the host variant of the ART boot image.
+		// These installed files will be used in ART tests.
+		if config.name == "art" {
+			for _, variant := range config.variants {
+				if variant.target.Os != ctx.Config().BuildOS {
+					// not a host variant
+					continue
+				}
+				artBootImageHostInstalls = append(artBootImageHostInstalls, variant.installs...)
+				artBootImageHostInstalls = append(artBootImageHostInstalls, variant.vdexInstalls...)
+			}
+		}
 	}
 	if len(profileInstalls) > 0 {
 		android.SetProvider(ctx, profileInstallInfoProvider, profileInstallInfo{
@@ -626,6 +640,15 @@
 			installFile(ctx, install)
 		}
 	}
+	// Set a provider containing the install files of the host variant of the ART boot image.
+	// The actual install rules will be created by `art_boot_images`
+	android.SetProvider(
+		ctx,
+		artBootImageHostInfoProvider,
+		artBootImageHostInfo{
+			installs: artBootImageHostInstalls,
+		},
+	)
 }
 
 // GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make.
@@ -968,6 +991,14 @@
 	}
 }
 
+var artBootImageHostInfoProvider = blueprint.NewProvider[artBootImageHostInfo]()
+
+// artBootImageHostInfo contains the install locations of the host variant of ART boot image
+// this contains both the primary and secondary arch locations
+type artBootImageHostInfo struct {
+	installs android.RuleBuilderInstalls
+}
+
 // Generate boot image build rules for a specific target.
 func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs {
 
@@ -1186,8 +1217,13 @@
 		return nil
 	}
 
-	defaultProfile := "frameworks/base/config/boot-image-profile.txt"
-	extraProfile := "frameworks/base/config/boot-image-profile-extra.txt"
+	defaultProfile := "frameworks/base/boot/boot-image-profile.txt"
+	// If ART is prebuilt, primarily in next release configs, this will still use
+	// the profile from source which represent the latest code, so it may not
+	// correspond to the BCP jars in the prebuilt APEX, but this is the profile we
+	// have access to.
+	artProfile := "art/build/boot/boot-image-profile.txt"
+	extraProfile := "frameworks/base/boot/boot-image-profile-extra.txt"
 
 	rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -1202,6 +1238,9 @@
 		// Return nil and continue without profile.
 		return nil
 	}
+	if path := android.ExistentPathForSource(ctx, artProfile); path.Valid() {
+		profiles = append(profiles, path.Path())
+	}
 	if path := android.ExistentPathForSource(ctx, extraProfile); path.Valid() {
 		profiles = append(profiles, path.Path())
 	}
@@ -1259,7 +1298,7 @@
 		return nil, nil
 	}
 
-	defaultProfile := "frameworks/base/config/boot-profile.txt"
+	defaultProfile := "frameworks/base/boot/boot-profile.txt"
 	bootFrameworkProfile := android.PathForSource(ctx, defaultProfile)
 
 	profile := image.dir.Join(ctx, "boot.bprof")
@@ -1344,18 +1383,7 @@
 	}
 
 	image := d.defaultBootImage
-	if image != nil {
-		if profileInstallInfo, ok := android.OtherModuleProvider(ctx, d, profileInstallInfoProvider); ok {
-			ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", profileInstallInfo.profileInstalls.String())
-			if profileInstallInfo.profileLicenseMetadataFile.Valid() {
-				ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", profileInstallInfo.profileLicenseMetadataFile.String())
-			}
-		}
-
-		if SkipDexpreoptBootJars(ctx) {
-			return
-		}
-
+	if image != nil && !SkipDexpreoptBootJars(ctx) {
 		global := dexpreopt.GetGlobalConfig(ctx)
 		dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
@@ -1396,3 +1424,70 @@
 		OutputFile: android.OptionalPathForPath(d.bootFrameworkProfile),
 	}}
 }
+
+// artBootImages is a thin wrapper around `dex_bootjars`.
+// it creates the installation rules for the host variant of the ART boot image.
+type artBootImages struct {
+	android.ModuleBase
+
+	// A non-empty file that will be written as `LOCAL_SOONG_INSTALLED_MODULE` in out/soong/Android-*.mk
+	outputFile android.OptionalPath
+}
+
+func artBootImagesFactory() android.Module {
+	m := &artBootImages{}
+	android.InitAndroidMultiTargetsArchModule(m, android.HostSupported, android.MultilibCommon)
+	return m
+}
+
+func (dbj *artBootImages) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Create a dependency on `dex_bootjars` to access the intermediate locations of host art boot image.
+	ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), dexpreoptBootJarDepTag, "dex_bootjars")
+}
+
+func (d *artBootImages) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(m android.Module) {
+		hostInstallsInfo, ok := android.OtherModuleProvider(ctx, m, artBootImageHostInfoProvider)
+		if !ok {
+			ctx.ModuleErrorf("Could not find information about the host variant of ART boot image")
+		}
+		installs := d.installFile(ctx, hostInstallsInfo.installs)
+		if len(installs) > 0 {
+			d.outputFile = android.OptionalPathForPath(installs[0])
+			// Create a phony target that can ART run-tests can depend on.
+			ctx.Phony(d.Name(), installs...)
+		} else {
+			// this might be true e.g. when building with `WITH_DEXPREOPT=false`
+			// create an empty file so that the `art_boot_images` is known to the packaging system.
+			d.outputFile = android.OptionalPathForPath(android.PathForModuleOut(ctx, "undefined_art_boot_images"))
+		}
+	})
+}
+
+// Creates an installation rule for host variant of ART boot image files.
+// Returns the list of install locations (out/host/linux-x86/...)
+func (d *artBootImages) installFile(ctx android.ModuleContext, ruleBuilderInstalls android.RuleBuilderInstalls) android.Paths {
+	var ret android.Paths
+	for _, ruleBuilderInstall := range ruleBuilderInstalls {
+		installDir := android.PathForModuleInstall(
+			ctx,
+			strings.TrimPrefix(filepath.Dir(ruleBuilderInstall.To), "/"),
+		)
+		filename := filepath.Base(ruleBuilderInstall.To)
+		ctx.InstallFile(
+			installDir,
+			filename,
+			ruleBuilderInstall.From,
+		)
+		ret = append(ret, installDir.Join(ctx, filename))
+	}
+	return ret
+}
+
+// Set `OutputFile` expclitly so that this module does not get elided when generating out/soong/Android-*.mk
+func (d *artBootImages) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: d.outputFile,
+	}}
+}
diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go
index 33be603..c971565 100644
--- a/java/dexpreopt_check.go
+++ b/java/dexpreopt_check.go
@@ -17,6 +17,8 @@
 import (
 	"strings"
 
+	"github.com/google/blueprint"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 
@@ -43,16 +45,12 @@
 type dexpreoptSystemserverCheck struct {
 	android.SingletonModuleBase
 
-	// Mapping from the module name to the install paths to the compilation artifacts.
-	artifactsByModuleName map[string][]string
-
 	// The install paths to the compilation artifacts.
 	artifacts []string
 }
 
 func dexpreoptSystemserverCheckFactory() android.SingletonModule {
 	m := &dexpreoptSystemserverCheck{}
-	m.artifactsByModuleName = make(map[string][]string)
 	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	return m
 }
@@ -62,7 +60,25 @@
 		ctx, "", strings.TrimPrefix(location, "/"))
 }
 
-func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+type systemServerDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+// systemServerJarDepTag willl be used for validation. Skip visiblility.
+func (b systemServerDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+var (
+	// dep tag for platform and apex system server jars
+	systemServerJarDepTag = systemServerDependencyTag{}
+)
+
+var _ android.ExcludeFromVisibilityEnforcementTag = systemServerJarDepTag
+
+// Add a depenendency on the system server jars. The dexpreopt files of those will be emitted to make.
+// The kati packaging system will verify that those files appear in installed files.
+// Adding the dependency allows the singleton module to determine whether an apex system server jar is system_ext specific.
+func (m *dexpreoptSystemserverCheck) DepsMutator(ctx android.BottomUpMutatorContext) {
 	global := dexpreopt.GetGlobalConfig(ctx)
 	targets := ctx.Config().Targets[android.Android]
 
@@ -72,23 +88,27 @@
 		return
 	}
 
-	systemServerJars := global.AllSystemServerJars(ctx)
-	for _, jar := range systemServerJars.CopyOfJars() {
-		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, jar)
-		odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType)
+	ctx.AddDependency(ctx.Module(), systemServerJarDepTag, global.AllSystemServerJars(ctx).CopyOfJars()...)
+}
+
+func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	targets := ctx.Config().Targets[android.Android]
+
+	ctx.VisitDirectDepsWithTag(systemServerJarDepTag, func(systemServerJar android.Module) {
+		partition := "system"
+		if systemServerJar.InstallInSystemExt() && ctx.Config().InstallApexSystemServerDexpreoptSamePartition() {
+			partition = ctx.DeviceConfig().SystemExtPath() // system_ext
+		}
+		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, systemServerJar.Name())
+		odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType, partition)
 		odexPath := getInstallPath(ctx, odexLocation)
 		vdexPath := getInstallPath(ctx, pathtools.ReplaceExtension(odexLocation, "vdex"))
-		m.artifactsByModuleName[jar] = []string{odexPath.String(), vdexPath.String()}
-	}
+		m.artifacts = append(m.artifacts, odexPath.String(), vdexPath.String())
+	})
 }
 
 func (m *dexpreoptSystemserverCheck) GenerateSingletonBuildActions(ctx android.SingletonContext) {
-	// Only keep modules defined in Soong.
-	ctx.VisitAllModules(func(module android.Module) {
-		if artifacts, ok := m.artifactsByModuleName[module.Name()]; ok {
-			m.artifacts = append(m.artifacts, artifacts...)
-		}
-	})
 }
 
 func (m *dexpreoptSystemserverCheck) MakeVars(ctx android.MakeVarsContext) {
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 37c54b9..33c682b 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -1335,8 +1335,6 @@
 DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTboot=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art
 DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTmainline=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art:out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/boot-framework-foo.art
 DEXPREOPT_IMAGE_NAMES=art boot mainline
-DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof:/system/etc/boot-image.prof out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof
-DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-extra1.oat:/apex/art_boot_images/javalib/arm/boot-extra1.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat:/apex/art_boot_images/javalib/arm64/boot-extra1.oat
 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat:/apex/art_boot_images/javalib/x86/boot-extra1.oat
diff --git a/java/droiddoc.go b/java/droiddoc.go
index a7e92d9..2dda72b 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -373,8 +373,7 @@
 				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 			}
 		case libTag, sdkLibTag:
-			if _, ok := module.(SdkLibraryDependency); ok {
-				sdkInfo, _ := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider)
+			if sdkInfo, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
 				generatingLibsString := android.PrettyConcat(
 					getGeneratingLibs(ctx, j.SdkVersion(ctx), module.Name(), sdkInfo), true, "or")
 				ctx.ModuleErrorf("cannot depend directly on java_sdk_library %q; try depending on %s instead", module.Name(), generatingLibsString)
@@ -428,9 +427,9 @@
 	// Find the corresponding aconfig_declarations module name for such case.
 	for _, src := range j.properties.Srcs {
 		if moduleName, tag := android.SrcIsModuleWithTag(src); moduleName != "" {
-			otherModule := android.GetModuleFromPathDep(ctx, moduleName, tag)
+			otherModule := android.GetModuleProxyFromPathDep(ctx, moduleName, tag)
 			if otherModule != nil {
-				if dep, ok := android.OtherModuleProvider(ctx, otherModule, android.CodegenInfoProvider); ok {
+				if dep, ok := android.OtherModuleProvider(ctx, *otherModule, android.CodegenInfoProvider); ok {
 					deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...)
 				}
 			}
@@ -579,7 +578,6 @@
 
 	rule.Build("javadoc", "javadoc")
 
-	ctx.SetOutputFiles(android.Paths{j.stubsSrcJar}, "")
 	ctx.SetOutputFiles(android.Paths{j.docZip}, ".docs.zip")
 }
 
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 6bcdf85..e955949 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -769,15 +769,6 @@
 // property is defined, apply transformations and only revert the flagged apis that are not
 // enabled via release configurations and are not specified in aconfig_declarations
 func generateRevertAnnotationArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, aconfigFlagsPaths android.Paths) {
-
-	if len(aconfigFlagsPaths) == 0 {
-		cmd.Flag("--revert-annotation android.annotation.FlaggedApi")
-		return
-	}
-
-	releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String()))
-	revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String()))
-
 	var filterArgs string
 	switch stubsType {
 	// No flagged apis specific flags need to be passed to metalava when generating
@@ -799,6 +790,15 @@
 		}
 	}
 
+	if len(aconfigFlagsPaths) == 0 {
+		// This argument should not be added for "everything" stubs
+		cmd.Flag("--revert-annotation android.annotation.FlaggedApi")
+		return
+	}
+
+	releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String()))
+	revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String()))
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        gatherReleasedFlaggedApisRule,
 		Inputs:      aconfigFlagsPaths,
@@ -997,12 +997,13 @@
 		msg := `$'` + // Enclose with $' ... '
 			`************************************************************\n` +
 			`Your API changes are triggering API Lint warnings or errors.\n` +
-			`To make these errors go away, fix the code according to the\n` +
-			`error and/or warning messages above.\n` +
 			`\n` +
-			`If it is not possible to do so, there are workarounds:\n` +
+			`To make the failures go away:\n` +
 			`\n` +
-			`1. You can suppress the errors with @SuppressLint("<id>")\n` +
+			`1. REQUIRED: Read the messages carefully and address them by` +
+			`   fixing the API if appropriate.\n` +
+			`2. If the failure is a false positive, you can suppress it with:\n` +
+			`        @SuppressLint("<id>")\n` +
 			`   where the <id> is given in brackets in the error message above.\n`
 
 		if baselineFile.Valid() {
@@ -1010,8 +1011,8 @@
 			cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
 
 			msg += fmt.Sprintf(``+
-				`2. You can update the baseline by executing the following\n`+
-				`   command:\n`+
+				`3. FOR LSC ONLY: You can update the baseline by executing\n`+
+				`   the following command:\n`+
 				`       (cd $ANDROID_BUILD_TOP && cp \\\n`+
 				`       "%s" \\\n`+
 				`       "%s")\n`+
@@ -1019,7 +1020,7 @@
 				`   repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
 		} else {
 			msg += fmt.Sprintf(``+
-				`2. You can add a baseline file of existing lint failures\n`+
+				`3. FOR LSC ONLY: You can add a baseline file of existing lint failures\n`+
 				`   to the build rule of %s.\n`, d.Name())
 		}
 		// Note the message ends with a ' (single quote), to close the $' ... ' .
@@ -1373,7 +1374,7 @@
 		for _, stubType := range android.SortedKeys(stubsTypeToPrefix) {
 			tagWithPrefix := stubsTypeToPrefix[stubType] + tag
 			outputFile, err := tagToOutputFileFunc[tag](stubType)
-			if err == nil {
+			if err == nil && outputFile != nil {
 				ctx.SetOutputFiles(android.Paths{outputFile}, tagWithPrefix)
 			}
 		}
diff --git a/java/fuzz.go b/java/fuzz.go
index d37c558..90f09a8 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -85,10 +85,11 @@
 
 func (j *JavaFuzzTest) DepsMutator(ctx android.BottomUpMutatorContext) {
 	if j.Os().Class.String() == deviceString {
-		j.testProperties.Jni_libs = append(j.testProperties.Jni_libs, artDeps...)
+		j.testProperties.Jni_libs.AppendSimpleValue(artDeps)
 	}
 
-	if len(j.testProperties.Jni_libs) > 0 {
+	jniLibs := j.testProperties.Jni_libs.GetOrDefault(ctx, nil)
+	if len(jniLibs) > 0 {
 		if j.fuzzPackagedModule.FuzzProperties.Fuzz_config == nil {
 			config := &fuzz.FuzzConfig{}
 			j.fuzzPackagedModule.FuzzProperties.Fuzz_config = config
@@ -98,7 +99,7 @@
 		j.fuzzPackagedModule.FuzzProperties.Fuzz_config.IsJni = proptools.BoolPtr(true)
 		for _, target := range ctx.MultiTargets() {
 			sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
-			ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, j.testProperties.Jni_libs...)
+			ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, jniLibs...)
 		}
 	}
 
@@ -109,6 +110,9 @@
 	if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
 		j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
 	}
+	if j.fuzzPackagedModule.FuzzProperties.Device_common_corpus != nil {
+		j.fuzzPackagedModule.Corpus = append(j.fuzzPackagedModule.Corpus, android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Device_common_corpus)...)
+	}
 	if j.fuzzPackagedModule.FuzzProperties.Data != nil {
 		j.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Data)
 	}
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 4144de8..3650058 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -298,13 +298,12 @@
 // available, or reports an error.
 func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path {
 	var dexJar OptionalDexJarPath
-	if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
+	if sdkLibrary, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
 		if ctx.Config().ReleaseHiddenApiExportableStubs() {
-			dexJar = sdkLibrary.SdkApiExportableStubDexJar(ctx, kind)
+			dexJar = sdkLibrary.ExportableStubDexJarPaths[kind]
 		} else {
-			dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind)
+			dexJar = sdkLibrary.EverythingStubDexJarPaths[kind]
 		}
-
 	} else if j, ok := module.(UsesLibraryDependency); ok {
 		dexJar = j.DexJarBuildPath(ctx)
 	} else {
@@ -853,15 +852,15 @@
 			i.StubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar)
 		}
 
-		if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
-			removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, sdkKind)
+		if sdkLibrary, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
+			removedTxtFile := sdkLibrary.RemovedTxtFiles[sdkKind]
 			i.RemovedTxtFiles = append(i.RemovedTxtFiles, removedTxtFile.AsPaths()...)
 		}
 	}
 
 	// If the contents includes any java_sdk_library modules then add them to the stubs.
 	for _, module := range contents {
-		if _, ok := module.(SdkLibraryDependency); ok {
+		if _, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
 			// Add information for every possible API scope needed by hidden API.
 			for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
 				addFromModule(ctx, module, apiScope)
diff --git a/java/java.go b/java/java.go
index c1187dc..e1cfea5 100644
--- a/java/java.go
+++ b/java/java.go
@@ -26,9 +26,9 @@
 	"strings"
 
 	"android/soong/remoteexec"
-	"android/soong/testing"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -70,9 +70,9 @@
 	// established, to not get the dependencies split into the wrong variants and
 	// to support the checks in dexpreoptDisabled().
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("dexpreopt_tool_deps", dexpreoptToolDepsMutator).Parallel()
+		ctx.BottomUp("dexpreopt_tool_deps", dexpreoptToolDepsMutator)
 		// needs access to ApexInfoProvider which is available after variant creation
-		ctx.BottomUp("jacoco_deps", jacocoDepsMutator).Parallel()
+		ctx.BottomUp("jacoco_deps", jacocoDepsMutator)
 	})
 
 	ctx.RegisterParallelSingletonType("kythe_java_extract", kytheExtractJavaFactory)
@@ -226,9 +226,9 @@
 
 	// Rule for generating device binary default wrapper
 	deviceBinaryWrapper = pctx.StaticRule("deviceBinaryWrapper", blueprint.RuleParams{
-		Command: `echo -e '#!/system/bin/sh\n` +
+		Command: `printf '#!/system/bin/sh\n` +
 			`export CLASSPATH=/system/framework/$jar_name\n` +
-			`exec app_process /$partition/bin $main_class "$$@"'> ${out}`,
+			`exec app_process /$partition/bin $main_class "$$@"\n'> ${out}`,
 		Description: "Generating device binary wrapper ${jar_name}",
 	}, "jar_name", "partition", "main_class")
 )
@@ -242,10 +242,10 @@
 	// TransitiveDepsProguardSpecFiles is a depset of paths to proguard flags files that are exported from
 	// all transitive deps. This list includes all proguard flags files from transitive static dependencies,
 	// and all proguard flags files from transitive libs dependencies which set `export_proguard_spec: true`.
-	ProguardFlagsFiles *android.DepSet[android.Path]
+	ProguardFlagsFiles depset.DepSet[android.Path]
 
 	// implementation detail to store transitive proguard flags files from exporting shared deps
-	UnconditionallyExportedProguardFlags *android.DepSet[android.Path]
+	UnconditionallyExportedProguardFlags depset.DepSet[android.Path]
 }
 
 var ProguardSpecInfoProvider = blueprint.NewProvider[ProguardSpecInfo]()
@@ -260,19 +260,19 @@
 	RepackagedHeaderJars android.Paths
 
 	// set of header jars for all transitive libs deps
-	TransitiveLibsHeaderJarsForR8 *android.DepSet[android.Path]
+	TransitiveLibsHeaderJarsForR8 depset.DepSet[android.Path]
 
 	// set of header jars for all transitive static libs deps
-	TransitiveStaticLibsHeaderJarsForR8 *android.DepSet[android.Path]
+	TransitiveStaticLibsHeaderJarsForR8 depset.DepSet[android.Path]
 
 	// depset of header jars for this module and all transitive static dependencies
-	TransitiveStaticLibsHeaderJars *android.DepSet[android.Path]
+	TransitiveStaticLibsHeaderJars depset.DepSet[android.Path]
 
 	// depset of implementation jars for this module and all transitive static dependencies
-	TransitiveStaticLibsImplementationJars *android.DepSet[android.Path]
+	TransitiveStaticLibsImplementationJars depset.DepSet[android.Path]
 
 	// depset of resource jars for this module and all transitive static dependencies
-	TransitiveStaticLibsResourceJars *android.DepSet[android.Path]
+	TransitiveStaticLibsResourceJars depset.DepSet[android.Path]
 
 	// ImplementationAndResourceJars is a list of jars that contain the implementations of classes
 	// in the module as well as any resources included in the module.
@@ -300,7 +300,7 @@
 	SrcJarDeps android.Paths
 
 	// The source files of this module and all its transitive static dependencies.
-	TransitiveSrcFiles *android.DepSet[android.Path]
+	TransitiveSrcFiles depset.DepSet[android.Path]
 
 	// ExportedPlugins is a list of paths that should be used as annotation processors for any
 	// module that depends on this module.
@@ -326,6 +326,8 @@
 	// AconfigIntermediateCacheOutputPaths is a path to the cache files collected from the
 	// java_aconfig_library modules that are statically linked to this module.
 	AconfigIntermediateCacheOutputPaths android.Paths
+
+	SdkVersion android.SdkSpec
 }
 
 var JavaInfoProvider = blueprint.NewProvider[*JavaInfo]()
@@ -451,7 +453,6 @@
 	javaApiContributionTag  = dependencyTag{name: "java-api-contribution"}
 	aconfigDeclarationTag   = dependencyTag{name: "aconfig-declaration"}
 	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)
@@ -593,9 +594,9 @@
 
 	disableTurbine bool
 
-	transitiveStaticLibsHeaderJars         []*android.DepSet[android.Path]
-	transitiveStaticLibsImplementationJars []*android.DepSet[android.Path]
-	transitiveStaticLibsResourceJars       []*android.DepSet[android.Path]
+	transitiveStaticLibsHeaderJars         []depset.DepSet[android.Path]
+	transitiveStaticLibsImplementationJars []depset.DepSet[android.Path]
+	transitiveStaticLibsResourceJars       []depset.DepSet[android.Path]
 }
 
 func checkProducesJars(ctx android.ModuleContext, dep android.SourceFileProducer) {
@@ -613,10 +614,8 @@
 	} 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.
+		// Build flag that controls whether Java 21 is used as the default
+		// target version, or Java 17.
 		return JAVA_VERSION_21
 	} else {
 		return JAVA_VERSION_17
@@ -951,6 +950,7 @@
 		// Even though the source javalib is not used, we need to hide it to prevent duplicate installation rules.
 		// TODO (b/331665856): Implement a principled solution for this.
 		j.HideFromMake()
+		j.SkipInstall()
 	}
 	j.provideHiddenAPIPropertyInfo(ctx)
 
@@ -1010,13 +1010,7 @@
 	}
 	j.compile(ctx, nil, nil, nil, nil)
 
-	// If this module is an impl library created from java_sdk_library,
-	// install the files under the java_sdk_library module outdir instead of this module outdir.
-	if j.SdkLibraryName() != nil && strings.HasSuffix(j.Name(), ".impl") {
-		j.setInstallRules(ctx, proptools.String(j.SdkLibraryName()))
-	} else {
-		j.setInstallRules(ctx, ctx.ModuleName())
-	}
+	j.setInstallRules(ctx)
 
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
 		TestOnly:       Bool(j.sourceProperties.Test_only),
@@ -1026,7 +1020,27 @@
 	setOutputFiles(ctx, j.Module)
 }
 
-func (j *Library) setInstallRules(ctx android.ModuleContext, installModuleName string) {
+func (j *Library) getJarInstallDir(ctx android.ModuleContext) android.InstallPath {
+	var installDir android.InstallPath
+	if ctx.InstallInTestcases() {
+		var archDir string
+		if !ctx.Host() {
+			archDir = ctx.DeviceConfig().DeviceArch()
+		}
+		installModuleName := ctx.ModuleName()
+		// If this module is an impl library created from java_sdk_library,
+		// install the files under the java_sdk_library module outdir instead of this module outdir.
+		if j.SdkLibraryName() != nil && strings.HasSuffix(j.Name(), ".impl") {
+			installModuleName = proptools.String(j.SdkLibraryName())
+		}
+		installDir = android.PathForModuleInstall(ctx, installModuleName, archDir)
+	} else {
+		installDir = android.PathForModuleInstall(ctx, "framework")
+	}
+	return installDir
+}
+
+func (j *Library) setInstallRules(ctx android.ModuleContext) {
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 
 	if (Bool(j.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() {
@@ -1040,17 +1054,7 @@
 				android.PathForHostDexInstall(ctx, "framework"),
 				j.Stem()+"-hostdex.jar", j.outputFile)
 		}
-		var installDir android.InstallPath
-		if ctx.InstallInTestcases() {
-			var archDir string
-			if !ctx.Host() {
-				archDir = ctx.DeviceConfig().DeviceArch()
-			}
-			installDir = android.PathForModuleInstall(ctx, installModuleName, archDir)
-		} else {
-			installDir = android.PathForModuleInstall(ctx, "framework")
-		}
-		j.installFile = ctx.InstallFileWithoutCheckbuild(installDir, j.Stem()+".jar", j.outputFile, extraInstallDeps...)
+		j.installFile = ctx.InstallFileWithoutCheckbuild(j.getJarInstallDir(ctx), j.Stem()+".jar", j.outputFile, extraInstallDeps...)
 	}
 }
 
@@ -1294,6 +1298,22 @@
 	// the test
 	Data []string `android:"path"`
 
+	// Same as data, but will add dependencies on modules using the device's os variation and
+	// the common arch variation. Useful for a host test that wants to embed a module built for
+	// device.
+	Device_common_data []string `android:"path_device_common"`
+
+	// same as data, but adds dependencies using the device's os variation and the device's first
+	// architecture's variation. Can be used to add a module built for device to the data of a
+	// host test.
+	Device_first_data []string `android:"path_device_first"`
+
+	// same as data, but adds dependencies using the device's os variation and the device's first
+	// 32-bit architecture's variation. If a 32-bit arch doesn't exist for this device, it will use
+	// a 64 bit arch instead. Can be used to add a module built for device to the data of a
+	// host test.
+	Device_first_prefer32_data []string `android:"path_device_first_prefer32"`
+
 	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
 	// explicitly.
@@ -1307,7 +1327,7 @@
 	Test_options TestOptions
 
 	// Names of modules containing JNI libraries that should be installed alongside the test.
-	Jni_libs []string
+	Jni_libs proptools.Configurable[[]string]
 
 	// Install the test into a folder named for the module in all test suites.
 	Per_testcase_directory *bool
@@ -1491,10 +1511,11 @@
 		}
 	}
 
-	if len(j.testProperties.Jni_libs) > 0 {
+	jniLibs := j.testProperties.Jni_libs.GetOrDefault(ctx, nil)
+	if len(jniLibs) > 0 {
 		for _, target := range ctx.MultiTargets() {
 			sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
-			ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, j.testProperties.Jni_libs...)
+			ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, jniLibs...)
 		}
 	}
 
@@ -1543,23 +1564,23 @@
 	}
 
 	j.Test.generateAndroidBuildActionsWithConfig(ctx, configs)
-	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 	android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
-		InstalledFiles:      j.data,
-		OutputFile:          j.outputFile,
-		TestConfig:          j.testConfig,
-		RequiredModuleNames: j.RequiredModuleNames(ctx),
-		TestSuites:          j.testProperties.Test_suites,
-		IsHost:              true,
-		LocalSdkVersion:     j.sdkVersion.String(),
-		IsUnitTest:          Bool(j.testProperties.Test_options.Unit_test),
+		TestcaseRelDataFiles: testcaseRel(j.data),
+		OutputFile:           j.outputFile,
+		TestConfig:           j.testConfig,
+		RequiredModuleNames:  j.RequiredModuleNames(ctx),
+		TestSuites:           j.testProperties.Test_suites,
+		IsHost:               true,
+		LocalSdkVersion:      j.sdkVersion.String(),
+		IsUnitTest:           Bool(j.testProperties.Test_options.Unit_test),
+		MkInclude:            "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+		MkAppClass:           "JAVA_LIBRARIES",
 	})
 }
 
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	checkMinSdkVersionMts(ctx, j.MinSdkVersion(ctx))
 	j.generateAndroidBuildActionsWithConfig(ctx, nil)
-	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, configs []tradefed.Config) {
@@ -1583,6 +1604,9 @@
 	})
 
 	j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
+	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_common_data)...)
+	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_first_data)...)
+	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_first_prefer32_data)...)
 
 	j.extraTestConfigs = android.PathsForModuleSrc(ctx, j.testProperties.Test_options.Extra_test_configs)
 
@@ -1594,6 +1618,8 @@
 		j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
 	})
 
+	var directImplementationDeps android.Paths
+	var transitiveImplementationDeps []depset.DepSet[android.Path]
 	ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) {
 		sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider)
 		if sharedLibInfo.SharedLibrary != nil {
@@ -1612,11 +1638,20 @@
 				Output: relocatedLib,
 			})
 			j.data = append(j.data, relocatedLib)
+
+			directImplementationDeps = append(directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+			if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+				transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps)
+			}
 		} else {
 			ctx.PropertyErrorf("jni_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
 		}
 	})
 
+	android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{
+		ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps),
+	})
+
 	j.Library.GenerateAndroidBuildActions(ctx)
 }
 
@@ -1800,8 +1835,7 @@
 	// Name of the class containing main to be inserted into the manifest as Main-Class.
 	Main_class *string
 
-	// Names of modules containing JNI libraries that should be installed alongside the host
-	// variant of the binary.
+	// Names of modules containing JNI libraries that should be installed alongside the binary.
 	Jni_libs []string `android:"arch_variant"`
 }
 
@@ -1810,10 +1844,10 @@
 
 	binaryProperties binaryProperties
 
-	isWrapperVariant bool
-
 	wrapperFile android.Path
 	binaryFile  android.InstallPath
+
+	androidMkNamesOfJniLibs []string
 }
 
 func (j *Binary) HostToolPath() android.OptionalPath {
@@ -1823,84 +1857,94 @@
 func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	j.stem = proptools.StringDefault(j.overridableProperties.Stem, ctx.ModuleName())
 
-	if ctx.Arch().ArchType == android.Common {
-		// Compile the jar
-		if j.binaryProperties.Main_class != nil {
-			if j.properties.Manifest != nil {
-				ctx.PropertyErrorf("main_class", "main_class cannot be used when manifest is set")
-			}
-			manifestFile := android.PathForModuleOut(ctx, "manifest.txt")
-			GenerateMainClassManifest(ctx, manifestFile, String(j.binaryProperties.Main_class))
-			j.overrideManifest = android.OptionalPathForPath(manifestFile)
-		}
-
-		j.Library.GenerateAndroidBuildActions(ctx)
+	// Handle the binary wrapper. This comes before compiling the jar so that the wrapper
+	// is the first PackagingSpec
+	if j.binaryProperties.Wrapper != nil {
+		j.wrapperFile = android.PathForModuleSrc(ctx, *j.binaryProperties.Wrapper)
 	} else {
-		// Handle the binary wrapper
-		j.isWrapperVariant = true
-
-		if j.binaryProperties.Wrapper != nil {
-			j.wrapperFile = android.PathForModuleSrc(ctx, *j.binaryProperties.Wrapper)
-		} else {
-			if ctx.Windows() {
-				ctx.PropertyErrorf("wrapper", "wrapper is required for Windows")
-			}
-
-			if ctx.Device() {
-				// device binary should have a main_class property if it does not
-				// have a specific wrapper, so that a default wrapper can
-				// be generated for it.
-				if j.binaryProperties.Main_class == nil {
-					ctx.PropertyErrorf("main_class", "main_class property "+
-						"is required for device binary if no default wrapper is assigned")
-				} else {
-					wrapper := android.PathForModuleOut(ctx, ctx.ModuleName()+".sh")
-					jarName := j.Stem() + ".jar"
-					partition := j.PartitionTag(ctx.DeviceConfig())
-					ctx.Build(pctx, android.BuildParams{
-						Rule:   deviceBinaryWrapper,
-						Output: wrapper,
-						Args: map[string]string{
-							"jar_name":   jarName,
-							"partition":  partition,
-							"main_class": String(j.binaryProperties.Main_class),
-						},
-					})
-					j.wrapperFile = wrapper
-				}
-			} else {
-				j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh")
-			}
-		}
-
-		ext := ""
 		if ctx.Windows() {
-			ext = ".bat"
+			ctx.PropertyErrorf("wrapper", "wrapper is required for Windows")
 		}
 
-		// The host installation rules make the installed wrapper depend on all the dependencies
-		// of the wrapper variant, which will include the common variant's jar file and any JNI
-		// libraries.  This is verified by TestBinary.
-		j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
-			ctx.ModuleName()+ext, j.wrapperFile)
-
-		setOutputFiles(ctx, j.Library.Module)
+		if ctx.Device() {
+			// device binary should have a main_class property if it does not
+			// have a specific wrapper, so that a default wrapper can
+			// be generated for it.
+			if j.binaryProperties.Main_class == nil {
+				ctx.PropertyErrorf("main_class", "main_class property "+
+					"is required for device binary if no default wrapper is assigned")
+			} else {
+				wrapper := android.PathForModuleOut(ctx, ctx.ModuleName()+".sh")
+				jarName := j.Stem() + ".jar"
+				partition := j.PartitionTag(ctx.DeviceConfig())
+				ctx.Build(pctx, android.BuildParams{
+					Rule:   deviceBinaryWrapper,
+					Output: wrapper,
+					Args: map[string]string{
+						"jar_name":   jarName,
+						"partition":  partition,
+						"main_class": String(j.binaryProperties.Main_class),
+					},
+				})
+				j.wrapperFile = wrapper
+			}
+		} else {
+			j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh")
+		}
 	}
+
+	ext := ""
+	if ctx.Windows() {
+		ext = ".bat"
+	}
+
+	// The host installation rules make the installed wrapper depend on all the dependencies
+	// of the wrapper variant, which will include the common variant's jar file and any JNI
+	// libraries.  This is verified by TestBinary. Also make it depend on the jar file so that
+	// the binary file timestamp will update when the jar file timestamp does. The jar file is
+	// built later on, in j.Library.GenerateAndroidBuildActions, so we have to create an identical
+	// installpath representing it here.
+	j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
+		ctx.ModuleName()+ext, j.wrapperFile, j.getJarInstallDir(ctx).Join(ctx, j.Stem()+".jar"))
+
+	// Set the jniLibs of this binary.
+	// These will be added to `LOCAL_REQUIRED_MODULES`, and the kati packaging system will
+	// install these alongside the java binary.
+	ctx.VisitDirectDepsWithTag(jniInstallTag, func(jni android.Module) {
+		// Use the BaseModuleName of the dependency (without any prebuilt_ prefix)
+		bmn, _ := jni.(interface{ BaseModuleName() string })
+		j.androidMkNamesOfJniLibs = append(j.androidMkNamesOfJniLibs, bmn.BaseModuleName()+":"+jni.Target().Arch.ArchType.Bitness())
+	})
+	// Check that native libraries are not listed in `required`. Prompt users to use `jni_libs` instead.
+	ctx.VisitDirectDepsWithTag(android.RequiredDepTag, func(dep android.Module) {
+		if _, hasSharedLibraryInfo := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider); hasSharedLibraryInfo {
+			ctx.ModuleErrorf("cc_library %s is no longer supported in `required` of java_binary modules. Please use jni_libs instead.", dep.Name())
+		}
+	})
+
+	// Compile the jar
+	if j.binaryProperties.Main_class != nil {
+		if j.properties.Manifest != nil {
+			ctx.PropertyErrorf("main_class", "main_class cannot be used when manifest is set")
+		}
+		manifestFile := android.PathForModuleOut(ctx, "manifest.txt")
+		GenerateMainClassManifest(ctx, manifestFile, String(j.binaryProperties.Main_class))
+		j.overrideManifest = android.OptionalPathForPath(manifestFile)
+	}
+
+	j.Library.GenerateAndroidBuildActions(ctx)
 }
 
 func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if ctx.Arch().ArchType == android.Common {
-		j.deps(ctx)
-	}
+	j.deps(ctx)
 	// These dependencies ensure the installation rules will install the jar file when the
-	// wrapper is installed, and the jni libraries on host when the wrapper is installed.
-	if ctx.Arch().ArchType != android.Common && ctx.Os().Class == android.Host {
-		ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
-	}
-	if ctx.Arch().ArchType != android.Common {
-		ctx.AddVariationDependencies(
-			[]blueprint.Variation{{Mutator: "arch", Variation: android.CommonArch.String()}},
-			binaryInstallTag, ctx.ModuleName())
+	// wrapper is installed, and the jni libraries when the wrapper is installed.
+	if ctx.Os().Class == android.Host {
+		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniInstallTag, j.binaryProperties.Jni_libs...)
+	} else if ctx.Os().Class == android.Device {
+		ctx.AddVariationDependencies(ctx.Config().AndroidFirstDeviceTarget.Variations(), jniInstallTag, j.binaryProperties.Jni_libs...)
+	} else {
+		ctx.ModuleErrorf("Unknown os class")
 	}
 }
 
@@ -1920,7 +1964,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
-	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst)
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 
 	return module
@@ -1938,7 +1982,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
-	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommonFirst)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	return module
 }
@@ -2275,14 +2319,17 @@
 		case libTag:
 			if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
 				classPaths = append(classPaths, provider.HeaderJars...)
+				al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.AconfigIntermediateCacheOutputPaths...)
 			}
 		case bootClasspathTag:
 			if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
 				bootclassPaths = append(bootclassPaths, provider.HeaderJars...)
+				al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.AconfigIntermediateCacheOutputPaths...)
 			}
 		case staticLibTag:
 			if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
 				staticLibs = append(staticLibs, provider.HeaderJars...)
+				al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.AconfigIntermediateCacheOutputPaths...)
 			}
 		case systemModulesTag:
 			if sm, ok := android.OtherModuleProvider(ctx, dep, SystemModulesProvider); ok {
@@ -2389,8 +2436,8 @@
 	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(al.stubsJar),
 		LocalHeaderJars:                        android.PathsIfNonNil(al.stubsJar),
-		TransitiveStaticLibsHeaderJars:         android.NewDepSet(android.PREORDER, android.PathsIfNonNil(al.stubsJar), nil),
-		TransitiveStaticLibsImplementationJars: android.NewDepSet(android.PREORDER, android.PathsIfNonNil(al.stubsJar), nil),
+		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, android.PathsIfNonNil(al.stubsJar), nil),
+		TransitiveStaticLibsImplementationJars: depset.New(depset.PREORDER, android.PathsIfNonNil(al.stubsJar), nil),
 		ImplementationAndResourcesJars:         android.PathsIfNonNil(al.stubsJar),
 		ImplementationJars:                     android.PathsIfNonNil(al.stubsJar),
 		AidlIncludeDirs:                        android.Paths{},
@@ -2447,7 +2494,7 @@
 	ret := []string{}
 	ret = append(ret, al.properties.Libs.GetOrDefault(ctx, nil)...)
 	ret = append(ret, al.properties.Static_libs.GetOrDefault(ctx, nil)...)
-	if al.properties.System_modules != nil {
+	if proptools.StringDefault(al.properties.System_modules, "none") != "none" {
 		ret = append(ret, proptools.String(al.properties.System_modules))
 	}
 	// Other non java_library dependencies like java_api_contribution are ignored for now.
@@ -2618,17 +2665,6 @@
 	return nil
 }
 
-func (j *Import) LintDepSets() LintDepSets {
-	return LintDepSets{}
-}
-
-func (j *Import) getStrictUpdatabilityLinting() bool {
-	return false
-}
-
-func (j *Import) setStrictUpdatabilityLinting(bool) {
-}
-
 func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
 	ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs.GetOrDefault(ctx, nil)...)
@@ -2665,11 +2701,11 @@
 
 	var flags javaBuilderFlags
 
-	var transitiveClasspathHeaderJars []*android.DepSet[android.Path]
-	var transitiveBootClasspathHeaderJars []*android.DepSet[android.Path]
-	var transitiveStaticLibsHeaderJars []*android.DepSet[android.Path]
-	var transitiveStaticLibsImplementationJars []*android.DepSet[android.Path]
-	var transitiveStaticLibsResourceJars []*android.DepSet[android.Path]
+	var transitiveClasspathHeaderJars []depset.DepSet[android.Path]
+	var transitiveBootClasspathHeaderJars []depset.DepSet[android.Path]
+	var transitiveStaticLibsHeaderJars []depset.DepSet[android.Path]
+	var transitiveStaticLibsImplementationJars []depset.DepSet[android.Path]
+	var transitiveStaticLibsResourceJars []depset.DepSet[android.Path]
 
 	j.collectTransitiveHeaderJarsForR8(ctx)
 	var staticJars android.Paths
@@ -2682,31 +2718,21 @@
 			case libTag, sdkLibTag:
 				flags.classpath = append(flags.classpath, dep.HeaderJars...)
 				flags.dexClasspath = append(flags.dexClasspath, dep.HeaderJars...)
-				if dep.TransitiveStaticLibsHeaderJars != nil {
-					transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-				}
+				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 			case staticLibTag:
 				flags.classpath = append(flags.classpath, dep.HeaderJars...)
 				staticJars = append(staticJars, dep.ImplementationJars...)
 				staticResourceJars = append(staticResourceJars, dep.ResourceJars...)
 				staticHeaderJars = append(staticHeaderJars, dep.HeaderJars...)
-				if dep.TransitiveStaticLibsHeaderJars != nil {
-					transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-					transitiveStaticLibsHeaderJars = append(transitiveStaticLibsHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-				}
-				if dep.TransitiveStaticLibsImplementationJars != nil {
-					transitiveStaticLibsImplementationJars = append(transitiveStaticLibsImplementationJars, dep.TransitiveStaticLibsImplementationJars)
-				}
-				if dep.TransitiveStaticLibsResourceJars != nil {
-					transitiveStaticLibsResourceJars = append(transitiveStaticLibsResourceJars, dep.TransitiveStaticLibsResourceJars)
-				}
+				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
+				transitiveStaticLibsHeaderJars = append(transitiveStaticLibsHeaderJars, dep.TransitiveStaticLibsHeaderJars)
+				transitiveStaticLibsImplementationJars = append(transitiveStaticLibsImplementationJars, dep.TransitiveStaticLibsImplementationJars)
+				transitiveStaticLibsResourceJars = append(transitiveStaticLibsResourceJars, dep.TransitiveStaticLibsResourceJars)
 			case bootClasspathTag:
 				flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars...)
-				if dep.TransitiveStaticLibsHeaderJars != nil {
-					transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-				}
+				transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 			}
-		} else if _, ok := module.(SdkLibraryDependency); ok {
+		} else if _, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok {
 			switch tag {
 			case libTag, sdkLibTag:
 				sdkInfo, _ := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider)
@@ -2729,9 +2755,9 @@
 		false, j.properties.Exclude_files, j.properties.Exclude_dirs)
 	localStrippedJars := android.Paths{localCombinedHeaderJar}
 
-	completeStaticLibsHeaderJars := android.NewDepSet(android.PREORDER, localStrippedJars, transitiveStaticLibsHeaderJars)
-	completeStaticLibsImplementationJars := android.NewDepSet(android.PREORDER, localStrippedJars, transitiveStaticLibsImplementationJars)
-	completeStaticLibsResourceJars := android.NewDepSet(android.PREORDER, nil, transitiveStaticLibsResourceJars)
+	completeStaticLibsHeaderJars := depset.New(depset.PREORDER, localStrippedJars, transitiveStaticLibsHeaderJars)
+	completeStaticLibsImplementationJars := depset.New(depset.PREORDER, localStrippedJars, transitiveStaticLibsImplementationJars)
+	completeStaticLibsResourceJars := depset.New(depset.PREORDER, nil, transitiveStaticLibsResourceJars)
 
 	// Always pass the input jars to TransformJarsToJar, even if there is only a single jar, we need the output
 	// file of the module to be named jarName.
@@ -2792,8 +2818,8 @@
 
 		// Enabling jetifier requires modifying classes from transitive dependencies, disable transitive
 		// classpath and use the combined header jar instead.
-		completeStaticLibsHeaderJars = android.NewDepSet(android.PREORDER, android.Paths{headerJar}, nil)
-		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, android.Paths{outputFile}, nil)
+		completeStaticLibsHeaderJars = depset.New(depset.PREORDER, android.Paths{headerJar}, nil)
+		completeStaticLibsImplementationJars = depset.New(depset.PREORDER, android.Paths{outputFile}, nil)
 	}
 
 	implementationJarFile := outputFile
@@ -3104,21 +3130,10 @@
 	return nil
 }
 
-func (a *DexImport) LintDepSets() LintDepSets {
-	return LintDepSets{}
-}
-
 func (j *DexImport) IsInstallable() bool {
 	return true
 }
 
-func (j *DexImport) getStrictUpdatabilityLinting() bool {
-	return false
-}
-
-func (j *DexImport) setStrictUpdatabilityLinting(bool) {
-}
-
 func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(j.properties.Jars) != 1 {
 		ctx.PropertyErrorf("jars", "exactly one jar must be provided")
@@ -3273,6 +3288,7 @@
 		&JavaApiLibraryProperties{},
 		&bootclasspathFragmentProperties{},
 		&SourceOnlyBootclasspathProperties{},
+		&ravenwoodTestProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -3321,7 +3337,7 @@
 	depName := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(depModule))
 
 	var sdkLib *string
-	if lib, ok := depModule.(SdkLibraryDependency); ok && lib.sharedLibrary() {
+	if lib, ok := android.OtherModuleProvider(ctx, depModule, SdkLibraryInfoProvider); ok && lib.SharedLibrary {
 		// A shared SDK library. This should be added as a top-level CLC element.
 		sdkLib = &depName
 	} else if lib, ok := depModule.(SdkLibraryComponentDependency); ok && lib.OptionalSdkLibraryImplementation() != nil {
@@ -3358,7 +3374,7 @@
 	if sdkLib != nil {
 		optional := false
 		if module, ok := ctx.Module().(ModuleWithUsesLibrary); ok {
-			if android.InList(*sdkLib, module.UsesLibrary().usesLibraryProperties.Optional_uses_libs) {
+			if android.InList(*sdkLib, module.UsesLibrary().usesLibraryProperties.Optional_uses_libs.GetOrDefault(ctx, nil)) {
 				optional = true
 			}
 		}
diff --git a/java/java_resources.go b/java/java_resources.go
index b0dc5a1..c525233 100644
--- a/java/java_resources.go
+++ b/java/java_resources.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"slices"
 	"strings"
 
 	"github.com/google/blueprint/pathtools"
@@ -99,10 +100,7 @@
 // that should not be treated as resources (including *.java).
 func ResourceFilesToJarArgs(ctx android.ModuleContext,
 	res, exclude []string) (args []string, deps android.Paths) {
-
-	exclude = append([]string(nil), exclude...)
-	exclude = append(exclude, resourceExcludes...)
-	return resourceFilesToJarArgs(ctx, res, exclude)
+	return resourceFilesToJarArgs(ctx, res, slices.Concat(exclude, resourceExcludes))
 }
 
 func resourceFilesToJarArgs(ctx android.ModuleContext,
diff --git a/java/java_test.go b/java/java_test.go
index e0fd0f2..d415679 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -569,8 +569,7 @@
 
 	bar := ctx.ModuleForTests("bar", buildOS+"_common")
 	barJar := bar.Output("bar.jar").Output.String()
-	barWrapper := ctx.ModuleForTests("bar", buildOS+"_x86_64")
-	barWrapperDeps := barWrapper.Output("bar").Implicits.Strings()
+	barWrapperDeps := bar.Output("bar").Implicits.Strings()
 
 	libjni := ctx.ModuleForTests("libjni", buildOS+"_x86_64_shared")
 	libjniSO := libjni.Rule("Cp").Output.String()
@@ -1174,7 +1173,7 @@
 
 				filegroup {
 					name: "core-jar",
-					srcs: [":core{.jar}"],
+					device_common_srcs: [":core{.jar}"],
 				}
 		`),
 	})
@@ -1190,7 +1189,7 @@
 
 				filegroup {
 					name: "core-jar",
-					srcs: [":core{.jar}"],
+					device_common_srcs: [":core{.jar}"],
 				}
 		`),
 	})
@@ -1931,7 +1930,7 @@
 			main_class: "foo.bar.jb",
 		}
 	`)
-	wrapperPath := fmt.Sprint(ctx.ModuleForTests("foo", "android_arm64_armv8-a").AllOutputs())
+	wrapperPath := fmt.Sprint(ctx.ModuleForTests("foo", "android_common").AllOutputs())
 	if !strings.Contains(wrapperPath, "foo.sh") {
 		t.Errorf("wrapper file foo.sh is not generated")
 	}
@@ -2454,37 +2453,6 @@
 	}
 }
 
-func TestJavaExcludeStaticLib(t *testing.T) {
-	ctx, _ := testJava(t, `
-	java_library {
-		name: "bar",
-	}
-	java_library {
-		name: "foo",
-	}
-	java_library {
-		name: "baz",
-		static_libs: [
-			"foo",
-			"bar",
-		],
-		exclude_static_libs: [
-			"bar",
-		],
-	}
-	`)
-
-	// "bar" not included as dependency of "baz"
-	CheckModuleDependencies(t, ctx, "baz", "android_common", []string{
-		`core-lambda-stubs`,
-		`ext`,
-		`foo`,
-		`framework`,
-		`stable-core-platform-api-stubs-system-modules`,
-		`stable.core.platform.api.stubs`,
-	})
-}
-
 func TestJavaLibraryWithResourcesStem(t *testing.T) {
 	ctx, _ := testJavaWithFS(t, `
     java_library {
@@ -3065,6 +3033,44 @@
 		"baz.jar", bazOutputPaths[0].Rel())
 }
 
+func TestCoverage(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		prepareForTestWithFrameworkJacocoInstrumentation,
+		PrepareForTestWithTransitiveClasspathEnabled,
+	).RunTestWithBp(t, `
+		android_app {
+			name: "foo",
+			srcs: ["foo.java"],
+			static_libs: ["android.car"],
+			platform_apis: true,
+		}
+
+		// A library in InstrumentFrameworkModules
+		java_library {
+			name: "android.car",
+			srcs: ["android.car.java"],
+			installable: true,
+		}
+	`)
+
+	foo := result.ModuleForTests("foo", "android_common")
+	androidCar := result.ModuleForTests("android.car", "android_common")
+
+	fooJacoco := foo.Rule("jacoco")
+	fooCombine := foo.Description("for javac")
+
+	androidCarJacoco := androidCar.Rule("jacoco")
+	androidCarJavac := androidCar.Rule("javac")
+
+	android.AssertStringEquals(t, "foo instrumentation rule inputs", fooJacoco.Input.String(), fooCombine.Output.String())
+	android.AssertStringEquals(t, "android.car instrumentation rule inputs", androidCarJacoco.Input.String(), androidCarJavac.Output.String())
+
+	// The input to instrumentation for the `foo` app contains the non-instrumented android.car classes.
+	android.AssertStringListContains(t, "foo combined inputs", fooCombine.Inputs.Strings(), androidCarJavac.Output.String())
+	android.AssertStringListDoesNotContain(t, "foo combined inputs", fooCombine.Inputs.Strings(), androidCarJacoco.Output.String())
+}
+
 func assertTestOnlyAndTopLevel(t *testing.T, ctx *android.TestResult, expectedTestOnly []string, expectedTopLevel []string) {
 	t.Helper()
 	actualTrueModules := []string{}
@@ -3095,3 +3101,30 @@
 		t.Errorf("top-level: Expected but not found: %v, Found but not expected: %v", left, right)
 	}
 }
+
+// Test that a dependency edge is created to the matching variant of a native library listed in `jni_libs` of java_binary
+func TestNativeRequiredDepOfJavaBinary(t *testing.T) {
+	findDepsOfModule := func(ctx *android.TestContext, module android.Module, depName string) []blueprint.Module {
+		var ret []blueprint.Module
+		ctx.VisitDirectDeps(module, func(dep blueprint.Module) {
+			if dep.Name() == depName {
+				ret = append(ret, dep)
+			}
+		})
+		return ret
+	}
+
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+java_binary {
+	name: "myjavabin",
+	main_class: "com.android.MyJava",
+	jni_libs: ["mynativelib"],
+}
+cc_library_shared {
+	name: "mynativelib",
+}
+`
+	res, _ := testJava(t, bp)
+	deps := findDepsOfModule(res, res.ModuleForTests("myjavabin", "android_common").Module(), "mynativelib")
+	android.AssertIntEquals(t, "Create a dep on the first variant", 1, len(deps))
+}
diff --git a/java/jdeps_test.go b/java/jdeps_test.go
index d282f19..1435000 100644
--- a/java/jdeps_test.go
+++ b/java/jdeps_test.go
@@ -109,7 +109,7 @@
 	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
-	android.AssertBoolEquals(t, "IdeInfo.Srcs of repackaged library should be empty", true, len(dpInfo.Srcs) == 0)
+	android.AssertStringEquals(t, "IdeInfo.Srcs of repackaged library should not be empty", "foo.java", dpInfo.Srcs[0])
 	android.AssertStringEquals(t, "IdeInfo.Jar_rules of repackaged library should not be empty", "jarjar_rules.txt", dpInfo.Jarjar_rules[0])
 	if !android.SubstringInList(dpInfo.Jars, "soong/.intermediates/javalib/android_common/jarjar/turbine/javalib.jar") {
 		t.Errorf("IdeInfo.Jars of repackaged library should contain the output of jarjar-ing. All outputs: %v\n", dpInfo.Jars)
@@ -134,3 +134,42 @@
 
 	android.AssertStringListContains(t, "IdeInfo.Deps should contain versioned sdk module", dpInfo.Deps, "sdk_public_29_android")
 }
+
+func TestDoNotAddNoneSystemModulesToDeps(t *testing.T) {
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		android.FixtureMergeEnv(
+			map[string]string{
+				"DISABLE_STUB_VALIDATION": "true",
+			},
+		),
+	).RunTestWithBp(t,
+		`
+		java_library {
+			name: "javalib",
+			srcs: ["foo.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+
+		java_api_library {
+			name: "javalib.stubs",
+			stubs_type: "everything",
+			api_contributions: ["javalib-current.txt"],
+			api_surface: "public",
+			system_modules: "none",
+		}
+		java_api_contribution {
+			name: "javalib-current.txt",
+			api_file: "javalib-current.txt",
+			api_surface: "public",
+		}
+	`)
+	javalib := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	dpInfo, _ := android.OtherModuleProvider(ctx, javalib, android.IdeInfoProviderKey)
+	android.AssertStringListDoesNotContain(t, "IdeInfo.Deps should contain not contain `none`", dpInfo.Deps, "none")
+
+	javalib_stubs := ctx.ModuleForTests("javalib.stubs", "android_common").Module().(*ApiLibrary)
+	dpInfo, _ = android.OtherModuleProvider(ctx, javalib_stubs, android.IdeInfoProviderKey)
+	android.AssertStringListDoesNotContain(t, "IdeInfo.Deps should contain not contain `none`", dpInfo.Deps, "none")
+}
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index f6e7fca..45eac01 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -500,8 +500,8 @@
 			name: "androidx.compose.runtime_runtime",
 		}
 
-		java_library_host {
-			name: "androidx.compose.compiler_compiler-hosted",
+		kotlin_plugin {
+			name: "androidx.compose.compiler_compiler-hosted-plugin",
 		}
 
 		java_library {
@@ -523,7 +523,7 @@
 
 	buildOS := result.Config.BuildOS.String()
 
-	composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted", buildOS+"_common").Rule("combineJar").Output
+	composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted-plugin", buildOS+"_common").Rule("combineJar").Output
 	withCompose := result.ModuleForTests("withcompose", "android_common")
 	noCompose := result.ModuleForTests("nocompose", "android_common")
 
@@ -542,3 +542,50 @@
 	android.AssertStringDoesNotContain(t, "unexpected compose compiler plugin",
 		noCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String())
 }
+
+func TestKotlinPlugin(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		kotlin_plugin {
+			name: "kotlin_plugin",
+		}
+
+		java_library {
+			name: "with_kotlin_plugin",
+			srcs: ["a.kt"],
+			plugins: ["plugin"],
+			kotlin_plugins: ["kotlin_plugin"],
+		}
+
+		java_library {
+			name: "no_kotlin_plugin",
+			srcs: ["a.kt"],
+		}
+
+		java_plugin {
+			name: "plugin",
+		}
+	`)
+
+	buildOS := result.Config.BuildOS.String()
+
+	kotlinPlugin := result.ModuleForTests("kotlin_plugin", buildOS+"_common").Rule("combineJar").Output
+	withKotlinPlugin := result.ModuleForTests("with_kotlin_plugin", "android_common")
+	noKotlinPlugin := result.ModuleForTests("no_kotlin_plugin", "android_common")
+
+	android.AssertStringListContains(t, "missing plugin compiler dependency",
+		withKotlinPlugin.Rule("kotlinc").Implicits.Strings(), kotlinPlugin.String())
+
+	android.AssertStringDoesContain(t, "missing kotlin plugin",
+		withKotlinPlugin.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+kotlinPlugin.String())
+
+	android.AssertStringListContains(t, "missing kapt kotlin plugin dependency",
+		withKotlinPlugin.Rule("kapt").Implicits.Strings(), kotlinPlugin.String())
+
+	android.AssertStringListDoesNotContain(t, "unexpected kotlin plugin dependency",
+		noKotlinPlugin.Rule("kotlinc").Implicits.Strings(), kotlinPlugin.String())
+
+	android.AssertStringDoesNotContain(t, "unexpected kotlin plugin",
+		noKotlinPlugin.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+kotlinPlugin.String())
+}
diff --git a/java/lint.go b/java/lint.go
index 6782adc..ac90e19 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -19,6 +19,8 @@
 	"sort"
 	"strings"
 
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -90,7 +92,6 @@
 	compileSdkKind          android.SdkKind
 	javaLanguageLevel       string
 	kotlinLanguageLevel     string
-	outputs                 lintOutputs
 	properties              LintProperties
 	extraMainlineLintErrors []string
 	compile_data            android.Paths
@@ -100,68 +101,47 @@
 	buildModuleReportZip bool
 }
 
-type lintOutputs struct {
-	html              android.Path
-	text              android.Path
-	xml               android.Path
-	referenceBaseline android.Path
-
-	depSets LintDepSets
-}
-
-type lintOutputsIntf interface {
-	lintOutputs() *lintOutputs
-}
-
-type LintDepSetsIntf interface {
-	LintDepSets() LintDepSets
-
-	// Methods used to propagate strict_updatability_linting values.
-	GetStrictUpdatabilityLinting() bool
-	SetStrictUpdatabilityLinting(bool)
-}
-
 type LintDepSets struct {
-	HTML, Text, XML *android.DepSet[android.Path]
+	HTML, Text, XML, Baseline depset.DepSet[android.Path]
 }
 
 type LintDepSetsBuilder struct {
-	HTML, Text, XML *android.DepSetBuilder[android.Path]
+	HTML, Text, XML, Baseline *depset.Builder[android.Path]
 }
 
 func NewLintDepSetBuilder() LintDepSetsBuilder {
 	return LintDepSetsBuilder{
-		HTML: android.NewDepSetBuilder[android.Path](android.POSTORDER),
-		Text: android.NewDepSetBuilder[android.Path](android.POSTORDER),
-		XML:  android.NewDepSetBuilder[android.Path](android.POSTORDER),
+		HTML:     depset.NewBuilder[android.Path](depset.POSTORDER),
+		Text:     depset.NewBuilder[android.Path](depset.POSTORDER),
+		XML:      depset.NewBuilder[android.Path](depset.POSTORDER),
+		Baseline: depset.NewBuilder[android.Path](depset.POSTORDER),
 	}
 }
 
-func (l LintDepSetsBuilder) Direct(html, text, xml android.Path) LintDepSetsBuilder {
+func (l LintDepSetsBuilder) Direct(html, text, xml android.Path, baseline android.OptionalPath) LintDepSetsBuilder {
 	l.HTML.Direct(html)
 	l.Text.Direct(text)
 	l.XML.Direct(xml)
+	if baseline.Valid() {
+		l.Baseline.Direct(baseline.Path())
+	}
 	return l
 }
 
-func (l LintDepSetsBuilder) Transitive(depSets LintDepSets) LintDepSetsBuilder {
-	if depSets.HTML != nil {
-		l.HTML.Transitive(depSets.HTML)
-	}
-	if depSets.Text != nil {
-		l.Text.Transitive(depSets.Text)
-	}
-	if depSets.XML != nil {
-		l.XML.Transitive(depSets.XML)
-	}
+func (l LintDepSetsBuilder) Transitive(info *LintInfo) LintDepSetsBuilder {
+	l.HTML.Transitive(info.TransitiveHTML)
+	l.Text.Transitive(info.TransitiveText)
+	l.XML.Transitive(info.TransitiveXML)
+	l.Baseline.Transitive(info.TransitiveBaseline)
 	return l
 }
 
 func (l LintDepSetsBuilder) Build() LintDepSets {
 	return LintDepSets{
-		HTML: l.HTML.Build(),
-		Text: l.Text.Build(),
-		XML:  l.XML.Build(),
+		HTML:     l.HTML.Build(),
+		Text:     l.Text.Build(),
+		XML:      l.XML.Build(),
+		Baseline: l.Baseline.Build(),
 	}
 }
 
@@ -209,24 +189,18 @@
 	},
 }
 
-func (l *linter) LintDepSets() LintDepSets {
-	return l.outputs.depSets
-}
+var LintProvider = blueprint.NewProvider[*LintInfo]()
 
-func (l *linter) GetStrictUpdatabilityLinting() bool {
-	return BoolDefault(l.properties.Lint.Strict_updatability_linting, false)
-}
+type LintInfo struct {
+	HTML              android.Path
+	Text              android.Path
+	XML               android.Path
+	ReferenceBaseline android.Path
 
-func (l *linter) SetStrictUpdatabilityLinting(strictLinting bool) {
-	l.properties.Lint.Strict_updatability_linting = &strictLinting
-}
-
-var _ LintDepSetsIntf = (*linter)(nil)
-
-var _ lintOutputsIntf = (*linter)(nil)
-
-func (l *linter) lintOutputs() *lintOutputs {
-	return &l.outputs
+	TransitiveHTML     depset.DepSet[android.Path]
+	TransitiveText     depset.DepSet[android.Path]
+	TransitiveXML      depset.DepSet[android.Path]
+	TransitiveBaseline depset.DepSet[android.Path]
 }
 
 func (l *linter) enabled() bool {
@@ -262,7 +236,9 @@
 	return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
 }
 
-func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path) lintPaths {
+func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path,
+	baselines android.Paths) lintPaths {
+
 	projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
 	// Lint looks for a lint.xml file next to the project.xml file, give it one.
 	configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
@@ -325,12 +301,10 @@
 	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
 	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
 
-	if l.GetStrictUpdatabilityLinting() {
+	if Bool(l.properties.Lint.Strict_updatability_linting) && len(baselines) > 0 {
 		// Verify the module does not baseline issues that endanger safe updatability.
-		if l.properties.Lint.Baseline_filename != nil {
-			cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename))
-			cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
-		}
+		strictUpdatabilityChecksOutputFile := VerifyStrictUpdatabilityChecks(ctx, baselines)
+		cmd.Validation(strictUpdatabilityChecksOutputFile)
 	}
 
 	return lintPaths{
@@ -342,6 +316,22 @@
 
 }
 
+func VerifyStrictUpdatabilityChecks(ctx android.ModuleContext, baselines android.Paths) android.Path {
+	rule := android.NewRuleBuilder(pctx, ctx)
+	baselineRspFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check_baselines.rsp")
+	outputFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check.stamp")
+	rule.Command().Text("rm -f").Output(outputFile)
+	rule.Command().
+		BuiltTool("lint_strict_updatability_checks").
+		FlagWithArg("--name ", ctx.ModuleName()).
+		FlagWithRspFileInputList("--baselines ", baselineRspFile, baselines).
+		FlagForEachArg("--disallowed_issues ", updatabilityChecks)
+	rule.Command().Text("touch").Output(outputFile)
+	rule.Build("lint_strict_updatability_checks", "lint strict updatability checks")
+
+	return outputFile
+}
+
 // generateManifest adds a command to the rule to write a simple manifest that contains the
 // minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
 func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.WritablePath {
@@ -411,6 +401,26 @@
 	l.extraLintCheckJars = append(l.extraLintCheckJars, android.PathForSource(ctx,
 		"prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar"))
 
+	var baseline android.OptionalPath
+	if l.properties.Lint.Baseline_filename != nil {
+		baseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename))
+	}
+
+	html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
+	text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
+	xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
+	referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
+
+	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml, baseline)
+
+	ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
+		if info, ok := android.OtherModuleProvider(ctx, dep, LintProvider); ok {
+			depSetsBuilder.Transitive(info)
+		}
+	})
+
+	depSets := depSetsBuilder.Build()
+
 	rule := android.NewRuleBuilder(pctx, ctx).
 		Sbox(android.PathForModuleOut(ctx, "lint"),
 			android.PathForModuleOut(ctx, "lint.sbox.textproto")).
@@ -437,20 +447,9 @@
 	srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp")
 	rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList).Implicits(l.compile_data)
 
-	lintPaths := l.writeLintProjectXML(ctx, rule, srcsList)
+	baselines := depSets.Baseline.ToList()
 
-	html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
-	text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
-	xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
-	referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
-
-	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
-
-	ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
-		if depLint, ok := dep.(LintDepSetsIntf); ok {
-			depSetsBuilder.Transitive(depLint.LintDepSets())
-		}
-	})
+	lintPaths := l.writeLintProjectXML(ctx, rule, srcsList, baselines)
 
 	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
@@ -505,8 +504,8 @@
 		cmd.FlagWithArg("--check ", checkOnly)
 	}
 
-	if l.properties.Lint.Baseline_filename != nil {
-		cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename))
+	if baseline.Valid() {
+		cmd.FlagWithInput("--baseline ", baseline.Path())
 	}
 
 	cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline)
@@ -530,25 +529,30 @@
 
 	rule.Build("lint", "lint")
 
-	l.outputs = lintOutputs{
-		html:              html,
-		text:              text,
-		xml:               xml,
-		referenceBaseline: referenceBaseline,
+	android.SetProvider(ctx, LintProvider, &LintInfo{
+		HTML:              html,
+		Text:              text,
+		XML:               xml,
+		ReferenceBaseline: referenceBaseline,
 
-		depSets: depSetsBuilder.Build(),
-	}
+		TransitiveHTML:     depSets.HTML,
+		TransitiveText:     depSets.Text,
+		TransitiveXML:      depSets.XML,
+		TransitiveBaseline: depSets.Baseline,
+	})
 
 	if l.buildModuleReportZip {
-		l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
+		l.reports = BuildModuleLintReportZips(ctx, depSets, nil)
 	}
 
 	// Create a per-module phony target to run the lint check.
 	phonyName := ctx.ModuleName() + "-lint"
 	ctx.Phony(phonyName, xml)
+
+	ctx.SetOutputFiles(android.Paths{xml}, ".lint")
 }
 
-func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
+func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets, validations android.Paths) android.Paths {
 	htmlList := android.SortedUniquePaths(depSets.HTML.ToList())
 	textList := android.SortedUniquePaths(depSets.Text.ToList())
 	xmlList := android.SortedUniquePaths(depSets.XML.ToList())
@@ -558,13 +562,13 @@
 	}
 
 	htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
-	lintZip(ctx, htmlList, htmlZip)
+	lintZip(ctx, htmlList, htmlZip, validations)
 
 	textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
-	lintZip(ctx, textList, textZip)
+	lintZip(ctx, textList, textZip, validations)
 
 	xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
-	lintZip(ctx, xmlList, xmlZip)
+	lintZip(ctx, xmlList, xmlZip, validations)
 
 	return android.Paths{htmlZip, textZip, xmlZip}
 }
@@ -642,7 +646,7 @@
 		return
 	}
 
-	var outputs []*lintOutputs
+	var outputs []*LintInfo
 	var dirs []string
 	ctx.VisitAllModules(func(m android.Module) {
 		if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
@@ -658,14 +662,14 @@
 			}
 		}
 
-		if l, ok := m.(lintOutputsIntf); ok {
-			outputs = append(outputs, l.lintOutputs())
+		if lintInfo, ok := android.OtherModuleProvider(ctx, m, LintProvider); ok {
+			outputs = append(outputs, lintInfo)
 		}
 	})
 
 	dirs = android.SortedUniqueStrings(dirs)
 
-	zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
+	zip := func(outputPath android.WritablePath, get func(*LintInfo) android.Path) {
 		var paths android.Paths
 
 		for _, output := range outputs {
@@ -674,20 +678,20 @@
 			}
 		}
 
-		lintZip(ctx, paths, outputPath)
+		lintZip(ctx, paths, outputPath, nil)
 	}
 
 	l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
-	zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
+	zip(l.htmlZip, func(l *LintInfo) android.Path { return l.HTML })
 
 	l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
-	zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
+	zip(l.textZip, func(l *LintInfo) android.Path { return l.Text })
 
 	l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
-	zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
+	zip(l.xmlZip, func(l *LintInfo) android.Path { return l.XML })
 
 	l.referenceBaselineZip = android.PathForOutput(ctx, "lint-report-reference-baselines.zip")
-	zip(l.referenceBaselineZip, func(l *lintOutputs) android.Path { return l.referenceBaseline })
+	zip(l.referenceBaselineZip, func(l *LintInfo) android.Path { return l.ReferenceBaseline })
 
 	ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
 }
@@ -703,17 +707,9 @@
 func init() {
 	android.RegisterParallelSingletonType("lint",
 		func() android.Singleton { return &lintSingleton{} })
-
-	registerLintBuildComponents(android.InitRegistrationContext)
 }
 
-func registerLintBuildComponents(ctx android.RegistrationContext) {
-	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("enforce_strict_updatability_linting", enforceStrictUpdatabilityLintingMutator).Parallel()
-	})
-}
-
-func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
+func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath, validations android.Paths) {
 	paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
 
 	sort.Slice(paths, func(i, j int) bool {
@@ -725,19 +721,8 @@
 	rule.Command().BuiltTool("soong_zip").
 		FlagWithOutput("-o ", outputPath).
 		FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
-		FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths)
+		FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths).
+		Validations(validations)
 
 	rule.Build(outputPath.Base(), outputPath.Base())
 }
-
-// Enforce the strict updatability linting to all applicable transitive dependencies.
-func enforceStrictUpdatabilityLintingMutator(ctx android.TopDownMutatorContext) {
-	m := ctx.Module()
-	if d, ok := m.(LintDepSetsIntf); ok && d.GetStrictUpdatabilityLinting() {
-		ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) {
-			if a, ok := d.(LintDepSetsIntf); ok {
-				a.SetStrictUpdatabilityLinting(true)
-			}
-		})
-	}
-}
diff --git a/java/lint_test.go b/java/lint_test.go
index b51753f..afe3914 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -164,7 +164,7 @@
 			sdk_version: "current",
 			lint: {
 				strict_updatability_linting: true,
-				baseline_filename: "lint-baseline.xml",
+				baseline_filename: "foo_lint_baseline.xml",
 			},
 		}
 
@@ -176,7 +176,7 @@
 			min_sdk_version: "29",
 			sdk_version: "current",
 			lint: {
-				baseline_filename: "lint-baseline.xml",
+				baseline_filename: "bar_lint_baseline.xml",
 			}
 		}
 	`
@@ -188,18 +188,13 @@
 		RunTestWithBp(t, bp)
 
 	foo := result.ModuleForTests("foo", "android_common")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, foo.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command,
-		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
+	strictUpdatabilityCheck := foo.Output("lint_strict_updatability_check.stamp")
+	if !strings.Contains(strictUpdatabilityCheck.RuleParams.Command,
+		"--disallowed_issues NewApi") {
 		t.Error("did not restrict baselining NewApi")
 	}
-
-	bar := result.ModuleForTests("bar", "android_common")
-	sboxProto = android.RuleBuilderSboxProtoForTests(t, result.TestContext, bar.Output("lint.sbox.textproto"))
-	if !strings.Contains(*sboxProto.Commands[0].Command,
-		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
-		t.Error("did not restrict baselining NewApi")
-	}
+	android.AssertStringListContains(t, "strict updatability check baseline inputs", strictUpdatabilityCheck.Inputs.Strings(), "foo_lint_baseline.xml")
+	android.AssertStringListContains(t, "strict updatability check baseline inputs", strictUpdatabilityCheck.Inputs.Strings(), "bar_lint_baseline.xml")
 }
 
 func TestJavaLintDatabaseSelectionFull(t *testing.T) {
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index d794e51..d09a02e 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -24,7 +24,7 @@
 }
 
 func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterParallelSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory)
+	ctx.RegisterModuleType("platform_bootclasspath", platformBootclasspathFactory)
 }
 
 // The tags used for the dependencies between the platform bootclasspath and any configured boot
@@ -33,10 +33,11 @@
 	platformBootclasspathArtBootJarDepTag  = bootclasspathDependencyTag{name: "art-boot-jar"}
 	platformBootclasspathBootJarDepTag     = bootclasspathDependencyTag{name: "platform-boot-jar"}
 	platformBootclasspathApexBootJarDepTag = bootclasspathDependencyTag{name: "apex-boot-jar"}
+	platformBootclasspathImplLibDepTag     = dependencyTag{name: "impl-lib-tag"}
 )
 
 type platformBootclasspathModule struct {
-	android.SingletonModuleBase
+	android.ModuleBase
 	ClasspathFragmentBase
 
 	properties platformBootclasspathProperties
@@ -63,7 +64,7 @@
 	HiddenAPIFlagFileProperties
 }
 
-func platformBootclasspathFactory() android.SingletonModule {
+func platformBootclasspathFactory() android.Module {
 	m := &platformBootclasspathModule{}
 	m.AddProperties(&m.properties)
 	initClasspathFragment(m, BOOTCLASSPATH)
@@ -111,12 +112,18 @@
 	// Add dependencies on all the ART jars.
 	global := dexpreopt.GetGlobalConfig(ctx)
 	addDependenciesOntoSelectedBootImageApexes(ctx, "com.android.art")
+
+	var bootImageModuleNames []string
+
 	// TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly
 	addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, platformBootclasspathArtBootJarDepTag)
+	bootImageModuleNames = append(bootImageModuleNames, global.ArtApexJars.CopyOfJars()...)
 
 	// Add dependencies on all the non-updatable jars, which are on the platform or in non-updatable
 	// APEXes.
-	addDependenciesOntoBootImageModules(ctx, b.platformJars(ctx), platformBootclasspathBootJarDepTag)
+	platformJars := b.platformJars(ctx)
+	addDependenciesOntoBootImageModules(ctx, platformJars, platformBootclasspathBootJarDepTag)
+	bootImageModuleNames = append(bootImageModuleNames, platformJars.CopyOfJars()...)
 
 	// Add dependencies on all the updatable jars, except the ART jars.
 	apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
@@ -127,9 +134,17 @@
 	addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...)
 	// TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly
 	addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag)
+	bootImageModuleNames = append(bootImageModuleNames, apexJars.CopyOfJars()...)
 
 	// Add dependencies on all the fragments.
 	b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx)
+
+	for _, bootImageModuleName := range bootImageModuleNames {
+		implLibName := implLibraryModuleName(bootImageModuleName)
+		if ctx.OtherModuleExists(implLibName) {
+			ctx.AddFarVariationDependencies(nil, platformBootclasspathImplLibDepTag, implLibName)
+		}
+	}
 }
 
 func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList, tag bootclasspathDependencyTag) {
@@ -141,18 +156,6 @@
 	}
 }
 
-// GenerateSingletonBuildActions does nothing and must never do anything.
-//
-// This module only implements android.SingletonModule so that it can implement
-// android.SingletonMakeVarsProvider.
-func (b *platformBootclasspathModule) GenerateSingletonBuildActions(android.SingletonContext) {
-	// Keep empty
-}
-
-func (d *platformBootclasspathModule) MakeVars(ctx android.MakeVarsContext) {
-	d.generateHiddenApiMakeVars(ctx)
-}
-
 func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Gather all the dependencies from the art, platform, and apex boot jars.
 	artModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathArtBootJarDepTag)
@@ -166,17 +169,22 @@
 	allModules = append(allModules, apexModules...)
 	b.configuredModules = allModules
 
+	// Do not add implLibModule to allModules as the impl lib is only used to collect the
+	// transitive source files
+	var implLibModule []android.Module
+	ctx.VisitDirectDepsWithTag(implLibraryTag, func(m android.Module) {
+		implLibModule = append(implLibModule, m)
+	})
+
 	var transitiveSrcFiles android.Paths
-	for _, module := range allModules {
+	for _, module := range append(allModules, implLibModule...) {
 		if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
-			if depInfo.TransitiveSrcFiles != nil {
-				transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...)
-			}
+			transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...)
 		}
 	}
 	jarArgs := resourcePathsToJarArgs(transitiveSrcFiles)
 	jarArgs = append(jarArgs, "-srcjar") // Move srcfiles to the right package
-	srcjar := android.PathForModuleOut(ctx, ctx.ModuleName()+"-transitive.srcjar").OutputPath
+	srcjar := android.PathForModuleOut(ctx, ctx.ModuleName()+"-transitive.srcjar")
 	TransformResourcesToJar(ctx, srcjar, jarArgs, transitiveSrcFiles)
 
 	// Gather all the fragments dependencies.
@@ -408,13 +416,3 @@
 
 	rule.Build(desc, desc)
 }
-
-// generateHiddenApiMakeVars generates make variables needed by hidden API related make rules, e.g.
-// veridex and run-appcompat.
-func (b *platformBootclasspathModule) generateHiddenApiMakeVars(ctx android.MakeVarsContext) {
-	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
-		return
-	}
-	// INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/.
-	ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", b.hiddenAPIFlagsCSV.String())
-}
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 0d2acae..7fa6ddb 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -298,10 +298,10 @@
 	platformBootclasspath := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
 	entries := android.AndroidMkEntriesForTest(t, result.TestContext, platformBootclasspath)
 	goals := entries[0].GetDistForGoals(platformBootclasspath)
-	android.AssertStringEquals(t, "platform dist goals phony", ".PHONY: droidcore\n", goals[0])
+	android.AssertStringEquals(t, "platform dist goals phony", ".PHONY: droidcore", goals[0])
 	android.AssertStringDoesContain(t, "platform dist goals meta check", goals[1], "$(if $(strip $(ALL_TARGETS.")
 	android.AssertStringDoesContain(t, "platform dist goals meta assign", goals[1], "),,$(eval ALL_TARGETS.")
-	android.AssertStringEquals(t, "platform dist goals call", "$(call dist-for-goals,droidcore,out/soong/hiddenapi/hiddenapi-flags.csv:hiddenapi-flags.csv)\n", android.StringRelativeToTop(result.Config, goals[2]))
+	android.AssertStringEquals(t, "platform dist goals call", "$(call dist-for-goals,droidcore,out/soong/hiddenapi/hiddenapi-flags.csv:hiddenapi-flags.csv)", android.StringRelativeToTop(result.Config, goals[2]))
 }
 
 func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) {
diff --git a/java/plugin.go b/java/plugin.go
index 9c4774a..610c9fd 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -24,6 +24,7 @@
 
 func registerJavaPluginBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("java_plugin", PluginFactory)
+	ctx.RegisterModuleType("kotlin_plugin", KotlinPluginFactory)
 }
 
 func PluginFactory() android.Module {
@@ -37,6 +38,16 @@
 	return module
 }
 
+func KotlinPluginFactory() android.Module {
+	module := &KotlinPlugin{}
+
+	module.addHostProperties()
+
+	InitJavaModule(module, android.HostSupported)
+
+	return module
+}
+
 // Plugin describes a java_plugin module, a host java library that will be used by javac as an annotation processor.
 type Plugin struct {
 	Library
@@ -53,3 +64,8 @@
 	// parallelism and cause more recompilation for modules that depend on modules that use this plugin.
 	Generates_api *bool
 }
+
+// Plugin describes a kotlin_plugin module, a host java/kotlin library that will be used by kotlinc as a compiler plugin.
+type KotlinPlugin struct {
+	Library
+}
diff --git a/java/ravenwood.go b/java/ravenwood.go
index 9239bbd..4c43a9f 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -14,6 +14,8 @@
 package java
 
 import (
+	"strconv"
+
 	"android/soong/android"
 	"android/soong/tradefed"
 
@@ -36,6 +38,14 @@
 var ravenwoodTestResourceApkTag = dependencyTag{name: "ravenwoodtestresapk"}
 var ravenwoodTestInstResourceApkTag = dependencyTag{name: "ravenwoodtest-inst-res-apk"}
 
+var genManifestProperties = pctx.AndroidStaticRule("genManifestProperties",
+	blueprint.RuleParams{
+		Command: "echo targetSdkVersionInt=$targetSdkVersionInt > $out && " +
+			"echo targetSdkVersionRaw=$targetSdkVersionRaw >> $out && " +
+			"echo packageName=$packageName >> $out && " +
+			"echo instPackageName=$instPackageName >> $out",
+	}, "targetSdkVersionInt", "targetSdkVersionRaw", "packageName", "instPackageName")
+
 const ravenwoodUtilsName = "ravenwood-utils"
 const ravenwoodRuntimeName = "ravenwood-runtime"
 
@@ -54,7 +64,7 @@
 }
 
 type ravenwoodTestProperties struct {
-	Jni_libs []string
+	Jni_libs proptools.Configurable[[]string]
 
 	// Specify another android_app module here to copy it to the test directory, so that
 	// the ravenwood test can access it. This APK will be loaded as resources of the test
@@ -68,6 +78,17 @@
 	// the ravenwood test can access it. This APK will be loaded as resources of the test
 	// instrumentation app itself.
 	Inst_resource_apk *string
+
+	// Specify the package name of the test target apk.
+	// This will be set to the target Context's package name.
+	// (i.e. Instrumentation.getTargetContext().getPackageName())
+	// If this is omitted, Package_name will be used.
+	Package_name *string
+
+	// Specify the package name of this test module.
+	// This will be set to the test Context's package name.
+	//(i.e. Instrumentation.getContext().getPackageName())
+	Inst_package_name *string
 }
 
 type ravenwoodTest struct {
@@ -126,7 +147,7 @@
 	}
 
 	// Add jni libs
-	for _, lib := range r.ravenwoodTestProperties.Jni_libs {
+	for _, lib := range r.ravenwoodTestProperties.Jni_libs.GetOrDefault(ctx, nil) {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
 	}
 
@@ -216,6 +237,27 @@
 	copyResApk(ravenwoodTestResourceApkTag, "ravenwood-res.apk")
 	copyResApk(ravenwoodTestInstResourceApkTag, "ravenwood-inst-res.apk")
 
+	// Generate manifest properties
+	propertiesOutputPath := android.PathForModuleGen(ctx, "ravenwood.properties")
+
+	targetSdkVersion := proptools.StringDefault(r.deviceProperties.Target_sdk_version, "")
+	targetSdkVersionInt := r.TargetSdkVersion(ctx).FinalOrFutureInt() // FinalOrFutureInt may be 10000.
+	packageName := proptools.StringDefault(r.ravenwoodTestProperties.Package_name, "")
+	instPackageName := proptools.StringDefault(r.ravenwoodTestProperties.Inst_package_name, "")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        genManifestProperties,
+		Description: "genManifestProperties",
+		Output:      propertiesOutputPath,
+		Args: map[string]string{
+			"targetSdkVersionInt": strconv.Itoa(targetSdkVersionInt),
+			"targetSdkVersionRaw": targetSdkVersion,
+			"packageName":         packageName,
+			"instPackageName":     instPackageName,
+		},
+	})
+	installProps := ctx.InstallFile(installPath, "ravenwood.properties", propertiesOutputPath)
+	installDeps = append(installDeps, installProps)
+
 	// Install our JAR with all dependencies
 	ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
 }
@@ -238,7 +280,7 @@
 type ravenwoodLibgroupProperties struct {
 	Libs []string
 
-	Jni_libs []string
+	Jni_libs proptools.Configurable[[]string]
 
 	// We use this to copy framework-res.apk to the ravenwood runtime directory.
 	Data []string `android:"path,arch_variant"`
@@ -280,7 +322,7 @@
 	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
 		ctx.AddVariationDependencies(nil, ravenwoodLibContentTag, lib)
 	}
-	for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs {
+	for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs.GetOrDefault(ctx, nil) {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
 	}
 }
diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go
index 753a118..6394a9a 100644
--- a/java/ravenwood_test.go
+++ b/java/ravenwood_test.go
@@ -162,17 +162,27 @@
 			srcs: ["jni.cpp"],
 			stem: "libpink",
 		}
+		java_defaults {
+			name: "ravenwood-test-defaults",
+			jni_libs: ["jni-lib2"],
+		}
 		android_ravenwood_test {
 			name: "ravenwood-test",
 			srcs: ["Test.java"],
+			defaults: ["ravenwood-test-defaults"],
 			jni_libs: [
 				"jni-lib1",
-				"jni-lib2",
 				"ravenwood-runtime-jni2",
 			],
 			resource_apk: "app2",
 			inst_resource_apk: "app3",
 			sdk_version: "test_current",
+			target_sdk_version: "34",
+			package_name: "a.b.c",
+			inst_package_name: "x.y.z",
+		}
+		android_ravenwood_test {
+			name: "ravenwood-test-empty",
 		}
 	`)
 
@@ -195,12 +205,16 @@
 	// Verify that we've emitted test artifacts in expected location
 	outputJar := module.Output(installPathPrefix + "/ravenwood-test/ravenwood-test.jar")
 	module.Output(installPathPrefix + "/ravenwood-test/ravenwood-test.config")
+	module.Output(installPathPrefix + "/ravenwood-test/ravenwood.properties")
 	module.Output(installPathPrefix + "/ravenwood-test/lib64/jni-lib1.so")
 	module.Output(installPathPrefix + "/ravenwood-test/lib64/libblue.so")
 	module.Output(installPathPrefix + "/ravenwood-test/lib64/libpink.so")
 	module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-res.apk")
 	module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-inst-res.apk")
 
+	module = ctx.ModuleForTests("ravenwood-test-empty", "android_common")
+	module.Output(installPathPrefix + "/ravenwood-test-empty/ravenwood.properties")
+
 	// ravenwood-runtime*.so are included in the runtime, so it shouldn't be emitted.
 	for _, o := range module.AllOutputs() {
 		android.AssertStringDoesNotContain(t, "runtime libs shouldn't be included", o, "/ravenwood-test/lib64/ravenwood-runtime")
diff --git a/java/robolectric.go b/java/robolectric.go
index 374fc5f..5f46267 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -19,7 +19,6 @@
 
 	"android/soong/android"
 	"android/soong/java/config"
-	"android/soong/testing"
 	"android/soong/tradefed"
 
 	"github.com/google/blueprint/proptools"
@@ -76,7 +75,7 @@
 	// Use strict mode to limit access of Robolectric API directly. See go/roboStrictMode
 	Strict_mode *bool
 
-	Jni_libs []string
+	Jni_libs proptools.Configurable[[]string]
 }
 
 type robolectricTest struct {
@@ -131,7 +130,7 @@
 	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
 		roboRuntimesTag, "robolectric-android-all-prebuilts")
 
-	for _, lib := range r.robolectricProperties.Jni_libs {
+	for _, lib := range r.robolectricProperties.Jni_libs.GetOrDefault(ctx, nil) {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
 	}
 }
@@ -149,6 +148,9 @@
 		HostTemplate:           "${RobolectricTestConfigTemplate}",
 	})
 	r.data = android.PathsForModuleSrc(ctx, r.testProperties.Data)
+	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_common_data)...)
+	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_first_data)...)
+	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_first_prefer32_data)...)
 
 	var ok bool
 	var instrumentedApp *AndroidApp
@@ -208,6 +210,11 @@
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	var installDeps android.InstallPaths
 
+	for _, data := range r.data {
+		installedData := ctx.InstallFile(installPath, data.Rel(), data)
+		installDeps = append(installDeps, installedData)
+	}
+
 	if manifest != nil {
 		r.data = append(r.data, manifest)
 		installedManifest := ctx.InstallFile(installPath, ctx.ModuleName()+"-AndroidManifest.xml", manifest)
@@ -228,11 +235,6 @@
 	installedConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig)
 	installDeps = append(installDeps, installedConfig)
 
-	for _, data := range android.PathsForModuleSrc(ctx, r.testProperties.Data) {
-		installedData := ctx.InstallFile(installPath, data.Rel(), data)
-		installDeps = append(installDeps, installedData)
-	}
-
 	soInstallPath := installPath.Join(ctx, getLibPath(r.forceArchType))
 	for _, jniLib := range collectTransitiveJniDeps(ctx) {
 		installJni := ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
@@ -240,7 +242,6 @@
 	}
 
 	r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
-	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile android.ModuleOutPath) {
diff --git a/java/rro.go b/java/rro.go
index 8bb9be2..ab4fafa 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -20,6 +20,7 @@
 import (
 	"android/soong/android"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -29,6 +30,7 @@
 
 func RegisterRuntimeResourceOverlayBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("runtime_resource_overlay", RuntimeResourceOverlayFactory)
+	ctx.RegisterModuleType("autogen_runtime_resource_overlay", AutogenRuntimeResourceOverlayFactory)
 	ctx.RegisterModuleType("override_runtime_resource_overlay", OverrideRuntimeResourceOverlayModuleFactory)
 }
 
@@ -50,7 +52,7 @@
 type RuntimeResourceOverlayProperties struct {
 	// the name of a certificate in the default certificate directory or an android_app_certificate
 	// module name in the form ":module".
-	Certificate *string
+	Certificate proptools.Configurable[string] `android:"replace_instead_of_append"`
 
 	// Name of the signing certificate lineage file.
 	Lineage *string
@@ -119,7 +121,7 @@
 		r.aapt.deps(ctx, sdkDep)
 	}
 
-	cert := android.SrcIsModule(String(r.properties.Certificate))
+	cert := android.SrcIsModule(r.properties.Certificate.GetOrDefault(ctx, ""))
 	if cert != "" {
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
@@ -166,7 +168,7 @@
 
 	// Sign the built package
 	_, _, certificates := collectAppDeps(ctx, r, false, false)
-	r.certificate, certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
+	r.certificate, certificates = processMainCert(r.ModuleBase, r.properties.Certificate.GetOrDefault(ctx, ""), certificates, ctx)
 	signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
 	var lineageFile android.Path
 	if lineage := String(r.properties.Lineage); lineage != "" {
@@ -269,3 +271,147 @@
 	android.InitOverrideModule(m)
 	return m
 }
+
+var (
+	generateOverlayManifestFile = pctx.AndroidStaticRule("generate_overlay_manifest",
+		blueprint.RuleParams{
+			Command: "build/make/tools/generate-enforce-rro-android-manifest.py " +
+				"--package-info $in " +
+				"--partition ${partition} " +
+				"--priority ${priority} -o $out",
+			CommandDeps: []string{"build/make/tools/generate-enforce-rro-android-manifest.py"},
+		}, "partition", "priority",
+	)
+)
+
+type AutogenRuntimeResourceOverlay struct {
+	android.ModuleBase
+	aapt
+
+	properties AutogenRuntimeResourceOverlayProperties
+
+	certificate Certificate
+	outputFile  android.Path
+}
+
+type AutogenRuntimeResourceOverlayProperties struct {
+	Base        *string
+	Sdk_version *string
+	Manifest    *string `android:"path"`
+}
+
+func AutogenRuntimeResourceOverlayFactory() android.Module {
+	m := &AutogenRuntimeResourceOverlay{}
+	m.AddProperties(&m.properties)
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+
+	return m
+}
+
+type rroDependencyTag struct {
+	blueprint.DependencyTag
+}
+
+// Autogenerated RROs should always depend on the source android_app that created it.
+func (tag rroDependencyTag) ReplaceSourceWithPrebuilt() bool {
+	return false
+}
+
+var rroDepTag = rroDependencyTag{}
+
+func (a *AutogenRuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
+	sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
+	if sdkDep.hasFrameworkLibs() {
+		a.aapt.deps(ctx, sdkDep)
+	}
+	ctx.AddDependency(ctx.Module(), rroDepTag, proptools.String(a.properties.Base))
+}
+
+func (a *AutogenRuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if !a.Enabled(ctx) {
+		return
+	}
+	var rroDirs android.Paths
+	// Get rro dirs of the base app
+	ctx.VisitDirectDepsWithTag(rroDepTag, func(m android.Module) {
+		aarDep, _ := m.(AndroidLibraryDependency)
+		if ctx.InstallInProduct() {
+			rroDirs = filterRRO(aarDep.RRODirsDepSet(), product)
+		} else {
+			rroDirs = filterRRO(aarDep.RRODirsDepSet(), device)
+		}
+	})
+
+	if len(rroDirs) == 0 {
+		return
+	}
+
+	// Generate a manifest file
+	genManifest := android.PathForModuleGen(ctx, "AndroidManifest.xml")
+	partition := "vendor"
+	priority := "0"
+	if ctx.InstallInProduct() {
+		partition = "product"
+		priority = "1"
+	}
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   generateOverlayManifestFile,
+		Input:  android.PathForModuleSrc(ctx, proptools.String(a.properties.Manifest)),
+		Output: genManifest,
+		Args: map[string]string{
+			"partition": partition,
+			"priority":  priority,
+		},
+	})
+
+	// Compile and link resources into package-res.apk
+	a.aapt.hasNoCode = true
+	aaptLinkFlags := []string{"--auto-add-overlay", "--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
+
+	a.aapt.buildActions(ctx,
+		aaptBuildActionOptions{
+			sdkContext:      a,
+			extraLinkFlags:  aaptLinkFlags,
+			rroDirs:         &rroDirs,
+			manifestForAapt: genManifest,
+		},
+	)
+
+	if a.exportPackage == nil {
+		return
+	}
+	// Sign the built package
+	var certificates []Certificate
+	a.certificate, certificates = processMainCert(a.ModuleBase, "", nil, ctx)
+	signed := android.PathForModuleOut(ctx, "signed", a.Name()+".apk")
+	SignAppPackage(ctx, signed, a.exportPackage, certificates, nil, nil, "")
+	a.outputFile = signed
+
+	// Install the signed apk
+	installDir := android.PathForModuleInstall(ctx, "overlay")
+	ctx.InstallFile(installDir, signed.Base(), signed)
+}
+
+func (a *AutogenRuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(a.properties.Sdk_version))
+}
+
+func (a *AutogenRuntimeResourceOverlay) SystemModules() string {
+	return ""
+}
+
+func (a *AutogenRuntimeResourceOverlay) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return a.SdkVersion(ctx).ApiLevel
+}
+
+func (r *AutogenRuntimeResourceOverlay) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
+	return android.SdkSpecPrivate.ApiLevel
+}
+
+func (a *AutogenRuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return a.SdkVersion(ctx).ApiLevel
+}
+
+func (a *AutogenRuntimeResourceOverlay) InstallInProduct() bool {
+	return a.ProductSpecific()
+}
diff --git a/java/rro_test.go b/java/rro_test.go
index 742c839..4d58bb4 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -255,103 +255,6 @@
 	}
 }
 
-func TestEnforceRRO_propagatesToDependencies(t *testing.T) {
-	testCases := []struct {
-		name              string
-		enforceRROTargets []string
-		rroDirs           map[string][]string
-	}{
-		{
-			name:              "no RRO",
-			enforceRROTargets: nil,
-			rroDirs: map[string][]string{
-				"foo": nil,
-				"bar": nil,
-			},
-		},
-		{
-			name:              "enforce RRO on all",
-			enforceRROTargets: []string{"*"},
-			rroDirs: map[string][]string{
-				"foo": {"product/vendor/blah/overlay/lib2/res"},
-				"bar": {"product/vendor/blah/overlay/lib2/res"},
-			},
-		},
-		{
-			name:              "enforce RRO on foo",
-			enforceRROTargets: []string{"foo"},
-			rroDirs: map[string][]string{
-				"foo": {"product/vendor/blah/overlay/lib2/res"},
-				"bar": {"product/vendor/blah/overlay/lib2/res"},
-			},
-		},
-	}
-
-	productResourceOverlays := []string{
-		"product/vendor/blah/overlay",
-	}
-
-	fs := android.MockFS{
-		"lib2/res/values/strings.xml":                             nil,
-		"product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
-	}
-
-	bp := `
-			android_app {
-				name: "foo",
-				sdk_version: "current",
-				resource_dirs: [],
-				static_libs: ["lib"],
-			}
-
-			android_app {
-				name: "bar",
-				sdk_version: "current",
-				resource_dirs: [],
-				static_libs: ["lib"],
-			}
-
-			android_library {
-				name: "lib",
-				sdk_version: "current",
-				resource_dirs: [],
-				static_libs: ["lib2"],
-			}
-
-			android_library {
-				name: "lib2",
-				sdk_version: "current",
-				resource_dirs: ["lib2/res"],
-			}
-		`
-
-	for _, testCase := range testCases {
-		t.Run(testCase.name, func(t *testing.T) {
-			result := android.GroupFixturePreparers(
-				PrepareForTestWithJavaDefaultModules,
-				fs.AddToFixture(),
-				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-					variables.ProductResourceOverlays = productResourceOverlays
-					if testCase.enforceRROTargets != nil {
-						variables.EnforceRROTargets = testCase.enforceRROTargets
-					}
-				}),
-			).RunTestWithBp(t, bp)
-
-			modules := []string{"foo", "bar"}
-			for _, moduleName := range modules {
-				module := result.ModuleForTests(moduleName, "android_common")
-				mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0]
-				actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
-				if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
-					t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
-						moduleName, testCase.rroDirs[moduleName], actualRRODirs)
-				}
-			}
-		})
-	}
-}
-
 func TestRuntimeResourceOverlayPartition(t *testing.T) {
 	bp := `
 		runtime_resource_overlay {
diff --git a/java/sdk.go b/java/sdk.go
index 1beb7d5..407fdcf 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -66,10 +66,8 @@
 	} 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.
+		// Build flag that controls whether Java 21 is used as the
+		// default target version, or Java 17.
 		return JAVA_VERSION_21
 	} else {
 		return JAVA_VERSION_17
diff --git a/java/sdk_library.go b/java/sdk_library.go
index bb0d7d0..7891776 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -29,11 +29,6 @@
 
 	"android/soong/android"
 	"android/soong/dexpreopt"
-	"android/soong/etc"
-)
-
-const (
-	sdkXmlFileSuffix = ".xml"
 )
 
 // A tag to associated a dependency with a specific api scope.
@@ -927,7 +922,7 @@
 	c.initSdkLibraryComponent(module)
 }
 
-func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool {
+func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied() bool {
 	namePtr := proptools.StringPtr(c.module.RootLibraryName())
 	c.sdkLibraryComponentProperties.SdkLibraryName = namePtr
 
@@ -949,62 +944,32 @@
 	return c.sharedLibrary()
 }
 
-func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.ModuleContext) {
+func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.ModuleContext) SdkLibraryInfo {
 	c.doctagPaths = android.PathsForModuleSrc(ctx, c.commonSdkLibraryProperties.Doctag_files)
-}
 
-func (c *commonToSdkLibraryAndImport) getImplLibraryModule() *Library {
-	return c.implLibraryModule
-}
+	everythingStubPaths := make(map[android.SdkKind]OptionalDexJarPath)
+	exportableStubPaths := make(map[android.SdkKind]OptionalDexJarPath)
+	removedApiFilePaths := make(map[android.SdkKind]android.OptionalPath)
+	for kind := android.SdkNone; kind <= android.SdkPrivate; kind += 1 {
+		everythingStubPath := makeUnsetDexJarPath()
+		exportableStubPath := makeUnsetDexJarPath()
+		removedApiFilePath := android.OptionalPath{}
+		if scopePath := c.findClosestScopePath(sdkKindToApiScope(kind)); scopePath != nil {
+			everythingStubPath = scopePath.stubsDexJarPath
+			exportableStubPath = scopePath.exportableStubsDexJarPath
+			removedApiFilePath = scopePath.removedApiFilePath
+		}
+		everythingStubPaths[kind] = everythingStubPath
+		exportableStubPaths[kind] = exportableStubPath
+		removedApiFilePaths[kind] = removedApiFilePath
+	}
 
-// Module name of the runtime implementation library
-func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
-	return c.module.RootLibraryName() + ".impl"
-}
-
-// Module name of the XML file for the lib
-func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
-	return c.module.RootLibraryName() + sdkXmlFileSuffix
-}
-
-// Name of the java_library module that compiles the stubs source.
-func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return apiScope.stubsLibraryModuleName(baseName)
-}
-
-// Name of the java_library module that compiles the exportable stubs source.
-func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return apiScope.exportableStubsLibraryModuleName(baseName)
-}
-
-// Name of the droidstubs module that generates the stubs source and may also
-// generate/check the API.
-func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return apiScope.stubsSourceModuleName(baseName)
-}
-
-// Name of the java_api_library module that generates the from-text stubs source
-// and compiles to a jar file.
-func (c *commonToSdkLibraryAndImport) apiLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return apiScope.apiLibraryModuleName(baseName)
-}
-
-// Name of the java_library module that compiles the stubs
-// generated from source Java files.
-func (c *commonToSdkLibraryAndImport) sourceStubsLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return apiScope.sourceStubsLibraryModuleName(baseName)
-}
-
-// Name of the java_library module that compiles the exportable stubs
-// generated from source Java files.
-func (c *commonToSdkLibraryAndImport) exportableSourceStubsLibraryModuleName(apiScope *apiScope) string {
-	baseName := c.module.RootLibraryName()
-	return apiScope.exportableSourceStubsLibraryModuleName(baseName)
+	return SdkLibraryInfo{
+		EverythingStubDexJarPaths: everythingStubPaths,
+		ExportableStubDexJarPaths: exportableStubPaths,
+		RemovedTxtFiles:           removedApiFilePaths,
+		SharedLibrary:             c.sharedLibrary(),
+	}
 }
 
 // The component names for different outputs of the java_sdk_library.
@@ -1078,29 +1043,6 @@
 	return nil
 }
 
-// selectScopePaths returns the *scopePaths appropriate for the specific kind.
-//
-// If the module does not support the specific kind then it will return the *scopePaths for the
-// closest kind which is a subset of the requested kind. e.g. if requesting android.SdkModule then
-// it will return *scopePaths for android.SdkSystem if available or android.SdkPublic of not.
-func (c *commonToSdkLibraryAndImport) selectScopePaths(ctx android.BaseModuleContext, kind android.SdkKind) *scopePaths {
-	apiScope := sdkKindToApiScope(kind)
-
-	paths := c.findClosestScopePath(apiScope)
-	if paths == nil {
-		var scopes []string
-		for _, s := range AllApiScopes {
-			if c.findScopePaths(s) != nil {
-				scopes = append(scopes, s.name)
-			}
-		}
-		ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.RootLibraryName(), scopes)
-		return nil
-	}
-
-	return paths
-}
-
 // sdkKindToApiScope maps from android.SdkKind to apiScope.
 func sdkKindToApiScope(kind android.SdkKind) *apiScope {
 	var apiScope *apiScope
@@ -1119,37 +1061,6 @@
 	return apiScope
 }
 
-// to satisfy SdkLibraryDependency interface
-func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath {
-	paths := c.selectScopePaths(ctx, kind)
-	if paths == nil {
-		return makeUnsetDexJarPath()
-	}
-
-	return paths.stubsDexJarPath
-}
-
-// to satisfy SdkLibraryDependency interface
-func (c *commonToSdkLibraryAndImport) SdkApiExportableStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath {
-	paths := c.selectScopePaths(ctx, kind)
-	if paths == nil {
-		return makeUnsetDexJarPath()
-	}
-
-	return paths.exportableStubsDexJarPath
-}
-
-// to satisfy SdkLibraryDependency interface
-func (c *commonToSdkLibraryAndImport) SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath {
-	apiScope := sdkKindToApiScope(kind)
-	paths := c.findScopePaths(apiScope)
-	if paths == nil {
-		return android.OptionalPath{}
-	}
-
-	return paths.removedApiFilePath
-}
-
 func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() interface{} {
 	componentProps := &struct {
 		SdkLibraryName              *string
@@ -1240,36 +1151,25 @@
 var _ SdkLibraryComponentDependency = (*SdkLibrary)(nil)
 var _ SdkLibraryComponentDependency = (*SdkLibraryImport)(nil)
 
-// Provides access to sdk_version related files, e.g. header and implementation jars.
-type SdkLibraryDependency interface {
-	SdkLibraryComponentDependency
-
-	// SdkApiStubDexJar returns the dex jar for the stubs for the prebuilt
-	// java_sdk_library_import module. It is needed by the hiddenapi processing tool which
-	// processes dex files.
-	SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath
-
-	// SdkApiExportableStubDexJar returns the exportable dex jar for the stubs for
-	// java_sdk_library module. It is needed by the hiddenapi processing tool which processes
-	// dex files.
-	SdkApiExportableStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath
-
-	// SdkRemovedTxtFile returns the optional path to the removed.txt file for the specified sdk kind.
-	SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath
-
-	// sharedLibrary returns true if this can be used as a shared library.
-	sharedLibrary() bool
-
-	// getImplLibraryModule returns the pointer to the implementation library submodule of this
-	// sdk library.
-	getImplLibraryModule() *Library
-}
-
 type SdkLibraryInfo struct {
 	// GeneratingLibs is the names of the library modules that this sdk library
 	// generates. Note that this only includes the name of the modules that other modules can
 	// depend on, and is not a holistic list of generated modules.
 	GeneratingLibs []string
+
+	// Map of sdk kind to the dex jar for the "everything" stubs.
+	// It is needed by the hiddenapi processing tool which processes dex files.
+	EverythingStubDexJarPaths map[android.SdkKind]OptionalDexJarPath
+
+	// Map of sdk kind to the dex jar for the "exportable" stubs.
+	// It is needed by the hiddenapi processing tool which processes dex files.
+	ExportableStubDexJarPaths map[android.SdkKind]OptionalDexJarPath
+
+	// Map of sdk kind to the optional path to the removed.txt file.
+	RemovedTxtFiles map[android.SdkKind]android.OptionalPath
+
+	// Whether if this can be used as a shared library.
+	SharedLibrary bool
 }
 
 var SdkLibraryInfoProvider = blueprint.NewProvider[SdkLibraryInfo]()
@@ -1303,12 +1203,13 @@
 	builtInstalledForApex []dexpreopterInstall
 }
 
-var _ SdkLibraryDependency = (*SdkLibrary)(nil)
-
 func (module *SdkLibrary) generateTestAndSystemScopesByDefault() bool {
 	return module.sdkLibraryProperties.Generate_system_and_test_apis
 }
 
+var _ UsesLibraryDependency = (*SdkLibrary)(nil)
+
+// To satisfy the UsesLibraryDependency interface
 func (module *SdkLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
 	if module.implLibraryModule != nil {
 		return module.implLibraryModule.DexJarBuildPath(ctx)
@@ -1316,6 +1217,7 @@
 	return makeUnsetDexJarPath()
 }
 
+// To satisfy the UsesLibraryDependency interface
 func (module *SdkLibrary) DexJarInstallPath() android.Path {
 	if module.implLibraryModule != nil {
 		return module.implLibraryModule.DexJarInstallPath()
@@ -1378,7 +1280,7 @@
 }
 
 func CheckMinSdkVersion(ctx android.ModuleContext, module *Library) {
-	android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.ModuleContext, do android.PayloadDepsCallback) {
+	android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.BaseModuleContext, do android.PayloadDepsCallback) {
 		ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
 			isExternal := !module.depIsInSameApex(ctx, child)
 			if am, ok := child.(android.ApexModule); ok {
@@ -1412,12 +1314,6 @@
 
 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"
 }
@@ -1433,7 +1329,7 @@
 		ctx.AddVariationDependencies(nil, apiScope.exportableStubsTag, exportableStubModuleName)
 
 		// Add a dependency on the stubs source in order to access both stubs source and api information.
-		ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope))
+		ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.droidstubsModuleName(apiScope))
 
 		if module.compareAgainstLatestApi(apiScope) {
 			// Add dependencies on the latest finalized version of the API .txt file.
@@ -1501,10 +1397,9 @@
 		// Even though the source javalib is not used, we need to hide it to prevent duplicate installation rules.
 		// TODO (b/331665856): Implement a principled solution for this.
 		module.HideFromMake()
+		module.SkipInstall()
 	}
 
-	module.generateCommonBuildActions(ctx)
-
 	module.stem = proptools.StringDefault(module.overridableProperties.Stem, ctx.ModuleName())
 
 	module.provideHiddenAPIPropertyInfo(ctx)
@@ -1537,11 +1432,11 @@
 			if dep, ok := android.OtherModuleProvider(ctx, to, JavaInfoProvider); ok {
 				module.implLibraryHeaderJars = append(module.implLibraryHeaderJars, dep.HeaderJars...)
 				module.implLibraryModule = to.(*Library)
-				android.SetProvider(ctx, JavaInfoProvider, dep)
 			}
 		}
 	})
 
+	sdkLibInfo := module.generateCommonBuildActions(ctx)
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
 		module.hideApexVariantFromMake = true
@@ -1570,7 +1465,10 @@
 		module.dexer.proguardDictionary = module.implLibraryModule.dexer.proguardDictionary
 		module.dexer.proguardUsageZip = module.implLibraryModule.dexer.proguardUsageZip
 		module.linter.reports = module.implLibraryModule.linter.reports
-		module.linter.outputs.depSets = module.implLibraryModule.LintDepSets()
+
+		if lintInfo, ok := android.OtherModuleProvider(ctx, module.implLibraryModule, LintProvider); ok {
+			android.SetProvider(ctx, LintProvider, lintInfo)
+		}
 
 		if !module.Host() {
 			module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile
@@ -1625,9 +1523,8 @@
 		setOutputFiles(ctx, module.implLibraryModule.Module)
 	}
 
-	android.SetProvider(ctx, SdkLibraryInfoProvider, SdkLibraryInfo{
-		GeneratingLibs: generatingLibs,
-	})
+	sdkLibInfo.GeneratingLibs = generatingLibs
+	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
 func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall {
@@ -1734,420 +1631,6 @@
 	return visibility
 }
 
-// Creates the implementation java library
-func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) {
-	visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility)
-
-	staticLibs := module.properties.Static_libs.Clone()
-	staticLibs.AppendSimpleValue(module.sdkLibraryProperties.Impl_only_static_libs)
-	props := struct {
-		Name           *string
-		Enabled        proptools.Configurable[bool]
-		Visibility     []string
-		Libs           []string
-		Static_libs    proptools.Configurable[[]string]
-		Apex_available []string
-		Stem           *string
-	}{
-		Name:       proptools.StringPtr(module.implLibraryModuleName()),
-		Enabled:    module.EnabledProperty(),
-		Visibility: visibility,
-
-		Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...),
-
-		Static_libs: staticLibs,
-		// Pass the apex_available settings down so that the impl library can be statically
-		// embedded within a library that is added to an APEX. Needed for updatable-media.
-		Apex_available: module.ApexAvailable(),
-
-		Stem: proptools.StringPtr(module.Name()),
-	}
-
-	properties := []interface{}{
-		&module.properties,
-		&module.protoProperties,
-		&module.deviceProperties,
-		&module.dexProperties,
-		&module.dexpreoptProperties,
-		&module.linter.properties,
-		&module.overridableProperties,
-		&props,
-		module.sdkComponentPropertiesForChildLibrary(),
-	}
-	mctx.CreateModule(LibraryFactory, properties...)
-}
-
-type libraryProperties struct {
-	Name           *string
-	Enabled        proptools.Configurable[bool]
-	Visibility     []string
-	Srcs           []string
-	Installable    *bool
-	Sdk_version    *string
-	System_modules *string
-	Patch_module   *string
-	Libs           []string
-	Static_libs    []string
-	Compile_dex    *bool
-	Java_version   *string
-	Openjdk9       struct {
-		Srcs       []string
-		Javacflags []string
-	}
-	Dist struct {
-		Targets []string
-		Dest    *string
-		Dir     *string
-		Tag     *string
-	}
-	Is_stubs_module       *bool
-	Stub_contributing_api *string
-}
-
-func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties {
-	props := libraryProperties{}
-	props.Enabled = module.EnabledProperty()
-	props.Visibility = []string{"//visibility:override", "//visibility:private"}
-	// sources are generated from the droiddoc
-	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
-	props.Sdk_version = proptools.StringPtr(sdkVersion)
-	props.System_modules = module.deviceProperties.System_modules
-	props.Patch_module = module.properties.Patch_module
-	props.Installable = proptools.BoolPtr(false)
-	props.Libs = module.sdkLibraryProperties.Stub_only_libs
-	props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...)
-	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
-	// The stub-annotations library contains special versions of the annotations
-	// with CLASS retention policy, so that they're kept.
-	if proptools.Bool(module.sdkLibraryProperties.Annotations_enabled) {
-		props.Libs = append(props.Libs, "stub-annotations")
-	}
-	props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs
-	props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags
-	// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
-	// interop with older developer tools that don't support 1.9.
-	props.Java_version = proptools.StringPtr("1.8")
-	props.Is_stubs_module = proptools.BoolPtr(true)
-	props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
-
-	return props
-}
-
-// Creates a static java library that has API stubs
-func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
-
-	props := module.stubsLibraryProps(mctx, apiScope)
-	props.Name = proptools.StringPtr(module.sourceStubsLibraryModuleName(apiScope))
-	props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope)}
-
-	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-// Create a static java library that compiles the "exportable" stubs
-func (module *SdkLibrary) createExportableStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
-	props := module.stubsLibraryProps(mctx, apiScope)
-	props.Name = proptools.StringPtr(module.exportableSourceStubsLibraryModuleName(apiScope))
-	props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope) + "{.exportable}"}
-
-	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-// Creates a droidstubs module that creates stubs source files from the given full source
-// files and also updates and checks the API specification files.
-func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) {
-	props := struct {
-		Name                             *string
-		Enabled                          proptools.Configurable[bool]
-		Visibility                       []string
-		Srcs                             []string
-		Installable                      *bool
-		Sdk_version                      *string
-		Api_surface                      *string
-		System_modules                   *string
-		Libs                             proptools.Configurable[[]string]
-		Output_javadoc_comments          *bool
-		Arg_files                        []string
-		Args                             *string
-		Java_version                     *string
-		Annotations_enabled              *bool
-		Merge_annotations_dirs           []string
-		Merge_inclusion_annotations_dirs []string
-		Generate_stubs                   *bool
-		Previous_api                     *string
-		Aconfig_declarations             []string
-		Check_api                        struct {
-			Current       ApiToCheck
-			Last_released ApiToCheck
-
-			Api_lint struct {
-				Enabled       *bool
-				New_since     *string
-				Baseline_file *string
-			}
-		}
-		Aidl struct {
-			Include_dirs       []string
-			Local_include_dirs []string
-		}
-		Dists []android.Dist
-	}{}
-
-	// The stubs source processing uses the same compile time classpath when extracting the
-	// API from the implementation library as it does when compiling it. i.e. the same
-	// * sdk version
-	// * system_modules
-	// * libs (static_libs/libs)
-
-	props.Name = proptools.StringPtr(name)
-	props.Enabled = module.EnabledProperty()
-	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility)
-	props.Srcs = append(props.Srcs, module.properties.Srcs...)
-	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
-	props.Sdk_version = module.deviceProperties.Sdk_version
-	props.Api_surface = &apiScope.name
-	props.System_modules = module.deviceProperties.System_modules
-	props.Installable = proptools.BoolPtr(false)
-	// A droiddoc module has only one Libs property and doesn't distinguish between
-	// shared libs and static libs. So we need to add both of these libs to Libs property.
-	props.Libs = proptools.NewConfigurable[[]string](nil, nil)
-	props.Libs.AppendSimpleValue(module.properties.Libs)
-	props.Libs.Append(module.properties.Static_libs)
-	props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
-	props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
-	props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs
-	props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs
-	props.Java_version = module.properties.Java_version
-
-	props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled
-	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
-	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
-	props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations
-
-	droidstubsArgs := []string{}
-	if len(module.sdkLibraryProperties.Api_packages) != 0 {
-		droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
-	}
-	droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
-	disabledWarnings := []string{"HiddenSuperclass"}
-	if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) {
-		disabledWarnings = append(disabledWarnings,
-			"BroadcastBehavior",
-			"DeprecationMismatch",
-			"MissingPermission",
-			"SdkConstant",
-			"Todo",
-		)
-	}
-	droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
-
-	// Output Javadoc comments for public scope.
-	if apiScope == apiScopePublic {
-		props.Output_javadoc_comments = proptools.BoolPtr(true)
-	}
-
-	// Add in scope specific arguments.
-	droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...)
-	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
-	props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " "))
-
-	// List of APIs identified from the provided source files are created. They are later
-	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
-	// last-released (a.k.a numbered) list of API.
-	currentApiFileName := apiScope.apiFilePrefix + "current.txt"
-	removedApiFileName := apiScope.apiFilePrefix + "removed.txt"
-	apiDir := module.getApiDir()
-	currentApiFileName = path.Join(apiDir, currentApiFileName)
-	removedApiFileName = path.Join(apiDir, removedApiFileName)
-
-	// check against the not-yet-release API
-	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
-	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
-
-	if module.compareAgainstLatestApi(apiScope) {
-		// check against the latest released API
-		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
-		props.Previous_api = latestApiFilegroupName
-		props.Check_api.Last_released.Api_file = latestApiFilegroupName
-		props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
-			module.latestRemovedApiFilegroupName(apiScope))
-		props.Check_api.Last_released.Baseline_file = proptools.StringPtr(
-			module.latestIncompatibilitiesFilegroupName(apiScope))
-
-		if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
-			// Enable api lint.
-			props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true)
-			props.Check_api.Api_lint.New_since = latestApiFilegroupName
-
-			// If it exists then pass a lint-baseline.txt through to droidstubs.
-			baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt")
-			baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath)
-			paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil)
-			if err != nil {
-				mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err)
-			}
-			if len(paths) == 1 {
-				props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath)
-			} else if len(paths) != 0 {
-				mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths)
-			}
-		}
-	}
-
-	if !Bool(module.sdkLibraryProperties.No_dist) {
-		// Dist the api txt and removed api txt artifacts for sdk builds.
-		distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
-		stubsTypeTagPrefix := ""
-		if mctx.Config().ReleaseHiddenApiExportableStubs() {
-			stubsTypeTagPrefix = ".exportable"
-		}
-		for _, p := range []struct {
-			tag     string
-			pattern string
-		}{
-			// "exportable" api files are copied to the dist directory instead of the
-			// "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag
-			// is set. Otherwise, the "everything" api files are copied to the dist directory.
-			{tag: "%s.api.txt", pattern: "%s.txt"},
-			{tag: "%s.removed-api.txt", pattern: "%s-removed.txt"},
-		} {
-			props.Dists = append(props.Dists, android.Dist{
-				Targets: []string{"sdk", "win_sdk"},
-				Dir:     distDir,
-				Dest:    proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())),
-				Tag:     proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)),
-			})
-		}
-	}
-
-	mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx)
-}
-
-func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
-	props := struct {
-		Name              *string
-		Enabled           proptools.Configurable[bool]
-		Visibility        []string
-		Api_contributions []string
-		Libs              proptools.Configurable[[]string]
-		Static_libs       []string
-		System_modules    *string
-		Enable_validation *bool
-		Stubs_type        *string
-		Sdk_version       *string
-		Previous_api      *string
-	}{}
-
-	props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope))
-	props.Enabled = module.EnabledProperty()
-	props.Visibility = []string{"//visibility:override", "//visibility:private"}
-
-	apiContributions := []string{}
-
-	// Api surfaces are not independent of each other, but have subset relationships,
-	// and so does the api files. To generate from-text stubs for api surfaces other than public,
-	// all subset api domains' api_contriubtions must be added as well.
-	scope := apiScope
-	for scope != nil {
-		apiContributions = append(apiContributions, module.stubsSourceModuleName(scope)+".api.contribution")
-		scope = scope.extends
-	}
-	if apiScope == apiScopePublic {
-		additionalApiContribution := module.apiLibraryAdditionalApiContribution()
-		if additionalApiContribution != "" {
-			apiContributions = append(apiContributions, additionalApiContribution)
-		}
-	}
-
-	props.Api_contributions = apiContributions
-
-	// Ensure that stub-annotations is added to the classpath before any other libs
-	props.Libs = proptools.NewConfigurable[[]string](nil, nil)
-	props.Libs.AppendSimpleValue([]string{"stub-annotations"})
-	props.Libs.AppendSimpleValue(module.properties.Libs)
-	props.Libs.Append(module.properties.Static_libs)
-	props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
-	props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
-	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
-
-	props.System_modules = module.deviceProperties.System_modules
-	props.Enable_validation = proptools.BoolPtr(true)
-	props.Stubs_type = proptools.StringPtr("everything")
-
-	if module.deviceProperties.Sdk_version != nil {
-		props.Sdk_version = module.deviceProperties.Sdk_version
-	}
-
-	if module.compareAgainstLatestApi(apiScope) {
-		// check against the latest released API
-		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
-		props.Previous_api = latestApiFilegroupName
-	}
-
-	mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties {
-	props := libraryProperties{}
-
-	props.Enabled = module.EnabledProperty()
-	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
-	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
-	props.Sdk_version = proptools.StringPtr(sdkVersion)
-
-	props.System_modules = module.deviceProperties.System_modules
-
-	// The imports need to be compiled to dex if the java_sdk_library requests it.
-	compileDex := module.dexProperties.Compile_dex
-	if module.stubLibrariesCompiledForDex() {
-		compileDex = proptools.BoolPtr(true)
-	}
-	props.Compile_dex = compileDex
-
-	props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
-
-	if !Bool(module.sdkLibraryProperties.No_dist) && doDist {
-		props.Dist.Targets = []string{"sdk", "win_sdk"}
-		props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem()))
-		props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope))
-		props.Dist.Tag = proptools.StringPtr(".jar")
-	}
-	props.Is_stubs_module = proptools.BoolPtr(true)
-
-	return props
-}
-
-func (module *SdkLibrary) createTopLevelStubsLibrary(
-	mctx android.DefaultableHookContext, apiScope *apiScope) {
-
-	// Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false
-	doDist := !mctx.Config().ReleaseHiddenApiExportableStubs()
-	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
-	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
-
-	// Add the stub compiling java_library/java_api_library as static lib based on build config
-	staticLib := module.sourceStubsLibraryModuleName(apiScope)
-	if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
-		staticLib = module.apiLibraryModuleName(apiScope)
-	}
-	props.Static_libs = append(props.Static_libs, staticLib)
-
-	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-func (module *SdkLibrary) createTopLevelExportableStubsLibrary(
-	mctx android.DefaultableHookContext, apiScope *apiScope) {
-
-	// Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true
-	doDist := mctx.Config().ReleaseHiddenApiExportableStubs()
-	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
-	props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope))
-
-	staticLib := module.exportableSourceStubsLibraryModuleName(apiScope)
-	props.Static_libs = append(props.Static_libs, staticLib)
-
-	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
 func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool {
 	return !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api)
 }
@@ -2173,40 +1656,6 @@
 	return proptools.BoolDefault(module.sdkLibraryProperties.Build_from_text_stub, true)
 }
 
-// Creates the xml file that publicizes the runtime library
-func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
-	moduleMinApiLevel := module.Library.MinSdkVersion(mctx)
-	var moduleMinApiLevelStr = moduleMinApiLevel.String()
-	if moduleMinApiLevel == android.NoneApiLevel {
-		moduleMinApiLevelStr = "current"
-	}
-	props := struct {
-		Name                      *string
-		Enabled                   proptools.Configurable[bool]
-		Lib_name                  *string
-		Apex_available            []string
-		On_bootclasspath_since    *string
-		On_bootclasspath_before   *string
-		Min_device_sdk            *string
-		Max_device_sdk            *string
-		Sdk_library_min_api_level *string
-		Uses_libs_dependencies    []string
-	}{
-		Name:                      proptools.StringPtr(module.xmlPermissionsModuleName()),
-		Enabled:                   module.EnabledProperty(),
-		Lib_name:                  proptools.StringPtr(module.BaseModuleName()),
-		Apex_available:            module.ApexProperties.Apex_available,
-		On_bootclasspath_since:    module.commonSdkLibraryProperties.On_bootclasspath_since,
-		On_bootclasspath_before:   module.commonSdkLibraryProperties.On_bootclasspath_before,
-		Min_device_sdk:            module.commonSdkLibraryProperties.Min_device_sdk,
-		Max_device_sdk:            module.commonSdkLibraryProperties.Max_device_sdk,
-		Sdk_library_min_api_level: &moduleMinApiLevelStr,
-		Uses_libs_dependencies:    module.usesLibraryProperties.Uses_libs,
-	}
-
-	mctx.CreateModule(sdkLibraryXmlFactory, &props)
-}
-
 var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries")
 
 func javaSdkLibraries(config android.Config) *[]string {
@@ -2272,10 +1721,10 @@
 
 	for _, scope := range generatedScopes {
 		// Use the stubs source name for legacy reasons.
-		module.createStubsSourcesAndApi(mctx, scope, module.stubsSourceModuleName(scope), scope.droidstubsArgs)
+		module.createDroidstubs(mctx, scope, module.droidstubsModuleName(scope), scope.droidstubsArgs)
 
-		module.createStubsLibrary(mctx, scope)
-		module.createExportableStubsLibrary(mctx, scope)
+		module.createFromSourceStubsLibrary(mctx, scope)
+		module.createExportableFromSourceStubsLibrary(mctx, scope)
 
 		if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
 			module.createApiLibrary(mctx, scope)
@@ -2386,7 +1835,7 @@
 			module.commonSdkLibraryProperties.Shared_library = proptools.BoolPtr(false)
 		}
 
-		if module.initCommonAfterDefaultsApplied(ctx) {
+		if module.initCommonAfterDefaultsApplied() {
 			module.CreateInternalModules(ctx)
 		}
 	})
@@ -2465,8 +1914,6 @@
 	installFile android.Path
 }
 
-var _ SdkLibraryDependency = (*SdkLibraryImport)(nil)
-
 // The type of a structure that contains a field of type sdkLibraryScopeProperties
 // for each apiscope in allApiScopes, e.g. something like:
 //
@@ -2522,7 +1969,7 @@
 	InitJavaModule(module, android.HostAndDeviceSupported)
 
 	module.SetDefaultableHook(func(mctx android.DefaultableHookContext) {
-		if module.initCommonAfterDefaultsApplied(mctx) {
+		if module.initCommonAfterDefaultsApplied() {
 			module.createInternalModules(mctx)
 		}
 	})
@@ -2576,86 +2023,6 @@
 	*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
 }
 
-func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
-	// Creates a java import for the jar with ".stubs" suffix
-	props := struct {
-		Name                             *string
-		Source_module_name               *string
-		Created_by_java_sdk_library_name *string
-		Sdk_version                      *string
-		Libs                             []string
-		Jars                             []string
-		Compile_dex                      *bool
-		Is_stubs_module                  *bool
-
-		android.UserSuppliedPrebuiltProperties
-	}{}
-	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
-	props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName()))
-	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
-	props.Sdk_version = scopeProperties.Sdk_version
-	// Prepend any of the libs from the legacy public properties to the libs for each of the
-	// scopes to avoid having to duplicate them in each scope.
-	props.Libs = append(module.properties.Libs, scopeProperties.Libs...)
-	props.Jars = scopeProperties.Jars
-
-	// The imports are preferred if the java_sdk_library_import is preferred.
-	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
-
-	// The imports need to be compiled to dex if the java_sdk_library_import requests it.
-	compileDex := module.properties.Compile_dex
-	if module.stubLibrariesCompiledForDex() {
-		compileDex = proptools.BoolPtr(true)
-	}
-	props.Compile_dex = compileDex
-	props.Is_stubs_module = proptools.BoolPtr(true)
-
-	mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
-	props := struct {
-		Name                             *string
-		Source_module_name               *string
-		Created_by_java_sdk_library_name *string
-		Srcs                             []string
-
-		android.UserSuppliedPrebuiltProperties
-	}{}
-	props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope))
-	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()))
-	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
-	props.Srcs = scopeProperties.Stub_srcs
-
-	// The stubs source is preferred if the java_sdk_library_import is preferred.
-	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
-
-	mctx.CreateModule(PrebuiltStubsSourcesFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
-func (module *SdkLibraryImport) createPrebuiltApiContribution(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
-	api_file := scopeProperties.Current_api
-	api_surface := &apiScope.name
-
-	props := struct {
-		Name                             *string
-		Source_module_name               *string
-		Created_by_java_sdk_library_name *string
-		Api_surface                      *string
-		Api_file                         *string
-		Visibility                       []string
-	}{}
-
-	props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope) + ".api.contribution")
-	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution")
-	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
-	props.Api_surface = api_surface
-	props.Api_file = api_file
-	props.Visibility = []string{"//visibility:override", "//visibility:public"}
-
-	mctx.CreateModule(ApiContributionImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
-}
-
 // Add the dependencies on the child module in the component deps mutator so that it
 // creates references to the prebuilt and not the source modules.
 func (module *SdkLibraryImport) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
@@ -2669,7 +2036,7 @@
 
 		if len(scopeProperties.Stub_srcs) > 0 {
 			// Add dependencies to the prebuilt stubs source library
-			ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, android.PrebuiltNameFromSource(module.stubsSourceModuleName(apiScope)))
+			ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, android.PrebuiltNameFromSource(module.droidstubsModuleName(apiScope)))
 		}
 	}
 }
@@ -2723,8 +2090,6 @@
 var _ hiddenAPIModule = (*SdkLibraryImport)(nil)
 
 func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	module.generateCommonBuildActions(ctx)
-
 	// Assume that source module(sdk_library) is installed in /<sdk_library partition>/framework
 	module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar")
 
@@ -2754,6 +2119,7 @@
 			}
 		}
 	})
+	sdkLibInfo := module.generateCommonBuildActions(ctx)
 
 	// Populate the scope paths with information from the properties.
 	for apiScope, scopeProperties := range module.scopeProperties {
@@ -2795,11 +2161,12 @@
 		setOutputFiles(ctx, module.implLibraryModule.Module)
 	}
 
-	android.SetProvider(ctx, SdkLibraryInfoProvider, SdkLibraryInfo{
-		GeneratingLibs: generatingLibs,
-	})
+	sdkLibInfo.GeneratingLibs = generatingLibs
+	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
+var _ UsesLibraryDependency = (*SdkLibraryImport)(nil)
+
 // to satisfy UsesLibraryDependency interface
 func (module *SdkLibraryImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
 	// The dex implementation jar extracted from the .apex file should be used in preference to the
@@ -2837,29 +2204,6 @@
 }
 
 // to satisfy apex.javaDependency interface
-func (module *SdkLibraryImport) LintDepSets() LintDepSets {
-	if module.implLibraryModule == nil {
-		return LintDepSets{}
-	} else {
-		return module.implLibraryModule.LintDepSets()
-	}
-}
-
-func (module *SdkLibraryImport) GetStrictUpdatabilityLinting() bool {
-	if module.implLibraryModule == nil {
-		return false
-	} else {
-		return module.implLibraryModule.GetStrictUpdatabilityLinting()
-	}
-}
-
-func (module *SdkLibraryImport) SetStrictUpdatabilityLinting(strictLinting bool) {
-	if module.implLibraryModule != nil {
-		module.implLibraryModule.SetStrictUpdatabilityLinting(strictLinting)
-	}
-}
-
-// to satisfy apex.javaDependency interface
 func (module *SdkLibraryImport) Stem() string {
 	return module.BaseModuleName()
 }
@@ -2900,333 +2244,6 @@
 	return proptools.Bool(j.importDexpreoptProperties.Dex_preopt.Profile_guided)
 }
 
-// java_sdk_library_xml
-type sdkLibraryXml struct {
-	android.ModuleBase
-	android.DefaultableModuleBase
-	android.ApexModuleBase
-
-	properties sdkLibraryXmlProperties
-
-	outputFilePath android.OutputPath
-	installDirPath android.InstallPath
-
-	hideApexVariantFromMake bool
-}
-
-type sdkLibraryXmlProperties struct {
-	// canonical name of the lib
-	Lib_name *string
-
-	// Signals that this shared library is part of the bootclasspath starting
-	// on the version indicated in this attribute.
-	//
-	// This will make platforms at this level and above to ignore
-	// <uses-library> tags with this library name because the library is already
-	// available
-	On_bootclasspath_since *string
-
-	// Signals that this shared library was part of the bootclasspath before
-	// (but not including) the version indicated in this attribute.
-	//
-	// The system will automatically add a <uses-library> tag with this library to
-	// apps that target any SDK less than the version indicated in this attribute.
-	On_bootclasspath_before *string
-
-	// Indicates that PackageManager should ignore this shared library if the
-	// platform is below the version indicated in this attribute.
-	//
-	// This means that the device won't recognise this library as installed.
-	Min_device_sdk *string
-
-	// Indicates that PackageManager should ignore this shared library if the
-	// platform is above the version indicated in this attribute.
-	//
-	// This means that the device won't recognise this library as installed.
-	Max_device_sdk *string
-
-	// The SdkLibrary's min api level as a string
-	//
-	// This value comes from the ApiLevel of the MinSdkVersion property.
-	Sdk_library_min_api_level *string
-
-	// Uses-libs dependencies that the shared library requires to work correctly.
-	//
-	// This will add dependency="foo:bar" to the <library> section.
-	Uses_libs_dependencies []string
-}
-
-// java_sdk_library_xml builds the permission xml file for a java_sdk_library.
-// Not to be used directly by users. java_sdk_library internally uses this.
-func sdkLibraryXmlFactory() android.Module {
-	module := &sdkLibraryXml{}
-
-	module.AddProperties(&module.properties)
-
-	android.InitApexModule(module)
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
-
-	return module
-}
-
-func (module *sdkLibraryXml) UniqueApexVariations() bool {
-	// sdkLibraryXml needs a unique variation per APEX because the generated XML file contains the path to the
-	// mounted APEX, which contains the name of the APEX.
-	return true
-}
-
-// from android.PrebuiltEtcModule
-func (module *sdkLibraryXml) BaseDir() string {
-	return "etc"
-}
-
-// from android.PrebuiltEtcModule
-func (module *sdkLibraryXml) SubDir() string {
-	return "permissions"
-}
-
-var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil)
-
-// from android.ApexModule
-func (module *sdkLibraryXml) AvailableFor(what string) bool {
-	return true
-}
-
-func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// do nothing
-}
-
-var _ android.ApexModule = (*sdkLibraryXml)(nil)
-
-// Implements android.ApexModule
-func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
-	return nil
-}
-
-// File path to the runtime implementation library
-func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string {
-	implName := proptools.String(module.properties.Lib_name)
-	if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() {
-		// TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name.
-		// In most cases, this works fine. But when apex_name is set or override_apex is used
-		// this can be wrong.
-		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName)
-	}
-	partition := "system"
-	if module.SocSpecific() {
-		partition = "vendor"
-	} else if module.DeviceSpecific() {
-		partition = "odm"
-	} else if module.ProductSpecific() {
-		partition = "product"
-	} else if module.SystemExtSpecific() {
-		partition = "system_ext"
-	}
-	return "/" + partition + "/framework/" + implName + ".jar"
-}
-
-func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string {
-	if value == nil {
-		return ""
-	}
-	apiLevel, err := android.ApiLevelFromUser(ctx, *value)
-	if err != nil {
-		// attributes in bp files have underscores but in the xml have dashes.
-		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
-		return ""
-	}
-	if apiLevel.IsCurrent() {
-		// passing "current" would always mean a future release, never the current (or the current in
-		// progress) which means some conditions would never be triggered.
-		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
-			`"current" is not an allowed value for this attribute`)
-		return ""
-	}
-	// "safeValue" is safe because it translates finalized codenames to a string
-	// with their SDK int.
-	safeValue := apiLevel.String()
-	return formattedOptionalAttribute(attrName, &safeValue)
-}
-
-// formats an attribute for the xml permissions file if the value is not null
-// returns empty string otherwise
-func formattedOptionalAttribute(attrName string, value *string) string {
-	if value == nil {
-		return ""
-	}
-	return fmt.Sprintf("        %s=\"%s\"\n", attrName, *value)
-}
-
-func formattedDependenciesAttribute(dependencies []string) string {
-	if dependencies == nil {
-		return ""
-	}
-	return fmt.Sprintf("        dependency=\"%s\"\n", strings.Join(dependencies, ":"))
-}
-
-func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string {
-	libName := proptools.String(module.properties.Lib_name)
-	libNameAttr := formattedOptionalAttribute("name", &libName)
-	filePath := module.implPath(ctx)
-	filePathAttr := formattedOptionalAttribute("file", &filePath)
-	implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since)
-	implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before)
-	minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk)
-	maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk)
-	dependenciesAttr := formattedDependenciesAttribute(module.properties.Uses_libs_dependencies)
-	// <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that).
-	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T
-	var libraryTag string
-	if module.properties.Min_device_sdk != nil {
-		libraryTag = "    <apex-library\n"
-	} else {
-		libraryTag = "    <library\n"
-	}
-
-	return strings.Join([]string{
-		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
-		"<!-- Copyright (C) 2018 The Android Open Source Project\n",
-		"\n",
-		"    Licensed under the Apache License, Version 2.0 (the \"License\");\n",
-		"    you may not use this file except in compliance with the License.\n",
-		"    You may obtain a copy of the License at\n",
-		"\n",
-		"        http://www.apache.org/licenses/LICENSE-2.0\n",
-		"\n",
-		"    Unless required by applicable law or agreed to in writing, software\n",
-		"    distributed under the License is distributed on an \"AS IS\" BASIS,\n",
-		"    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
-		"    See the License for the specific language governing permissions and\n",
-		"    limitations under the License.\n",
-		"-->\n",
-		"<permissions>\n",
-		libraryTag,
-		libNameAttr,
-		filePathAttr,
-		implicitFromAttr,
-		implicitUntilAttr,
-		minSdkAttr,
-		maxSdkAttr,
-		dependenciesAttr,
-		"    />\n",
-		"</permissions>\n",
-	}, "")
-}
-
-func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
-	module.hideApexVariantFromMake = !apexInfo.IsForPlatform()
-
-	libName := proptools.String(module.properties.Lib_name)
-	module.selfValidate(ctx)
-	xmlContent := module.permissionsContents(ctx)
-
-	module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
-	android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent)
-
-	module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
-	ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
-
-	ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "")
-}
-
-func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
-	if module.hideApexVariantFromMake {
-		return []android.AndroidMkEntries{{
-			Disabled: true,
-		}}
-	}
-
-	return []android.AndroidMkEntries{{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(module.outputFilePath),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_TAGS", "optional")
-				entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
-			},
-		},
-	}}
-}
-
-func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) {
-	module.validateAtLeastTAttributes(ctx)
-	module.validateMinAndMaxDeviceSdk(ctx)
-	module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx)
-	module.validateOnBootclasspathBeforeRequirements(ctx)
-}
-
-func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) {
-	t := android.ApiLevelOrPanic(ctx, "Tiramisu")
-	module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk")
-	module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk")
-	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before")
-	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since")
-}
-
-func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) {
-	if attr != nil {
-		if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil {
-			// we will inform the user of invalid inputs when we try to write the
-			// permissions xml file so we don't need to do it here
-			if t.GreaterThan(level) {
-				ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T")
-			}
-		}
-	}
-}
-
-func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) {
-	if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil {
-		min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
-		max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
-		if minErr == nil && maxErr == nil {
-			// we will inform the user of invalid inputs when we try to write the
-			// permissions xml file so we don't need to do it here
-			if min.GreaterThan(max) {
-				ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk")
-			}
-		}
-	}
-}
-
-func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) {
-	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
-	if module.properties.Min_device_sdk != nil {
-		api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
-		if err == nil {
-			if moduleMinApi.GreaterThan(api) {
-				ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
-			}
-		}
-	}
-	if module.properties.Max_device_sdk != nil {
-		api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
-		if err == nil {
-			if moduleMinApi.GreaterThan(api) {
-				ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
-			}
-		}
-	}
-}
-
-func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) {
-	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
-	if module.properties.On_bootclasspath_before != nil {
-		t := android.ApiLevelOrPanic(ctx, "Tiramisu")
-		// if we use the attribute, then we need to do this validation
-		if moduleMinApi.LessThan(t) {
-			// if minAPi is < T, then we need to have min_device_sdk (which only accepts T+)
-			if module.properties.Min_device_sdk == nil {
-				ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T")
-			}
-		}
-	}
-}
-
 type sdkLibrarySdkMemberType struct {
 	android.SdkMemberTypeBase
 }
@@ -3372,7 +2389,7 @@
 	s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk
 	s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk
 
-	implLibrary := sdk.getImplLibraryModule()
+	implLibrary := sdk.implLibraryModule
 	if implLibrary != nil && implLibrary.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
 		s.DexPreoptProfileGuided = proptools.BoolPtr(true)
 	}
@@ -3459,19 +2476,3 @@
 		propertySet.AddProperty("doctag_files", dests)
 	}
 }
-
-// TODO(b/358613520): This can be removed when modules are no longer allowed to depend on the top-level library.
-func (s *SdkLibrary) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	s.Library.IDEInfo(ctx, dpInfo)
-	if s.implLibraryModule != nil {
-		dpInfo.Deps = append(dpInfo.Deps, s.implLibraryModule.Name())
-	} else {
-		// This java_sdk_library does not have an implementation (it sets `api_only` to true).
-		// Examples of this are `art.module.intra.core.api` (IntraCore api surface).
-		// Return the "public" stubs for these.
-		stubPaths := s.findClosestScopePath(apiScopePublic)
-		if len(stubPaths.stubsHeaderPath) > 0 {
-			dpInfo.Jars = append(dpInfo.Jars, stubPaths.stubsHeaderPath[0].String())
-		}
-	}
-}
diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go
deleted file mode 100644
index 4f83981..0000000
--- a/java/sdk_library_external.go
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2020 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 java
-
-import (
-	"android/soong/android"
-)
-
-type partitionGroup int
-
-// Representation of partition group for checking inter-partition library dependencies.
-// Between system and system_ext, there are no restrictions of dependencies,
-// so we can treat these partitions as the same in terms of inter-partition dependency.
-// Same policy is applied between vendor and odm partiton.
-const (
-	partitionGroupNone partitionGroup = iota
-	// group for system, and system_ext partition
-	partitionGroupSystem
-	// group for vendor and odm partition
-	partitionGroupVendor
-	// product partition
-	partitionGroupProduct
-)
-
-func (g partitionGroup) String() string {
-	switch g {
-	case partitionGroupSystem:
-		return "system"
-	case partitionGroupVendor:
-		return "vendor"
-	case partitionGroupProduct:
-		return "product"
-	}
-
-	return ""
-}
-
-// Get partition group of java module that can be used at inter-partition dependency check.
-// We currently have three groups
-//
-//	(system, system_ext) => system partition group
-//	(vendor, odm) => vendor partition group
-//	(product) => product partition group
-func (j *Module) partitionGroup(ctx android.EarlyModuleContext) partitionGroup {
-	// system and system_ext partition can be treated as the same in terms of inter-partition dependency.
-	if j.Platform() || j.SystemExtSpecific() {
-		return partitionGroupSystem
-	}
-
-	// vendor and odm partition can be treated as the same in terms of inter-partition dependency.
-	if j.SocSpecific() || j.DeviceSpecific() {
-		return partitionGroupVendor
-	}
-
-	// product partition is independent.
-	if j.ProductSpecific() {
-		return partitionGroupProduct
-	}
-
-	panic("Cannot determine partition type")
-}
-
-func (j *Module) allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool {
-	return inList(j.Name(), ctx.Config().InterPartitionJavaLibraryAllowList())
-}
-
-func (j *Module) syspropWithPublicStubs() bool {
-	return j.deviceProperties.SyspropPublicStub != ""
-}
-
-type javaSdkLibraryEnforceContext interface {
-	Name() string
-	allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool
-	partitionGroup(ctx android.EarlyModuleContext) partitionGroup
-	syspropWithPublicStubs() bool
-}
-
-var _ javaSdkLibraryEnforceContext = (*Module)(nil)
-
-func (j *Module) checkPartitionsForJavaDependency(ctx android.EarlyModuleContext, propName string, dep javaSdkLibraryEnforceContext) {
-	if dep.allowListedInterPartitionJavaLibrary(ctx) {
-		return
-	}
-
-	if dep.syspropWithPublicStubs() {
-		return
-	}
-
-	// If product interface is not enforced, skip check between system and product partition.
-	// But still need to check between product and vendor partition because product interface flag
-	// just represents enforcement between product and system, and vendor interface enforcement
-	// that is enforced here by precondition is representing enforcement between vendor and other partitions.
-	if !ctx.Config().EnforceProductPartitionInterface() {
-		productToSystem := j.partitionGroup(ctx) == partitionGroupProduct && dep.partitionGroup(ctx) == partitionGroupSystem
-		systemToProduct := j.partitionGroup(ctx) == partitionGroupSystem && dep.partitionGroup(ctx) == partitionGroupProduct
-
-		if productToSystem || systemToProduct {
-			return
-		}
-	}
-
-	// If module and dependency library is inter-partition
-	if j.partitionGroup(ctx) != dep.partitionGroup(ctx) {
-		errorFormat := "dependency on java_library (%q) is not allowed across the partitions (%s -> %s), use java_sdk_library instead"
-		ctx.PropertyErrorf(propName, errorFormat, dep.Name(), j.partitionGroup(ctx), dep.partitionGroup(ctx))
-	}
-}
diff --git a/java/sdk_library_internal.go b/java/sdk_library_internal.go
new file mode 100644
index 0000000..768e57a
--- /dev/null
+++ b/java/sdk_library_internal.go
@@ -0,0 +1,1017 @@
+// 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 java
+
+import (
+	"android/soong/android"
+	"android/soong/etc"
+	"fmt"
+	"path"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+// ---------------------------------------------------------------------------------------------
+// Naming scheme of the submodules generated by java_sdk_library and java_sdk_library_import
+// ---------------------------------------------------------------------------------------------
+
+const (
+	sdkXmlFileSuffix = ".xml"
+	implLibSuffix    = ".impl"
+)
+
+func implLibraryModuleName(sdkLibName string) string {
+	return sdkLibName + implLibSuffix
+}
+
+// Module name of the runtime implementation library
+func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
+	return implLibraryModuleName(c.module.RootLibraryName())
+}
+
+// Module name of the XML file for the lib
+func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
+	return c.module.RootLibraryName() + sdkXmlFileSuffix
+}
+
+// Name of the java_library module that compiles the stubs source.
+func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.stubsLibraryModuleName(baseName)
+}
+
+// Name of the java_library module that compiles the exportable stubs source.
+func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.exportableStubsLibraryModuleName(baseName)
+}
+
+// Name of the droidstubs module that generates the stubs source and may also
+// generate/check the API.
+func (c *commonToSdkLibraryAndImport) droidstubsModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.stubsSourceModuleName(baseName)
+}
+
+// Name of the java_api_library module that generates the from-text stubs source
+// and compiles to a jar file.
+func (c *commonToSdkLibraryAndImport) fromTextStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.apiLibraryModuleName(baseName)
+}
+
+// Name of the java_library module that compiles the stubs
+// generated from source Java files.
+func (c *commonToSdkLibraryAndImport) fromSourceStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.sourceStubsLibraryModuleName(baseName)
+}
+
+// Name of the java_library module that compiles the exportable stubs
+// generated from source Java files.
+func (c *commonToSdkLibraryAndImport) exportableFromSourceStubsLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.RootLibraryName()
+	return apiScope.exportableSourceStubsLibraryModuleName(baseName)
+}
+
+// ---------------------------------------------------------------------------------------------
+// Build rules of the submodules generated by java_sdk_library.
+// java_sdk_library "framework-foo" generates the following submodules:
+//
+// - "framework-foo.impl" (type: [Library]): the implementation library, which generates the
+//		compilation outputs that include the implementation details and the private apis
+//		(i.e. class/methods that are annotated @hide).
+//
+// - "framework-foo.stubs.source.<[apiScope.name]>" (type: [Droidstubs]): droidstubs module that
+//		generates the stubs and the api files for the given api scope.
+//
+// - "framework-foo.stubs.<[apiScope.name]>" (type: [Library]): stub library module that
+//		provides the compilation output of the stubs to the reverse dependencies. The module
+//		itself does not perform any compilation actions; the module statically depends on one of
+//		the from-source stub module or the from-text stub configuration based on the build
+// 		configuration.
+//
+// - "framework-foo.stubs.<[apiScope.name]>.from-source" (type: [Library]): stub library module
+//		that compiles the stubs generated by the droidstubs submodule. This module is a static
+//		dependency of the stub library module when
+//		[android/soong/android/config.BuildFromTextStub()] is false.
+//
+// - "framework-foo.stubs.<[apiScope.name]>.from-text" (type: [ApiLibrary]): api library module
+//		that generates and compiles the stubs from the api files checked in the tree instead of
+//		the source Java files (e.g. *-current.txt files). This module is a static dependency of
+//		the stub library module when [android/soong/android/config.BuildFromTextStub()] is true.
+//
+// - "framework-foo.stubs.exportable.<[apiScope.name]>" (type: [Library]): stub library module
+//		that provides the "exportable" stubs. "exportable" stubs are the stubs that do not
+//		include in-development flagged apis. This module is only used for SDK builds to generate
+//		the SDK artifacts, and not purposed for consumption for other modules.
+//
+// - "framework-foo.stubs.exportable.<[apiScope.name]>.from-source" (type: [Library]): stub
+//		library module that compiles the "exportable" stubs generated by the droidstubs
+//		submodule. This module is always a static dependency of the "exportable" stub library
+//		module given that from-text stubs cannot be used for SDK builds as it does not contain
+//		documentations.
+//
+// - "framework-foo.xml" (type: [sdkLibraryXml]): xml library that generates the permission xml
+//		file, which allows [SdkLibrary] to be used with <uses-permission> tag in the
+//		AndroidManifest.xml files.
+// ---------------------------------------------------------------------------------------------
+
+// Creates the implementation [Library] with ".impl" suffix.
+func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) {
+	visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility)
+
+	staticLibs := module.properties.Static_libs.Clone()
+	staticLibs.AppendSimpleValue(module.sdkLibraryProperties.Impl_only_static_libs)
+	props := struct {
+		Name           *string
+		Enabled        proptools.Configurable[bool]
+		Visibility     []string
+		Libs           []string
+		Static_libs    proptools.Configurable[[]string]
+		Apex_available []string
+		Stem           *string
+	}{
+		Name:       proptools.StringPtr(module.implLibraryModuleName()),
+		Enabled:    module.EnabledProperty(),
+		Visibility: visibility,
+
+		Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...),
+
+		Static_libs: staticLibs,
+		// Pass the apex_available settings down so that the impl library can be statically
+		// embedded within a library that is added to an APEX. Needed for updatable-media.
+		Apex_available: module.ApexAvailable(),
+
+		Stem: proptools.StringPtr(module.Name()),
+	}
+
+	properties := []interface{}{
+		&module.properties,
+		&module.protoProperties,
+		&module.deviceProperties,
+		&module.dexProperties,
+		&module.dexpreoptProperties,
+		&module.linter.properties,
+		&module.overridableProperties,
+		&props,
+		module.sdkComponentPropertiesForChildLibrary(),
+	}
+	mctx.CreateModule(LibraryFactory, properties...)
+}
+
+// Creates the [Droidstubs] module with ".stubs.source.<[apiScope.name]>" that creates stubs
+// source files from the given full source files and also updates and checks the API
+// specification files (i.e. "*-current.txt", "*-removed.txt" files).
+func (module *SdkLibrary) createDroidstubs(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) {
+	props := struct {
+		Name                             *string
+		Enabled                          proptools.Configurable[bool]
+		Visibility                       []string
+		Srcs                             []string
+		Installable                      *bool
+		Sdk_version                      *string
+		Api_surface                      *string
+		System_modules                   *string
+		Libs                             proptools.Configurable[[]string]
+		Output_javadoc_comments          *bool
+		Arg_files                        []string
+		Args                             *string
+		Java_version                     *string
+		Annotations_enabled              *bool
+		Merge_annotations_dirs           []string
+		Merge_inclusion_annotations_dirs []string
+		Generate_stubs                   *bool
+		Previous_api                     *string
+		Aconfig_declarations             []string
+		Check_api                        struct {
+			Current       ApiToCheck
+			Last_released ApiToCheck
+
+			Api_lint struct {
+				Enabled       *bool
+				New_since     *string
+				Baseline_file *string
+			}
+		}
+		Aidl struct {
+			Include_dirs       []string
+			Local_include_dirs []string
+		}
+		Dists []android.Dist
+	}{}
+
+	// The stubs source processing uses the same compile time classpath when extracting the
+	// API from the implementation library as it does when compiling it. i.e. the same
+	// * sdk version
+	// * system_modules
+	// * libs (static_libs/libs)
+
+	props.Name = proptools.StringPtr(name)
+	props.Enabled = module.EnabledProperty()
+	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility)
+	props.Srcs = append(props.Srcs, module.properties.Srcs...)
+	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
+	props.Sdk_version = module.deviceProperties.Sdk_version
+	props.Api_surface = &apiScope.name
+	props.System_modules = module.deviceProperties.System_modules
+	props.Installable = proptools.BoolPtr(false)
+	// A droiddoc module has only one Libs property and doesn't distinguish between
+	// shared libs and static libs. So we need to add both of these libs to Libs property.
+	props.Libs = proptools.NewConfigurable[[]string](nil, nil)
+	props.Libs.AppendSimpleValue(module.properties.Libs)
+	props.Libs.Append(module.properties.Static_libs)
+	props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
+	props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
+	props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs
+	props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs
+	props.Java_version = module.properties.Java_version
+
+	props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled
+	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
+	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
+	props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations
+
+	droidstubsArgs := []string{}
+	if len(module.sdkLibraryProperties.Api_packages) != 0 {
+		droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
+	}
+	droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
+	disabledWarnings := []string{"HiddenSuperclass"}
+	if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) {
+		disabledWarnings = append(disabledWarnings,
+			"BroadcastBehavior",
+			"DeprecationMismatch",
+			"MissingPermission",
+			"SdkConstant",
+			"Todo",
+		)
+	}
+	droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
+
+	// Output Javadoc comments for public scope.
+	if apiScope == apiScopePublic {
+		props.Output_javadoc_comments = proptools.BoolPtr(true)
+	}
+
+	// Add in scope specific arguments.
+	droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...)
+	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
+	props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " "))
+
+	// List of APIs identified from the provided source files are created. They are later
+	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
+	// last-released (a.k.a numbered) list of API.
+	currentApiFileName := apiScope.apiFilePrefix + "current.txt"
+	removedApiFileName := apiScope.apiFilePrefix + "removed.txt"
+	apiDir := module.getApiDir()
+	currentApiFileName = path.Join(apiDir, currentApiFileName)
+	removedApiFileName = path.Join(apiDir, removedApiFileName)
+
+	// check against the not-yet-release API
+	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
+	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
+
+	if module.compareAgainstLatestApi(apiScope) {
+		// check against the latest released API
+		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
+		props.Previous_api = latestApiFilegroupName
+		props.Check_api.Last_released.Api_file = latestApiFilegroupName
+		props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
+			module.latestRemovedApiFilegroupName(apiScope))
+		props.Check_api.Last_released.Baseline_file = proptools.StringPtr(
+			module.latestIncompatibilitiesFilegroupName(apiScope))
+
+		if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
+			// Enable api lint.
+			props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true)
+			props.Check_api.Api_lint.New_since = latestApiFilegroupName
+
+			// If it exists then pass a lint-baseline.txt through to droidstubs.
+			baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt")
+			baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath)
+			paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil)
+			if err != nil {
+				mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err)
+			}
+			if len(paths) == 1 {
+				props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath)
+			} else if len(paths) != 0 {
+				mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths)
+			}
+		}
+	}
+
+	if !Bool(module.sdkLibraryProperties.No_dist) {
+		// Dist the api txt and removed api txt artifacts for sdk builds.
+		distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
+		stubsTypeTagPrefix := ""
+		if mctx.Config().ReleaseHiddenApiExportableStubs() {
+			stubsTypeTagPrefix = ".exportable"
+		}
+		for _, p := range []struct {
+			tag     string
+			pattern string
+		}{
+			// "exportable" api files are copied to the dist directory instead of the
+			// "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag
+			// is set. Otherwise, the "everything" api files are copied to the dist directory.
+			{tag: "%s.api.txt", pattern: "%s.txt"},
+			{tag: "%s.removed-api.txt", pattern: "%s-removed.txt"},
+		} {
+			props.Dists = append(props.Dists, android.Dist{
+				Targets: []string{"sdk", "win_sdk"},
+				Dir:     distDir,
+				Dest:    proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())),
+				Tag:     proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)),
+			})
+		}
+	}
+
+	mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx)
+}
+
+type libraryProperties struct {
+	Name           *string
+	Enabled        proptools.Configurable[bool]
+	Visibility     []string
+	Srcs           []string
+	Installable    *bool
+	Sdk_version    *string
+	System_modules *string
+	Patch_module   *string
+	Libs           []string
+	Static_libs    []string
+	Compile_dex    *bool
+	Java_version   *string
+	Openjdk9       struct {
+		Srcs       []string
+		Javacflags []string
+	}
+	Dist struct {
+		Targets []string
+		Dest    *string
+		Dir     *string
+		Tag     *string
+	}
+	Is_stubs_module       *bool
+	Stub_contributing_api *string
+}
+
+func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties {
+	props := libraryProperties{}
+	props.Enabled = module.EnabledProperty()
+	props.Visibility = []string{"//visibility:override", "//visibility:private"}
+	// sources are generated from the droiddoc
+	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
+	props.Sdk_version = proptools.StringPtr(sdkVersion)
+	props.System_modules = module.deviceProperties.System_modules
+	props.Patch_module = module.properties.Patch_module
+	props.Installable = proptools.BoolPtr(false)
+	props.Libs = module.sdkLibraryProperties.Stub_only_libs
+	props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...)
+	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
+	// The stub-annotations library contains special versions of the annotations
+	// with CLASS retention policy, so that they're kept.
+	if proptools.Bool(module.sdkLibraryProperties.Annotations_enabled) {
+		props.Libs = append(props.Libs, "stub-annotations")
+	}
+	props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs
+	props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags
+	// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
+	// interop with older developer tools that don't support 1.9.
+	props.Java_version = proptools.StringPtr("1.8")
+	props.Is_stubs_module = proptools.BoolPtr(true)
+	props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
+
+	return props
+}
+
+// Creates the from-source stub [Library] with ".stubs.<[apiScope.name]>.from-source" suffix.
+func (module *SdkLibrary) createFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
+
+	props := module.stubsLibraryProps(mctx, apiScope)
+	props.Name = proptools.StringPtr(module.fromSourceStubsLibraryModuleName(apiScope))
+	props.Srcs = []string{":" + module.droidstubsModuleName(apiScope)}
+
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Creates the "exportable" from-source stub [Library] with
+// ".stubs.exportable.<[apiScope.name]>" suffix.
+func (module *SdkLibrary) createExportableFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
+	props := module.stubsLibraryProps(mctx, apiScope)
+	props.Name = proptools.StringPtr(module.exportableFromSourceStubsLibraryModuleName(apiScope))
+	props.Srcs = []string{":" + module.droidstubsModuleName(apiScope) + "{.exportable}"}
+
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Creates the from-text stub [ApiLibrary] with ".stubs.<[apiScope.name]>.from-text" suffix.
+func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
+	props := struct {
+		Name              *string
+		Enabled           proptools.Configurable[bool]
+		Visibility        []string
+		Api_contributions []string
+		Libs              proptools.Configurable[[]string]
+		Static_libs       []string
+		System_modules    *string
+		Enable_validation *bool
+		Stubs_type        *string
+		Sdk_version       *string
+		Previous_api      *string
+	}{}
+
+	props.Name = proptools.StringPtr(module.fromTextStubsLibraryModuleName(apiScope))
+	props.Enabled = module.EnabledProperty()
+	props.Visibility = []string{"//visibility:override", "//visibility:private"}
+
+	apiContributions := []string{}
+
+	// Api surfaces are not independent of each other, but have subset relationships,
+	// and so does the api files. To generate from-text stubs for api surfaces other than public,
+	// all subset api domains' api_contriubtions must be added as well.
+	scope := apiScope
+	for scope != nil {
+		apiContributions = append(apiContributions, module.droidstubsModuleName(scope)+".api.contribution")
+		scope = scope.extends
+	}
+	if apiScope == apiScopePublic {
+		additionalApiContribution := module.apiLibraryAdditionalApiContribution()
+		if additionalApiContribution != "" {
+			apiContributions = append(apiContributions, additionalApiContribution)
+		}
+	}
+
+	props.Api_contributions = apiContributions
+
+	// Ensure that stub-annotations is added to the classpath before any other libs
+	props.Libs = proptools.NewConfigurable[[]string](nil, nil)
+	props.Libs.AppendSimpleValue([]string{"stub-annotations"})
+	props.Libs.AppendSimpleValue(module.properties.Libs)
+	props.Libs.Append(module.properties.Static_libs)
+	props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
+	props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
+	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
+
+	props.System_modules = module.deviceProperties.System_modules
+	props.Enable_validation = proptools.BoolPtr(true)
+	props.Stubs_type = proptools.StringPtr("everything")
+
+	if module.deviceProperties.Sdk_version != nil {
+		props.Sdk_version = module.deviceProperties.Sdk_version
+	}
+
+	if module.compareAgainstLatestApi(apiScope) {
+		// check against the latest released API
+		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
+		props.Previous_api = latestApiFilegroupName
+	}
+
+	mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties {
+	props := libraryProperties{}
+
+	props.Enabled = module.EnabledProperty()
+	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
+	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
+	props.Sdk_version = proptools.StringPtr(sdkVersion)
+
+	props.System_modules = module.deviceProperties.System_modules
+
+	// The imports need to be compiled to dex if the java_sdk_library requests it.
+	compileDex := module.dexProperties.Compile_dex
+	if module.stubLibrariesCompiledForDex() {
+		compileDex = proptools.BoolPtr(true)
+	}
+	props.Compile_dex = compileDex
+
+	props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
+
+	if !Bool(module.sdkLibraryProperties.No_dist) && doDist {
+		props.Dist.Targets = []string{"sdk", "win_sdk"}
+		props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem()))
+		props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope))
+		props.Dist.Tag = proptools.StringPtr(".jar")
+	}
+	props.Is_stubs_module = proptools.BoolPtr(true)
+
+	return props
+}
+
+// Creates the stub [Library] with ".stubs.<[apiScope.name]>" suffix.
+func (module *SdkLibrary) createTopLevelStubsLibrary(
+	mctx android.DefaultableHookContext, apiScope *apiScope) {
+
+	// Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false
+	doDist := !mctx.Config().ReleaseHiddenApiExportableStubs()
+	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
+	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
+
+	// Add the stub compiling java_library/java_api_library as static lib based on build config
+	staticLib := module.fromSourceStubsLibraryModuleName(apiScope)
+	if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
+		staticLib = module.fromTextStubsLibraryModuleName(apiScope)
+	}
+	props.Static_libs = append(props.Static_libs, staticLib)
+
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Creates the "exportable" stub [Library] with ".stubs.exportable.<[apiScope.name]>" suffix.
+func (module *SdkLibrary) createTopLevelExportableStubsLibrary(
+	mctx android.DefaultableHookContext, apiScope *apiScope) {
+
+	// Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true
+	doDist := mctx.Config().ReleaseHiddenApiExportableStubs()
+	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
+	props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope))
+
+	staticLib := module.exportableFromSourceStubsLibraryModuleName(apiScope)
+	props.Static_libs = append(props.Static_libs, staticLib)
+
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Creates the [sdkLibraryXml] with ".xml" suffix.
+func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
+	moduleMinApiLevel := module.Library.MinSdkVersion(mctx)
+	var moduleMinApiLevelStr = moduleMinApiLevel.String()
+	if moduleMinApiLevel == android.NoneApiLevel {
+		moduleMinApiLevelStr = "current"
+	}
+	props := struct {
+		Name                      *string
+		Enabled                   proptools.Configurable[bool]
+		Lib_name                  *string
+		Apex_available            []string
+		On_bootclasspath_since    *string
+		On_bootclasspath_before   *string
+		Min_device_sdk            *string
+		Max_device_sdk            *string
+		Sdk_library_min_api_level *string
+		Uses_libs_dependencies    proptools.Configurable[[]string]
+	}{
+		Name:                      proptools.StringPtr(module.xmlPermissionsModuleName()),
+		Enabled:                   module.EnabledProperty(),
+		Lib_name:                  proptools.StringPtr(module.BaseModuleName()),
+		Apex_available:            module.ApexProperties.Apex_available,
+		On_bootclasspath_since:    module.commonSdkLibraryProperties.On_bootclasspath_since,
+		On_bootclasspath_before:   module.commonSdkLibraryProperties.On_bootclasspath_before,
+		Min_device_sdk:            module.commonSdkLibraryProperties.Min_device_sdk,
+		Max_device_sdk:            module.commonSdkLibraryProperties.Max_device_sdk,
+		Sdk_library_min_api_level: &moduleMinApiLevelStr,
+		Uses_libs_dependencies:    module.usesLibraryProperties.Uses_libs.Clone(),
+	}
+
+	mctx.CreateModule(sdkLibraryXmlFactory, &props)
+}
+
+// ---------------------------------------------------------------------------------------------
+// Build rules of the submodules generated by java_sdk_library_import.
+// Note that the java_sdk_library_import module does not generate the implementation library.
+// Instead, it will create a dependency to the source implemenetation library if one exists.
+// java_sdk_library_import "framework-foo" generates the following submodules:
+//
+// - "framework-foo.stubs.<[apiScope.name]>" (type: [Import]): prebuilt stub library module that
+//		provides the stub jar file checked in the tree.
+//
+// - "framework-foo.stubs.source.<[apiScope.name]>" (type: [PrebuiltStubsSources]): prebuilt
+//		droidstubs module that provides the stub source jar file checked in the tree.
+//
+// - "framework-foo.stubs.source.<[apiScope.name]>.api.contribution"
+//		(type [JavaApiContributionImport]): prebuilt java_api_contribution module that provides
+//		the prebuilt api file for previously released from-text stub generation.
+// ---------------------------------------------------------------------------------------------
+
+// Creates the prebuilt stub [Import] with ".stubs.<[apiScope.name]>" suffix.
+func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
+	// Creates a java import for the jar with ".stubs" suffix
+	props := struct {
+		Name                             *string
+		Source_module_name               *string
+		Created_by_java_sdk_library_name *string
+		Sdk_version                      *string
+		Libs                             []string
+		Jars                             []string
+		Compile_dex                      *bool
+		Is_stubs_module                  *bool
+
+		android.UserSuppliedPrebuiltProperties
+	}{}
+	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
+	props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName()))
+	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
+	props.Sdk_version = scopeProperties.Sdk_version
+	// Prepend any of the libs from the legacy public properties to the libs for each of the
+	// scopes to avoid having to duplicate them in each scope.
+	props.Libs = append(module.properties.Libs, scopeProperties.Libs...)
+	props.Jars = scopeProperties.Jars
+
+	// The imports are preferred if the java_sdk_library_import is preferred.
+	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
+
+	// The imports need to be compiled to dex if the java_sdk_library_import requests it.
+	compileDex := module.properties.Compile_dex
+	if module.stubLibrariesCompiledForDex() {
+		compileDex = proptools.BoolPtr(true)
+	}
+	props.Compile_dex = compileDex
+	props.Is_stubs_module = proptools.BoolPtr(true)
+
+	mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
+	props := struct {
+		Name                             *string
+		Source_module_name               *string
+		Created_by_java_sdk_library_name *string
+		Srcs                             []string
+
+		android.UserSuppliedPrebuiltProperties
+	}{}
+	props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope))
+	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()))
+	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
+	props.Srcs = scopeProperties.Stub_srcs
+
+	// The stubs source is preferred if the java_sdk_library_import is preferred.
+	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
+
+	mctx.CreateModule(PrebuiltStubsSourcesFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// Creates the prebuilt api contribution [JavaApiContributionImport] with
+// ".stubs.source.<[apiScope.name]>.api.contribution" suffix.
+func (module *SdkLibraryImport) createPrebuiltApiContribution(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
+	api_file := scopeProperties.Current_api
+	api_surface := &apiScope.name
+
+	props := struct {
+		Name                             *string
+		Source_module_name               *string
+		Created_by_java_sdk_library_name *string
+		Api_surface                      *string
+		Api_file                         *string
+		Visibility                       []string
+	}{}
+
+	props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope) + ".api.contribution")
+	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution")
+	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
+	props.Api_surface = api_surface
+	props.Api_file = api_file
+	props.Visibility = []string{"//visibility:override", "//visibility:public"}
+
+	mctx.CreateModule(ApiContributionImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
+}
+
+// ---------------------------------------------------------------------------------------------
+// End of the build rules of the submodules generated by java_sdk_library_import.
+// ---------------------------------------------------------------------------------------------
+
+// Definition of the [sdkLibraryXml] module. The module generates the permissions xml file,
+// so that the apps can specify the java_sdk_library using <uses-permission> tag in the
+// AndroidManifest.xml file.
+type sdkLibraryXml struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	android.ApexModuleBase
+
+	properties sdkLibraryXmlProperties
+
+	outputFilePath android.OutputPath
+	installDirPath android.InstallPath
+
+	hideApexVariantFromMake bool
+}
+
+type sdkLibraryXmlProperties struct {
+	// canonical name of the lib
+	Lib_name *string
+
+	// Signals that this shared library is part of the bootclasspath starting
+	// on the version indicated in this attribute.
+	//
+	// This will make platforms at this level and above to ignore
+	// <uses-library> tags with this library name because the library is already
+	// available
+	On_bootclasspath_since *string
+
+	// Signals that this shared library was part of the bootclasspath before
+	// (but not including) the version indicated in this attribute.
+	//
+	// The system will automatically add a <uses-library> tag with this library to
+	// apps that target any SDK less than the version indicated in this attribute.
+	On_bootclasspath_before *string
+
+	// Indicates that PackageManager should ignore this shared library if the
+	// platform is below the version indicated in this attribute.
+	//
+	// This means that the device won't recognise this library as installed.
+	Min_device_sdk *string
+
+	// Indicates that PackageManager should ignore this shared library if the
+	// platform is above the version indicated in this attribute.
+	//
+	// This means that the device won't recognise this library as installed.
+	Max_device_sdk *string
+
+	// The SdkLibrary's min api level as a string
+	//
+	// This value comes from the ApiLevel of the MinSdkVersion property.
+	Sdk_library_min_api_level *string
+
+	// Uses-libs dependencies that the shared library requires to work correctly.
+	//
+	// This will add dependency="foo:bar" to the <library> section.
+	Uses_libs_dependencies proptools.Configurable[[]string]
+}
+
+// java_sdk_library_xml builds the permission xml file for a java_sdk_library.
+// Not to be used directly by users. java_sdk_library internally uses this.
+func sdkLibraryXmlFactory() android.Module {
+	module := &sdkLibraryXml{}
+
+	module.AddProperties(&module.properties)
+
+	android.InitApexModule(module)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+	return module
+}
+
+func (module *sdkLibraryXml) UniqueApexVariations() bool {
+	// sdkLibraryXml needs a unique variation per APEX because the generated XML file contains the path to the
+	// mounted APEX, which contains the name of the APEX.
+	return true
+}
+
+// from android.PrebuiltEtcModule
+func (module *sdkLibraryXml) BaseDir() string {
+	return "etc"
+}
+
+// from android.PrebuiltEtcModule
+func (module *sdkLibraryXml) SubDir() string {
+	return "permissions"
+}
+
+var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil)
+
+// from android.ApexModule
+func (module *sdkLibraryXml) AvailableFor(what string) bool {
+	return true
+}
+
+func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// do nothing
+}
+
+var _ android.ApexModule = (*sdkLibraryXml)(nil)
+
+// Implements android.ApexModule
+func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+	sdkVersion android.ApiLevel) error {
+	// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
+	return nil
+}
+
+// File path to the runtime implementation library
+func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string {
+	implName := proptools.String(module.properties.Lib_name)
+	if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() {
+		// TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name.
+		// In most cases, this works fine. But when apex_name is set or override_apex is used
+		// this can be wrong.
+		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName)
+	}
+	partition := "system"
+	if module.SocSpecific() {
+		partition = "vendor"
+	} else if module.DeviceSpecific() {
+		partition = "odm"
+	} else if module.ProductSpecific() {
+		partition = "product"
+	} else if module.SystemExtSpecific() {
+		partition = "system_ext"
+	}
+	return "/" + partition + "/framework/" + implName + ".jar"
+}
+
+func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string {
+	if value == nil {
+		return ""
+	}
+	apiLevel, err := android.ApiLevelFromUser(ctx, *value)
+	if err != nil {
+		// attributes in bp files have underscores but in the xml have dashes.
+		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
+		return ""
+	}
+	if apiLevel.IsCurrent() {
+		// passing "current" would always mean a future release, never the current (or the current in
+		// progress) which means some conditions would never be triggered.
+		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
+			`"current" is not an allowed value for this attribute`)
+		return ""
+	}
+	// "safeValue" is safe because it translates finalized codenames to a string
+	// with their SDK int.
+	safeValue := apiLevel.String()
+	return formattedOptionalAttribute(attrName, &safeValue)
+}
+
+// formats an attribute for the xml permissions file if the value is not null
+// returns empty string otherwise
+func formattedOptionalAttribute(attrName string, value *string) string {
+	if value == nil {
+		return ""
+	}
+	return fmt.Sprintf("        %s=\"%s\"\n", attrName, *value)
+}
+
+func formattedDependenciesAttribute(dependencies []string) string {
+	if dependencies == nil {
+		return ""
+	}
+	return fmt.Sprintf("        dependency=\"%s\"\n", strings.Join(dependencies, ":"))
+}
+
+func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string {
+	libName := proptools.String(module.properties.Lib_name)
+	libNameAttr := formattedOptionalAttribute("name", &libName)
+	filePath := module.implPath(ctx)
+	filePathAttr := formattedOptionalAttribute("file", &filePath)
+	implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since)
+	implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before)
+	minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk)
+	maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk)
+	dependenciesAttr := formattedDependenciesAttribute(module.properties.Uses_libs_dependencies.GetOrDefault(ctx, nil))
+	// <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that).
+	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T
+	var libraryTag string
+	if module.properties.Min_device_sdk != nil {
+		libraryTag = "    <apex-library\n"
+	} else {
+		libraryTag = "    <library\n"
+	}
+
+	return strings.Join([]string{
+		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
+		"<!-- Copyright (C) 2018 The Android Open Source Project\n",
+		"\n",
+		"    Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+		"    you may not use this file except in compliance with the License.\n",
+		"    You may obtain a copy of the License at\n",
+		"\n",
+		"        http://www.apache.org/licenses/LICENSE-2.0\n",
+		"\n",
+		"    Unless required by applicable law or agreed to in writing, software\n",
+		"    distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+		"    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+		"    See the License for the specific language governing permissions and\n",
+		"    limitations under the License.\n",
+		"-->\n",
+		"<permissions>\n",
+		libraryTag,
+		libNameAttr,
+		filePathAttr,
+		implicitFromAttr,
+		implicitUntilAttr,
+		minSdkAttr,
+		maxSdkAttr,
+		dependenciesAttr,
+		"    />\n",
+		"</permissions>\n",
+	}, "")
+}
+
+func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	module.hideApexVariantFromMake = !apexInfo.IsForPlatform()
+
+	libName := proptools.String(module.properties.Lib_name)
+	module.selfValidate(ctx)
+	xmlContent := module.permissionsContents(ctx)
+
+	module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
+	android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent)
+
+	module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
+	ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
+
+	ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "")
+}
+
+func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
+	if module.hideApexVariantFromMake {
+		return []android.AndroidMkEntries{{
+			Disabled: true,
+		}}
+	}
+
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(module.outputFilePath),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_TAGS", "optional")
+				entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
+			},
+		},
+	}}
+}
+
+func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) {
+	module.validateAtLeastTAttributes(ctx)
+	module.validateMinAndMaxDeviceSdk(ctx)
+	module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx)
+	module.validateOnBootclasspathBeforeRequirements(ctx)
+}
+
+func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) {
+	t := android.ApiLevelOrPanic(ctx, "Tiramisu")
+	module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk")
+	module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk")
+	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before")
+	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since")
+}
+
+func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) {
+	if attr != nil {
+		if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil {
+			// we will inform the user of invalid inputs when we try to write the
+			// permissions xml file so we don't need to do it here
+			if t.GreaterThan(level) {
+				ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T")
+			}
+		}
+	}
+}
+
+func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) {
+	if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil {
+		min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
+		max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
+		if minErr == nil && maxErr == nil {
+			// we will inform the user of invalid inputs when we try to write the
+			// permissions xml file so we don't need to do it here
+			if min.GreaterThan(max) {
+				ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk")
+			}
+		}
+	}
+}
+
+func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) {
+	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
+	if module.properties.Min_device_sdk != nil {
+		api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
+		if err == nil {
+			if moduleMinApi.GreaterThan(api) {
+				ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
+			}
+		}
+	}
+	if module.properties.Max_device_sdk != nil {
+		api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
+		if err == nil {
+			if moduleMinApi.GreaterThan(api) {
+				ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
+			}
+		}
+	}
+}
+
+func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) {
+	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
+	if module.properties.On_bootclasspath_before != nil {
+		t := android.ApiLevelOrPanic(ctx, "Tiramisu")
+		// if we use the attribute, then we need to do this validation
+		if moduleMinApi.LessThan(t) {
+			// if minAPi is < T, then we need to have min_device_sdk (which only accepts T+)
+			if module.properties.Min_device_sdk == nil {
+				ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T")
+			}
+		}
+	}
+}
diff --git a/java/system_modules.go b/java/system_modules.go
index d9430b2..e955aec 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -19,6 +19,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -129,7 +130,7 @@
 	OutputDirDeps android.Paths
 
 	// depset of header jars for this module and all transitive static dependencies
-	TransitiveStaticLibsHeaderJars *android.DepSet[android.Path]
+	TransitiveStaticLibsHeaderJars depset.DepSet[android.Path]
 }
 
 var SystemModulesProvider = blueprint.NewProvider[*SystemModulesProviderInfo]()
@@ -152,13 +153,11 @@
 func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	var jars android.Paths
 
-	var transitiveStaticLibsHeaderJars []*android.DepSet[android.Path]
+	var transitiveStaticLibsHeaderJars []depset.DepSet[android.Path]
 	ctx.VisitDirectDepsWithTag(systemModulesLibsTag, func(module android.Module) {
 		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			jars = append(jars, dep.HeaderJars...)
-			if dep.TransitiveStaticLibsHeaderJars != nil {
-				transitiveStaticLibsHeaderJars = append(transitiveStaticLibsHeaderJars, dep.TransitiveStaticLibsHeaderJars)
-			}
+			transitiveStaticLibsHeaderJars = append(transitiveStaticLibsHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 		}
 	})
 
@@ -168,7 +167,7 @@
 		HeaderJars:                     jars,
 		OutputDir:                      system.outputDir,
 		OutputDirDeps:                  system.outputDeps,
-		TransitiveStaticLibsHeaderJars: android.NewDepSet(android.PREORDER, nil, transitiveStaticLibsHeaderJars),
+		TransitiveStaticLibsHeaderJars: depset.New(depset.PREORDER, nil, transitiveStaticLibsHeaderJars),
 	})
 }
 
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 924abd4..3176ad9 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -19,6 +19,7 @@
 	"android/soong/dexpreopt"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -98,12 +99,12 @@
 	// List of system_server classpath jars, could be either java_library, or java_sdk_library.
 	//
 	// The order of this list matters as it is the order that is used in the SYSTEMSERVERCLASSPATH.
-	Contents []string
+	Contents proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// List of jars that system_server loads dynamically using separate classloaders.
 	//
 	// The order does not matter.
-	Standalone_contents []string
+	Standalone_contents proptools.Configurable[[]string] `android:"arch_variant"`
 }
 
 func systemServerClasspathFactory() android.Module {
@@ -116,7 +117,7 @@
 }
 
 func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if len(s.properties.Contents) == 0 && len(s.properties.Standalone_contents) == 0 {
+	if len(s.properties.Contents.GetOrDefault(ctx, nil)) == 0 && len(s.properties.Standalone_contents.GetOrDefault(ctx, nil)) == 0 {
 		ctx.PropertyErrorf("contents", "Either contents or standalone_contents needs to be non-empty")
 	}
 
@@ -127,12 +128,32 @@
 	configuredJars = configuredJars.AppendList(&standaloneConfiguredJars)
 	classpathJars = append(classpathJars, standaloneClasspathJars...)
 	s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
+	s.setPartitionInfoOfLibraries(ctx)
+}
+
+// Map of java library name to their install partition.
+type LibraryNameToPartitionInfo struct {
+	LibraryNameToPartition map[string]string
+}
+
+// LibraryNameToPartitionInfoProvider will be used by the top-level apex to enforce that dexpreopt files
+// of apex system server jars are installed in the same partition as the top-level apex.
+var LibraryNameToPartitionInfoProvider = blueprint.NewProvider[LibraryNameToPartitionInfo]()
+
+func (s *SystemServerClasspathModule) setPartitionInfoOfLibraries(ctx android.ModuleContext) {
+	libraryNameToPartition := map[string]string{}
+	ctx.VisitDirectDepsWithTag(systemServerClasspathFragmentContentDepTag, func(m android.Module) {
+		libraryNameToPartition[m.Name()] = m.PartitionTag(ctx.DeviceConfig())
+	})
+	android.SetProvider(ctx, LibraryNameToPartitionInfoProvider, LibraryNameToPartitionInfo{
+		LibraryNameToPartition: libraryNameToPartition,
+	})
 }
 
 func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Contents, systemServerClasspathFragmentContentDepTag)
+	possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Contents.GetOrDefault(ctx, nil), systemServerClasspathFragmentContentDepTag)
 	jars, unknown := global.ApexSystemServerJars.Filter(possibleUpdatableModules)
 	// TODO(satayev): remove geotz ssc_fragment, since geotz is not part of SSCP anymore.
 	_, unknown = android.RemoveFromList("geotz", unknown)
@@ -164,7 +185,7 @@
 func (s *SystemServerClasspathModule) standaloneConfiguredJars(ctx android.ModuleContext) android.ConfiguredJarList {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Standalone_contents, systemServerClasspathFragmentContentDepTag)
+	possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Standalone_contents.GetOrDefault(ctx, nil), systemServerClasspathFragmentContentDepTag)
 	jars, _ := global.ApexStandaloneSystemServerJars.Filter(possibleUpdatableModules)
 
 	// TODO(jiakaiz): add a check to ensure that the contents are declared in make.
@@ -197,16 +218,11 @@
 	return true
 }
 
-// Contents of system server fragments in an apex are considered to be directly in the apex, as if
-// they were listed in java_libs.
-func (systemServerClasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {}
-
 // Contents of system server fragments require files from prebuilt apex files.
 func (systemServerClasspathFragmentContentDependencyTag) RequiresFilesFromPrebuiltApex() {}
 
 var _ android.ReplaceSourceWithPrebuilt = systemServerClasspathFragmentContentDepTag
 var _ android.SdkMemberDependencyTag = systemServerClasspathFragmentContentDepTag
-var _ android.CopyDirectlyInAnyApexTag = systemServerClasspathFragmentContentDepTag
 var _ android.RequiresFilesFromPrebuiltApexTag = systemServerClasspathFragmentContentDepTag
 
 // The tag used for the dependency between the systemserverclasspath_fragment module and its contents.
@@ -225,8 +241,8 @@
 	module := ctx.Module()
 	_, isSourceModule := module.(*SystemServerClasspathModule)
 	var deps []string
-	deps = append(deps, s.properties.Contents...)
-	deps = append(deps, s.properties.Standalone_contents...)
+	deps = append(deps, s.properties.Contents.GetOrDefault(ctx, nil)...)
+	deps = append(deps, s.properties.Standalone_contents.GetOrDefault(ctx, nil)...)
 
 	for _, name := range deps {
 		// A systemserverclasspath_fragment must depend only on other source modules, while the
@@ -240,8 +256,8 @@
 
 // Collect information for opening IDE project files in java/jdeps.go.
 func (s *SystemServerClasspathModule) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	dpInfo.Deps = append(dpInfo.Deps, s.properties.Contents...)
-	dpInfo.Deps = append(dpInfo.Deps, s.properties.Standalone_contents...)
+	dpInfo.Deps = append(dpInfo.Deps, s.properties.Contents.GetOrDefault(ctx, nil)...)
+	dpInfo.Deps = append(dpInfo.Deps, s.properties.Standalone_contents.GetOrDefault(ctx, nil)...)
 }
 
 type systemServerClasspathFragmentMemberType struct {
@@ -282,8 +298,8 @@
 func (s *systemServerClasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
 	module := variant.(*SystemServerClasspathModule)
 
-	s.Contents = module.properties.Contents
-	s.Standalone_contents = module.properties.Standalone_contents
+	s.Contents = module.properties.Contents.GetOrDefault(ctx.SdkModuleContext(), nil)
+	s.Standalone_contents = module.properties.Standalone_contents.GetOrDefault(ctx.SdkModuleContext(), nil)
 }
 
 func (s *systemServerClasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
diff --git a/java/test_spec_test.go b/java/test_spec_test.go
deleted file mode 100644
index f0a5fdb..0000000
--- a/java/test_spec_test.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package java
-
-import (
-	"strings"
-	"testing"
-
-	"android/soong/android"
-	soongTesting "android/soong/testing"
-	"android/soong/testing/test_spec_proto"
-	"google.golang.org/protobuf/proto"
-)
-
-func TestTestSpec(t *testing.T) {
-	bp := `test_spec {
-		name: "module-name",
-		teamId: "12345",
-		tests: [
-			"java-test-module-name-one",
-			"java-test-module-name-two"
-		]
-	}
-
-	java_test {
-		name: "java-test-module-name-one",
-	}
-
-	java_test {
-		name: "java-test-module-name-two",
-	}`
-	result := runTestSpecTest(t, android.FixtureExpectsNoErrors, bp)
-
-	module := result.ModuleForTests("module-name", "")
-
-	// Check that the provider has the right contents
-	data, _ := android.OtherModuleProvider(result, module.Module(), soongTesting.TestSpecProviderKey)
-	if !strings.HasSuffix(
-		data.IntermediatePath.String(), "/intermediateTestSpecMetadata.pb",
-	) {
-		t.Errorf(
-			"Missing intermediates path in provider: %s",
-			data.IntermediatePath.String(),
-		)
-	}
-
-	metadata := android.ContentFromFileRuleForTests(t, result.TestContext,
-		module.Output(data.IntermediatePath.String()))
-
-	metadataList := make([]*test_spec_proto.TestSpec_OwnershipMetadata, 0, 2)
-	teamId := "12345"
-	bpFilePath := "Android.bp"
-	targetNames := []string{
-		"java-test-module-name-one", "java-test-module-name-two",
-	}
-
-	for _, test := range targetNames {
-		targetName := test
-		metadata := test_spec_proto.TestSpec_OwnershipMetadata{
-			TrendyTeamId: &teamId,
-			TargetName:   &targetName,
-			Path:         &bpFilePath,
-		}
-		metadataList = append(metadataList, &metadata)
-	}
-	testSpecMetadata := test_spec_proto.TestSpec{OwnershipMetadataList: metadataList}
-	protoData, _ := proto.Marshal(&testSpecMetadata)
-	expectedMetadata := string(protoData)
-
-	if metadata != expectedMetadata {
-		t.Errorf(
-			"Retrieved metadata: %s doesn't contain expectedMetadata: %s", metadata,
-			expectedMetadata,
-		)
-	}
-
-	// Tests for all_test_spec singleton.
-	singleton := result.SingletonForTests("all_test_specs")
-	rule := singleton.Rule("all_test_specs_rule")
-	prebuiltOs := result.Config.PrebuiltOS()
-	expectedCmd := "out/soong/host/" + prebuiltOs + "/bin/metadata -rule test_spec -inputFile out/soong/all_test_spec_paths.rsp -outputFile out/soong/ownership/all_test_specs.pb"
-	expectedOutputFile := "out/soong/ownership/all_test_specs.pb"
-	expectedInputFile := "out/soong/.intermediates/module-name/intermediateTestSpecMetadata.pb"
-	if !strings.Contains(
-		strings.TrimSpace(rule.Output.String()),
-		expectedOutputFile,
-	) {
-		t.Errorf(
-			"Retrieved singletonOutputFile: %s is not equal to expectedSingletonOutputFile: %s",
-			rule.Output.String(), expectedOutputFile,
-		)
-	}
-
-	if !strings.Contains(
-		strings.TrimSpace(rule.Inputs[0].String()),
-		expectedInputFile,
-	) {
-		t.Errorf(
-			"Retrieved singletonInputFile: %s is not equal to expectedSingletonInputFile: %s",
-			rule.Inputs[0].String(), expectedInputFile,
-		)
-	}
-
-	if !strings.Contains(
-		strings.TrimSpace(rule.RuleParams.Command),
-		expectedCmd,
-	) {
-		t.Errorf(
-			"Retrieved cmd: %s is not equal to expectedCmd: %s",
-			rule.RuleParams.Command, expectedCmd,
-		)
-	}
-}
-
-func runTestSpecTest(
-	t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
-) *android.TestResult {
-	return android.GroupFixturePreparers(
-		soongTesting.PrepareForTestWithTestingBuildComponents,
-		PrepareForIntegrationTestWithJava,
-	).
-		ExtendWithErrorHandler(errorHandler).
-		RunTestWithBp(t, bp)
-}
diff --git a/java/testing.go b/java/testing.go
index 700314f..ba33d90 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -30,6 +30,7 @@
 )
 
 const defaultJavaDir = "default/java"
+const testDefaultUpdatableModuleVersion = "340090000"
 
 // Test fixture preparer that will register most java build components.
 //
@@ -61,6 +62,7 @@
 		// Needed for the global lint checks provided from frameworks/base
 		"prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar": nil,
 	}.AddToFixture(),
+	android.PrepareForTestWithBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION", testDefaultUpdatableModuleVersion),
 )
 
 var prepareForTestWithFrameworkDeps = android.GroupFixturePreparers(
@@ -188,6 +190,7 @@
 				"//apex_available:anyapex",
 				"//apex_available:platform",
 			],
+			compile_dex: true,
 		}
 	`)),
 )
@@ -388,7 +391,6 @@
 	RegisterStubsBuildComponents(ctx)
 	RegisterSystemModulesBuildComponents(ctx)
 	registerSystemserverClasspathBuildComponents(ctx)
-	registerLintBuildComponents(ctx)
 	android.RegisterApexContributionsBuildComponents(ctx)
 }
 
@@ -425,7 +427,7 @@
 		"kotlin-annotations",
 		"stub-annotations",
 		"aconfig-annotations-lib",
-		"aconfig_storage_reader_java",
+		"aconfig_storage_stub",
 		"unsupportedappusage",
 	}
 
diff --git a/kernel/prebuilt_kernel_modules.go b/kernel/prebuilt_kernel_modules.go
index e200ee2..001a1e7 100644
--- a/kernel/prebuilt_kernel_modules.go
+++ b/kernel/prebuilt_kernel_modules.go
@@ -32,7 +32,7 @@
 }
 
 func registerKernelBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory)
+	ctx.RegisterModuleType("prebuilt_kernel_modules", PrebuiltKernelModulesFactory)
 }
 
 type prebuiltKernelModules struct {
@@ -47,6 +47,20 @@
 	// List or filegroup of prebuilt kernel module files. Should have .ko suffix.
 	Srcs []string `android:"path,arch_variant"`
 
+	// List of system_dlkm kernel modules that the local kernel modules depend on.
+	// The deps will be assembled into intermediates directory for running depmod
+	// but will not be added to the current module's installed files.
+	System_deps []string `android:"path,arch_variant"`
+
+	// If false, then srcs will not be included in modules.load.
+	// This feature is used by system_dlkm
+	Load_by_default *bool
+
+	Blocklist_file *string `android:"path"`
+
+	// Path to the kernel module options file
+	Options_file *string `android:"path"`
+
 	// Kernel version that these modules are for. Kernel modules are installed to
 	// /lib/modules/<kernel_version> directory in the corresponding partition. Default is "".
 	Kernel_version *string
@@ -58,7 +72,7 @@
 // prebuilt_kernel_modules installs a set of prebuilt kernel module files to the correct directory.
 // In addition, this module builds modules.load, modules.dep, modules.softdep and modules.alias
 // using depmod and installs them as well.
-func prebuiltKernelModulesFactory() android.Module {
+func PrebuiltKernelModulesFactory() android.Module {
 	module := &prebuiltKernelModules{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
@@ -81,12 +95,21 @@
 	if !pkm.installable() {
 		pkm.SkipInstall()
 	}
-	modules := android.PathsForModuleSrc(ctx, pkm.properties.Srcs)
 
-	depmodOut := runDepmod(ctx, modules)
+	modules := android.PathsForModuleSrc(ctx, pkm.properties.Srcs)
+	systemModules := android.PathsForModuleSrc(ctx, pkm.properties.System_deps)
+
+	depmodOut := pkm.runDepmod(ctx, modules, systemModules)
 	strippedModules := stripDebugSymbols(ctx, modules)
 
 	installDir := android.PathForModuleInstall(ctx, "lib", "modules")
+	// Kernel module is installed to vendor_ramdisk/lib/modules regardless of product
+	// configuration. This matches the behavior in make and prevents the files from being
+	// installed in `vendor_ramdisk/first_stage_ramdisk`.
+	if pkm.InstallInVendorRamdisk() {
+		installDir = android.PathForModuleInPartitionInstall(ctx, "vendor_ramdisk", "lib", "modules")
+	}
+
 	if pkm.KernelVersion() != "" {
 		installDir = installDir.Join(ctx, pkm.KernelVersion())
 	}
@@ -98,6 +121,38 @@
 	ctx.InstallFile(installDir, "modules.dep", depmodOut.modulesDep)
 	ctx.InstallFile(installDir, "modules.softdep", depmodOut.modulesSoftdep)
 	ctx.InstallFile(installDir, "modules.alias", depmodOut.modulesAlias)
+	pkm.installBlocklistFile(ctx, installDir)
+	pkm.installOptionsFile(ctx, installDir)
+
+	ctx.SetOutputFiles(modules, ".modules")
+}
+
+func (pkm *prebuiltKernelModules) installBlocklistFile(ctx android.ModuleContext, installDir android.InstallPath) {
+	if pkm.properties.Blocklist_file == nil {
+		return
+	}
+	blocklistOut := android.PathForModuleOut(ctx, "modules.blocklist")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   processBlocklistFile,
+		Input:  android.PathForModuleSrc(ctx, proptools.String(pkm.properties.Blocklist_file)),
+		Output: blocklistOut,
+	})
+	ctx.InstallFile(installDir, "modules.blocklist", blocklistOut)
+}
+
+func (pkm *prebuiltKernelModules) installOptionsFile(ctx android.ModuleContext, installDir android.InstallPath) {
+	if pkm.properties.Options_file == nil {
+		return
+	}
+	optionsOut := android.PathForModuleOut(ctx, "modules.options")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   processOptionsFile,
+		Input:  android.PathForModuleSrc(ctx, proptools.String(pkm.properties.Options_file)),
+		Output: optionsOut,
+	})
+	ctx.InstallFile(installDir, "modules.options", optionsOut)
 }
 
 var (
@@ -137,35 +192,106 @@
 	modulesAlias   android.OutputPath
 }
 
-func runDepmod(ctx android.ModuleContext, modules android.Paths) depmodOutputs {
+var (
+	// system/lib/modules/foo.ko: system/lib/modules/bar.ko
+	// will be converted to
+	// /system/lib/modules/foo.ko: /system/lib/modules/bar.ko
+	addLeadingSlashToPaths = pctx.AndroidStaticRule("add_leading_slash",
+		blueprint.RuleParams{
+			Command: `sed -e 's|\([^: ]*lib/modules/[^: ]*\)|/\1|g' $in > $out`,
+		},
+	)
+	// Remove empty lines. Raise an exception if line is _not_ formatted as `blocklist $name.ko`
+	processBlocklistFile = pctx.AndroidStaticRule("process_blocklist_file",
+		blueprint.RuleParams{
+			Command: `rm -rf $out && awk <$in > $out` +
+				` '/^#/ { print; next }` +
+				` NF == 0 { next }` +
+				` NF != 2 || $$1 != "blocklist"` +
+				` { print "Invalid blocklist line " FNR ": " $$0 >"/dev/stderr";` +
+				` exit_status = 1; next }` +
+				` { $$1 = $$1; print }` +
+				` END { exit exit_status }'`,
+		},
+	)
+	// Remove empty lines. Raise an exception if line is _not_ formatted as `options $name.ko`
+	processOptionsFile = pctx.AndroidStaticRule("process_options_file",
+		blueprint.RuleParams{
+			Command: `rm -rf $out && awk <$in > $out` +
+				` '/^#/ { print; next }` +
+				` NF == 0 { next }` +
+				` NF < 2 || $$1 != "options"` +
+				` { print "Invalid options line " FNR ": " $$0 >"/dev/stderr";` +
+				` exit_status = 1; next }` +
+				` { $$1 = $$1; print }` +
+				` END { exit exit_status }'`,
+		},
+	)
+)
+
+// This is the path in soong intermediates where the .ko files will be copied.
+// The layout should match the layout on device so that depmod can create meaningful modules.* files.
+func modulesDirForAndroidDlkm(ctx android.ModuleContext, modulesDir android.OutputPath, system bool) android.OutputPath {
+	if ctx.InstallInSystemDlkm() || system {
+		// The first component can be either system or system_dlkm
+		// system works because /system/lib/modules is a symlink to /system_dlkm/lib/modules.
+		// system was chosen to match the contents of the kati built modules.dep
+		return modulesDir.Join(ctx, "system", "lib", "modules")
+	} else if ctx.InstallInVendorDlkm() {
+		return modulesDir.Join(ctx, "vendor", "lib", "modules")
+	} else if ctx.InstallInOdmDlkm() {
+		return modulesDir.Join(ctx, "odm", "lib", "modules")
+	} else {
+		// not an android dlkm module.
+		return modulesDir
+	}
+}
+
+func (pkm *prebuiltKernelModules) runDepmod(ctx android.ModuleContext, modules android.Paths, systemModules android.Paths) depmodOutputs {
 	baseDir := android.PathForModuleOut(ctx, "depmod").OutputPath
 	fakeVer := "0.0" // depmod demands this anyway
 	modulesDir := baseDir.Join(ctx, "lib", "modules", fakeVer)
+	modulesCpDir := modulesDirForAndroidDlkm(ctx, modulesDir, false)
 
 	builder := android.NewRuleBuilder(pctx, ctx)
 
 	// Copy the module files to a temporary dir
-	builder.Command().Text("rm").Flag("-rf").Text(modulesDir.String())
-	builder.Command().Text("mkdir").Flag("-p").Text(modulesDir.String())
+	builder.Command().Text("rm").Flag("-rf").Text(modulesCpDir.String())
+	builder.Command().Text("mkdir").Flag("-p").Text(modulesCpDir.String())
 	for _, m := range modules {
-		builder.Command().Text("cp").Input(m).Text(modulesDir.String())
+		builder.Command().Text("cp").Input(m).Text(modulesCpDir.String())
+	}
+
+	modulesDirForSystemDlkm := modulesDirForAndroidDlkm(ctx, modulesDir, true)
+	if len(systemModules) > 0 {
+		builder.Command().Text("mkdir").Flag("-p").Text(modulesDirForSystemDlkm.String())
+	}
+	for _, m := range systemModules {
+		builder.Command().Text("cp").Input(m).Text(modulesDirForSystemDlkm.String())
 	}
 
 	// Enumerate modules to load
 	modulesLoad := modulesDir.Join(ctx, "modules.load")
-	var basenames []string
-	for _, m := range modules {
-		basenames = append(basenames, filepath.Base(m.String()))
+	// If Load_by_default is set to false explicitly, create an empty modules.load
+	if pkm.properties.Load_by_default != nil && !*pkm.properties.Load_by_default {
+		builder.Command().Text("rm").Flag("-rf").Text(modulesLoad.String())
+		builder.Command().Text("touch").Output(modulesLoad)
+	} else {
+		var basenames []string
+		for _, m := range modules {
+			basenames = append(basenames, filepath.Base(m.String()))
+		}
+		builder.Command().
+			Text("echo").Flag("\"" + strings.Join(basenames, " ") + "\"").
+			Text("|").Text("tr").Flag("\" \"").Flag("\"\\n\"").
+			Text(">").Output(modulesLoad)
 	}
-	builder.Command().
-		Text("echo").Flag("\"" + strings.Join(basenames, " ") + "\"").
-		Text("|").Text("tr").Flag("\" \"").Flag("\"\\n\"").
-		Text(">").Output(modulesLoad)
 
 	// Run depmod to build modules.dep/softdep/alias files
 	modulesDep := modulesDir.Join(ctx, "modules.dep")
 	modulesSoftdep := modulesDir.Join(ctx, "modules.softdep")
 	modulesAlias := modulesDir.Join(ctx, "modules.alias")
+	builder.Command().Text("mkdir").Flag("-p").Text(modulesDir.String())
 	builder.Command().
 		BuiltTool("depmod").
 		FlagWithArg("-b ", baseDir.String()).
@@ -176,5 +302,16 @@
 
 	builder.Build("depmod", fmt.Sprintf("depmod %s", ctx.ModuleName()))
 
-	return depmodOutputs{modulesLoad, modulesDep, modulesSoftdep, modulesAlias}
+	finalModulesDep := modulesDep
+	// Add a leading slash to paths in modules.dep of android dlkm
+	if ctx.InstallInSystemDlkm() || ctx.InstallInVendorDlkm() || ctx.InstallInOdmDlkm() {
+		finalModulesDep := modulesDep.ReplaceExtension(ctx, "intermediates")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   addLeadingSlashToPaths,
+			Input:  modulesDep,
+			Output: finalModulesDep,
+		})
+	}
+
+	return depmodOutputs{modulesLoad, finalModulesDep, modulesSoftdep, modulesAlias}
 }
diff --git a/licenses/Android.bp b/licenses/Android.bp
index e4e5da7..f420110 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -32,6 +32,17 @@
 }
 
 license_kind {
+    name: "BSD-Binary-Only",
+    conditions: [
+        "notice",
+        "by_exception_only",
+        "proprietary",
+    ],
+}
+
+// Deprecated. All users of the following license should be changed to
+// BSD-Binary-Only and it should be removed.
+license_kind {
     name: "BSD-Like-Binary-Only",
     conditions: [
         "notice",
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 05b99fd..d422871 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -77,7 +77,7 @@
 	output := android.PathForModuleOut(ctx, "linker.config.pb").OutputPath
 
 	builder := android.NewRuleBuilder(pctx, ctx)
-	BuildLinkerConfig(ctx, builder, input, nil, nil, output)
+	BuildLinkerConfig(ctx, builder, android.Paths{input}, nil, nil, output)
 	builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String())
 
 	l.outputFilePath = output
@@ -91,16 +91,18 @@
 }
 
 func BuildLinkerConfig(ctx android.ModuleContext, builder *android.RuleBuilder,
-	input android.Path, provideModules []android.Module, requireModules []android.Module, output android.OutputPath) {
+	inputs android.Paths, provideModules []android.Module, requireModules []android.Module, output android.OutputPath) {
 
 	// First, convert the input json to protobuf format
 	interimOutput := android.PathForModuleOut(ctx, "temp.pb")
-	builder.Command().
+	cmd := builder.Command().
 		BuiltTool("conv_linker_config").
 		Flag("proto").
-		Flag("--force").
-		FlagWithInput("-s ", input).
-		FlagWithOutput("-o ", interimOutput)
+		Flag("--force")
+	for _, input := range inputs {
+		cmd.FlagWithInput("-s ", input)
+	}
+	cmd.FlagWithOutput("-o ", interimOutput)
 
 	// Secondly, if there's provideLibs gathered from provideModules, append them
 	var provideLibs []string
diff --git a/provenance/provenance_singleton.go b/provenance/provenance_singleton.go
index 679632c..c372db2 100644
--- a/provenance/provenance_singleton.go
+++ b/provenance/provenance_singleton.go
@@ -46,7 +46,7 @@
 )
 
 type ProvenanceMetadata interface {
-	ProvenanceMetaDataFile() android.OutputPath
+	ProvenanceMetaDataFile() android.Path
 }
 
 func init() {
@@ -74,7 +74,7 @@
 			return false
 		}
 		if p, ok := module.(ProvenanceMetadata); ok {
-			return p.ProvenanceMetaDataFile().String() != ""
+			return p.ProvenanceMetaDataFile() != nil
 		}
 		return false
 	}
@@ -101,7 +101,7 @@
 	context.Phony("droidcore", android.PathForPhony(context, "provenance_metadata"))
 }
 
-func GenerateArtifactProvenanceMetaData(ctx android.ModuleContext, artifactPath android.Path, installedFile android.InstallPath) android.OutputPath {
+func GenerateArtifactProvenanceMetaData(ctx android.ModuleContext, artifactPath android.Path, installedFile android.InstallPath) android.Path {
 	onDevicePathOfInstalledFile := android.InstallPathToOnDevicePath(ctx, installedFile)
 	artifactMetaDataFile := android.PathForIntermediates(ctx, "provenance_metadata", ctx.ModuleDir(), ctx.ModuleName(), "provenance_metadata.textproto")
 	ctx.Build(pctx, android.BuildParams{
diff --git a/python/Android.bp b/python/Android.bp
index 14e83c1..3b54455 100644
--- a/python/Android.bp
+++ b/python/Android.bp
@@ -10,7 +10,6 @@
         "soong-android",
         "soong-tradefed",
         "soong-cc",
-        "soong-testing",
     ],
     srcs: [
         "binary.go",
diff --git a/python/python.go b/python/python.go
index 01ac86c..d3e5743 100644
--- a/python/python.go
+++ b/python/python.go
@@ -90,6 +90,11 @@
 	// the test. the file extension can be arbitrary except for (.py).
 	Data []string `android:"path,arch_variant"`
 
+	// Same as data, but will add dependencies on modules using the device's os variation and
+	// the common arch variation. Useful for a host test that wants to embed a module built for
+	// device.
+	Device_common_data []string `android:"path_device_common"`
+
 	// list of java modules that provide data that should be installed alongside the test.
 	Java_data []string
 
@@ -451,6 +456,7 @@
 
 	// expand data files from "data" property.
 	expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
+	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, p.properties.Device_common_data)...)
 
 	// Emulate the data property for java_data dependencies.
 	for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
diff --git a/python/test.go b/python/test.go
index 85decf9..9f57bea 100644
--- a/python/test.go
+++ b/python/test.go
@@ -17,8 +17,6 @@
 import (
 	"fmt"
 
-	"android/soong/testing"
-
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -65,6 +63,11 @@
 	// the test
 	Data []string `android:"path,arch_variant"`
 
+	// Same as data, but will add dependencies on modules using the device's os variation and
+	// the common arch variation. Useful for a host test that wants to embed a module built for
+	// device.
+	Device_common_data []string `android:"path_device_common"`
+
 	// list of java modules that provide data that should be installed alongside the test.
 	Java_data []string
 
@@ -183,6 +186,9 @@
 	for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) {
 		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
 	}
+	for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Device_common_data) {
+		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
+	}
 
 	if p.isTestHost() && len(p.testProperties.Data_device_bins_both) > 0 {
 		ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) {
@@ -200,8 +206,6 @@
 	installDir := installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName())
 	installedData := ctx.InstallTestData(installDir, p.data)
 	p.installedDest = ctx.InstallFile(installDir, p.installSource.Base(), p.installSource, installedData...)
-
-	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
 func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/root.bp b/root.bp
index 7e0c1ed..ee208a0 100644
--- a/root.bp
+++ b/root.bp
@@ -2,3 +2,12 @@
 // subdirs= and optional_subdirs= are obsolete and this file no longer
 // needs a list of the top level directories that may contain Android.bp
 // files.
+
+// TODO(b/253827323) Remove this. A module in internal builds needs to disable a new check,
+// IdentifierName, when errorprone is updated. In order to avoid having the update errorprone
+// in internal first, and then aosp, create this variable that we can fill out in internal in the
+// same topic as the errorprone update, then move the flag out of the variable after the update,
+// then remove the variable.
+disable_identifiername_for_errorprone_update = [
+    "-Xep:IdentifierName:OFF",
+]
diff --git a/rust/Android.bp b/rust/Android.bp
index 781f325..54ba9d4 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -12,7 +12,6 @@
         "soong-bloaty",
         "soong-cc",
         "soong-rust-config",
-        "soong-testing",
     ],
     srcs: [
         "afdo.go",
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 31aa137..898e792 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -186,7 +186,7 @@
 	// Default clang flags
 	cflags = append(cflags, "${cc_config.CommonGlobalCflags}")
 	if ctx.Device() {
-		cflags = append(cflags, "${cc_config.DeviceGlobalCflags}")
+		cflags = append(cflags, "${cc_config.DeviceGlobalCflags}", "-nostdlibinc")
 	}
 
 	// Toolchain clang flags
@@ -198,18 +198,20 @@
 		cflags = append(cflags, "-D__ANDROID_VNDK__")
 		if ctx.RustModule().InVendor() {
 			cflags = append(cflags, "-D__ANDROID_VENDOR__")
-
-			vendorApiLevel := ctx.Config().VendorApiLevel()
-			if vendorApiLevel == "" {
-				// TODO(b/314036847): This is a fallback for UDC targets.
-				// This must be a build failure when UDC is no longer built
-				// from this source tree.
-				vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
-			}
-			cflags = append(cflags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel)
 		} else if ctx.RustModule().InProduct() {
 			cflags = append(cflags, "-D__ANDROID_PRODUCT__")
 		}
+
+		// Define __ANDROID_VENDOR_API__ for both product and vendor variants
+		// because they both use the same LLNDK libraries.
+		vendorApiLevel := ctx.Config().VendorApiLevel()
+		if vendorApiLevel == "" {
+			// TODO(b/314036847): This is a fallback for UDC targets.
+			// This must be a build failure when UDC is no longer built
+			// from this source tree.
+			vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
+		}
+		cflags = append(cflags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel)
 	}
 
 	if ctx.RustModule().InRecovery() {
@@ -250,7 +252,7 @@
 
 	// Module defined clang flags and include paths
 	cflags = append(cflags, esc(cflagsProp)...)
-	for _, include := range b.ClangProperties.Local_include_dirs {
+	for _, include := range b.ClangProperties.Local_include_dirs.GetOrDefault(ctx, nil) {
 		cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String())
 		implicits = append(implicits, android.PathForModuleSrc(ctx, include))
 	}
@@ -298,6 +300,11 @@
 	// it cannot recognize. Turn off unknown warning flags warning.
 	cflags = append(cflags, "-Wno-unknown-warning-option")
 
+	// Suppress warnings while testing a new compiler.
+	if ctx.Config().IsEnvTrue("LLVM_NEXT") {
+		cflags = append(cflags, "-Wno-everything")
+	}
+
 	outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs")
 
 	var cmd, cmdDesc string
diff --git a/rust/builder.go b/rust/builder.go
index f469f56..a1e17fc 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -170,6 +170,9 @@
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib"))
 }
 
+// TransformRlibstoStaticlib is assumed to be called from the cc module, and
+// thus needs to reconstruct the common set of flags which need to be passed
+// to the rustc compiler.
 func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep,
 	outputFile android.WritablePath) android.Path {
 
diff --git a/rust/compiler.go b/rust/compiler.go
index a2546a1..fd86917 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -39,13 +39,13 @@
 	initialize(ctx ModuleContext)
 	compilerFlags(ctx ModuleContext, flags Flags) Flags
 	cfgFlags(ctx ModuleContext, flags Flags) Flags
-	featureFlags(ctx ModuleContext, flags Flags) Flags
+	featureFlags(ctx ModuleContext, module *Module, flags Flags) Flags
 	compilerProps() []interface{}
 	compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput
 	compilerDeps(ctx DepsContext, deps Deps) Deps
 	crateName() string
 	edition() string
-	features() []string
+	features(ctx android.ConfigurableEvaluatorContext, module *Module) []string
 	rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath
 	Thinlto() bool
 
@@ -154,7 +154,7 @@
 
 	// list of rust automatic crate dependencies.
 	// Rustlibs linkage is rlib for host targets and dylib for device targets.
-	Rustlibs []string `android:"arch_variant"`
+	Rustlibs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of rust proc_macro crate dependencies
 	Proc_macros []string `android:"arch_variant"`
@@ -194,7 +194,7 @@
 	Crate_name string `android:"arch_variant"`
 
 	// list of features to enable for this crate
-	Features []string `android:"arch_variant"`
+	Features proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of configuration options to enable for this crate. To enable features, use the "features" property.
 	Cfgs proptools.Configurable[[]string] `android:"arch_variant"`
@@ -346,22 +346,23 @@
 	return flags
 }
 
-func (compiler *baseCompiler) features() []string {
-	return compiler.Properties.Features
+func (compiler *baseCompiler) features(ctx android.ConfigurableEvaluatorContext, module *Module) []string {
+	eval := module.ConfigurableEvaluator(ctx)
+	return compiler.Properties.Features.GetOrDefault(eval, nil)
 }
 
-func (compiler *baseCompiler) featuresToFlags() []string {
+func (compiler *baseCompiler) featuresToFlags(ctx android.ConfigurableEvaluatorContext, module *Module) []string {
 	flags := []string{}
-	for _, feature := range compiler.features() {
+	for _, feature := range compiler.features(ctx, module) {
 		flags = append(flags, "--cfg 'feature=\""+feature+"\"'")
 	}
 
 	return flags
 }
 
-func (compiler *baseCompiler) featureFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags()...)
-	flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags()...)
+func (compiler *baseCompiler) featureFlags(ctx ModuleContext, module *Module, flags Flags) Flags {
+	flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(ctx, module)...)
+	flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags(ctx, module)...)
 
 	return flags
 }
@@ -496,7 +497,7 @@
 
 func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
-	deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs...)
+	deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs.GetOrDefault(ctx, nil)...)
 	deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...)
 	deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
 	deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...)
diff --git a/rust/config/global.go b/rust/config/global.go
index 68a74c2..7b79fca 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"fmt"
 	"strings"
 
 	"android/soong/android"
@@ -93,6 +94,16 @@
 	}
 )
 
+func RustPath(ctx android.PathContext) string {
+	// I can't see any way to flatten the static variable inside Soong, so this
+	// reproduces the init logic.
+	var RustBase string = RustDefaultBase
+	if override := ctx.Config().Getenv("RUST_PREBUILTS_BASE"); override != "" {
+		RustBase = override
+	}
+	return fmt.Sprintf("%s/%s/%s", RustBase, HostPrebuiltTag(ctx.Config()), GetRustVersion(ctx))
+}
+
 func init() {
 	pctx.SourcePathVariable("RustDefaultBase", RustDefaultBase)
 	pctx.VariableConfigMethod("HostPrebuiltTag", HostPrebuiltTag)
diff --git a/rust/config/x86_64_device.go b/rust/config/x86_64_device.go
index fee1923..3c484d8 100644
--- a/rust/config/x86_64_device.go
+++ b/rust/config/x86_64_device.go
@@ -29,6 +29,7 @@
 
 	x86_64ArchVariantRustFlags = map[string][]string{
 		"":                            []string{},
+		"alderlake":                   []string{"-C target-cpu=alderlake"},
 		"broadwell":                   []string{"-C target-cpu=broadwell"},
 		"goldmont":                    []string{"-C target-cpu=goldmont"},
 		"goldmont-plus":               []string{"-C target-cpu=goldmont-plus"},
diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go
index 5d9d88a..3c597cc 100644
--- a/rust/config/x86_device.go
+++ b/rust/config/x86_device.go
@@ -27,6 +27,7 @@
 
 	x86ArchVariantRustFlags = map[string][]string{
 		"":                            []string{},
+		"alderlake":                   []string{"-C target-cpu=alderlake"},
 		"atom":                        []string{"-C target-cpu=atom"},
 		"broadwell":                   []string{"-C target-cpu=broadwell"},
 		"goldmont":                    []string{"-C target-cpu=goldmont"},
diff --git a/rust/image.go b/rust/image.go
index 26929b1..51b8289 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -85,7 +85,7 @@
 	mod.Properties.VendorVariantNeeded = b
 }
 
-func (mod *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
+func (mod *Module) SnapshotVersion(mctx android.ImageInterfaceContext) string {
 	if snapshot, ok := mod.compiler.(cc.SnapshotInterface); ok {
 		return snapshot.Version()
 	} else {
@@ -94,35 +94,35 @@
 	}
 }
 
-func (mod *Module) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+func (mod *Module) VendorVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return mod.Properties.VendorVariantNeeded
 }
 
-func (mod *Module) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+func (mod *Module) ProductVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return mod.Properties.ProductVariantNeeded
 }
 
-func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (mod *Module) VendorRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return mod.Properties.VendorRamdiskVariantNeeded
 }
 
-func (mod *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+func (mod *Module) CoreVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return mod.Properties.CoreVariantNeeded
 }
 
-func (mod *Module) RamdiskVariantNeeded(android.BaseModuleContext) bool {
+func (mod *Module) RamdiskVariantNeeded(android.ImageInterfaceContext) bool {
 	return mod.Properties.RamdiskVariantNeeded
 }
 
-func (mod *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (mod *Module) DebugRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
+func (mod *Module) RecoveryVariantNeeded(android.ImageInterfaceContext) bool {
 	return mod.Properties.RecoveryVariantNeeded
 }
 
-func (mod *Module) ExtraImageVariations(android.BaseModuleContext) []string {
+func (mod *Module) ExtraImageVariations(android.ImageInterfaceContext) []string {
 	return mod.Properties.ExtraVariants
 }
 
@@ -213,7 +213,7 @@
 	return mod.InVendor() || mod.InProduct()
 }
 
-func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant string) {
+func (mod *Module) SetImageVariation(ctx android.ImageInterfaceContext, variant string) {
 	if variant == android.VendorRamdiskVariation {
 		mod.MakeAsPlatform()
 	} else if variant == android.RecoveryVariation {
@@ -231,7 +231,7 @@
 	}
 }
 
-func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
+func (mod *Module) ImageMutatorBegin(mctx android.ImageInterfaceContext) {
 	if Bool(mod.VendorProperties.Double_loadable) {
 		mctx.PropertyErrorf("double_loadable",
 			"Rust modules do not yet support double loading")
diff --git a/rust/library.go b/rust/library.go
index 7db8f36..bd3359b 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -21,6 +21,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 
 	"android/soong/android"
 	"android/soong/cc"
@@ -616,11 +617,14 @@
 			TableOfContents: android.OptionalPathForPath(tocFile),
 			SharedLibrary:   outputFile,
 			Target:          ctx.Target(),
+			// TODO: when rust supports stubs uses the stubs state rather than inferring it from
+			//  apex_exclude.
+			IsStubs: Bool(library.Properties.Apex_exclude),
 		})
 	}
 
 	if library.static() {
-		depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(outputFile).Build()
+		depSet := depset.NewBuilder[android.Path](depset.TOPOLOGICAL).Direct(outputFile).Build()
 		android.SetProvider(ctx, cc.StaticLibraryInfoProvider, cc.StaticLibraryInfo{
 			StaticLibrary: outputFile,
 
diff --git a/rust/project_json.go b/rust/project_json.go
index 24dcc89..6e8cebe 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -19,6 +19,7 @@
 	"fmt"
 
 	"android/soong/android"
+	"android/soong/rust/config"
 )
 
 // This singleton collects Rust crate definitions and generates a JSON file
@@ -44,17 +45,19 @@
 }
 
 type rustProjectCrate struct {
-	DisplayName string            `json:"display_name"`
-	RootModule  string            `json:"root_module"`
-	Edition     string            `json:"edition,omitempty"`
-	Deps        []rustProjectDep  `json:"deps"`
-	Cfg         []string          `json:"cfg"`
-	Env         map[string]string `json:"env"`
-	ProcMacro   bool              `json:"is_proc_macro"`
+	DisplayName    string            `json:"display_name"`
+	RootModule     string            `json:"root_module"`
+	Edition        string            `json:"edition,omitempty"`
+	Deps           []rustProjectDep  `json:"deps"`
+	Cfg            []string          `json:"cfg"`
+	Env            map[string]string `json:"env"`
+	ProcMacro      bool              `json:"is_proc_macro"`
+	ProcMacroDylib *string           `json:"proc_macro_dylib_path"`
 }
 
 type rustProjectJson struct {
-	Crates []rustProjectCrate `json:"crates"`
+	Sysroot string             `json:"sysroot"`
+	Crates  []rustProjectCrate `json:"crates"`
 }
 
 // crateInfo is used during the processing to keep track of the known crates.
@@ -135,23 +138,28 @@
 		return 0, false
 	}
 
-	_, procMacro := rModule.compiler.(*procMacroDecorator)
+	var procMacroDylib *string = nil
+	if procDec, procMacro := rModule.compiler.(*procMacroDecorator); procMacro {
+		procMacroDylib = new(string)
+		*procMacroDylib = procDec.baseCompiler.unstrippedOutputFilePath().String()
+	}
 
 	crate := rustProjectCrate{
-		DisplayName: rModule.Name(),
-		RootModule:  rootModule.String(),
-		Edition:     rModule.compiler.edition(),
-		Deps:        make([]rustProjectDep, 0),
-		Cfg:         make([]string, 0),
-		Env:         make(map[string]string),
-		ProcMacro:   procMacro,
+		DisplayName:    rModule.Name(),
+		RootModule:     rootModule.String(),
+		Edition:        rModule.compiler.edition(),
+		Deps:           make([]rustProjectDep, 0),
+		Cfg:            make([]string, 0),
+		Env:            make(map[string]string),
+		ProcMacro:      procMacroDylib != nil,
+		ProcMacroDylib: procMacroDylib,
 	}
 
 	if rModule.compiler.cargoOutDir().Valid() {
 		crate.Env["OUT_DIR"] = rModule.compiler.cargoOutDir().String()
 	}
 
-	for _, feature := range rModule.compiler.features() {
+	for _, feature := range rModule.compiler.features(ctx, rModule) {
 		crate.Cfg = append(crate.Cfg, "feature=\""+feature+"\"")
 	}
 
@@ -197,6 +205,8 @@
 		return
 	}
 
+	singleton.project.Sysroot = config.RustPath(ctx)
+
 	singleton.knownCrates = make(map[string]crateInfo)
 	ctx.VisitAllModules(func(module android.Module) {
 		singleton.appendCrateAndDependencies(ctx, module)
diff --git a/rust/rust.go b/rust/rust.go
index 50f822b..eeb228c 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -20,9 +20,9 @@
 	"strings"
 
 	"android/soong/bloaty"
-	"android/soong/testing"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/depset"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -47,11 +47,11 @@
 func registerPreDepsMutators(ctx android.RegisterMutatorsContext) {
 	ctx.Transition("rust_libraries", &libraryTransitionMutator{})
 	ctx.Transition("rust_stdlinkage", &libstdTransitionMutator{})
-	ctx.BottomUp("rust_begin", BeginMutator).Parallel()
+	ctx.BottomUp("rust_begin", BeginMutator)
 }
 
 func registerPostDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
+	ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator)
 }
 
 type Flags struct {
@@ -179,7 +179,7 @@
 	// For apex variants, this is set as apex.min_sdk_version
 	apexSdkVersion android.ApiLevel
 
-	transitiveAndroidMkSharedLibs *android.DepSet[string]
+	transitiveAndroidMkSharedLibs depset.DepSet[string]
 }
 
 func (mod *Module) Header() bool {
@@ -454,6 +454,9 @@
 	// Paths to generated source files
 	SrcDeps          android.Paths
 	srcProviderFiles android.Paths
+
+	directImplementationDeps     android.Paths
+	transitiveImplementationDeps []depset.DepSet[android.Path]
 }
 
 type RustLibraries []RustLibrary
@@ -719,6 +722,10 @@
 	return false
 }
 
+func (mod *Module) HasStubsVariants() bool {
+	return false
+}
+
 func (mod *Module) IsStubs() bool {
 	return false
 }
@@ -920,7 +927,7 @@
 	if mod.compiler != nil {
 		flags = mod.compiler.compilerFlags(ctx, flags)
 		flags = mod.compiler.cfgFlags(ctx, flags)
-		flags = mod.compiler.featureFlags(ctx, flags)
+		flags = mod.compiler.featureFlags(ctx, mod, flags)
 	}
 	if mod.coverage != nil {
 		flags, deps = mod.coverage.flags(ctx, flags, deps)
@@ -974,6 +981,7 @@
 			// side dependencies. In particular, proc-macros need to be captured in the
 			// host snapshot.
 			mod.HideFromMake()
+			mod.SkipInstall()
 		} else if !mod.installable(apexInfo) {
 			mod.SkipInstall()
 		}
@@ -990,11 +998,16 @@
 
 		}
 
+		android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{
+			ImplementationDeps: depset.New(depset.PREORDER, deps.directImplementationDeps, deps.transitiveImplementationDeps),
+		})
+
 		ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
 	}
-	if mod.testModule {
-		android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
-	}
+
+	android.SetProvider(ctx, cc.LinkableInfoKey, cc.LinkableInfo{
+		StaticExecutable: mod.StaticExecutable(),
+	})
 
 	mod.setOutputFiles(ctx)
 
@@ -1217,7 +1230,7 @@
 
 	skipModuleList := map[string]bool{}
 
-	var transitiveAndroidMkSharedLibs []*android.DepSet[string]
+	var transitiveAndroidMkSharedLibs []depset.DepSet[string]
 	var directAndroidMkSharedLibs []string
 
 	ctx.VisitDirectDeps(func(dep android.Module) {
@@ -1246,6 +1259,11 @@
 				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName)
 				mod.Properties.SnapshotDylibs = append(mod.Properties.SnapshotDylibs, cc.BaseLibName(depName))
 
+				depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+				if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+					depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+				}
+
 			case depTag == rlibDepTag:
 				rlib, ok := rustDep.compiler.(libraryInterface)
 				if !ok || !rlib.rlib() {
@@ -1261,6 +1279,11 @@
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
 
+				// rlibs are not installed, so don't add the output file to directImplementationDeps
+				if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+					depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+				}
+
 			case depTag == procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
@@ -1393,6 +1416,13 @@
 				// dependency crosses the APEX boundaries).
 				sharedLibraryInfo, exportedInfo := cc.ChooseStubOrImpl(ctx, dep)
 
+				if !sharedLibraryInfo.IsStubs {
+					depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+					if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+						depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+					}
+				}
+
 				// Re-get linkObject as ChooseStubOrImpl actually tells us which
 				// object (either from stub or non-stub) to use.
 				linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
@@ -1453,7 +1483,7 @@
 		}
 	})
 
-	mod.transitiveAndroidMkSharedLibs = android.NewDepSet[string](android.PREORDER, directAndroidMkSharedLibs, transitiveAndroidMkSharedLibs)
+	mod.transitiveAndroidMkSharedLibs = depset.New[string](depset.PREORDER, directAndroidMkSharedLibs, transitiveAndroidMkSharedLibs)
 
 	var rlibDepFiles RustLibraries
 	aliases := mod.compiler.Aliases()
@@ -1756,6 +1786,16 @@
 
 var _ android.ApexModule = (*Module)(nil)
 
+// If a module is marked for exclusion from apexes, don't provide apex variants.
+// TODO(b/362509506): remove this once stubs are properly supported by rust_ffi targets.
+func (m *Module) CanHaveApexVariants() bool {
+	if m.ApexExclude() {
+		return false
+	} else {
+		return m.ApexModuleBase.CanHaveApexVariants()
+	}
+}
+
 func (mod *Module) MinSdkVersion() string {
 	return String(mod.Properties.Min_sdk_version)
 }
diff --git a/rust/rust_test.go b/rust/rust_test.go
index eeedf3f..767508d 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -494,7 +494,7 @@
 	}
 
 	// 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") {
+	if !strings.Contains(libcc_shared_ld.Args["libFlags"], "generated_rust_staticlib/librustlibs.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"])
 	}
@@ -511,7 +511,7 @@
 	}
 
 	// 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") {
+	if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/librustlibs.a") {
 		t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v",
 			"ccBin.generated_rust_staticlib.a", ccbin_ld.Args["libFlags"])
 	}
diff --git a/rust/sanitize.go b/rust/sanitize.go
index c086880..b8f922f 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -94,14 +94,6 @@
 	"-C llvm-args=--hwasan-with-ifunc",
 }
 
-func boolPtr(v bool) *bool {
-	if v {
-		return &v
-	} else {
-		return nil
-	}
-}
-
 func init() {
 }
 func (sanitize *sanitize) props() []interface{} {
@@ -111,6 +103,11 @@
 func (sanitize *sanitize) begin(ctx BaseModuleContext) {
 	s := &sanitize.Properties.Sanitize
 
+	// Disable sanitizers for musl x86 modules, rustc does not support any sanitizers.
+	if ctx.Os() == android.LinuxMusl && ctx.Arch().ArchType == android.X86 {
+		s.Never = proptools.BoolPtr(true)
+	}
+
 	// Never always wins.
 	if Bool(s.Never) {
 		return
@@ -212,11 +209,6 @@
 		s.Memtag_heap = nil
 	}
 
-	// Disable sanitizers for musl x86 modules, rustc does not support any sanitizers.
-	if ctx.Os() == android.LinuxMusl && ctx.Arch().ArchType == android.X86 {
-		s.Never = boolPtr(true)
-	}
-
 	// TODO:(b/178369775)
 	// For now sanitizing is only supported on non-windows targets
 	if ctx.Os() != android.Windows && (Bool(s.Hwaddress) || Bool(s.Address) || Bool(s.Memtag_heap) || Bool(s.Fuzzer)) {
@@ -318,16 +310,16 @@
 	sanitizerSet := false
 	switch t {
 	case cc.Fuzzer:
-		sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
+		sanitize.Properties.Sanitize.Fuzzer = proptools.BoolPtr(b)
 		sanitizerSet = true
 	case cc.Asan:
-		sanitize.Properties.Sanitize.Address = boolPtr(b)
+		sanitize.Properties.Sanitize.Address = proptools.BoolPtr(b)
 		sanitizerSet = true
 	case cc.Hwasan:
-		sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
+		sanitize.Properties.Sanitize.Hwaddress = proptools.BoolPtr(b)
 		sanitizerSet = true
 	case cc.Memtag_heap:
-		sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b)
+		sanitize.Properties.Sanitize.Memtag_heap = proptools.BoolPtr(b)
 		sanitizerSet = true
 	default:
 		panic(fmt.Errorf("setting unsupported sanitizerType %d", t))
diff --git a/rust/test.go b/rust/test.go
index b7ddd06..20ccfb3 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -46,6 +46,9 @@
 	// the test
 	Data []string `android:"path,arch_variant"`
 
+	// Same as data, but will add dependencies on the device's
+	Device_common_data []string `android:"path_device_common"`
+
 	// list of shared library modules that should be installed alongside the test
 	Data_libs []string `android:"arch_variant"`
 
@@ -143,6 +146,7 @@
 	})
 
 	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
+	dataSrcPaths = append(dataSrcPaths, android.PathsForModuleSrc(ctx, test.Properties.Device_common_data)...)
 
 	ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 3d81b83..00b3ca5 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -184,12 +184,21 @@
     libs: ["ninja_rsp"],
 }
 
-python_test_host {
-    name: "lint_project_xml_test",
-    main: "lint_project_xml_test.py",
+python_binary_host {
+    name: "lint_strict_updatability_checks",
+    main: "lint_strict_updatability_checks.py",
     srcs: [
-        "lint_project_xml_test.py",
-        "lint_project_xml.py",
+        "lint_strict_updatability_checks.py",
+    ],
+    libs: ["ninja_rsp"],
+}
+
+python_test_host {
+    name: "lint_strict_updatability_checks_test",
+    main: "lint_strict_updatability_checks_test.py",
+    srcs: [
+        "lint_strict_updatability_checks_test.py",
+        "lint_strict_updatability_checks.py",
     ],
     libs: ["ninja_rsp"],
     test_suites: ["general-tests"],
diff --git a/scripts/build-apex-bundle.py b/scripts/build-apex-bundle.py
index dcdd9ef..277e112 100644
--- a/scripts/build-apex-bundle.py
+++ b/scripts/build-apex-bundle.py
@@ -16,8 +16,6 @@
 #
 """A tool to create an APEX bundle out of Soong-built base.zip"""
 
-from __future__ import print_function
-
 import argparse
 import sys
 import tempfile
diff --git a/scripts/check_boot_jars/check_boot_jars.py b/scripts/check_boot_jars/check_boot_jars.py
index b711f9d..174b96e 100755
--- a/scripts/check_boot_jars/check_boot_jars.py
+++ b/scripts/check_boot_jars/check_boot_jars.py
@@ -4,7 +4,6 @@
 Usage: check_boot_jars.py <dexdump_path> <package_allow_list_file> <jar1> \
 <jar2> ...
 """
-from __future__ import print_function
 import logging
 import re
 import subprocess
diff --git a/scripts/construct_context.py b/scripts/construct_context.py
index fc3a89e..882c2db 100755
--- a/scripts/construct_context.py
+++ b/scripts/construct_context.py
@@ -16,8 +16,6 @@
 #
 """A tool for constructing class loader context."""
 
-from __future__ import print_function
-
 import argparse
 import json
 import sys
diff --git a/scripts/diff_build_graphs.sh b/scripts/diff_build_graphs.sh
deleted file mode 100755
index 8d01124..0000000
--- a/scripts/diff_build_graphs.sh
+++ /dev/null
@@ -1,170 +0,0 @@
-#!/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.
-
-set -e
-
-# This file makes it easy to confirm that a set of changes in source code don't result in any
-# changes to the generated ninja files. This is to reduce the effort required to be confident
-# in the correctness of refactorings
-
-function die() {
-  echo "$@" >&2
-  exit 1
-}
-
-function usage() {
-  violation="$1"
-  die "$violation
-
-  Usage: diff_build_graphs.sh [--products=product1,product2...] <OLD_VERSIONS> <NEW_VERSIONS>
-
-  This file builds and parses the build files (Android.mk, Android.bp, etc) for each requested
-  product and for both sets of versions, and checks whether the ninja files (which implement
-  the build graph) changed between the two versions.
-
-  Example: diff_build_graphs.sh 'build/soong:work^ build/blueprint:work^' 'build/soong:work build/blueprint:work'
-
-  Options:
-    --products=PRODUCTS  comma-separated list of products to check"
-}
-
-PRODUCTS_ARG=""
-OLD_VERSIONS=""
-NEW_VERSIONS=""
-function parse_args() {
-  # parse optional arguments
-  while true; do
-    arg="${1-}"
-    case "$arg" in
-      --products=*) PRODUCTS_ARG="$arg";;
-      *) break;;
-    esac
-    shift
-  done
-  # parse required arguments
-  if [ "$#" != "2" ]; then
-    usage ""
-  fi
-  #argument validation
-  OLD_VERSIONS="$1"
-  NEW_VERSIONS="$2"
-
-}
-parse_args "$@"
-
-
-# find some file paths
-cd "$(dirname $0)"
-SCRIPT_DIR="$PWD"
-cd ../../..
-CHECKOUT_ROOT="$PWD"
-OUT_DIR="${OUT_DIR-}"
-if [ -z "$OUT_DIR" ]; then
-  OUT_DIR=out
-fi
-WORK_DIR="$OUT_DIR/diff"
-OUT_DIR_OLD="$WORK_DIR/out_old"
-OUT_DIR_NEW="$WORK_DIR/out_new"
-OUT_DIR_TEMP="$WORK_DIR/out_temp"
-
-
-function checkout() {
-  versionSpecs="$1"
-  for versionSpec in $versionSpecs; do
-    project="$(echo $versionSpec | sed 's|\([^:]*\):\([^:]*\)|\1|')"
-    ref="$(echo     $versionSpec | sed 's|\([^:]*\):\([^:]*\)|\2|')"
-    echo "checking out ref $ref in project $project"
-    git -C "$project" checkout "$ref"
-  done
-}
-
-function run_build() {
-  echo
-  echo "Starting build"
-  # rebuild multiproduct_kati, in case it was missing before,
-  # or in case it is affected by some of the changes we're testing
-  make blueprint_tools
-  # find multiproduct_kati and have it build the ninja files for each product
-  builder="$(echo $OUT_DIR/host/*/bin/multiproduct_kati)"
-  BUILD_NUMBER=sample "$builder" $PRODUCTS_ARG --keep --out "$OUT_DIR_TEMP" || true
-  echo
-}
-
-function diffProduct() {
-  product="$1"
-
-  zip1="$OUT_DIR_OLD/${product}.zip"
-  unzipped1="$OUT_DIR_OLD/$product"
-
-  zip2="$OUT_DIR_NEW/${product}.zip"
-  unzipped2="$OUT_DIR_NEW/$product"
-
-  unzip -qq "$zip1" -d "$unzipped1"
-  unzip -qq "$zip2" -d "$unzipped2"
-
-  #do a diff of the ninja files
-  diffFile="$WORK_DIR/diff.txt"
-  diff -r "$unzipped1" "$unzipped2" -x build_date.txt -x build_number.txt -x '\.*' -x '*.log' -x build_fingerprint.txt -x build.ninja.d -x '*.zip' > $diffFile || true
-  if [[ -s "$diffFile" ]]; then
-    # outputs are different, so remove the unzipped versions but keep the zipped versions
-    echo "First few differences (total diff linecount=$(wc -l $diffFile)) for product $product:"
-    cat "$diffFile" | head -n 10
-    echo "End of differences for product $product"
-    rm -rf "$unzipped1" "$unzipped2"
-  else
-    # outputs are the same, so remove all of the outputs
-    rm -rf "$zip1" "$unzipped1" "$zip2" "$unzipped2"
-  fi
-}
-
-function do_builds() {
-  #reset work dir
-  rm -rf "$WORK_DIR"
-  mkdir "$WORK_DIR"
-
-  #build new code
-  checkout "$NEW_VERSIONS"
-  run_build
-  mv "$OUT_DIR_TEMP" "$OUT_DIR_NEW"
-
-  #build old code
-  #TODO do we want to cache old results? Maybe by the time we care to cache old results this will
-  #be running on a remote server somewhere and be completely different
-  checkout "$OLD_VERSIONS"
-  run_build
-  mv "$OUT_DIR_TEMP" "$OUT_DIR_OLD"
-
-  #cleanup
-  echo created "$OUT_DIR_OLD" and "$OUT_DIR_NEW"
-}
-
-function main() {
-  do_builds
-  checkout "$NEW_VERSIONS"
-
-  #find all products
-  productsFile="$WORK_DIR/all_products.txt"
-  find $OUT_DIR_OLD $OUT_DIR_NEW -mindepth 1 -maxdepth 1 -name "*.zip" | sed "s|^$OUT_DIR_OLD/||" | sed "s|^$OUT_DIR_NEW/||" | sed "s|\.zip$||" | sort | uniq > "$productsFile"
-  echo Diffing products
-  for product in $(cat $productsFile); do
-    diffProduct "$product"
-  done
-  echo Done diffing products
-  echo "Any differing outputs can be seen at $OUT_DIR_OLD/*.zip and $OUT_DIR_NEW/*.zip"
-  echo "See $WORK_DIR/diff.txt for the full list of differences for the latest product checked"
-}
-
-main
diff --git a/scripts/gen_build_prop.py b/scripts/gen_build_prop.py
index b82c8f9..3e4eb48 100644
--- a/scripts/gen_build_prop.py
+++ b/scripts/gen_build_prop.py
@@ -139,6 +139,7 @@
   print("####################################")
 
   config = args.config
+  build_flags = config["BuildFlags"]
   partition = args.partition
 
   if partition == "system":
@@ -190,6 +191,7 @@
   print(f"ro.{partition}.build.version.release={config['Platform_version_last_stable']}")
   print(f"ro.{partition}.build.version.release_or_codename={config['Platform_version_name']}")
   print(f"ro.{partition}.build.version.sdk={config['Platform_sdk_version']}")
+  print(f"ro.{partition}.build.version.sdk_minor={build_flags['RELEASE_PLATFORM_SDK_MINOR_VERSION']}")
 
 def generate_build_info(args):
   print()
@@ -225,6 +227,7 @@
     print(f"ro.build.display.id?={config['BuildDesc']}")
   print(f"ro.build.version.incremental={config['BuildNumber']}")
   print(f"ro.build.version.sdk={config['Platform_sdk_version']}")
+  print(f"ro.build.version.sdk_minor={build_flags['RELEASE_PLATFORM_SDK_MINOR_VERSION']}")
   print(f"ro.build.version.preview_sdk={config['Platform_preview_sdk_version']}")
   print(f"ro.build.version.preview_sdk_fingerprint={config['PlatformPreviewSdkFingerprint']}")
   print(f"ro.build.version.codename={config['Platform_sdk_codename']}")
@@ -460,7 +463,9 @@
   # Build system set BOARD_API_LEVEL to show the api level of the vendor API surface.
   # This must not be altered outside of build system.
   if config["VendorApiLevel"]:
-    props.append(f"ro.board.api_level={config['VendorApiLevel']}")
+    props.append(f"ro.board.api_level?={config['VendorApiLevel']}")
+    if config["VendorApiLevelPropOverride"]:
+      props.append(f"ro.board.api_level={config['VendorApiLevelPropOverride']}")
 
   # RELEASE_BOARD_API_LEVEL_FROZEN is true when the vendor API surface is frozen.
   if build_flags["RELEASE_BOARD_API_LEVEL_FROZEN"]:
@@ -553,7 +558,6 @@
 
   build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
 
-'''
 def build_vendor_prop(args):
   config = args.config
 
@@ -570,7 +574,6 @@
     ]
 
   build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
-'''
 
 def build_product_prop(args):
   config = args.config
@@ -638,8 +641,10 @@
         build_odm_prop(args)
       case "product":
         build_product_prop(args)
-      # case "vendor":  # NOT IMPLEMENTED
-      #  build_vendor_prop(args)
+      case "vendor":
+        build_vendor_prop(args)
+      case "system_dlkm" | "vendor_dlkm" | "odm_dlkm" | "bootimage":
+        build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=[])
       case _:
         sys.exit(f"not supported partition {args.partition}")
 
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index 43edf44..061af19 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -27,6 +27,12 @@
     libs: [
         "signature_trie",
     ],
+    target: {
+        windows: {
+            // go modules (bpmodify) don't support windows
+            enabled: false,
+        },
+    },
 }
 
 python_test_host {
@@ -44,6 +50,12 @@
     test_options: {
         unit_test: true,
     },
+    target: {
+        windows: {
+            // go modules (bpmodify) don't support windows
+            enabled: false,
+        },
+    },
 }
 
 python_binary_host {
diff --git a/scripts/keep-flagged-apis.sh b/scripts/keep-flagged-apis.sh
index 9c48fdb..48efb7a 100755
--- a/scripts/keep-flagged-apis.sh
+++ b/scripts/keep-flagged-apis.sh
@@ -25,21 +25,12 @@
 # Convert the list of feature flags in the input file to Metalava options
 # of the form `--revert-annotation !android.annotation.FlaggedApi("<flag>")`
 # to prevent the annotated APIs from being hidden, i.e. include the annotated
-# APIs in the SDK snapshots. This also preserves the line comments, they will
-# be ignored by Metalava but might be useful when debugging.
+# APIs in the SDK snapshots.
 while read -r line; do
-  key=$(echo "$line" | cut -d= -f1)
-  value=$(echo "$line" | cut -d= -f2)
-
-  # Skip if value is not true and line does not start with '#'
-  if [[ ( $value != "true" ) && ( $line =~ ^[^#] )]]; then
-    continue
-  fi
-
   # Escape and quote the key for sed
-  escaped_key=$(echo "$key" | sed "s/'/\\\'/g; s/ /\\ /g")
+  escaped_line=$(echo "$line" | sed "s/'/\\\'/g; s/ /\\ /g")
 
-  echo $line | sed "s|^[^#].*$|--revert-annotation '!$FLAGGED(\"$escaped_key\")'|"
+  echo "--revert-annotation '!$FLAGGED(\"$escaped_line\")'"
 done < "$FLAGS"
 
 # Revert all flagged APIs, unless listed above.
diff --git a/scripts/lint_project_xml.py b/scripts/lint_project_xml.py
index c40b07d..ce6aa21 100755
--- a/scripts/lint_project_xml.py
+++ b/scripts/lint_project_xml.py
@@ -75,8 +75,6 @@
                       help='file containing the module\'s manifest.')
   parser.add_argument('--merged_manifest', dest='merged_manifest',
                       help='file containing merged manifest for the module and its dependencies.')
-  parser.add_argument('--baseline', dest='baseline_path',
-                      help='file containing baseline lint issues.')
   parser.add_argument('--library', dest='library', action='store_true',
                       help='mark the module as a library.')
   parser.add_argument('--test', dest='test', action='store_true',
@@ -94,8 +92,6 @@
                      help='treat a lint issue as a warning.')
   group.add_argument('--disable_check', dest='checks', action=check_action('ignore'), default=[],
                      help='disable a lint issue.')
-  group.add_argument('--disallowed_issues', dest='disallowed_issues', default=[],
-                     help='lint issues disallowed in the baseline file')
   return parser.parse_args()
 
 
@@ -140,30 +136,10 @@
   f.write("</lint>\n")
 
 
-def check_baseline_for_disallowed_issues(baseline, forced_checks):
-  issues_element = baseline.documentElement
-  if issues_element.tagName != 'issues':
-    raise RuntimeError('expected issues tag at root')
-  issues = issues_element.getElementsByTagName('issue')
-  disallowed = set()
-  for issue in issues:
-    id = issue.getAttribute('id')
-    if id in forced_checks:
-      disallowed.add(id)
-  return disallowed
-
-
 def main():
   """Program entry point."""
   args = parse_args()
 
-  if args.baseline_path:
-    baseline = minidom.parse(args.baseline_path)
-    disallowed_issues = check_baseline_for_disallowed_issues(baseline, args.disallowed_issues)
-    if disallowed_issues:
-      sys.exit('disallowed issues %s found in lint baseline file %s for module %s'
-                         % (disallowed_issues, args.baseline_path, args.name))
-
   if args.project_out:
     with open(args.project_out, 'w') as f:
       write_project_xml(f, args)
diff --git a/scripts/lint_strict_updatability_checks.py b/scripts/lint_strict_updatability_checks.py
new file mode 100755
index 0000000..5b5dfd8
--- /dev/null
+++ b/scripts/lint_strict_updatability_checks.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 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.
+#
+
+"""This file checks baselines passed to Android Lint for checks that must not be baselined."""
+
+import argparse
+import sys
+from xml.dom import minidom
+
+from ninja_rsp import NinjaRspFileReader
+
+
+def parse_args():
+  """Parse commandline arguments."""
+
+  def convert_arg_line_to_args(arg_line):
+    for arg in arg_line.split():
+      if arg.startswith('#'):
+        return
+      if not arg.strip():
+        continue
+      yield arg
+
+  parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
+  parser.convert_arg_line_to_args = convert_arg_line_to_args
+  parser.add_argument('--name', dest='name',
+                      help='name of the module.')
+  parser.add_argument('--baselines', dest='baselines', action='append', default=[],
+                      help='file containing whitespace separated list of baseline files.')
+  parser.add_argument('--disallowed_issues', dest='disallowed_issues', default=[],
+                     help='lint issues disallowed in the baseline file')
+  return parser.parse_args()
+
+
+def check_baseline_for_disallowed_issues(baseline, forced_checks):
+  issues_element = baseline.documentElement
+  if issues_element.tagName != 'issues':
+    raise RuntimeError('expected issues tag at root')
+  issues = issues_element.getElementsByTagName('issue')
+  disallowed = set()
+  for issue in issues:
+    id = issue.getAttribute('id')
+    if id in forced_checks:
+      disallowed.add(id)
+  return disallowed
+
+
+def main():
+  """Program entry point."""
+  args = parse_args()
+
+  error = False
+  for baseline_rsp_file in args.baselines:
+    for baseline_path in NinjaRspFileReader(baseline_rsp_file):
+      baseline = minidom.parse(baseline_path)
+      disallowed_issues = check_baseline_for_disallowed_issues(baseline, args.disallowed_issues)
+      if disallowed_issues:
+        print('disallowed issues %s found in lint baseline file %s for module %s'
+                % (disallowed_issues, baseline_path, args.name))
+        error = True
+
+  if error:
+    sys.exit(1)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/lint_project_xml_test.py b/scripts/lint_strict_updatability_checks_test.py
old mode 100644
new mode 100755
similarity index 88%
rename from scripts/lint_project_xml_test.py
rename to scripts/lint_strict_updatability_checks_test.py
index 344691d..fd8610f
--- a/scripts/lint_project_xml_test.py
+++ b/scripts/lint_strict_updatability_checks_test.py
@@ -15,12 +15,12 @@
 # limitations under the License.
 #
 
-"""Unit tests for lint_project_xml.py."""
+"""Unit tests for lint_strict_updatability_checks.py."""
 
 import unittest
 from xml.dom import minidom
 
-import lint_project_xml
+import lint_strict_updatability_checks
 
 
 class CheckBaselineForDisallowedIssuesTest(unittest.TestCase):
@@ -44,7 +44,7 @@
       '</issues>\n')
 
   def test_check_baseline_for_disallowed_issues(self):
-    disallowed_issues = lint_project_xml.check_baseline_for_disallowed_issues(self.baseline_xml, ["foo", "bar", "qux"])
+    disallowed_issues = lint_strict_updatability_checks.check_baseline_for_disallowed_issues(self.baseline_xml, ["foo", "bar", "qux"])
     self.assertEqual({"foo", "bar"}, disallowed_issues)
 
 
diff --git a/scripts/manifest.py b/scripts/manifest.py
index 32603e8..87f4f0c 100755
--- a/scripts/manifest.py
+++ b/scripts/manifest.py
@@ -16,7 +16,6 @@
 #
 """A tool for inserting values from the build system into a manifest or a test config."""
 
-from __future__ import print_function
 from xml.dom import minidom
 
 
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index 1e32d1d..175451e 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -16,8 +16,6 @@
 #
 """A tool for checking that a manifest agrees with the build system."""
 
-from __future__ import print_function
-
 import argparse
 import json
 import re
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index 9847ad5..ad3b313 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -16,8 +16,6 @@
 #
 """A tool for inserting values from the build system into a manifest."""
 
-from __future__ import print_function
-
 import argparse
 import sys
 from xml.dom import minidom
diff --git a/scripts/modify_permissions_allowlist.py b/scripts/modify_permissions_allowlist.py
index 38ec7ec..4a0ca8f 100755
--- a/scripts/modify_permissions_allowlist.py
+++ b/scripts/modify_permissions_allowlist.py
@@ -16,8 +16,6 @@
 #
 """A tool for modifying privileged permission allowlists."""
 
-from __future__ import print_function
-
 import argparse
 import sys
 from xml.dom import minidom
diff --git a/scripts/modify_permissions_allowlist_test.py b/scripts/modify_permissions_allowlist_test.py
index ee8b12c..577388f 100755
--- a/scripts/modify_permissions_allowlist_test.py
+++ b/scripts/modify_permissions_allowlist_test.py
@@ -16,8 +16,6 @@
 #
 """Unit tests for modify_permissions_allowlist.py."""
 
-from __future__ import print_function
-
 import unittest
 
 from xml.dom import minidom
diff --git a/scripts/ninja_determinism_test.py b/scripts/ninja_determinism_test.py
new file mode 100755
index 0000000..e207b96
--- /dev/null
+++ b/scripts/ninja_determinism_test.py
@@ -0,0 +1,210 @@
+#!/usr/bin/env python3
+
+import asyncio
+import argparse
+import dataclasses
+import hashlib
+import os
+import re
+import socket
+import subprocess
+import sys
+import zipfile
+
+from typing import List
+
+def get_top() -> str:
+  path = '.'
+  while not os.path.isfile(os.path.join(path, 'build/soong/soong_ui.bash')):
+    if os.path.abspath(path) == '/':
+      sys.exit('Could not find android source tree root.')
+    path = os.path.join(path, '..')
+  return os.path.abspath(path)
+
+
+_PRODUCT_REGEX = re.compile(r'([a-zA-Z_][a-zA-Z0-9_]*)(?:(?:-([a-zA-Z_][a-zA-Z0-9_]*))?-(user|userdebug|eng))?')
+
+
+@dataclasses.dataclass(frozen=True)
+class Product:
+  """Represents a TARGET_PRODUCT and TARGET_BUILD_VARIANT."""
+  product: str
+  release: str
+  variant: str
+
+  def __post_init__(self):
+    if not _PRODUCT_REGEX.match(str(self)):
+      raise ValueError(f'Invalid product name: {self}')
+
+  def __str__(self):
+    return self.product + '-' + self.release + '-' + self.variant
+
+
+async def run_make_nothing(product: Product, out_dir: str) -> bool:
+  """Runs a build and returns if it succeeded or not."""
+  with open(os.path.join(out_dir, 'build.log'), 'wb') as f:
+    result = await asyncio.create_subprocess_exec(
+        'prebuilts/build-tools/linux-x86/bin/nsjail',
+        '-q',
+        '--cwd',
+        os.getcwd(),
+        '-e',
+        '-B',
+        '/',
+        '-B',
+        f'{os.path.abspath(out_dir)}:{os.path.join(os.getcwd(), "out")}',
+        '--time_limit',
+        '0',
+        '--skip_setsid',
+        '--keep_caps',
+        '--disable_clone_newcgroup',
+        '--disable_clone_newnet',
+        '--rlimit_as',
+        'soft',
+        '--rlimit_core',
+        'soft',
+        '--rlimit_cpu',
+        'soft',
+        '--rlimit_fsize',
+        'soft',
+        '--rlimit_nofile',
+        'soft',
+        '--proc_rw',
+        '--hostname',
+        socket.gethostname(),
+        '--',
+        'build/soong/soong_ui.bash',
+        '--make-mode',
+        f'TARGET_PRODUCT={product.product}',
+        f'TARGET_RELEASE={product.release}',
+        f'TARGET_BUILD_VARIANT={product.variant}',
+        '--skip-ninja',
+        'nothing', stdout=f, stderr=subprocess.STDOUT)
+    return await result.wait() == 0
+
+SUBNINJA_OR_INCLUDE_REGEX = re.compile(rb'\n(?:include|subninja) ')
+
+def find_subninjas_and_includes(contents) -> List[str]:
+  results = []
+  def get_path_from_directive(i):
+    j = contents.find(b'\n', i)
+    if j < 0:
+      path_bytes = contents[i:]
+    else:
+      path_bytes = contents[i:j]
+    path_bytes = path_bytes.removesuffix(b'\r')
+    path = path_bytes.decode()
+    if '$' in path:
+      sys.exit('includes/subninjas with variables are unsupported: '+path)
+    return path
+
+  if contents.startswith(b"include "):
+    results.append(get_path_from_directive(len(b"include ")))
+  elif contents.startswith(b"subninja "):
+    results.append(get_path_from_directive(len(b"subninja ")))
+
+  for match in SUBNINJA_OR_INCLUDE_REGEX.finditer(contents):
+    results.append(get_path_from_directive(match.end()))
+
+  return results
+
+
+def transitively_included_ninja_files(out_dir: str, ninja_file: str, seen):
+  with open(ninja_file, 'rb') as f:
+    contents = f.read()
+
+  results = [ninja_file]
+  seen[ninja_file] = True
+  sub_files = find_subninjas_and_includes(contents)
+  for sub_file in sub_files:
+    sub_file = os.path.join(out_dir, sub_file.removeprefix('out/'))
+    if sub_file not in seen:
+      results.extend(transitively_included_ninja_files(out_dir, sub_file, seen))
+
+  return results
+
+
+def hash_ninja_file(out_dir: str, ninja_file: str, hasher):
+  with open(ninja_file, 'rb') as f:
+    contents = f.read()
+
+  sub_files = find_subninjas_and_includes(contents)
+
+  hasher.update(contents)
+
+  for sub_file in sub_files:
+    hash_ninja_file(out_dir, os.path.join(out_dir, sub_file.removeprefix('out/')), hasher)
+
+
+def hash_files(files: List[str]) -> str:
+  hasher = hashlib.md5()
+  for file in files:
+    with open(file, 'rb') as f:
+      hasher.update(f.read())
+  return hasher.hexdigest()
+
+
+def dist_ninja_files(out_dir: str, zip_name: str, ninja_files: List[str]):
+  dist_dir = os.getenv('DIST_DIR', os.path.join(os.getenv('OUT_DIR', 'out'), 'dist'))
+  os.makedirs(dist_dir, exist_ok=True)
+
+  with open(os.path.join(dist_dir, zip_name), 'wb') as f:
+    with zipfile.ZipFile(f, mode='w') as zf:
+      for ninja_file in ninja_files:
+        zf.write(ninja_file, arcname=os.path.basename(out_dir)+'/out/' + os.path.relpath(ninja_file, out_dir))
+
+
+async def main():
+    parser = argparse.ArgumentParser()
+    args = parser.parse_args()
+
+    os.chdir(get_top())
+    subprocess.check_call(['touch', 'build/soong/Android.bp'])
+
+    product = Product(
+      'aosp_cf_x86_64_phone',
+      'trunk_staging',
+      'userdebug',
+    )
+    os.environ['TARGET_PRODUCT'] = 'aosp_cf_x86_64_phone'
+    os.environ['TARGET_RELEASE'] = 'trunk_staging'
+    os.environ['TARGET_BUILD_VARIANT'] = 'userdebug'
+
+    out_dir1 = os.path.join(os.getenv('OUT_DIR', 'out'), 'determinism_test_out1')
+    out_dir2 = os.path.join(os.getenv('OUT_DIR', 'out'), 'determinism_test_out2')
+
+    os.makedirs(out_dir1, exist_ok=True)
+    os.makedirs(out_dir2, exist_ok=True)
+
+    success1, success2 = await asyncio.gather(
+      run_make_nothing(product, out_dir1),
+      run_make_nothing(product, out_dir2))
+
+    if not success1:
+      with open(os.path.join(out_dir1, 'build.log'), 'r') as f:
+        print(f.read(), file=sys.stderr)
+      sys.exit('build failed')
+    if not success2:
+      with open(os.path.join(out_dir2, 'build.log'), 'r') as f:
+        print(f.read(), file=sys.stderr)
+      sys.exit('build failed')
+
+    ninja_files1 = transitively_included_ninja_files(out_dir1, os.path.join(out_dir1, f'combined-{product.product}.ninja'), {})
+    ninja_files2 = transitively_included_ninja_files(out_dir2, os.path.join(out_dir2, f'combined-{product.product}.ninja'), {})
+
+    dist_ninja_files(out_dir1, 'determinism_test_files_1.zip', ninja_files1)
+    dist_ninja_files(out_dir2, 'determinism_test_files_2.zip', ninja_files2)
+
+    hash1 = hash_files(ninja_files1)
+    hash2 = hash_files(ninja_files2)
+
+    if hash1 != hash2:
+        sys.exit("ninja files were not deterministic! See disted determinism_test_files_1/2.zip")
+
+    print("Success, ninja files were deterministic")
+
+
+if __name__ == "__main__":
+    asyncio.run(main())
+
+
diff --git a/scripts/test_config_fixer.py b/scripts/test_config_fixer.py
index 2876bcb..91a83f2 100644
--- a/scripts/test_config_fixer.py
+++ b/scripts/test_config_fixer.py
@@ -16,8 +16,6 @@
 #
 """A tool for modifying values in a test config."""
 
-from __future__ import print_function
-
 import argparse
 import json
 import sys
diff --git a/scripts/text_file_processor.py b/scripts/text_file_processor.py
new file mode 100755
index 0000000..10186ce
--- /dev/null
+++ b/scripts/text_file_processor.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# 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 argparse
+import re
+
+def main():
+    parser = argparse.ArgumentParser(description='This script looks for '
+        '`{CONTENTS_OF:path/to/file}` markers in the input file and replaces them with the actual '
+        'contents of that file, with leading/trailing whitespace stripped. The idea is that this '
+        'script could be extended to support more types of markers in the future.')
+    parser.add_argument('input')
+    parser.add_argument('output')
+    args = parser.parse_args()
+
+    with open(args.input, 'r') as f:
+        contents = f.read()
+
+    i = 0
+    replacedContents = ''
+    for m in re.finditer(r'{CONTENTS_OF:([a-zA-Z0-9 _/.-]+)}', contents):
+        replacedContents += contents[i:m.start()]
+        with open(m.group(1), 'r') as f:
+            replacedContents += f.read().strip()
+        i = m.end()
+    replacedContents += contents[i:]
+
+    with open(args.output, 'w') as f:
+        f.write(replacedContents)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/sdk/Android.bp b/sdk/Android.bp
index f436320..f42b478 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -18,7 +18,6 @@
         "bp.go",
         "build_release.go",
         "exports.go",
-        "genrule.go",
         "member_trait.go",
         "member_type.go",
         "sdk.go",
@@ -31,7 +30,6 @@
         "cc_sdk_test.go",
         "compat_config_sdk_test.go",
         "exports_test.go",
-        "genrule_test.go",
         "java_sdk_test.go",
         "license_sdk_test.go",
         "member_trait_test.go",
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 39c8123..34e11f0 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -46,8 +46,9 @@
 				],
 			}
 		`, apex, fragment, extraFragments)),
-		android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
-		android.FixtureAddFile("frameworks/base/config/boot-image-profile.txt", nil),
+		android.FixtureAddFile("frameworks/base/boot/boot-profile.txt", nil),
+		android.FixtureAddFile("frameworks/base/boot/boot-image-profile.txt", nil),
+		android.FixtureAddFile("art/build/boot/boot-image-profile.txt", nil),
 		android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
 	)
 }
diff --git a/sdk/genrule.go b/sdk/genrule.go
deleted file mode 100644
index 347ab05..0000000
--- a/sdk/genrule.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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 sdk
-
-import (
-	"android/soong/android"
-	"android/soong/genrule"
-)
-
-func init() {
-	registerGenRuleBuildComponents(android.InitRegistrationContext)
-}
-
-func registerGenRuleBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("sdk_genrule", SdkGenruleFactory)
-}
-
-// sdk_genrule_host is a genrule that can depend on sdk and sdk_snapshot module types
-//
-// What this means is that it's a genrule with only the "common_os" variant.
-// sdk modules have 3 variants: host, android, and common_os. The common_os one depends
-// on the host/device ones and packages their result into a final snapshot zip.
-// Genrules probably want access to this snapshot zip when they depend on an sdk module,
-// which means they want to depend on the common_os variant and not the host/android
-// variants.
-func SdkGenruleFactory() android.Module {
-	module := genrule.NewGenRule()
-
-	android.InitCommonOSAndroidMultiTargetsArchModule(module, android.NeitherHostNorDeviceSupported, android.MultilibCommon)
-	android.InitDefaultableModule(module)
-
-	return module
-}
diff --git a/sdk/genrule_test.go b/sdk/genrule_test.go
deleted file mode 100644
index 6e52a3d..0000000
--- a/sdk/genrule_test.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2018 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 sdk
-
-import (
-	"testing"
-
-	"android/soong/android"
-	"android/soong/genrule"
-	"android/soong/java"
-)
-
-func TestSdkGenrule(t *testing.T) {
-	// Test that an sdk_genrule can depend on an sdk, and that a genrule can depend on an sdk_genrule
-	bp := `
-				sdk {
-					name: "my_sdk",
-				}
-				sdk_genrule {
-					name: "my_sdk_genrule",
-					tool_files: ["tool"],
-					cmd: "$(location tool) $(in) $(out)",
-					srcs: [":my_sdk"],
-					out: ["out"],
-				}
-				genrule {
-					name: "my_regular_genrule",
-					srcs: [":my_sdk_genrule"],
-					out: ["out"],
-					cmd: "cp $(in) $(out)",
-				}
-			`
-	android.GroupFixturePreparers(
-		// if java components aren't registered, the sdk module doesn't create a snapshot for some reason.
-		java.PrepareForTestWithJavaBuildComponents,
-		genrule.PrepareForTestWithGenRuleBuildComponents,
-		PrepareForTestWithSdkBuildComponents,
-		android.FixtureRegisterWithContext(registerGenRuleBuildComponents),
-	).RunTestWithBp(t, bp)
-}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 09a7c9e..4db163c 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -1360,10 +1360,9 @@
 }
 `),
 		snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) {
-			ctx := android.ModuleInstallPathContextForTesting(result.Config)
 			dexJarBuildPath := func(name string, kind android.SdkKind) string {
-				dep := result.Module(name, "android_common").(java.SdkLibraryDependency)
-				path := dep.SdkApiExportableStubDexJar(ctx, kind).Path()
+				sdkLibInfo, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), result.Module(name, "android_common"), java.SdkLibraryInfoProvider)
+				path := sdkLibInfo.ExportableStubDexJarPaths[kind].Path()
 				return path.RelativeToTop().String()
 			}
 
@@ -1725,7 +1724,7 @@
 
 		filegroup {
 			name: "mygroup",
-			srcs: [":myjavalib{.doctags}"],
+			device_common_srcs: [":myjavalib{.doctags}"],
 		}
 	`)
 
diff --git a/sdk/update.go b/sdk/update.go
index 7f4f80a..5a899a2 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -1201,7 +1201,7 @@
 // the snapshot.
 func (s *snapshotBuilder) snapshotSdkMemberName(name string, required bool) string {
 	if _, ok := s.allMembersByName[name]; !ok {
-		if required {
+		if required && !s.ctx.Config().AllowMissingDependencies() {
 			s.ctx.ModuleErrorf("Required member reference %s is not a member of the sdk", name)
 		}
 		return name
diff --git a/sh/Android.bp b/sh/Android.bp
index 930fcf5..1deedc7 100644
--- a/sh/Android.bp
+++ b/sh/Android.bp
@@ -11,7 +11,6 @@
         "soong-android",
         "soong-cc",
         "soong-java",
-        "soong-testing",
         "soong-tradefed",
     ],
     srcs: [
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 2e48d83..7f5a426 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -15,11 +15,10 @@
 package sh
 
 import (
+	"fmt"
 	"path/filepath"
 	"strings"
 
-	"android/soong/testing"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -47,6 +46,7 @@
 	ctx.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
 	ctx.RegisterModuleType("sh_test", ShTestFactory)
 	ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
+	ctx.RegisterModuleType("sh_defaults", ShDefaultsFactory)
 }
 
 // Test fixture preparer that will register most sh build components.
@@ -120,6 +120,16 @@
 	// the test.
 	Data []string `android:"path,arch_variant"`
 
+	// same as data, but adds dependencies using the device's os variation and the common
+	// architecture's variation. Can be used to add a module built for device to the data of a
+	// host test.
+	Device_common_data []string `android:"path_device_common"`
+
+	// same as data, but adds dependencies using the device's os variation and the device's first
+	// architecture's variation. Can be used to add a module built for device to the data of a
+	// host test.
+	Device_first_data []string `android:"path_device_first"`
+
 	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
 	// with root permission.
 	Require_root *bool
@@ -155,10 +165,14 @@
 
 	// Test options.
 	Test_options android.CommonTestOptions
+
+	// a list of extra test configuration files that should be installed with the module.
+	Extra_test_configs []string `android:"path,arch_variant"`
 }
 
 type ShBinary struct {
 	android.ModuleBase
+	android.DefaultableModuleBase
 
 	properties shBinaryProperties
 
@@ -176,8 +190,9 @@
 
 	installDir android.InstallPath
 
-	data       []android.DataPath
-	testConfig android.Path
+	data             []android.DataPath
+	testConfig       android.Path
+	extraTestConfigs android.Paths
 
 	dataModules map[string]android.Path
 }
@@ -189,7 +204,7 @@
 func (s *ShBinary) DepsMutator(ctx android.BottomUpMutatorContext) {
 }
 
-func (s *ShBinary) OutputFile() android.OutputPath {
+func (s *ShBinary) OutputFile() android.Path {
 	return s.outputFilePath
 }
 
@@ -210,41 +225,41 @@
 
 var _ android.ImageInterface = (*ShBinary)(nil)
 
-func (s *ShBinary) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+func (s *ShBinary) ImageMutatorBegin(ctx android.ImageInterfaceContext) {}
 
-func (s *ShBinary) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *ShBinary) VendorVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return s.InstallInVendor()
 }
 
-func (s *ShBinary) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *ShBinary) ProductVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return s.InstallInProduct()
 }
 
-func (s *ShBinary) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *ShBinary) CoreVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return !s.InstallInRecovery() && !s.InstallInRamdisk() && !s.InstallInVendorRamdisk() && !s.ModuleBase.InstallInVendor()
 }
 
-func (s *ShBinary) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *ShBinary) RamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return proptools.Bool(s.properties.Ramdisk_available) || s.InstallInRamdisk()
 }
 
-func (s *ShBinary) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *ShBinary) VendorRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return proptools.Bool(s.properties.Vendor_ramdisk_available) || s.InstallInVendorRamdisk()
 }
 
-func (s *ShBinary) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *ShBinary) DebugRamdiskVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return false
 }
 
-func (s *ShBinary) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *ShBinary) RecoveryVariantNeeded(ctx android.ImageInterfaceContext) bool {
 	return proptools.Bool(s.properties.Recovery_available) || s.InstallInRecovery()
 }
 
-func (s *ShBinary) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+func (s *ShBinary) ExtraImageVariations(ctx android.ImageInterfaceContext) []string {
 	return nil
 }
 
-func (s *ShBinary) SetImageVariation(ctx android.BaseModuleContext, variation string) {
+func (s *ShBinary) SetImageVariation(ctx android.ImageInterfaceContext, variation string) {
 	s.properties.ImageVariation = variation
 }
 
@@ -407,6 +422,8 @@
 	s.ShBinary.generateAndroidBuildActions(ctx)
 
 	expandedData := android.PathsForModuleSrc(ctx, s.testProperties.Data)
+	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, s.testProperties.Device_common_data)...)
+	expandedData = append(expandedData, android.PathsForModuleSrc(ctx, s.testProperties.Device_first_data)...)
 	// Emulate the data property for java_data dependencies.
 	for _, javaData := range ctx.GetDirectDepsWithTag(shTestJavaDataTag) {
 		expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...)
@@ -459,6 +476,7 @@
 		HostTemplate:           "${ShellTestConfigTemplate}",
 	})
 
+	s.extraTestConfigs = android.PathsForModuleSrc(ctx, s.testProperties.Extra_test_configs)
 	s.dataModules = make(map[string]android.Path)
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depTag := ctx.OtherModuleDependencyTag(dep)
@@ -499,7 +517,26 @@
 	installedData := ctx.InstallTestData(s.installDir, s.data)
 	s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath, installedData...)
 
-	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+	mkEntries := s.AndroidMkEntries()[0]
+	android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
+		TestcaseRelDataFiles: addArch(ctx.Arch().ArchType.String(), installedData.Paths()),
+		OutputFile:           s.outputFilePath,
+		TestConfig:           s.testConfig,
+		TestSuites:           s.testProperties.Test_suites,
+		IsHost:               false,
+		IsUnitTest:           Bool(s.testProperties.Test_options.Unit_test),
+		MkInclude:            mkEntries.Include,
+		MkAppClass:           mkEntries.Class,
+		InstallDir:           s.installDir,
+	})
+}
+
+func addArch(archType string, paths android.Paths) []string {
+	archRelPaths := []string{}
+	for _, p := range paths {
+		archRelPaths = append(archRelPaths, fmt.Sprintf("%s/%s", archType, p.Rel()))
+	}
+	return archRelPaths
 }
 
 func (s *ShTest) InstallInData() bool {
@@ -523,6 +560,9 @@
 					entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
 				}
 				entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(s.testProperties.Per_testcase_directory))
+				if len(s.extraTestConfigs) > 0 {
+					entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", s.extraTestConfigs.Strings()...)
+				}
 
 				s.testProperties.Test_options.SetAndroidMkEntries(entries)
 			},
@@ -540,6 +580,7 @@
 	module := &ShBinary{}
 	initShBinaryModule(module)
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
 	return module
 }
 
@@ -549,6 +590,7 @@
 	module := &ShBinary{}
 	initShBinaryModule(module)
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
 	return module
 }
 
@@ -559,6 +601,7 @@
 	module.AddProperties(&module.testProperties)
 
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
 	return module
 }
 
@@ -573,6 +616,21 @@
 	}
 
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+type ShDefaults struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+}
+
+func ShDefaultsFactory() android.Module {
+	module := &ShDefaults{}
+
+	module.AddProperties(&shBinaryProperties{}, &TestProperties{})
+	android.InitDefaultsModule(module)
+
 	return module
 }
 
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 37450b0..28f997d 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -176,6 +176,22 @@
 	android.AssertBoolEquals(t, "LOCAL_IS_UNIT_TEST", true, actualData)
 }
 
+func TestShTestExtraTestConfig(t *testing.T) {
+	result, _ := testShBinary(t, `
+		sh_test {
+			name: "foo",
+			src: "test.sh",
+			filename: "test.sh",
+                        extra_test_configs: ["config1.xml", "config2.xml"],
+		}
+	`)
+
+	mod := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+	entries := android.AndroidMkEntriesForTest(t, result, mod)[0]
+	actualData := entries.EntryMap["LOCAL_EXTRA_FULL_TEST_CONFIGS"]
+	android.AssertStringPathsRelativeToTopEquals(t, "extra_configs", result.Config(), []string{"config1.xml", "config2.xml"}, actualData)
+}
+
 func TestShTestHost_dataDeviceModules(t *testing.T) {
 	ctx, config := testShBinary(t, `
 		sh_test_host {
@@ -294,3 +310,92 @@
 	actualData := entries.EntryMap["LOCAL_TEST_DATA"]
 	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", config, expectedData, actualData)
 }
+
+func TestDefaultsForTests(t *testing.T) {
+	ctx, config := testShBinary(t, `
+		sh_defaults {
+			name: "defaults",
+			src: "test.sh",
+			filename: "test.sh",
+			data: [
+				"testdata/data1",
+				"testdata/sub/data2",
+			],
+		}
+		sh_test_host {
+			name: "foo",
+			defaults: ["defaults"],
+			data: [
+				"testdata/more_data",
+			],
+			java_data: [
+				"javalib",
+			],
+		}
+
+		java_library_host {
+			name: "javalib",
+			srcs: [],
+		}
+
+		sh_test {
+			name: "sh-test",
+			defaults: ["defaults"],
+		}
+
+	`)
+	buildOS := ctx.Config().BuildOS.String()
+	mod := ctx.ModuleForTests("foo", buildOS+"_x86_64").Module().(*ShTest)
+	if !mod.Host() {
+		t.Errorf("host bit is not set for a sh_test_host module.")
+	}
+	expectedData := []string{
+		":testdata/data1",
+		":testdata/sub/data2",
+		":testdata/more_data",
+		"out/soong/.intermediates/javalib/" + buildOS + "_common/combined/:javalib.jar",
+	}
+
+	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+	actualData := entries.EntryMap["LOCAL_TEST_DATA"]
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", config, expectedData, actualData)
+
+	// Just the defaults
+	expectedData = []string{
+		":testdata/data1",
+		":testdata/sub/data2",
+	}
+	mod = ctx.ModuleForTests("sh-test", "android_arm64_armv8-a").Module().(*ShTest)
+	entries = android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+	actualData = entries.EntryMap["LOCAL_TEST_DATA"]
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", config, expectedData, actualData)
+}
+
+func TestDefaultsForBinaries(t *testing.T) {
+	ctx, _ := testShBinary(t, `
+		sh_defaults {
+			name: "defaults",
+			src: "test.sh",
+			filename: "test.sh",
+		}
+		sh_binary_host {
+			name: "the-host-binary",
+			defaults: ["defaults"],
+		}
+		sh_binary{
+			name: "the-binary",
+			defaults: ["defaults"],
+		}
+	`)
+	buildOS := ctx.Config().BuildOS.String()
+	mod := ctx.ModuleForTests("the-host-binary", buildOS+"_x86_64").Module().(*ShBinary)
+	if !mod.Host() {
+		t.Errorf("host bit is not set for a sh_binary_host module.")
+	}
+
+	expectedFilename := "test.sh"
+	android.AssertStringEquals(t, "Filename", expectedFilename, *mod.properties.Filename)
+
+	mod = ctx.ModuleForTests("the-binary", "android_arm64_armv8-a").Module().(*ShBinary)
+	android.AssertStringEquals(t, "Filename", expectedFilename, *mod.properties.Filename)
+}
diff --git a/shared/paths.go b/shared/paths.go
index 1ee66d5..64f94ba 100644
--- a/shared/paths.go
+++ b/shared/paths.go
@@ -20,14 +20,6 @@
 	"path/filepath"
 )
 
-// A SharedPaths represents a list of paths that are shared between
-// soong_ui and soong.
-type SharedPaths interface {
-	// BazelMetricsDir returns the path where a set of bazel profile
-	// files are stored for later processed by the metrics pipeline.
-	BazelMetricsDir() string
-}
-
 // Joins the path strings in the argument list, taking absolute paths into
 // account. That is, if one of the strings is an absolute path, the ones before
 // are ignored.
diff --git a/soong_ui.bash b/soong_ui.bash
index 7737880..be78b68 100755
--- a/soong_ui.bash
+++ b/soong_ui.bash
@@ -14,18 +14,20 @@
 # 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
+
 # To track how long we took to startup.
 case $(uname -s) in
   Darwin)
-    export TRACE_BEGIN_SOONG=`$T/prebuilts/build-tools/path/darwin-x86/date +%s%3N`
+    export TRACE_BEGIN_SOONG=`$TOP/prebuilts/build-tools/path/darwin-x86/date +%s%3N`
     ;;
   *)
     export TRACE_BEGIN_SOONG=$(date +%s%N)
     ;;
 esac
 
-source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../make/shell_utils.sh
-require_top
+setup_cog_env_if_needed
 
 # Save the current PWD for use in soong_ui
 export ORIGINAL_PWD=${PWD}
diff --git a/systemfeatures/Android.bp b/systemfeatures/Android.bp
new file mode 100644
index 0000000..a65a6b6
--- /dev/null
+++ b/systemfeatures/Android.bp
@@ -0,0 +1,18 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-systemfeatures",
+    pkgPath: "android/soong/systemfeatures",
+    deps: [
+        "blueprint",
+        "blueprint-proptools",
+        "soong",
+        "soong-android",
+        "soong-java",
+    ],
+    srcs: ["system_features.go"],
+    testSrcs: ["system_features_test.go"],
+    pluginFor: ["soong_build"],
+}
diff --git a/systemfeatures/OWNERS b/systemfeatures/OWNERS
new file mode 100644
index 0000000..3e44806
--- /dev/null
+++ b/systemfeatures/OWNERS
@@ -0,0 +1,2 @@
+jdduke@google.com
+shayba@google.com
diff --git a/systemfeatures/system_features.go b/systemfeatures/system_features.go
new file mode 100644
index 0000000..0c1a566
--- /dev/null
+++ b/systemfeatures/system_features.go
@@ -0,0 +1,102 @@
+// 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 systemfeatures
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/genrule"
+)
+
+var (
+	pctx = android.NewPackageContext("android/soong/systemfeatures")
+)
+
+func init() {
+	registerSystemFeaturesComponents(android.InitRegistrationContext)
+}
+
+func registerSystemFeaturesComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("java_system_features_srcs", JavaSystemFeaturesSrcsFactory)
+}
+
+type javaSystemFeaturesSrcs struct {
+	android.ModuleBase
+	properties struct {
+		// The fully qualified class name for the generated code, e.g., com.android.Foo
+		Full_class_name string
+	}
+	outputFiles android.WritablePaths
+}
+
+var _ genrule.SourceFileGenerator = (*javaSystemFeaturesSrcs)(nil)
+var _ android.SourceFileProducer = (*javaSystemFeaturesSrcs)(nil)
+
+func (m *javaSystemFeaturesSrcs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Create a file name appropriate for the given fully qualified (w/ package) class name.
+	classNameParts := strings.Split(m.properties.Full_class_name, ".")
+	outputDir := android.PathForModuleGen(ctx)
+	outputFileName := classNameParts[len(classNameParts)-1] + ".java"
+	outputFile := android.PathForModuleGen(ctx, outputFileName).OutputPath
+
+	// Collect all RELEASE_SYSTEM_FEATURE_$K:$V build flags into a list of "$K:$V" pairs.
+	var features []string
+	for k, v := range ctx.Config().ProductVariables().BuildFlags {
+		if strings.HasPrefix(k, "RELEASE_SYSTEM_FEATURE_") {
+			shortFeatureName := strings.TrimPrefix(k, "RELEASE_SYSTEM_FEATURE_")
+			features = append(features, fmt.Sprintf("%s:%s", shortFeatureName, v))
+		}
+	}
+	// Ensure sorted outputs for consistency of flag ordering in ninja outputs.
+	sort.Strings(features)
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().Text("rm -rf").Text(outputDir.String())
+	rule.Command().Text("mkdir -p").Text(outputDir.String())
+	rule.Command().
+		BuiltTool("systemfeatures-gen-tool").
+		Flag(m.properties.Full_class_name).
+		FlagForEachArg("--feature=", features).
+		FlagWithArg("--readonly=", fmt.Sprint(ctx.Config().ReleaseUseSystemFeatureBuildFlags())).
+		FlagWithOutput(" > ", outputFile)
+	rule.Build(ctx.ModuleName(), "Generating systemfeatures srcs filegroup")
+
+	m.outputFiles = append(m.outputFiles, outputFile)
+}
+
+func (m *javaSystemFeaturesSrcs) Srcs() android.Paths {
+	return m.outputFiles.Paths()
+}
+
+func (m *javaSystemFeaturesSrcs) GeneratedSourceFiles() android.Paths {
+	return m.outputFiles.Paths()
+}
+
+func (m *javaSystemFeaturesSrcs) GeneratedDeps() android.Paths {
+	return m.outputFiles.Paths()
+}
+
+func (m *javaSystemFeaturesSrcs) GeneratedHeaderDirs() android.Paths {
+	return nil
+}
+
+func JavaSystemFeaturesSrcsFactory() android.Module {
+	module := &javaSystemFeaturesSrcs{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidModule(module)
+	return module
+}
diff --git a/systemfeatures/system_features_test.go b/systemfeatures/system_features_test.go
new file mode 100644
index 0000000..558bb95
--- /dev/null
+++ b/systemfeatures/system_features_test.go
@@ -0,0 +1,51 @@
+// 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 systemfeatures
+
+import (
+	"android/soong/android"
+
+	"testing"
+)
+
+func TestJavaSystemFeaturesSrcs(t *testing.T) {
+	bp := `
+java_system_features_srcs {
+    name: "system-features-srcs",
+	full_class_name: "com.android.test.RoSystemFeatures",
+}
+`
+
+	res := android.GroupFixturePreparers(
+		android.FixtureRegisterWithContext(registerSystemFeaturesComponents),
+		android.PrepareForTestWithBuildFlag("RELEASE_USE_SYSTEM_FEATURE_BUILD_FLAGS", "true"),
+		android.PrepareForTestWithBuildFlag("RELEASE_SYSTEM_FEATURE_AUTOMOTIVE", "0"),
+		android.PrepareForTestWithBuildFlag("RELEASE_SYSTEM_FEATURE_TELEVISION", "UNAVAILABLE"),
+		android.PrepareForTestWithBuildFlag("RELEASE_SYSTEM_FEATURE_WATCH", ""),
+		android.PrepareForTestWithBuildFlag("RELEASE_NOT_SYSTEM_FEATURE_FOO", "BAR"),
+	).RunTestWithBp(t, bp)
+
+	module := res.ModuleForTests("system-features-srcs", "")
+	cmd := module.Rule("system-features-srcs").RuleParams.Command
+	android.AssertStringDoesContain(t, "Expected fully class name", cmd, " com.android.test.RoSystemFeatures ")
+	android.AssertStringDoesContain(t, "Expected readonly flag", cmd, "--readonly=true")
+	android.AssertStringDoesContain(t, "Expected AUTOMOTIVE feature flag", cmd, "--feature=AUTOMOTIVE:0 ")
+	android.AssertStringDoesContain(t, "Expected TELEVISION feature flag", cmd, "--feature=TELEVISION:UNAVAILABLE ")
+	android.AssertStringDoesContain(t, "Expected WATCH feature flag", cmd, "--feature=WATCH: ")
+	android.AssertStringDoesNotContain(t, "Unexpected FOO arg from non-system feature flag", cmd, "FOO")
+
+	systemFeaturesModule := module.Module().(*javaSystemFeaturesSrcs)
+	expectedOutputPath := "out/soong/.intermediates/system-features-srcs/gen/RoSystemFeatures.java"
+	android.AssertPathsRelativeToTopEquals(t, "Expected output file", []string{expectedOutputPath}, systemFeaturesModule.Srcs())
+}
diff --git a/testing/Android.bp b/testing/Android.bp
deleted file mode 100644
index 43040b0..0000000
--- a/testing/Android.bp
+++ /dev/null
@@ -1,24 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
-    name: "soong-testing",
-    pkgPath: "android/soong/testing",
-    deps: [
-        "blueprint",
-        "soong-android",
-        "soong-testing-code_metadata_internal_proto",
-        "soong-testing-test_spec_proto",
-
-    ],
-    srcs: [
-        "all_code_metadata.go",
-        "all_test_specs.go",
-        "code_metadata.go",
-        "test_spec.go",
-        "init.go",
-        "test.go",
-    ],
-    pluginFor: ["soong_build"],
-}
diff --git a/testing/OWNERS b/testing/OWNERS
deleted file mode 100644
index 03bcdf1..0000000
--- a/testing/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-dariofreni@google.com
-joeo@google.com
-ronish@google.com
-caditya@google.com
diff --git a/testing/all_code_metadata.go b/testing/all_code_metadata.go
deleted file mode 100644
index e89b281..0000000
--- a/testing/all_code_metadata.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package testing
-
-import (
-	"android/soong/android"
-)
-
-const fileContainingCodeMetadataFilePaths = "all_code_metadata_paths.rsp"
-const allCodeMetadataFile = "all_code_metadata.pb"
-
-func AllCodeMetadataFactory() android.Singleton {
-	return &allCodeMetadataSingleton{}
-}
-
-type allCodeMetadataSingleton struct {
-	// Path where the collected metadata is stored after successful validation.
-	outputPath android.OutputPath
-}
-
-func (this *allCodeMetadataSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	var intermediateMetadataPaths android.Paths
-
-	ctx.VisitAllModules(
-		func(module android.Module) {
-			if metadata, ok := android.OtherModuleProvider(ctx, module, CodeMetadataProviderKey); ok {
-				intermediateMetadataPaths = append(intermediateMetadataPaths, metadata.IntermediatePath)
-			}
-		},
-	)
-
-	rspFile := android.PathForOutput(ctx, fileContainingCodeMetadataFilePaths)
-	this.outputPath = android.PathForOutput(ctx, ownershipDirectory, allCodeMetadataFile)
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-	cmd := rule.Command().
-		BuiltTool("metadata").
-		FlagWithArg("-rule ", "code_metadata").
-		FlagWithRspFileInputList("-inputFile ", rspFile, intermediateMetadataPaths)
-	cmd.FlagWithOutput("-outputFile ", this.outputPath)
-	rule.Build("all_code_metadata_rule", "Generate all code metadata")
-
-	ctx.Phony("all_code_metadata", this.outputPath)
-}
-
-func (this *allCodeMetadataSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.DistForGoal("code_metadata", this.outputPath)
-}
diff --git a/testing/all_test_specs.go b/testing/all_test_specs.go
deleted file mode 100644
index 68f24d1..0000000
--- a/testing/all_test_specs.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package testing
-
-import (
-	"android/soong/android"
-)
-
-const ownershipDirectory = "ownership"
-const fileContainingFilePaths = "all_test_spec_paths.rsp"
-const allTestSpecsFile = "all_test_specs.pb"
-
-func AllTestSpecsFactory() android.Singleton {
-	return &allTestSpecsSingleton{}
-}
-
-type allTestSpecsSingleton struct {
-	// Path where the collected metadata is stored after successful validation.
-	outputPath android.OutputPath
-}
-
-func (this *allTestSpecsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	var intermediateMetadataPaths android.Paths
-
-	ctx.VisitAllModules(func(module android.Module) {
-		if metadata, ok := android.OtherModuleProvider(ctx, module, TestSpecProviderKey); ok {
-			intermediateMetadataPaths = append(intermediateMetadataPaths, metadata.IntermediatePath)
-		}
-	})
-
-	rspFile := android.PathForOutput(ctx, fileContainingFilePaths)
-	this.outputPath = android.PathForOutput(ctx, ownershipDirectory, allTestSpecsFile)
-
-	rule := android.NewRuleBuilder(pctx, ctx)
-	cmd := rule.Command().
-		BuiltTool("metadata").
-		FlagWithArg("-rule ", "test_spec").
-		FlagWithRspFileInputList("-inputFile ", rspFile, intermediateMetadataPaths)
-	cmd.FlagWithOutput("-outputFile ", this.outputPath)
-	rule.Build("all_test_specs_rule", "Generate all test specifications")
-	ctx.Phony("all_test_specs", this.outputPath)
-}
-
-func (this *allTestSpecsSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.DistForGoal("test_specs", this.outputPath)
-}
diff --git a/testing/code_metadata.go b/testing/code_metadata.go
deleted file mode 100644
index 11ba430..0000000
--- a/testing/code_metadata.go
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2020 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 testing
-
-import (
-	"path/filepath"
-
-	"android/soong/android"
-	"android/soong/testing/code_metadata_internal_proto"
-	"github.com/google/blueprint"
-
-	"google.golang.org/protobuf/proto"
-)
-
-func CodeMetadataFactory() android.Module {
-	module := &CodeMetadataModule{}
-
-	android.InitAndroidModule(module)
-	android.InitDefaultableModule(module)
-	module.AddProperties(&module.properties)
-
-	return module
-}
-
-type CodeMetadataModule struct {
-	android.ModuleBase
-	android.DefaultableModuleBase
-
-	// Properties for "code_metadata"
-	properties struct {
-		// Specifies the name of the code_config.
-		Name string
-		// Specifies the team ID.
-		TeamId string
-		// Specifies the list of modules that this code_metadata covers.
-		Code []string
-		// An optional field to specify if multiple ownerships for source files is allowed.
-		MultiOwnership bool
-	}
-}
-
-type codeDepTagType struct {
-	blueprint.BaseDependencyTag
-}
-
-var codeDepTag = codeDepTagType{}
-
-func (module *CodeMetadataModule) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// Validate Properties
-	if len(module.properties.TeamId) == 0 {
-		ctx.PropertyErrorf(
-			"TeamId",
-			"Team Id not found in the code_metadata module. Hint: Maybe the teamId property hasn't been properly specified.",
-		)
-	}
-	if !isInt(module.properties.TeamId) {
-		ctx.PropertyErrorf(
-			"TeamId", "Invalid value for Team ID. The Team ID must be an integer.",
-		)
-	}
-	if len(module.properties.Code) == 0 {
-		ctx.PropertyErrorf(
-			"Code",
-			"Targets to be attributed cannot be empty. Hint: Maybe the code property hasn't been properly specified.",
-		)
-	}
-	ctx.AddDependency(ctx.Module(), codeDepTag, module.properties.Code...)
-}
-
-// Provider published by CodeMetadata
-type CodeMetadataProviderData struct {
-	IntermediatePath android.WritablePath
-}
-
-var CodeMetadataProviderKey = blueprint.NewProvider[CodeMetadataProviderData]()
-
-func (module *CodeMetadataModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	metadataList := make(
-		[]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership, 0,
-		len(module.properties.Code),
-	)
-	bpFilePath := filepath.Join(ctx.ModuleDir(), ctx.BlueprintsFile())
-
-	for _, m := range ctx.GetDirectDepsWithTag(codeDepTag) {
-		targetName := m.Name()
-		var moduleSrcs []string
-		if srcsFileInfo, ok := android.OtherModuleProvider(ctx, m, blueprint.SrcsFileProviderKey); ok {
-			moduleSrcs = srcsFileInfo.SrcPaths
-		}
-		if module.properties.MultiOwnership {
-			metadata := &code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
-				TargetName:     &targetName,
-				TrendyTeamId:   &module.properties.TeamId,
-				Path:           &bpFilePath,
-				MultiOwnership: &module.properties.MultiOwnership,
-				SourceFiles:    moduleSrcs,
-			}
-			metadataList = append(metadataList, metadata)
-		} else {
-			metadata := &code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
-				TargetName:   &targetName,
-				TrendyTeamId: &module.properties.TeamId,
-				Path:         &bpFilePath,
-				SourceFiles:  moduleSrcs,
-			}
-			metadataList = append(metadataList, metadata)
-		}
-
-	}
-	codeMetadata := &code_metadata_internal_proto.CodeMetadataInternal{TargetOwnershipList: metadataList}
-	protoData, err := proto.Marshal(codeMetadata)
-	if err != nil {
-		ctx.ModuleErrorf("Error marshaling code metadata: %s", err.Error())
-		return
-	}
-	intermediatePath := android.PathForModuleOut(
-		ctx, "intermediateCodeMetadata.pb",
-	)
-	android.WriteFileRuleVerbatim(ctx, intermediatePath, string(protoData))
-
-	android.SetProvider(ctx,
-		CodeMetadataProviderKey,
-		CodeMetadataProviderData{IntermediatePath: intermediatePath},
-	)
-}
diff --git a/testing/code_metadata_internal_proto/OWNERS b/testing/code_metadata_internal_proto/OWNERS
deleted file mode 100644
index 03bcdf1..0000000
--- a/testing/code_metadata_internal_proto/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-dariofreni@google.com
-joeo@google.com
-ronish@google.com
-caditya@google.com
diff --git a/testing/code_metadata_internal_proto/code_metadata_internal.pb.go b/testing/code_metadata_internal_proto/code_metadata_internal.pb.go
deleted file mode 100644
index ecb8b86..0000000
--- a/testing/code_metadata_internal_proto/code_metadata_internal.pb.go
+++ /dev/null
@@ -1,277 +0,0 @@
-// 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: code_metadata_internal.proto
-
-package code_metadata_internal_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 CodeMetadataInternal struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// List of all code targets and their metadata.
-	TargetOwnershipList []*CodeMetadataInternal_TargetOwnership `protobuf:"bytes,1,rep,name=target_ownership_list,json=targetOwnershipList" json:"target_ownership_list,omitempty"`
-}
-
-func (x *CodeMetadataInternal) Reset() {
-	*x = CodeMetadataInternal{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_code_metadata_internal_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *CodeMetadataInternal) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*CodeMetadataInternal) ProtoMessage() {}
-
-func (x *CodeMetadataInternal) ProtoReflect() protoreflect.Message {
-	mi := &file_code_metadata_internal_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 CodeMetadataInternal.ProtoReflect.Descriptor instead.
-func (*CodeMetadataInternal) Descriptor() ([]byte, []int) {
-	return file_code_metadata_internal_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *CodeMetadataInternal) GetTargetOwnershipList() []*CodeMetadataInternal_TargetOwnership {
-	if x != nil {
-		return x.TargetOwnershipList
-	}
-	return nil
-}
-
-type CodeMetadataInternal_TargetOwnership struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// REQUIRED: Name of the build target
-	TargetName *string `protobuf:"bytes,1,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
-	// REQUIRED: Code location of the target.
-	// To be used to support legacy/backup systems that use OWNERS file and is
-	// also required for our dashboard to support per code location basis UI
-	Path *string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
-	// REQUIRED: Team ID of the team that owns this target.
-	TrendyTeamId *string `protobuf:"bytes,3,opt,name=trendy_team_id,json=trendyTeamId" json:"trendy_team_id,omitempty"`
-	// OPTIONAL: The src files of the target.
-	// To be used to determine ownership of a file for ownership
-	SourceFiles []string `protobuf:"bytes,4,rep,name=source_files,json=sourceFiles" json:"source_files,omitempty"`
-	// OPTIONAL: Specify if multiple ownerships of the source files are allowed.
-	MultiOwnership *bool `protobuf:"varint,5,opt,name=multi_ownership,json=multiOwnership" json:"multi_ownership,omitempty"`
-}
-
-func (x *CodeMetadataInternal_TargetOwnership) Reset() {
-	*x = CodeMetadataInternal_TargetOwnership{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_code_metadata_internal_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *CodeMetadataInternal_TargetOwnership) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*CodeMetadataInternal_TargetOwnership) ProtoMessage() {}
-
-func (x *CodeMetadataInternal_TargetOwnership) ProtoReflect() protoreflect.Message {
-	mi := &file_code_metadata_internal_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 CodeMetadataInternal_TargetOwnership.ProtoReflect.Descriptor instead.
-func (*CodeMetadataInternal_TargetOwnership) Descriptor() ([]byte, []int) {
-	return file_code_metadata_internal_proto_rawDescGZIP(), []int{0, 0}
-}
-
-func (x *CodeMetadataInternal_TargetOwnership) GetTargetName() string {
-	if x != nil && x.TargetName != nil {
-		return *x.TargetName
-	}
-	return ""
-}
-
-func (x *CodeMetadataInternal_TargetOwnership) GetPath() string {
-	if x != nil && x.Path != nil {
-		return *x.Path
-	}
-	return ""
-}
-
-func (x *CodeMetadataInternal_TargetOwnership) GetTrendyTeamId() string {
-	if x != nil && x.TrendyTeamId != nil {
-		return *x.TrendyTeamId
-	}
-	return ""
-}
-
-func (x *CodeMetadataInternal_TargetOwnership) GetSourceFiles() []string {
-	if x != nil {
-		return x.SourceFiles
-	}
-	return nil
-}
-
-func (x *CodeMetadataInternal_TargetOwnership) GetMultiOwnership() bool {
-	if x != nil && x.MultiOwnership != nil {
-		return *x.MultiOwnership
-	}
-	return false
-}
-
-var File_code_metadata_internal_proto protoreflect.FileDescriptor
-
-var file_code_metadata_internal_proto_rawDesc = []byte{
-	0x0a, 0x1c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f,
-	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c,
-	0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x6e,
-	0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc9, 0x02, 0x0a,
-	0x14, 0x43, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x49, 0x6e, 0x74,
-	0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x76, 0x0a, 0x15, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
-	0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01,
-	0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61,
-	0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
-	0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f,
-	0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x13, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
-	0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0xb8, 0x01,
-	0x0a, 0x0f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69,
-	0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61,
-	0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79,
-	0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
-	0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c,
-	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03,
-	0x28, 0x09, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12,
-	0x27, 0x0a, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68,
-	0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4f,
-	0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x42, 0x34, 0x5a, 0x32, 0x61, 0x6e, 0x64, 0x72,
-	0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e,
-	0x67, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f,
-	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-}
-
-var (
-	file_code_metadata_internal_proto_rawDescOnce sync.Once
-	file_code_metadata_internal_proto_rawDescData = file_code_metadata_internal_proto_rawDesc
-)
-
-func file_code_metadata_internal_proto_rawDescGZIP() []byte {
-	file_code_metadata_internal_proto_rawDescOnce.Do(func() {
-		file_code_metadata_internal_proto_rawDescData = protoimpl.X.CompressGZIP(file_code_metadata_internal_proto_rawDescData)
-	})
-	return file_code_metadata_internal_proto_rawDescData
-}
-
-var file_code_metadata_internal_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_code_metadata_internal_proto_goTypes = []interface{}{
-	(*CodeMetadataInternal)(nil),                 // 0: code_metadata_internal_proto.CodeMetadataInternal
-	(*CodeMetadataInternal_TargetOwnership)(nil), // 1: code_metadata_internal_proto.CodeMetadataInternal.TargetOwnership
-}
-var file_code_metadata_internal_proto_depIdxs = []int32{
-	1, // 0: code_metadata_internal_proto.CodeMetadataInternal.target_ownership_list:type_name -> code_metadata_internal_proto.CodeMetadataInternal.TargetOwnership
-	1, // [1:1] is the sub-list for method output_type
-	1, // [1:1] is the sub-list for method input_type
-	1, // [1:1] is the sub-list for extension type_name
-	1, // [1:1] is the sub-list for extension extendee
-	0, // [0:1] is the sub-list for field type_name
-}
-
-func init() { file_code_metadata_internal_proto_init() }
-func file_code_metadata_internal_proto_init() {
-	if File_code_metadata_internal_proto != nil {
-		return
-	}
-	if !protoimpl.UnsafeEnabled {
-		file_code_metadata_internal_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CodeMetadataInternal); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_code_metadata_internal_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CodeMetadataInternal_TargetOwnership); 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_code_metadata_internal_proto_rawDesc,
-			NumEnums:      0,
-			NumMessages:   2,
-			NumExtensions: 0,
-			NumServices:   0,
-		},
-		GoTypes:           file_code_metadata_internal_proto_goTypes,
-		DependencyIndexes: file_code_metadata_internal_proto_depIdxs,
-		MessageInfos:      file_code_metadata_internal_proto_msgTypes,
-	}.Build()
-	File_code_metadata_internal_proto = out.File
-	file_code_metadata_internal_proto_rawDesc = nil
-	file_code_metadata_internal_proto_goTypes = nil
-	file_code_metadata_internal_proto_depIdxs = nil
-}
diff --git a/testing/code_metadata_internal_proto/code_metadata_internal.proto b/testing/code_metadata_internal_proto/code_metadata_internal.proto
deleted file mode 100644
index 14edc0f..0000000
--- a/testing/code_metadata_internal_proto/code_metadata_internal.proto
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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 code_metadata_internal_proto;
-option go_package = "android/soong/testing/code_metadata_internal_proto";
-
-message CodeMetadataInternal {
-
-  message TargetOwnership {
-    // REQUIRED: Name of the build target
-    optional string target_name = 1;
-
-    // REQUIRED: Code location of the target.
-    // To be used to support legacy/backup systems that use OWNERS file and is
-    // also required for our dashboard to support per code location basis UI
-    optional string path = 2;
-
-    // REQUIRED: Team ID of the team that owns this target.
-    optional string trendy_team_id = 3;
-
-    // OPTIONAL: The src files of the target.
-    // To be used to determine ownership of a file for ownership
-    repeated string source_files = 4;
-
-    // OPTIONAL: Specify if multiple ownerships of the source files are allowed.
-    optional bool multi_ownership = 5;
-  }
-
-  // List of all code targets and their metadata.
-  repeated TargetOwnership target_ownership_list = 1;
-}
diff --git a/testing/code_metadata_internal_proto/regen.sh b/testing/code_metadata_internal_proto/regen.sh
deleted file mode 100644
index f101a02..0000000
--- a/testing/code_metadata_internal_proto/regen.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-aprotoc --go_out=paths=source_relative:. code_metadata_internal.proto
diff --git a/testing/code_metadata_proto/Android.bp b/testing/code_metadata_proto/Android.bp
deleted file mode 100644
index ae41d4a..0000000
--- a/testing/code_metadata_proto/Android.bp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2022 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 {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
-    name: "soong-testing-code_metadata_proto",
-    pkgPath: "android/soong/testing/code_metadata_proto",
-    deps: [
-        "golang-protobuf-reflect-protoreflect",
-        "golang-protobuf-runtime-protoimpl",
-    ],
-    srcs: [
-        "code_metadata.pb.go",
-    ],
-    visibility: ["//build/make/tools/metadata"],
-}
-
-python_library_host {
-    name: "code-metadata-proto-py",
-    pkg_path: "code_metadata",
-    srcs: [
-        "code_metadata.proto",
-    ],
-    libs: [
-        "libprotobuf-python",
-    ],
-    proto: {
-        canonical_path_from_root: false,
-    },
-    visibility: ["//tools/asuite/team_build_scripts"],
-}
diff --git a/testing/code_metadata_proto/OWNERS b/testing/code_metadata_proto/OWNERS
deleted file mode 100644
index 03bcdf1..0000000
--- a/testing/code_metadata_proto/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-dariofreni@google.com
-joeo@google.com
-ronish@google.com
-caditya@google.com
diff --git a/testing/code_metadata_proto/code_metadata.pb.go b/testing/code_metadata_proto/code_metadata.pb.go
deleted file mode 100644
index 711bf7a..0000000
--- a/testing/code_metadata_proto/code_metadata.pb.go
+++ /dev/null
@@ -1,263 +0,0 @@
-// 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: code_metadata.proto
-
-package code_metadata_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 CodeMetadata struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// List of all code targets and their metadata.
-	TargetOwnershipList []*CodeMetadata_TargetOwnership `protobuf:"bytes,1,rep,name=target_ownership_list,json=targetOwnershipList" json:"target_ownership_list,omitempty"`
-}
-
-func (x *CodeMetadata) Reset() {
-	*x = CodeMetadata{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_code_metadata_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *CodeMetadata) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*CodeMetadata) ProtoMessage() {}
-
-func (x *CodeMetadata) ProtoReflect() protoreflect.Message {
-	mi := &file_code_metadata_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 CodeMetadata.ProtoReflect.Descriptor instead.
-func (*CodeMetadata) Descriptor() ([]byte, []int) {
-	return file_code_metadata_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *CodeMetadata) GetTargetOwnershipList() []*CodeMetadata_TargetOwnership {
-	if x != nil {
-		return x.TargetOwnershipList
-	}
-	return nil
-}
-
-type CodeMetadata_TargetOwnership struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// REQUIRED: Name of the build target
-	TargetName *string `protobuf:"bytes,1,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
-	// REQUIRED: Code location of the target.
-	// To be used to support legacy/backup systems that use OWNERS file and is
-	// also required for our dashboard to support per code location basis UI
-	Path *string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
-	// REQUIRED: Team ID of the team that owns this target.
-	TrendyTeamId *string `protobuf:"bytes,3,opt,name=trendy_team_id,json=trendyTeamId" json:"trendy_team_id,omitempty"`
-	// OPTIONAL: The src files of the target.
-	// To be used to determine ownership of a file for ownership
-	SourceFiles []string `protobuf:"bytes,4,rep,name=source_files,json=sourceFiles" json:"source_files,omitempty"`
-}
-
-func (x *CodeMetadata_TargetOwnership) Reset() {
-	*x = CodeMetadata_TargetOwnership{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_code_metadata_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *CodeMetadata_TargetOwnership) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*CodeMetadata_TargetOwnership) ProtoMessage() {}
-
-func (x *CodeMetadata_TargetOwnership) ProtoReflect() protoreflect.Message {
-	mi := &file_code_metadata_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 CodeMetadata_TargetOwnership.ProtoReflect.Descriptor instead.
-func (*CodeMetadata_TargetOwnership) Descriptor() ([]byte, []int) {
-	return file_code_metadata_proto_rawDescGZIP(), []int{0, 0}
-}
-
-func (x *CodeMetadata_TargetOwnership) GetTargetName() string {
-	if x != nil && x.TargetName != nil {
-		return *x.TargetName
-	}
-	return ""
-}
-
-func (x *CodeMetadata_TargetOwnership) GetPath() string {
-	if x != nil && x.Path != nil {
-		return *x.Path
-	}
-	return ""
-}
-
-func (x *CodeMetadata_TargetOwnership) GetTrendyTeamId() string {
-	if x != nil && x.TrendyTeamId != nil {
-		return *x.TrendyTeamId
-	}
-	return ""
-}
-
-func (x *CodeMetadata_TargetOwnership) GetSourceFiles() []string {
-	if x != nil {
-		return x.SourceFiles
-	}
-	return nil
-}
-
-var File_code_metadata_proto protoreflect.FileDescriptor
-
-var file_code_metadata_proto_rawDesc = []byte{
-	0x0a, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61,
-	0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x87, 0x02, 0x0a, 0x0c, 0x43,
-	0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x65, 0x0a, 0x15, 0x74,
-	0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x5f,
-	0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, 0x64,
-	0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x2e, 0x43, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61,
-	0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x13, 0x74,
-	0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x4c, 0x69,
-	0x73, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x0f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e,
-	0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
-	0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72,
-	0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x74,
-	0x72, 0x65, 0x6e, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x54, 0x65, 0x61, 0x6d, 0x49,
-	0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65,
-	0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46,
-	0x69, 0x6c, 0x65, 0x73, 0x42, 0x2b, 0x5a, 0x29, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
-	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x63, 0x6f,
-	0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74,
-	0x6f,
-}
-
-var (
-	file_code_metadata_proto_rawDescOnce sync.Once
-	file_code_metadata_proto_rawDescData = file_code_metadata_proto_rawDesc
-)
-
-func file_code_metadata_proto_rawDescGZIP() []byte {
-	file_code_metadata_proto_rawDescOnce.Do(func() {
-		file_code_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_code_metadata_proto_rawDescData)
-	})
-	return file_code_metadata_proto_rawDescData
-}
-
-var file_code_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_code_metadata_proto_goTypes = []interface{}{
-	(*CodeMetadata)(nil),                 // 0: code_metadata_proto.CodeMetadata
-	(*CodeMetadata_TargetOwnership)(nil), // 1: code_metadata_proto.CodeMetadata.TargetOwnership
-}
-var file_code_metadata_proto_depIdxs = []int32{
-	1, // 0: code_metadata_proto.CodeMetadata.target_ownership_list:type_name -> code_metadata_proto.CodeMetadata.TargetOwnership
-	1, // [1:1] is the sub-list for method output_type
-	1, // [1:1] is the sub-list for method input_type
-	1, // [1:1] is the sub-list for extension type_name
-	1, // [1:1] is the sub-list for extension extendee
-	0, // [0:1] is the sub-list for field type_name
-}
-
-func init() { file_code_metadata_proto_init() }
-func file_code_metadata_proto_init() {
-	if File_code_metadata_proto != nil {
-		return
-	}
-	if !protoimpl.UnsafeEnabled {
-		file_code_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CodeMetadata); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_code_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CodeMetadata_TargetOwnership); 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_code_metadata_proto_rawDesc,
-			NumEnums:      0,
-			NumMessages:   2,
-			NumExtensions: 0,
-			NumServices:   0,
-		},
-		GoTypes:           file_code_metadata_proto_goTypes,
-		DependencyIndexes: file_code_metadata_proto_depIdxs,
-		MessageInfos:      file_code_metadata_proto_msgTypes,
-	}.Build()
-	File_code_metadata_proto = out.File
-	file_code_metadata_proto_rawDesc = nil
-	file_code_metadata_proto_goTypes = nil
-	file_code_metadata_proto_depIdxs = nil
-}
diff --git a/testing/code_metadata_proto/code_metadata.proto b/testing/code_metadata_proto/code_metadata.proto
deleted file mode 100644
index 2548363..0000000
--- a/testing/code_metadata_proto/code_metadata.proto
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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 code_metadata_proto;
-option go_package = "android/soong/testing/code_metadata_proto";
-
-message CodeMetadata {
-
-  message TargetOwnership {
-    // REQUIRED: Name of the build target
-    optional string target_name = 1;
-
-    // REQUIRED: Code location of the target.
-    // To be used to support legacy/backup systems that use OWNERS file and is
-    // also required for our dashboard to support per code location basis UI
-    optional string path = 2;
-
-    // REQUIRED: Team ID of the team that owns this target.
-    optional string trendy_team_id = 3;
-
-    // OPTIONAL: The src files of the target.
-    // To be used to determine ownership of a file for ownership
-    repeated string source_files = 4;
-  }
-
-  // List of all code targets and their metadata.
-  repeated TargetOwnership target_ownership_list = 1;
-}
diff --git a/testing/code_metadata_proto/regen.sh b/testing/code_metadata_proto/regen.sh
deleted file mode 100644
index ffe06f7..0000000
--- a/testing/code_metadata_proto/regen.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-aprotoc --go_out=paths=source_relative:. code_metadata.proto
diff --git a/testing/init.go b/testing/init.go
deleted file mode 100644
index edcbf59..0000000
--- a/testing/init.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2022 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 testing
-
-import (
-	"android/soong/android"
-)
-
-var (
-	pctx = android.NewPackageContext("android/soong/testing")
-)
-
-func init() {
-	RegisterBuildComponents(android.InitRegistrationContext)
-	pctx.HostBinToolVariable("metadata", "metadata")
-}
-
-func RegisterBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("code_metadata", CodeMetadataFactory)
-	ctx.RegisterModuleType("test_spec", TestSpecFactory)
-	ctx.RegisterParallelSingletonType("all_code_metadata", AllCodeMetadataFactory)
-	ctx.RegisterParallelSingletonType("all_test_specs", AllTestSpecsFactory)
-}
diff --git a/testing/test.go b/testing/test.go
deleted file mode 100644
index cd97a8f..0000000
--- a/testing/test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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 testing
-
-import (
-	"android/soong/android"
-)
-
-var PrepareForTestWithTestingBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents)
diff --git a/testing/test_spec.go b/testing/test_spec.go
deleted file mode 100644
index 4d885c6..0000000
--- a/testing/test_spec.go
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2020 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 testing
-
-import (
-	"path/filepath"
-	"strconv"
-
-	"android/soong/android"
-	"android/soong/testing/test_spec_proto"
-	"google.golang.org/protobuf/proto"
-
-	"github.com/google/blueprint"
-)
-
-// ErrTestModuleDataNotFound is the error message for missing test module provider data.
-const ErrTestModuleDataNotFound = "The module '%s' does not provide test specification data. Hint: This issue could arise if either the module is not a valid testing module or if it lacks the required 'TestModuleProviderKey' provider.\n"
-
-func TestSpecFactory() android.Module {
-	module := &TestSpecModule{}
-
-	android.InitAndroidModule(module)
-	android.InitDefaultableModule(module)
-	module.AddProperties(&module.properties)
-
-	return module
-}
-
-type TestSpecModule struct {
-	android.ModuleBase
-	android.DefaultableModuleBase
-
-	// Properties for "test_spec"
-	properties struct {
-		// Specifies the name of the test config.
-		Name string
-		// Specifies the team ID.
-		TeamId string
-		// Specifies the list of tests covered under this module.
-		Tests []string
-	}
-}
-
-type testsDepTagType struct {
-	blueprint.BaseDependencyTag
-}
-
-var testsDepTag = testsDepTagType{}
-
-func (module *TestSpecModule) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// Validate Properties
-	if len(module.properties.TeamId) == 0 {
-		ctx.PropertyErrorf("TeamId", "Team Id not found in the test_spec module. Hint: Maybe the TeamId property hasn't been properly specified.")
-	}
-	if !isInt(module.properties.TeamId) {
-		ctx.PropertyErrorf("TeamId", "Invalid value for Team ID. The Team ID must be an integer.")
-	}
-	if len(module.properties.Tests) == 0 {
-		ctx.PropertyErrorf("Tests", "Expected to attribute some test but none found. Hint: Maybe the test property hasn't been properly specified.")
-	}
-	ctx.AddDependency(ctx.Module(), testsDepTag, module.properties.Tests...)
-}
-func isInt(s string) bool {
-	_, err := strconv.Atoi(s)
-	return err == nil
-}
-
-// Provider published by TestSpec
-type TestSpecProviderData struct {
-	IntermediatePath android.WritablePath
-}
-
-var TestSpecProviderKey = blueprint.NewProvider[TestSpecProviderData]()
-
-type TestModuleProviderData struct {
-}
-
-var TestModuleProviderKey = blueprint.NewProvider[TestModuleProviderData]()
-
-func (module *TestSpecModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	for _, m := range ctx.GetDirectDepsWithTag(testsDepTag) {
-		if _, ok := android.OtherModuleProvider(ctx, m, TestModuleProviderKey); !ok {
-			ctx.ModuleErrorf(ErrTestModuleDataNotFound, m.Name())
-		}
-	}
-	bpFilePath := filepath.Join(ctx.ModuleDir(), ctx.BlueprintsFile())
-	metadataList := make(
-		[]*test_spec_proto.TestSpec_OwnershipMetadata, 0,
-		len(module.properties.Tests),
-	)
-	for _, test := range module.properties.Tests {
-		targetName := test
-		metadata := test_spec_proto.TestSpec_OwnershipMetadata{
-			TrendyTeamId: &module.properties.TeamId,
-			TargetName:   &targetName,
-			Path:         &bpFilePath,
-		}
-		metadataList = append(metadataList, &metadata)
-	}
-	intermediatePath := android.PathForModuleOut(
-		ctx, "intermediateTestSpecMetadata.pb",
-	)
-	testSpecMetadata := test_spec_proto.TestSpec{OwnershipMetadataList: metadataList}
-	protoData, err := proto.Marshal(&testSpecMetadata)
-	if err != nil {
-		ctx.ModuleErrorf("Error: %s", err.Error())
-	}
-	android.WriteFileRuleVerbatim(ctx, intermediatePath, string(protoData))
-
-	android.SetProvider(ctx,
-		TestSpecProviderKey, TestSpecProviderData{
-			IntermediatePath: intermediatePath,
-		},
-	)
-}
diff --git a/testing/test_spec_proto/Android.bp b/testing/test_spec_proto/Android.bp
deleted file mode 100644
index 1070d1a..0000000
--- a/testing/test_spec_proto/Android.bp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2022 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 {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
-    name: "soong-testing-test_spec_proto",
-    pkgPath: "android/soong/testing/test_spec_proto",
-    deps: [
-        "golang-protobuf-reflect-protoreflect",
-        "golang-protobuf-runtime-protoimpl",
-    ],
-    srcs: [
-        "test_spec.pb.go",
-    ],
-    visibility: [
-        "//build/make/tools/metadata",
-        "//build/soong:__subpackages__",
-        "//vendor:__subpackages__",
-    ],
-}
-
-python_library_host {
-    name: "test-spec-proto-py",
-    pkg_path: "test_spec",
-    srcs: [
-        "test_spec.proto",
-    ],
-    libs: [
-        "libprotobuf-python",
-    ],
-    proto: {
-        canonical_path_from_root: false,
-    },
-    visibility: ["//tools/asuite/team_build_scripts"],
-}
diff --git a/testing/test_spec_proto/OWNERS b/testing/test_spec_proto/OWNERS
deleted file mode 100644
index 03bcdf1..0000000
--- a/testing/test_spec_proto/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-dariofreni@google.com
-joeo@google.com
-ronish@google.com
-caditya@google.com
diff --git a/testing/test_spec_proto/regen.sh b/testing/test_spec_proto/regen.sh
deleted file mode 100644
index 2cf8203..0000000
--- a/testing/test_spec_proto/regen.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-aprotoc --go_out=paths=source_relative:. test_spec.proto
diff --git a/testing/test_spec_proto/test_spec.pb.go b/testing/test_spec_proto/test_spec.pb.go
deleted file mode 100644
index 5cce600..0000000
--- a/testing/test_spec_proto/test_spec.pb.go
+++ /dev/null
@@ -1,244 +0,0 @@
-// 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: test_spec.proto
-
-package test_spec_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 TestSpec struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// List of all test targets and their metadata.
-	OwnershipMetadataList []*TestSpec_OwnershipMetadata `protobuf:"bytes,1,rep,name=ownership_metadata_list,json=ownershipMetadataList" json:"ownership_metadata_list,omitempty"`
-}
-
-func (x *TestSpec) Reset() {
-	*x = TestSpec{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_test_spec_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *TestSpec) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*TestSpec) ProtoMessage() {}
-
-func (x *TestSpec) ProtoReflect() protoreflect.Message {
-	mi := &file_test_spec_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 TestSpec.ProtoReflect.Descriptor instead.
-func (*TestSpec) Descriptor() ([]byte, []int) {
-	return file_test_spec_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *TestSpec) GetOwnershipMetadataList() []*TestSpec_OwnershipMetadata {
-	if x != nil {
-		return x.OwnershipMetadataList
-	}
-	return nil
-}
-
-type TestSpec_OwnershipMetadata struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	TargetName   *string `protobuf:"bytes,1,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
-	Path         *string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
-	TrendyTeamId *string `protobuf:"bytes,3,opt,name=trendy_team_id,json=trendyTeamId" json:"trendy_team_id,omitempty"`
-}
-
-func (x *TestSpec_OwnershipMetadata) Reset() {
-	*x = TestSpec_OwnershipMetadata{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_test_spec_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *TestSpec_OwnershipMetadata) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*TestSpec_OwnershipMetadata) ProtoMessage() {}
-
-func (x *TestSpec_OwnershipMetadata) ProtoReflect() protoreflect.Message {
-	mi := &file_test_spec_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 TestSpec_OwnershipMetadata.ProtoReflect.Descriptor instead.
-func (*TestSpec_OwnershipMetadata) Descriptor() ([]byte, []int) {
-	return file_test_spec_proto_rawDescGZIP(), []int{0, 0}
-}
-
-func (x *TestSpec_OwnershipMetadata) GetTargetName() string {
-	if x != nil && x.TargetName != nil {
-		return *x.TargetName
-	}
-	return ""
-}
-
-func (x *TestSpec_OwnershipMetadata) GetPath() string {
-	if x != nil && x.Path != nil {
-		return *x.Path
-	}
-	return ""
-}
-
-func (x *TestSpec_OwnershipMetadata) GetTrendyTeamId() string {
-	if x != nil && x.TrendyTeamId != nil {
-		return *x.TrendyTeamId
-	}
-	return ""
-}
-
-var File_test_spec_proto protoreflect.FileDescriptor
-
-var file_test_spec_proto_rawDesc = []byte{
-	0x0a, 0x0f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x12, 0x0f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x22, 0xdf, 0x01, 0x0a, 0x08, 0x54, 0x65, 0x73, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12,
-	0x63, 0x0a, 0x17, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x5f, 0x6d, 0x65, 0x74,
-	0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x2b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x4f, 0x77, 0x6e, 0x65,
-	0x72, 0x73, 0x68, 0x69, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x15, 0x6f,
-	0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
-	0x4c, 0x69, 0x73, 0x74, 0x1a, 0x6e, 0x0a, 0x11, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69,
-	0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72,
-	0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
-	0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61,
-	0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24,
-	0x0a, 0x0e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64,
-	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x54, 0x65,
-	0x61, 0x6d, 0x49, 0x64, 0x42, 0x27, 0x5a, 0x25, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
-	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x74, 0x65,
-	0x73, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-}
-
-var (
-	file_test_spec_proto_rawDescOnce sync.Once
-	file_test_spec_proto_rawDescData = file_test_spec_proto_rawDesc
-)
-
-func file_test_spec_proto_rawDescGZIP() []byte {
-	file_test_spec_proto_rawDescOnce.Do(func() {
-		file_test_spec_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_spec_proto_rawDescData)
-	})
-	return file_test_spec_proto_rawDescData
-}
-
-var file_test_spec_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_test_spec_proto_goTypes = []interface{}{
-	(*TestSpec)(nil),                   // 0: test_spec_proto.TestSpec
-	(*TestSpec_OwnershipMetadata)(nil), // 1: test_spec_proto.TestSpec.OwnershipMetadata
-}
-var file_test_spec_proto_depIdxs = []int32{
-	1, // 0: test_spec_proto.TestSpec.ownership_metadata_list:type_name -> test_spec_proto.TestSpec.OwnershipMetadata
-	1, // [1:1] is the sub-list for method output_type
-	1, // [1:1] is the sub-list for method input_type
-	1, // [1:1] is the sub-list for extension type_name
-	1, // [1:1] is the sub-list for extension extendee
-	0, // [0:1] is the sub-list for field type_name
-}
-
-func init() { file_test_spec_proto_init() }
-func file_test_spec_proto_init() {
-	if File_test_spec_proto != nil {
-		return
-	}
-	if !protoimpl.UnsafeEnabled {
-		file_test_spec_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*TestSpec); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_test_spec_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*TestSpec_OwnershipMetadata); 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_test_spec_proto_rawDesc,
-			NumEnums:      0,
-			NumMessages:   2,
-			NumExtensions: 0,
-			NumServices:   0,
-		},
-		GoTypes:           file_test_spec_proto_goTypes,
-		DependencyIndexes: file_test_spec_proto_depIdxs,
-		MessageInfos:      file_test_spec_proto_msgTypes,
-	}.Build()
-	File_test_spec_proto = out.File
-	file_test_spec_proto_rawDesc = nil
-	file_test_spec_proto_goTypes = nil
-	file_test_spec_proto_depIdxs = nil
-}
diff --git a/testing/test_spec_proto/test_spec.proto b/testing/test_spec_proto/test_spec.proto
deleted file mode 100644
index 86bc789..0000000
--- a/testing/test_spec_proto/test_spec.proto
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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 test_spec_proto;
-option go_package = "android/soong/testing/test_spec_proto";
-
-message TestSpec {
-
-  message OwnershipMetadata {
-    // REQUIRED: Name of the build target
-    optional string target_name = 1;
-
-    // REQUIRED: Code location of the target.
-    // To be used to support legacy/backup systems that use OWNERS file and is
-    // also required for our dashboard to support per code location basis UI
-    optional string path = 2;
-
-    // REQUIRED: Team ID of the team that owns this target.
-    optional string trendy_team_id = 3;
-  }
-
-  // List of all test targets and their metadata.
-  repeated OwnershipMetadata ownership_metadata_list = 1;
-}
diff --git a/tests/b_args_test.sh b/tests/b_args_test.sh
deleted file mode 100755
index 0dfbabf..0000000
--- a/tests/b_args_test.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/bash -eu
-
-# This file tests the creation of bazel commands for b usage
-set -o pipefail
-source "$(dirname "$0")/../../bazel/lib.sh"
-
-BES_UUID="blank"
-OUT_DIR="arbitrary_out"
-b_args=$(formulate_b_args "build --config=nonsense foo:bar")
-
-if [[ $b_args != "build --profile=$OUT_DIR/bazel_metrics-profile --config=bp2build --invocation_id=$BES_UUID --config=metrics_data --config=nonsense foo:bar" ]]; then
-   echo "b args are malformed"
-   echo "Expected : build --profile=$OUT_DIR/bazel_metrics-profile --config=bp2build  --invocation_id=$BES_UUID --config=metrics_data --config=nonsense foo:bar"
-   echo "Actual: $b_args"
-   exit 1
-fi
-
-b_args=$(formulate_b_args "build --config=nonsense --disable_bes --package_path \"my package\" foo:bar")
-
-if [[ $b_args != "build --profile=$OUT_DIR/bazel_metrics-profile --config=bp2build --invocation_id=$BES_UUID --config=nonsense --package_path \"my package\" foo:bar" ]]; then
-   echo "b args are malformed"
-   echo "Expected : build --profile=$OUT_DIR/bazel_metrics-profile --config=bp2build  --invocation_id=$BES_UUID --config=nonsense --package_path \"my package\" foo:bar"
-   echo "Actual: $b_args"
-   exit 1
-fi
-
-# Test with startup option
-b_args=$(formulate_b_args "--batch build --config=nonsense --disable_bes --package_path \"my package\" foo:bar")
-if [[ $b_args != "--batch build --profile=$OUT_DIR/bazel_metrics-profile --config=bp2build --invocation_id=$BES_UUID --config=nonsense --package_path \"my package\" foo:bar" ]]; then
-   echo "b args are malformed"
-   echo "Expected : --batch build --profile=$OUT_DIR/bazel_metrics-profile --config=bp2build  --invocation_id=$BES_UUID --config=nonsense --package_path \"my package\" foo:bar"
-   echo "Actual: $b_args"
-   exit 1
-fi
-
-OUT_DIR="mock_out"
-TEST_PROFILE_OUT=$(get_profile_out_dir)
-if [[ $TEST_PROFILE_OUT != "mock_out" ]]; then
-   echo "Profile Out is malformed."
-   echo "Expected: mock_out"
-   echo "Actual: $TEST_PROFILE_OUT"
-   exit 1
-fi
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 715f976..5a660e9 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -577,20 +577,6 @@
 
 }
 
-function test_queryview_null_build() {
-  setup
-
-  run_soong queryview
-  local -r output_mtime1=$(stat -c "%y" out/soong/queryview.marker)
-
-  run_soong queryview
-  local -r output_mtime2=$(stat -c "%y" out/soong/queryview.marker)
-
-  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
-    fail "Queryview marker file changed on null build"
-  fi
-}
-
 # This test verifies that adding a new glob to a blueprint file only
 # causes build."${target_product}".ninja to be regenerated on the *next* build, and *not*
 # the build after. (This is a regression test for a bug where globs
diff --git a/tests/lib.sh b/tests/lib.sh
index 4c320d0..0e26de5 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -91,7 +91,6 @@
 }
 
 function create_mock_soong {
-  create_mock_bazel
   copy_directory build/blueprint
   copy_directory build/soong
   copy_directory build/make
@@ -143,41 +142,6 @@
   USE_RBE=false TARGET_PRODUCT=aosp_arm TARGET_RELEASE=trunk_staging TARGET_BUILD_VARIANT=userdebug build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
 }
 
-function create_mock_bazel {
-  copy_directory build/bazel
-  copy_directory build/bazel_common_rules
-
-  # This requires pulling more tools into the mock top to build partitions
-  delete_directory build/bazel/examples/partitions
-
-  symlink_directory packages/modules/common/build
-  symlink_directory prebuilts/bazel
-  symlink_directory prebuilts/clang
-  symlink_directory prebuilts/jdk
-  symlink_directory external/bazel-skylib
-  symlink_directory external/bazelbuild-rules_android
-  symlink_directory external/bazelbuild-rules_go
-  symlink_directory external/bazelbuild-rules_license
-  symlink_directory external/bazelbuild-kotlin-rules
-  symlink_directory external/bazelbuild-rules_cc
-  symlink_directory external/bazelbuild-rules_python
-  symlink_directory external/bazelbuild-rules_java
-  symlink_directory external/bazelbuild-rules_rust
-  symlink_directory external/bazelbuild-rules_testing
-  symlink_directory external/rust/crates/tinyjson
-
-  symlink_file WORKSPACE
-  symlink_file BUILD
-}
-
-function run_bazel {
-  # Remove the ninja_build output marker file to communicate to buildbot that this is not a regular Ninja build, and its
-  # output should not be parsed as such.
-  rm -rf out/ninja_build
-
-  build/bazel/bin/bazel "$@"
-}
-
 function run_ninja {
   build/soong/soong_ui.bash --make-mode --skip-config --soong-only --skip-soong-tests "$@"
 }
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index ddd0a80..e230795 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -196,16 +196,16 @@
 }
 
 var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{
-	Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}",
+	Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs} ${extraTestRunnerConfigs}",
 	CommandDeps: []string{
 		"${AutoGenTestConfigScript}",
 		"${EmptyTestConfig}",
 		"$template",
 	},
-}, "name", "template", "extraConfigs")
+}, "name", "template", "extraConfigs", "extraTestRunnerConfigs")
 
 func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config) android.Path {
+	testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config, testRunnerConfigs []Option) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
 	var configStrings []string
 	if autogenPath != nil {
@@ -220,15 +220,26 @@
 		extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent))
 		extraConfigs = fmt.Sprintf("--extra-configs '%s'", extraConfigs)
 
+		var testRunnerConfigStrings []string
+		for _, config := range testRunnerConfigs {
+			testRunnerConfigStrings = append(testRunnerConfigStrings, config.Config())
+		}
+		extraTestRunnerConfigs := strings.Join(testRunnerConfigStrings, fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent))
+		if len(extraTestRunnerConfigs) > 0 {
+			extraTestRunnerConfigs += fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent)
+		}
+		extraTestRunnerConfigs = fmt.Sprintf("--extra-test-runner-configs '%s'", extraTestRunnerConfigs)
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        autogenInstrumentationTest,
 			Description: "test config",
 			Input:       manifest,
 			Output:      autogenPath,
 			Args: map[string]string{
-				"name":         ctx.ModuleName(),
-				"template":     template,
-				"extraConfigs": extraConfigs,
+				"name":                   ctx.ModuleName(),
+				"template":               template,
+				"extraConfigs":           extraConfigs,
+				"extraTestRunnerConfigs": extraTestRunnerConfigs,
 			},
 		})
 		return autogenPath
diff --git a/tradefed/providers.go b/tradefed/providers.go
index 0abac12..0ae841d 100644
--- a/tradefed/providers.go
+++ b/tradefed/providers.go
@@ -9,8 +9,8 @@
 // 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
+	// data files and apps installed for tests, relative to testcases dir.
+	TestcaseRelDataFiles []string
 	// apk for android_test
 	OutputFile android.Path
 	// Either handwritten or generated TF xml.
@@ -28,6 +28,12 @@
 	LocalCertificate string
 	// Indicates if the base module was a unit test.
 	IsUnitTest bool
+	// The .mk file is used AndroidMkEntries for base (soong_java_prebuilt, etc.)
+	MkInclude string
+	// The AppClass to use for the AndroidMkEntries for the base.
+	MkAppClass string
+	// value for LOCAL_MODULE_PATH.  The directory where the module is installed.
+	InstallDir android.InstallPath
 }
 
 var BaseTestProviderKey = blueprint.NewProvider[BaseTestProviderData]()
diff --git a/tradefed_modules/Android.bp b/tradefed_modules/Android.bp
index 9969ae2..a765a05 100644
--- a/tradefed_modules/Android.bp
+++ b/tradefed_modules/Android.bp
@@ -9,13 +9,16 @@
         "blueprint",
         "soong-android",
         "soong-java",
+        "soong-sh",
         "soong-tradefed",
     ],
     srcs: [
         "test_module_config.go",
+        "test_suite.go",
     ],
     testSrcs: [
         "test_module_config_test.go",
+        "test_suite_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index 7a04c19..988352c 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -196,7 +196,7 @@
 	module := &testModuleConfigModule{}
 
 	module.AddProperties(&module.tradefedProperties)
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
 	android.InitDefaultableModule(module)
 
 	return module
@@ -216,13 +216,28 @@
 // Implements android.AndroidMkEntriesProvider
 var _ android.AndroidMkEntriesProvider = (*testModuleConfigModule)(nil)
 
+func (m *testModuleConfigModule) nativeExtraEntries(entries *android.AndroidMkEntries) {
+	// TODO(ron) provider for suffix and STEM?
+	entries.SetString("LOCAL_MODULE_SUFFIX", "")
+	// Should the stem and path use the base name or our module name?
+	entries.SetString("LOCAL_MODULE_STEM", m.provider.OutputFile.Rel())
+	entries.SetPath("LOCAL_MODULE_PATH", m.provider.InstallDir)
+}
+
+func (m *testModuleConfigModule) javaExtraEntries(entries *android.AndroidMkEntries) {
+	// 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))
+	entries.SetString("LOCAL_MODULE_TAGS", "tests")
+}
+
 func (m *testModuleConfigModule) AndroidMkEntries() []android.AndroidMkEntries {
-	appClass := "APPS"
-	include := "$(BUILD_SYSTEM)/soong_app_prebuilt.mk"
-	if m.isHost {
-		appClass = "JAVA_LIBRARIES"
-		include = "$(BUILD_SYSTEM)/soong_java_prebuilt.mk"
-	}
+	appClass := m.provider.MkAppClass
+	include := m.provider.MkInclude
 	return []android.AndroidMkEntries{{
 		Class:      appClass,
 		OutputFile: android.OptionalPathForPath(m.manifest),
@@ -231,7 +246,6 @@
 		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)
@@ -244,13 +258,11 @@
 				entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
 				entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", m.provider.HostRequiredModuleNames...)
 
-				// 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 m.provider.MkAppClass == "NATIVE_TESTS" {
+					m.nativeExtraEntries(entries)
+				} else {
+					m.javaExtraEntries(entries)
+				}
 
 				// 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.
@@ -357,16 +369,19 @@
 	// 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()
+	if m.provider.MkAppClass != "NATIVE_TESTS" {
+		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)
 	}
-	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
@@ -374,8 +389,7 @@
 	// ├── data
 	// │   └── broken_shortcut.xml
 	// ├── JobTestApp.apk
-	for _, f := range m.provider.InstalledFiles {
-		symlinkName := f.Rel()
+	for _, symlinkName := range m.provider.TestcaseRelDataFiles {
 		target := installedBaseRelativeToHere(symlinkName, *m.tradefedProperties.Base)
 		installedPath := ctx.InstallAbsoluteSymlink(installDir, symlinkName, target)
 		m.supportFiles = append(m.supportFiles, installedPath)
@@ -383,10 +397,31 @@
 
 	// 4) Module.config / AndroidTest.xml
 	m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig)
+
+	// 5) We provide so we can be listed in test_suites.
+	android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
+		TestcaseRelDataFiles:    testcaseRel(m.supportFiles.Paths()),
+		OutputFile:              baseApk,
+		TestConfig:              m.testConfig,
+		HostRequiredModuleNames: m.provider.HostRequiredModuleNames,
+		RequiredModuleNames:     m.provider.RequiredModuleNames,
+		TestSuites:              m.tradefedProperties.Test_suites,
+		IsHost:                  m.provider.IsHost,
+		LocalCertificate:        m.provider.LocalCertificate,
+		IsUnitTest:              m.provider.IsUnitTest,
+	})
 }
 
 var _ android.AndroidMkEntriesProvider = (*testModuleConfigHostModule)(nil)
 
+func testcaseRel(paths android.Paths) []string {
+	relPaths := []string{}
+	for _, p := range paths {
+		relPaths = append(relPaths, p.Rel())
+	}
+	return relPaths
+}
+
 // 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".
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index f76a152..efd4a04 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"
+	"android/soong/sh"
 	"fmt"
 	"strconv"
 	"strings"
@@ -54,6 +55,8 @@
 
 `
 
+const variant = "android_arm64_armv8-a"
+
 // Ensure we create files needed and set the AndroidMkEntries needed
 func TestModuleConfigAndroidTest(t *testing.T) {
 
@@ -62,7 +65,7 @@
 		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
 	).RunTestWithBp(t, bp)
 
-	derived := ctx.ModuleForTests("derived_test", "android_common")
+	derived := ctx.ModuleForTests("derived_test", variant)
 	// Assert there are rules to create these files.
 	derived.Output("test_module_config.manifest")
 	derived.Output("test_config_fixer/derived_test.config")
@@ -88,7 +91,7 @@
 	// And some new derived entries are there.
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE_TAGS"], []string{"tests"})
 
-	android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config")
+	android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], fmt.Sprintf("derived_test/%s/test_config_fixer/derived_test.config", variant))
 
 	// Check the footer lines.  Our support files should depend on base's support files.
 	convertedActual := make([]string, 5)
@@ -105,6 +108,80 @@
 	})
 }
 
+func TestModuleConfigShTest(t *testing.T) {
+	ctx := android.GroupFixturePreparers(
+		sh.PrepareForTestWithShBuildComponents,
+		android.PrepareForTestWithAndroidBuildComponents,
+		android.FixtureMergeMockFs(android.MockFS{
+			"test.sh":            nil,
+			"testdata/data1":     nil,
+			"testdata/sub/data2": nil,
+		}),
+		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+	).RunTestWithBp(t, `
+		sh_test {
+			name: "shell_test",
+			src: "test.sh",
+			filename: "test.sh",
+                        test_suites: ["general-tests"],
+			data: [
+				"testdata/data1",
+				"testdata/sub/data2",
+			],
+		}
+                test_module_config {
+                        name: "conch",
+                        base: "shell_test",
+                        test_suites: ["general-tests"],
+                        options: [{name: "SomeName", value: "OptionValue"}],
+                }
+         `)
+	derived := ctx.ModuleForTests("conch", variant) //
+	conch := derived.Module().(*testModuleConfigModule)
+	android.AssertArrayString(t, "TestcaseRelDataFiles", []string{"arm64/testdata/data1", "arm64/testdata/sub/data2"}, conch.provider.TestcaseRelDataFiles)
+	android.AssertStringEquals(t, "Rel OutputFile", "test.sh", conch.provider.OutputFile.Rel())
+
+	// Assert there are rules to create these files.
+	derived.Output("test_module_config.manifest")
+	derived.Output("test_config_fixer/conch.config")
+
+	// Ensure some basic rules exist.
+	entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
+
+	// Ensure some entries from base are there, specifically support files for data and helper apps.
+	// 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/conch/arm64/testdata/data1",
+			"out/soong/target/product/test_device/testcases/conch/arm64/testdata/sub/data2"},
+		entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{})
+
+	android.AssertStringEquals(t, "app class", "NATIVE_TESTS", entries.Class)
+	android.AssertArrayString(t, "required modules", []string{"shell_test"}, entries.EntryMap["LOCAL_REQUIRED_MODULES"])
+	android.AssertArrayString(t, "host required modules", []string{}, entries.EntryMap["LOCAL_HOST_REQUIRED_MODULES"])
+	android.AssertArrayString(t, "cert", []string{}, entries.EntryMap["LOCAL_CERTIFICATE"])
+
+	// And some new derived entries are there.
+	android.AssertArrayString(t, "tags", []string{}, entries.EntryMap["LOCAL_MODULE_TAGS"])
+
+	android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0],
+		fmt.Sprintf("conch/%s/test_config_fixer/conch.config", variant))
+
+	// Check the footer lines.  Our support files should depend on base's support files.
+	convertedActual := make([]string, 4)
+	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_cc_rust_prebuilt.mk",
+		"/target/product/test_device/testcases/conch/arm64/testdata/data1: /target/product/test_device/testcases/shell_test/arm64/testdata/data1",
+		"/target/product/test_device/testcases/conch/arm64/testdata/sub/data2: /target/product/test_device/testcases/shell_test/arm64/testdata/sub/data2",
+		"",
+	})
+
+}
+
 // Make sure we call test-config-fixer with the right args.
 func TestModuleConfigOptions(t *testing.T) {
 
@@ -114,7 +191,7 @@
 	).RunTestWithBp(t, bp)
 
 	// Check that we generate a rule to make a new AndroidTest.xml/Module.config file.
-	derived := ctx.ModuleForTests("derived_test", "android_common")
+	derived := ctx.ModuleForTests("derived_test", variant)
 	rule_cmd := derived.Rule("fix_test_config").RuleParams.Command
 	android.AssertStringDoesContain(t, "Bad FixConfig rule inputs", rule_cmd,
 		`--test-runner-options='[{"Name":"exclude-filter","Key":"","Value":"android.test.example.devcodelab.DevCodelabTest#testHelloFail"},{"Name":"include-annotation","Key":"","Value":"android.platform.test.annotations.LargeTest"}]'`)
@@ -123,24 +200,24 @@
 // Ensure we error for a base we don't support.
 func TestModuleConfigWithHostBaseShouldFailWithExplicitMessage(t *testing.T) {
 	badBp := `
-		java_test_host {
-			name: "base",
-                        srcs: ["a.java"],
+        java_test {
+            name: "base",
+            srcs: ["a.java"],
 		}
 
-                test_module_config {
-                        name: "derived_test",
-                        base: "base",
-                        exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
-                        include_annotations: ["android.platform.test.annotations.LargeTest"],
-                        test_suites: ["general-tests"],
-                }`
+        test_module_config {
+            name: "derived_test",
+            base: "base",
+            exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+            include_annotations: ["android.platform.test.annotations.LargeTest"],
+            test_suites: ["general-tests"],
+        }`
 
 	android.GroupFixturePreparers(
 		java.PrepareForTestWithJavaDefaultModules,
 		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
 	).ExtendWithErrorHandler(
-		android.FixtureExpectsAtLeastOneErrorMatchingPattern("'java_test_host' module used as base, but 'android_test' expected")).
+		android.FixtureExpectsAtLeastOneErrorMatchingPattern("'base' module used as base but it is not a 'android_test' module.")).
 		RunTestWithBp(t, badBp)
 }
 
@@ -211,8 +288,7 @@
 	).ExtendWithErrorHandler(
 		android.FixtureExpectsAtLeastOneErrorMatchingPattern("Test options must be given")).
 		RunTestWithBp(t, badBp)
-
-	ctx.ModuleForTests("derived_test", "android_common")
+	ctx.ModuleForTests("derived_test", variant)
 }
 
 func TestModuleConfigMultipleDerivedTestsWriteDistinctMakeEntries(t *testing.T) {
@@ -250,7 +326,7 @@
 	).RunTestWithBp(t, multiBp)
 
 	{
-		derived := ctx.ModuleForTests("derived_test", "android_common")
+		derived := ctx.ModuleForTests("derived_test", variant)
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 		// All these should be the same in both derived tests
 		android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
@@ -260,13 +336,13 @@
 			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")
+		android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], fmt.Sprintf("derived_test/%s/test_config_fixer/derived_test.config", variant))
 		// And this one, the module name.
 		android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE"], []string{"derived_test"})
 	}
 
 	{
-		derived := ctx.ModuleForTests("another_derived_test", "android_common")
+		derived := ctx.ModuleForTests("another_derived_test", variant)
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 		// All these should be the same in both derived tests
 		android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
@@ -275,7 +351,8 @@
 				"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")
+		android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0],
+			fmt.Sprintf("another_derived_test/%s/test_config_fixer/another_derived_test.config", variant))
 		// And this one, the module name.
 		android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE"], []string{"another_derived_test"})
 	}
diff --git a/tradefed_modules/test_suite.go b/tradefed_modules/test_suite.go
new file mode 100644
index 0000000..00585f5
--- /dev/null
+++ b/tradefed_modules/test_suite.go
@@ -0,0 +1,173 @@
+// 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 tradefed_modules
+
+import (
+	"encoding/json"
+	"path"
+	"path/filepath"
+
+	"android/soong/android"
+	"android/soong/tradefed"
+	"github.com/google/blueprint"
+)
+
+const testSuiteModuleType = "test_suite"
+
+type testSuiteTag struct{
+	blueprint.BaseDependencyTag
+}
+
+type testSuiteManifest struct {
+	Name  string `json:"name"`
+	Files []string `json:"files"`
+}
+
+func init() {
+	RegisterTestSuiteBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterTestSuiteBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType(testSuiteModuleType, TestSuiteFactory)
+}
+
+var PrepareForTestWithTestSuiteBuildComponents = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+)
+
+type testSuiteProperties struct {
+	Description string
+	Tests []string `android:"path,arch_variant"`
+}
+
+type testSuiteModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	testSuiteProperties
+}
+
+func (t *testSuiteModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	for _, test := range t.Tests {
+		if ctx.OtherModuleDependencyVariantExists(ctx.Config().BuildOSCommonTarget.Variations(), test) {
+			// Host tests.
+			ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), testSuiteTag{}, test)
+		} else {
+			// Target tests.
+			ctx.AddDependency(ctx.Module(), testSuiteTag{}, test)
+		}
+	}
+}
+
+func (t *testSuiteModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	suiteName := ctx.ModuleName()
+	modulesByName := make(map[string]android.Module)
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		// Recurse into test_suite dependencies.
+		if ctx.OtherModuleType(child) == testSuiteModuleType {
+			ctx.Phony(suiteName, android.PathForPhony(ctx, child.Name()))
+			return true
+		}
+
+		// Only write out top level test suite dependencies here.
+		if _, ok := ctx.OtherModuleDependencyTag(child).(testSuiteTag); !ok {
+			return false
+		}
+
+		if !child.InstallInTestcases() {
+			ctx.ModuleErrorf("test_suite only supports modules installed in testcases. %q is not installed in testcases.", child.Name())
+			return false
+		}
+
+		modulesByName[child.Name()] = child
+		return false
+	})
+
+	var files []string
+	for name, module := range modulesByName {
+		// Get the test provider data from the child.
+		tp, ok := android.OtherModuleProvider(ctx, module, tradefed.BaseTestProviderKey)
+		if !ok {
+			// TODO: Consider printing out a list of all module types.
+			ctx.ModuleErrorf("%q is not a test module.", name)
+			continue
+		}
+
+		files = append(files, packageModuleFiles(ctx, suiteName, module, tp)...)
+		ctx.Phony(suiteName, android.PathForPhony(ctx, name))
+	}
+
+	manifestPath := android.PathForSuiteInstall(ctx, suiteName, suiteName+".json")
+	b, err := json.Marshal(testSuiteManifest{Name: suiteName, Files: files})
+	if err != nil {
+		ctx.ModuleErrorf("Failed to marshal manifest: %v", err)
+		return
+	}
+	android.WriteFileRule(ctx, manifestPath, string(b))
+
+	ctx.Phony(suiteName, manifestPath)
+}
+
+func TestSuiteFactory() android.Module {
+	module := &testSuiteModule{}
+	module.AddProperties(&module.testSuiteProperties)
+
+	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+
+	return module
+}
+
+func packageModuleFiles(ctx android.ModuleContext, suiteName string, module android.Module, tp tradefed.BaseTestProviderData) []string {
+
+	hostOrTarget := "target"
+	if tp.IsHost {
+		hostOrTarget = "host"
+	}
+
+	// suiteRoot at out/soong/packaging/<suiteName>.
+	suiteRoot := android.PathForSuiteInstall(ctx, suiteName)
+
+	var installed android.InstallPaths
+	// Install links to installed files from the module.
+	if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok {
+		for _, f := range installFilesInfo.InstallFiles {
+			// rel is anything under .../<partition>, normally under .../testcases.
+			rel := android.Rel(ctx, f.PartitionDir(), f.String())
+
+			// Install the file under <suiteRoot>/<host|target>/<partition>.
+			installDir := suiteRoot.Join(ctx, hostOrTarget, f.Partition(), path.Dir(rel))
+			linkTo, err := filepath.Rel(installDir.String(), f.String())
+			if err != nil {
+				ctx.ModuleErrorf("Failed to get relative path from %s to %s: %v", installDir.String(), f.String(), err)
+				continue
+			}
+			installed = append(installed, ctx.InstallAbsoluteSymlink(installDir, path.Base(rel), linkTo))
+		}
+	}
+
+	// Install config file.
+	if tp.TestConfig != nil {
+		moduleRoot := suiteRoot.Join(ctx, hostOrTarget, "testcases", module.Name())
+		installed = append(installed, ctx.InstallFile(moduleRoot, module.Name() + ".config", tp.TestConfig))
+	}
+
+	// Add to phony and manifest, manifestpaths are relative to suiteRoot.
+	var manifestEntries []string
+	for _, f := range installed {
+		manifestEntries = append(manifestEntries, android.Rel(ctx, suiteRoot.String(), f.String()))
+		ctx.Phony(suiteName, f)
+	}
+	return manifestEntries
+}
diff --git a/tradefed_modules/test_suite_test.go b/tradefed_modules/test_suite_test.go
new file mode 100644
index 0000000..3c0a9eb
--- /dev/null
+++ b/tradefed_modules/test_suite_test.go
@@ -0,0 +1,151 @@
+// 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 tradefed_modules
+
+import (
+	"android/soong/android"
+	"android/soong/java"
+	"encoding/json"
+	"slices"
+	"testing"
+)
+
+func TestTestSuites(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+	).RunTestWithBp(t, `
+		android_test {
+			name: "TestModule1",
+			sdk_version: "current",
+		}
+
+		android_test {
+			name: "TestModule2",
+			sdk_version: "current",
+		}
+
+		test_suite {
+			name: "my-suite",
+			description: "a test suite",
+			tests: [
+				"TestModule1",
+				"TestModule2",
+			]
+		}
+	`)
+	manifestPath := ctx.ModuleForTests("my-suite", "android_common").Output("out/soong/test_suites/my-suite/my-suite.json")
+	var actual testSuiteManifest
+	if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil {
+		t.Errorf("failed to unmarshal manifest: %v", err)
+	}
+	slices.Sort(actual.Files)
+
+	expected := testSuiteManifest{
+		Name: "my-suite",
+		Files: []string{
+			"target/testcases/TestModule1/TestModule1.config",
+			"target/testcases/TestModule1/arm64/TestModule1.apk",
+			"target/testcases/TestModule2/TestModule2.config",
+			"target/testcases/TestModule2/arm64/TestModule2.apk",
+		},
+	}
+
+	android.AssertDeepEquals(t, "manifests differ", expected, actual)
+}
+
+func TestTestSuitesWithNested(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+	).RunTestWithBp(t, `
+		android_test {
+			name: "TestModule1",
+			sdk_version: "current",
+		}
+
+		android_test {
+			name: "TestModule2",
+			sdk_version: "current",
+		}
+
+		android_test {
+			name: "TestModule3",
+			sdk_version: "current",
+		}
+
+		test_suite {
+			name: "my-child-suite",
+			description: "a child test suite",
+			tests: [
+				"TestModule1",
+				"TestModule2",
+			]
+		}
+
+		test_suite {
+			name: "my-all-tests-suite",
+			description: "a parent test suite",
+			tests: [
+				"TestModule1",
+				"TestModule3",
+				"my-child-suite",
+			]
+		}
+	`)
+	manifestPath := ctx.ModuleForTests("my-all-tests-suite", "android_common").Output("out/soong/test_suites/my-all-tests-suite/my-all-tests-suite.json")
+	var actual testSuiteManifest
+	if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil {
+		t.Errorf("failed to unmarshal manifest: %v", err)
+	}
+	slices.Sort(actual.Files)
+
+	expected := testSuiteManifest{
+		Name: "my-all-tests-suite",
+		Files: []string{
+			"target/testcases/TestModule1/TestModule1.config",
+			"target/testcases/TestModule1/arm64/TestModule1.apk",
+			"target/testcases/TestModule2/TestModule2.config",
+			"target/testcases/TestModule2/arm64/TestModule2.apk",
+			"target/testcases/TestModule3/TestModule3.config",
+			"target/testcases/TestModule3/arm64/TestModule3.apk",
+		},
+	}
+
+	android.AssertDeepEquals(t, "manifests differ", expected, actual)
+}
+
+func TestTestSuitesNotInstalledInTestcases(t *testing.T) {
+	t.Parallel()
+	android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+	).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{
+		`"SomeHostTest" is not installed in testcases`,
+	})).RunTestWithBp(t, `
+			java_test_host {
+				name: "SomeHostTest",
+				srcs: ["a.java"],
+			}
+			test_suite {
+				name: "my-suite",
+				description: "a test suite",
+				tests: [
+					"SomeHostTest",
+				]
+			}
+	`)
+}
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index fcf29c5..dc1abd9 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -35,9 +35,9 @@
         "blueprint",
         "blueprint-bootstrap",
         "blueprint-microfactory",
-        "soong-android",
         "soong-elf",
         "soong-finder",
+        "soong-finder-fs",
         "soong-remoteexec",
         "soong-shared",
         "soong-ui-build-paths",
diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go
index 2ec8972..6f57cb1 100644
--- a/ui/build/androidmk_denylist.go
+++ b/ui/build/androidmk_denylist.go
@@ -29,6 +29,9 @@
 	"device/google_car/",
 	"device/sample/",
 	"frameworks/",
+	"hardware/libhardware/",
+	"hardware/libhardware_legacy/",
+	"hardware/ril/",
 	// Do not block other directories in kernel/, see b/319658303.
 	"kernel/configs/",
 	"kernel/prebuilts/",
@@ -37,8 +40,10 @@
 	"libnativehelper/",
 	"packages/",
 	"pdk/",
+	"platform_testing/",
 	"prebuilts/",
 	"sdk/",
+	"system/",
 	"test/",
 	"trusty/",
 	// Add back toolchain/ once defensive Android.mk files are removed
@@ -64,3 +69,37 @@
 		}
 	}
 }
+
+// The Android.mk files in these directories are for NDK build system.
+var external_ndk_androidmks []string = []string{
+	"external/fmtlib/",
+	"external/google-breakpad/",
+	"external/googletest/",
+	"external/libaom/",
+	"external/libusb/",
+	"external/libvpx/",
+	"external/libwebm/",
+	"external/libwebsockets/",
+	"external/vulkan-validation-layers/",
+	"external/walt/",
+	"external/webp/",
+}
+
+func ignoreNdkAndroidMks(androidMks []string) (filtered []string) {
+	filter := func(s string) bool {
+		for _, d := range external_ndk_androidmks {
+			if strings.HasPrefix(s, d) {
+				return false
+			}
+		}
+		return true
+	}
+
+	for _, l := range androidMks {
+		if filter(l) {
+			filtered = append(filtered, l)
+		}
+	}
+
+	return
+}
diff --git a/ui/build/build.go b/ui/build/build.go
index 28c3284..d5a20b4 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -428,7 +428,7 @@
 	if !config.SoongBuildInvocationNeeded() {
 		// This means that the output of soong_build is not needed and thus it would
 		// run unnecessarily. In addition, if this code wasn't there invocations
-		// with only special-cased target names like "m bp2build" would result in
+		// with only special-cased target names would result in
 		// passing Ninja the empty target list and it would then build the default
 		// targets which is not what the user asked for.
 		what = what &^ RunNinja
diff --git a/ui/build/config.go b/ui/build/config.go
index 851a22a..209404e 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -30,6 +30,7 @@
 	"syscall"
 	"time"
 
+	"android/soong/finder/fs"
 	"android/soong/shared"
 	"android/soong/ui/metrics"
 
@@ -68,7 +69,6 @@
 
 type configImpl struct {
 	// Some targets that are implemented in soong_build
-	// (bp2build, json-module-graph) are not here and have their own bits below.
 	arguments     []string
 	goma          bool
 	environ       *Environment
@@ -83,7 +83,6 @@
 	checkbuild               bool
 	dist                     bool
 	jsonModuleGraph          bool
-	queryview                bool
 	reportMkMetrics          bool // Collect and report mk2bp migration progress metrics.
 	soongDocs                bool
 	skipConfig               bool
@@ -108,7 +107,9 @@
 	sandboxConfig   *SandboxConfig
 
 	// Autodetected
-	totalRAM uint64
+	totalRAM      uint64
+	systemCpuInfo *metrics.CpuInfo
+	systemMemInfo *metrics.MemInfo
 
 	brokenDupRules       bool
 	brokenUsesNetwork    bool
@@ -239,6 +240,14 @@
 	ret.keepGoing = 1
 
 	ret.totalRAM = detectTotalRAM(ctx)
+	ret.systemCpuInfo, err = metrics.NewCpuInfo(fs.OsFs)
+	if err != nil {
+		ctx.Fatalln("Failed to get cpuinfo:", err)
+	}
+	ret.systemMemInfo, err = metrics.NewMemInfo(fs.OsFs)
+	if err != nil {
+		ctx.Fatalln("Failed to get meminfo:", err)
+	}
 	ret.parseArgs(ctx, args)
 
 	if ret.ninjaWeightListSource == HINT_FROM_SOONG {
@@ -297,6 +306,20 @@
 		ret.moduleDebugFile, _ = filepath.Abs(shared.JoinPath(ret.SoongOutDir(), "soong-debug-info.json"))
 	}
 
+	// If SOONG_USE_PARTIAL_COMPILE is set, make it one of "true" or the empty string.
+	// This simplifies the generated Ninja rules, so that they only need to check for the empty string.
+	if value, ok := os.LookupEnv("SOONG_USE_PARTIAL_COMPILE"); ok {
+		if value == "true" || value == "1" || value == "y" || value == "yes" {
+			value = "true"
+		} else {
+			value = ""
+		}
+		err = os.Setenv("SOONG_USE_PARTIAL_COMPILE", value)
+		if err != nil {
+			ctx.Fatalln("Failed to set SOONG_USE_PARTIAL_COMPILE: %v", err)
+		}
+	}
+
 	ret.ninjaCommand = NINJA_NINJA
 	switch os.Getenv("SOONG_NINJA") {
 	case "n2":
@@ -538,9 +561,23 @@
 
 	ctx.Metrics.BuildConfig(buildConfig(config))
 
+	cpuInfo := &smpb.SystemCpuInfo{
+		VendorId:  proto.String(config.systemCpuInfo.VendorId),
+		ModelName: proto.String(config.systemCpuInfo.ModelName),
+		CpuCores:  proto.Int32(config.systemCpuInfo.CpuCores),
+		Flags:     proto.String(config.systemCpuInfo.Flags),
+	}
+	memInfo := &smpb.SystemMemInfo{
+		MemTotal:     proto.Uint64(config.systemMemInfo.MemTotal),
+		MemFree:      proto.Uint64(config.systemMemInfo.MemFree),
+		MemAvailable: proto.Uint64(config.systemMemInfo.MemAvailable),
+	}
+
 	s := &smpb.SystemResourceInfo{
 		TotalPhysicalMemory: proto.Uint64(config.TotalRAM()),
 		AvailableCpus:       proto.Int32(int32(runtime.NumCPU())),
+		CpuInfo:             cpuInfo,
+		MemInfo:             memInfo,
 	}
 	ctx.Metrics.SystemResourceInfo(s)
 }
@@ -897,8 +934,6 @@
 			c.dist = true
 		} else if arg == "json-module-graph" {
 			c.jsonModuleGraph = true
-		} else if arg == "queryview" {
-			c.queryview = true
 		} else if arg == "soong_docs" {
 			c.soongDocs = true
 		} else {
@@ -993,7 +1028,7 @@
 		return true
 	}
 
-	if !c.JsonModuleGraph() && !c.Queryview() && !c.SoongDocs() {
+	if !c.JsonModuleGraph() && !c.SoongDocs() {
 		// Command line was empty, the default Ninja target is built
 		return true
 	}
@@ -1066,10 +1101,6 @@
 	return shared.JoinPath(c.SoongOutDir(), "docs/soong_build.html")
 }
 
-func (c *configImpl) QueryviewMarkerFile() string {
-	return shared.JoinPath(c.SoongOutDir(), "queryview.marker")
-}
-
 func (c *configImpl) ModuleGraphFile() string {
 	return shared.JoinPath(c.SoongOutDir(), "module-graph.json")
 }
@@ -1107,10 +1138,6 @@
 	return c.jsonModuleGraph
 }
 
-func (c *configImpl) Queryview() bool {
-	return c.queryview
-}
-
 func (c *configImpl) SoongDocs() bool {
 	return c.soongDocs
 }
@@ -1327,7 +1354,7 @@
 
 func (c *configImpl) UseRBE() bool {
 	// These alternate modes of running Soong do not use RBE / reclient.
-	if c.Queryview() || c.JsonModuleGraph() {
+	if c.JsonModuleGraph() {
 		return false
 	}
 
@@ -1668,12 +1695,10 @@
 }
 
 func (c *configImpl) PrebuiltBuildTool(name string) string {
-	if v, ok := c.environ.Get("SANITIZE_HOST"); ok {
-		if sanitize := strings.Fields(v); inList("address", sanitize) {
-			asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
-			if _, err := os.Stat(asan); err == nil {
-				return asan
-			}
+	if c.environ.IsEnvTrue("SANITIZE_BUILD_TOOL_PREBUILTS") {
+		asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
+		if _, err := os.Stat(asan); err == nil {
+			return asan
 		}
 	}
 	return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name)
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 573df21..da7f255 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -128,6 +128,7 @@
 
 	// Stop searching a subdirectory recursively after finding an Android.mk.
 	androidMks := f.FindFirstNamedAt(".", "Android.mk")
+	androidMks = ignoreNdkAndroidMks(androidMks)
 	blockAndroidMks(ctx, androidMks)
 	err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
 	if err != nil {
@@ -170,6 +171,7 @@
 
 	// Recursively look for all METADATA files.
 	metadataFiles := f.FindNamedAt(".", "METADATA")
+	metadataFiles = ignoreNonAndroidMetadataFiles(metadataFiles)
 	err = dumpListToFile(ctx, config, metadataFiles, filepath.Join(dumpDir, "METADATA.list"))
 	if err != nil {
 		ctx.Fatalf("Could not find METADATA: %v", err)
@@ -222,3 +224,16 @@
 
 	return nil
 }
+
+func ignoreNonAndroidMetadataFiles(metadataFiles []string) []string {
+	result := make([]string, 0, len(metadataFiles))
+	for _, file := range metadataFiles {
+		// Ignore files like prebuilts/clang/host/linux-x86/clang-r536225/python3/lib/python3.11/site-packages/pip-23.1.2.dist-info/METADATA
+		// these METADATA files are from upstream and are not the METADATA files used in Android codebase.
+		if strings.Contains(file, "prebuilts/clang/host/") && strings.Contains(file, "/site-packages/") {
+			continue
+		}
+		result = append(result, file)
+	}
+	return result
+}
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 5743ff7..4dfb710 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -183,6 +183,23 @@
 		username = usernameFromEnv
 	}
 
+	// SOONG_USE_PARTIAL_COMPILE may be used in makefiles, but both cases must be supported.
+	//
+	// In general, the partial compile features will be implemented in Soong-based rules. We
+	// also allow them to be used in makefiles.  Clear the environment variable when calling
+	// kati so that we avoid reanalysis when the user changes it.  We will pass it to Ninja.
+	// As a result, rules where we want to allow the developer to toggle the feature ("use
+	// the partial compile feature" vs "legacy, aka full compile behavior") need to use this
+	// in the rule, since changing it will not cause reanalysis.
+	//
+	// Shell syntax in the rule might look something like this:
+	//     if [[ -n ${SOONG_USE_PARTIAL_COMPILE} ]]; then
+	//         # partial compile behavior
+	//     else
+	//         # legacy behavior
+	//     fi
+	cmd.Environment.Unset("SOONG_USE_PARTIAL_COMPILE")
+
 	hostname, ok := cmd.Environment.Get("BUILD_HOSTNAME")
 	// Unset BUILD_HOSTNAME during kati run to avoid kati rerun, kati will use BUILD_HOSTNAME from a file.
 	cmd.Environment.Unset("BUILD_HOSTNAME")
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index def0783..f5f637f 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -241,6 +241,9 @@
 			"SOONG_USE_N2",
 			"RUST_BACKTRACE",
 			"RUST_LOG",
+
+			// SOONG_USE_PARTIAL_COMPILE only determines which half of the rule we execute.
+			"SOONG_USE_PARTIAL_COMPILE",
 		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
 	}
 
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 41425ac..0963f76 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -52,7 +52,6 @@
 
 	soongBuildTag      = "build"
 	jsonModuleGraphTag = "modulegraph"
-	queryviewTag       = "queryview"
 	soongDocsTag       = "soong_docs"
 
 	// bootstrapEpoch is used to determine if an incremental build is incompatible with the current
@@ -288,6 +287,15 @@
 	ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
 	defer ctx.EndTrace()
 
+	st := ctx.Status.StartTool()
+	defer st.Finish()
+	st.SetTotalActions(1)
+	action := &status.Action{
+		Description: "bootstrap blueprint",
+		Outputs:     []string{"bootstrap blueprint"},
+	}
+	st.StartAction(action)
+
 	// Clean up some files for incremental builds across incompatible changes.
 	bootstrapEpochCleanup(ctx, config)
 
@@ -307,8 +315,6 @@
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--incremental-build-actions")
 	}
 
-	queryviewDir := filepath.Join(config.SoongOutDir(), "queryview")
-
 	pbfs := []PrimaryBuilderFactory{
 		{
 			name:         soongBuildTag,
@@ -328,15 +334,6 @@
 			),
 		},
 		{
-			name:        queryviewTag,
-			description: fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
-			config:      config,
-			output:      config.QueryviewMarkerFile(),
-			specificArgs: append(baseArgs,
-				"--bazel_queryview_dir", queryviewDir,
-			),
-		},
-		{
 			name:        soongDocsTag,
 			description: fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
 			config:      config,
@@ -407,8 +404,17 @@
 	// since `bootstrap.ninja` is regenerated unconditionally, we ignore the deps, i.e. little
 	// reason to write a `bootstrap.ninja.d` file
 	_, err := bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
+
+	result := status.ActionResult{
+		Action: action,
+	}
 	if err != nil {
-		ctx.Fatal(err)
+		result.Error = err
+		result.Output = err.Error()
+	}
+	st.FinishAction(result)
+	if err != nil {
+		ctx.Fatalf("bootstrap failed")
 	}
 }
 
@@ -427,13 +433,13 @@
 	}
 }
 
-func updateSymlinks(ctx Context, dir, prevCWD, cwd string) error {
+func updateSymlinks(ctx Context, dir, prevCWD, cwd string, updateSemaphore chan struct{}) error {
 	defer symlinkWg.Done()
 
 	visit := func(path string, d fs.DirEntry, err error) error {
 		if d.IsDir() && path != dir {
 			symlinkWg.Add(1)
-			go updateSymlinks(ctx, path, prevCWD, cwd)
+			go updateSymlinks(ctx, path, prevCWD, cwd, updateSemaphore)
 			return filepath.SkipDir
 		}
 		f, err := d.Info()
@@ -464,12 +470,27 @@
 		return nil
 	}
 
+	<-updateSemaphore
+	defer func() { updateSemaphore <- struct{}{} }()
 	if err := filepath.WalkDir(dir, visit); err != nil {
 		return err
 	}
 	return nil
 }
 
+// b/376466642: If the concurrency of updateSymlinks is unbounded, Go's runtime spawns a
+// theoretically unbounded number of threads to handle blocking syscalls. This causes the runtime to
+// panic due to hitting thread limits in rare cases. Limiting to GOMAXPROCS concurrent symlink
+// updates should make this a non-issue.
+func newUpdateSemaphore() chan struct{} {
+	numPermits := runtime.GOMAXPROCS(0)
+	c := make(chan struct{}, numPermits)
+	for i := 0; i < numPermits; i++ {
+		c <- struct{}{}
+	}
+	return c
+}
+
 func fixOutDirSymlinks(ctx Context, config Config, outDir string) error {
 	cwd, err := os.Getwd()
 	if err != nil {
@@ -502,7 +523,7 @@
 	}
 
 	symlinkWg.Add(1)
-	if err := updateSymlinks(ctx, outDir, prevCWD, cwd); err != nil {
+	if err := updateSymlinks(ctx, outDir, prevCWD, cwd, newUpdateSemaphore()); err != nil {
 		return err
 	}
 	symlinkWg.Wait()
@@ -572,10 +593,6 @@
 			checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(jsonModuleGraphTag))
 		}
 
-		if config.Queryview() {
-			checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(queryviewTag))
-		}
-
 		if config.SoongDocs() {
 			checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(soongDocsTag))
 		}
@@ -670,10 +687,6 @@
 		targets = append(targets, config.ModuleGraphFile())
 	}
 
-	if config.Queryview() {
-		targets = append(targets, config.QueryviewMarkerFile())
-	}
-
 	if config.SoongDocs() {
 		targets = append(targets, config.SoongDocsHtml())
 	}
@@ -766,7 +779,11 @@
 	globsChan := make(chan pathtools.GlobResult)
 	errorsChan := make(chan error)
 	wg := sync.WaitGroup{}
+
 	hasChangedGlobs := false
+	var changedGlobNameMutex sync.Mutex
+	var changedGlobName string
+
 	for i := 0; i < runtime.NumCPU()*2; i++ {
 		wg.Add(1)
 		go func() {
@@ -804,6 +821,15 @@
 				} else {
 					if !slices.Equal(result.Matches, cachedGlob.Matches) {
 						hasChangedGlobs = true
+
+						changedGlobNameMutex.Lock()
+						defer changedGlobNameMutex.Unlock()
+						changedGlobName = result.Pattern
+						if len(result.Excludes) > 2 {
+							changedGlobName += fmt.Sprintf(" (excluding %d other patterns)", len(result.Excludes))
+						} else if len(result.Excludes) > 0 {
+							changedGlobName += " (excluding " + strings.Join(result.Excludes, " and ") + ")"
+						}
 					}
 				}
 			}
@@ -855,6 +881,7 @@
 
 	if hasChangedGlobs {
 		fmt.Fprintf(os.Stdout, "Globs changed, rerunning soong...\n")
+		fmt.Fprintf(os.Stdout, "One culprit glob (may be more): %s\n", changedGlobName)
 		// Write the current time to the glob_results file. We just need
 		// some unique value to trigger a rerun, it doesn't matter what it is.
 		err = os.WriteFile(
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index bd1517c..591e3cc 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -21,20 +21,34 @@
     pkgPath: "android/soong/ui/metrics",
     deps: [
         "golang-protobuf-proto",
-        "soong-ui-bp2build_metrics_proto",
-        "soong-ui-bazel_metrics_proto",
+        "soong-finder-fs",
         "soong-ui-metrics_upload_proto",
         "soong-ui-metrics_proto",
         "soong-ui-mk_metrics_proto",
         "soong-shared",
+        "soong-ui-metrics_combined_proto",
     ],
     srcs: [
+        "hostinfo.go",
         "metrics.go",
         "event.go",
     ],
     testSrcs: [
         "event_test.go",
     ],
+    linux: {
+        srcs: [
+            "hostinfo_linux.go",
+        ],
+        testSrcs: [
+            "hostinfo_linux_test.go",
+        ],
+    },
+    darwin: {
+        srcs: [
+            "hostinfo_darwin.go",
+        ],
+    },
 }
 
 bootstrap_go_package {
@@ -50,6 +64,19 @@
 }
 
 bootstrap_go_package {
+    name: "soong-ui-metrics_combined_proto",
+    pkgPath: "android/soong/ui/metrics/combined_metrics_proto",
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-find_input_delta-proto",
+    ],
+    srcs: [
+        "metrics_proto/metrics.pb.go",
+    ],
+}
+
+bootstrap_go_package {
     name: "soong-ui-metrics_upload_proto",
     pkgPath: "android/soong/ui/metrics/upload_proto",
     deps: [
@@ -62,30 +89,6 @@
 }
 
 bootstrap_go_package {
-    name: "soong-ui-bp2build_metrics_proto",
-    pkgPath: "android/soong/ui/metrics/bp2build_metrics_proto",
-    deps: [
-        "golang-protobuf-reflect-protoreflect",
-        "golang-protobuf-runtime-protoimpl",
-    ],
-    srcs: [
-        "bp2build_metrics_proto/bp2build_metrics.pb.go",
-    ],
-}
-
-bootstrap_go_package {
-    name: "soong-ui-bazel_metrics_proto",
-    pkgPath: "android/soong/ui/metrics/bazel_metrics_proto",
-    deps: [
-        "golang-protobuf-reflect-protoreflect",
-        "golang-protobuf-runtime-protoimpl",
-    ],
-    srcs: [
-        "bazel_metrics_proto/bazel_metrics.pb.go",
-    ],
-}
-
-bootstrap_go_package {
     name: "soong-ui-mk_metrics_proto",
     pkgPath: "android/soong/ui/metrics/mk_metrics_proto",
     deps: [
diff --git a/ui/metrics/BUILD.bazel b/ui/metrics/BUILD.bazel
deleted file mode 100644
index ca39c59..0000000
--- a/ui/metrics/BUILD.bazel
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2023 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.
-
-load("//build/bazel/rules/python:py_proto.bzl", "py_proto_library")
-
-py_proto_library(
-    name = "metrics-py-proto",
-    visibility = ["//build/bazel/scripts:__subpackages__"],
-    deps = [":metrics-proto"],
-)
-
-proto_library(
-    name = "metrics-proto",
-    srcs = [
-        "bazel_metrics_proto/bazel_metrics.proto",
-        "bp2build_metrics_proto/bp2build_metrics.proto",
-        "metrics_proto/metrics.proto",
-    ],
-    strip_import_prefix = "",
-)
diff --git a/ui/metrics/bazel_metrics_proto/bazel_metrics.pb.go b/ui/metrics/bazel_metrics_proto/bazel_metrics.pb.go
deleted file mode 100644
index 8b97b83..0000000
--- a/ui/metrics/bazel_metrics_proto/bazel_metrics.pb.go
+++ /dev/null
@@ -1,290 +0,0 @@
-// Copyright 2022 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.
-
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// 	protoc-gen-go v1.30.0
-// 	protoc        v3.21.12
-// source: bazel_metrics.proto
-
-package bazel_metrics_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 BazelMetrics struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	PhaseTimings []*PhaseTiming `protobuf:"bytes,1,rep,name=phase_timings,json=phaseTimings,proto3" json:"phase_timings,omitempty"`
-	Total        *int64         `protobuf:"varint,2,opt,name=total,proto3,oneof" json:"total,omitempty"`
-	ExitCode     *int32         `protobuf:"varint,3,opt,name=exit_code,json=exitCode,proto3,oneof" json:"exit_code,omitempty"`
-	BesId        *string        `protobuf:"bytes,4,opt,name=bes_id,json=besId,proto3,oneof" json:"bes_id,omitempty"`
-}
-
-func (x *BazelMetrics) Reset() {
-	*x = BazelMetrics{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_bazel_metrics_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *BazelMetrics) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*BazelMetrics) ProtoMessage() {}
-
-func (x *BazelMetrics) ProtoReflect() protoreflect.Message {
-	mi := &file_bazel_metrics_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 BazelMetrics.ProtoReflect.Descriptor instead.
-func (*BazelMetrics) Descriptor() ([]byte, []int) {
-	return file_bazel_metrics_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *BazelMetrics) GetPhaseTimings() []*PhaseTiming {
-	if x != nil {
-		return x.PhaseTimings
-	}
-	return nil
-}
-
-func (x *BazelMetrics) GetTotal() int64 {
-	if x != nil && x.Total != nil {
-		return *x.Total
-	}
-	return 0
-}
-
-func (x *BazelMetrics) GetExitCode() int32 {
-	if x != nil && x.ExitCode != nil {
-		return *x.ExitCode
-	}
-	return 0
-}
-
-func (x *BazelMetrics) GetBesId() string {
-	if x != nil && x.BesId != nil {
-		return *x.BesId
-	}
-	return ""
-}
-
-type PhaseTiming struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// E.g. "execution", "analysis", "launch"
-	PhaseName     *string `protobuf:"bytes,1,opt,name=phase_name,json=phaseName,proto3,oneof" json:"phase_name,omitempty"`
-	DurationNanos *int64  `protobuf:"varint,2,opt,name=duration_nanos,json=durationNanos,proto3,oneof" json:"duration_nanos,omitempty"`
-	// What portion of the build time this phase took, with ten-thousandths precision.
-	// E.g., 1111 = 11.11%, 111 = 1.11%
-	PortionOfBuildTime *int32 `protobuf:"varint,3,opt,name=portion_of_build_time,json=portionOfBuildTime,proto3,oneof" json:"portion_of_build_time,omitempty"`
-}
-
-func (x *PhaseTiming) Reset() {
-	*x = PhaseTiming{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_bazel_metrics_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *PhaseTiming) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*PhaseTiming) ProtoMessage() {}
-
-func (x *PhaseTiming) ProtoReflect() protoreflect.Message {
-	mi := &file_bazel_metrics_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 PhaseTiming.ProtoReflect.Descriptor instead.
-func (*PhaseTiming) Descriptor() ([]byte, []int) {
-	return file_bazel_metrics_proto_rawDescGZIP(), []int{1}
-}
-
-func (x *PhaseTiming) GetPhaseName() string {
-	if x != nil && x.PhaseName != nil {
-		return *x.PhaseName
-	}
-	return ""
-}
-
-func (x *PhaseTiming) GetDurationNanos() int64 {
-	if x != nil && x.DurationNanos != nil {
-		return *x.DurationNanos
-	}
-	return 0
-}
-
-func (x *PhaseTiming) GetPortionOfBuildTime() int32 {
-	if x != nil && x.PortionOfBuildTime != nil {
-		return *x.PortionOfBuildTime
-	}
-	return 0
-}
-
-var File_bazel_metrics_proto protoreflect.FileDescriptor
-
-var file_bazel_metrics_proto_rawDesc = []byte{
-	0x0a, 0x13, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-	0x22, 0xd7, 0x01, 0x0a, 0x0c, 0x42, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
-	0x73, 0x12, 0x4b, 0x0a, 0x0d, 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x69, 0x6e,
-	0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
-	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x68, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67,
-	0x52, 0x0c, 0x70, 0x68, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19,
-	0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52,
-	0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x65, 0x78, 0x69,
-	0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x08,
-	0x65, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x06, 0x62,
-	0x65, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x05, 0x62,
-	0x65, 0x73, 0x49, 0x64, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x74, 0x6f, 0x74, 0x61,
-	0x6c, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x42,
-	0x09, 0x0a, 0x07, 0x5f, 0x62, 0x65, 0x73, 0x5f, 0x69, 0x64, 0x22, 0xd1, 0x01, 0x0a, 0x0b, 0x50,
-	0x68, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x22, 0x0a, 0x0a, 0x70, 0x68,
-	0x61, 0x73, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
-	0x52, 0x09, 0x70, 0x68, 0x61, 0x73, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2a,
-	0x0a, 0x0e, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x0d, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69,
-	0x6f, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x15, 0x70, 0x6f,
-	0x72, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x66, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74,
-	0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x12, 0x70, 0x6f, 0x72,
-	0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x66, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x88,
-	0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x6e, 0x61, 0x6d,
-	0x65, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e,
-	0x61, 0x6e, 0x6f, 0x73, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e,
-	0x5f, 0x6f, 0x66, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x2e,
-	0x5a, 0x2c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
-	0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x62, 0x61, 0x7a, 0x65, 0x6c,
-	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
-}
-
-var (
-	file_bazel_metrics_proto_rawDescOnce sync.Once
-	file_bazel_metrics_proto_rawDescData = file_bazel_metrics_proto_rawDesc
-)
-
-func file_bazel_metrics_proto_rawDescGZIP() []byte {
-	file_bazel_metrics_proto_rawDescOnce.Do(func() {
-		file_bazel_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_bazel_metrics_proto_rawDescData)
-	})
-	return file_bazel_metrics_proto_rawDescData
-}
-
-var file_bazel_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_bazel_metrics_proto_goTypes = []interface{}{
-	(*BazelMetrics)(nil), // 0: soong_build_bazel_metrics.BazelMetrics
-	(*PhaseTiming)(nil),  // 1: soong_build_bazel_metrics.PhaseTiming
-}
-var file_bazel_metrics_proto_depIdxs = []int32{
-	1, // 0: soong_build_bazel_metrics.BazelMetrics.phase_timings:type_name -> soong_build_bazel_metrics.PhaseTiming
-	1, // [1:1] is the sub-list for method output_type
-	1, // [1:1] is the sub-list for method input_type
-	1, // [1:1] is the sub-list for extension type_name
-	1, // [1:1] is the sub-list for extension extendee
-	0, // [0:1] is the sub-list for field type_name
-}
-
-func init() { file_bazel_metrics_proto_init() }
-func file_bazel_metrics_proto_init() {
-	if File_bazel_metrics_proto != nil {
-		return
-	}
-	if !protoimpl.UnsafeEnabled {
-		file_bazel_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*BazelMetrics); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_bazel_metrics_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PhaseTiming); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-	}
-	file_bazel_metrics_proto_msgTypes[0].OneofWrappers = []interface{}{}
-	file_bazel_metrics_proto_msgTypes[1].OneofWrappers = []interface{}{}
-	type x struct{}
-	out := protoimpl.TypeBuilder{
-		File: protoimpl.DescBuilder{
-			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
-			RawDescriptor: file_bazel_metrics_proto_rawDesc,
-			NumEnums:      0,
-			NumMessages:   2,
-			NumExtensions: 0,
-			NumServices:   0,
-		},
-		GoTypes:           file_bazel_metrics_proto_goTypes,
-		DependencyIndexes: file_bazel_metrics_proto_depIdxs,
-		MessageInfos:      file_bazel_metrics_proto_msgTypes,
-	}.Build()
-	File_bazel_metrics_proto = out.File
-	file_bazel_metrics_proto_rawDesc = nil
-	file_bazel_metrics_proto_goTypes = nil
-	file_bazel_metrics_proto_depIdxs = nil
-}
diff --git a/ui/metrics/bazel_metrics_proto/bazel_metrics.proto b/ui/metrics/bazel_metrics_proto/bazel_metrics.proto
deleted file mode 100644
index e45d2bf..0000000
--- a/ui/metrics/bazel_metrics_proto/bazel_metrics.proto
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2022 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.
-
-syntax = "proto3";
-
-package soong_build_bazel_metrics;
-option go_package = "android/soong/ui/metrics/bazel_metrics_proto";
-
-message BazelMetrics {
-  repeated PhaseTiming phase_timings = 1;
-  optional int64 total = 2;
-  optional int32 exit_code = 3;
-  optional string bes_id = 4;
-}
-
-message PhaseTiming {
-  // E.g. "execution", "analysis", "launch"
-  optional string phase_name = 1;
-  optional int64 duration_nanos = 2;
-  // What portion of the build time this phase took, with ten-thousandths precision.
-  // E.g., 1111 = 11.11%, 111 = 1.11%
-  optional int32 portion_of_build_time = 3;
-}
diff --git a/ui/metrics/bazel_metrics_proto/regen.sh b/ui/metrics/bazel_metrics_proto/regen.sh
deleted file mode 100755
index 2cf2bf6..0000000
--- a/ui/metrics/bazel_metrics_proto/regen.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash -e
-
-# Copyright 2022 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.
-
-# Generates the golang source file of bp2build_metrics.proto protobuf file.
-
-function die() { echo "ERROR: $1" >&2; exit 1; }
-
-readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
-
-if ! hash aprotoc &>/dev/null; then
-  die "could not find aprotoc. ${error_msg}"
-fi
-
-if ! aprotoc --go_out=paths=source_relative:. bazel_metrics.proto; then
-  die "build failed. ${error_msg}"
-fi
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
deleted file mode 100644
index b34c2b6..0000000
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
+++ /dev/null
@@ -1,590 +0,0 @@
-// Copyright 2021 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.
-
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// 	protoc-gen-go v1.30.0
-// 	protoc        v3.21.12
-// source: bp2build_metrics.proto
-
-package bp2build_metrics_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 UnconvertedReasonType int32
-
-const (
-	// Bp2build does not know how to convert this specific module for some reason
-	// not covered by other reason types. The reason detail should explain the
-	// specific issue.
-	UnconvertedReasonType_UNSUPPORTED UnconvertedReasonType = 0
-	// The module was already defined in a BUILD file available in the source tree.
-	UnconvertedReasonType_DEFINED_IN_BUILD_FILE UnconvertedReasonType = 1
-	// The module was explicitly denylisted by name.
-	UnconvertedReasonType_DENYLISTED UnconvertedReasonType = 2
-	// The module's type has no bp2build implementation.
-	UnconvertedReasonType_TYPE_UNSUPPORTED UnconvertedReasonType = 3
-	// The module has a property not yet supported. The detail field should
-	// name the unsupported property name.
-	UnconvertedReasonType_PROPERTY_UNSUPPORTED UnconvertedReasonType = 4
-	// The module has an unconverted dependency. The detail should consist of
-	// the name of the unconverted module.
-	UnconvertedReasonType_UNCONVERTED_DEP UnconvertedReasonType = 5
-	// The module has a source file with the same name as the module itself.
-	UnconvertedReasonType_SRC_NAME_COLLISION UnconvertedReasonType = 6
-)
-
-// Enum value maps for UnconvertedReasonType.
-var (
-	UnconvertedReasonType_name = map[int32]string{
-		0: "UNSUPPORTED",
-		1: "DEFINED_IN_BUILD_FILE",
-		2: "DENYLISTED",
-		3: "TYPE_UNSUPPORTED",
-		4: "PROPERTY_UNSUPPORTED",
-		5: "UNCONVERTED_DEP",
-		6: "SRC_NAME_COLLISION",
-	}
-	UnconvertedReasonType_value = map[string]int32{
-		"UNSUPPORTED":           0,
-		"DEFINED_IN_BUILD_FILE": 1,
-		"DENYLISTED":            2,
-		"TYPE_UNSUPPORTED":      3,
-		"PROPERTY_UNSUPPORTED":  4,
-		"UNCONVERTED_DEP":       5,
-		"SRC_NAME_COLLISION":    6,
-	}
-)
-
-func (x UnconvertedReasonType) Enum() *UnconvertedReasonType {
-	p := new(UnconvertedReasonType)
-	*p = x
-	return p
-}
-
-func (x UnconvertedReasonType) String() string {
-	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
-}
-
-func (UnconvertedReasonType) Descriptor() protoreflect.EnumDescriptor {
-	return file_bp2build_metrics_proto_enumTypes[0].Descriptor()
-}
-
-func (UnconvertedReasonType) Type() protoreflect.EnumType {
-	return &file_bp2build_metrics_proto_enumTypes[0]
-}
-
-func (x UnconvertedReasonType) Number() protoreflect.EnumNumber {
-	return protoreflect.EnumNumber(x)
-}
-
-// Deprecated: Use UnconvertedReasonType.Descriptor instead.
-func (UnconvertedReasonType) EnumDescriptor() ([]byte, []int) {
-	return file_bp2build_metrics_proto_rawDescGZIP(), []int{0}
-}
-
-type Bp2BuildMetrics struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// Total number of Soong modules converted to generated targets
-	GeneratedModuleCount uint64 `protobuf:"varint,1,opt,name=generatedModuleCount,proto3" json:"generatedModuleCount,omitempty"`
-	// Total number of Soong modules converted to handcrafted targets
-	HandCraftedModuleCount uint64 `protobuf:"varint,2,opt,name=handCraftedModuleCount,proto3" json:"handCraftedModuleCount,omitempty"`
-	// Total number of unconverted Soong modules
-	UnconvertedModuleCount uint64 `protobuf:"varint,3,opt,name=unconvertedModuleCount,proto3" json:"unconvertedModuleCount,omitempty"`
-	// Counts of symlinks in synthetic bazel workspace
-	WorkspaceSymlinkCount uint64 `protobuf:"varint,9,opt,name=workspaceSymlinkCount,proto3" json:"workspaceSymlinkCount,omitempty"`
-	// Counts of mkdir calls during creation of synthetic bazel workspace
-	WorkspaceMkDirCount uint64 `protobuf:"varint,10,opt,name=workspaceMkDirCount,proto3" json:"workspaceMkDirCount,omitempty"`
-	// Counts of generated Bazel targets per Bazel rule class
-	RuleClassCount map[string]uint64 `protobuf:"bytes,4,rep,name=ruleClassCount,proto3" json:"ruleClassCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
-	// List of converted modules
-	ConvertedModules []string `protobuf:"bytes,5,rep,name=convertedModules,proto3" json:"convertedModules,omitempty"`
-	// Unconverted modules, mapped to the reason the module was not converted.
-	UnconvertedModules map[string]*UnconvertedReason `protobuf:"bytes,11,rep,name=unconvertedModules,proto3" json:"unconvertedModules,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
-	// Counts of converted modules by module type.
-	ConvertedModuleTypeCount map[string]uint64 `protobuf:"bytes,6,rep,name=convertedModuleTypeCount,proto3" json:"convertedModuleTypeCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
-	// Counts of total modules by module type.
-	TotalModuleTypeCount map[string]uint64 `protobuf:"bytes,7,rep,name=totalModuleTypeCount,proto3" json:"totalModuleTypeCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
-	// List of traced runtime events of bp2build, useful for tracking bp2build
-	// runtime.
-	Events []*Event `protobuf:"bytes,8,rep,name=events,proto3" json:"events,omitempty"`
-}
-
-func (x *Bp2BuildMetrics) Reset() {
-	*x = Bp2BuildMetrics{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_bp2build_metrics_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Bp2BuildMetrics) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Bp2BuildMetrics) ProtoMessage() {}
-
-func (x *Bp2BuildMetrics) ProtoReflect() protoreflect.Message {
-	mi := &file_bp2build_metrics_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 Bp2BuildMetrics.ProtoReflect.Descriptor instead.
-func (*Bp2BuildMetrics) Descriptor() ([]byte, []int) {
-	return file_bp2build_metrics_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *Bp2BuildMetrics) GetGeneratedModuleCount() uint64 {
-	if x != nil {
-		return x.GeneratedModuleCount
-	}
-	return 0
-}
-
-func (x *Bp2BuildMetrics) GetHandCraftedModuleCount() uint64 {
-	if x != nil {
-		return x.HandCraftedModuleCount
-	}
-	return 0
-}
-
-func (x *Bp2BuildMetrics) GetUnconvertedModuleCount() uint64 {
-	if x != nil {
-		return x.UnconvertedModuleCount
-	}
-	return 0
-}
-
-func (x *Bp2BuildMetrics) GetWorkspaceSymlinkCount() uint64 {
-	if x != nil {
-		return x.WorkspaceSymlinkCount
-	}
-	return 0
-}
-
-func (x *Bp2BuildMetrics) GetWorkspaceMkDirCount() uint64 {
-	if x != nil {
-		return x.WorkspaceMkDirCount
-	}
-	return 0
-}
-
-func (x *Bp2BuildMetrics) GetRuleClassCount() map[string]uint64 {
-	if x != nil {
-		return x.RuleClassCount
-	}
-	return nil
-}
-
-func (x *Bp2BuildMetrics) GetConvertedModules() []string {
-	if x != nil {
-		return x.ConvertedModules
-	}
-	return nil
-}
-
-func (x *Bp2BuildMetrics) GetUnconvertedModules() map[string]*UnconvertedReason {
-	if x != nil {
-		return x.UnconvertedModules
-	}
-	return nil
-}
-
-func (x *Bp2BuildMetrics) GetConvertedModuleTypeCount() map[string]uint64 {
-	if x != nil {
-		return x.ConvertedModuleTypeCount
-	}
-	return nil
-}
-
-func (x *Bp2BuildMetrics) GetTotalModuleTypeCount() map[string]uint64 {
-	if x != nil {
-		return x.TotalModuleTypeCount
-	}
-	return nil
-}
-
-func (x *Bp2BuildMetrics) GetEvents() []*Event {
-	if x != nil {
-		return x.Events
-	}
-	return nil
-}
-
-// Traced runtime event of bp2build.
-type Event struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// The event name.
-	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
-	// The absolute start time of the event
-	// The number of nanoseconds elapsed since January 1, 1970 UTC.
-	StartTime uint64 `protobuf:"varint,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"`
-	// The real running time.
-	// The number of nanoseconds elapsed since start_time.
-	RealTime uint64 `protobuf:"varint,3,opt,name=real_time,json=realTime,proto3" json:"real_time,omitempty"`
-}
-
-func (x *Event) Reset() {
-	*x = Event{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_bp2build_metrics_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Event) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Event) ProtoMessage() {}
-
-func (x *Event) ProtoReflect() protoreflect.Message {
-	mi := &file_bp2build_metrics_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 Event.ProtoReflect.Descriptor instead.
-func (*Event) Descriptor() ([]byte, []int) {
-	return file_bp2build_metrics_proto_rawDescGZIP(), []int{1}
-}
-
-func (x *Event) GetName() string {
-	if x != nil {
-		return x.Name
-	}
-	return ""
-}
-
-func (x *Event) GetStartTime() uint64 {
-	if x != nil {
-		return x.StartTime
-	}
-	return 0
-}
-
-func (x *Event) GetRealTime() uint64 {
-	if x != nil {
-		return x.RealTime
-	}
-	return 0
-}
-
-type UnconvertedReason struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// The type of reason that the module could not be converted.
-	Type UnconvertedReasonType `protobuf:"varint,1,opt,name=type,proto3,enum=soong_build_bp2build_metrics.UnconvertedReasonType" json:"type,omitempty"`
-	// Descriptive details describing why the module could not be converted.
-	// This detail should be kept very short and should be in the context of
-	// the type. (Otherwise, this would significantly bloat metrics.)
-	Detail string `protobuf:"bytes,2,opt,name=detail,proto3" json:"detail,omitempty"`
-}
-
-func (x *UnconvertedReason) Reset() {
-	*x = UnconvertedReason{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_bp2build_metrics_proto_msgTypes[2]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *UnconvertedReason) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*UnconvertedReason) ProtoMessage() {}
-
-func (x *UnconvertedReason) ProtoReflect() protoreflect.Message {
-	mi := &file_bp2build_metrics_proto_msgTypes[2]
-	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 UnconvertedReason.ProtoReflect.Descriptor instead.
-func (*UnconvertedReason) Descriptor() ([]byte, []int) {
-	return file_bp2build_metrics_proto_rawDescGZIP(), []int{2}
-}
-
-func (x *UnconvertedReason) GetType() UnconvertedReasonType {
-	if x != nil {
-		return x.Type
-	}
-	return UnconvertedReasonType_UNSUPPORTED
-}
-
-func (x *UnconvertedReason) GetDetail() string {
-	if x != nil {
-		return x.Detail
-	}
-	return ""
-}
-
-var File_bp2build_metrics_proto protoreflect.FileDescriptor
-
-var file_bp2build_metrics_proto_rawDesc = []byte{
-	0x0a, 0x16, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xc0, 0x09, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
-	0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x67, 0x65,
-	0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75,
-	0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
-	0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36,
-	0x0a, 0x16, 0x68, 0x61, 0x6e, 0x64, 0x43, 0x72, 0x61, 0x66, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64,
-	0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16,
-	0x68, 0x61, 0x6e, 0x64, 0x43, 0x72, 0x61, 0x66, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76,
-	0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-	0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
-	0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x34,
-	0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x79, 0x6d, 0x6c, 0x69,
-	0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x77,
-	0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x43,
-	0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
-	0x65, 0x4d, 0x6b, 0x44, 0x69, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x6b, 0x44, 0x69,
-	0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x69, 0x0a, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c,
-	0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41,
-	0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70,
-	0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x52, 0x75,
-	0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72,
-	0x79, 0x52, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e,
-	0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
-	0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e,
-	0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x75, 0x0a,
-	0x12, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75,
-	0x6c, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
-	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c,
-	0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65,
-	0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
-	0x52, 0x12, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64,
-	0x75, 0x6c, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74,
-	0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
-	0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64,
-	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45,
-	0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d,
-	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x7b,
-	0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70,
-	0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x73,
-	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42,
-	0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x6f, 0x74, 0x61,
-	0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-	0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75,
-	0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x06, 0x65,
-	0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73, 0x6f,
-	0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74,
-	0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c, 0x65,
-	0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
-	0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
-	0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
-	0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x76, 0x0a, 0x17, 0x55,
-	0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
-	0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x45, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
-	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74,
-	0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
-	0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64,
-	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45,
-	0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
-	0x1a, 0x47, 0x0a, 0x19, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54,
-	0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
-	0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
-	0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x57, 0x0a, 0x05, 0x45, 0x76, 0x65,
-	0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f,
-	0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72,
-	0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69,
-	0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69,
-	0x6d, 0x65, 0x22, 0x74, 0x0a, 0x11, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65,
-	0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x33, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x2e, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64,
-	0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
-	0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x2a, 0xb0, 0x01, 0x0a, 0x15, 0x55, 0x6e, 0x63,
-	0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x54, 0x79,
-	0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x55, 0x50, 0x50, 0x4f, 0x52, 0x54, 0x45,
-	0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x45, 0x44, 0x5f, 0x49,
-	0x4e, 0x5f, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x0e,
-	0x0a, 0x0a, 0x44, 0x45, 0x4e, 0x59, 0x4c, 0x49, 0x53, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x14,
-	0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x55, 0x50, 0x50, 0x4f, 0x52, 0x54,
-	0x45, 0x44, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x52, 0x4f, 0x50, 0x45, 0x52, 0x54, 0x59,
-	0x5f, 0x55, 0x4e, 0x53, 0x55, 0x50, 0x50, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x04, 0x12, 0x13,
-	0x0a, 0x0f, 0x55, 0x4e, 0x43, 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x54, 0x45, 0x44, 0x5f, 0x44, 0x45,
-	0x50, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x52, 0x43, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x5f,
-	0x43, 0x4f, 0x4c, 0x4c, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x06, 0x42, 0x31, 0x5a, 0x2f, 0x61,
-	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
-}
-
-var (
-	file_bp2build_metrics_proto_rawDescOnce sync.Once
-	file_bp2build_metrics_proto_rawDescData = file_bp2build_metrics_proto_rawDesc
-)
-
-func file_bp2build_metrics_proto_rawDescGZIP() []byte {
-	file_bp2build_metrics_proto_rawDescOnce.Do(func() {
-		file_bp2build_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_bp2build_metrics_proto_rawDescData)
-	})
-	return file_bp2build_metrics_proto_rawDescData
-}
-
-var file_bp2build_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_bp2build_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
-var file_bp2build_metrics_proto_goTypes = []interface{}{
-	(UnconvertedReasonType)(0), // 0: soong_build_bp2build_metrics.UnconvertedReasonType
-	(*Bp2BuildMetrics)(nil),    // 1: soong_build_bp2build_metrics.Bp2BuildMetrics
-	(*Event)(nil),              // 2: soong_build_bp2build_metrics.Event
-	(*UnconvertedReason)(nil),  // 3: soong_build_bp2build_metrics.UnconvertedReason
-	nil,                        // 4: soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
-	nil,                        // 5: soong_build_bp2build_metrics.Bp2BuildMetrics.UnconvertedModulesEntry
-	nil,                        // 6: soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
-	nil,                        // 7: soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
-}
-var file_bp2build_metrics_proto_depIdxs = []int32{
-	4, // 0: soong_build_bp2build_metrics.Bp2BuildMetrics.ruleClassCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
-	5, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.unconvertedModules:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.UnconvertedModulesEntry
-	6, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.convertedModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
-	7, // 3: soong_build_bp2build_metrics.Bp2BuildMetrics.totalModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
-	2, // 4: soong_build_bp2build_metrics.Bp2BuildMetrics.events:type_name -> soong_build_bp2build_metrics.Event
-	0, // 5: soong_build_bp2build_metrics.UnconvertedReason.type:type_name -> soong_build_bp2build_metrics.UnconvertedReasonType
-	3, // 6: soong_build_bp2build_metrics.Bp2BuildMetrics.UnconvertedModulesEntry.value:type_name -> soong_build_bp2build_metrics.UnconvertedReason
-	7, // [7:7] is the sub-list for method output_type
-	7, // [7:7] is the sub-list for method input_type
-	7, // [7:7] is the sub-list for extension type_name
-	7, // [7:7] is the sub-list for extension extendee
-	0, // [0:7] is the sub-list for field type_name
-}
-
-func init() { file_bp2build_metrics_proto_init() }
-func file_bp2build_metrics_proto_init() {
-	if File_bp2build_metrics_proto != nil {
-		return
-	}
-	if !protoimpl.UnsafeEnabled {
-		file_bp2build_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Bp2BuildMetrics); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_bp2build_metrics_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Event); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_bp2build_metrics_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UnconvertedReason); 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_bp2build_metrics_proto_rawDesc,
-			NumEnums:      1,
-			NumMessages:   7,
-			NumExtensions: 0,
-			NumServices:   0,
-		},
-		GoTypes:           file_bp2build_metrics_proto_goTypes,
-		DependencyIndexes: file_bp2build_metrics_proto_depIdxs,
-		EnumInfos:         file_bp2build_metrics_proto_enumTypes,
-		MessageInfos:      file_bp2build_metrics_proto_msgTypes,
-	}.Build()
-	File_bp2build_metrics_proto = out.File
-	file_bp2build_metrics_proto_rawDesc = nil
-	file_bp2build_metrics_proto_goTypes = nil
-	file_bp2build_metrics_proto_depIdxs = nil
-}
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
deleted file mode 100644
index 49cb2b4..0000000
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2021 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.
-
-syntax = "proto3";
-
-package soong_build_bp2build_metrics;
-option go_package = "android/soong/ui/metrics/bp2build_metrics_proto";
-
-message Bp2BuildMetrics {
-  // Total number of Soong modules converted to generated targets
-  uint64 generatedModuleCount = 1;
-
-  // Total number of Soong modules converted to handcrafted targets
-  uint64 handCraftedModuleCount = 2;
-
-  // Total number of unconverted Soong modules
-  uint64 unconvertedModuleCount = 3;
-
-  // Counts of symlinks in synthetic bazel workspace
-  uint64 workspaceSymlinkCount= 9;
-
-  // Counts of mkdir calls during creation of synthetic bazel workspace
-  uint64 workspaceMkDirCount= 10;
-
-  // Counts of generated Bazel targets per Bazel rule class
-  map<string, uint64> ruleClassCount = 4;
-
-  // List of converted modules
-  repeated string convertedModules = 5;
-
-  // Unconverted modules, mapped to the reason the module was not converted.
-  map<string, UnconvertedReason> unconvertedModules = 11;
-
-  // Counts of converted modules by module type.
-  map<string, uint64> convertedModuleTypeCount = 6;
-
-  // Counts of total modules by module type.
-  map<string, uint64> totalModuleTypeCount = 7;
-
-  // List of traced runtime events of bp2build, useful for tracking bp2build
-  // runtime.
-  repeated Event events = 8;
-}
-
-// Traced runtime event of bp2build.
-message Event {
-  // The event name.
-  string name = 1;
-
-  // The absolute start time of the event
-  // The number of nanoseconds elapsed since January 1, 1970 UTC.
-  uint64 start_time = 2;
-
-  // The real running time.
-  // The number of nanoseconds elapsed since start_time.
-  uint64 real_time = 3;
-}
-
-message UnconvertedReason {
-  // The type of reason that the module could not be converted.
-  UnconvertedReasonType type = 1;
-
-  // Descriptive details describing why the module could not be converted.
-  // This detail should be kept very short and should be in the context of
-  // the type. (Otherwise, this would significantly bloat metrics.)
-  string detail = 2;
-}
-
-enum UnconvertedReasonType {
-  // Bp2build does not know how to convert this specific module for some reason
-  // not covered by other reason types. The reason detail should explain the
-  // specific issue.
-  UNSUPPORTED = 0;
-
-  // The module was already defined in a BUILD file available in the source tree.
-  DEFINED_IN_BUILD_FILE = 1;
-
-  // The module was explicitly denylisted by name.
-  DENYLISTED = 2;
-
-  // The module's type has no bp2build implementation.
-  TYPE_UNSUPPORTED = 3;
-
-  // The module has a property not yet supported. The detail field should
-  // name the unsupported property name.
-  PROPERTY_UNSUPPORTED = 4;
-
-  // The module has an unconverted dependency. The detail should consist of
-  // the name of the unconverted module.
-  UNCONVERTED_DEP = 5;
-
-  // The module has a source file with the same name as the module itself.
-  SRC_NAME_COLLISION = 6;
-}
\ No newline at end of file
diff --git a/ui/metrics/bp2build_metrics_proto/regen.sh b/ui/metrics/bp2build_metrics_proto/regen.sh
deleted file mode 100755
index bfe4294..0000000
--- a/ui/metrics/bp2build_metrics_proto/regen.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash -e
-
-# Copyright 2021 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.
-
-# Generates the golang source file of bp2build_metrics.proto protobuf file.
-
-function die() { echo "ERROR: $1" >&2; exit 1; }
-
-readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
-
-if ! hash aprotoc &>/dev/null; then
-  die "could not find aprotoc. ${error_msg}"
-fi
-
-if ! aprotoc --go_out=paths=source_relative:. bp2build_metrics.proto; then
-  die "build failed. ${error_msg}"
-fi
diff --git a/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel b/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
deleted file mode 100644
index f6c6df8..0000000
--- a/ui/metrics/bp2build_progress_metrics_proto/BUILD.bazel
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-
-load("//build/bazel/rules/python:py_proto.bzl", "py_proto_library")
-
-proto_library(
-    name = "bp2build_proto",
-    srcs = ["bp2build.proto"],
-    strip_import_prefix = "",
-)
-
-py_proto_library(
-    name = "bp2build_py_proto",
-    visibility = ["//build/bazel/scripts/bp2build_progress:__pkg__"],
-    deps = [":bp2build_proto"],
-)
diff --git a/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto b/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto
deleted file mode 100644
index 5b44002..0000000
--- a/ui/metrics/bp2build_progress_metrics_proto/bp2build.proto
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto3";
-
-package bp2build_proto;
-
-
-// Conversion progress report for root_modules .
-message Bp2buildConversionProgress {
-
-  // Soong module identifying information.
-  message Module {
-    // Name of the Soong module.
-    string name = 1;
-
-    // Directory that the Soong module is in.
-    string directory = 2;
-
-    // Module type of this module.
-    string type = 3;
-
-    // All unconverted transitive dependencies.
-    repeated string unconverted_deps = 4;
-
-    // Total number of transitive dependencies.
-    int32 num_deps = 5;
-
-    // Unconverted reasons from heuristics
-    repeated string unconverted_reasons_from_heuristics = 6;
-  }
-
-  // Modules that the transitive dependencies were identified for.
-  repeated string root_modules = 1;
-
-  // Names of all dependencies of the root_modules.
-  int32 num_deps = 2;
-
-  // Module with all its unconverted transitive dependencies.
-  repeated Module unconverted = 3;
-}
diff --git a/ui/metrics/hostinfo.go b/ui/metrics/hostinfo.go
new file mode 100644
index 0000000..f401b2a
--- /dev/null
+++ b/ui/metrics/hostinfo.go
@@ -0,0 +1,111 @@
+// 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 metrics
+
+// The hostinfo* files contain code to extract host information from
+// /proc/cpuinfo and /proc/meminfo relevant to machine performance
+
+import (
+	"strconv"
+	"strings"
+)
+
+// CpuInfo holds information regarding the host's CPU cores.
+type CpuInfo struct {
+	// The vendor id
+	VendorId string
+
+	// The model name
+	ModelName string
+
+	// The number of CPU cores
+	CpuCores int32
+
+	// The CPU flags
+	Flags string
+}
+
+// MemInfo holds information regarding the host's memory.
+// The memory size in each of the field is in bytes.
+type MemInfo struct {
+	// The total memory.
+	MemTotal uint64
+
+	// The amount of free memory.
+	MemFree uint64
+
+	// The amount of available memory.
+	MemAvailable uint64
+}
+
+// fillCpuInfo takes the key and value, converts the value
+// to the proper size unit and is stores it in CpuInfo.
+func (c *CpuInfo) fillInfo(key, value string) {
+	switch key {
+	case "vendor_id":
+		c.VendorId = value
+	case "model name":
+		c.ModelName = value
+	case "cpu cores":
+		v, err := strconv.ParseInt(value, 10, 32)
+		if err == nil {
+			c.CpuCores = int32(v)
+		}
+	case "flags":
+		c.Flags = value
+	default:
+		// Ignore unknown keys
+	}
+}
+
+// fillCpuInfo takes the key and value, converts the value
+// to the proper size unit and is stores it in CpuInfo.
+func (m *MemInfo) fillInfo(key, value string) {
+	v := strToUint64(value)
+	switch key {
+	case "MemTotal":
+		m.MemTotal = v
+	case "MemFree":
+		m.MemFree = v
+	case "MemAvailable":
+		m.MemAvailable = v
+	default:
+		// Ignore unknown keys
+	}
+}
+
+// strToUint64 takes the string and converts to unsigned 64-bit integer.
+// If the string contains a memory unit such as kB and is converted to
+// bytes.
+func strToUint64(v string) uint64 {
+	// v could be "1024 kB" so scan for the empty space and
+	// split between the value and the unit.
+	var separatorIndex int
+	if separatorIndex = strings.IndexAny(v, " "); separatorIndex < 0 {
+		separatorIndex = len(v)
+	}
+	value, err := strconv.ParseUint(v[:separatorIndex], 10, 64)
+	if err != nil {
+		return 0
+	}
+
+	var scale uint64 = 1
+	switch strings.TrimSpace(v[separatorIndex:]) {
+	case "kB", "KB":
+		scale = 1024
+	case "mB", "MB":
+		scale = 1024 * 1024
+	}
+	return value * scale
+}
diff --git a/ui/metrics/hostinfo_darwin.go b/ui/metrics/hostinfo_darwin.go
new file mode 100644
index 0000000..6f35c7d
--- /dev/null
+++ b/ui/metrics/hostinfo_darwin.go
@@ -0,0 +1,29 @@
+// 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 metrics
+
+// This file contain code to extract host information on linux from
+// /proc/cpuinfo and /proc/meminfo relevant to machine performance
+
+import (
+	"android/soong/finder/fs"
+)
+
+func NewCpuInfo(fileSystem fs.FileSystem) (*CpuInfo, error) {
+	return &CpuInfo{}, nil
+}
+
+func NewMemInfo(fileSystem fs.FileSystem) (*MemInfo, error) {
+	return &MemInfo{}, nil
+}
diff --git a/ui/metrics/hostinfo_linux.go b/ui/metrics/hostinfo_linux.go
new file mode 100644
index 0000000..b983dbc
--- /dev/null
+++ b/ui/metrics/hostinfo_linux.go
@@ -0,0 +1,72 @@
+// 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 metrics
+
+// This file contain code to extract host information on linux from
+// /proc/cpuinfo and /proc/meminfo relevant to machine performance
+
+import (
+	"io/ioutil"
+	"strings"
+
+	"android/soong/finder/fs"
+)
+
+type fillable interface {
+	fillInfo(key, value string)
+}
+
+func NewCpuInfo(fileSystem fs.FileSystem) (*CpuInfo, error) {
+	c := &CpuInfo{}
+	if err := parseFile(c, "/proc/cpuinfo", true, fileSystem); err != nil {
+		return &CpuInfo{}, err
+	}
+	return c, nil
+}
+
+func NewMemInfo(fileSystem fs.FileSystem) (*MemInfo, error) {
+	m := &MemInfo{}
+	if err := parseFile(m, "/proc/meminfo", false, fileSystem); err != nil {
+		return &MemInfo{}, err
+	}
+	return m, nil
+}
+
+func parseFile(obj fillable, fileName string, endOnBlank bool, fileSystem fs.FileSystem) error {
+	fd, err := fileSystem.Open(fileName)
+	if err != nil {
+		return err
+	}
+	defer fd.Close()
+
+	data, err := ioutil.ReadAll(fd)
+	if err != nil {
+		return err
+	}
+
+	for _, l := range strings.Split(string(data), "\n") {
+		if !strings.Contains(l, ":") {
+			// Terminate after the first blank line.
+			if endOnBlank && strings.TrimSpace(l) == "" {
+				break
+			}
+			// If the line is not of the form "key: values", just skip it.
+			continue
+		}
+
+		kv := strings.SplitN(l, ":", 2)
+		obj.fillInfo(strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]))
+	}
+	return nil
+}
diff --git a/ui/metrics/hostinfo_linux_test.go b/ui/metrics/hostinfo_linux_test.go
new file mode 100644
index 0000000..c44e453
--- /dev/null
+++ b/ui/metrics/hostinfo_linux_test.go
@@ -0,0 +1,118 @@
+// 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 metrics
+
+// This file contain code to extract host information on linux from
+// /proc/cpuinfo and /proc/meminfo relevant to machine performance
+
+import (
+	"reflect"
+	"testing"
+
+	"android/soong/finder/fs"
+)
+
+func TestNewCpuInfo(t *testing.T) {
+	fs := fs.NewMockFs(nil)
+
+	if err := fs.MkDirs("/proc"); err != nil {
+		t.Fatalf("failed to create /proc dir: %v", err)
+	}
+	cpuFileName := "/proc/cpuinfo"
+
+	if err := fs.WriteFile(cpuFileName, cpuData, 0644); err != nil {
+		t.Fatalf("failed to write file %s: %v", cpuFileName, err)
+	}
+
+	cpuInfo, err := NewCpuInfo(fs)
+	if err != nil {
+		t.Fatalf("got %v, want nil for error", err)
+	}
+
+	if !reflect.DeepEqual(cpuInfo, expectedCpuInfo) {
+		t.Errorf("got %v, expecting %v for CpuInfo", cpuInfo, expectedCpuInfo)
+	}
+
+}
+
+func TestNewMemInfo(t *testing.T) {
+	fs := fs.NewMockFs(nil)
+
+	if err := fs.MkDirs("/proc"); err != nil {
+		t.Fatalf("failed to create /proc dir: %v", err)
+	}
+	memFileName := "/proc/meminfo"
+
+	if err := fs.WriteFile(memFileName, memData, 0644); err != nil {
+		t.Fatalf("failed to write file %s: %v", memFileName, err)
+	}
+
+	memInfo, err := NewMemInfo(fs)
+	if err != nil {
+		t.Fatalf("got %v, want nil for error", err)
+	}
+
+	if !reflect.DeepEqual(memInfo, expectedMemInfo) {
+		t.Errorf("got %v, expecting %v for MemInfo", memInfo, expectedMemInfo)
+	}
+
+}
+
+var cpuData = []byte(`processor	: 0
+vendor_id	: %%VENDOR%%
+cpu family	: 123
+model		: 456
+model name	: %%CPU MODEL NAME%%
+stepping	: 0
+cpu MHz		: 5555.555
+cache size	: 512 KB
+physical id	: 0
+siblings	: 128
+core id		: 0
+cpu cores	: 64
+apicid		: 0
+initial apicid	: 0
+fpu		: yes
+fpu_exception	: yes
+cpuid level	: 789
+wp		: yes
+flags		: %%cpu flags go here%%
+bugs		: %%bugs go here%%
+
+processor	: 1
+vendor_id	: %%BADVENDOR%%
+cpu family	: 234
+model		: 567
+model name	: %%BAD MODEL NAME%%
+flags		: %%BAD cpu flags go here%%
+`)
+
+var expectedCpuInfo = &CpuInfo{
+	VendorId:  "%%VENDOR%%",
+	ModelName: "%%CPU MODEL NAME%%",
+	CpuCores:  64,
+	Flags:     "%%cpu flags go here%%",
+}
+
+var memData = []byte(`MemTotal:       1000 mB
+MemFree:        10240000
+MemAvailable:   3000 kB
+Buffers:         7177844 kB
+`)
+
+var expectedMemInfo = &MemInfo{
+	MemTotal:     1048576000,
+	MemFree:      10240000,
+	MemAvailable: 3072000,
+}
diff --git a/ui/metrics/metrics_proto/Android.bp b/ui/metrics/metrics_proto/Android.bp
new file mode 100644
index 0000000..aae5266
--- /dev/null
+++ b/ui/metrics/metrics_proto/Android.bp
@@ -0,0 +1,16 @@
+python_library_host {
+    name: "soong-metrics-proto-py",
+    srcs: [
+        "metrics.proto",
+    ],
+    visibility: [
+        "//build/make/ci",
+    ],
+    libs: [
+        "libprotobuf-python",
+    ],
+    proto: {
+        include_dirs: ["external/protobuf/src"],
+        canonical_path_from_root: false,
+    },
+}
diff --git a/ui/metrics/metrics_proto/combined_metrics.pb.go b/ui/metrics/metrics_proto/combined_metrics.pb.go
new file mode 100644
index 0000000..f49d64d
--- /dev/null
+++ b/ui/metrics/metrics_proto/combined_metrics.pb.go
@@ -0,0 +1,239 @@
+// Copyright 2018 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        v3.21.12
+// source: combined_metrics.proto
+
+package metrics_proto
+
+import (
+	find_input_delta_proto "android/soong/cmd/find_input_delta/find_input_delta_proto"
+	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)
+)
+
+// These field numbers are also found in the inner message declarations.
+// We verify that the values are the same, and that every enum value is checked
+// in combined_metrics_test.go.
+// Do not change this enum without also updating:
+//   - the submessage's .proto file
+//   - combined_metrics_test.go
+type FieldNumbers int32
+
+const (
+	FieldNumbers_FIELD_NUMBERS_UNSPECIFIED FieldNumbers = 0
+	FieldNumbers_FIELD_NUMBERS_FILE_LIST   FieldNumbers = 1
+)
+
+// Enum value maps for FieldNumbers.
+var (
+	FieldNumbers_name = map[int32]string{
+		0: "FIELD_NUMBERS_UNSPECIFIED",
+		1: "FIELD_NUMBERS_FILE_LIST",
+	}
+	FieldNumbers_value = map[string]int32{
+		"FIELD_NUMBERS_UNSPECIFIED": 0,
+		"FIELD_NUMBERS_FILE_LIST":   1,
+	}
+)
+
+func (x FieldNumbers) Enum() *FieldNumbers {
+	p := new(FieldNumbers)
+	*p = x
+	return p
+}
+
+func (x FieldNumbers) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (FieldNumbers) Descriptor() protoreflect.EnumDescriptor {
+	return file_combined_metrics_proto_enumTypes[0].Descriptor()
+}
+
+func (FieldNumbers) Type() protoreflect.EnumType {
+	return &file_combined_metrics_proto_enumTypes[0]
+}
+
+func (x FieldNumbers) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *FieldNumbers) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+	if err != nil {
+		return err
+	}
+	*x = FieldNumbers(num)
+	return nil
+}
+
+// Deprecated: Use FieldNumbers.Descriptor instead.
+func (FieldNumbers) EnumDescriptor() ([]byte, []int) {
+	return file_combined_metrics_proto_rawDescGZIP(), []int{0}
+}
+
+type SoongCombinedMetrics struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// cmd/find_input_delta/find_input_delta_proto.FileList
+	FileList *find_input_delta_proto.FileList `protobuf:"bytes,1,opt,name=file_list,json=fileList" json:"file_list,omitempty"`
+}
+
+func (x *SoongCombinedMetrics) Reset() {
+	*x = SoongCombinedMetrics{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_combined_metrics_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SoongCombinedMetrics) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SoongCombinedMetrics) ProtoMessage() {}
+
+func (x *SoongCombinedMetrics) ProtoReflect() protoreflect.Message {
+	mi := &file_combined_metrics_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 SoongCombinedMetrics.ProtoReflect.Descriptor instead.
+func (*SoongCombinedMetrics) Descriptor() ([]byte, []int) {
+	return file_combined_metrics_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *SoongCombinedMetrics) GetFileList() *find_input_delta_proto.FileList {
+	if x != nil {
+		return x.FileList
+	}
+	return nil
+}
+
+var File_combined_metrics_proto protoreflect.FileDescriptor
+
+var file_combined_metrics_proto_rawDesc = []byte{
+	0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0x3b, 0x63,
+	0x6d, 0x64, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65,
+	0x6c, 0x74, 0x61, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64,
+	0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x5f,
+	0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5d, 0x0a, 0x14, 0x53, 0x6f,
+	0x6f, 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x12, 0x45, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+	0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
+	0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52,
+	0x08, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x2a, 0x4a, 0x0a, 0x0c, 0x46, 0x69, 0x65,
+	0x6c, 0x64, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x49, 0x45,
+	0x4c, 0x44, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
+	0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x49, 0x45, 0x4c,
+	0x44, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x4c,
+	0x49, 0x53, 0x54, 0x10, 0x01, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_combined_metrics_proto_rawDescOnce sync.Once
+	file_combined_metrics_proto_rawDescData = file_combined_metrics_proto_rawDesc
+)
+
+func file_combined_metrics_proto_rawDescGZIP() []byte {
+	file_combined_metrics_proto_rawDescOnce.Do(func() {
+		file_combined_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_combined_metrics_proto_rawDescData)
+	})
+	return file_combined_metrics_proto_rawDescData
+}
+
+var file_combined_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_combined_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_combined_metrics_proto_goTypes = []interface{}{
+	(FieldNumbers)(0),                       // 0: soong_build_metrics.FieldNumbers
+	(*SoongCombinedMetrics)(nil),            // 1: soong_build_metrics.SoongCombinedMetrics
+	(*find_input_delta_proto.FileList)(nil), // 2: android.find_input_delta_proto.FileList
+}
+var file_combined_metrics_proto_depIdxs = []int32{
+	2, // 0: soong_build_metrics.SoongCombinedMetrics.file_list:type_name -> android.find_input_delta_proto.FileList
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_combined_metrics_proto_init() }
+func file_combined_metrics_proto_init() {
+	if File_combined_metrics_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_combined_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SoongCombinedMetrics); 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_combined_metrics_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_combined_metrics_proto_goTypes,
+		DependencyIndexes: file_combined_metrics_proto_depIdxs,
+		EnumInfos:         file_combined_metrics_proto_enumTypes,
+		MessageInfos:      file_combined_metrics_proto_msgTypes,
+	}.Build()
+	File_combined_metrics_proto = out.File
+	file_combined_metrics_proto_rawDesc = nil
+	file_combined_metrics_proto_goTypes = nil
+	file_combined_metrics_proto_depIdxs = nil
+}
diff --git a/ui/metrics/metrics_proto/combined_metrics.proto b/ui/metrics/metrics_proto/combined_metrics.proto
new file mode 100644
index 0000000..3cd9a53
--- /dev/null
+++ b/ui/metrics/metrics_proto/combined_metrics.proto
@@ -0,0 +1,36 @@
+// Copyright 2018 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.
+
+syntax = "proto2";
+
+package soong_build_metrics;
+option go_package = "android/soong/ui/metrics/metrics_proto";
+
+import "cmd/find_input_delta/find_input_delta_proto/file_list.proto";
+
+// These field numbers are also found in the inner message declarations.
+// We verify that the values are the same, and that every enum value is checked
+// in combined_metrics_test.go.
+// Do not change this enum without also updating:
+//  - the submessage's .proto file
+//  - combined_metrics_test.go
+enum FieldNumbers {
+  FIELD_NUMBERS_UNSPECIFIED = 0;
+  FIELD_NUMBERS_FILE_LIST = 1;
+}
+
+message SoongCombinedMetrics {
+  // cmd/find_input_delta/find_input_delta_proto.FileList
+  optional android.find_input_delta_proto.FileList file_list = 1;
+}
diff --git a/ui/metrics/metrics_proto/combined_metrics_test.go b/ui/metrics/metrics_proto/combined_metrics_test.go
new file mode 100644
index 0000000..eedb12a
--- /dev/null
+++ b/ui/metrics/metrics_proto/combined_metrics_test.go
@@ -0,0 +1,33 @@
+package metrics_proto
+
+import (
+	"testing"
+
+	find_input_delta_proto "android/soong/cmd/find_input_delta/find_input_delta_proto"
+)
+
+func TestCombinedMetricsMessageNums(t *testing.T) {
+	testCases := []struct {
+		Name         string
+		FieldNumbers map[string]int32
+	}{
+		{
+			Name:         "find_input_delta_proto",
+			FieldNumbers: find_input_delta_proto.FieldNumbers_value,
+		},
+	}
+	verifiedMap := make(map[string]bool)
+	for _, tc := range testCases {
+		for k, v := range tc.FieldNumbers {
+			if FieldNumbers_value[k] != v {
+				t.Errorf("%s: Expected FieldNumbers.%s == %v, found %v", tc.Name, k, FieldNumbers_value[k], v)
+			}
+			verifiedMap[k] = true
+		}
+	}
+	for k, v := range FieldNumbers_value {
+		if !verifiedMap[k] {
+			t.Errorf("No test case verifies FieldNumbers.%s=%v", k, v)
+		}
+	}
+}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index b75f572..72fdbe8 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.30.0
+// 	protoc-gen-go v1.33.0
 // 	protoc        v3.21.12
 // source: metrics.proto
 
@@ -279,7 +279,7 @@
 
 // Deprecated: Use ModuleTypeInfo_BuildSystem.Descriptor instead.
 func (ModuleTypeInfo_BuildSystem) EnumDescriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{8, 0}
+	return file_metrics_proto_rawDescGZIP(), []int{10, 0}
 }
 
 type ExpConfigFetcher_ConfigStatus int32
@@ -341,7 +341,7 @@
 
 // Deprecated: Use ExpConfigFetcher_ConfigStatus.Descriptor instead.
 func (ExpConfigFetcher_ConfigStatus) EnumDescriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{12, 0}
+	return file_metrics_proto_rawDescGZIP(), []int{14, 0}
 }
 
 type MetricsBase struct {
@@ -425,6 +425,8 @@
 	// Note that not all changed environment variables result in analysis retriggering.
 	// If there was no previous build, this list will be empty.
 	ChangedEnvironmentVariable []string `protobuf:"bytes,34,rep,name=changed_environment_variable,json=changedEnvironmentVariable" json:"changed_environment_variable,omitempty"`
+	// Metrics related to optimized builds.
+	OptimizedBuildMetrics *OptimizedBuildMetrics `protobuf:"bytes,35,opt,name=optimized_build_metrics,json=optimizedBuildMetrics" json:"optimized_build_metrics,omitempty"`
 }
 
 // Default values for MetricsBase fields.
@@ -706,6 +708,13 @@
 	return nil
 }
 
+func (x *MetricsBase) GetOptimizedBuildMetrics() *OptimizedBuildMetrics {
+	if x != nil {
+		return x.OptimizedBuildMetrics
+	}
+	return nil
+}
+
 type BuildConfig struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -834,6 +843,10 @@
 	TotalPhysicalMemory *uint64 `protobuf:"varint,1,opt,name=total_physical_memory,json=totalPhysicalMemory" json:"total_physical_memory,omitempty"`
 	// The total of available cores for building
 	AvailableCpus *int32 `protobuf:"varint,2,opt,name=available_cpus,json=availableCpus" json:"available_cpus,omitempty"`
+	// Information about the machine's CPU(s).
+	CpuInfo *SystemCpuInfo `protobuf:"bytes,3,opt,name=cpu_info,json=cpuInfo" json:"cpu_info,omitempty"`
+	// Information about the machine's memory.
+	MemInfo *SystemMemInfo `protobuf:"bytes,4,opt,name=mem_info,json=memInfo" json:"mem_info,omitempty"`
 }
 
 func (x *SystemResourceInfo) Reset() {
@@ -882,6 +895,161 @@
 	return 0
 }
 
+func (x *SystemResourceInfo) GetCpuInfo() *SystemCpuInfo {
+	if x != nil {
+		return x.CpuInfo
+	}
+	return nil
+}
+
+func (x *SystemResourceInfo) GetMemInfo() *SystemMemInfo {
+	if x != nil {
+		return x.MemInfo
+	}
+	return nil
+}
+
+type SystemCpuInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The vendor id
+	VendorId *string `protobuf:"bytes,1,opt,name=vendor_id,json=vendorId" json:"vendor_id,omitempty"`
+	// The model name
+	ModelName *string `protobuf:"bytes,2,opt,name=model_name,json=modelName" json:"model_name,omitempty"`
+	// The number of CPU cores
+	CpuCores *int32 `protobuf:"varint,3,opt,name=cpu_cores,json=cpuCores" json:"cpu_cores,omitempty"`
+	// The CPU flags
+	Flags *string `protobuf:"bytes,4,opt,name=flags" json:"flags,omitempty"`
+}
+
+func (x *SystemCpuInfo) Reset() {
+	*x = SystemCpuInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SystemCpuInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SystemCpuInfo) ProtoMessage() {}
+
+func (x *SystemCpuInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[3]
+	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 SystemCpuInfo.ProtoReflect.Descriptor instead.
+func (*SystemCpuInfo) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *SystemCpuInfo) GetVendorId() string {
+	if x != nil && x.VendorId != nil {
+		return *x.VendorId
+	}
+	return ""
+}
+
+func (x *SystemCpuInfo) GetModelName() string {
+	if x != nil && x.ModelName != nil {
+		return *x.ModelName
+	}
+	return ""
+}
+
+func (x *SystemCpuInfo) GetCpuCores() int32 {
+	if x != nil && x.CpuCores != nil {
+		return *x.CpuCores
+	}
+	return 0
+}
+
+func (x *SystemCpuInfo) GetFlags() string {
+	if x != nil && x.Flags != nil {
+		return *x.Flags
+	}
+	return ""
+}
+
+type SystemMemInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The total system memory
+	MemTotal *uint64 `protobuf:"varint,1,opt,name=mem_total,json=memTotal" json:"mem_total,omitempty"`
+	// The free system memory
+	MemFree *uint64 `protobuf:"varint,2,opt,name=mem_free,json=memFree" json:"mem_free,omitempty"`
+	// The available system memory
+	MemAvailable *uint64 `protobuf:"varint,3,opt,name=mem_available,json=memAvailable" json:"mem_available,omitempty"`
+}
+
+func (x *SystemMemInfo) Reset() {
+	*x = SystemMemInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SystemMemInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SystemMemInfo) ProtoMessage() {}
+
+func (x *SystemMemInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[4]
+	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 SystemMemInfo.ProtoReflect.Descriptor instead.
+func (*SystemMemInfo) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *SystemMemInfo) GetMemTotal() uint64 {
+	if x != nil && x.MemTotal != nil {
+		return *x.MemTotal
+	}
+	return 0
+}
+
+func (x *SystemMemInfo) GetMemFree() uint64 {
+	if x != nil && x.MemFree != nil {
+		return *x.MemFree
+	}
+	return 0
+}
+
+func (x *SystemMemInfo) GetMemAvailable() uint64 {
+	if x != nil && x.MemAvailable != nil {
+		return *x.MemAvailable
+	}
+	return 0
+}
+
 type PerfInfo struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -913,7 +1081,7 @@
 func (x *PerfInfo) Reset() {
 	*x = PerfInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[3]
+		mi := &file_metrics_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -926,7 +1094,7 @@
 func (*PerfInfo) ProtoMessage() {}
 
 func (x *PerfInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[3]
+	mi := &file_metrics_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -939,7 +1107,7 @@
 
 // Deprecated: Use PerfInfo.ProtoReflect.Descriptor instead.
 func (*PerfInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{3}
+	return file_metrics_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *PerfInfo) GetDescription() string {
@@ -1013,7 +1181,7 @@
 func (x *PerfCounters) Reset() {
 	*x = PerfCounters{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[4]
+		mi := &file_metrics_proto_msgTypes[6]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1026,7 +1194,7 @@
 func (*PerfCounters) ProtoMessage() {}
 
 func (x *PerfCounters) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[4]
+	mi := &file_metrics_proto_msgTypes[6]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1039,7 +1207,7 @@
 
 // Deprecated: Use PerfCounters.ProtoReflect.Descriptor instead.
 func (*PerfCounters) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{4}
+	return file_metrics_proto_rawDescGZIP(), []int{6}
 }
 
 func (x *PerfCounters) GetTime() uint64 {
@@ -1070,7 +1238,7 @@
 func (x *PerfCounterGroup) Reset() {
 	*x = PerfCounterGroup{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[5]
+		mi := &file_metrics_proto_msgTypes[7]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1083,7 +1251,7 @@
 func (*PerfCounterGroup) ProtoMessage() {}
 
 func (x *PerfCounterGroup) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[5]
+	mi := &file_metrics_proto_msgTypes[7]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1096,7 +1264,7 @@
 
 // Deprecated: Use PerfCounterGroup.ProtoReflect.Descriptor instead.
 func (*PerfCounterGroup) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{5}
+	return file_metrics_proto_rawDescGZIP(), []int{7}
 }
 
 func (x *PerfCounterGroup) GetName() string {
@@ -1127,7 +1295,7 @@
 func (x *PerfCounter) Reset() {
 	*x = PerfCounter{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[6]
+		mi := &file_metrics_proto_msgTypes[8]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1140,7 +1308,7 @@
 func (*PerfCounter) ProtoMessage() {}
 
 func (x *PerfCounter) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[6]
+	mi := &file_metrics_proto_msgTypes[8]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1153,7 +1321,7 @@
 
 // Deprecated: Use PerfCounter.ProtoReflect.Descriptor instead.
 func (*PerfCounter) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{6}
+	return file_metrics_proto_rawDescGZIP(), []int{8}
 }
 
 func (x *PerfCounter) GetName() string {
@@ -1200,7 +1368,7 @@
 func (x *ProcessResourceInfo) Reset() {
 	*x = ProcessResourceInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[7]
+		mi := &file_metrics_proto_msgTypes[9]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1213,7 +1381,7 @@
 func (*ProcessResourceInfo) ProtoMessage() {}
 
 func (x *ProcessResourceInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[7]
+	mi := &file_metrics_proto_msgTypes[9]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1226,7 +1394,7 @@
 
 // Deprecated: Use ProcessResourceInfo.ProtoReflect.Descriptor instead.
 func (*ProcessResourceInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{7}
+	return file_metrics_proto_rawDescGZIP(), []int{9}
 }
 
 func (x *ProcessResourceInfo) GetName() string {
@@ -1320,7 +1488,7 @@
 func (x *ModuleTypeInfo) Reset() {
 	*x = ModuleTypeInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[8]
+		mi := &file_metrics_proto_msgTypes[10]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1333,7 +1501,7 @@
 func (*ModuleTypeInfo) ProtoMessage() {}
 
 func (x *ModuleTypeInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[8]
+	mi := &file_metrics_proto_msgTypes[10]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1346,7 +1514,7 @@
 
 // Deprecated: Use ModuleTypeInfo.ProtoReflect.Descriptor instead.
 func (*ModuleTypeInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{8}
+	return file_metrics_proto_rawDescGZIP(), []int{10}
 }
 
 func (x *ModuleTypeInfo) GetBuildSystem() ModuleTypeInfo_BuildSystem {
@@ -1384,7 +1552,7 @@
 func (x *CriticalUserJourneyMetrics) Reset() {
 	*x = CriticalUserJourneyMetrics{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[9]
+		mi := &file_metrics_proto_msgTypes[11]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1397,7 +1565,7 @@
 func (*CriticalUserJourneyMetrics) ProtoMessage() {}
 
 func (x *CriticalUserJourneyMetrics) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[9]
+	mi := &file_metrics_proto_msgTypes[11]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1410,7 +1578,7 @@
 
 // Deprecated: Use CriticalUserJourneyMetrics.ProtoReflect.Descriptor instead.
 func (*CriticalUserJourneyMetrics) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{9}
+	return file_metrics_proto_rawDescGZIP(), []int{11}
 }
 
 func (x *CriticalUserJourneyMetrics) GetName() string {
@@ -1439,7 +1607,7 @@
 func (x *CriticalUserJourneysMetrics) Reset() {
 	*x = CriticalUserJourneysMetrics{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[10]
+		mi := &file_metrics_proto_msgTypes[12]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1452,7 +1620,7 @@
 func (*CriticalUserJourneysMetrics) ProtoMessage() {}
 
 func (x *CriticalUserJourneysMetrics) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[10]
+	mi := &file_metrics_proto_msgTypes[12]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1465,7 +1633,7 @@
 
 // Deprecated: Use CriticalUserJourneysMetrics.ProtoReflect.Descriptor instead.
 func (*CriticalUserJourneysMetrics) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{10}
+	return file_metrics_proto_rawDescGZIP(), []int{12}
 }
 
 func (x *CriticalUserJourneysMetrics) GetCujs() []*CriticalUserJourneyMetrics {
@@ -1501,7 +1669,7 @@
 func (x *SoongBuildMetrics) Reset() {
 	*x = SoongBuildMetrics{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[11]
+		mi := &file_metrics_proto_msgTypes[13]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1514,7 +1682,7 @@
 func (*SoongBuildMetrics) ProtoMessage() {}
 
 func (x *SoongBuildMetrics) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[11]
+	mi := &file_metrics_proto_msgTypes[13]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1527,7 +1695,7 @@
 
 // Deprecated: Use SoongBuildMetrics.ProtoReflect.Descriptor instead.
 func (*SoongBuildMetrics) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{11}
+	return file_metrics_proto_rawDescGZIP(), []int{13}
 }
 
 func (x *SoongBuildMetrics) GetModules() uint32 {
@@ -1605,7 +1773,7 @@
 func (x *ExpConfigFetcher) Reset() {
 	*x = ExpConfigFetcher{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[12]
+		mi := &file_metrics_proto_msgTypes[14]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1618,7 +1786,7 @@
 func (*ExpConfigFetcher) ProtoMessage() {}
 
 func (x *ExpConfigFetcher) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[12]
+	mi := &file_metrics_proto_msgTypes[14]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1631,7 +1799,7 @@
 
 // Deprecated: Use ExpConfigFetcher.ProtoReflect.Descriptor instead.
 func (*ExpConfigFetcher) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{12}
+	return file_metrics_proto_rawDescGZIP(), []int{14}
 }
 
 func (x *ExpConfigFetcher) GetStatus() ExpConfigFetcher_ConfigStatus {
@@ -1669,7 +1837,7 @@
 func (x *MixedBuildsInfo) Reset() {
 	*x = MixedBuildsInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[13]
+		mi := &file_metrics_proto_msgTypes[15]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1682,7 +1850,7 @@
 func (*MixedBuildsInfo) ProtoMessage() {}
 
 func (x *MixedBuildsInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[13]
+	mi := &file_metrics_proto_msgTypes[15]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1695,7 +1863,7 @@
 
 // Deprecated: Use MixedBuildsInfo.ProtoReflect.Descriptor instead.
 func (*MixedBuildsInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{13}
+	return file_metrics_proto_rawDescGZIP(), []int{15}
 }
 
 func (x *MixedBuildsInfo) GetMixedBuildEnabledModules() []string {
@@ -1732,7 +1900,7 @@
 func (x *CriticalPathInfo) Reset() {
 	*x = CriticalPathInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[14]
+		mi := &file_metrics_proto_msgTypes[16]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1745,7 +1913,7 @@
 func (*CriticalPathInfo) ProtoMessage() {}
 
 func (x *CriticalPathInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[14]
+	mi := &file_metrics_proto_msgTypes[16]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1758,7 +1926,7 @@
 
 // Deprecated: Use CriticalPathInfo.ProtoReflect.Descriptor instead.
 func (*CriticalPathInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{14}
+	return file_metrics_proto_rawDescGZIP(), []int{16}
 }
 
 func (x *CriticalPathInfo) GetElapsedTimeMicros() uint64 {
@@ -1803,7 +1971,7 @@
 func (x *JobInfo) Reset() {
 	*x = JobInfo{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_metrics_proto_msgTypes[15]
+		mi := &file_metrics_proto_msgTypes[17]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1816,7 +1984,7 @@
 func (*JobInfo) ProtoMessage() {}
 
 func (x *JobInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_metrics_proto_msgTypes[15]
+	mi := &file_metrics_proto_msgTypes[17]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1829,7 +1997,7 @@
 
 // Deprecated: Use JobInfo.ProtoReflect.Descriptor instead.
 func (*JobInfo) Descriptor() ([]byte, []int) {
-	return file_metrics_proto_rawDescGZIP(), []int{15}
+	return file_metrics_proto_rawDescGZIP(), []int{17}
 }
 
 func (x *JobInfo) GetElapsedTimeMicros() uint64 {
@@ -1846,12 +2014,229 @@
 	return ""
 }
 
+type OptimizedBuildMetrics struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The total time spent analyzing what/how to optimize everything.
+	AnalysisPerf *PerfInfo `protobuf:"bytes,1,opt,name=analysis_perf,json=analysisPerf" json:"analysis_perf,omitempty"`
+	// The total time spent packaging artifacts.
+	PackagingPerf *PerfInfo `protobuf:"bytes,2,opt,name=packaging_perf,json=packagingPerf" json:"packaging_perf,omitempty"`
+	// Information for a single target (e.g. general-tests).
+	TargetResult []*OptimizedBuildMetrics_TargetOptimizationResult `protobuf:"bytes,3,rep,name=target_result,json=targetResult" json:"target_result,omitempty"`
+}
+
+func (x *OptimizedBuildMetrics) Reset() {
+	*x = OptimizedBuildMetrics{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[18]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *OptimizedBuildMetrics) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OptimizedBuildMetrics) ProtoMessage() {}
+
+func (x *OptimizedBuildMetrics) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[18]
+	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 OptimizedBuildMetrics.ProtoReflect.Descriptor instead.
+func (*OptimizedBuildMetrics) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{18}
+}
+
+func (x *OptimizedBuildMetrics) GetAnalysisPerf() *PerfInfo {
+	if x != nil {
+		return x.AnalysisPerf
+	}
+	return nil
+}
+
+func (x *OptimizedBuildMetrics) GetPackagingPerf() *PerfInfo {
+	if x != nil {
+		return x.PackagingPerf
+	}
+	return nil
+}
+
+func (x *OptimizedBuildMetrics) GetTargetResult() []*OptimizedBuildMetrics_TargetOptimizationResult {
+	if x != nil {
+		return x.TargetResult
+	}
+	return nil
+}
+
+type OptimizedBuildMetrics_TargetOptimizationResult struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Target name (e.g. general-tests).
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// Whether or not this target was optimized.
+	Optimized *bool `protobuf:"varint,2,opt,name=optimized" json:"optimized,omitempty"`
+	// Reasoning for why the target wasn't optimized if it wasn't
+	OptimizationRationale *string `protobuf:"bytes,3,opt,name=optimization_rationale,json=optimizationRationale" json:"optimization_rationale,omitempty"`
+	// Time spent packaging this specific target (if it was optimized).
+	PackagingPerf *PerfInfo `protobuf:"bytes,4,opt,name=packaging_perf,json=packagingPerf" json:"packaging_perf,omitempty"`
+	// Information for each different artifact produced by this target (if it
+	// was optimized).
+	OutputArtifact []*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact `protobuf:"bytes,5,rep,name=output_artifact,json=outputArtifact" json:"output_artifact,omitempty"`
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult) Reset() {
+	*x = OptimizedBuildMetrics_TargetOptimizationResult{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[19]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OptimizedBuildMetrics_TargetOptimizationResult) ProtoMessage() {}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[19]
+	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 OptimizedBuildMetrics_TargetOptimizationResult.ProtoReflect.Descriptor instead.
+func (*OptimizedBuildMetrics_TargetOptimizationResult) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{18, 0}
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult) GetOptimized() bool {
+	if x != nil && x.Optimized != nil {
+		return *x.Optimized
+	}
+	return false
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult) GetOptimizationRationale() string {
+	if x != nil && x.OptimizationRationale != nil {
+		return *x.OptimizationRationale
+	}
+	return ""
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult) GetPackagingPerf() *PerfInfo {
+	if x != nil {
+		return x.PackagingPerf
+	}
+	return nil
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult) GetOutputArtifact() []*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact {
+	if x != nil {
+		return x.OutputArtifact
+	}
+	return nil
+}
+
+type OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Artifact file name (e.g. general-tests.zip)
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// Size of the file.
+	Size *int64 `protobuf:"varint,2,opt,name=size" json:"size,omitempty"`
+	// Lists of modules packaged into this artifact.
+	IncludedModules []string `protobuf:"bytes,3,rep,name=included_modules,json=includedModules" json:"included_modules,omitempty"`
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) Reset() {
+	*x = OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[20]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) ProtoMessage() {}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[20]
+	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 OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact.ProtoReflect.Descriptor instead.
+func (*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) Descriptor() ([]byte, []int) {
+	return file_metrics_proto_rawDescGZIP(), []int{18, 0, 0}
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) GetSize() int64 {
+	if x != nil && x.Size != nil {
+		return *x.Size
+	}
+	return 0
+}
+
+func (x *OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact) GetIncludedModules() []string {
+	if x != nil {
+		return x.IncludedModules
+	}
+	return nil
+}
+
 var File_metrics_proto protoreflect.FileDescriptor
 
 var file_metrics_proto_rawDesc = []byte{
 	0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
 	0x13, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x22, 0xcc, 0x0f, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x72, 0x69, 0x63, 0x73, 0x22, 0xb0, 0x10, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
 	0x42, 0x61, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x61,
 	0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
 	0x28, 0x03, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
@@ -1969,220 +2354,293 @@
 	0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
 	0x65, 0x18, 0x22, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64,
 	0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61,
-	0x62, 0x6c, 0x65, 0x22, 0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69,
-	0x61, 0x6e, 0x74, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0d, 0x0a,
-	0x09, 0x55, 0x53, 0x45, 0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03,
-	0x45, 0x4e, 0x47, 0x10, 0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x0b, 0x0a,
-	0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x52,
-	0x4d, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, 0x02, 0x12, 0x07,
-	0x0a, 0x03, 0x58, 0x38, 0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58, 0x38, 0x36, 0x5f, 0x36,
-	0x34, 0x10, 0x04, 0x22, 0x8a, 0x04, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x17,
-	0x0a, 0x07, 0x75, 0x73, 0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
-	0x06, 0x75, 0x73, 0x65, 0x52, 0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x63, 0x65,
-	0x5f, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
-	0x0c, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x24, 0x0a,
-	0x0e, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x18,
-	0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x41, 0x73, 0x4e, 0x69,
-	0x6e, 0x6a, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69, 0x78,
-	0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f,
-	0x62, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12,
-	0x18, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09,
-	0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x44, 0x0a, 0x1f, 0x66, 0x6f, 0x72,
-	0x63, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c,
-	0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x07, 0x20, 0x01,
-	0x28, 0x08, 0x52, 0x1b, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
-	0x42, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12,
-	0x79, 0x0a, 0x18, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f,
-	0x6c, 0x69, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28,
-	0x0e, 0x32, 0x36, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x2e, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c,
-	0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55,
-	0x53, 0x45, 0x44, 0x52, 0x15, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74,
-	0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x74, 0x0a, 0x15, 0x4e, 0x69,
-	0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75,
-	0x72, 0x63, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x44, 0x10,
-	0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x49, 0x4e, 0x4a, 0x41, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x01,
-	0x12, 0x16, 0x0a, 0x12, 0x45, 0x56, 0x45, 0x4e, 0x4c, 0x59, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52,
-	0x49, 0x42, 0x55, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x58, 0x54, 0x45,
-	0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x48,
-	0x49, 0x4e, 0x54, 0x5f, 0x46, 0x52, 0x4f, 0x4d, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x04,
-	0x22, 0x6f, 0x0a, 0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
-	0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
-	0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x68, 0x79, 0x73,
-	0x69, 0x63, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x76,
-	0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x05, 0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x70, 0x75,
-	0x73, 0x22, 0xca, 0x02, 0x0a, 0x08, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20,
-	0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
-	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69,
-	0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54,
-	0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65,
-	0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65,
-	0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05,
-	0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
-	0x55, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73,
-	0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06,
-	0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65,
-	0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15,
-	0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
-	0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x6e, 0x5f, 0x7a, 0x65, 0x72,
-	0x6f, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f,
-	0x6e, 0x5a, 0x65, 0x72, 0x6f, 0x45, 0x78, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72,
-	0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x61,
-	0x0a, 0x0c, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x12,
-	0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x69,
-	0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x25, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75,
-	0x6e, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70,
-	0x73, 0x22, 0x64, 0x0a, 0x10, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
-	0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x63, 0x6f, 0x75,
-	0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f,
-	0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-	0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x08, 0x63,
-	0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0x37, 0x0a, 0x0b, 0x50, 0x65, 0x72, 0x66, 0x43,
-	0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
-	0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
-	0x22, 0xb9, 0x03, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f,
-	0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10,
-	0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65,
-	0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
-	0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01,
-	0x28, 0x04, 0x52, 0x10, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69,
-	0x63, 0x72, 0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f,
-	0x6b, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73,
-	0x4b, 0x62, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65,
-	0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d,
-	0x69, 0x6e, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a,
-	0x0a, 0x11, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75,
-	0x6c, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72,
-	0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f,
-	0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52,
-	0x09, 0x69, 0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f,
-	0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04,
-	0x52, 0x0a, 0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a,
-	0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78,
-	0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04,
-	0x52, 0x18, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65,
-	0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e,
-	0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78,
-	0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04,
-	0x52, 0x1a, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e,
-	0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a,
-	0x0e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12,
-	0x5b, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
-	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75,
-	0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64,
-	0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52,
-	0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b,
-	0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a,
-	0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18,
-	0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64, 0x75,
-	0x6c, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74,
-	0x65, 0x6d, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12,
-	0x09, 0x0a, 0x05, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41,
-	0x4b, 0x45, 0x10, 0x02, 0x22, 0x6c, 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
-	0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x73, 0x22, 0x62, 0x0a, 0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73,
-	0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
-	0x73, 0x12, 0x43, 0x0a, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
-	0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73,
-	0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-	0x52, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x22, 0x94, 0x03, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67,
-	0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07,
-	0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d,
-	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e,
-	0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e,
-	0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f,
-	0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74,
-	0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28,
-	0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73, 0x69,
-	0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41,
-	0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f,
-	0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52,
-	0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x06,
-	0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73,
+	0x62, 0x6c, 0x65, 0x12, 0x62, 0x0a, 0x17, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64,
+	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x23,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d,
+	0x69, 0x7a, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x52, 0x15, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64,
+	0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64,
+	0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10,
+	0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x53, 0x45, 0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01,
+	0x12, 0x07, 0x0a, 0x03, 0x45, 0x4e, 0x47, 0x10, 0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63,
+	0x68, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07,
+	0x0a, 0x03, 0x41, 0x52, 0x4d, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34,
+	0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x58, 0x38, 0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58,
+	0x38, 0x36, 0x5f, 0x36, 0x34, 0x10, 0x04, 0x22, 0x8a, 0x04, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c,
+	0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67,
+	0x6f, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f,
+	0x6d, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x73, 0x65, 0x52, 0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66,
+	0x6f, 0x72, 0x63, 0x65, 0x5f, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d,
+	0x61, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69,
+	0x6e, 0x6a, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c,
+	0x41, 0x73, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c,
+	0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01,
+	0x28, 0x08, 0x52, 0x0f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75,
+	0x69, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x44, 0x0a,
+	0x1f, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62,
+	0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
+	0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x44, 0x69, 0x73,
+	0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75,
+	0x69, 0x6c, 0x64, 0x12, 0x79, 0x0a, 0x18, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x77, 0x65, 0x69,
+	0x67, 0x68, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18,
+	0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c,
+	0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69,
+	0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x08, 0x4e,
+	0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x44, 0x52, 0x15, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65,
+	0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x74,
+	0x0a, 0x15, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73,
+	0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55,
+	0x53, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x49, 0x4e, 0x4a, 0x41, 0x5f, 0x4c,
+	0x4f, 0x47, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x56, 0x45, 0x4e, 0x4c, 0x59, 0x5f, 0x44,
+	0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d,
+	0x45, 0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x12,
+	0x13, 0x0a, 0x0f, 0x48, 0x49, 0x4e, 0x54, 0x5f, 0x46, 0x52, 0x4f, 0x4d, 0x5f, 0x53, 0x4f, 0x4f,
+	0x4e, 0x47, 0x10, 0x04, 0x22, 0xed, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52,
+	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74,
+	0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65,
+	0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61,
+	0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12,
+	0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75,
+	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
+	0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x12, 0x3d, 0x0a, 0x08, 0x63, 0x70, 0x75, 0x5f, 0x69, 0x6e,
+	0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53,
+	0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x70, 0x75, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x63, 0x70,
+	0x75, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x08, 0x6d, 0x65, 0x6d, 0x5f, 0x69, 0x6e, 0x66,
+	0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53, 0x79,
+	0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6d, 0x65, 0x6d,
+	0x49, 0x6e, 0x66, 0x6f, 0x22, 0x7e, 0x0a, 0x0d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x70,
+	0x75, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f,
+	0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72,
+	0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, 0x6d,
+	0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x70, 0x75, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x14,
+	0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66,
+	0x6c, 0x61, 0x67, 0x73, 0x22, 0x6c, 0x0a, 0x0d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65,
+	0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x74, 0x6f, 0x74,
+	0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x54, 0x6f, 0x74,
+	0x61, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x65, 0x6d, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x46, 0x72, 0x65, 0x65, 0x12, 0x23, 0x0a,
+	0x0d, 0x6d, 0x65, 0x6d, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x65, 0x6d, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
+	0x6c, 0x65, 0x22, 0xca, 0x02, 0x0a, 0x08, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12,
+	0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74,
+	0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74,
+	0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d,
+	0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d,
+	0x65, 0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x18,
+	0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72,
+	0x79, 0x55, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65,
+	0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18,
+	0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63,
+	0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52,
+	0x15, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x6e, 0x5f, 0x7a, 0x65,
+	0x72, 0x6f, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e,
+	0x6f, 0x6e, 0x5a, 0x65, 0x72, 0x6f, 0x45, 0x78, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72,
+	0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
+	0x61, 0x0a, 0x0c, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12,
+	0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74,
+	0x69, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c,
+	0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f,
+	0x75, 0x6e, 0x74, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75,
+	0x70, 0x73, 0x22, 0x64, 0x0a, 0x10, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65,
+	0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x63, 0x6f,
+	0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73,
 	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65,
-	0x6e, 0x74, 0x73, 0x12, 0x50, 0x0a, 0x11, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24,
-	0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73,
-	0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64,
-	0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x46, 0x0a, 0x0d, 0x70, 0x65, 0x72, 0x66, 0x5f, 0x63, 0x6f,
-	0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x73,
-	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52,
-	0x0c, 0x70, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0xdb, 0x01,
-	0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68,
-	0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a,
-	0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x69,
-	0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6d, 0x69, 0x63, 0x72,
-	0x6f, 0x73, 0x22, 0x47, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74,
-	0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10,
-	0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a,
-	0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x49, 0x53, 0x53,
-	0x49, 0x4e, 0x47, 0x5f, 0x47, 0x43, 0x45, 0x52, 0x54, 0x10, 0x03, 0x22, 0x91, 0x01, 0x0a, 0x0f,
-	0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12,
-	0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x65,
-	0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01,
-	0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64,
-	0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x3f,
-	0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x69,
-	0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02,
-	0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64,
-	0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22,
-	0x8a, 0x02, 0x0a, 0x10, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68,
-	0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x5f,
-	0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69,
-	0x63, 0x72, 0x6f, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
-	0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f,
-	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61,
-	0x6c, 0x50, 0x61, 0x74, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12,
-	0x41, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68,
-	0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62,
-	0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61,
-	0x74, 0x68, 0x12, 0x48, 0x0a, 0x11, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x69,
-	0x6e, 0x67, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
+	0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x08,
+	0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0x37, 0x0a, 0x0b, 0x50, 0x65, 0x72, 0x66,
+	0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
+	0x65, 0x22, 0xb9, 0x03, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73,
+	0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a,
+	0x10, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f,
+	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d,
+	0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65,
+	0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x10, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d,
+	0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73,
+	0x5f, 0x6b, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73,
+	0x73, 0x4b, 0x62, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67,
+	0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f,
+	0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12,
+	0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61,
+	0x75, 0x6c, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f,
+	0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69,
+	0x6f, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x09, 0x69, 0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69,
+	0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x0a, 0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a,
+	0x1a, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x18, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74,
+	0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69,
+	0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x1a, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f,
+	0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01,
+	0x0a, 0x0e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f,
+	0x12, 0x5b, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64,
+	0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c,
+	0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,
+	0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a,
+	0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24,
+	0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64,
+	0x75, 0x6c, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73,
+	0x74, 0x65, 0x6d, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00,
+	0x12, 0x09, 0x0a, 0x05, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d,
+	0x41, 0x4b, 0x45, 0x10, 0x02, 0x22, 0x6c, 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61,
+	0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x22, 0x62, 0x0a, 0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55,
+	0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55,
+	0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x52, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x22, 0x94, 0x03, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e,
+	0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a,
+	0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
+	0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61,
+	0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61,
+	0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c,
+	0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f,
+	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
+	0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73,
+	0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+	0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78,
+	0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a,
+	0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
 	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
-	0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6c, 0x6f, 0x6e,
-	0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x62, 0x0a, 0x07,
-	0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70, 0x73,
-	0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d,
-	0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6a, 0x6f, 0x62, 0x5f, 0x64,
-	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0e, 0x6a, 0x6f, 0x62, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-	0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e,
-	0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76,
+	0x65, 0x6e, 0x74, 0x73, 0x12, 0x50, 0x0a, 0x11, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x24, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64,
+	0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c,
+	0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x46, 0x0a, 0x0d, 0x70, 0x65, 0x72, 0x66, 0x5f, 0x63,
+	0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e,
+	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73,
+	0x52, 0x0c, 0x70, 0x65, 0x72, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x22, 0xdb,
+	0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63,
+	0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c,
+	0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
+	0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d,
+	0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6d, 0x69, 0x63,
+	0x72, 0x6f, 0x73, 0x22, 0x47, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47,
+	0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x01, 0x12, 0x09,
+	0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x49, 0x53,
+	0x53, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x43, 0x45, 0x52, 0x54, 0x10, 0x03, 0x22, 0x91, 0x01, 0x0a,
+	0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f,
+	0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+	0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c,
+	0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12,
+	0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64,
+	0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18,
+	0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c,
+	0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
+	0x22, 0x8a, 0x02, 0x0a, 0x10, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74,
+	0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64,
+	0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d,
+	0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61,
+	0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72,
+	0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63,
+	0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
+	0x12, 0x41, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74,
+	0x68, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f,
+	0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50,
+	0x61, 0x74, 0x68, 0x12, 0x48, 0x0a, 0x11, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6e, 0x6e,
+	0x69, 0x6e, 0x67, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c,
+	0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x6c, 0x6f,
+	0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x62, 0x0a,
+	0x07, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70,
+	0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69,
+	0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6a, 0x6f, 0x62, 0x5f,
+	0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x0e, 0x6a, 0x6f, 0x62, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x22, 0xb9, 0x05, 0x0a, 0x15, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x42,
+	0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x0d, 0x61,
+	0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
+	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66,
+	0x6f, 0x52, 0x0c, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x50, 0x65, 0x72, 0x66, 0x12,
+	0x44, 0x0a, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72,
+	0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65,
+	0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x69, 0x6e,
+	0x67, 0x50, 0x65, 0x72, 0x66, 0x12, 0x68, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
+	0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x73,
+	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c,
+	0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f,
+	0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c,
+	0x74, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a,
+	0xab, 0x03, 0x0a, 0x18, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69,
+	0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 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, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x35,
+	0x0a, 0x16, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15,
+	0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x61, 0x6c, 0x65, 0x12, 0x44, 0x0a, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x69,
+	0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
+	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x70, 0x61,
+	0x63, 0x6b, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x72, 0x66, 0x12, 0x7b, 0x0a, 0x0f, 0x6f,
+	0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x18, 0x05,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x52, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d,
+	0x69, 0x7a, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74,
+	0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74,
+	0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x1a, 0x63, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x70,
+	0x75, 0x74, 0x41, 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, 0x12,
+	0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69,
+	0x7a, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x6d,
+	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6e,
+	0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x42, 0x28, 0x5a,
+	0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75,
+	0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
@@ -2198,64 +2656,77 @@
 }
 
 var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
-var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
+var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
 var file_metrics_proto_goTypes = []interface{}{
-	(MetricsBase_BuildVariant)(0),          // 0: soong_build_metrics.MetricsBase.BuildVariant
-	(MetricsBase_Arch)(0),                  // 1: soong_build_metrics.MetricsBase.Arch
-	(BuildConfig_NinjaWeightListSource)(0), // 2: soong_build_metrics.BuildConfig.NinjaWeightListSource
-	(ModuleTypeInfo_BuildSystem)(0),        // 3: soong_build_metrics.ModuleTypeInfo.BuildSystem
-	(ExpConfigFetcher_ConfigStatus)(0),     // 4: soong_build_metrics.ExpConfigFetcher.ConfigStatus
-	(*MetricsBase)(nil),                    // 5: soong_build_metrics.MetricsBase
-	(*BuildConfig)(nil),                    // 6: soong_build_metrics.BuildConfig
-	(*SystemResourceInfo)(nil),             // 7: soong_build_metrics.SystemResourceInfo
-	(*PerfInfo)(nil),                       // 8: soong_build_metrics.PerfInfo
-	(*PerfCounters)(nil),                   // 9: soong_build_metrics.PerfCounters
-	(*PerfCounterGroup)(nil),               // 10: soong_build_metrics.PerfCounterGroup
-	(*PerfCounter)(nil),                    // 11: soong_build_metrics.PerfCounter
-	(*ProcessResourceInfo)(nil),            // 12: soong_build_metrics.ProcessResourceInfo
-	(*ModuleTypeInfo)(nil),                 // 13: soong_build_metrics.ModuleTypeInfo
-	(*CriticalUserJourneyMetrics)(nil),     // 14: soong_build_metrics.CriticalUserJourneyMetrics
-	(*CriticalUserJourneysMetrics)(nil),    // 15: soong_build_metrics.CriticalUserJourneysMetrics
-	(*SoongBuildMetrics)(nil),              // 16: soong_build_metrics.SoongBuildMetrics
-	(*ExpConfigFetcher)(nil),               // 17: soong_build_metrics.ExpConfigFetcher
-	(*MixedBuildsInfo)(nil),                // 18: soong_build_metrics.MixedBuildsInfo
-	(*CriticalPathInfo)(nil),               // 19: soong_build_metrics.CriticalPathInfo
-	(*JobInfo)(nil),                        // 20: soong_build_metrics.JobInfo
+	(MetricsBase_BuildVariant)(0),                          // 0: soong_build_metrics.MetricsBase.BuildVariant
+	(MetricsBase_Arch)(0),                                  // 1: soong_build_metrics.MetricsBase.Arch
+	(BuildConfig_NinjaWeightListSource)(0),                 // 2: soong_build_metrics.BuildConfig.NinjaWeightListSource
+	(ModuleTypeInfo_BuildSystem)(0),                        // 3: soong_build_metrics.ModuleTypeInfo.BuildSystem
+	(ExpConfigFetcher_ConfigStatus)(0),                     // 4: soong_build_metrics.ExpConfigFetcher.ConfigStatus
+	(*MetricsBase)(nil),                                    // 5: soong_build_metrics.MetricsBase
+	(*BuildConfig)(nil),                                    // 6: soong_build_metrics.BuildConfig
+	(*SystemResourceInfo)(nil),                             // 7: soong_build_metrics.SystemResourceInfo
+	(*SystemCpuInfo)(nil),                                  // 8: soong_build_metrics.SystemCpuInfo
+	(*SystemMemInfo)(nil),                                  // 9: soong_build_metrics.SystemMemInfo
+	(*PerfInfo)(nil),                                       // 10: soong_build_metrics.PerfInfo
+	(*PerfCounters)(nil),                                   // 11: soong_build_metrics.PerfCounters
+	(*PerfCounterGroup)(nil),                               // 12: soong_build_metrics.PerfCounterGroup
+	(*PerfCounter)(nil),                                    // 13: soong_build_metrics.PerfCounter
+	(*ProcessResourceInfo)(nil),                            // 14: soong_build_metrics.ProcessResourceInfo
+	(*ModuleTypeInfo)(nil),                                 // 15: soong_build_metrics.ModuleTypeInfo
+	(*CriticalUserJourneyMetrics)(nil),                     // 16: soong_build_metrics.CriticalUserJourneyMetrics
+	(*CriticalUserJourneysMetrics)(nil),                    // 17: soong_build_metrics.CriticalUserJourneysMetrics
+	(*SoongBuildMetrics)(nil),                              // 18: soong_build_metrics.SoongBuildMetrics
+	(*ExpConfigFetcher)(nil),                               // 19: soong_build_metrics.ExpConfigFetcher
+	(*MixedBuildsInfo)(nil),                                // 20: soong_build_metrics.MixedBuildsInfo
+	(*CriticalPathInfo)(nil),                               // 21: soong_build_metrics.CriticalPathInfo
+	(*JobInfo)(nil),                                        // 22: soong_build_metrics.JobInfo
+	(*OptimizedBuildMetrics)(nil),                          // 23: soong_build_metrics.OptimizedBuildMetrics
+	(*OptimizedBuildMetrics_TargetOptimizationResult)(nil), // 24: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult
+	(*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact)(nil), // 25: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.OutputArtifact
 }
 var file_metrics_proto_depIdxs = []int32{
 	0,  // 0: soong_build_metrics.MetricsBase.target_build_variant:type_name -> soong_build_metrics.MetricsBase.BuildVariant
 	1,  // 1: soong_build_metrics.MetricsBase.target_arch:type_name -> soong_build_metrics.MetricsBase.Arch
 	1,  // 2: soong_build_metrics.MetricsBase.host_arch:type_name -> soong_build_metrics.MetricsBase.Arch
 	1,  // 3: soong_build_metrics.MetricsBase.host_2nd_arch:type_name -> soong_build_metrics.MetricsBase.Arch
-	8,  // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
-	8,  // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
-	8,  // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
-	8,  // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
-	8,  // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
-	16, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
+	10, // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
+	10, // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
+	10, // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
+	10, // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
+	10, // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
+	18, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
 	6,  // 10: soong_build_metrics.MetricsBase.build_config:type_name -> soong_build_metrics.BuildConfig
 	7,  // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
-	8,  // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
-	17, // 13: soong_build_metrics.MetricsBase.exp_config_fetcher:type_name -> soong_build_metrics.ExpConfigFetcher
-	19, // 14: soong_build_metrics.MetricsBase.critical_path_info:type_name -> soong_build_metrics.CriticalPathInfo
-	2,  // 15: soong_build_metrics.BuildConfig.ninja_weight_list_source:type_name -> soong_build_metrics.BuildConfig.NinjaWeightListSource
-	12, // 16: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
-	10, // 17: soong_build_metrics.PerfCounters.groups:type_name -> soong_build_metrics.PerfCounterGroup
-	11, // 18: soong_build_metrics.PerfCounterGroup.counters:type_name -> soong_build_metrics.PerfCounter
-	3,  // 19: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
-	5,  // 20: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
-	14, // 21: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
-	8,  // 22: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
-	18, // 23: soong_build_metrics.SoongBuildMetrics.mixed_builds_info:type_name -> soong_build_metrics.MixedBuildsInfo
-	9,  // 24: soong_build_metrics.SoongBuildMetrics.perf_counters:type_name -> soong_build_metrics.PerfCounters
-	4,  // 25: soong_build_metrics.ExpConfigFetcher.status:type_name -> soong_build_metrics.ExpConfigFetcher.ConfigStatus
-	20, // 26: soong_build_metrics.CriticalPathInfo.critical_path:type_name -> soong_build_metrics.JobInfo
-	20, // 27: soong_build_metrics.CriticalPathInfo.long_running_jobs:type_name -> soong_build_metrics.JobInfo
-	28, // [28:28] is the sub-list for method output_type
-	28, // [28:28] is the sub-list for method input_type
-	28, // [28:28] is the sub-list for extension type_name
-	28, // [28:28] is the sub-list for extension extendee
-	0,  // [0:28] is the sub-list for field type_name
+	10, // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
+	19, // 13: soong_build_metrics.MetricsBase.exp_config_fetcher:type_name -> soong_build_metrics.ExpConfigFetcher
+	21, // 14: soong_build_metrics.MetricsBase.critical_path_info:type_name -> soong_build_metrics.CriticalPathInfo
+	23, // 15: soong_build_metrics.MetricsBase.optimized_build_metrics:type_name -> soong_build_metrics.OptimizedBuildMetrics
+	2,  // 16: soong_build_metrics.BuildConfig.ninja_weight_list_source:type_name -> soong_build_metrics.BuildConfig.NinjaWeightListSource
+	8,  // 17: soong_build_metrics.SystemResourceInfo.cpu_info:type_name -> soong_build_metrics.SystemCpuInfo
+	9,  // 18: soong_build_metrics.SystemResourceInfo.mem_info:type_name -> soong_build_metrics.SystemMemInfo
+	14, // 19: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
+	12, // 20: soong_build_metrics.PerfCounters.groups:type_name -> soong_build_metrics.PerfCounterGroup
+	13, // 21: soong_build_metrics.PerfCounterGroup.counters:type_name -> soong_build_metrics.PerfCounter
+	3,  // 22: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
+	5,  // 23: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
+	16, // 24: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
+	10, // 25: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
+	20, // 26: soong_build_metrics.SoongBuildMetrics.mixed_builds_info:type_name -> soong_build_metrics.MixedBuildsInfo
+	11, // 27: soong_build_metrics.SoongBuildMetrics.perf_counters:type_name -> soong_build_metrics.PerfCounters
+	4,  // 28: soong_build_metrics.ExpConfigFetcher.status:type_name -> soong_build_metrics.ExpConfigFetcher.ConfigStatus
+	22, // 29: soong_build_metrics.CriticalPathInfo.critical_path:type_name -> soong_build_metrics.JobInfo
+	22, // 30: soong_build_metrics.CriticalPathInfo.long_running_jobs:type_name -> soong_build_metrics.JobInfo
+	10, // 31: soong_build_metrics.OptimizedBuildMetrics.analysis_perf:type_name -> soong_build_metrics.PerfInfo
+	10, // 32: soong_build_metrics.OptimizedBuildMetrics.packaging_perf:type_name -> soong_build_metrics.PerfInfo
+	24, // 33: soong_build_metrics.OptimizedBuildMetrics.target_result:type_name -> soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult
+	10, // 34: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.packaging_perf:type_name -> soong_build_metrics.PerfInfo
+	25, // 35: soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.output_artifact:type_name -> soong_build_metrics.OptimizedBuildMetrics.TargetOptimizationResult.OutputArtifact
+	36, // [36:36] is the sub-list for method output_type
+	36, // [36:36] is the sub-list for method input_type
+	36, // [36:36] is the sub-list for extension type_name
+	36, // [36:36] is the sub-list for extension extendee
+	0,  // [0:36] is the sub-list for field type_name
 }
 
 func init() { file_metrics_proto_init() }
@@ -2301,7 +2772,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PerfInfo); i {
+			switch v := v.(*SystemCpuInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2313,7 +2784,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PerfCounters); i {
+			switch v := v.(*SystemMemInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2325,7 +2796,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PerfCounterGroup); i {
+			switch v := v.(*PerfInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2337,7 +2808,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PerfCounter); i {
+			switch v := v.(*PerfCounters); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2349,7 +2820,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ProcessResourceInfo); i {
+			switch v := v.(*PerfCounterGroup); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2361,7 +2832,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ModuleTypeInfo); i {
+			switch v := v.(*PerfCounter); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2373,7 +2844,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CriticalUserJourneyMetrics); i {
+			switch v := v.(*ProcessResourceInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2385,7 +2856,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CriticalUserJourneysMetrics); i {
+			switch v := v.(*ModuleTypeInfo); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2397,7 +2868,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SoongBuildMetrics); i {
+			switch v := v.(*CriticalUserJourneyMetrics); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2409,7 +2880,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ExpConfigFetcher); i {
+			switch v := v.(*CriticalUserJourneysMetrics); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2421,7 +2892,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*MixedBuildsInfo); i {
+			switch v := v.(*SoongBuildMetrics); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2433,7 +2904,7 @@
 			}
 		}
 		file_metrics_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CriticalPathInfo); i {
+			switch v := v.(*ExpConfigFetcher); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -2445,6 +2916,30 @@
 			}
 		}
 		file_metrics_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MixedBuildsInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CriticalPathInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*JobInfo); i {
 			case 0:
 				return &v.state
@@ -2456,6 +2951,42 @@
 				return nil
 			}
 		}
+		file_metrics_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OptimizedBuildMetrics); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OptimizedBuildMetrics_TargetOptimizationResult); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OptimizedBuildMetrics_TargetOptimizationResult_OutputArtifact); 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{
@@ -2463,7 +2994,7 @@
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_metrics_proto_rawDesc,
 			NumEnums:      5,
-			NumMessages:   16,
+			NumMessages:   21,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 11fcba7..3fbe97c 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -137,6 +137,9 @@
   // Note that not all changed environment variables result in analysis retriggering.
   // If there was no previous build, this list will be empty.
   repeated string changed_environment_variable = 34;
+
+  // Metrics related to optimized builds.
+  optional OptimizedBuildMetrics optimized_build_metrics = 35;
 }
 
 message BuildConfig {
@@ -182,6 +185,37 @@
 
   // The total of available cores for building
   optional int32 available_cpus = 2;
+
+  // Information about the machine's CPU(s).
+  optional SystemCpuInfo cpu_info = 3;
+
+  // Information about the machine's memory.
+  optional SystemMemInfo mem_info = 4;
+}
+
+message SystemCpuInfo {
+  // The vendor id
+  optional string vendor_id = 1;
+
+  // The model name
+  optional string model_name = 2;
+
+  // The number of CPU cores
+  optional int32 cpu_cores = 3;
+
+  // The CPU flags
+  optional string flags = 4;
+}
+
+message SystemMemInfo {
+  // The total system memory
+  optional uint64 mem_total = 1;
+
+  // The free system memory
+  optional uint64 mem_free = 2;
+
+  // The available system memory
+  optional uint64 mem_available = 3;
 }
 
 message PerfInfo {
@@ -385,3 +419,35 @@
   // Description of a job
   optional string job_description = 2;
 }
+
+message OptimizedBuildMetrics {
+  // The total time spent analyzing what/how to optimize everything.
+  optional PerfInfo analysis_perf = 1;
+  // The total time spent packaging artifacts.
+  optional PerfInfo packaging_perf = 2;
+  // Information for a single target (e.g. general-tests).
+  repeated TargetOptimizationResult target_result = 3;
+
+  message TargetOptimizationResult {
+    // Target name (e.g. general-tests).
+    optional string name = 1;
+    // Whether or not this target was optimized.
+    optional bool optimized = 2;
+    // Reasoning for why the target wasn't optimized if it wasn't
+    optional string optimization_rationale = 3;
+    // Time spent packaging this specific target (if it was optimized).
+    optional PerfInfo packaging_perf = 4;
+    // Information for each different artifact produced by this target (if it
+    // was optimized).
+    repeated OutputArtifact output_artifact = 5;
+
+    message OutputArtifact {
+      // Artifact file name (e.g. general-tests.zip)
+      optional string name = 1;
+      // Size of the file.
+      optional int64 size = 2;
+      // Lists of modules packaged into this artifact.
+      repeated string included_modules = 3;
+    }
+  }
+}
diff --git a/ui/metrics/metrics_proto/regen.sh b/ui/metrics/metrics_proto/regen.sh
index 8eb2d74..5e5f9b8 100755
--- a/ui/metrics/metrics_proto/regen.sh
+++ b/ui/metrics/metrics_proto/regen.sh
@@ -12,6 +12,6 @@
   die "could not find aprotoc. ${error_msg}"
 fi
 
-if ! aprotoc --go_out=paths=source_relative:. metrics.proto; then
+if ! aprotoc --go_out=paths=source_relative:. -I .:../../.. metrics.proto combined_metrics.proto; then
   die "build failed. ${error_msg}"
 fi
diff --git a/ui/status/log.go b/ui/status/log.go
index 14df346..7bfd396 100644
--- a/ui/status/log.go
+++ b/ui/status/log.go
@@ -22,6 +22,8 @@
 	"io/ioutil"
 	"os"
 	"strings"
+	"sync"
+	"time"
 
 	"google.golang.org/protobuf/proto"
 
@@ -31,7 +33,10 @@
 )
 
 type verboseLog struct {
-	w io.WriteCloser
+	w    *gzip.Writer
+	lock *sync.Mutex
+	data chan []string
+	stop chan bool
 }
 
 func NewVerboseLog(log logger.Logger, filename string) StatusOutput {
@@ -47,9 +52,42 @@
 
 	w := gzip.NewWriter(f)
 
-	return &verboseLog{
-		w: w,
+	l := &verboseLog{
+		w:    w,
+		lock: &sync.Mutex{},
+		data: make(chan []string),
+		stop: make(chan bool),
 	}
+	l.startWriter()
+	return l
+}
+
+func (v *verboseLog) startWriter() {
+	go func() {
+		tick := time.Tick(time.Second)
+		for {
+			select {
+			case <-v.stop:
+				close(v.data)
+				v.w.Close()
+				return
+			case <-tick:
+				v.w.Flush()
+			case dataList := <-v.data:
+				for _, data := range dataList {
+					fmt.Fprint(v.w, data)
+				}
+			}
+		}
+	}()
+}
+
+func (v *verboseLog) stopWriter() {
+	v.stop <- true
+}
+
+func (v *verboseLog) queueWrite(s ...string) {
+	v.data <- s
 }
 
 func (v *verboseLog) StartAction(action *Action, counts Counts) {}
@@ -60,27 +98,27 @@
 		cmd = result.Description
 	}
 
-	fmt.Fprintf(v.w, "[%d/%d] %s\n", counts.FinishedActions, counts.TotalActions, cmd)
+	v.queueWrite(fmt.Sprintf("[%d/%d] ", counts.FinishedActions, counts.TotalActions), cmd, "\n")
 
 	if result.Error != nil {
-		fmt.Fprintf(v.w, "FAILED: %s\n", strings.Join(result.Outputs, " "))
+		v.queueWrite("FAILED: ", strings.Join(result.Outputs, " "), "\n")
 	}
 
 	if result.Output != "" {
-		fmt.Fprintln(v.w, result.Output)
+		v.queueWrite(result.Output, "\n")
 	}
 }
 
 func (v *verboseLog) Flush() {
-	v.w.Close()
+	v.stopWriter()
 }
 
 func (v *verboseLog) Message(level MsgLevel, message string) {
-	fmt.Fprintf(v.w, "%s%s\n", level.Prefix(), message)
+	v.queueWrite(level.Prefix(), message, "\n")
 }
 
 func (v *verboseLog) Write(p []byte) (int, error) {
-	fmt.Fprint(v.w, string(p))
+	v.queueWrite(string(p))
 	return len(p), nil
 }