Merge "Revert "sandbox_linux: set CCACHE_DIR as a writable path"" into android-15
diff --git a/Android.bp b/Android.bp
index 2c0ef47..535246e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,5 +1,8 @@
 package {
     default_applicable_licenses: ["Android-Apache-2.0"],
+    default_visibility: [
+        "//build/soong:__subpackages__",
+    ],
 }
 
 subdirs = [
@@ -23,6 +26,8 @@
     srcs: [
         "doc.go",
     ],
+    // Used by plugins, though probably shouldn't be.
+    visibility: ["//visibility:public"],
 }
 
 //
@@ -40,6 +45,7 @@
             enabled: true,
         },
     },
+    defaults_visibility: ["//visibility:public"],
 }
 
 //
@@ -51,6 +57,7 @@
     vendor: true,
     recovery_available: true,
     min_sdk_version: "apex_inherit",
+    visibility: ["//visibility:public"],
 }
 
 cc_genrule {
@@ -75,6 +82,7 @@
     cmd: "$(location) -s $(out) $(in)",
     srcs: [":linker"],
     out: ["linker.s"],
+    visibility: ["//bionic/libc"],
 }
 
 cc_genrule {
@@ -99,12 +107,13 @@
     cmd: "$(location) -T $(out) $(in)",
     srcs: [":linker"],
     out: ["linker.script"],
+    visibility: ["//visibility:public"],
 }
 
 // Instantiate the dex_bootjars singleton module.
 dex_bootjars {
     name: "dex_bootjars",
-    no_full_install: true,
+    visibility: ["//visibility:public"],
 }
 
 // Pseudo-test that's run on checkbuilds to ensure that get_clang_version can
@@ -121,18 +130,51 @@
     name: "dexpreopt_systemserver_check",
 }
 
-// buildinfo.prop contains common properties for system/build.prop, like ro.build.version.*
-buildinfo_prop {
-    name: "buildinfo.prop",
-
-    // not installable because this will be included to system/build.prop
-    installable: false,
-
-    // Currently, only microdroid can refer to buildinfo.prop
-    visibility: ["//packages/modules/Virtualization/microdroid"],
-}
-
 // container for apex_contributions selected using build flags
 all_apex_contributions {
     name: "all_apex_contributions",
+    visibility: ["//visibility:public"],
+}
+
+product_config {
+    name: "product_config",
+    visibility: ["//device/google/cuttlefish/system_image"],
+}
+
+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
+    visibility: [
+        "//device/google/cuttlefish/system_image",
+        "//packages/modules/Virtualization/build/microdroid",
+    ],
+}
+
+build_prop {
+    name: "system_ext-build.prop",
+    stem: "build.prop",
+    system_ext_specific: true,
+    product_config: ":product_config",
+    relative_install_path: "etc", // system_ext/etc/build.prop
+    visibility: ["//visibility:private"],
+}
+
+build_prop {
+    name: "product-build.prop",
+    stem: "build.prop",
+    product_specific: true,
+    product_config: ":product_config",
+    relative_install_path: "etc", // product/etc/build.prop
+    visibility: ["//visibility:private"],
+}
+
+build_prop {
+    name: "odm-build.prop",
+    stem: "build.prop",
+    device_specific: true,
+    product_config: ":product_config",
+    relative_install_path: "etc", // odm/etc/build.prop
+    visibility: ["//visibility:private"],
 }
diff --git a/README.md b/README.md
index 140822b..ad282a5 100644
--- a/README.md
+++ b/README.md
@@ -594,19 +594,13 @@
 by all of the vendor's other modules using the normal namespace and visibility
 rules.
 
-`soongConfigTraceMutator` enables modules affected by soong config variables to
-write outputs into a hashed directory path. It does this by recording accesses
-to soong config variables on each module, and then accumulating records of each
-module's all dependencies. `m soong_config_trace` builds information about
-hashes to `$OUT_DIR/soong/soong_config_trace.json`.
-
 ## Build logic
 
 The build logic is written in Go using the
-[blueprint](http://godoc.org/github.com/google/blueprint) framework.  Build
-logic receives module definitions parsed into Go structures using reflection
-and produces build rules.  The build rules are collected by blueprint and
-written to a [ninja](http://ninja-build.org) build file.
+[blueprint](https://android.googlesource.com/platform/build/blueprint)
+framework.  Build logic receives module definitions parsed into Go structures
+using reflection and produces build rules.  The build rules are collected by
+blueprint and written to a [ninja](http://ninja-build.org) build file.
 
 ## Environment Variables Config File
 
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index dac0ae3..d9a862c 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -15,6 +15,8 @@
 package aconfig
 
 import (
+	"path/filepath"
+	"slices"
 	"strings"
 
 	"android/soong/android"
@@ -22,9 +24,15 @@
 	"github.com/google/blueprint"
 )
 
+type AconfigReleaseConfigValue struct {
+	ReleaseConfig string
+	Values        []string `blueprint:"mutated"`
+}
+
 type DeclarationsModule struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	blueprint.IncrementalModule
 
 	// Properties for "aconfig_declarations"
 	properties struct {
@@ -34,8 +42,10 @@
 		// Release config flag package
 		Package string
 
-		// Values from TARGET_RELEASE / RELEASE_ACONFIG_VALUE_SETS
-		Values []string `blueprint:"mutated"`
+		// Values for release configs / RELEASE_ACONFIG_VALUE_SETS
+		// The current release config is `ReleaseConfig: ""`, others
+		// are from RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS.
+		ReleaseConfigValues []AconfigReleaseConfigValue
 
 		// Container(system/vendor/apex) that this module belongs to
 		Container string
@@ -57,6 +67,10 @@
 
 type implicitValuesTagType struct {
 	blueprint.BaseDependencyTag
+
+	// The release config name for these values.
+	// Empty string for the actual current release config.
+	ReleaseConfig string
 }
 
 var implicitValuesTag = implicitValuesTagType{}
@@ -74,6 +88,13 @@
 		ctx.PropertyErrorf("container", "missing container property")
 	}
 
+	// treating system_ext as system partition as we are combining them as one container
+	// TODO remove this logic once we start enforcing that system_ext cannot be specified as
+	// container in the container field.
+	if module.properties.Container == "system_ext" {
+		module.properties.Container = "system"
+	}
+
 	// Add a dependency on the aconfig_value_sets defined in
 	// RELEASE_ACONFIG_VALUE_SETS, and add any aconfig_values that
 	// match our package.
@@ -81,6 +102,11 @@
 	if len(valuesFromConfig) > 0 {
 		ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig...)
 	}
+	for rcName, valueSets := range ctx.Config().ReleaseAconfigExtraReleaseConfigsValueSets() {
+		if len(valueSets) > 0 {
+			ctx.AddDependency(ctx.Module(), implicitValuesTagType{ReleaseConfig: rcName}, valueSets...)
+		}
+	}
 }
 
 func joinAndPrefix(prefix string, values []string) string {
@@ -101,59 +127,103 @@
 	return sb.String()
 }
 
+// Assemble the actual filename.
+// If `rcName` is not empty, then insert "-{rcName}" into the path before the
+// file extension.
+func assembleFileName(rcName, path string) string {
+	if rcName == "" {
+		return path
+	}
+	dir, file := filepath.Split(path)
+	rcName = "-" + rcName
+	ext := filepath.Ext(file)
+	base := file[:len(file)-len(ext)]
+	return dir + base + rcName + ext
+}
+
 func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag
-	valuesFiles := make([]android.Path, 0)
+	// Determine which release configs we are processing.
+	//
+	// We always process the current release config (empty string).
+	// We may have been told to also create artifacts for some others.
+	configs := append([]string{""}, ctx.Config().ReleaseAconfigExtraReleaseConfigs()...)
+	slices.Sort(configs)
+
+	values := make(map[string][]string)
+	valuesFiles := make(map[string][]android.Path, 0)
+	providerData := android.AconfigReleaseDeclarationsProviderData{}
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		if depData, ok := android.OtherModuleProvider(ctx, dep, valueSetProviderKey); ok {
-			paths, ok := depData.AvailablePackages[module.properties.Package]
-			if ok {
-				valuesFiles = append(valuesFiles, paths...)
-				for _, path := range paths {
-					module.properties.Values = append(module.properties.Values, path.String())
+			depTag := ctx.OtherModuleDependencyTag(dep)
+			for _, config := range configs {
+				tag := implicitValuesTagType{ReleaseConfig: config}
+				if depTag == tag {
+					paths, ok := depData.AvailablePackages[module.properties.Package]
+					if ok {
+						valuesFiles[config] = append(valuesFiles[config], paths...)
+						for _, path := range paths {
+							values[config] = append(values[config], path.String())
+						}
+					}
 				}
 			}
 		}
 	})
+	for _, config := range configs {
+		module.properties.ReleaseConfigValues = append(module.properties.ReleaseConfigValues, AconfigReleaseConfigValue{
+			ReleaseConfig: config,
+			Values:        values[config],
+		})
 
-	// Intermediate format
-	declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs)
-	intermediateCacheFilePath := android.PathForModuleOut(ctx, "intermediate.pb")
-	defaultPermission := ctx.Config().ReleaseAconfigFlagDefaultPermission()
-	inputFiles := make([]android.Path, len(declarationFiles))
-	copy(inputFiles, declarationFiles)
-	inputFiles = append(inputFiles, valuesFiles...)
-	args := map[string]string{
-		"release_version":    ctx.Config().ReleaseVersion(),
-		"package":            module.properties.Package,
-		"declarations":       android.JoinPathsWithPrefix(declarationFiles, "--declarations "),
-		"values":             joinAndPrefix(" --values ", module.properties.Values),
-		"default-permission": optionalVariable(" --default-permission ", defaultPermission),
+		// Intermediate format
+		declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs)
+		intermediateCacheFilePath := android.PathForModuleOut(ctx, assembleFileName(config, "intermediate.pb"))
+		var defaultPermission string
+		defaultPermission = ctx.Config().ReleaseAconfigFlagDefaultPermission()
+		if config != "" {
+			if confPerm, ok := ctx.Config().GetBuildFlag("RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION_" + config); ok {
+				defaultPermission = confPerm
+			}
+		}
+		inputFiles := make([]android.Path, len(declarationFiles))
+		copy(inputFiles, declarationFiles)
+		inputFiles = append(inputFiles, valuesFiles[config]...)
+		args := map[string]string{
+			"release_version":    ctx.Config().ReleaseVersion(),
+			"package":            module.properties.Package,
+			"declarations":       android.JoinPathsWithPrefix(declarationFiles, "--declarations "),
+			"values":             joinAndPrefix(" --values ", values[config]),
+			"default-permission": optionalVariable(" --default-permission ", defaultPermission),
+		}
+		if len(module.properties.Container) > 0 {
+			args["container"] = "--container " + module.properties.Container
+		}
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        aconfigRule,
+			Output:      intermediateCacheFilePath,
+			Inputs:      inputFiles,
+			Description: "aconfig_declarations",
+			Args:        args,
+		})
+
+		intermediateDumpFilePath := android.PathForModuleOut(ctx, assembleFileName(config, "intermediate.txt"))
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        aconfigTextRule,
+			Output:      intermediateDumpFilePath,
+			Inputs:      android.Paths{intermediateCacheFilePath},
+			Description: "aconfig_text",
+		})
+
+		providerData[config] = android.AconfigDeclarationsProviderData{
+			Package:                     module.properties.Package,
+			Container:                   module.properties.Container,
+			Exportable:                  module.properties.Exportable,
+			IntermediateCacheOutputPath: intermediateCacheFilePath,
+			IntermediateDumpOutputPath:  intermediateDumpFilePath,
+		}
 	}
-	if len(module.properties.Container) > 0 {
-		args["container"] = "--container " + module.properties.Container
-	}
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        aconfigRule,
-		Output:      intermediateCacheFilePath,
-		Inputs:      inputFiles,
-		Description: "aconfig_declarations",
-		Args:        args,
-	})
-
-	intermediateDumpFilePath := android.PathForModuleOut(ctx, "intermediate.txt")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        aconfigTextRule,
-		Output:      intermediateDumpFilePath,
-		Inputs:      android.Paths{intermediateCacheFilePath},
-		Description: "aconfig_text",
-	})
-
-	android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, android.AconfigDeclarationsProviderData{
-		Package:                     module.properties.Package,
-		Container:                   module.properties.Container,
-		Exportable:                  module.properties.Exportable,
-		IntermediateCacheOutputPath: intermediateCacheFilePath,
-		IntermediateDumpOutputPath:  intermediateDumpFilePath,
-	})
+	android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, providerData[""])
+	android.SetProvider(ctx, android.AconfigReleaseDeclarationsProviderKey, providerData)
 }
+
+var _ blueprint.Incremental = &DeclarationsModule{}
diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go
index c37274c..e89cd31 100644
--- a/aconfig/aconfig_declarations_test.go
+++ b/aconfig/aconfig_declarations_test.go
@@ -15,6 +15,7 @@
 package aconfig
 
 import (
+	"slices"
 	"strings"
 	"testing"
 
@@ -39,7 +40,7 @@
 	module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
 
 	// Check that the provider has the right contents
-	depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
+	depData, _ := android.OtherModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
 	android.AssertStringEquals(t, "package", depData.Package, "com.example.package")
 	android.AssertStringEquals(t, "container", depData.Container, "com.android.foo")
 	android.AssertBoolEquals(t, "exportable", depData.Exportable, true)
@@ -66,7 +67,7 @@
 	result := runTest(t, android.FixtureExpectsNoErrors, bp)
 
 	module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
-	depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
+	depData, _ := android.OtherModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
 	android.AssertBoolEquals(t, "exportable", depData.Exportable, false)
 }
 
@@ -134,3 +135,95 @@
 		})
 	}
 }
+
+func TestAssembleFileName(t *testing.T) {
+	testCases := []struct {
+		name          string
+		releaseConfig string
+		path          string
+		expectedValue string
+	}{
+		{
+			name:          "active release config",
+			path:          "file.path",
+			expectedValue: "file.path",
+		},
+		{
+			name:          "release config FOO",
+			releaseConfig: "FOO",
+			path:          "file.path",
+			expectedValue: "file-FOO.path",
+		},
+	}
+	for _, test := range testCases {
+		actualValue := assembleFileName(test.releaseConfig, test.path)
+		if actualValue != test.expectedValue {
+			t.Errorf("Expected %q found %q", test.expectedValue, actualValue)
+		}
+	}
+}
+
+func TestGenerateAndroidBuildActions(t *testing.T) {
+	testCases := []struct {
+		name         string
+		buildFlags   map[string]string
+		bp           string
+		errorHandler android.FixtureErrorHandler
+	}{
+		{
+			name: "generate extra",
+			buildFlags: map[string]string{
+				"RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS": "config2",
+				"RELEASE_ACONFIG_VALUE_SETS":            "aconfig_value_set-config1",
+				"RELEASE_ACONFIG_VALUE_SETS_config2":    "aconfig_value_set-config2",
+			},
+			bp: `
+				aconfig_declarations {
+					name: "module_name",
+					package: "com.example.package",
+					container: "com.android.foo",
+					srcs: [
+						"foo.aconfig",
+						"bar.aconfig",
+					],
+				}
+				aconfig_value_set {
+					name: "aconfig_value_set-config1",
+					values: []
+				}
+				aconfig_value_set {
+					name: "aconfig_value_set-config2",
+					values: []
+				}
+			`,
+		},
+	}
+	for _, test := range testCases {
+		fixture := PrepareForTest(t, addBuildFlagsForTest(test.buildFlags))
+		if test.errorHandler != nil {
+			fixture = fixture.ExtendWithErrorHandler(test.errorHandler)
+		}
+		result := fixture.RunTestWithBp(t, test.bp)
+		module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
+		depData, _ := android.OtherModuleProvider(result, module, android.AconfigReleaseDeclarationsProviderKey)
+		expectedKeys := []string{""}
+		for _, rc := range strings.Split(test.buildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"], " ") {
+			expectedKeys = append(expectedKeys, rc)
+		}
+		slices.Sort(expectedKeys)
+		actualKeys := []string{}
+		for rc := range depData {
+			actualKeys = append(actualKeys, rc)
+		}
+		slices.Sort(actualKeys)
+		android.AssertStringEquals(t, "provider keys", strings.Join(expectedKeys, " "), strings.Join(actualKeys, " "))
+		for _, rc := range actualKeys {
+			if !strings.HasSuffix(depData[rc].IntermediateCacheOutputPath.String(), assembleFileName(rc, "/intermediate.pb")) {
+				t.Errorf("Incorrect intermediates proto path in provider for release config %s: %s", rc, depData[rc].IntermediateCacheOutputPath.String())
+			}
+			if !strings.HasSuffix(depData[rc].IntermediateDumpOutputPath.String(), assembleFileName(rc, "/intermediate.txt")) {
+				t.Errorf("Incorrect intermediates text path in provider for release config %s: %s", rc, depData[rc].IntermediateDumpOutputPath.String())
+			}
+		}
+	}
+}
diff --git a/aconfig/aconfig_value_set.go b/aconfig/aconfig_value_set.go
index 7ba76c0..d72ec48 100644
--- a/aconfig/aconfig_value_set.go
+++ b/aconfig/aconfig_value_set.go
@@ -16,6 +16,9 @@
 
 import (
 	"android/soong/android"
+	"fmt"
+	"strings"
+
 	"github.com/google/blueprint"
 )
 
@@ -27,6 +30,9 @@
 	properties struct {
 		// aconfig_values modules
 		Values []string
+
+		// Paths to the Android.bp files where the aconfig_values modules are defined.
+		Srcs []string
 	}
 }
 
@@ -56,7 +62,35 @@
 
 var valueSetProviderKey = blueprint.NewProvider[valueSetProviderData]()
 
+func (module *ValueSetModule) FindAconfigValuesFromSrc(ctx android.BottomUpMutatorContext) map[string]android.Path {
+	moduleDir := ctx.ModuleDir()
+	srcs := android.PathsForModuleSrcExcludes(ctx, module.properties.Srcs, []string{ctx.BlueprintsFile()})
+
+	aconfigValuesPrefix := strings.Replace(module.Name(), "aconfig_value_set", "aconfig-values", 1)
+	moduleNamesSrcMap := make(map[string]android.Path)
+	for _, src := range srcs {
+		subDir := strings.TrimPrefix(src.String(), moduleDir+"/")
+		packageName, _, found := strings.Cut(subDir, "/")
+		if found {
+			moduleName := fmt.Sprintf("%s-%s-all", aconfigValuesPrefix, packageName)
+			moduleNamesSrcMap[moduleName] = src
+		}
+	}
+	return moduleNamesSrcMap
+}
+
 func (module *ValueSetModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+
+	// TODO: b/366285733 - Replace the file path based solution with more robust solution.
+	aconfigValuesMap := module.FindAconfigValuesFromSrc(ctx)
+	for _, moduleName := range android.SortedKeys(aconfigValuesMap) {
+		if ctx.OtherModuleExists(moduleName) {
+			ctx.AddDependency(ctx.Module(), valueSetTag, moduleName)
+		} else {
+			ctx.ModuleErrorf("module %q not found. Rename the aconfig_values module defined in %q to %q", moduleName, aconfigValuesMap[moduleName], moduleName)
+		}
+	}
+
 	deps := ctx.AddDependency(ctx.Module(), valueSetTag, module.properties.Values...)
 	for _, dep := range deps {
 		_, ok := dep.(*ValuesModule)
diff --git a/aconfig/aconfig_value_set_test.go b/aconfig/aconfig_value_set_test.go
index 7d18999..3b7281e 100644
--- a/aconfig/aconfig_value_set_test.go
+++ b/aconfig/aconfig_value_set_test.go
@@ -18,6 +18,8 @@
 	"testing"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint"
 )
 
 func TestAconfigValueSet(t *testing.T) {
@@ -38,6 +40,115 @@
 	module := result.ModuleForTests("module_name", "").Module().(*ValueSetModule)
 
 	// Check that the provider has the right contents
-	depData, _ := android.SingletonModuleProvider(result, module, valueSetProviderKey)
+	depData, _ := android.OtherModuleProvider(result, module, valueSetProviderKey)
 	android.AssertStringEquals(t, "AvailablePackages", "blah.aconfig_values", depData.AvailablePackages["foo.package"][0].String())
 }
+
+func TestAconfigValueSetBpGlob(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		android.FixtureMergeMockFs(
+			map[string][]byte{
+				// .../some_release/android.foo/
+				"some_release/android.foo/Android.bp": []byte(`
+				aconfig_values {
+					name: "aconfig-values-platform_build_release-some_release-android.foo-all",
+					package: "android.foo",
+					srcs: [
+						"*.textproto",
+					],
+				}
+				`),
+				"some_release/android.foo/flag.textproto": nil,
+
+				// .../some_release/android.bar/
+				"some_release/android.bar/Android.bp": []byte(`
+				aconfig_values {
+					name: "aconfig-values-platform_build_release-some_release-android.bar-all",
+					package: "android.bar",
+					srcs: [
+						"*.textproto",
+					],
+				}
+				`),
+				"some_release/android.bar/flag.textproto": nil,
+
+				// .../some_release/
+				"some_release/Android.bp": []byte(`
+				aconfig_value_set {
+					name: "aconfig_value_set-platform_build_release-some_release",
+					srcs: [
+						"*/Android.bp",
+					],
+				}
+				`),
+			},
+		),
+	).RunTest(t)
+
+	checkModuleHasDependency := func(name, variant, dep string) bool {
+		t.Helper()
+		module := result.ModuleForTests(name, variant).Module()
+		depFound := false
+		result.VisitDirectDeps(module, func(m blueprint.Module) {
+			if m.Name() == dep {
+				depFound = true
+			}
+		})
+		return depFound
+	}
+	android.AssertBoolEquals(t,
+		"aconfig_value_set expected to depend on aconfig_value via srcs",
+		true,
+		checkModuleHasDependency(
+			"aconfig_value_set-platform_build_release-some_release",
+			"",
+			"aconfig-values-platform_build_release-some_release-android.foo-all",
+		),
+	)
+	android.AssertBoolEquals(t,
+		"aconfig_value_set expected to depend on aconfig_value via srcs",
+		true,
+		checkModuleHasDependency(
+			"aconfig_value_set-platform_build_release-some_release",
+			"",
+			"aconfig-values-platform_build_release-some_release-android.bar-all",
+		),
+	)
+}
+
+func TestAconfigValueSetBpGlobError(t *testing.T) {
+	android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		android.FixtureMergeMockFs(
+			map[string][]byte{
+				// .../some_release/android.bar/
+				"some_release/android.bar/Android.bp": []byte(`
+				aconfig_values {
+					name: "aconfig-values-platform_build_release-some_release-android_bar-all",
+					package: "android.bar",
+					srcs: [
+						"*.textproto",
+					],
+				}
+				`),
+				"some_release/android.bar/flag.textproto": nil,
+
+				// .../some_release/
+				"some_release/Android.bp": []byte(`
+				aconfig_value_set {
+					name: "aconfig_value_set-platform_build_release-some_release",
+					srcs: [
+						"*/Android.bp",
+					],
+				}
+				`),
+			},
+		),
+	).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		`module "aconfig_value_set-platform_build_release-some_release": module ` +
+			`"aconfig-values-platform_build_release-some_release-android.bar-all" not found. ` +
+			`Rename the aconfig_values module defined in "some_release/android.bar/Android.bp" ` +
+			`to "aconfig-values-platform_build_release-some_release-android.bar-all"`),
+	).RunTest(t)
+}
diff --git a/aconfig/aconfig_values_test.go b/aconfig/aconfig_values_test.go
index 526579c..ddbea57 100644
--- a/aconfig/aconfig_values_test.go
+++ b/aconfig/aconfig_values_test.go
@@ -33,7 +33,7 @@
 	module := result.ModuleForTests("module_name", "").Module().(*ValuesModule)
 
 	// Check that the provider has the right contents
-	depData, _ := android.SingletonModuleProvider(result, module, valuesProviderKey)
+	depData, _ := android.OtherModuleProvider(result, module, valuesProviderKey)
 	android.AssertStringEquals(t, "package", "foo.package", depData.Package)
 	android.AssertPathsEndWith(t, "srcs", []string{"blah.aconfig_values"}, depData.Values)
 }
diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go
index e771d05..6ad54da 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -15,8 +15,10 @@
 package aconfig
 
 import (
-	"android/soong/android"
 	"fmt"
+	"slices"
+
+	"android/soong/android"
 )
 
 // A singleton module that collects all of the aconfig flags declared in the
@@ -27,70 +29,90 @@
 // 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 AllAconfigDeclarationsFactory() android.Singleton {
-	return &allAconfigDeclarationsSingleton{}
+	return &allAconfigDeclarationsSingleton{releaseMap: make(map[string]allAconfigReleaseDeclarationsSingleton)}
 }
 
-type allAconfigDeclarationsSingleton struct {
+type allAconfigReleaseDeclarationsSingleton struct {
 	intermediateBinaryProtoPath android.OutputPath
 	intermediateTextProtoPath   android.OutputPath
 }
 
+type allAconfigDeclarationsSingleton struct {
+	releaseMap map[string]allAconfigReleaseDeclarationsSingleton
+}
+
+func (this *allAconfigDeclarationsSingleton) sortedConfigNames() []string {
+	var names []string
+	for k := range this.releaseMap {
+		names = append(names, k)
+	}
+	slices.Sort(names)
+	return names
+}
+
 func (this *allAconfigDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	// Find all of the aconfig_declarations modules
-	var packages = make(map[string]int)
-	var cacheFiles android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
-		decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
-		if !ok {
-			return
+	for _, rcName := range append([]string{""}, ctx.Config().ReleaseAconfigExtraReleaseConfigs()...) {
+		// Find all of the aconfig_declarations modules
+		var packages = make(map[string]int)
+		var cacheFiles android.Paths
+		ctx.VisitAllModules(func(module android.Module) {
+			decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigReleaseDeclarationsProviderKey)
+			if !ok {
+				return
+			}
+			cacheFiles = append(cacheFiles, decl[rcName].IntermediateCacheOutputPath)
+			packages[decl[rcName].Package]++
+		})
+
+		var numOffendingPkg = 0
+		for pkg, cnt := range packages {
+			if cnt > 1 {
+				fmt.Printf("%d aconfig_declarations found for package %s\n", cnt, pkg)
+				numOffendingPkg++
+			}
 		}
-		cacheFiles = append(cacheFiles, decl.IntermediateCacheOutputPath)
-		packages[decl.Package]++
-	})
 
-	var numOffendingPkg = 0
-	for pkg, cnt := range packages {
-		if cnt > 1 {
-			fmt.Printf("%d aconfig_declarations found for package %s\n", cnt, pkg)
-			numOffendingPkg++
+		if numOffendingPkg > 0 {
+			panic(fmt.Errorf("Only one aconfig_declarations allowed for each package."))
 		}
+
+		// Generate build action for aconfig (binary proto output)
+		paths := allAconfigReleaseDeclarationsSingleton{
+			intermediateBinaryProtoPath: android.PathForIntermediates(ctx, assembleFileName(rcName, "all_aconfig_declarations.pb")),
+			intermediateTextProtoPath:   android.PathForIntermediates(ctx, assembleFileName(rcName, "all_aconfig_declarations.textproto")),
+		}
+		this.releaseMap[rcName] = paths
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        AllDeclarationsRule,
+			Inputs:      cacheFiles,
+			Output:      this.releaseMap[rcName].intermediateBinaryProtoPath,
+			Description: "all_aconfig_declarations",
+			Args: map[string]string{
+				"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
+			},
+		})
+		ctx.Phony("all_aconfig_declarations", this.releaseMap[rcName].intermediateBinaryProtoPath)
+
+		// Generate build action for aconfig (text proto output)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        AllDeclarationsRuleTextProto,
+			Inputs:      cacheFiles,
+			Output:      this.releaseMap[rcName].intermediateTextProtoPath,
+			Description: "all_aconfig_declarations_textproto",
+			Args: map[string]string{
+				"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
+			},
+		})
+		ctx.Phony("all_aconfig_declarations_textproto", this.releaseMap[rcName].intermediateTextProtoPath)
 	}
-
-	if numOffendingPkg > 0 {
-		panic(fmt.Errorf("Only one aconfig_declarations allowed for each package."))
-	}
-
-	// Generate build action for aconfig (binary proto output)
-	this.intermediateBinaryProtoPath = android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        AllDeclarationsRule,
-		Inputs:      cacheFiles,
-		Output:      this.intermediateBinaryProtoPath,
-		Description: "all_aconfig_declarations",
-		Args: map[string]string{
-			"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
-		},
-	})
-	ctx.Phony("all_aconfig_declarations", this.intermediateBinaryProtoPath)
-
-	// Generate build action for aconfig (text proto output)
-	this.intermediateTextProtoPath = android.PathForIntermediates(ctx, "all_aconfig_declarations.textproto")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        AllDeclarationsRuleTextProto,
-		Inputs:      cacheFiles,
-		Output:      this.intermediateTextProtoPath,
-		Description: "all_aconfig_declarations_textproto",
-		Args: map[string]string{
-			"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
-		},
-	})
-	ctx.Phony("all_aconfig_declarations_textproto", this.intermediateTextProtoPath)
 }
 
 func (this *allAconfigDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.DistForGoal("droid", this.intermediateBinaryProtoPath)
-	for _, goal := range []string{"docs", "droid", "sdk"} {
-		ctx.DistForGoalWithFilename(goal, this.intermediateBinaryProtoPath, "flags.pb")
-		ctx.DistForGoalWithFilename(goal, this.intermediateTextProtoPath, "flags.textproto")
+	for _, rcName := range this.sortedConfigNames() {
+		ctx.DistForGoal("droid", this.releaseMap[rcName].intermediateBinaryProtoPath)
+		for _, goal := range []string{"docs", "droid", "sdk"} {
+			ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateBinaryProtoPath, assembleFileName(rcName, "flags.pb"))
+			ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateTextProtoPath, assembleFileName(rcName, "flags.textproto"))
+		}
 	}
 }
diff --git a/aconfig/build_flags/all_build_flag_declarations.go b/aconfig/build_flags/all_build_flag_declarations.go
index 282c9dc..5f02912 100644
--- a/aconfig/build_flags/all_build_flag_declarations.go
+++ b/aconfig/build_flags/all_build_flag_declarations.go
@@ -38,7 +38,7 @@
 	// Find all of the build_flag_declarations modules
 	var intermediateFiles android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		decl, ok := android.SingletonModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
+		decl, ok := android.OtherModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
 		if !ok {
 			return
 		}
diff --git a/aconfig/codegen/Android.bp b/aconfig/codegen/Android.bp
index 0c78b94..5fac0a8 100644
--- a/aconfig/codegen/Android.bp
+++ b/aconfig/codegen/Android.bp
@@ -12,7 +12,6 @@
         "soong",
         "soong-aconfig",
         "soong-android",
-        "soong-bazel",
         "soong-java",
         "soong-rust",
     ],
diff --git a/aconfig/codegen/init.go b/aconfig/codegen/init.go
index 98d288f..ed0b3ed 100644
--- a/aconfig/codegen/init.go
+++ b/aconfig/codegen/init.go
@@ -32,6 +32,7 @@
 				`    --mode ${mode}` +
 				`    --cache ${in}` +
 				`    --out ${out}.tmp` +
+				`    --allow-instrumentation ${debug}` +
 				` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
 				` && rm -rf ${out}.tmp`,
 			CommandDeps: []string{
@@ -39,7 +40,7 @@
 				"$soong_zip",
 			},
 			Restat: true,
-		}, "mode")
+		}, "mode", "debug")
 
 	// For cc_aconfig_library: Generate C++ library
 	cppRule = pctx.AndroidStaticRule("cc_aconfig_library",
diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
index 673ac2a..ebca413 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -20,6 +20,7 @@
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
+	"strconv"
 )
 
 type declarationsTagType struct {
@@ -71,6 +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")
 	}
 }
 
@@ -102,7 +104,8 @@
 		Output:      srcJarPath,
 		Description: "aconfig.srcjar",
 		Args: map[string]string{
-			"mode": mode,
+			"mode":  mode,
+			"debug": strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorage()),
 		},
 	})
 
diff --git a/aconfig/codegen/java_aconfig_library_test.go b/aconfig/codegen/java_aconfig_library_test.go
index 87b54a4..d8372f3 100644
--- a/aconfig/codegen/java_aconfig_library_test.go
+++ b/aconfig/codegen/java_aconfig_library_test.go
@@ -260,7 +260,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations_bar",
 				package: "com.example.package.bar",
-				container: "system_ext",
+				container: "vendor",
 				srcs: ["bar.aconfig"],
 			}
 
diff --git a/aconfig/exported_java_aconfig_library.go b/aconfig/exported_java_aconfig_library.go
index 291938f..a64cac8 100644
--- a/aconfig/exported_java_aconfig_library.go
+++ b/aconfig/exported_java_aconfig_library.go
@@ -30,7 +30,7 @@
 	// Find all of the aconfig_declarations modules
 	var cacheFiles android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
+		decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
 		if !ok {
 			return
 		}
diff --git a/aconfig/init.go b/aconfig/init.go
index 4655467..6f91d8e 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -44,7 +44,7 @@
 	// For create-device-config-sysprops: Generate aconfig flag value map text file
 	aconfigTextRule = pctx.AndroidStaticRule("aconfig_text",
 		blueprint.RuleParams{
-			Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}'` +
+			Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}:{permission}={state:bool}'` +
 				` --cache ${in}` +
 				` --out ${out}.tmp` +
 				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
diff --git a/aconfig/testing.go b/aconfig/testing.go
index f6489ec..4ceb6b3 100644
--- a/aconfig/testing.go
+++ b/aconfig/testing.go
@@ -23,7 +23,25 @@
 var PrepareForTestWithAconfigBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents)
 
 func runTest(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult {
-	return android.GroupFixturePreparers(PrepareForTestWithAconfigBuildComponents).
+	return PrepareForTest(t).
 		ExtendWithErrorHandler(errorHandler).
 		RunTestWithBp(t, bp)
 }
+
+func PrepareForTest(t *testing.T, preparers ...android.FixturePreparer) android.FixturePreparer {
+	preparers = append([]android.FixturePreparer{PrepareForTestWithAconfigBuildComponents}, preparers...)
+	return android.GroupFixturePreparers(preparers...)
+}
+
+func addBuildFlagsForTest(buildFlags map[string]string) android.FixturePreparer {
+	return android.GroupFixturePreparers(
+		android.FixtureModifyProductVariables(func(vars android.FixtureProductVariables) {
+			if vars.BuildFlags == nil {
+				vars.BuildFlags = make(map[string]string)
+			}
+			for k, v := range buildFlags {
+				vars.BuildFlags[k] = v
+			}
+		}),
+	)
+}
diff --git a/aidl_library/Android.bp b/aidl_library/Android.bp
index ec21504..07472a4 100644
--- a/aidl_library/Android.bp
+++ b/aidl_library/Android.bp
@@ -29,4 +29,5 @@
         "aidl_library_test.go",
     ],
     pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
 }
diff --git a/aidl_library/aidl_library_test.go b/aidl_library/aidl_library_test.go
index 01eab0e..1660456 100644
--- a/aidl_library/aidl_library_test.go
+++ b/aidl_library/aidl_library_test.go
@@ -15,8 +15,9 @@
 package aidl_library
 
 import (
-	"android/soong/android"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestAidlLibrary(t *testing.T) {
@@ -46,7 +47,7 @@
 	).RunTest(t).TestContext
 
 	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
-	actualInfo, _ := android.SingletonModuleProvider(ctx, foo, AidlLibraryProvider)
+	actualInfo, _ := android.OtherModuleProvider(ctx, foo, AidlLibraryProvider)
 
 	android.AssertArrayString(
 		t,
@@ -95,7 +96,7 @@
 	).RunTest(t).TestContext
 
 	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
-	actualInfo, _ := android.SingletonModuleProvider(ctx, foo, AidlLibraryProvider)
+	actualInfo, _ := android.OtherModuleProvider(ctx, foo, AidlLibraryProvider)
 
 	android.AssertArrayString(
 		t,
diff --git a/android/Android.bp b/android/Android.bp
index 9ce8cdc..2adedfe 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -38,8 +38,11 @@
         "arch_list.go",
         "arch_module_context.go",
         "base_module_context.go",
-        "buildinfo_prop.go",
+        "build_prop.go",
+        "compliance_metadata.go",
         "config.go",
+        "container_violations.go",
+        "container.go",
         "test_config.go",
         "configurable_properties.go",
         "configured_jars.go",
@@ -56,6 +59,7 @@
         "gen_notice.go",
         "hooks.go",
         "image.go",
+        "init.go",
         "license.go",
         "license_kind.go",
         "license_metadata.go",
@@ -83,12 +87,15 @@
         "plugin.go",
         "prebuilt.go",
         "prebuilt_build_tool.go",
+        "product_config.go",
+        "product_config_to_bp.go",
         "proto.go",
         "provider.go",
         "raw_files.go",
         "register.go",
         "rule_builder.go",
         "sandbox.go",
+        "sbom.go",
         "sdk.go",
         "sdk_version.go",
         "shared_properties.go",
@@ -102,6 +109,7 @@
         "updatable_modules.go",
         "util.go",
         "variable.go",
+        "vintf_fragment.go",
         "visibility.go",
     ],
     testSrcs: [
@@ -110,6 +118,7 @@
         "androidmk_test.go",
         "apex_test.go",
         "arch_test.go",
+        "blueprint_e2e_test.go",
         "config_test.go",
         "configured_jars_test.go",
         "csuite_config_test.go",
@@ -143,6 +152,9 @@
         "test_suites_test.go",
         "util_test.go",
         "variable_test.go",
+        "vintf_fragment_test.go",
         "visibility_test.go",
     ],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index ee9891d..d2a9622 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -43,6 +43,10 @@
 
 var AconfigDeclarationsProviderKey = blueprint.NewProvider[AconfigDeclarationsProviderData]()
 
+type AconfigReleaseDeclarationsProviderData map[string]AconfigDeclarationsProviderData
+
+var AconfigReleaseDeclarationsProviderKey = blueprint.NewProvider[AconfigReleaseDeclarationsProviderData]()
+
 type ModeInfo struct {
 	Container string
 	Mode      string
@@ -112,6 +116,8 @@
 		if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok {
 			mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath)
 		}
+		// If we were generating on-device artifacts for other release configs, we would need to add code here to propagate
+		// those artifacts as well.  See also b/298444886.
 		if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok {
 			for container, v := range dep.AconfigFiles {
 				mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...)
@@ -130,12 +136,12 @@
 			AconfigFiles: mergedAconfigFiles,
 			ModeInfos:    mergedModeInfos,
 		})
-		ctx.Module().base().aconfigFilePaths = getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles)
+		ctx.setAconfigPaths(getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles))
 	}
 }
 
 func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) {
-	info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
+	info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
 	// If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done.
 	if !ok || len(info.AconfigFiles) == 0 {
 		return
@@ -166,7 +172,7 @@
 	if len(*entries) == 0 {
 		return
 	}
-	info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
+	info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
 	if !ok || len(info.AconfigFiles) == 0 {
 		return
 	}
@@ -181,6 +187,20 @@
 	}
 }
 
+func aconfigUpdateAndroidMkInfos(ctx fillInEntriesContext, mod Module, infos *AndroidMkProviderInfo) {
+	info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
+	if !ok || len(info.AconfigFiles) == 0 {
+		return
+	}
+	// All of the files in the module potentially depend on the aconfig flag values.
+	infos.PrimaryInfo.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
+	if len(infos.ExtraInfo) > 0 {
+		for _, ei := range (*infos).ExtraInfo {
+			ei.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
+		}
+	}
+}
+
 func mergeAconfigFiles(ctx ModuleContext, container string, inputs Paths, generateRule bool) Paths {
 	inputs = SortedUniquePaths(inputs)
 	if len(inputs) == 1 {
@@ -213,7 +233,8 @@
 	} else if m.ProductSpecific() {
 		container = "product"
 	} else if m.SystemExtSpecific() {
-		container = "system_ext"
+		// system_ext and system partitions should be treated as one container
+		container = "system"
 	}
 
 	paths = append(paths, aconfigFiles[container]...)
diff --git a/android/all_teams.go b/android/all_teams.go
index d4bf7d0..01be396 100644
--- a/android/all_teams.go
+++ b/android/all_teams.go
@@ -1,9 +1,11 @@
 package android
 
 import (
-	"android/soong/android/team_proto"
+	"path"
 	"path/filepath"
 
+	"android/soong/android/team_proto"
+
 	"google.golang.org/protobuf/proto"
 )
 
@@ -93,7 +95,7 @@
 		}
 
 		testModInfo := TestModuleInformation{}
-		if tmi, ok := SingletonModuleProvider(ctx, module, TestOnlyProviderKey); ok {
+		if tmi, ok := OtherModuleProvider(ctx, module, TestOnlyProviderKey); ok {
 			testModInfo = tmi
 		}
 
@@ -152,6 +154,11 @@
 		} else {
 			teamProperties, found = t.lookupDefaultTeam(m.bpFile)
 		}
+		// Deal with one blueprint file including another by looking up the default
+		// in the main Android.bp rather than one listed with "build = [My.bp]"
+		if !found {
+			teamProperties, found = t.lookupDefaultTeam(path.Join(path.Dir(m.bpFile), "Android.bp"))
+		}
 
 		trendy_team_id := ""
 		if found {
diff --git a/android/all_teams_test.go b/android/all_teams_test.go
index 96ed92f..fa8c048 100644
--- a/android/all_teams_test.go
+++ b/android/all_teams_test.go
@@ -264,6 +264,84 @@
 	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
 }
 
+func TestPackageLookupForIncludedBlueprintFiles(t *testing.T) {
+	t.Parallel()
+	rootBp := `
+                package { default_team: "team_top"}
+		team {
+			name: "team_top",
+			trendy_team_id: "trendy://team_top",
+		}
+                build = ["include.bp"]
+ `
+	includeBp := `
+		fake {
+			name: "IncludedModule",
+		} `
+
+	ctx := GroupFixturePreparers(
+		prepareForTestWithTeamAndFakes,
+		PrepareForTestWithPackageModule,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
+		}),
+		FixtureAddTextFile("Android.bp", rootBp),
+		FixtureAddTextFile("include.bp", includeBp),
+	).RunTest(t)
+
+	var teams *team_proto.AllTeams
+	teams = getTeamProtoOutput(t, ctx)
+
+	// map of module name -> trendy team name.
+	actualTeams := make(map[string]*string)
+	for _, teamProto := range teams.Teams {
+		actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
+	}
+	expectedTeams := map[string]*string{
+		"IncludedModule": proto.String("trendy://team_top"),
+	}
+	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
+}
+
+func TestPackageLookupForIncludedBlueprintFilesWithPackageInChildBlueprint(t *testing.T) {
+	t.Parallel()
+	rootBp := `
+		team {
+			name: "team_top",
+			trendy_team_id: "trendy://team_top",
+		}
+                build = ["include.bp"]
+ `
+	includeBp := `
+                package { default_team: "team_top"}
+		fake {
+			name: "IncludedModule",
+		} `
+
+	ctx := GroupFixturePreparers(
+		prepareForTestWithTeamAndFakes,
+		PrepareForTestWithPackageModule,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
+		}),
+		FixtureAddTextFile("Android.bp", rootBp),
+		FixtureAddTextFile("include.bp", includeBp),
+	).RunTest(t)
+
+	var teams *team_proto.AllTeams
+	teams = getTeamProtoOutput(t, ctx)
+
+	// map of module name -> trendy team name.
+	actualTeams := make(map[string]*string)
+	for _, teamProto := range teams.Teams {
+		actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
+	}
+	expectedTeams := map[string]*string{
+		"IncludedModule": proto.String("trendy://team_top"),
+	}
+	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
+}
+
 type fakeForTests struct {
 	ModuleBase
 
diff --git a/android/androidmk.go b/android/androidmk.go
index 66f42f9..cac2cfe 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -34,7 +34,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
 )
@@ -157,6 +156,7 @@
 }
 
 type AndroidMkEntriesContext interface {
+	OtherModuleProviderContext
 	Config() Config
 }
 
@@ -170,7 +170,7 @@
 }
 
 func (a *androidMkExtraEntriesContext) Provider(provider blueprint.AnyProviderKey) (any, bool) {
-	return a.ctx.moduleProvider(a.mod, provider)
+	return a.ctx.otherModuleProvider(a.mod, provider)
 }
 
 type AndroidMkExtraEntriesFunc func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries)
@@ -354,14 +354,15 @@
 		availableTaggedDists = availableTaggedDists.addPathsForTag(DefaultDistTag, a.OutputFile.Path())
 	}
 
+	info := OtherModuleProviderOrDefault(a.entryContext, mod, InstallFilesProvider)
 	// If the distFiles created by GenerateTaggedDistFiles contains paths for the
 	// DefaultDistTag then that takes priority so delete any existing paths.
-	if _, ok := amod.distFiles[DefaultDistTag]; ok {
+	if _, ok := info.DistFiles[DefaultDistTag]; ok {
 		delete(availableTaggedDists, DefaultDistTag)
 	}
 
 	// Finally, merge the distFiles created by GenerateTaggedDistFiles.
-	availableTaggedDists = availableTaggedDists.merge(amod.distFiles)
+	availableTaggedDists = availableTaggedDists.merge(info.DistFiles)
 
 	if len(availableTaggedDists) == 0 {
 		// Nothing dist-able for this module.
@@ -372,7 +373,7 @@
 	distContributions := &distContributions{}
 
 	if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) {
-		distContributions.licenseMetadataFile = amod.licenseMetadataFile
+		distContributions.licenseMetadataFile = info.LicenseMetadataFile
 	}
 
 	// Iterate over this module's dist structs, merged from the dist and dists properties.
@@ -497,8 +498,10 @@
 	ModuleDir(module blueprint.Module) string
 	ModuleSubDir(module blueprint.Module) string
 	Config() Config
-	moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
+	otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 	ModuleType(module blueprint.Module) string
+	OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{})
+	HasMutatorFinished(mutatorName string) bool
 }
 
 func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
@@ -514,7 +517,8 @@
 	if a.Include == "" {
 		a.Include = "$(BUILD_PREBUILT)"
 	}
-	a.Required = append(a.Required, amod.RequiredModuleNames()...)
+	a.Required = append(a.Required, amod.RequiredModuleNames(ctx)...)
+	a.Required = append(a.Required, amod.VintfFragmentModuleNames(ctx)...)
 	a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
 	a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
 
@@ -535,13 +539,14 @@
 	a.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
 
 	// If the install rule was generated by Soong tell Make about it.
-	if len(base.katiInstalls) > 0 {
+	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
+	if len(info.KatiInstalls) > 0 {
 		// Assume the primary install file is last since it probably needs to depend on any other
 		// installed files.  If that is not the case we can add a method to specify the primary
 		// installed file.
-		a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", base.katiInstalls[len(base.katiInstalls)-1].to)
-		a.SetString("LOCAL_SOONG_INSTALL_PAIRS", base.katiInstalls.BuiltInstalled())
-		a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", base.katiSymlinks.InstallPaths().Paths())
+		a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", info.KatiInstalls[len(info.KatiInstalls)-1].to)
+		a.SetString("LOCAL_SOONG_INSTALL_PAIRS", info.KatiInstalls.BuiltInstalled())
+		a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", info.KatiSymlinks.InstallPaths().Paths())
 	} else {
 		// Soong may not have generated the install rule also when `no_full_install: true`.
 		// Mark this module as uninstallable in order to prevent Make from creating an
@@ -549,8 +554,16 @@
 		a.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
 	}
 
-	if len(base.testData) > 0 {
-		a.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(base.testData)...)
+	if info.UncheckedModule {
+		a.SetBool("LOCAL_DONT_CHECK_MODULE", true)
+	} else if info.CheckbuildTarget != nil {
+		a.SetPath("LOCAL_CHECKED_MODULE", info.CheckbuildTarget)
+	} else {
+		a.SetOptionalPath("LOCAL_CHECKED_MODULE", a.OutputFile)
+	}
+
+	if len(info.TestData) > 0 {
+		a.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...)
 	}
 
 	if am, ok := mod.(ApexModule); ok {
@@ -587,10 +600,10 @@
 		}
 
 		if !base.InVendorRamdisk() {
-			a.AddPaths("LOCAL_FULL_INIT_RC", base.initRcPaths)
+			a.AddPaths("LOCAL_FULL_INIT_RC", info.InitRcPaths)
 		}
-		if len(base.vintfFragmentsPaths) > 0 {
-			a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", base.vintfFragmentsPaths)
+		if len(info.VintfFragmentsPaths) > 0 {
+			a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", info.VintfFragmentsPaths)
 		}
 		a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary))
 		if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) {
@@ -632,11 +645,11 @@
 		}
 	}
 
-	if licenseMetadata, ok := SingletonModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
+	if licenseMetadata, ok := OtherModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
 		a.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
 	}
 
-	if _, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+	if _, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
 		a.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true)
 	}
 
@@ -793,15 +806,19 @@
 
 	// Additional cases here require review for correct license propagation to make.
 	var err error
-	switch x := mod.(type) {
-	case AndroidMkDataProvider:
-		err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
-	case bootstrap.GoBinaryTool:
-		err = translateGoBinaryModule(ctx, w, mod, x)
-	case AndroidMkEntriesProvider:
-		err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
-	default:
-		// Not exported to make so no make variables to set.
+
+	if info, ok := ctx.otherModuleProvider(mod, AndroidMkInfoProvider); ok {
+		androidMkEntriesInfos := info.(*AndroidMkProviderInfo)
+		err = translateAndroidMkEntriesInfoModule(ctx, w, moduleInfoJSONs, mod, androidMkEntriesInfos)
+	} else {
+		switch x := mod.(type) {
+		case AndroidMkDataProvider:
+			err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
+		case AndroidMkEntriesProvider:
+			err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
+		default:
+			// Not exported to make so no make variables to set.
+		}
 	}
 
 	if err != nil {
@@ -811,23 +828,6 @@
 	return err
 }
 
-// A simple, special Android.mk entry output func to make it possible to build blueprint tools using
-// m by making them phony targets.
-func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
-	goBinary bootstrap.GoBinaryTool) error {
-
-	name := ctx.ModuleName(mod)
-	fmt.Fprintln(w, ".PHONY:", name)
-	fmt.Fprintln(w, name+":", goBinary.InstallPath())
-	fmt.Fprintln(w, "")
-	// Assuming no rules in make include go binaries in distributables.
-	// If the assumption is wrong, make will fail to build without the necessary .meta_lic and .meta_module files.
-	// In that case, add the targets and rules here to build a .meta_lic file for `name` and a .meta_module for
-	// `goBinary.InstallPath()` pointing to the `name`.meta_lic file.
-
-	return nil
-}
-
 func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Module) {
 	// Get the preamble content through AndroidMkEntries logic.
 	data.Entries = AndroidMkEntries{
@@ -860,6 +860,7 @@
 	}
 
 	data := provider.AndroidMk()
+
 	if data.Include == "" {
 		data.Include = "$(BUILD_PREBUILT)"
 	}
@@ -899,6 +900,7 @@
 		case "*android_sdk.sdkRepoHost": // doesn't go through base_rules
 		case "*apex.apexBundle": // license properties written
 		case "*bpf.bpf": // license properties written (both for module and objs)
+		case "*libbpf_prog.libbpfProg": // license properties written (both for module and objs)
 		case "*genrule.Module": // writes non-custom before adding .phony
 		case "*java.SystemModules": // doesn't go through base_rules
 		case "*java.systemModulesImport": // doesn't go through base_rules
@@ -906,6 +908,7 @@
 		case "*phony.PhonyRule": // writes phony deps and acts like `.PHONY`
 		case "*selinux.selinuxContextsModule": // license properties written
 		case "*sysprop.syspropLibrary": // license properties written
+		case "*vintf.vintfCompatibilityMatrixRule": // use case like phony
 		default:
 			if !ctx.Config().IsEnvFalse("ANDROID_REQUIRE_LICENSES") {
 				return fmt.Errorf("custom make rules not allowed for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), ctx.ModuleName(mod))
@@ -917,7 +920,7 @@
 	}
 
 	if !data.Entries.disabled() {
-		if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
 			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
 		}
 	}
@@ -959,7 +962,7 @@
 	}
 
 	if len(entriesList) > 0 && !entriesList[0].disabled() {
-		if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
 			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
 		}
 	}
@@ -967,11 +970,11 @@
 	return nil
 }
 
-func ShouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module Module) bool {
+func ShouldSkipAndroidMkProcessing(ctx ConfigurableEvaluatorContext, module Module) bool {
 	return shouldSkipAndroidMkProcessing(ctx, module.base())
 }
 
-func shouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module *ModuleBase) bool {
+func shouldSkipAndroidMkProcessing(ctx ConfigurableEvaluatorContext, module *ModuleBase) bool {
 	if !module.commonProperties.NamespaceExportedToMake {
 		// TODO(jeffrygaston) do we want to validate that there are no modules being
 		// exported to Kati that depend on this module?
@@ -1047,3 +1050,564 @@
 	}
 	fmt.Fprintln(w)
 }
+
+type AndroidMkProviderInfo struct {
+	PrimaryInfo AndroidMkInfo
+	ExtraInfo   []AndroidMkInfo
+}
+
+type AndroidMkInfo struct {
+	// Android.mk class string, e.g. EXECUTABLES, JAVA_LIBRARIES, ETC
+	Class string
+	// Optional suffix to append to the module name. Useful when a module wants to return multiple
+	// AndroidMkEntries objects. For example, when a java_library returns an additional entry for
+	// its hostdex sub-module, this SubName field is set to "-hostdex" so that it can have a
+	// different name than the parent's.
+	SubName string
+	// If set, this value overrides the base module name. SubName is still appended.
+	OverrideName string
+	// Dist files to output
+	DistFiles TaggedDistFiles
+	// The output file for Kati to process and/or install. If absent, the module is skipped.
+	OutputFile OptionalPath
+	// If true, the module is skipped and does not appear on the final Android-<product name>.mk
+	// file. Useful when a module needs to be skipped conditionally.
+	Disabled bool
+	// The postprocessing mk file to include, e.g. $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk
+	// If not set, $(BUILD_SYSTEM)/prebuilt.mk is used.
+	Include string
+	// Required modules that need to be built and included in the final build output when building
+	// this module.
+	Required []string
+	// Required host modules that need to be built and included in the final build output when
+	// building this module.
+	Host_required []string
+	// Required device modules that need to be built and included in the final build output when
+	// building this module.
+	Target_required []string
+
+	HeaderStrings []string
+	FooterStrings []string
+
+	// A map that holds the up-to-date Make variable values. Can be accessed from tests.
+	EntryMap map[string][]string
+	// A list of EntryMap keys in insertion order. This serves a few purposes:
+	// 1. Prevents churns. Golang map doesn't provide consistent iteration order, so without this,
+	// the outputted Android-*.mk file may change even though there have been no content changes.
+	// 2. Allows modules to refer to other variables, like LOCAL_BAR_VAR := $(LOCAL_FOO_VAR),
+	// without worrying about the variables being mixed up in the actual mk file.
+	// 3. Makes troubleshooting and spotting errors easier.
+	EntryOrder []string
+}
+
+// TODO: rename it to AndroidMkEntriesProvider after AndroidMkEntriesProvider interface is gone.
+var AndroidMkInfoProvider = blueprint.NewProvider[*AndroidMkProviderInfo]()
+
+func translateAndroidMkEntriesInfoModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
+	mod blueprint.Module, providerInfo *AndroidMkProviderInfo) error {
+	if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) {
+		return nil
+	}
+
+	// Deep copy the provider info since we need to modify the info later
+	info := deepCopyAndroidMkProviderInfo(providerInfo)
+
+	aconfigUpdateAndroidMkInfos(ctx, mod.(Module), &info)
+
+	// Any new or special cases here need review to verify correct propagation of license information.
+	info.PrimaryInfo.fillInEntries(ctx, mod)
+	info.PrimaryInfo.write(w)
+	if len(info.ExtraInfo) > 0 {
+		for _, ei := range info.ExtraInfo {
+			ei.fillInEntries(ctx, mod)
+			ei.write(w)
+		}
+	}
+
+	if !info.PrimaryInfo.disabled() {
+		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
+		}
+	}
+
+	return nil
+}
+
+// Utility funcs to manipulate Android.mk variable entries.
+
+// SetString sets a Make variable with the given name to the given value.
+func (a *AndroidMkInfo) SetString(name, value string) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = []string{value}
+}
+
+// SetPath sets a Make variable with the given name to the given path string.
+func (a *AndroidMkInfo) SetPath(name string, path Path) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = []string{path.String()}
+}
+
+// SetOptionalPath sets a Make variable with the given name to the given path string if it is valid.
+// It is a no-op if the given path is invalid.
+func (a *AndroidMkInfo) SetOptionalPath(name string, path OptionalPath) {
+	if path.Valid() {
+		a.SetPath(name, path.Path())
+	}
+}
+
+// AddPath appends the given path string to a Make variable with the given name.
+func (a *AndroidMkInfo) AddPath(name string, path Path) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = append(a.EntryMap[name], path.String())
+}
+
+// AddOptionalPath appends the given path string to a Make variable with the given name if it is
+// valid. It is a no-op if the given path is invalid.
+func (a *AndroidMkInfo) AddOptionalPath(name string, path OptionalPath) {
+	if path.Valid() {
+		a.AddPath(name, path.Path())
+	}
+}
+
+// SetPaths sets a Make variable with the given name to a slice of the given path strings.
+func (a *AndroidMkInfo) SetPaths(name string, paths Paths) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = paths.Strings()
+}
+
+// SetOptionalPaths sets a Make variable with the given name to a slice of the given path strings
+// only if there are a non-zero amount of paths.
+func (a *AndroidMkInfo) SetOptionalPaths(name string, paths Paths) {
+	if len(paths) > 0 {
+		a.SetPaths(name, paths)
+	}
+}
+
+// AddPaths appends the given path strings to a Make variable with the given name.
+func (a *AndroidMkInfo) AddPaths(name string, paths Paths) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...)
+}
+
+// SetBoolIfTrue sets a Make variable with the given name to true if the given flag is true.
+// It is a no-op if the given flag is false.
+func (a *AndroidMkInfo) SetBoolIfTrue(name string, flag bool) {
+	if flag {
+		if _, ok := a.EntryMap[name]; !ok {
+			a.EntryOrder = append(a.EntryOrder, name)
+		}
+		a.EntryMap[name] = []string{"true"}
+	}
+}
+
+// SetBool sets a Make variable with the given name to if the given bool flag value.
+func (a *AndroidMkInfo) SetBool(name string, flag bool) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	if flag {
+		a.EntryMap[name] = []string{"true"}
+	} else {
+		a.EntryMap[name] = []string{"false"}
+	}
+}
+
+// AddStrings appends the given strings to a Make variable with the given name.
+func (a *AndroidMkInfo) AddStrings(name string, value ...string) {
+	if len(value) == 0 {
+		return
+	}
+	if _, ok := a.EntryMap[name]; !ok {
+		a.EntryOrder = append(a.EntryOrder, name)
+	}
+	a.EntryMap[name] = append(a.EntryMap[name], value...)
+}
+
+// AddCompatibilityTestSuites adds the supplied test suites to the EntryMap, with special handling
+// for partial MTS and MCTS test suites.
+func (a *AndroidMkInfo) AddCompatibilityTestSuites(suites ...string) {
+	// M(C)TS supports a full test suite and partial per-module MTS test suites, with naming mts-${MODULE}.
+	// To reduce repetition, if we find a partial M(C)TS test suite without an full M(C)TS test suite,
+	// we add the full test suite to our list.
+	if PrefixInList(suites, "mts-") && !InList("mts", suites) {
+		suites = append(suites, "mts")
+	}
+	if PrefixInList(suites, "mcts-") && !InList("mcts", suites) {
+		suites = append(suites, "mcts")
+	}
+	a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...)
+}
+
+func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
+	helperInfo := AndroidMkInfo{
+		EntryMap: make(map[string][]string),
+	}
+
+	amod := mod.(Module)
+	base := amod.base()
+	name := base.BaseModuleName()
+	if a.OverrideName != "" {
+		name = a.OverrideName
+	}
+
+	if a.Include == "" {
+		a.Include = "$(BUILD_PREBUILT)"
+	}
+	a.Required = append(a.Required, amod.RequiredModuleNames(ctx)...)
+	a.Required = append(a.Required, amod.VintfFragmentModuleNames(ctx)...)
+	a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
+	a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
+
+	for _, distString := range a.GetDistForGoals(ctx, mod) {
+		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)))
+
+	// Collect make variable assignment entries.
+	helperInfo.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
+	helperInfo.SetString("LOCAL_MODULE", name+a.SubName)
+	helperInfo.SetString("LOCAL_MODULE_CLASS", a.Class)
+	helperInfo.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
+	helperInfo.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
+	helperInfo.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
+	helperInfo.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
+	helperInfo.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
+
+	// If the install rule was generated by Soong tell Make about it.
+	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
+	if len(info.KatiInstalls) > 0 {
+		// Assume the primary install file is last since it probably needs to depend on any other
+		// installed files.  If that is not the case we can add a method to specify the primary
+		// installed file.
+		helperInfo.SetPath("LOCAL_SOONG_INSTALLED_MODULE", info.KatiInstalls[len(info.KatiInstalls)-1].to)
+		helperInfo.SetString("LOCAL_SOONG_INSTALL_PAIRS", info.KatiInstalls.BuiltInstalled())
+		helperInfo.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", info.KatiSymlinks.InstallPaths().Paths())
+	} else {
+		// Soong may not have generated the install rule also when `no_full_install: true`.
+		// Mark this module as uninstallable in order to prevent Make from creating an
+		// install rule there.
+		helperInfo.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
+	}
+
+	if len(info.TestData) > 0 {
+		helperInfo.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...)
+	}
+
+	if am, ok := mod.(ApexModule); ok {
+		helperInfo.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform())
+	}
+
+	archStr := base.Arch().ArchType.String()
+	host := false
+	switch base.Os().Class {
+	case Host:
+		if base.Target().HostCross {
+			// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
+			if base.Arch().ArchType != Common {
+				helperInfo.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
+			}
+		} else {
+			// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
+			if base.Arch().ArchType != Common {
+				helperInfo.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
+			}
+		}
+		host = true
+	case Device:
+		// Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
+		if base.Arch().ArchType != Common {
+			if base.Target().NativeBridge {
+				hostArchStr := base.Target().NativeBridgeHostArchName
+				if hostArchStr != "" {
+					helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
+				}
+			} else {
+				helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", archStr)
+			}
+		}
+
+		if !base.InVendorRamdisk() {
+			helperInfo.AddPaths("LOCAL_FULL_INIT_RC", info.InitRcPaths)
+		}
+		if len(info.VintfFragmentsPaths) > 0 {
+			helperInfo.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", info.VintfFragmentsPaths)
+		}
+		helperInfo.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary))
+		if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) {
+			helperInfo.SetString("LOCAL_VENDOR_MODULE", "true")
+		}
+		helperInfo.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(base.commonProperties.Device_specific))
+		helperInfo.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(base.commonProperties.Product_specific))
+		helperInfo.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(base.commonProperties.System_ext_specific))
+		if base.commonProperties.Owner != nil {
+			helperInfo.SetString("LOCAL_MODULE_OWNER", *base.commonProperties.Owner)
+		}
+	}
+
+	if host {
+		makeOs := base.Os().String()
+		if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl {
+			makeOs = "linux"
+		}
+		helperInfo.SetString("LOCAL_MODULE_HOST_OS", makeOs)
+		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)
+	}
+
+	if _, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+		helperInfo.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true)
+	}
+
+	a.mergeEntries(&helperInfo)
+
+	// Write to footer.
+	a.FooterStrings = append([]string{"include " + a.Include}, a.FooterStrings...)
+}
+
+// This method merges the entries to helperInfo, then replaces a's EntryMap and
+// EntryOrder with helperInfo's
+func (a *AndroidMkInfo) mergeEntries(helperInfo *AndroidMkInfo) {
+	for _, extraEntry := range a.EntryOrder {
+		if v, ok := helperInfo.EntryMap[extraEntry]; ok {
+			v = append(v, a.EntryMap[extraEntry]...)
+		} else {
+			helperInfo.EntryMap[extraEntry] = a.EntryMap[extraEntry]
+			helperInfo.EntryOrder = append(helperInfo.EntryOrder, extraEntry)
+		}
+	}
+	a.EntryOrder = helperInfo.EntryOrder
+	a.EntryMap = helperInfo.EntryMap
+}
+
+func (a *AndroidMkInfo) disabled() bool {
+	return a.Disabled || !a.OutputFile.Valid()
+}
+
+// write  flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the
+// given Writer object.
+func (a *AndroidMkInfo) write(w io.Writer) {
+	if a.disabled() {
+		return
+	}
+
+	combinedHeaderString := strings.Join(a.HeaderStrings, "\n")
+	combinedFooterString := strings.Join(a.FooterStrings, "\n")
+	w.Write([]byte(combinedHeaderString))
+	for _, name := range a.EntryOrder {
+		AndroidMkEmitAssignList(w, name, a.EntryMap[name])
+	}
+	w.Write([]byte(combinedFooterString))
+}
+
+// Compute the list of Make strings to declare phony goals and dist-for-goals
+// calls from the module's dist and dists properties.
+func (a *AndroidMkInfo) GetDistForGoals(ctx fillInEntriesContext, mod blueprint.Module) []string {
+	distContributions := a.getDistContributions(ctx, mod)
+	if distContributions == nil {
+		return nil
+	}
+
+	return generateDistContributionsForMake(distContributions)
+}
+
+// Compute the contributions that the module makes to the dist.
+func (a *AndroidMkInfo) getDistContributions(ctx fillInEntriesContext, mod blueprint.Module) *distContributions {
+	amod := mod.(Module).base()
+	name := amod.BaseModuleName()
+
+	// Collate the set of associated tag/paths available for copying to the dist.
+	// Start with an empty (nil) set.
+	var availableTaggedDists TaggedDistFiles
+
+	// Then merge in any that are provided explicitly by the module.
+	if a.DistFiles != nil {
+		// Merge the DistFiles into the set.
+		availableTaggedDists = availableTaggedDists.merge(a.DistFiles)
+	}
+
+	// If no paths have been provided for the DefaultDistTag and the output file is
+	// valid then add that as the default dist path.
+	if _, ok := availableTaggedDists[DefaultDistTag]; !ok && a.OutputFile.Valid() {
+		availableTaggedDists = availableTaggedDists.addPathsForTag(DefaultDistTag, a.OutputFile.Path())
+	}
+
+	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
+	// If the distFiles created by GenerateTaggedDistFiles contains paths for the
+	// DefaultDistTag then that takes priority so delete any existing paths.
+	if _, ok := info.DistFiles[DefaultDistTag]; ok {
+		delete(availableTaggedDists, DefaultDistTag)
+	}
+
+	// Finally, merge the distFiles created by GenerateTaggedDistFiles.
+	availableTaggedDists = availableTaggedDists.merge(info.DistFiles)
+
+	if len(availableTaggedDists) == 0 {
+		// Nothing dist-able for this module.
+		return nil
+	}
+
+	// Collate the contributions this module makes to the dist.
+	distContributions := &distContributions{}
+
+	if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) {
+		distContributions.licenseMetadataFile = info.LicenseMetadataFile
+	}
+
+	// Iterate over this module's dist structs, merged from the dist and dists properties.
+	for _, dist := range amod.Dists() {
+		// Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
+		goals := strings.Join(dist.Targets, " ")
+
+		// Get the tag representing the output files to be dist'd. e.g. ".jar", ".proguard_map"
+		var tag string
+		if dist.Tag == nil {
+			// If the dist struct does not specify a tag, use the default output files tag.
+			tag = DefaultDistTag
+		} else {
+			tag = *dist.Tag
+		}
+
+		// Get the paths of the output files to be dist'd, represented by the tag.
+		// Can be an empty list.
+		tagPaths := availableTaggedDists[tag]
+		if len(tagPaths) == 0 {
+			// Nothing to dist for this tag, continue to the next dist.
+			continue
+		}
+
+		if len(tagPaths) > 1 && (dist.Dest != nil || dist.Suffix != nil) {
+			errorMessage := "%s: Cannot apply dest/suffix for more than one dist " +
+				"file for %q goals tag %q in module %s. The list of dist files, " +
+				"which should have a single element, is:\n%s"
+			panic(fmt.Errorf(errorMessage, mod, goals, tag, name, tagPaths))
+		}
+
+		copiesForGoals := distContributions.getCopiesForGoals(goals)
+
+		// Iterate over each path adding a copy instruction to copiesForGoals
+		for _, path := range tagPaths {
+			// It's possible that the Path is nil from errant modules. Be defensive here.
+			if path == nil {
+				tagName := "default" // for error message readability
+				if dist.Tag != nil {
+					tagName = *dist.Tag
+				}
+				panic(fmt.Errorf("Dist file should not be nil for the %s tag in %s", tagName, name))
+			}
+
+			dest := filepath.Base(path.String())
+
+			if dist.Dest != nil {
+				var err error
+				if dest, err = validateSafePath(*dist.Dest); err != nil {
+					// This was checked in ModuleBase.GenerateBuildActions
+					panic(err)
+				}
+			}
+
+			ext := filepath.Ext(dest)
+			suffix := ""
+			if dist.Suffix != nil {
+				suffix = *dist.Suffix
+			}
+
+			productString := ""
+			if dist.Append_artifact_with_product != nil && *dist.Append_artifact_with_product {
+				productString = fmt.Sprintf("_%s", ctx.Config().DeviceProduct())
+			}
+
+			if suffix != "" || productString != "" {
+				dest = strings.TrimSuffix(dest, ext) + suffix + productString + ext
+			}
+
+			if dist.Dir != nil {
+				var err error
+				if dest, err = validateSafePath(*dist.Dir, dest); err != nil {
+					// This was checked in ModuleBase.GenerateBuildActions
+					panic(err)
+				}
+			}
+
+			copiesForGoals.addCopyInstruction(path, dest)
+		}
+	}
+
+	return distContributions
+}
+
+func deepCopyAndroidMkProviderInfo(providerInfo *AndroidMkProviderInfo) AndroidMkProviderInfo {
+	info := AndroidMkProviderInfo{
+		PrimaryInfo: deepCopyAndroidMkInfo(&providerInfo.PrimaryInfo),
+	}
+	if len(providerInfo.ExtraInfo) > 0 {
+		for _, i := range providerInfo.ExtraInfo {
+			info.ExtraInfo = append(info.ExtraInfo, deepCopyAndroidMkInfo(&i))
+		}
+	}
+	return info
+}
+
+func deepCopyAndroidMkInfo(mkinfo *AndroidMkInfo) AndroidMkInfo {
+	info := AndroidMkInfo{
+		Class:        mkinfo.Class,
+		SubName:      mkinfo.SubName,
+		OverrideName: mkinfo.OverrideName,
+		// There is no modification on DistFiles or OutputFile, so no need to
+		// make their deep copy.
+		DistFiles:       mkinfo.DistFiles,
+		OutputFile:      mkinfo.OutputFile,
+		Disabled:        mkinfo.Disabled,
+		Include:         mkinfo.Include,
+		Required:        deepCopyStringSlice(mkinfo.Required),
+		Host_required:   deepCopyStringSlice(mkinfo.Host_required),
+		Target_required: deepCopyStringSlice(mkinfo.Target_required),
+		HeaderStrings:   deepCopyStringSlice(mkinfo.HeaderStrings),
+		FooterStrings:   deepCopyStringSlice(mkinfo.FooterStrings),
+		EntryOrder:      deepCopyStringSlice(mkinfo.EntryOrder),
+	}
+	info.EntryMap = make(map[string][]string)
+	for k, v := range mkinfo.EntryMap {
+		info.EntryMap[k] = deepCopyStringSlice(v)
+	}
+
+	return info
+}
+
+func deepCopyStringSlice(original []string) []string {
+	result := make([]string, len(original))
+	copy(result, original)
+	return result
+}
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index ae2187f..c37eeab 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -36,10 +36,6 @@
 	data       AndroidMkData
 	distFiles  TaggedDistFiles
 	outputFile OptionalPath
-
-	// The paths that will be used as the default dist paths if no tag is
-	// specified.
-	defaultDistPaths Paths
 }
 
 const (
@@ -50,7 +46,7 @@
 
 func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 
-	m.base().licenseMetadataFile = PathForOutput(ctx, "meta_lic")
+	var defaultDistPaths Paths
 
 	// If the dist_output_file: true then create an output file that is stored in
 	// the OutputFile property of the AndroidMkEntry.
@@ -62,7 +58,7 @@
 		// property in AndroidMkEntry when determining the default dist paths.
 		// Setting this first allows it to be overridden based on the
 		// default_dist_files setting replicating that previous behavior.
-		m.defaultDistPaths = Paths{path}
+		defaultDistPaths = Paths{path}
 	}
 
 	// Based on the setting of the default_dist_files property possibly create a
@@ -71,29 +67,40 @@
 	defaultDistFiles := proptools.StringDefault(m.properties.Default_dist_files, defaultDistFiles_Tagged)
 	switch defaultDistFiles {
 	case defaultDistFiles_None:
-		// Do nothing
+		m.setOutputFiles(ctx, defaultDistPaths)
 
 	case defaultDistFiles_Default:
 		path := PathForTesting("default-dist.out")
-		m.defaultDistPaths = Paths{path}
+		defaultDistPaths = Paths{path}
+		m.setOutputFiles(ctx, defaultDistPaths)
 		m.distFiles = MakeDefaultDistFiles(path)
 
 	case defaultDistFiles_Tagged:
 		// Module types that set AndroidMkEntry.DistFiles to the result of calling
 		// GenerateTaggedDistFiles(ctx) relied on no tag being treated as "" which
-		// meant that the default dist paths would be whatever was returned by
-		// OutputFiles(""). In order to preserve that behavior when treating no tag
-		// as being equal to DefaultDistTag this ensures that
-		// OutputFiles(DefaultDistTag) will return the same as OutputFiles("").
-		m.defaultDistPaths = PathsForTesting("one.out")
+		// meant that the default dist paths would be the same as empty-string-tag
+		// output files. In order to preserve that behavior when treating no tag
+		// as being equal to DefaultDistTag this ensures that DefaultDistTag output
+		// will be the same as empty-string-tag output.
+		defaultDistPaths = PathsForTesting("one.out")
+		m.setOutputFiles(ctx, defaultDistPaths)
 
 		// This must be called after setting defaultDistPaths/outputFile as
-		// GenerateTaggedDistFiles calls into OutputFiles(tag) which may use those
-		// fields.
+		// GenerateTaggedDistFiles calls into outputFiles property which may use
+		// those fields.
 		m.distFiles = m.GenerateTaggedDistFiles(ctx)
 	}
 }
 
+func (m *customModule) setOutputFiles(ctx ModuleContext, defaultDistPaths Paths) {
+	ctx.SetOutputFiles(PathsForTesting("one.out"), "")
+	ctx.SetOutputFiles(PathsForTesting("two.out", "three/four.out"), ".multiple")
+	ctx.SetOutputFiles(PathsForTesting("another.out"), ".another-tag")
+	if defaultDistPaths != nil {
+		ctx.SetOutputFiles(defaultDistPaths, DefaultDistTag)
+	}
+}
+
 func (m *customModule) AndroidMk() AndroidMkData {
 	return AndroidMkData{
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
@@ -102,25 +109,6 @@
 	}
 }
 
-func (m *customModule) OutputFiles(tag string) (Paths, error) {
-	switch tag {
-	case DefaultDistTag:
-		if m.defaultDistPaths != nil {
-			return m.defaultDistPaths, nil
-		} else {
-			return nil, fmt.Errorf("default dist tag is not available")
-		}
-	case "":
-		return PathsForTesting("one.out"), nil
-	case ".multiple":
-		return PathsForTesting("two.out", "three/four.out"), nil
-	case ".another-tag":
-		return PathsForTesting("another.out"), nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (m *customModule) AndroidMkEntries() []AndroidMkEntries {
 	return []AndroidMkEntries{
 		{
@@ -287,7 +275,8 @@
 		)
 	}
 	for idx, line := range androidMkLines {
-		expectedLine := strings.ReplaceAll(expectedAndroidMkLines[idx], "meta_lic", module.base().licenseMetadataFile.String())
+		expectedLine := strings.ReplaceAll(expectedAndroidMkLines[idx], "meta_lic",
+			OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider).LicenseMetadataFile.String())
 		if line != expectedLine {
 			t.Errorf(
 				"Expected AndroidMk line to be '%s', got '%s'",
diff --git a/android/apex.go b/android/apex.go
index 2ac6ed0..114fe29 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"reflect"
 	"slices"
 	"sort"
 	"strconv"
@@ -84,6 +85,12 @@
 
 	// Returns the name of the test apexes that this module is included in.
 	TestApexes []string
+
+	// Returns the name of the overridden apex (com.android.foo)
+	BaseApexName string
+
+	// Returns the value of `apex_available_name`
+	ApexAvailableName string
 }
 
 // AllApexInfo holds the ApexInfo of all apexes that include this module.
@@ -142,6 +149,17 @@
 	return false
 }
 
+// To satisfy the comparable interface
+func (i ApexInfo) Equal(other any) bool {
+	otherApexInfo, ok := other.(ApexInfo)
+	return ok && i.ApexVariationName == otherApexInfo.ApexVariationName &&
+		i.MinSdkVersion == otherApexInfo.MinSdkVersion &&
+		i.Updatable == otherApexInfo.Updatable &&
+		i.UsePlatformApis == otherApexInfo.UsePlatformApis &&
+		reflect.DeepEqual(i.InApexVariants, otherApexInfo.InApexVariants) &&
+		reflect.DeepEqual(i.InApexModules, otherApexInfo.InApexModules)
+}
+
 // 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 {
@@ -277,7 +295,7 @@
 	//
 	// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
 	// "//apex_available:platform" refers to non-APEX partitions like "system.img".
-	// "com.android.gki.*" matches any APEX module name with the prefix "com.android.gki.".
+	// Prefix pattern (com.foo.*) can be used to match with any APEX name with the prefix(com.foo.).
 	// Default is ["//apex_available:platform"].
 	Apex_available []string
 
@@ -470,15 +488,6 @@
 const (
 	AvailableToPlatform = "//apex_available:platform"
 	AvailableToAnyApex  = "//apex_available:anyapex"
-	AvailableToGkiApex  = "com.android.gki.*"
-)
-
-var (
-	AvailableToRecognziedWildcards = []string{
-		AvailableToPlatform,
-		AvailableToAnyApex,
-		AvailableToGkiApex,
-	}
 )
 
 // CheckAvailableForApex provides the default algorithm for checking the apex availability. When the
@@ -491,11 +500,27 @@
 	if len(apex_available) == 0 {
 		return what == AvailableToPlatform
 	}
-	return InList(what, apex_available) ||
-		(what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available)) ||
-		(strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available)) ||
-		(what == "com.google.mainline.primary.libs") || // TODO b/248601389
-		(what == "com.google.mainline.go.primary.libs") // TODO b/248601389
+
+	// TODO b/248601389
+	if what == "com.google.mainline.primary.libs" || what == "com.google.mainline.go.primary.libs" {
+		return true
+	}
+
+	for _, apex_name := range apex_available {
+		// exact match.
+		if apex_name == what {
+			return true
+		}
+		// //apex_available:anyapex matches with any apex name, but not //apex_available:platform
+		if apex_name == AvailableToAnyApex && what != AvailableToPlatform {
+			return true
+		}
+		// prefix match.
+		if strings.HasSuffix(apex_name, ".*") && strings.HasPrefix(what, strings.TrimSuffix(apex_name, "*")) {
+			return true
+		}
+	}
+	return false
 }
 
 // Implements ApexModule
@@ -521,7 +546,20 @@
 // This function makes sure that the apex_available property is valid
 func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
 	for _, n := range m.ApexProperties.Apex_available {
-		if n == AvailableToPlatform || n == AvailableToAnyApex || n == AvailableToGkiApex {
+		if n == AvailableToPlatform || n == AvailableToAnyApex {
+			continue
+		}
+		// Prefix pattern should end with .* and has at least two components.
+		if strings.Contains(n, "*") {
+			if !strings.HasSuffix(n, ".*") {
+				mctx.PropertyErrorf("apex_available", "Wildcard should end with .* like com.foo.*")
+			}
+			if strings.Count(n, ".") < 2 {
+				mctx.PropertyErrorf("apex_available", "Wildcard requires two or more components like com.foo.*")
+			}
+			if strings.Count(n, "*") != 1 {
+				mctx.PropertyErrorf("apex_available", "Wildcard is not allowed in the middle.")
+			}
 			continue
 		}
 		if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() {
@@ -607,9 +645,15 @@
 		return ""
 	}
 
-	// If this module has no apex variations the use the platform variation.
 	if len(apexInfos) == 0 {
-		return ""
+		if ctx.IsAddingDependency() {
+			// If this module has no apex variations we can't do any mapping on the incoming variation, just return it
+			// and let the caller get a "missing variant" error.
+			return incomingVariation
+		} else {
+			// If this module has no apex variations the use the platform variation.
+			return ""
+		}
 	}
 
 	// Convert the list of apex infos into from the AllApexInfoProvider into the merged list
@@ -671,7 +715,7 @@
 	base.ApexProperties.InAnyApex = true
 	base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex
 
-	if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) {
+	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
 		// effects.  TODO(jiyong): move this routine to somewhere else
diff --git a/android/arch.go b/android/arch.go
index e0c6908..942727a 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -19,10 +19,10 @@
 	"fmt"
 	"reflect"
 	"runtime"
+	"slices"
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -396,45 +396,21 @@
 // device_supported and host_supported properties to determine which OsTypes are enabled for this
 // module, then searches through the Targets to determine which have enabled Targets for this
 // module.
-func osMutator(bpctx blueprint.BottomUpMutatorContext) {
-	var module Module
-	var ok bool
-	if module, ok = bpctx.Module().(Module); !ok {
-		// The module is not a Soong module, it is a Blueprint module.
-		if bootstrap.IsBootstrapModule(bpctx.Module()) {
-			// Bootstrap Go modules are always the build OS or linux bionic.
-			config := bpctx.Config().(Config)
-			osNames := []string{config.BuildOSTarget.OsVariation()}
-			for _, hostCrossTarget := range config.Targets[LinuxBionic] {
-				if hostCrossTarget.Arch.ArchType == config.BuildOSTarget.Arch.ArchType {
-					osNames = append(osNames, hostCrossTarget.OsVariation())
-				}
-			}
-			osNames = FirstUniqueStrings(osNames)
-			bpctx.CreateVariations(osNames...)
-		}
-		return
-	}
+type osTransitionMutator struct{}
 
-	// Bootstrap Go module support above requires this mutator to be a
-	// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
-	// filters out non-Soong modules.  Now that we've handled them, create a
-	// normal android.BottomUpMutatorContext.
-	mctx := bottomUpMutatorContextFactory(bpctx, module, false)
-	defer bottomUpMutatorContextPool.Put(mctx)
+type allOsInfo struct {
+	Os         map[string]OsType
+	Variations []string
+}
 
-	base := module.base()
+var allOsProvider = blueprint.NewMutatorProvider[*allOsInfo]("os_propagate")
 
-	// Nothing to do for modules that are not architecture specific (e.g. a genrule).
-	if !base.ArchSpecific() {
-		return
-	}
-
-	// Collect a list of OSTypes supported by this module based on the HostOrDevice value
-	// passed to InitAndroidArchModule and the device_supported and host_supported properties.
+// moduleOSList collects a list of OSTypes supported by this module based on the HostOrDevice
+// value passed to InitAndroidArchModule and the device_supported and host_supported properties.
+func moduleOSList(ctx ConfigContext, base *ModuleBase) []OsType {
 	var moduleOSList []OsType
 	for _, os := range osTypeList {
-		for _, t := range mctx.Config().Targets[os] {
+		for _, t := range ctx.Config().Targets[os] {
 			if base.supportsTarget(t) {
 				moduleOSList = append(moduleOSList, os)
 				break
@@ -442,53 +418,91 @@
 		}
 	}
 
-	createCommonOSVariant := base.commonProperties.CreateCommonOSVariant
+	if base.commonProperties.CreateCommonOSVariant {
+		// A CommonOS variant was requested so add it to the list of OS variants to
+		// create. It needs to be added to the end because it needs to depend on the
+		// the other variants and inter variant dependencies can only be created from a
+		// later variant in that list to an earlier one. That is because variants are
+		// always processed in the order in which they are created.
+		moduleOSList = append(moduleOSList, CommonOS)
+	}
+
+	return moduleOSList
+}
+
+func (o *osTransitionMutator) Split(ctx BaseModuleContext) []string {
+	module := ctx.Module()
+	base := module.base()
+
+	// Nothing to do for modules that are not architecture specific (e.g. a genrule).
+	if !base.ArchSpecific() {
+		return []string{""}
+	}
+
+	moduleOSList := moduleOSList(ctx, base)
 
 	// If there are no supported OSes then disable the module.
-	if len(moduleOSList) == 0 && !createCommonOSVariant {
+	if len(moduleOSList) == 0 {
 		base.Disable()
-		return
+		return []string{""}
 	}
 
 	// Convert the list of supported OsTypes to the variation names.
 	osNames := make([]string, len(moduleOSList))
+	osMapping := make(map[string]OsType, len(moduleOSList))
 	for i, os := range moduleOSList {
 		osNames[i] = os.String()
+		osMapping[osNames[i]] = os
 	}
 
-	if createCommonOSVariant {
-		// A CommonOS variant was requested so add it to the list of OS variants to
-		// create. It needs to be added to the end because it needs to depend on the
-		// the other variants in the list returned by CreateVariations(...) and inter
-		// variant dependencies can only be created from a later variant in that list to
-		// an earlier one. That is because variants are always processed in the order in
-		// which they are returned from CreateVariations(...).
-		osNames = append(osNames, CommonOS.Name)
-		moduleOSList = append(moduleOSList, CommonOS)
+	SetProvider(ctx, allOsProvider, &allOsInfo{
+		Os:         osMapping,
+		Variations: osNames,
+	})
+
+	return osNames
+}
+
+func (o *osTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (o *osTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	module := ctx.Module()
+	base := module.base()
+
+	if !base.ArchSpecific() {
+		return ""
 	}
 
-	// Create the variations, annotate each one with which OS it was created for, and
+	return incomingVariation
+}
+
+func (o *osTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+	module := ctx.Module()
+	base := module.base()
+
+	if variation == "" {
+		return
+	}
+
+	allOsInfo, ok := ModuleProvider(ctx, allOsProvider)
+	if !ok {
+		panic(fmt.Errorf("missing allOsProvider"))
+	}
+
+	// Annotate this variant with which OS it was created for, and
 	// squash the appropriate OS-specific properties into the top level properties.
-	modules := mctx.CreateVariations(osNames...)
-	for i, m := range modules {
-		m.base().commonProperties.CompileOS = moduleOSList[i]
-		m.base().setOSProperties(mctx)
-	}
+	base.commonProperties.CompileOS = allOsInfo.Os[variation]
+	base.setOSProperties(ctx)
 
-	if createCommonOSVariant {
+	if variation == CommonOS.String() {
 		// A CommonOS variant was requested so add dependencies from it (the last one in
 		// the list) to the OS type specific variants.
-		last := len(modules) - 1
-		commonOSVariant := modules[last]
-		commonOSVariant.base().commonProperties.CommonOSVariant = true
-		for _, module := range modules[0:last] {
-			// Ignore modules that are enabled. Note, this will only avoid adding
-			// dependencies on OsType variants that are explicitly disabled in their
-			// properties. The CommonOS variant will still depend on disabled variants
-			// if they are disabled afterwards, e.g. in archMutator if
-			if module.Enabled(mctx) {
-				mctx.AddInterVariantDependency(commonOsToOsSpecificVariantTag, commonOSVariant, module)
-			}
+		osList := allOsInfo.Variations[:len(allOsInfo.Variations)-1]
+		for _, os := range osList {
+			variation := []blueprint.Variation{{"os", os}}
+			ctx.AddVariationDependencies(variation, commonOsToOsSpecificVariantTag, ctx.ModuleName())
 		}
 	}
 }
@@ -521,7 +535,7 @@
 
 var DarwinUniversalVariantTag = archDepTag{name: "darwin universal binary"}
 
-// archMutator splits a module into a variant for each Target requested by the module.  Target selection
+// archTransitionMutator splits a module into a variant for each Target requested by the module.  Target selection
 // for a module is in three levels, OsClass, multilib, and then Target.
 // OsClass selection is determined by:
 //   - The HostOrDeviceSupported value passed in to InitAndroidArchModule by the module type factory, which selects
@@ -552,54 +566,47 @@
 //
 // Modules can be initialized with InitAndroidMultiTargetsArchModule, in which case they will be split by OsClass,
 // but will have a common Target that is expected to handle all other selected Targets via ctx.MultiTargets().
-func archMutator(bpctx blueprint.BottomUpMutatorContext) {
-	var module Module
-	var ok bool
-	if module, ok = bpctx.Module().(Module); !ok {
-		if bootstrap.IsBootstrapModule(bpctx.Module()) {
-			// Bootstrap Go modules are always the build architecture.
-			bpctx.CreateVariations(bpctx.Config().(Config).BuildOSTarget.ArchVariation())
-		}
-		return
-	}
+type archTransitionMutator struct{}
 
-	// Bootstrap Go module support above requires this mutator to be a
-	// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
-	// filters out non-Soong modules.  Now that we've handled them, create a
-	// normal android.BottomUpMutatorContext.
-	mctx := bottomUpMutatorContextFactory(bpctx, module, false)
-	defer bottomUpMutatorContextPool.Put(mctx)
+type allArchInfo struct {
+	Targets      map[string]Target
+	MultiTargets []Target
+	Primary      string
+	Multilib     string
+}
 
+var allArchProvider = blueprint.NewMutatorProvider[*allArchInfo]("arch_propagate")
+
+func (a *archTransitionMutator) Split(ctx BaseModuleContext) []string {
+	module := ctx.Module()
 	base := module.base()
 
 	if !base.ArchSpecific() {
-		return
+		return []string{""}
 	}
 
 	os := base.commonProperties.CompileOS
 	if os == CommonOS {
-		// Make sure that the target related properties are initialized for the
-		// CommonOS variant.
-		addTargetProperties(module, commonTargetMap[os.Name], nil, true)
-
 		// Do not create arch specific variants for the CommonOS variant.
-		return
+		return []string{""}
 	}
 
-	osTargets := mctx.Config().Targets[os]
+	osTargets := ctx.Config().Targets[os]
+
 	image := base.commonProperties.ImageVariation
 	// Filter NativeBridge targets unless they are explicitly supported.
 	// Skip creating native bridge variants for non-core modules.
 	if os == Android && !(base.IsNativeBridgeSupported() && image == CoreVariation) {
+		osTargets = slices.DeleteFunc(slices.Clone(osTargets), func(t Target) bool {
+			return bool(t.NativeBridge)
+		})
+	}
 
-		var targets []Target
-		for _, t := range osTargets {
-			if !t.NativeBridge {
-				targets = append(targets, t)
-			}
-		}
-
-		osTargets = targets
+	// Filter HostCross targets if disabled.
+	if base.HostSupported() && !base.HostCrossSupported() {
+		osTargets = slices.DeleteFunc(slices.Clone(osTargets), func(t Target) bool {
+			return t.HostCross
+		})
 	}
 
 	// only the primary arch in the ramdisk / vendor_ramdisk / recovery partition
@@ -611,19 +618,18 @@
 	prefer32 := os == Windows
 
 	// Determine the multilib selection for this module.
-	ignorePrefer32OnDevice := mctx.Config().IgnorePrefer32OnDevice()
-	multilib, extraMultilib := decodeMultilib(base, os, ignorePrefer32OnDevice)
+	multilib, extraMultilib := decodeMultilib(ctx, base)
 
 	// Convert the multilib selection into a list of Targets.
 	targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
 	if err != nil {
-		mctx.ModuleErrorf("%s", err.Error())
+		ctx.ModuleErrorf("%s", err.Error())
 	}
 
 	// If there are no supported targets disable the module.
 	if len(targets) == 0 {
 		base.Disable()
-		return
+		return []string{""}
 	}
 
 	// If the module is using extraMultilib, decode the extraMultilib selection into
@@ -632,7 +638,7 @@
 	if extraMultilib != "" {
 		multiTargets, err = decodeMultilibTargets(extraMultilib, osTargets, prefer32)
 		if err != nil {
-			mctx.ModuleErrorf("%s", err.Error())
+			ctx.ModuleErrorf("%s", err.Error())
 		}
 		multiTargets = filterHostCross(multiTargets, targets[0].HostCross)
 	}
@@ -640,7 +646,7 @@
 	// Recovery is always the primary architecture, filter out any other architectures.
 	// Common arch is also allowed
 	if image == RecoveryVariation {
-		primaryArch := mctx.Config().DevicePrimaryArchType()
+		primaryArch := ctx.Config().DevicePrimaryArchType()
 		targets = filterToArch(targets, primaryArch, Common)
 		multiTargets = filterToArch(multiTargets, primaryArch, Common)
 	}
@@ -648,37 +654,109 @@
 	// If there are no supported targets disable the module.
 	if len(targets) == 0 {
 		base.Disable()
-		return
+		return []string{""}
 	}
 
 	// Convert the targets into a list of arch variation names.
 	targetNames := make([]string, len(targets))
+	targetMapping := make(map[string]Target, len(targets))
 	for i, target := range targets {
 		targetNames[i] = target.ArchVariation()
+		targetMapping[targetNames[i]] = targets[i]
 	}
 
-	// Create the variations, annotate each one with which Target it was created for, and
-	// squash the appropriate arch-specific properties into the top level properties.
-	modules := mctx.CreateVariations(targetNames...)
-	for i, m := range modules {
-		addTargetProperties(m, targets[i], multiTargets, i == 0)
-		m.base().setArchProperties(mctx)
+	SetProvider(ctx, allArchProvider, &allArchInfo{
+		Targets:      targetMapping,
+		MultiTargets: multiTargets,
+		Primary:      targetNames[0],
+		Multilib:     multilib,
+	})
+	return targetNames
+}
 
-		// Install support doesn't understand Darwin+Arm64
-		if os == Darwin && targets[i].HostCross {
-			m.base().commonProperties.SkipInstall = true
+func (a *archTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (a *archTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	module := ctx.Module()
+	base := module.base()
+
+	if !base.ArchSpecific() {
+		return ""
+	}
+
+	os := base.commonProperties.CompileOS
+	if os == CommonOS {
+		// Do not create arch specific variants for the CommonOS variant.
+		return ""
+	}
+
+	if incomingVariation == "" {
+		multilib, _ := decodeMultilib(ctx, base)
+		if multilib == "common" {
+			return "common"
 		}
 	}
+	return incomingVariation
+}
+
+func (a *archTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+	module := ctx.Module()
+	base := module.base()
+	os := base.commonProperties.CompileOS
+
+	if os == CommonOS {
+		// Make sure that the target related properties are initialized for the
+		// CommonOS variant.
+		addTargetProperties(module, commonTargetMap[os.Name], nil, true)
+		return
+	}
+
+	if variation == "" {
+		return
+	}
+
+	if !base.ArchSpecific() {
+		panic(fmt.Errorf("found variation %q for non arch specifc module", variation))
+	}
+
+	allArchInfo, ok := ModuleProvider(ctx, allArchProvider)
+	if !ok {
+		return
+	}
+
+	target, ok := allArchInfo.Targets[variation]
+	if !ok {
+		panic(fmt.Errorf("missing Target for %q", variation))
+	}
+	primary := variation == allArchInfo.Primary
+	multiTargets := allArchInfo.MultiTargets
+
+	// Annotate the new variant with which Target it was created for, and
+	// squash the appropriate arch-specific properties into the top level properties.
+	addTargetProperties(ctx.Module(), target, multiTargets, primary)
+	base.setArchProperties(ctx)
+
+	// Install support doesn't understand Darwin+Arm64
+	if os == Darwin && target.HostCross {
+		base.commonProperties.SkipInstall = true
+	}
 
 	// 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 {
-		if multilib == "darwin_universal" && len(modules) == 2 {
-			mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[1], modules[0])
-		} else if multilib == "darwin_universal_common_first" && len(modules) == 3 {
-			mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[2], modules[1])
+		isUniversalBinary := (allArchInfo.Multilib == "darwin_universal" && len(allArchInfo.Targets) == 2) ||
+			allArchInfo.Multilib == "darwin_universal_common_first" && len(allArchInfo.Targets) == 3
+		isPrimary := variation == ctx.Config().BuildArch.String()
+		hasSecondaryConfigured := len(ctx.Config().Targets[Darwin]) > 1
+		if isUniversalBinary && isPrimary && hasSecondaryConfigured {
+			secondaryArch := ctx.Config().Targets[Darwin][1].Arch.String()
+			variation := []blueprint.Variation{{"arch", secondaryArch}}
+			ctx.AddVariationDependencies(variation, DarwinUniversalVariantTag, ctx.ModuleName())
 		}
 	}
+
 }
 
 // addTargetProperties annotates a variant with the Target is is being compiled for, the list
@@ -695,7 +773,9 @@
 // multilib from the factory's call to InitAndroidArchModule if none was set.  For modules that
 // called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns
 // the actual multilib in extraMultilib.
-func decodeMultilib(base *ModuleBase, os OsType, ignorePrefer32OnDevice bool) (multilib, extraMultilib string) {
+func decodeMultilib(ctx ConfigContext, base *ModuleBase) (multilib, extraMultilib string) {
+	os := base.commonProperties.CompileOS
+	ignorePrefer32OnDevice := ctx.Config().IgnorePrefer32OnDevice()
 	// First check the "android.compile_multilib" or "host.compile_multilib" properties.
 	switch os.Class {
 	case Device:
diff --git a/android/arch_list.go b/android/arch_list.go
index 4233456..9501c87 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -16,7 +16,6 @@
 
 var archVariants = map[ArchType][]string{
 	Arm: {
-		"armv7-a",
 		"armv7-a-neon",
 		"armv8-a",
 		"armv8-2a",
@@ -27,6 +26,7 @@
 		"armv8-2a",
 		"armv8-2a-dotprod",
 		"armv9-a",
+		"armv9-2a",
 	},
 	X86: {
 		"amberlake",
@@ -160,6 +160,9 @@
 		"armv9-a": {
 			"dotprod",
 		},
+		"armv9-2a": {
+			"dotprod",
+		},
 	},
 	X86: {
 		"amberlake": {
diff --git a/android/arch_test.go b/android/arch_test.go
index f0a58a9..57c9010 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -332,6 +332,12 @@
 		}
 
 		module {
+			name: "nohostcross",
+			host_supported: true,
+			host_cross_supported: false,
+		}
+
+		module {
 			name: "baz",
 			device_supported: false,
 		}
@@ -355,13 +361,14 @@
 	`
 
 	testCases := []struct {
-		name          string
-		preparer      FixturePreparer
-		fooVariants   []string
-		barVariants   []string
-		bazVariants   []string
-		quxVariants   []string
-		firstVariants []string
+		name                string
+		preparer            FixturePreparer
+		fooVariants         []string
+		barVariants         []string
+		noHostCrossVariants []string
+		bazVariants         []string
+		quxVariants         []string
+		firstVariants       []string
 
 		multiTargetVariants    []string
 		multiTargetVariantsMap map[string][]string
@@ -373,6 +380,7 @@
 			preparer:            nil,
 			fooVariants:         []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
 			barVariants:         append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
+			noHostCrossVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
 			bazVariants:         nil,
 			quxVariants:         append(buildOS32Variants, "android_arm_armv7-a-neon"),
 			firstVariants:       append(buildOS64Variants, "android_arm64_armv8-a"),
@@ -390,6 +398,7 @@
 			}),
 			fooVariants:         nil,
 			barVariants:         buildOSVariants,
+			noHostCrossVariants: buildOSVariants,
 			bazVariants:         nil,
 			quxVariants:         buildOS32Variants,
 			firstVariants:       buildOS64Variants,
@@ -406,6 +415,7 @@
 			}),
 			fooVariants:         []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
 			barVariants:         []string{"linux_musl_x86_64", "linux_musl_arm64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"},
+			noHostCrossVariants: []string{"linux_musl_x86_64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"},
 			bazVariants:         nil,
 			quxVariants:         []string{"linux_musl_x86", "android_arm_armv7-a-neon"},
 			firstVariants:       []string{"linux_musl_x86_64", "linux_musl_arm64", "android_arm64_armv8-a"},
@@ -441,7 +451,7 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
-			if tt.goOS != runtime.GOOS {
+			if tt.goOS != "" && tt.goOS != runtime.GOOS {
 				t.Skipf("requries runtime.GOOS %s", tt.goOS)
 			}
 
@@ -461,6 +471,10 @@
 				t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g)
 			}
 
+			if g, w := enabledVariants(ctx, "nohostcross"), tt.noHostCrossVariants; !reflect.DeepEqual(w, g) {
+				t.Errorf("want nohostcross variants:\n%q\ngot:\n%q\n", w, g)
+			}
+
 			if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) {
 				t.Errorf("want baz variants:\n%q\ngot:\n%q\n", w, g)
 			}
diff --git a/android/base_module_context.go b/android/base_module_context.go
index 5506000..bb81377 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -220,6 +220,10 @@
 	// 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 {
@@ -270,6 +274,10 @@
 	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)
 }
diff --git a/android/blueprint_e2e_test.go b/android/blueprint_e2e_test.go
new file mode 100644
index 0000000..b274512
--- /dev/null
+++ b/android/blueprint_e2e_test.go
@@ -0,0 +1,105 @@
+// 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"
+)
+
+var testCases []struct {
+	name          string
+	fs            MockFS
+	expectedError string
+} = []struct {
+	name          string
+	fs            MockFS
+	expectedError string
+}{
+	{
+		name: "Can't reference variable before assignment",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+x = foo
+foo = "hello"
+`),
+		},
+		expectedError: "undefined variable foo",
+	},
+	{
+		name: "Can't append to variable before assigned to",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+foo += "world"
+foo = "hello"
+`),
+		},
+		expectedError: "modified non-existent variable \"foo\" with \\+=",
+	},
+	{
+		name: "Can't reassign variable",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+foo = "hello"
+foo = "world"
+`),
+		},
+		expectedError: "variable already set, previous assignment:",
+	},
+	{
+		name: "Can't reassign variable in inherited scope",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+foo = "hello"
+`),
+			"foo/Android.bp": []byte(`
+foo = "world"
+`),
+		},
+		expectedError: "variable already set in inherited scope, previous assignment:",
+	},
+	{
+		name: "Can't modify variable in inherited scope",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+foo = "hello"
+`),
+			"foo/Android.bp": []byte(`
+foo += "world"
+`),
+		},
+		expectedError: "modified non-local variable \"foo\" with \\+=",
+	},
+	{
+		name: "Can't modify variable after referencing",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+foo = "hello"
+x = foo
+foo += "world"
+`),
+		},
+		expectedError: "modified variable \"foo\" with \\+= after referencing",
+	},
+}
+
+func TestBlueprintErrors(t *testing.T) {
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			fixtures := FixtureMergeMockFs(tc.fs)
+			fixtures = fixtures.ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(tc.expectedError))
+			fixtures.RunTest(t)
+		})
+	}
+}
diff --git a/android/build_prop.go b/android/build_prop.go
new file mode 100644
index 0000000..ede93ed
--- /dev/null
+++ b/android/build_prop.go
@@ -0,0 +1,195 @@
+// 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/proptools"
+)
+
+func init() {
+	ctx := InitRegistrationContext
+	ctx.RegisterModuleType("build_prop", buildPropFactory)
+}
+
+type buildPropProperties struct {
+	// Output file name. Defaults to "build.prop"
+	Stem *string
+
+	// List of prop names to exclude. This affects not only common build properties but also
+	// properties in prop_files.
+	Block_list []string
+
+	// Files to be appended at the end of build.prop. These files are appended after
+	// post_process_props without any further checking.
+	Footer_files []string `android:"path"`
+
+	// Path to a JSON file containing product configs.
+	Product_config *string `android:"path"`
+
+	// Optional subdirectory under which this file is installed into
+	Relative_install_path *string
+}
+
+type buildPropModule struct {
+	ModuleBase
+
+	properties buildPropProperties
+
+	outputFilePath OutputPath
+	installPath    InstallPath
+}
+
+func (p *buildPropModule) stem() string {
+	return proptools.StringDefault(p.properties.Stem, "build.prop")
+}
+
+func (p *buildPropModule) propFiles(ctx ModuleContext) Paths {
+	partition := p.partition(ctx.DeviceConfig())
+	if partition == "system" {
+		return ctx.Config().SystemPropFiles(ctx)
+	} else if partition == "system_ext" {
+		return ctx.Config().SystemExtPropFiles(ctx)
+	} else if partition == "product" {
+		return ctx.Config().ProductPropFiles(ctx)
+	} else if partition == "odm" {
+		return ctx.Config().OdmPropFiles(ctx)
+	}
+	return nil
+}
+
+func shouldAddBuildThumbprint(config Config) bool {
+	knownOemProperties := []string{
+		"ro.product.brand",
+		"ro.product.name",
+		"ro.product.device",
+	}
+
+	for _, knownProp := range knownOemProperties {
+		if InList(knownProp, config.OemProperties()) {
+			return true
+		}
+	}
+	return false
+}
+
+// Can't use PartitionTag() because PartitionTag() returns the partition this module is actually
+// installed (e.g. odm module's partition tag can be either "odm" or "vendor")
+func (p *buildPropModule) partition(config DeviceConfig) string {
+	if p.SocSpecific() {
+		return "vendor"
+	} else if p.DeviceSpecific() {
+		return "odm"
+	} else if p.ProductSpecific() {
+		return "product"
+	} else if p.SystemExtSpecific() {
+		return "system_ext"
+	}
+	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
+	}
+
+	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)
+
+	config := ctx.Config()
+
+	cmd := rule.Command().BuiltTool("gen_build_prop")
+
+	cmd.FlagWithInput("--build-hostname-file=", config.BuildHostnameFile(ctx))
+	cmd.FlagWithInput("--build-number-file=", config.BuildNumberFile(ctx))
+	// shouldn't depend on BuildFingerprintFile and BuildThumbprintFile to prevent from rebuilding
+	// on every incremental build.
+	cmd.FlagWithArg("--build-fingerprint-file=", config.BuildFingerprintFile(ctx).String())
+	// Export build thumbprint only if the product has specified at least one oem fingerprint property
+	// b/17888863
+	if shouldAddBuildThumbprint(config) {
+		// In the previous make implementation, a dependency was not added on the thumbprint file
+		cmd.FlagWithArg("--build-thumbprint-file=", config.BuildThumbprintFile(ctx).String())
+	}
+	cmd.FlagWithArg("--build-username=", config.Getenv("BUILD_USERNAME"))
+	// shouldn't depend on BUILD_DATETIME_FILE to prevent from rebuilding on every incremental
+	// build.
+	cmd.FlagWithArg("--date-file=", ctx.Config().Getenv("BUILD_DATETIME_FILE"))
+	cmd.FlagWithInput("--platform-preview-sdk-fingerprint-file=", ApiFingerprintPath(ctx))
+	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)
+
+	postProcessCmd := rule.Command().BuiltTool("post_process_props")
+	if ctx.DeviceConfig().BuildBrokenDupSysprop() {
+		postProcessCmd.Flag("--allow-dup")
+	}
+	postProcessCmd.FlagWithArg("--sdk-version ", config.PlatformSdkVersion().String())
+	if ctx.Config().EnableUffdGc() == "default" {
+		postProcessCmd.FlagWithInput("--kernel-version-file-for-uffd-gc ", PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt"))
+	} else {
+		// 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.Flags(p.properties.Block_list)
+
+	rule.Command().Text("echo").Text(proptools.NinjaAndShellEscape("# end of file")).FlagWithArg(">> ", p.outputFilePath.String())
+
+	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.SetOutputFiles(Paths{p.outputFilePath}, "")
+}
+
+func (p *buildPropModule) AndroidMkEntries() []AndroidMkEntries {
+	return []AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: OptionalPathForPath(p.outputFilePath),
+		ExtraEntries: []AndroidMkExtraEntriesFunc{
+			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", p.installPath.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
+			},
+		},
+	}}
+}
+
+// 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 {
+	module := &buildPropModule{}
+	module.AddProperties(&module.properties)
+	InitAndroidArchModule(module, DeviceSupported, MultilibCommon)
+	return module
+}
diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go
deleted file mode 100644
index 083f3ef..0000000
--- a/android/buildinfo_prop.go
+++ /dev/null
@@ -1,195 +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 android
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/google/blueprint/proptools"
-)
-
-func init() {
-	ctx := InitRegistrationContext
-	ctx.RegisterModuleType("buildinfo_prop", buildinfoPropFactory)
-}
-
-type buildinfoPropProperties struct {
-	// Whether this module is directly installable to one of the partitions. Default: true.
-	Installable *bool
-}
-
-type buildinfoPropModule struct {
-	ModuleBase
-
-	properties buildinfoPropProperties
-
-	outputFilePath OutputPath
-	installPath    InstallPath
-}
-
-var _ OutputFileProducer = (*buildinfoPropModule)(nil)
-
-func (p *buildinfoPropModule) installable() bool {
-	return proptools.BoolDefault(p.properties.Installable, true)
-}
-
-// OutputFileProducer
-func (p *buildinfoPropModule) OutputFiles(tag string) (Paths, error) {
-	if tag != "" {
-		return nil, fmt.Errorf("unsupported tag %q", tag)
-	}
-	return Paths{p.outputFilePath}, nil
-}
-
-func getBuildVariant(config Config) string {
-	if config.Eng() {
-		return "eng"
-	} else if config.Debuggable() {
-		return "userdebug"
-	} else {
-		return "user"
-	}
-}
-
-func getBuildFlavor(config Config) string {
-	buildFlavor := config.DeviceProduct() + "-" + getBuildVariant(config)
-	if InList("address", config.SanitizeDevice()) && !strings.Contains(buildFlavor, "_asan") {
-		buildFlavor += "_asan"
-	}
-	return buildFlavor
-}
-
-func shouldAddBuildThumbprint(config Config) bool {
-	knownOemProperties := []string{
-		"ro.product.brand",
-		"ro.product.name",
-		"ro.product.device",
-	}
-
-	for _, knownProp := range knownOemProperties {
-		if InList(knownProp, config.OemProperties()) {
-			return true
-		}
-	}
-	return false
-}
-
-func (p *buildinfoPropModule) GenerateAndroidBuildActions(ctx ModuleContext) {
-	if ctx.ModuleName() != "buildinfo.prop" || ctx.ModuleDir() != "build/soong" {
-		ctx.ModuleErrorf("There can only be one buildinfo_prop module in build/soong")
-		return
-	}
-	p.outputFilePath = PathForModuleOut(ctx, p.Name()).OutputPath
-	if !ctx.Config().KatiEnabled() {
-		WriteFileRule(ctx, p.outputFilePath, "# no buildinfo.prop if kati is disabled")
-		return
-	}
-
-	rule := NewRuleBuilder(pctx, ctx)
-
-	config := ctx.Config()
-	buildVariant := getBuildVariant(config)
-	buildFlavor := getBuildFlavor(config)
-
-	cmd := rule.Command().BuiltTool("buildinfo")
-
-	if config.BoardUseVbmetaDigestInFingerprint() {
-		cmd.Flag("--use-vbmeta-digest-in-fingerprint")
-	}
-
-	cmd.FlagWithArg("--build-flavor=", buildFlavor)
-	cmd.FlagWithInput("--build-hostname-file=", config.BuildHostnameFile(ctx))
-	cmd.FlagWithArg("--build-id=", config.BuildId())
-	cmd.FlagWithArg("--build-keys=", config.BuildKeys())
-
-	// Note: depending on BuildNumberFile will cause the build.prop file to be rebuilt
-	// every build, but that's intentional.
-	cmd.FlagWithInput("--build-number-file=", config.BuildNumberFile(ctx))
-	if shouldAddBuildThumbprint(config) {
-		// In the previous make implementation, a dependency was not added on the thumbprint file
-		cmd.FlagWithArg("--build-thumbprint-file=", config.BuildThumbprintFile(ctx).String())
-	}
-
-	cmd.FlagWithArg("--build-type=", config.BuildType())
-	cmd.FlagWithArg("--build-username=", config.Getenv("BUILD_USERNAME"))
-	cmd.FlagWithArg("--build-variant=", buildVariant)
-	cmd.FlagForEachArg("--cpu-abis=", config.DeviceAbi())
-
-	// Technically we should also have a dependency on BUILD_DATETIME_FILE,
-	// but it can be either an absolute or relative path, which is hard to turn into
-	// a Path object. So just rely on the BuildNumberFile always changing to cause
-	// us to rebuild.
-	cmd.FlagWithArg("--date-file=", ctx.Config().Getenv("BUILD_DATETIME_FILE"))
-
-	if len(config.ProductLocales()) > 0 {
-		cmd.FlagWithArg("--default-locale=", config.ProductLocales()[0])
-	}
-
-	cmd.FlagForEachArg("--default-wifi-channels=", config.ProductDefaultWifiChannels())
-	cmd.FlagWithArg("--device=", config.DeviceName())
-	if config.DisplayBuildNumber() {
-		cmd.Flag("--display-build-number")
-	}
-
-	cmd.FlagWithArg("--platform-base-os=", config.PlatformBaseOS())
-	cmd.FlagWithArg("--platform-display-version=", config.PlatformDisplayVersionName())
-	cmd.FlagWithArg("--platform-min-supported-target-sdk-version=", config.PlatformMinSupportedTargetSdkVersion())
-	cmd.FlagWithInput("--platform-preview-sdk-fingerprint-file=", ApiFingerprintPath(ctx))
-	cmd.FlagWithArg("--platform-preview-sdk-version=", config.PlatformPreviewSdkVersion())
-	cmd.FlagWithArg("--platform-sdk-version=", config.PlatformSdkVersion().String())
-	cmd.FlagWithArg("--platform-security-patch=", config.PlatformSecurityPatch())
-	cmd.FlagWithArg("--platform-version=", config.PlatformVersionName())
-	cmd.FlagWithArg("--platform-version-codename=", config.PlatformSdkCodename())
-	cmd.FlagForEachArg("--platform-version-all-codenames=", config.PlatformVersionActiveCodenames())
-	cmd.FlagWithArg("--platform-version-known-codenames=", config.PlatformVersionKnownCodenames())
-	cmd.FlagWithArg("--platform-version-last-stable=", config.PlatformVersionLastStable())
-	cmd.FlagWithArg("--product=", config.DeviceProduct())
-
-	cmd.FlagWithOutput("--out=", p.outputFilePath)
-
-	rule.Build(ctx.ModuleName(), "generating buildinfo props")
-
-	if !p.installable() {
-		p.SkipInstall()
-	}
-
-	p.installPath = PathForModuleInstall(ctx)
-	ctx.InstallFile(p.installPath, p.Name(), p.outputFilePath)
-}
-
-func (p *buildinfoPropModule) AndroidMkEntries() []AndroidMkEntries {
-	return []AndroidMkEntries{{
-		Class:      "ETC",
-		OutputFile: OptionalPathForPath(p.outputFilePath),
-		ExtraEntries: []AndroidMkExtraEntriesFunc{
-			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", p.installPath.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
-				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
-			},
-		},
-	}}
-}
-
-// buildinfo_prop module generates a build.prop file, which contains a set of common
-// system/build.prop properties, such as ro.build.version.*.  Not all properties are implemented;
-// currently this module is only for microdroid.
-func buildinfoPropFactory() Module {
-	module := &buildinfoPropModule{}
-	module.AddProperties(&module.properties)
-	InitAndroidModule(module)
-	return module
-}
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
new file mode 100644
index 0000000..38f1382
--- /dev/null
+++ b/android/compliance_metadata.go
@@ -0,0 +1,337 @@
+// 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 (
+	"bytes"
+	"encoding/csv"
+	"encoding/gob"
+	"fmt"
+	"slices"
+	"strconv"
+	"strings"
+
+	"github.com/google/blueprint"
+)
+
+var (
+	// Constants of property names used in compliance metadata of modules
+	ComplianceMetadataProp = struct {
+		NAME                   string
+		PACKAGE                string
+		MODULE_TYPE            string
+		OS                     string
+		ARCH                   string
+		IS_PRIMARY_ARCH        string
+		VARIANT                string
+		IS_STATIC_LIB          string
+		INSTALLED_FILES        string
+		BUILT_FILES            string
+		STATIC_DEPS            string
+		STATIC_DEP_FILES       string
+		WHOLE_STATIC_DEPS      string
+		WHOLE_STATIC_DEP_FILES string
+		LICENSES               string
+
+		// module_type=package
+		PKG_DEFAULT_APPLICABLE_LICENSES string
+
+		// module_type=license
+		LIC_LICENSE_KINDS string
+		LIC_LICENSE_TEXT  string
+		LIC_PACKAGE_NAME  string
+
+		// module_type=license_kind
+		LK_CONDITIONS string
+		LK_URL        string
+	}{
+		"name",
+		"package",
+		"module_type",
+		"os",
+		"arch",
+		"is_primary_arch",
+		"variant",
+		"is_static_lib",
+		"installed_files",
+		"built_files",
+		"static_deps",
+		"static_dep_files",
+		"whole_static_deps",
+		"whole_static_dep_files",
+		"licenses",
+
+		"pkg_default_applicable_licenses",
+
+		"lic_license_kinds",
+		"lic_license_text",
+		"lic_package_name",
+
+		"lk_conditions",
+		"lk_url",
+	}
+
+	// A constant list of all property names in compliance metadata
+	// Order of properties here is the order of columns in the exported CSV file.
+	COMPLIANCE_METADATA_PROPS = []string{
+		ComplianceMetadataProp.NAME,
+		ComplianceMetadataProp.PACKAGE,
+		ComplianceMetadataProp.MODULE_TYPE,
+		ComplianceMetadataProp.OS,
+		ComplianceMetadataProp.ARCH,
+		ComplianceMetadataProp.VARIANT,
+		ComplianceMetadataProp.IS_STATIC_LIB,
+		ComplianceMetadataProp.IS_PRIMARY_ARCH,
+		// Space separated installed files
+		ComplianceMetadataProp.INSTALLED_FILES,
+		// Space separated built files
+		ComplianceMetadataProp.BUILT_FILES,
+		// Space separated module names of static dependencies
+		ComplianceMetadataProp.STATIC_DEPS,
+		// Space separated file paths of static dependencies
+		ComplianceMetadataProp.STATIC_DEP_FILES,
+		// Space separated module names of whole static dependencies
+		ComplianceMetadataProp.WHOLE_STATIC_DEPS,
+		// Space separated file paths of whole static dependencies
+		ComplianceMetadataProp.WHOLE_STATIC_DEP_FILES,
+		ComplianceMetadataProp.LICENSES,
+		// module_type=package
+		ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES,
+		// module_type=license
+		ComplianceMetadataProp.LIC_LICENSE_KINDS,
+		ComplianceMetadataProp.LIC_LICENSE_TEXT, // resolve to file paths
+		ComplianceMetadataProp.LIC_PACKAGE_NAME,
+		// module_type=license_kind
+		ComplianceMetadataProp.LK_CONDITIONS,
+		ComplianceMetadataProp.LK_URL,
+	}
+)
+
+// ComplianceMetadataInfo provides all metadata of a module, e.g. name, module type, package, license,
+// dependencies, built/installed files, etc. It is a wrapper on a map[string]string with some utility
+// methods to get/set properties' values.
+type ComplianceMetadataInfo 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
+	}
+
+	return w.Bytes(), nil
+}
+
+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
+}
+
+func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) {
+	if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
+		panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
+	}
+	c.properties[propertyName] = value
+}
+
+func (c *ComplianceMetadataInfo) SetListValue(propertyName string, value []string) {
+	c.SetStringValue(propertyName, strings.TrimSpace(strings.Join(value, " ")))
+}
+
+func (c *ComplianceMetadataInfo) getStringValue(propertyName string) string {
+	if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
+		panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
+	}
+	return c.properties[propertyName]
+}
+
+func (c *ComplianceMetadataInfo) getAllValues() map[string]string {
+	return c.properties
+}
+
+var (
+	ComplianceMetadataProvider = blueprint.NewProvider[*ComplianceMetadataInfo]()
+)
+
+// buildComplianceMetadataProvider starts with the ModuleContext.ComplianceMetadataInfo() and fills in more common metadata
+// for different module types without accessing their private fields but through android.Module interface
+// and public/private fields of package android. The final metadata is stored to a module's ComplianceMetadataProvider.
+func buildComplianceMetadataProvider(ctx *moduleContext, m *ModuleBase) {
+	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
+	complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.NAME, m.Name())
+	complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.PACKAGE, ctx.ModuleDir())
+	complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.MODULE_TYPE, ctx.ModuleType())
+
+	switch ctx.ModuleType() {
+	case "license":
+		licenseModule := m.module.(*licenseModule)
+		complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_KINDS, licenseModule.properties.License_kinds)
+		complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_TEXT, PathsForModuleSrc(ctx, licenseModule.properties.License_text).Strings())
+		complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LIC_PACKAGE_NAME, String(licenseModule.properties.Package_name))
+	case "license_kind":
+		licenseKindModule := m.module.(*licenseKindModule)
+		complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LK_CONDITIONS, licenseKindModule.properties.Conditions)
+		complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LK_URL, licenseKindModule.properties.Url)
+	default:
+		complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.OS, ctx.Os().String())
+		complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.ARCH, ctx.Arch().String())
+		complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.IS_PRIMARY_ARCH, strconv.FormatBool(ctx.PrimaryArch()))
+		complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.VARIANT, ctx.ModuleSubDir())
+		if m.primaryLicensesProperty != nil && m.primaryLicensesProperty.getName() == "licenses" {
+			complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LICENSES, m.primaryLicensesProperty.getStrings())
+		}
+
+		var installed InstallPaths
+		installed = append(installed, ctx.installFiles...)
+		installed = append(installed, ctx.katiInstalls.InstallPaths()...)
+		installed = append(installed, ctx.katiSymlinks.InstallPaths()...)
+		installed = append(installed, ctx.katiInitRcInstalls.InstallPaths()...)
+		installed = append(installed, ctx.katiVintfInstalls.InstallPaths()...)
+		complianceMetadataInfo.SetListValue(ComplianceMetadataProp.INSTALLED_FILES, FirstUniqueStrings(installed.Strings()))
+	}
+	ctx.setProvider(ComplianceMetadataProvider, complianceMetadataInfo)
+}
+
+func init() {
+	RegisterComplianceMetadataSingleton(InitRegistrationContext)
+}
+
+func RegisterComplianceMetadataSingleton(ctx RegistrationContext) {
+	ctx.RegisterParallelSingletonType("compliance_metadata_singleton", complianceMetadataSingletonFactory)
+}
+
+var (
+	// sqlite3 command line tool
+	sqlite3 = pctx.HostBinToolVariable("sqlite3", "sqlite3")
+
+	// Command to import .csv files to sqlite3 database
+	importCsv = pctx.AndroidStaticRule("importCsv",
+		blueprint.RuleParams{
+			Command: `rm -rf $out && ` +
+				`${sqlite3} $out ".import --csv $in modules" && ` +
+				`${sqlite3} $out ".import --csv ${make_metadata} make_metadata" && ` +
+				`${sqlite3} $out ".import --csv ${make_modules} make_modules"`,
+			CommandDeps: []string{"${sqlite3}"},
+		}, "make_metadata", "make_modules")
+)
+
+func complianceMetadataSingletonFactory() Singleton {
+	return &complianceMetadataSingleton{}
+}
+
+type complianceMetadataSingleton struct {
+}
+
+func writerToCsv(csvWriter *csv.Writer, row []string) {
+	err := csvWriter.Write(row)
+	if err != nil {
+		panic(err)
+	}
+}
+
+// Collect compliance metadata from all Soong modules, write to a CSV file and
+// import compliance metadata from Make and Soong to a sqlite3 database.
+func (c *complianceMetadataSingleton) GenerateBuildActions(ctx SingletonContext) {
+	if !ctx.Config().HasDeviceProduct() {
+		return
+	}
+	var buffer bytes.Buffer
+	csvWriter := csv.NewWriter(&buffer)
+
+	// Collect compliance metadata of modules in Soong and write to out/soong/compliance-metadata/<product>/soong-modules.csv file.
+	columnNames := []string{"id"}
+	columnNames = append(columnNames, COMPLIANCE_METADATA_PROPS...)
+	writerToCsv(csvWriter, columnNames)
+
+	rowId := -1
+	ctx.VisitAllModules(func(module Module) {
+		if !module.Enabled(ctx) {
+			return
+		}
+		moduleType := ctx.ModuleType(module)
+		if moduleType == "package" {
+			metadataMap := map[string]string{
+				ComplianceMetadataProp.NAME:                            ctx.ModuleName(module),
+				ComplianceMetadataProp.MODULE_TYPE:                     ctx.ModuleType(module),
+				ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(module.base().primaryLicensesProperty.getStrings(), " "),
+			}
+			rowId = rowId + 1
+			metadata := []string{strconv.Itoa(rowId)}
+			for _, propertyName := range COMPLIANCE_METADATA_PROPS {
+				metadata = append(metadata, metadataMap[propertyName])
+			}
+			writerToCsv(csvWriter, metadata)
+			return
+		}
+		if provider, ok := ctx.otherModuleProvider(module, ComplianceMetadataProvider); ok {
+			metadataInfo := provider.(*ComplianceMetadataInfo)
+			rowId = rowId + 1
+			metadata := []string{strconv.Itoa(rowId)}
+			for _, propertyName := range COMPLIANCE_METADATA_PROPS {
+				metadata = append(metadata, metadataInfo.getStringValue(propertyName))
+			}
+			writerToCsv(csvWriter, metadata)
+			return
+		}
+	})
+	csvWriter.Flush()
+
+	deviceProduct := ctx.Config().DeviceProduct()
+	modulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "soong-modules.csv")
+	WriteFileRuleVerbatim(ctx, modulesCsv, buffer.String())
+
+	// Metadata generated in Make
+	makeMetadataCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-metadata.csv")
+	makeModulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-modules.csv")
+
+	// Import metadata from Make and Soong to sqlite3 database
+	complianceMetadataDb := PathForOutput(ctx, "compliance-metadata", deviceProduct, "compliance-metadata.db")
+	ctx.Build(pctx, BuildParams{
+		Rule:  importCsv,
+		Input: modulesCsv,
+		Implicits: []Path{
+			makeMetadataCsv,
+			makeModulesCsv,
+		},
+		Output: complianceMetadataDb,
+		Args: map[string]string{
+			"make_metadata": makeMetadataCsv.String(),
+			"make_modules":  makeModulesCsv.String(),
+		},
+	})
+
+	// Phony rule "compliance-metadata.db". "m compliance-metadata.db" to create the compliance metadata database.
+	ctx.Build(pctx, BuildParams{
+		Rule:   blueprint.Phony,
+		Inputs: []Path{complianceMetadataDb},
+		Output: PathForPhony(ctx, "compliance-metadata.db"),
+	})
+
+}
diff --git a/android/config.go b/android/config.go
index 4745a16..00fc823 100644
--- a/android/config.go
+++ b/android/config.go
@@ -173,6 +173,13 @@
 	return c.IsEnvTrue("DISABLE_VERIFY_OVERLAPS") || c.ReleaseDisableVerifyOverlaps() || !c.ReleaseDefaultModuleBuildFromSource()
 }
 
+func (c Config) CoverageSuffix() string {
+	if v := c.IsEnvTrue("EMMA_INSTRUMENT"); v {
+		return "coverage."
+	}
+	return ""
+}
+
 // MaxPageSizeSupported returns the max page size supported by the device. This
 // value will define the ELF segment alignment for binaries (executables and
 // shared libraries).
@@ -198,12 +205,44 @@
 	return c.config.productVariables.ReleaseAconfigValueSets
 }
 
+func (c Config) ReleaseAconfigExtraReleaseConfigs() []string {
+	result := []string{}
+	if val, ok := c.config.productVariables.BuildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"]; ok {
+		if len(val) > 0 {
+			// Remove any duplicates from the list.
+			found := make(map[string]bool)
+			for _, k := range strings.Split(val, " ") {
+				if !found[k] {
+					found[k] = true
+					result = append(result, k)
+				}
+			}
+		}
+	}
+	return result
+}
+
+func (c Config) ReleaseAconfigExtraReleaseConfigsValueSets() map[string][]string {
+	result := make(map[string][]string)
+	for _, rcName := range c.ReleaseAconfigExtraReleaseConfigs() {
+		if value, ok := c.config.productVariables.BuildFlags["RELEASE_ACONFIG_VALUE_SETS_"+rcName]; ok {
+			result[rcName] = strings.Split(value, " ")
+		}
+	}
+	return result
+}
+
 // The flag default permission value passed to aconfig
 // derived from RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION
 func (c Config) ReleaseAconfigFlagDefaultPermission() string {
 	return c.config.productVariables.ReleaseAconfigFlagDefaultPermission
 }
 
+// Enable object size sanitizer
+func (c Config) ReleaseBuildObjectSizeSanitizer() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_BUILD_OBJECT_SIZE_SANITIZER")
+}
+
 // The flag indicating behavior for the tree wrt building modules or using prebuilts
 // derived from RELEASE_DEFAULT_MODULE_BUILD_FROM_SOURCE
 func (c Config) ReleaseDefaultModuleBuildFromSource() bool {
@@ -321,9 +360,6 @@
 	// modules that aren't mixed-built for at least one variant will cause a build
 	// failure
 	ensureAllowlistIntegrity bool
-
-	// List of Api libraries that contribute to Api surfaces.
-	apiLibraries map[string]struct{}
 }
 
 type deviceConfig struct {
@@ -573,40 +609,6 @@
 	setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
 	setBuildMode(cmdArgs.DocFile, GenerateDocFile)
 
-	// TODO(b/276958307): Replace the hardcoded list to a sdk_library local prop.
-	config.apiLibraries = map[string]struct{}{
-		"android.net.ipsec.ike":             {},
-		"art.module.public.api":             {},
-		"conscrypt.module.public.api":       {},
-		"framework-adservices":              {},
-		"framework-appsearch":               {},
-		"framework-bluetooth":               {},
-		"framework-configinfrastructure":    {},
-		"framework-connectivity":            {},
-		"framework-connectivity-t":          {},
-		"framework-devicelock":              {},
-		"framework-graphics":                {},
-		"framework-healthfitness":           {},
-		"framework-location":                {},
-		"framework-media":                   {},
-		"framework-mediaprovider":           {},
-		"framework-nfc":                     {},
-		"framework-ondevicepersonalization": {},
-		"framework-pdf":                     {},
-		"framework-pdf-v":                   {},
-		"framework-permission":              {},
-		"framework-permission-s":            {},
-		"framework-scheduling":              {},
-		"framework-sdkextensions":           {},
-		"framework-statsd":                  {},
-		"framework-sdksandbox":              {},
-		"framework-tethering":               {},
-		"framework-uwb":                     {},
-		"framework-virtualization":          {},
-		"framework-wifi":                    {},
-		"i18n.module.public.api":            {},
-	}
-
 	config.productVariables.Build_from_text_stub = boolPtr(config.BuildFromTextStub())
 
 	return Config{config}, err
@@ -784,6 +786,17 @@
 	return Bool(c.productVariables.DisplayBuildNumber)
 }
 
+// BuildFingerprintFile returns the path to a text file containing metadata
+// representing the current build's fingerprint.
+//
+// Rules that want to reference the build fingerprint should read from this file
+// without depending on it. They will run whenever their other dependencies
+// require them to run and get the current build fingerprint. This ensures they
+// don't rebuild on every incremental build when the build number changes.
+func (c *config) BuildFingerprintFile(ctx PathContext) Path {
+	return PathForArbitraryOutput(ctx, "target", "product", c.DeviceName(), String(c.productVariables.BuildFingerprintFile))
+}
+
 // BuildNumberFile returns the path to a text file containing metadata
 // representing the current build's number.
 //
@@ -1022,6 +1035,22 @@
 	return defaultDir.Join(ctx, "testkey.x509.pem"), defaultDir.Join(ctx, "testkey.pk8")
 }
 
+func (c *config) ExtraOtaKeys(ctx PathContext, recovery bool) []SourcePath {
+	var otaKeys []string
+	if recovery {
+		otaKeys = c.productVariables.ExtraOtaRecoveryKeys
+	} else {
+		otaKeys = c.productVariables.ExtraOtaKeys
+	}
+
+	otaPaths := make([]SourcePath, len(otaKeys))
+	for i, key := range otaKeys {
+		otaPaths[i] = PathForSource(ctx, key+".x509.pem")
+	}
+
+	return otaPaths
+}
+
 func (c *config) BuildKeys() string {
 	defaultCert := String(c.productVariables.DefaultAppCertificate)
 	if defaultCert == "" || defaultCert == filepath.Join(testKeyDir, "testkey") {
@@ -1146,6 +1175,10 @@
 	return Bool(c.productVariables.UseGoma)
 }
 
+func (c *config) UseABFS() bool {
+	return Bool(c.productVariables.UseABFS)
+}
+
 func (c *config) UseRBE() bool {
 	return Bool(c.productVariables.UseRBE)
 }
@@ -1307,10 +1340,6 @@
 	return ExistentPathForSource(ctx, "frameworks", "base", "Android.bp").Valid()
 }
 
-func (c *config) VndkSnapshotBuildArtifacts() bool {
-	return Bool(c.productVariables.VndkSnapshotBuildArtifacts)
-}
-
 func (c *config) HasMultilibConflict(arch ArchType) bool {
 	return c.multilibConflicts[arch]
 }
@@ -1374,10 +1403,6 @@
 	return "vendor"
 }
 
-func (c *deviceConfig) RecoverySnapshotVersion() string {
-	return String(c.config.productVariables.RecoverySnapshotVersion)
-}
-
 func (c *deviceConfig) CurrentApiLevelForVendorModules() string {
 	return StringDefault(c.config.productVariables.DeviceCurrentApiLevelForVendorModules, "current")
 }
@@ -1475,11 +1500,6 @@
 		}
 	}
 	if coverage && len(c.config.productVariables.NativeCoverageExcludePaths) > 0 {
-		// Workaround coverage boot failure.
-		// http://b/269981180
-		if strings.HasPrefix(path, "external/protobuf") {
-			coverage = false
-		}
 		if HasAnyPrefix(path, c.config.productVariables.NativeCoverageExcludePaths) {
 			coverage = false
 		}
@@ -1648,6 +1668,17 @@
 	return Bool(c.productVariables.TrimmedApex)
 }
 
+func (c *config) UseSoongSystemImage() bool {
+	return Bool(c.productVariables.UseSoongSystemImage)
+}
+
+func (c *config) SoongDefinedSystemImage() string {
+	if c.UseSoongSystemImage() {
+		return String(c.productVariables.ProductSoongDefinedSystemImage)
+	}
+	return ""
+}
+
 func (c *config) EnforceSystemCertificate() bool {
 	return Bool(c.productVariables.EnforceSystemCertificate)
 }
@@ -1755,22 +1786,6 @@
 	return c.SystemExtSepolicyPrebuiltApiDir() != "" || c.ProductSepolicyPrebuiltApiDir() != ""
 }
 
-func (c *deviceConfig) DirectedVendorSnapshot() bool {
-	return c.config.productVariables.DirectedVendorSnapshot
-}
-
-func (c *deviceConfig) VendorSnapshotModules() map[string]bool {
-	return c.config.productVariables.VendorSnapshotModules
-}
-
-func (c *deviceConfig) DirectedRecoverySnapshot() bool {
-	return c.config.productVariables.DirectedRecoverySnapshot
-}
-
-func (c *deviceConfig) RecoverySnapshotModules() map[string]bool {
-	return c.config.productVariables.RecoverySnapshotModules
-}
-
 func createDirsMap(previous map[string]bool, dirs []string) (map[string]bool, error) {
 	var ret = make(map[string]bool)
 	for _, dir := range dirs {
@@ -1797,40 +1812,6 @@
 	return dirMap.(map[string]bool)
 }
 
-var vendorSnapshotDirsExcludedKey = NewOnceKey("VendorSnapshotDirsExcludedMap")
-
-func (c *deviceConfig) VendorSnapshotDirsExcludedMap() map[string]bool {
-	return c.createDirsMapOnce(vendorSnapshotDirsExcludedKey, nil,
-		c.config.productVariables.VendorSnapshotDirsExcluded)
-}
-
-var vendorSnapshotDirsIncludedKey = NewOnceKey("VendorSnapshotDirsIncludedMap")
-
-func (c *deviceConfig) VendorSnapshotDirsIncludedMap() map[string]bool {
-	excludedMap := c.VendorSnapshotDirsExcludedMap()
-	return c.createDirsMapOnce(vendorSnapshotDirsIncludedKey, excludedMap,
-		c.config.productVariables.VendorSnapshotDirsIncluded)
-}
-
-var recoverySnapshotDirsExcludedKey = NewOnceKey("RecoverySnapshotDirsExcludedMap")
-
-func (c *deviceConfig) RecoverySnapshotDirsExcludedMap() map[string]bool {
-	return c.createDirsMapOnce(recoverySnapshotDirsExcludedKey, nil,
-		c.config.productVariables.RecoverySnapshotDirsExcluded)
-}
-
-var recoverySnapshotDirsIncludedKey = NewOnceKey("RecoverySnapshotDirsIncludedMap")
-
-func (c *deviceConfig) RecoverySnapshotDirsIncludedMap() map[string]bool {
-	excludedMap := c.RecoverySnapshotDirsExcludedMap()
-	return c.createDirsMapOnce(recoverySnapshotDirsIncludedKey, excludedMap,
-		c.config.productVariables.RecoverySnapshotDirsIncluded)
-}
-
-func (c *deviceConfig) HostFakeSnapshotEnabled() bool {
-	return c.config.productVariables.HostFakeSnapshotEnabled
-}
-
 func (c *deviceConfig) ShippingApiLevel() ApiLevel {
 	if c.config.productVariables.Shipping_api_level == nil {
 		return NoneApiLevel
@@ -1863,10 +1844,6 @@
 	return c.config.productVariables.BuildBrokenTrebleSyspropNeverallow
 }
 
-func (c *deviceConfig) BuildBrokenUsesSoongPython2Modules() bool {
-	return c.config.productVariables.BuildBrokenUsesSoongPython2Modules
-}
-
 func (c *deviceConfig) BuildDebugfsRestrictionsEnabled() bool {
 	return c.config.productVariables.BuildDebugfsRestrictionsEnabled
 }
@@ -1883,6 +1860,10 @@
 	return c.config.productVariables.BuildBrokenDontCheckSystemSdk
 }
 
+func (c *deviceConfig) BuildBrokenDupSysprop() bool {
+	return c.config.productVariables.BuildBrokenDupSysprop
+}
+
 func (c *config) BuildWarningBadOptionalUsesLibsAllowlist() []string {
 	return c.productVariables.BuildWarningBadOptionalUsesLibsAllowlist
 }
@@ -1980,17 +1961,6 @@
 	c.productVariables.Build_from_text_stub = boolPtr(b)
 }
 
-func (c *config) SetApiLibraries(libs []string) {
-	c.apiLibraries = make(map[string]struct{})
-	for _, lib := range libs {
-		c.apiLibraries[lib] = struct{}{}
-	}
-}
-
-func (c *config) GetApiLibraries() map[string]struct{} {
-	return c.apiLibraries
-}
-
 func (c *deviceConfig) CheckVendorSeappViolations() bool {
 	return Bool(c.config.productVariables.CheckVendorSeappViolations)
 }
@@ -2000,10 +1970,18 @@
 	return val, ok
 }
 
+func (c *config) UseOptimizedResourceShrinkingByDefault() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT")
+}
+
 func (c *config) UseResourceProcessorByDefault() bool {
 	return c.productVariables.GetBuildFlagBool("RELEASE_USE_RESOURCE_PROCESSOR_BY_DEFAULT")
 }
 
+func (c *config) UseTransitiveJarsInClasspath() bool {
+	return c.productVariables.GetBuildFlagBool("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH")
+}
+
 var (
 	mainlineApexContributionBuildFlagsToApexNames = map[string]string{
 		"RELEASE_APEX_CONTRIBUTIONS_ADBD":                    "com.android.adbd",
@@ -2078,3 +2056,47 @@
 func (c *config) OemProperties() []string {
 	return c.productVariables.OemProperties
 }
+
+func (c *config) UseDebugArt() bool {
+	if c.productVariables.ArtTargetIncludeDebugBuild != nil {
+		return Bool(c.productVariables.ArtTargetIncludeDebugBuild)
+	}
+
+	return Bool(c.productVariables.Eng)
+}
+
+func (c *config) SystemPropFiles(ctx PathContext) Paths {
+	return PathsForSource(ctx, c.productVariables.SystemPropFiles)
+}
+
+func (c *config) SystemExtPropFiles(ctx PathContext) Paths {
+	return PathsForSource(ctx, c.productVariables.SystemExtPropFiles)
+}
+
+func (c *config) ProductPropFiles(ctx PathContext) Paths {
+	return PathsForSource(ctx, c.productVariables.ProductPropFiles)
+}
+
+func (c *config) OdmPropFiles(ctx PathContext) Paths {
+	return PathsForSource(ctx, c.productVariables.OdmPropFiles)
+}
+
+func (c *config) EnableUffdGc() string {
+	return String(c.productVariables.EnableUffdGc)
+}
+
+func (c *config) DeviceFrameworkCompatibilityMatrixFile() []string {
+	return c.productVariables.DeviceFrameworkCompatibilityMatrixFile
+}
+
+func (c *config) DeviceProductCompatibilityMatrixFile() []string {
+	return c.productVariables.DeviceProductCompatibilityMatrixFile
+}
+
+func (c *config) BoardAvbEnable() bool {
+	return Bool(c.productVariables.BoardAvbEnable)
+}
+
+func (c *config) BoardAvbSystemAddHashtreeFooterArgs() []string {
+	return c.productVariables.BoardAvbSystemAddHashtreeFooterArgs
+}
diff --git a/android/config_test.go b/android/config_test.go
index 7d327a2..7732168 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -125,6 +125,38 @@
 	}
 }
 
+func TestReleaseAconfigExtraReleaseConfigs(t *testing.T) {
+	testCases := []struct {
+		name     string
+		flag     string
+		expected []string
+	}{
+		{
+			name:     "empty",
+			flag:     "",
+			expected: []string{},
+		},
+		{
+			name:     "specified",
+			flag:     "bar foo",
+			expected: []string{"bar", "foo"},
+		},
+		{
+			name:     "duplicates",
+			flag:     "foo bar foo",
+			expected: []string{"foo", "bar"},
+		},
+	}
+
+	for _, tc := range testCases {
+		fixture := GroupFixturePreparers(
+			PrepareForTestWithBuildFlag("RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS", tc.flag),
+		)
+		actual := fixture.RunTest(t).Config.ReleaseAconfigExtraReleaseConfigs()
+		AssertArrayString(t, tc.name, tc.expected, actual)
+	}
+}
+
 func TestConfiguredJarList(t *testing.T) {
 	list1 := CreateTestConfiguredJarList([]string{"apex1:jarA"})
 
diff --git a/android/configurable_properties.go b/android/configurable_properties.go
index dad42fa..2c794a1 100644
--- a/android/configurable_properties.go
+++ b/android/configurable_properties.go
@@ -26,3 +26,9 @@
 		resultCases,
 	)
 }
+
+func NewSimpleConfigurable[T proptools.ConfigurableElements](value T) proptools.Configurable[T] {
+	return proptools.NewConfigurable(nil, []proptools.ConfigurableCase[T]{
+		proptools.NewConfigurableCase(nil, &value),
+	})
+}
diff --git a/android/container.go b/android/container.go
new file mode 100644
index 0000000..c048d6c
--- /dev/null
+++ b/android/container.go
@@ -0,0 +1,506 @@
+// 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"
+	"reflect"
+	"slices"
+	"strings"
+
+	"github.com/google/blueprint"
+)
+
+// ----------------------------------------------------------------------------
+// Start of the definitions of exception functions and the lookup table.
+//
+// Functions cannot be used as a value passed in providers, because functions are not
+// hashable. As a workaround, the [exceptionHandleFuncLabel] enum values are passed using providers,
+// and the corresponding functions are called from [exceptionHandleFunctionsTable] map.
+// ----------------------------------------------------------------------------
+
+type exceptionHandleFunc func(ModuleContext, Module, Module) bool
+
+type StubsAvailableModule interface {
+	IsStubsModule() bool
+}
+
+// Returns true if the dependency module is a stubs module
+var depIsStubsModule exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
+	if stubsModule, ok := dep.(StubsAvailableModule); ok {
+		return stubsModule.IsStubsModule()
+	}
+	return false
+}
+
+// Returns true if the dependency module belongs to any of the apexes.
+var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _, dep Module) bool {
+	depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
+	return InList(ApexContainer, depContainersInfo.belongingContainers)
+}
+
+// Returns true if the module and the dependent module belongs to common apexes.
+var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m, dep Module) bool {
+	mContainersInfo, _ := getContainerModuleInfo(mctx, m)
+	depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
+
+	return HasIntersection(mContainersInfo.ApexNames(), depContainersInfo.ApexNames())
+}
+
+// Returns true when all apexes that the module belongs to are non updatable.
+// For an apex module to be allowed to depend on a non-apex partition module,
+// all apexes that the module belong to must be non updatable.
+var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m, _ Module) bool {
+	mContainersInfo, _ := getContainerModuleInfo(mctx, m)
+
+	return !mContainersInfo.UpdatableApex()
+}
+
+// Returns true if the dependency is added via dependency tags that are not used to tag dynamic
+// dependency tags.
+var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
+	mInstallable, _ := m.(InstallableModule)
+	depTag := ctx.OtherModuleDependencyTag(dep)
+	return !InList(depTag, mInstallable.DynamicDependencyTags())
+}
+
+// Returns true if the dependency is added via dependency tags that are not used to tag static
+// or dynamic dependency tags. These dependencies do not affect the module in compile time or in
+// runtime, thus are not significant enough to raise an error.
+var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
+	mInstallable, _ := m.(InstallableModule)
+	depTag := ctx.OtherModuleDependencyTag(dep)
+	return !InList(depTag, append(mInstallable.StaticDependencyTags(), mInstallable.DynamicDependencyTags()...))
+}
+
+var globallyAllowlistedDependencies = []string{
+	// Modules that provide annotations used within the platform and apexes.
+	"aconfig-annotations-lib",
+	"framework-annotations-lib",
+	"unsupportedappusage",
+
+	// 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",
+
+	// 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
+	// dependency specifies sdk_version.
+	"framework-res",
+
+	// jacocoagent is implicitly added as a dependency in coverage builds, and is not installed
+	// on the device.
+	"jacocoagent",
+}
+
+// Returns true when the dependency is globally allowlisted for inter-container dependency
+var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
+	return InList(dep.Name(), globallyAllowlistedDependencies)
+}
+
+// Labels of exception functions, which are used to determine special dependencies that allow
+// otherwise restricted inter-container dependencies
+type exceptionHandleFuncLabel int
+
+const (
+	checkStubs exceptionHandleFuncLabel = iota
+	checkApexModule
+	checkInCommonApexes
+	checkApexIsNonUpdatable
+	checkNotDynamicDepTag
+	checkNotStaticOrDynamicDepTag
+	checkGlobalAllowlistedDep
+)
+
+// Map of [exceptionHandleFuncLabel] to the [exceptionHandleFunc]
+var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]exceptionHandleFunc{
+	checkStubs:                    depIsStubsModule,
+	checkApexModule:               depIsApexModule,
+	checkInCommonApexes:           belongsToCommonApexes,
+	checkApexIsNonUpdatable:       belongsToNonUpdatableApex,
+	checkNotDynamicDepTag:         depIsNotDynamicDepTag,
+	checkNotStaticOrDynamicDepTag: depIsNotStaticOrDynamicDepTag,
+	checkGlobalAllowlistedDep:     depIsGloballyAllowlisted,
+}
+
+// ----------------------------------------------------------------------------
+// Start of the definitions of container determination functions.
+//
+// Similar to the above section, below defines the functions used to determine
+// the container of each modules.
+// ----------------------------------------------------------------------------
+
+type containerBoundaryFunc func(mctx ModuleContext) bool
+
+var vendorContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	m, ok := mctx.Module().(ImageInterface)
+	return mctx.Module().InstallInVendor() || (ok && m.VendorVariantNeeded(mctx))
+}
+
+var systemContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	module := mctx.Module()
+
+	return !module.InstallInTestcases() &&
+		!module.InstallInData() &&
+		!module.InstallInRamdisk() &&
+		!module.InstallInVendorRamdisk() &&
+		!module.InstallInDebugRamdisk() &&
+		!module.InstallInRecovery() &&
+		!module.InstallInVendor() &&
+		!module.InstallInOdm() &&
+		!module.InstallInProduct() &&
+		determineModuleKind(module.base(), mctx.blueprintBaseModuleContext()) == platformModule
+}
+
+var productContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	m, ok := mctx.Module().(ImageInterface)
+	return mctx.Module().InstallInProduct() || (ok && m.ProductVariantNeeded(mctx))
+}
+
+var apexContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	_, ok := ModuleProvider(mctx, AllApexInfoProvider)
+	return ok
+}
+
+var ctsContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	props := mctx.Module().GetProperties()
+	for _, prop := range props {
+		val := reflect.ValueOf(prop).Elem()
+		if val.Kind() == reflect.Struct {
+			testSuites := val.FieldByName("Test_suites")
+			if testSuites.IsValid() && testSuites.Kind() == reflect.Slice && slices.Contains(testSuites.Interface().([]string), "cts") {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+type unstableInfo struct {
+	// Determines if the module contains the private APIs of the platform.
+	ContainsPlatformPrivateApis bool
+}
+
+var unstableInfoProvider = blueprint.NewProvider[unstableInfo]()
+
+func determineUnstableModule(mctx ModuleContext) bool {
+	module := mctx.Module()
+	unstableModule := module.Name() == "framework-minus-apex"
+	if installable, ok := module.(InstallableModule); ok {
+		for _, staticDepTag := range installable.StaticDependencyTags() {
+			mctx.VisitDirectDepsWithTag(staticDepTag, func(dep Module) {
+				if unstableInfo, ok := OtherModuleProvider(mctx, dep, unstableInfoProvider); ok {
+					unstableModule = unstableModule || unstableInfo.ContainsPlatformPrivateApis
+				}
+			})
+		}
+	}
+	return unstableModule
+}
+
+var unstableContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	return determineUnstableModule(mctx)
+}
+
+// Map of [*container] to the [containerBoundaryFunc]
+var containerBoundaryFunctionsTable = map[*container]containerBoundaryFunc{
+	VendorContainer:   vendorContainerBoundaryFunc,
+	SystemContainer:   systemContainerBoundaryFunc,
+	ProductContainer:  productContainerBoundaryFunc,
+	ApexContainer:     apexContainerBoundaryFunc,
+	CtsContainer:      ctsContainerBoundaryFunc,
+	UnstableContainer: unstableContainerBoundaryFunc,
+}
+
+// ----------------------------------------------------------------------------
+// End of the definitions of container determination functions.
+// ----------------------------------------------------------------------------
+
+type InstallableModule interface {
+	StaticDependencyTags() []blueprint.DependencyTag
+	DynamicDependencyTags() []blueprint.DependencyTag
+}
+
+type restriction struct {
+	// container of the dependency
+	dependency *container
+
+	// Error message to be emitted to the user when the dependency meets this restriction
+	errorMessage string
+
+	// List of labels of allowed exception functions that allows bypassing this restriction.
+	// If any of the functions mapped to each labels returns true, this dependency would be
+	// considered allowed and an error will not be thrown.
+	allowedExceptions []exceptionHandleFuncLabel
+}
+type container struct {
+	// The name of the container i.e. partition, api domain
+	name string
+
+	// Map of dependency restricted containers.
+	restricted []restriction
+}
+
+var (
+	VendorContainer = &container{
+		name:       VendorVariation,
+		restricted: nil,
+	}
+
+	SystemContainer = &container{
+		name: "system",
+		restricted: []restriction{
+			{
+				dependency: VendorContainer,
+				errorMessage: "Module belonging to the system partition other than HALs is " +
+					"not allowed to depend on the vendor partition module, in order to support " +
+					"independent development/update cycles and to support the Generic System " +
+					"Image. Try depending on HALs, VNDK or AIDL instead.",
+				allowedExceptions: []exceptionHandleFuncLabel{
+					checkStubs,
+					checkNotDynamicDepTag,
+					checkGlobalAllowlistedDep,
+				},
+			},
+		},
+	}
+
+	ProductContainer = &container{
+		name: ProductVariation,
+		restricted: []restriction{
+			{
+				dependency: VendorContainer,
+				errorMessage: "Module belonging to the product partition is not allowed to " +
+					"depend on the vendor partition module, as this may lead to security " +
+					"vulnerabilities. Try depending on the HALs or utilize AIDL instead.",
+				allowedExceptions: []exceptionHandleFuncLabel{
+					checkStubs,
+					checkNotDynamicDepTag,
+					checkGlobalAllowlistedDep,
+				},
+			},
+		},
+	}
+
+	ApexContainer = initializeApexContainer()
+
+	CtsContainer = &container{
+		name: "cts",
+		restricted: []restriction{
+			{
+				dependency: UnstableContainer,
+				errorMessage: "CTS module should not depend on the modules that contain the " +
+					"platform implementation details, including \"framework\". Depending on these " +
+					"modules may lead to disclosure of implementation details and regression " +
+					"due to API changes across platform versions. Try depending on the stubs instead " +
+					"and ensure that the module sets an appropriate 'sdk_version'.",
+				allowedExceptions: []exceptionHandleFuncLabel{
+					checkStubs,
+					checkNotStaticOrDynamicDepTag,
+					checkGlobalAllowlistedDep,
+				},
+			},
+		},
+	}
+
+	// Container signifying that the module contains unstable platform private APIs
+	UnstableContainer = &container{
+		name:       "unstable",
+		restricted: nil,
+	}
+
+	allContainers = []*container{
+		VendorContainer,
+		SystemContainer,
+		ProductContainer,
+		ApexContainer,
+		CtsContainer,
+		UnstableContainer,
+	}
+)
+
+func initializeApexContainer() *container {
+	apexContainer := &container{
+		name: "apex",
+		restricted: []restriction{
+			{
+				dependency: SystemContainer,
+				errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " +
+					"modules belonging to the system partition. Either statically depend on the " +
+					"module or convert the depending module to java_sdk_library and depend on " +
+					"the stubs.",
+				allowedExceptions: []exceptionHandleFuncLabel{
+					checkStubs,
+					checkApexModule,
+					checkInCommonApexes,
+					checkApexIsNonUpdatable,
+					checkNotStaticOrDynamicDepTag,
+					checkGlobalAllowlistedDep,
+				},
+			},
+		},
+	}
+
+	apexContainer.restricted = append(apexContainer.restricted, restriction{
+		dependency: apexContainer,
+		errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " +
+			"modules belonging to other Apex(es). Either include the depending " +
+			"module in the Apex or convert the depending module to java_sdk_library " +
+			"and depend on its stubs.",
+		allowedExceptions: []exceptionHandleFuncLabel{
+			checkStubs,
+			checkInCommonApexes,
+			checkNotStaticOrDynamicDepTag,
+			checkGlobalAllowlistedDep,
+		},
+	})
+
+	return apexContainer
+}
+
+type ContainersInfo struct {
+	belongingContainers []*container
+
+	belongingApexes []ApexInfo
+}
+
+func (c *ContainersInfo) BelongingContainers() []*container {
+	return c.belongingContainers
+}
+
+func (c *ContainersInfo) ApexNames() (ret []string) {
+	for _, apex := range c.belongingApexes {
+		ret = append(ret, apex.InApexModules...)
+	}
+	slices.Sort(ret)
+	return ret
+}
+
+// Returns true if any of the apex the module belongs to is updatable.
+func (c *ContainersInfo) UpdatableApex() bool {
+	for _, apex := range c.belongingApexes {
+		if apex.Updatable {
+			return true
+		}
+	}
+	return false
+}
+
+var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]()
+
+func satisfyAllowedExceptions(ctx ModuleContext, allowedExceptionLabels []exceptionHandleFuncLabel, m, dep Module) bool {
+	for _, label := range allowedExceptionLabels {
+		if exceptionHandleFunctionsTable[label](ctx, m, dep) {
+			return true
+		}
+	}
+	return false
+}
+
+func (c *ContainersInfo) GetViolations(mctx ModuleContext, m, dep Module, depInfo ContainersInfo) []string {
+	var violations []string
+
+	// Any containers that the module belongs to but the dependency does not belong to must be examined.
+	_, containersUniqueToModule, _ := ListSetDifference(c.belongingContainers, depInfo.belongingContainers)
+
+	// Apex container should be examined even if both the module and the dependency belong to
+	// the apex container to check that the two modules belong to the same apex.
+	if InList(ApexContainer, c.belongingContainers) && !InList(ApexContainer, containersUniqueToModule) {
+		containersUniqueToModule = append(containersUniqueToModule, ApexContainer)
+	}
+
+	for _, containerUniqueToModule := range containersUniqueToModule {
+		for _, restriction := range containerUniqueToModule.restricted {
+			if InList(restriction.dependency, depInfo.belongingContainers) {
+				if !satisfyAllowedExceptions(mctx, restriction.allowedExceptions, m, dep) {
+					violations = append(violations, restriction.errorMessage)
+				}
+			}
+		}
+	}
+
+	return violations
+}
+
+func generateContainerInfo(ctx ModuleContext) ContainersInfo {
+	var containers []*container
+
+	for _, cnt := range allContainers {
+		if containerBoundaryFunctionsTable[cnt](ctx) {
+			containers = append(containers, cnt)
+		}
+	}
+
+	var belongingApexes []ApexInfo
+	if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
+		belongingApexes = apexInfo.ApexInfos
+	}
+
+	return ContainersInfo{
+		belongingContainers: containers,
+		belongingApexes:     belongingApexes,
+	}
+}
+
+func getContainerModuleInfo(ctx ModuleContext, module Module) (ContainersInfo, bool) {
+	if ctx.Module() == module {
+		return ctx.getContainersInfo(), true
+	}
+
+	return OtherModuleProvider(ctx, module, ContainersInfoProvider)
+}
+
+func setContainerInfo(ctx ModuleContext) {
+	// Required to determine the unstable container. This provider is set here instead of the
+	// unstableContainerBoundaryFunc in order to prevent setting the provider multiple times.
+	SetProvider(ctx, unstableInfoProvider, unstableInfo{
+		ContainsPlatformPrivateApis: determineUnstableModule(ctx),
+	})
+
+	if _, ok := ctx.Module().(InstallableModule); ok {
+		containersInfo := generateContainerInfo(ctx)
+		ctx.setContainersInfo(containersInfo)
+		SetProvider(ctx, ContainersInfoProvider, containersInfo)
+	}
+}
+
+func checkContainerViolations(ctx ModuleContext) {
+	if _, ok := ctx.Module().(InstallableModule); ok {
+		containersInfo, _ := getContainerModuleInfo(ctx, ctx.Module())
+		ctx.VisitDirectDepsIgnoreBlueprint(func(dep Module) {
+			if !dep.Enabled(ctx) {
+				return
+			}
+
+			// Pre-existing violating dependencies are tracked in containerDependencyViolationAllowlist.
+			// If this dependency is allowlisted, do not check for violation.
+			// If not, check if this dependency matches any restricted dependency and
+			// satisfies any exception functions, which allows bypassing the
+			// restriction. If all of the exceptions are not satisfied, throw an error.
+			if depContainersInfo, ok := getContainerModuleInfo(ctx, dep); ok {
+				if allowedViolations, ok := ContainerDependencyViolationAllowlist[ctx.ModuleName()]; ok && InList(dep.Name(), allowedViolations) {
+					return
+				} else {
+					violations := containersInfo.GetViolations(ctx, ctx.Module(), dep, depContainersInfo)
+					if len(violations) > 0 {
+						errorMessage := fmt.Sprintf("%s cannot depend on %s. ", ctx.ModuleName(), dep.Name())
+						errorMessage += strings.Join(violations, " ")
+						ctx.ModuleErrorf(errorMessage)
+					}
+				}
+			}
+		})
+	}
+}
diff --git a/android/container_violations.go b/android/container_violations.go
new file mode 100644
index 0000000..efbc8da
--- /dev/null
+++ b/android/container_violations.go
@@ -0,0 +1,1145 @@
+// 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
+
+var ContainerDependencyViolationAllowlist = map[string][]string{
+	"adservices-service-core": {
+		"gson", // apex [com.android.adservices, com.android.extservices] -> apex [com.android.virt]
+	},
+
+	"android.car-module.impl": {
+		"modules-utils-preconditions", // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi]
+	},
+
+	"AppInstalledOnMultipleUsers": {
+		"framework", // cts -> unstable
+	},
+
+	"art-aconfig-flags-java-lib": {
+		"framework-api-annotations-lib", // apex [com.android.art, com.android.art.debug, com.android.art.testing, test_imgdiag_com.android.art, test_jitzygote_com.android.art] -> system
+	},
+
+	"Bluetooth": {
+		"app-compat-annotations",         // apex [com.android.btservices] -> system
+		"framework-bluetooth-pre-jarjar", // apex [com.android.btservices] -> system
+	},
+
+	"bluetooth-nano-protos": {
+		"libprotobuf-java-nano", // apex [com.android.btservices] -> apex [com.android.wifi, test_com.android.wifi]
+	},
+
+	"bluetooth.change-ids": {
+		"app-compat-annotations", // apex [com.android.btservices] -> system
+	},
+
+	"CarServiceUpdatable": {
+		"modules-utils-os",                    // apex [com.android.car.framework] -> apex [com.android.permission, test_com.android.permission]
+		"modules-utils-preconditions",         // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi]
+		"modules-utils-shell-command-handler", // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.art, com.android.art.debug, com.android.art.testing, com.android.btservices, com.android.configinfrastructure, com.android.mediaprovider, com.android.nfcservices, com.android.permission, com.android.scheduling, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.mediaprovider, test_com.android.permission, test_com.android.wifi, test_imgdiag_com.android.art, test_jitzygote_com.android.art]
+	},
+
+	"cellbroadcastreceiver_aconfig_flags_lib": {
+		"ext",       // apex [com.android.cellbroadcast, test_com.android.cellbroadcast] -> system
+		"framework", // apex [com.android.cellbroadcast, test_com.android.cellbroadcast] -> system
+	},
+
+	"connectivity-net-module-utils-bpf": {
+		"net-utils-device-common-struct-base", // apex [com.android.tethering] -> system
+	},
+
+	"conscrypt-aconfig-flags-lib": {
+		"aconfig-annotations-lib-sdk-none", // apex [com.android.conscrypt, test_com.android.conscrypt] -> system
+	},
+
+	"cronet_aml_base_base_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+		"jsr305", // apex [com.android.tethering] -> apex [com.android.adservices, com.android.devicelock, com.android.extservices, com.android.healthfitness, com.android.media, com.android.mediaprovider, test_com.android.media, test_com.android.mediaprovider]
+	},
+
+	"cronet_aml_build_android_build_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_components_cronet_android_base_feature_overrides_java_proto": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_components_cronet_android_cronet_api_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_components_cronet_android_cronet_impl_common_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_components_cronet_android_cronet_impl_native_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+		"jsr305", // apex [com.android.tethering] -> apex [com.android.adservices, com.android.devicelock, com.android.extservices, com.android.healthfitness, com.android.media, com.android.mediaprovider, test_com.android.media, test_com.android.mediaprovider]
+	},
+
+	"cronet_aml_components_cronet_android_cronet_jni_registration_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_components_cronet_android_cronet_shared_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_components_cronet_android_cronet_stats_log_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_components_cronet_android_cronet_urlconnection_impl_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_components_cronet_android_flags_java_proto": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_components_cronet_android_request_context_config_java_proto": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_net_android_net_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+		"jsr305", // apex [com.android.tethering] -> apex [com.android.adservices, com.android.devicelock, com.android.extservices, com.android.healthfitness, com.android.media, com.android.mediaprovider, test_com.android.media, test_com.android.mediaprovider]
+	},
+
+	"cronet_aml_net_android_net_thread_stats_uid_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_third_party_jni_zero_jni_zero_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"cronet_aml_url_url_java": {
+		"framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system
+	},
+
+	"CtsAdservicesHostTestApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAdServicesNotInAllowListEndToEndTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAdServicesPermissionsAppOptOutEndToEndTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAdServicesPermissionsNoPermEndToEndTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAdServicesPermissionsValidEndToEndTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAlarmManagerTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAndroidAppTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAppExitTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAppFgsStartTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAppFgsTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAppFunctionTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAppOpsTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAppSearchTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAppStartTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAppTestStubsApp2": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsAudioHostTestApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsBackgroundActivityAppAllowCrossUidFlagDefault": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsBatterySavingTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsBluetoothTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsBootDisplayModeApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsBroadcastTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsBRSTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsCompanionDeviceManagerCoreTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsCompanionDeviceManagerMultiProcessTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsCompanionDeviceManagerUiAutomationTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsContentSuggestionsTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsContentTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsCredentialManagerBackupRestoreApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsCrossProfileEnabledApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsCrossProfileEnabledNoPermsApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsCrossProfileNotEnabledApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsCrossProfileUserEnabledApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDeviceAndProfileOwnerApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDeviceAndProfileOwnerApp23": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDeviceAndProfileOwnerApp25": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDeviceAndProfileOwnerApp30": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDeviceLockTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDeviceOwnerApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDevicePolicySimTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDevicePolicyTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDocumentContentTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDreamsTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsDrmTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsEmptyTestApp_RejectedByVerifier": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsEphemeralTestsEphemeralApp1": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsFgsBootCompletedTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsFgsBootCompletedTestCasesApi35": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsFgsStartTestHelperApi34": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsFgsStartTestHelperCurrent": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsFgsTimeoutTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsFileDescriptorTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsFingerprintTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsHostsideCompatChangeTestsApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsHostsideNetworkPolicyTestsApp2": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsIdentityTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsIkeTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsInstalledLoadingProgressDeviceTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsInstantAppTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsIntentSenderApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsJobSchedulerTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsKeystoreTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsLegacyNotification27TestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsLibcoreTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsLibcoreWycheproofConscryptTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsListeningPortsTest": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsLocationCoarseTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsLocationFineTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsLocationNoneTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsLocationPrivilegedTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsManagedProfileApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaAudioTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaBetterTogetherTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaCodecTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaDecoderTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaDrmFrameworkTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaEncoderTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaExtractorTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaMiscTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaMuxerTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaPerformanceClassTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaPlayerTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaProjectionSDK33TestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaProjectionSDK34TestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaProjectionTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaProviderTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaProviderTranscodeTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaRecorderTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaRouterHostSideTestBluetoothPermissionsApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaRouterHostSideTestMediaRoutingControlApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaRouterHostSideTestModifyAudioRoutingApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMediaV2TestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMimeMapTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsModifyQuietModeEnabledApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsMusicRecognitionTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsNativeMediaAAudioTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsNetTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsNetTestCasesLegacyApi22": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsNetTestCasesMaxTargetSdk30": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsNetTestCasesMaxTargetSdk31": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsNetTestCasesMaxTargetSdk33": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsNetTestCasesUpdateStatsPermission": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsNfcTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsOnDeviceIntelligenceServiceTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsOnDevicePersonalizationTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsPackageInstallerApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsPackageManagerTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsPackageSchemeTestsWithoutVisibility": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsPackageSchemeTestsWithVisibility": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsPackageWatchdogTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsPermissionsSyncTestApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsPreservedSettingsApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsProtoTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsProviderTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsProxyMediaRouterTestHelperApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsRebootReadinessTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsResourcesLoaderTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsResourcesTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSandboxedAdIdManagerTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSandboxedAppSetIdManagerTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSandboxedFledgeManagerTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSandboxedMeasurementManagerTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSandboxedTopicsManagerTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSdkExtensionsTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSdkSandboxInprocessTests": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSecureElementTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSecurityTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSelinuxEphemeralTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSelinuxTargetSdk25TestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSelinuxTargetSdk27TestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSelinuxTargetSdk28TestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSelinuxTargetSdk29TestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSelinuxTargetSdk30TestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSelinuxTargetSdkCurrentTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSettingsDeviceOwnerApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSharedUserMigrationTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsShortFgsTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSimRestrictedApisTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSliceTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSpeechTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsStatsSecurityApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSuspendAppsTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsSystemUiTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsTareTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsTelephonyTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsTetheringTest": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsThreadNetworkTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsTvInputTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsTvTunerTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsUsageStatsTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsUsbManagerTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsUserRestrictionTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsUtilTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsUwbTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsVcnTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsVideoCodecTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsVideoTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsViewReceiveContentTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsVirtualDevicesAppLaunchTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsVirtualDevicesAudioTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsVirtualDevicesCameraTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsVirtualDevicesSensorTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsVirtualDevicesTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsWearableSensingServiceTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsWebViewCompatChangeApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsWidgetTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsWidgetTestCases29": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsWifiNonUpdatableTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsWifiTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsWindowManagerExternalApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsWindowManagerTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"CtsZipValidateApp": {
+		"framework", // cts -> unstable
+	},
+
+	"CVE-2021-0965": {
+		"framework", // cts -> unstable
+	},
+
+	"device_config_reboot_flags_java_lib": {
+		"ext",       // apex [com.android.configinfrastructure] -> system
+		"framework", // apex [com.android.configinfrastructure] -> system
+	},
+
+	"devicelockcontroller-lib": {
+		"modules-utils-expresslog", // apex [com.android.devicelock] -> apex [com.android.btservices, com.android.car.framework]
+	},
+
+	"FederatedCompute": {
+		"auto_value_annotations", // apex [com.android.ondevicepersonalization] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
+	},
+
+	"framework-adservices.impl": {
+		"adservices_flags_lib", // apex [com.android.adservices, com.android.extservices] -> system
+	},
+
+	"framework-bluetooth.impl": {
+		"app-compat-annotations", // apex [com.android.btservices] -> system
+	},
+
+	"framework-configinfrastructure.impl": {
+		"configinfra_framework_flags_java_lib", // apex [com.android.configinfrastructure] -> system
+	},
+
+	"framework-connectivity-t.impl": {
+		"app-compat-annotations",            // apex [com.android.tethering] -> system
+		"framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system
+	},
+
+	"framework-connectivity.impl": {
+		"app-compat-annotations", // apex [com.android.tethering] -> system
+	},
+
+	"framework-ondevicepersonalization.impl": {
+		"app-compat-annotations",            // apex [com.android.ondevicepersonalization] -> system
+		"ondevicepersonalization_flags_lib", // apex [com.android.ondevicepersonalization] -> system
+	},
+
+	"framework-pdf-v.impl": {
+		"app-compat-annotations",      // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> system
+		"modules-utils-preconditions", // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi]
+	},
+
+	"framework-pdf.impl": {
+		"modules-utils-preconditions", // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi]
+	},
+
+	"framework-permission-s.impl": {
+		"app-compat-annotations", // apex [com.android.permission, test_com.android.permission] -> system
+	},
+
+	"framework-wifi.impl": {
+		"aconfig_storage_reader_java", // apex [com.android.wifi, test_com.android.wifi] -> system
+		"app-compat-annotations",      // apex [com.android.wifi, test_com.android.wifi] -> system
+	},
+
+	"grpc-java-core-internal": {
+		"gson",             // apex [com.android.adservices, com.android.devicelock, com.android.extservices] -> apex [com.android.virt]
+		"perfmark-api-lib", // apex [com.android.adservices, com.android.devicelock, com.android.extservices] -> system
+	},
+
+	"httpclient_impl": {
+		"httpclient_api", // apex [com.android.tethering] -> system
+	},
+
+	"IncrementalTestAppValidator": {
+		"framework", // cts -> unstable
+	},
+
+	"libcore-aconfig-flags-lib": {
+		"framework-api-annotations-lib", // apex [com.android.art, com.android.art.debug, com.android.art.testing, test_imgdiag_com.android.art, test_jitzygote_com.android.art] -> system
+	},
+
+	"loadlibrarytest_product_app": {
+		"libnativeloader_vendor_shared_lib", // product -> vendor
+	},
+
+	"loadlibrarytest_testlib": {
+		"libnativeloader_vendor_shared_lib", // system -> vendor
+	},
+
+	"MctsMediaBetterTogetherTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaCodecTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaDecoderTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaDrmFrameworkTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaEncoderTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaExtractorTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaMiscTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaMuxerTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaPlayerTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaRecorderTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaTranscodingTestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MctsMediaV2TestCases": {
+		"framework", // cts -> unstable
+	},
+
+	"MediaProvider": {
+		"app-compat-annotations", // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> system
+	},
+
+	"mediaprovider_flags_java_lib": {
+		"ext",       // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> system
+		"framework", // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> system
+	},
+
+	"MockSatelliteGatewayServiceApp": {
+		"framework", // cts -> unstable
+	},
+
+	"MockSatelliteServiceApp": {
+		"framework", // cts -> unstable
+	},
+
+	"net-utils-device-common-netlink": {
+		"net-utils-device-common-struct-base", // apex [com.android.tethering] -> system
+	},
+
+	"net-utils-device-common-struct": {
+		"net-utils-device-common-struct-base", // apex [com.android.tethering] -> system
+	},
+
+	"NfcNciApex": {
+		"android.permission.flags-aconfig-java", // apex [com.android.nfcservices] -> apex [com.android.permission, test_com.android.permission]
+	},
+
+	"okhttp-norepackage": {
+		"okhttp-android-util-log", // apex [com.android.adservices, com.android.devicelock, com.android.extservices] -> system
+	},
+
+	"ondevicepersonalization-plugin-lib": {
+		"auto_value_annotations", // apex [com.android.ondevicepersonalization] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
+	},
+
+	"opencensus-java-api": {
+		"auto_value_annotations", // apex [com.android.devicelock] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
+	},
+
+	"PermissionController-lib": {
+		"safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system
+	},
+
+	"PlatformProperties": {
+		"sysprop-library-stub-platform", // apex [com.android.btservices, com.android.nfcservices, com.android.tethering, com.android.virt, com.android.wifi, test_com.android.wifi] -> system
+	},
+
+	"safety-center-config": {
+		"safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system
+	},
+
+	"safety-center-internal-data": {
+		"safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system
+	},
+
+	"safety-center-pending-intents": {
+		"safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system
+	},
+
+	"safety-center-persistence": {
+		"safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system
+	},
+
+	"safety-center-resources-lib": {
+		"safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system
+	},
+
+	"SdkSandboxManagerDisabledTests": {
+		"framework", // cts -> unstable
+	},
+
+	"SdkSandboxManagerTests": {
+		"framework", // cts -> unstable
+	},
+
+	"service-art.impl": {
+		"auto_value_annotations", // apex [com.android.art, com.android.art.debug, com.android.art.testing, test_imgdiag_com.android.art, test_jitzygote_com.android.art] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
+	},
+
+	"service-bluetooth-pre-jarjar": {
+		"framework-bluetooth-pre-jarjar", // apex [com.android.btservices] -> system
+		"service-bluetooth.change-ids",   // apex [com.android.btservices] -> system
+	},
+
+	"service-connectivity": {
+		"libprotobuf-java-nano", // apex [com.android.tethering] -> apex [com.android.wifi, test_com.android.wifi]
+	},
+
+	"service-connectivity-pre-jarjar": {
+		"framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system
+	},
+
+	"service-connectivity-protos": {
+		"libprotobuf-java-nano", // apex [com.android.tethering] -> apex [com.android.wifi, test_com.android.wifi]
+	},
+
+	"service-connectivity-tiramisu-pre-jarjar": {
+		"framework-connectivity-pre-jarjar",   // apex [com.android.tethering] -> system
+		"framework-connectivity-t-pre-jarjar", // apex [com.android.tethering] -> system
+	},
+
+	"service-entitlement": {
+		"auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
+	},
+
+	"service-entitlement-api": {
+		"auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
+	},
+
+	"service-entitlement-data": {
+		"auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
+	},
+
+	"service-entitlement-impl": {
+		"auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
+	},
+
+	"service-healthfitness.impl": {
+		"modules-utils-preconditions", // apex [com.android.healthfitness] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi]
+	},
+
+	"service-networksecurity-pre-jarjar": {
+		"framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system
+	},
+
+	"service-permission.impl": {
+		"jsr305",                    // apex [com.android.permission, test_com.android.permission] -> apex [com.android.adservices, com.android.devicelock, com.android.extservices, com.android.healthfitness, com.android.media, com.android.mediaprovider, test_com.android.media, test_com.android.mediaprovider]
+		"safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system
+	},
+
+	"service-remoteauth-pre-jarjar": {
+		"framework-connectivity-pre-jarjar",   // apex [com.android.tethering] -> system
+		"framework-connectivity-t-pre-jarjar", // apex [com.android.tethering] -> system
+	},
+
+	"service-thread-pre-jarjar": {
+		"framework-connectivity-pre-jarjar",   // apex [com.android.tethering] -> system
+		"framework-connectivity-t-pre-jarjar", // apex [com.android.tethering] -> system
+	},
+
+	"service-uwb-pre-jarjar": {
+		"framework-uwb-pre-jarjar", // apex [com.android.uwb] -> system
+	},
+
+	"service-wifi": {
+		"auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
+	},
+
+	"TelephonyDeviceTest": {
+		"framework", // cts -> unstable
+	},
+
+	"tensorflowlite_java": {
+		"android-support-annotations", // apex [com.android.adservices, com.android.extservices, com.android.ondevicepersonalization] -> system
+	},
+
+	"TestExternalImsServiceApp": {
+		"framework", // cts -> unstable
+	},
+
+	"TestSmsRetrieverApp": {
+		"framework", // cts -> unstable
+	},
+
+	"TetheringApiCurrentLib": {
+		"connectivity-internal-api-util", // apex [com.android.tethering] -> system
+	},
+
+	"TetheringNext": {
+		"connectivity-internal-api-util", // apex [com.android.tethering] -> system
+	},
+
+	"tetheringstatsprotos": {
+		"ext",       // apex [com.android.tethering] -> system
+		"framework", // apex [com.android.tethering] -> system
+	},
+
+	"uwb_aconfig_flags_lib": {
+		"ext",       // apex [com.android.uwb] -> system
+		"framework", // apex [com.android.uwb] -> system
+	},
+
+	"uwb_androidx_backend": {
+		"android-support-annotations", // apex [com.android.tethering] -> system
+	},
+
+	"wifi-service-pre-jarjar": {
+		"app-compat-annotations",    // apex [com.android.wifi, test_com.android.wifi] -> system
+		"auto_value_annotations",    // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus]
+		"framework-wifi-pre-jarjar", // apex [com.android.wifi, test_com.android.wifi] -> system
+		"jsr305",                    // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.devicelock, com.android.extservices, com.android.healthfitness, com.android.media, com.android.mediaprovider, test_com.android.media, test_com.android.mediaprovider]
+	},
+}
diff --git a/android/deapexer.go b/android/deapexer.go
index 61ae64e..4049d2b 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -15,7 +15,6 @@
 package android
 
 import (
-	"fmt"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -109,10 +108,6 @@
 	return i.exportedModuleNames
 }
 
-// Provider that can be used from within the `GenerateAndroidBuildActions` of a module that depends
-// on a `deapexer` module to retrieve its `DeapexerInfo`.
-var DeapexerProvider = blueprint.NewProvider[DeapexerInfo]()
-
 // NewDeapexerInfo creates and initializes a DeapexerInfo that is suitable
 // for use with a prebuilt_apex module.
 //
@@ -169,41 +164,6 @@
 	RequiresFilesFromPrebuiltApex()
 }
 
-// FindDeapexerProviderForModule searches through the direct dependencies of the current context
-// module for a DeapexerTag dependency and returns its DeapexerInfo. If a single nonambiguous
-// deapexer module isn't found then it returns it an error
-// clients should check the value of error and call ctx.ModuleErrof if a non nil error is received
-func FindDeapexerProviderForModule(ctx ModuleContext) (*DeapexerInfo, error) {
-	var di *DeapexerInfo
-	var err error
-	ctx.VisitDirectDepsWithTag(DeapexerTag, func(m Module) {
-		if err != nil {
-			// An err has been found. Do not visit further.
-			return
-		}
-		c, _ := OtherModuleProvider(ctx, m, DeapexerProvider)
-		p := &c
-		if di != nil {
-			// If two DeapexerInfo providers have been found then check if they are
-			// equivalent. If they are then use the selected one, otherwise fail.
-			if selected := equivalentDeapexerInfoProviders(di, p); selected != nil {
-				di = selected
-				return
-			}
-			err = fmt.Errorf("Multiple installable prebuilt APEXes provide ambiguous deapexers: %s and %s", di.ApexModuleName(), p.ApexModuleName())
-		}
-		di = p
-	})
-	if err != nil {
-		return nil, err
-	}
-	if di != nil {
-		return di, nil
-	}
-	ai, _ := ModuleProvider(ctx, ApexInfoProvider)
-	return nil, fmt.Errorf("No prebuilt APEX provides a deapexer module for APEX variant %s", ai.ApexVariationName)
-}
-
 // removeCompressedApexSuffix removes the _compressed suffix from the name if present.
 func removeCompressedApexSuffix(name string) string {
 	return strings.TrimSuffix(name, "_compressed")
diff --git a/android/defaults.go b/android/defaults.go
index ff79002..3d06c69 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -69,7 +69,7 @@
 
 	// Apply defaults from the supplied Defaults to the property structures supplied to
 	// setProperties(...).
-	applyDefaults(TopDownMutatorContext, []Defaults)
+	applyDefaults(BottomUpMutatorContext, []Defaults)
 
 	// Set the hook to be called after any defaults have been applied.
 	//
@@ -101,6 +101,7 @@
 // A restricted subset of context methods, similar to LoadHookContext.
 type DefaultableHookContext interface {
 	EarlyModuleContext
+	OtherModuleProviderContext
 
 	CreateModule(ModuleFactory, ...interface{}) Module
 	AddMissingDependencies(missingDeps []string)
@@ -209,7 +210,7 @@
 
 var _ Defaults = (*DefaultsModuleBase)(nil)
 
-func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext,
+func (defaultable *DefaultableModuleBase) applyDefaults(ctx BottomUpMutatorContext,
 	defaultsList []Defaults) {
 
 	for _, defaults := range defaultsList {
@@ -226,7 +227,7 @@
 // Product variable properties need special handling, the type of the filtered product variable
 // property struct may not be identical between the defaults module and the defaultable module.
 // Use PrependMatchingProperties to apply whichever properties match.
-func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx TopDownMutatorContext,
+func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx BottomUpMutatorContext,
 	defaults Defaults, defaultableProp interface{}) {
 	if defaultableProp == nil {
 		return
@@ -254,7 +255,7 @@
 	}
 }
 
-func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMutatorContext,
+func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx BottomUpMutatorContext,
 	defaults Defaults, defaultableProp interface{}) {
 
 	for _, def := range defaults.properties() {
@@ -273,7 +274,7 @@
 
 func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel()
-	ctx.TopDown("defaults", defaultsMutator).Parallel()
+	ctx.BottomUp("defaults", defaultsMutator).Parallel()
 }
 
 func defaultsDepsMutator(ctx BottomUpMutatorContext) {
@@ -282,9 +283,14 @@
 	}
 }
 
-func defaultsMutator(ctx TopDownMutatorContext) {
+func defaultsMutator(ctx BottomUpMutatorContext) {
 	if defaultable, ok := ctx.Module().(Defaultable); ok {
-		if len(defaultable.defaults().Defaults) > 0 {
+		if _, isDefaultsModule := ctx.Module().(Defaults); isDefaultsModule {
+			// Don't squash transitive defaults into defaults modules
+			return
+		}
+		defaults := defaultable.defaults().Defaults
+		if len(defaults) > 0 {
 			var defaultsList []Defaults
 			seen := make(map[Defaults]bool)
 
diff --git a/android/defs.go b/android/defs.go
index 78cdea2..9f3fb1e 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -16,7 +16,6 @@
 
 import (
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 )
 
 var (
@@ -120,8 +119,3 @@
 		return ctx.Config().RBEWrapper()
 	})
 }
-
-// GlobToListFileRule creates a rule that writes a list of files matching a pattern to a file.
-func GlobToListFileRule(ctx ModuleContext, pattern string, excludes []string, file WritablePath) {
-	bootstrap.GlobFile(ctx.blueprintModuleContext(), pattern, excludes, file.String())
-}
diff --git a/android/depset_generic.go b/android/depset_generic.go
index 45c1937..690987a 100644
--- a/android/depset_generic.go
+++ b/android/depset_generic.go
@@ -15,6 +15,9 @@
 package android
 
 import (
+	"bytes"
+	"encoding/gob"
+	"errors"
 	"fmt"
 )
 
@@ -65,6 +68,30 @@
 	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
diff --git a/android/image.go b/android/image.go
index 9cad056..6e5a551 100644
--- a/android/image.go
+++ b/android/image.go
@@ -14,11 +14,17 @@
 
 package android
 
-// ImageInterface is implemented by modules that need to be split by the imageMutator.
+// 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)
 
+	// VendorVariantNeeded should return true if the module needs a vendor variant (installed on the vendor image).
+	VendorVariantNeeded(ctx BaseModuleContext) bool
+
+	// ProductVariantNeeded should return true if the module needs a product variant (installed on the product image).
+	ProductVariantNeeded(ctx BaseModuleContext) bool
+
 	// CoreVariantNeeded should return true if the module needs a core variant (installed on the system image).
 	CoreVariantNeeded(ctx BaseModuleContext) bool
 
@@ -49,6 +55,14 @@
 }
 
 const (
+	// VendorVariation is the variant name used for /vendor code that does not
+	// compile against the VNDK.
+	VendorVariation string = "vendor"
+
+	// ProductVariation is the variant name used for /product code that does not
+	// compile against the VNDK.
+	ProductVariation string = "product"
+
 	// CoreVariation is the variant used for framework-private libraries, or
 	// SDK libraries. (which framework-private libraries can use), which
 	// will be installed to the system image.
@@ -67,18 +81,16 @@
 	DebugRamdiskVariation string = "debug_ramdisk"
 )
 
-// imageMutator creates variants for modules that implement the ImageInterface that
+// imageTransitionMutator creates variants for modules that implement the ImageInterface that
 // allow them to build differently for each partition (recovery, core, vendor, etc.).
-func imageMutator(ctx BottomUpMutatorContext) {
-	if ctx.Os() != Android {
-		return
-	}
+type imageTransitionMutator struct{}
 
-	if m, ok := ctx.Module().(ImageInterface); ok {
+func (imageTransitionMutator) Split(ctx BaseModuleContext) []string {
+	var variations []string
+
+	if m, ok := ctx.Module().(ImageInterface); ctx.Os() == Android && ok {
 		m.ImageMutatorBegin(ctx)
 
-		var variations []string
-
 		if m.CoreVariantNeeded(ctx) {
 			variations = append(variations, CoreVariation)
 		}
@@ -94,18 +106,38 @@
 		if m.RecoveryVariantNeeded(ctx) {
 			variations = append(variations, RecoveryVariation)
 		}
+		if m.VendorVariantNeeded(ctx) {
+			variations = append(variations, VendorVariation)
+		}
+		if m.ProductVariantNeeded(ctx) {
+			variations = append(variations, ProductVariation)
+		}
 
 		extraVariations := m.ExtraImageVariations(ctx)
 		variations = append(variations, extraVariations...)
+	}
 
-		if len(variations) == 0 {
-			return
-		}
+	if len(variations) == 0 {
+		variations = append(variations, "")
+	}
 
-		mod := ctx.CreateVariations(variations...)
-		for i, v := range variations {
-			mod[i].base().setImageVariation(v)
-			mod[i].(ImageInterface).SetImageVariation(ctx, v)
-		}
+	return variations
+}
+
+func (imageTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (imageTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	if _, ok := ctx.Module().(ImageInterface); ctx.Os() != Android || !ok {
+		return CoreVariation
+	}
+	return incomingVariation
+}
+
+func (imageTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+	ctx.Module().base().setImageVariation(variation)
+	if m, ok := ctx.Module().(ImageInterface); ok {
+		m.SetImageVariation(ctx, variation)
 	}
 }
diff --git a/android/init.go b/android/init.go
new file mode 100644
index 0000000..b462292
--- /dev/null
+++ b/android/init.go
@@ -0,0 +1,23 @@
+// 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 "encoding/gob"
+
+func init() {
+	gob.Register(ModuleOutPath{})
+	gob.Register(PhonyPath{})
+	gob.Register(unstableInfo{})
+}
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 3fea029..0ac975f 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -33,7 +33,7 @@
 	}, "args")
 )
 
-func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
+func buildLicenseMetadata(ctx *moduleContext, licenseMetadataFile WritablePath) {
 	base := ctx.Module().base()
 
 	if !base.Enabled(ctx) {
@@ -45,16 +45,15 @@
 	}
 
 	var outputFiles Paths
-	if outputFileProducer, ok := ctx.Module().(OutputFileProducer); ok {
-		outputFiles, _ = outputFileProducer.OutputFiles("")
+	if outputFiles, err := outputFilesForModule(ctx, ctx.Module(), ""); err == nil {
 		outputFiles = PathsIfNonNil(outputFiles...)
 	}
 
 	// Only pass the last installed file to isContainerFromFileExtensions so a *.zip file in test data
 	// doesn't mark the whole module as a container.
 	var installFiles InstallPaths
-	if len(base.installFiles) > 0 {
-		installFiles = InstallPaths{base.installFiles[len(base.installFiles)-1]}
+	if len(ctx.installFiles) > 0 {
+		installFiles = InstallPaths{ctx.installFiles[len(ctx.installFiles)-1]}
 	}
 
 	isContainer := isContainerFromFileExtensions(installFiles, outputFiles)
@@ -93,7 +92,7 @@
 
 			allDepMetadataArgs = append(allDepMetadataArgs, info.LicenseMetadataPath.String()+depAnnotations)
 
-			if depInstallFiles := dep.base().installFiles; len(depInstallFiles) > 0 {
+			if depInstallFiles := OtherModuleProviderOrDefault(ctx, dep, InstallFilesProvider).InstallFiles; len(depInstallFiles) > 0 {
 				allDepOutputFiles = append(allDepOutputFiles, depInstallFiles.Paths()...)
 			} else if depOutputFiles, err := outputFilesForModule(ctx, dep, ""); err == nil {
 				depOutputFiles = PathsIfNonNil(depOutputFiles...)
@@ -153,7 +152,7 @@
 
 	// Install map
 	args = append(args,
-		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.licenseInstallMap), "-m "))
+		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(ctx.licenseInstallMap), "-m "))
 
 	// Built files
 	if len(outputFiles) > 0 {
@@ -163,7 +162,7 @@
 
 	// Installed files
 	args = append(args,
-		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.installFiles.Strings()), "-i "))
+		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(ctx.installFiles.Strings()), "-i "))
 
 	if isContainer {
 		args = append(args, "--is_container")
diff --git a/android/logtags.go b/android/logtags.go
index d11cccf..7929057 100644
--- a/android/logtags.go
+++ b/android/logtags.go
@@ -42,7 +42,7 @@
 		if !module.ExportedToMake() {
 			return
 		}
-		if logtagsInfo, ok := SingletonModuleProvider(ctx, module, LogtagsProviderKey); ok {
+		if logtagsInfo, ok := OtherModuleProvider(ctx, module, LogtagsProviderKey); ok {
 			allLogtags = append(allLogtags, logtagsInfo.Logtags...)
 		}
 	})
diff --git a/android/makevars.go b/android/makevars.go
index f92f458..8305d8e 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -94,7 +94,7 @@
 	ModuleDir(module blueprint.Module) string
 	ModuleSubDir(module blueprint.Module) string
 	ModuleType(module blueprint.Module) string
-	moduleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool)
+	otherModuleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool)
 	BlueprintFile(module blueprint.Module) string
 
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
@@ -279,10 +279,11 @@
 		}
 
 		if m.ExportedToMake() {
-			katiInstalls = append(katiInstalls, m.base().katiInstalls...)
-			katiInitRcInstalls = append(katiInitRcInstalls, m.base().katiInitRcInstalls...)
-			katiVintfManifestInstalls = append(katiVintfManifestInstalls, m.base().katiVintfInstalls...)
-			katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...)
+			info := OtherModuleProviderOrDefault(ctx, m, InstallFilesProvider)
+			katiInstalls = append(katiInstalls, info.KatiInstalls...)
+			katiInitRcInstalls = append(katiInitRcInstalls, info.KatiInitRcInstalls...)
+			katiVintfManifestInstalls = append(katiVintfManifestInstalls, info.KatiVintfInstalls...)
+			katiSymlinks = append(katiSymlinks, info.KatiSymlinks...)
 		}
 	})
 
diff --git a/android/module.go b/android/module.go
index dc585d2..e2b7e11 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,9 +15,9 @@
 package android
 
 import (
-	"crypto/md5"
-	"encoding/hex"
-	"encoding/json"
+	"bytes"
+	"encoding/gob"
+	"errors"
 	"fmt"
 	"net/url"
 	"path/filepath"
@@ -26,8 +26,6 @@
 	"sort"
 	"strings"
 
-	"android/soong/bazel"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -60,7 +58,7 @@
 
 	base() *ModuleBase
 	Disable()
-	Enabled(ctx ConfigAndErrorContext) bool
+	Enabled(ctx ConfigurableEvaluatorContext) bool
 	Target() Target
 	MultiTargets() []Target
 
@@ -92,8 +90,6 @@
 	ReplacedByPrebuilt()
 	IsReplacedByPrebuilt() bool
 	ExportedToMake() bool
-	InitRc() Paths
-	VintfFragments() Paths
 	EffectiveLicenseKinds() []string
 	EffectiveLicenseFiles() Paths
 
@@ -113,18 +109,12 @@
 	// Get information about the properties that can contain visibility rules.
 	visibilityProperties() []visibilityProperty
 
-	RequiredModuleNames() []string
+	RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
+	VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string
 
-	FilesToInstall() InstallPaths
-	PackagingSpecs() []PackagingSpec
-
-	// TransitivePackagingSpecs returns the PackagingSpecs for this module and any transitive
-	// dependencies with dependency tags for which IsInstallDepNeeded() returns true.
-	TransitivePackagingSpecs() []PackagingSpec
-
-	ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator
+	ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator
 }
 
 // Qualified id for a module
@@ -249,31 +239,6 @@
 	return l[:k+1]
 }
 
-// soongConfigTrace holds all references to VendorVars. Uses []string for blueprint:"mutated"
-type soongConfigTrace struct {
-	Bools   []string `json:",omitempty"`
-	Strings []string `json:",omitempty"`
-	IsSets  []string `json:",omitempty"`
-}
-
-func (c *soongConfigTrace) isEmpty() bool {
-	return len(c.Bools) == 0 && len(c.Strings) == 0 && len(c.IsSets) == 0
-}
-
-// Returns hash of serialized trace records (empty string if there's no trace recorded)
-func (c *soongConfigTrace) hash() string {
-	// Use MD5 for speed. We don't care collision or preimage attack
-	if c.isEmpty() {
-		return ""
-	}
-	j, err := json.Marshal(c)
-	if err != nil {
-		panic(fmt.Errorf("json marshal of %#v failed: %#v", *c, err))
-	}
-	hash := md5.Sum(j)
-	return hex.EncodeToString(hash[:])
-}
-
 type nameProperties struct {
 	// The name of the module.  Must be unique across all modules.
 	Name *string
@@ -416,13 +381,13 @@
 	Native_bridge_supported *bool `android:"arch_variant"`
 
 	// init.rc files to be installed if this module is installed
-	Init_rc []string `android:"arch_variant,path"`
+	Init_rc proptools.Configurable[[]string] `android:"arch_variant,path"`
 
 	// VINTF manifest fragments to be installed if this module is installed
-	Vintf_fragments []string `android:"path"`
+	Vintf_fragments proptools.Configurable[[]string] `android:"path"`
 
 	// names of other modules to install if this module is installed
-	Required []string `android:"arch_variant"`
+	Required proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// names of other modules to install on host if this module is installed
 	Host_required []string `android:"arch_variant"`
@@ -478,12 +443,6 @@
 	// Set at module initialization time by calling InitCommonOSAndroidMultiTargetsArchModule
 	CreateCommonOSVariant bool `blueprint:"mutated"`
 
-	// If set to true then this variant is the CommonOS variant that has dependencies on its
-	// OsType specific variants.
-	//
-	// Set by osMutator.
-	CommonOSVariant bool `blueprint:"mutated"`
-
 	// When set to true, this module is not installed to the full install path (ex: under
 	// out/target/product/<name>/<partition>). It can be installed only to the packaging
 	// modules like android_filesystem.
@@ -525,16 +484,15 @@
 	// constants in image.go, but can also be set to a custom value by individual module types.
 	ImageVariation string `blueprint:"mutated"`
 
-	// SoongConfigTrace records accesses to VendorVars (soong_config). The trace will be hashed
-	// and used as a subdir of PathForModuleOut.  Note that we mainly focus on incremental
-	// builds among similar products (e.g. aosp_cf_x86_64_phone and aosp_cf_x86_64_foldable),
-	// and there are variables other than soong_config, which isn't captured by soong config
-	// trace, but influence modules among products.
-	SoongConfigTrace     soongConfigTrace `blueprint:"mutated"`
-	SoongConfigTraceHash string           `blueprint:"mutated"`
-
 	// The team (defined by the owner/vendor) who owns the property.
 	Team *string `android:"path"`
+
+	// vintf_fragment Modules required from this module.
+	Vintf_fragment_modules proptools.Configurable[[]string] `android:"path"`
+
+	// List of module names that are prevented from being installed when this module gets
+	// installed.
+	Overrides []string
 }
 
 type distProperties struct {
@@ -641,6 +599,11 @@
 	Device_supported *bool
 }
 
+type hostCrossProperties struct {
+	// If set to true, build a variant of the module for the host cross.  Defaults to true.
+	Host_cross_supported *bool
+}
+
 type Multilib string
 
 const (
@@ -756,6 +719,10 @@
 		m.AddProperties(&base.hostAndDeviceProperties)
 	}
 
+	if hod&hostCrossSupported != 0 {
+		m.AddProperties(&base.hostCrossProperties)
+	}
+
 	initArchModule(m)
 }
 
@@ -841,6 +808,7 @@
 	distProperties          distProperties
 	variableProperties      interface{}
 	hostAndDeviceProperties hostAndDeviceProperties
+	hostCrossProperties     hostCrossProperties
 
 	// Arch specific versions of structs in GetProperties() prior to
 	// initialization in InitAndroidArchModule, lets call it `generalProperties`.
@@ -849,9 +817,6 @@
 	// archPropRoot that is filled with arch specific values by the arch mutator.
 	archProperties [][]interface{}
 
-	// Properties specific to the Blueprint to BUILD migration.
-	bazelTargetModuleProperties bazel.BazelTargetModuleProperties
-
 	// Information about all the properties on the module that contains visibility rules that need
 	// checking.
 	visibilityPropertyInfo []visibilityProperty
@@ -862,30 +827,7 @@
 	// The primary licenses property, may be nil, records license metadata for the module.
 	primaryLicensesProperty applicableLicensesProperty
 
-	noAddressSanitizer   bool
-	installFiles         InstallPaths
-	installFilesDepSet   *DepSet[InstallPath]
-	checkbuildFiles      Paths
-	packagingSpecs       []PackagingSpec
-	packagingSpecsDepSet *DepSet[PackagingSpec]
-	// katiInstalls tracks the install rules that were created by Soong but are being exported
-	// to Make to convert to ninja rules so that Make can add additional dependencies.
-	katiInstalls katiInstalls
-	// katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are
-	// allowed to have duplicates across modules and variants.
-	katiInitRcInstalls katiInstalls
-	katiVintfInstalls  katiInstalls
-	katiSymlinks       katiInstalls
-	testData           []DataPath
-
-	// The files to copy to the dist as explicitly specified in the .bp file.
-	distFiles TaggedDistFiles
-
-	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
-	// Only set on the final variant of each module
-	installTarget    WritablePath
-	checkbuildTarget WritablePath
-	blueprintDir     string
+	noAddressSanitizer bool
 
 	hooks hooks
 
@@ -895,30 +837,6 @@
 	buildParams []BuildParams
 	ruleParams  map[blueprint.Rule]blueprint.RuleParams
 	variables   map[string]string
-
-	initRcPaths         Paths
-	vintfFragmentsPaths Paths
-
-	installedInitRcPaths         InstallPaths
-	installedVintfFragmentsPaths InstallPaths
-
-	// Merged Aconfig files for all transitive deps.
-	aconfigFilePaths Paths
-
-	// set of dependency module:location mappings used to populate the license metadata for
-	// apex containers.
-	licenseInstallMap []string
-
-	// The path to the generated license metadata file for the module.
-	licenseMetadataFile WritablePath
-
-	// moduleInfoJSON can be filled out by GenerateAndroidBuildActions to write a JSON file that will
-	// be included in the final module-info.json produced by Make.
-	moduleInfoJSON *ModuleInfoJSON
-
-	// outputFiles stores the output of a module by tag and is used to set
-	// the OutputFilesProvider in GenerateBuildActions
-	outputFiles OutputFilesInfo
 }
 
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
@@ -1055,6 +973,7 @@
 	fullManifest := pv.DeviceArch != nil && pv.DeviceName != nil
 	if fullManifest {
 		addRequiredDeps(ctx)
+		addVintfFragmentDeps(ctx)
 	}
 }
 
@@ -1101,7 +1020,7 @@
 	hostTargets = append(hostTargets, ctx.Config().BuildOSCommonTarget)
 
 	if ctx.Device() {
-		for _, depName := range ctx.Module().RequiredModuleNames() {
+		for _, depName := range ctx.Module().RequiredModuleNames(ctx) {
 			for _, target := range deviceTargets {
 				addDep(target, depName)
 			}
@@ -1114,7 +1033,7 @@
 	}
 
 	if ctx.Host() {
-		for _, depName := range ctx.Module().RequiredModuleNames() {
+		for _, depName := range ctx.Module().RequiredModuleNames(ctx) {
 			for _, target := range hostTargets {
 				// When a host module requires another host module, don't make a
 				// dependency if they have different OSes (i.e. hostcross).
@@ -1132,6 +1051,16 @@
 	}
 }
 
+var vintfDepTag = struct {
+	blueprint.BaseDependencyTag
+	InstallAlwaysNeededDependencyTag
+}{}
+
+func addVintfFragmentDeps(ctx BottomUpMutatorContext) {
+	mod := ctx.Module()
+	ctx.AddDependency(mod, vintfDepTag, mod.VintfFragmentModuleNames(ctx)...)
+}
+
 // AddProperties "registers" the provided props
 // each value in props MUST be a pointer to a struct
 func (m *ModuleBase) AddProperties(props ...interface{}) {
@@ -1235,27 +1164,16 @@
 		// the special tag name which represents that.
 		tag := proptools.StringDefault(dist.Tag, DefaultDistTag)
 
-		if outputFileProducer, ok := m.module.(OutputFileProducer); ok {
-			// Call the OutputFiles(tag) method to get the paths associated with the tag.
-			distFilesForTag, err := outputFileProducer.OutputFiles(tag)
-
-			// If the tag was not supported and is not DefaultDistTag then it is an error.
-			// Failing to find paths for DefaultDistTag is not an error. It just means
-			// that the module type requires the legacy behavior.
+		distFileForTagFromProvider, err := outputFilesForModuleFromProvider(ctx, m.module, tag)
+		if err != OutputFilesProviderNotSet {
 			if err != nil && tag != DefaultDistTag {
 				ctx.PropertyErrorf("dist.tag", "%s", err.Error())
+			} else {
+				distFiles = distFiles.addPathsForTag(tag, distFileForTagFromProvider...)
+				continue
 			}
-
-			distFiles = distFiles.addPathsForTag(tag, distFilesForTag...)
-		} else if tag != DefaultDistTag {
-			// If the tag was specified then it is an error if the module does not
-			// implement OutputFileProducer because there is no other way of accessing
-			// the paths for the specified tag.
-			ctx.PropertyErrorf("dist.tag",
-				"tag %s not supported because the module does not implement OutputFileProducer", tag)
 		}
 	}
-
 	return distFiles
 }
 
@@ -1297,7 +1215,7 @@
 
 // True if the current variant is a CommonOS variant, false otherwise.
 func (m *ModuleBase) IsCommonOSVariant() bool {
-	return m.commonProperties.CommonOSVariant
+	return m.commonProperties.CompileOS == CommonOS
 }
 
 // supportsTarget returns true if the given Target is supported by the current module.
@@ -1347,7 +1265,11 @@
 	// hostEnabled is true if the host_supported property is true or the HostOrDeviceSupported
 	// value has the hostDefault bit set.
 	hostEnabled := proptools.BoolDefault(m.hostAndDeviceProperties.Host_supported, hod&hostDefault != 0)
-	return hod&hostCrossSupported != 0 && hostEnabled
+
+	// Default true for the Host_cross_supported property
+	hostCrossEnabled := proptools.BoolDefault(m.hostCrossProperties.Host_cross_supported, true)
+
+	return hod&hostCrossSupported != 0 && hostEnabled && hostCrossEnabled
 }
 
 func (m *ModuleBase) Platform() bool {
@@ -1411,13 +1333,21 @@
 	return partition
 }
 
-func (m *ModuleBase) Enabled(ctx ConfigAndErrorContext) bool {
+func (m *ModuleBase) Enabled(ctx ConfigurableEvaluatorContext) bool {
 	if m.commonProperties.ForcedDisabled {
 		return false
 	}
 	return m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled)
 }
 
+// Returns a copy of the enabled property, useful for passing it on to sub-modules
+func (m *ModuleBase) EnabledProperty() proptools.Configurable[bool] {
+	if m.commonProperties.ForcedDisabled {
+		return proptools.NewSimpleConfigurable(false)
+	}
+	return m.commonProperties.Enabled.Clone()
+}
+
 func (m *ModuleBase) Disable() {
 	m.commonProperties.ForcedDisabled = true
 }
@@ -1487,12 +1417,13 @@
 		if isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
 			// Installation is still handled by Make, so anything hidden from Make is not
 			// installable.
+			info := OtherModuleProviderOrDefault(ctx, dep, InstallFilesProvider)
 			if !dep.IsHideFromMake() && !dep.IsSkipInstall() {
-				installDeps = append(installDeps, dep.base().installFilesDepSet)
+				installDeps = append(installDeps, info.TransitiveInstallFiles)
 			}
 			// Add packaging deps even when the dependency is not installed so that uninstallable
 			// modules can still be packaged.  Often the package will be installed instead.
-			packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet)
+			packagingSpecs = append(packagingSpecs, info.TransitivePackagingSpecs)
 		}
 	})
 
@@ -1510,18 +1441,6 @@
 	return IsInstallDepNeededTag(tag)
 }
 
-func (m *ModuleBase) FilesToInstall() InstallPaths {
-	return m.installFiles
-}
-
-func (m *ModuleBase) PackagingSpecs() []PackagingSpec {
-	return m.packagingSpecs
-}
-
-func (m *ModuleBase) TransitivePackagingSpecs() []PackagingSpec {
-	return m.packagingSpecsDepSet.ToList()
-}
-
 func (m *ModuleBase) NoAddressSanitizer() bool {
 	return m.noAddressSanitizer
 }
@@ -1619,8 +1538,8 @@
 	return m.base().commonProperties.ImageVariation == RecoveryVariation
 }
 
-func (m *ModuleBase) RequiredModuleNames() []string {
-	return m.base().commonProperties.Required
+func (m *ModuleBase) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string {
+	return m.base().commonProperties.Required.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
 }
 
 func (m *ModuleBase) HostRequiredModuleNames() []string {
@@ -1631,58 +1550,71 @@
 	return m.base().commonProperties.Target_required
 }
 
-func (m *ModuleBase) InitRc() Paths {
-	return append(Paths{}, m.initRcPaths...)
+func (m *ModuleBase) VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string {
+	return m.base().commonProperties.Vintf_fragment_modules.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
 }
 
-func (m *ModuleBase) VintfFragments() Paths {
-	return append(Paths{}, m.vintfFragmentsPaths...)
-}
-
-func (m *ModuleBase) CompileMultilib() *string {
-	return m.base().commonProperties.Compile_multilib
-}
-
-// SetLicenseInstallMap stores the set of dependency module:location mappings for files in an
-// apex container for use when generation the license metadata file.
-func (m *ModuleBase) SetLicenseInstallMap(installMap []string) {
-	m.licenseInstallMap = append(m.licenseInstallMap, installMap...)
-}
-
-func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
-	var allInstalledFiles InstallPaths
-	var allCheckbuildFiles Paths
-	ctx.VisitAllModuleVariants(func(module Module) {
-		a := module.base()
-		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
-		// 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) {
-			allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
-		}
-	})
-
-	var deps Paths
-
+func (m *ModuleBase) generateVariantTarget(ctx *moduleContext) {
 	namespacePrefix := ctx.Namespace().id
 	if namespacePrefix != "" {
 		namespacePrefix = namespacePrefix + "-"
 	}
 
+	if !ctx.uncheckedModule {
+		name := namespacePrefix + ctx.ModuleName() + "-" + ctx.ModuleSubDir() + "-checkbuild"
+		ctx.Phony(name, ctx.checkbuildFiles...)
+		ctx.checkbuildTarget = PathForPhony(ctx, name)
+	}
+
+}
+
+func (m *ModuleBase) generateModuleTarget(ctx *moduleContext) {
+	var allInstalledFiles InstallPaths
+	var allCheckbuildTargets Paths
+	ctx.VisitAllModuleVariants(func(module Module) {
+		a := module.base()
+		var checkbuildTarget Path
+		var uncheckedModule bool
+		if a == m {
+			allInstalledFiles = append(allInstalledFiles, ctx.installFiles...)
+			checkbuildTarget = ctx.checkbuildTarget
+			uncheckedModule = ctx.uncheckedModule
+		} else {
+			info := OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider)
+			allInstalledFiles = append(allInstalledFiles, info.InstallFiles...)
+			checkbuildTarget = info.CheckbuildTarget
+			uncheckedModule = info.UncheckedModule
+		}
+		// 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 {
+			allCheckbuildTargets = append(allCheckbuildTargets, checkbuildTarget)
+		}
+	})
+
+	var deps Paths
+
+	var namespacePrefix string
+	nameSpace := ctx.Namespace().Path
+	if nameSpace != "." {
+		namespacePrefix = strings.ReplaceAll(nameSpace, "/", ".") + "-"
+	}
+
+	var info FinalModuleBuildTargetsInfo
+
 	if len(allInstalledFiles) > 0 {
 		name := namespacePrefix + ctx.ModuleName() + "-install"
 		ctx.Phony(name, allInstalledFiles.Paths()...)
-		m.installTarget = PathForPhony(ctx, name)
-		deps = append(deps, m.installTarget)
+		info.InstallTarget = PathForPhony(ctx, name)
+		deps = append(deps, info.InstallTarget)
 	}
 
-	if len(allCheckbuildFiles) > 0 {
+	if len(allCheckbuildTargets) > 0 {
 		name := namespacePrefix + ctx.ModuleName() + "-checkbuild"
-		ctx.Phony(name, allCheckbuildFiles...)
-		m.checkbuildTarget = PathForPhony(ctx, name)
-		deps = append(deps, m.checkbuildTarget)
+		ctx.Phony(name, allCheckbuildTargets...)
+		deps = append(deps, PathForPhony(ctx, name))
 	}
 
 	if len(deps) > 0 {
@@ -1693,7 +1625,8 @@
 
 		ctx.Phony(namespacePrefix+ctx.ModuleName()+suffix, deps...)
 
-		m.blueprintDir = ctx.ModuleDir()
+		info.BlueprintDir = ctx.ModuleDir()
+		SetProvider(ctx, FinalModuleBuildTargetsProvider, info)
 	}
 }
 
@@ -1772,7 +1705,11 @@
 	}
 }
 
-func (m *ModuleBase) archModuleContextFactory(ctx blueprint.IncomingTransitionContext) archModuleContext {
+type archModuleContextFactoryContext interface {
+	Config() interface{}
+}
+
+func (m *ModuleBase) archModuleContextFactory(ctx archModuleContextFactoryContext) archModuleContext {
 	config := ctx.Config().(Config)
 	target := m.Target()
 	primaryArch := false
@@ -1793,21 +1730,69 @@
 
 }
 
+type InstallFilesInfo struct {
+	InstallFiles     InstallPaths
+	CheckbuildFiles  Paths
+	CheckbuildTarget Path
+	UncheckedModule  bool
+	PackagingSpecs   []PackagingSpec
+	// katiInstalls tracks the install rules that were created by Soong but are being exported
+	// to Make to convert to ninja rules so that Make can add additional dependencies.
+	KatiInstalls             katiInstalls
+	KatiSymlinks             katiInstalls
+	TestData                 []DataPath
+	TransitivePackagingSpecs *DepSet[PackagingSpec]
+	LicenseMetadataFile      WritablePath
+
+	// The following fields are private before, make it private again once we have
+	// better solution.
+	TransitiveInstallFiles *DepSet[InstallPath]
+	// katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are
+	// allowed to have duplicates across modules and variants.
+	KatiInitRcInstalls           katiInstalls
+	KatiVintfInstalls            katiInstalls
+	InitRcPaths                  Paths
+	VintfFragmentsPaths          Paths
+	InstalledInitRcPaths         InstallPaths
+	InstalledVintfFragmentsPaths InstallPaths
+
+	// The files to copy to the dist as explicitly specified in the .bp file.
+	DistFiles TaggedDistFiles
+}
+
+var InstallFilesProvider = blueprint.NewProvider[InstallFilesInfo]()
+
+type FinalModuleBuildTargetsInfo struct {
+	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
+	// Only set on the final variant of each module
+	InstallTarget    WritablePath
+	CheckbuildTarget WritablePath
+	BlueprintDir     string
+}
+
+var FinalModuleBuildTargetsProvider = blueprint.NewProvider[FinalModuleBuildTargetsInfo]()
+
 func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
 	ctx := &moduleContext{
 		module:            m.module,
 		bp:                blueprintCtx,
 		baseModuleContext: m.baseModuleContextFactory(blueprintCtx),
 		variables:         make(map[string]string),
+		phonies:           make(map[string]Paths),
 	}
 
-	m.licenseMetadataFile = PathForModuleOut(ctx, "meta_lic")
+	setContainerInfo(ctx)
+	if ctx.Config().Getenv("DISABLE_CONTAINER_CHECK") != "true" {
+		checkContainerViolations(ctx)
+	}
+
+	ctx.licenseMetadataFile = PathForModuleOut(ctx, "meta_lic")
 
 	dependencyInstallFiles, dependencyPackagingSpecs := m.computeInstallDeps(ctx)
-	// set m.installFilesDepSet to only the transitive dependencies to be used as the dependencies
+	// 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.
-	m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, nil, dependencyInstallFiles)
+	ctx.TransitiveInstallFiles = NewDepSet[InstallPath](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.
@@ -1851,6 +1836,8 @@
 		checkDistProperties(ctx, fmt.Sprintf("dists[%d]", i), &m.distProperties.Dists[i])
 	}
 
+	var installFiles InstallFilesInfo
+
 	if m.Enabled(ctx) {
 		// ensure all direct android.Module deps are enabled
 		ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) {
@@ -1868,30 +1855,36 @@
 			// so only a single rule is created for each init.rc or vintf fragment file.
 
 			if !m.InVendorRamdisk() {
-				m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
+				ctx.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc.GetOrDefault(ctx, nil))
 				rcDir := PathForModuleInstall(ctx, "etc", "init")
-				for _, src := range m.initRcPaths {
+				for _, src := range ctx.initRcPaths {
 					installedInitRc := rcDir.Join(ctx, src.Base())
-					m.katiInitRcInstalls = append(m.katiInitRcInstalls, katiInstall{
+					ctx.katiInitRcInstalls = append(ctx.katiInitRcInstalls, katiInstall{
 						from: src,
 						to:   installedInitRc,
 					})
 					ctx.PackageFile(rcDir, src.Base(), src)
-					m.installedInitRcPaths = append(m.installedInitRcPaths, installedInitRc)
+					ctx.installedInitRcPaths = append(ctx.installedInitRcPaths, installedInitRc)
 				}
+				installFiles.InitRcPaths = ctx.initRcPaths
+				installFiles.KatiInitRcInstalls = ctx.katiInitRcInstalls
+				installFiles.InstalledInitRcPaths = ctx.installedInitRcPaths
 			}
 
-			m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments)
+			ctx.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments.GetOrDefault(ctx, nil))
 			vintfDir := PathForModuleInstall(ctx, "etc", "vintf", "manifest")
-			for _, src := range m.vintfFragmentsPaths {
+			for _, src := range ctx.vintfFragmentsPaths {
 				installedVintfFragment := vintfDir.Join(ctx, src.Base())
-				m.katiVintfInstalls = append(m.katiVintfInstalls, katiInstall{
+				ctx.katiVintfInstalls = append(ctx.katiVintfInstalls, katiInstall{
 					from: src,
 					to:   installedVintfFragment,
 				})
 				ctx.PackageFile(vintfDir, src.Base(), src)
-				m.installedVintfFragmentsPaths = append(m.installedVintfFragmentsPaths, installedVintfFragment)
+				ctx.installedVintfFragmentsPaths = append(ctx.installedVintfFragmentsPaths, installedVintfFragment)
 			}
+			installFiles.VintfFragmentsPaths = ctx.vintfFragmentsPaths
+			installFiles.KatiVintfInstalls = ctx.katiVintfInstalls
+			installFiles.InstalledVintfFragmentsPaths = ctx.installedVintfFragmentsPaths
 		}
 
 		licensesPropertyFlattener(ctx)
@@ -1918,21 +1911,33 @@
 			return
 		}
 
+		if x, ok := m.module.(IDEInfo); ok {
+			var result IdeInfo
+			x.IDEInfo(ctx, &result)
+			result.BaseModuleName = x.BaseModuleName()
+			SetProvider(ctx, IdeInfoProviderKey, result)
+		}
+
 		// Create the set of tagged dist files after calling GenerateAndroidBuildActions
 		// as GenerateTaggedDistFiles() calls OutputFiles(tag) and so relies on the
 		// output paths being set which must be done before or during
 		// GenerateAndroidBuildActions.
-		m.distFiles = m.GenerateTaggedDistFiles(ctx)
+		installFiles.DistFiles = m.GenerateTaggedDistFiles(ctx)
 		if ctx.Failed() {
 			return
 		}
 
-		m.installFiles = append(m.installFiles, ctx.installFiles...)
-		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
-		m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
-		m.katiInstalls = append(m.katiInstalls, ctx.katiInstalls...)
-		m.katiSymlinks = append(m.katiSymlinks, ctx.katiSymlinks...)
-		m.testData = append(m.testData, ctx.testData...)
+		m.generateVariantTarget(ctx)
+
+		installFiles.LicenseMetadataFile = ctx.licenseMetadataFile
+		installFiles.InstallFiles = ctx.installFiles
+		installFiles.CheckbuildFiles = ctx.checkbuildFiles
+		installFiles.CheckbuildTarget = ctx.checkbuildTarget
+		installFiles.UncheckedModule = ctx.uncheckedModule
+		installFiles.PackagingSpecs = ctx.packagingSpecs
+		installFiles.KatiInstalls = ctx.katiInstalls
+		installFiles.KatiSymlinks = ctx.katiSymlinks
+		installFiles.TestData = ctx.testData
 	} else if ctx.Config().AllowMissingDependencies() {
 		// If the module is not enabled it will not create any build rules, nothing will call
 		// ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled
@@ -1948,17 +1953,19 @@
 		}
 	}
 
-	m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, m.installFiles, dependencyInstallFiles)
-	m.packagingSpecsDepSet = NewDepSet[PackagingSpec](TOPOLOGICAL, m.packagingSpecs, dependencyPackagingSpecs)
+	ctx.TransitiveInstallFiles = NewDepSet[InstallPath](TOPOLOGICAL, ctx.installFiles, dependencyInstallFiles)
+	installFiles.TransitiveInstallFiles = ctx.TransitiveInstallFiles
+	installFiles.TransitivePackagingSpecs = NewDepSet[PackagingSpec](TOPOLOGICAL, ctx.packagingSpecs, dependencyPackagingSpecs)
 
-	buildLicenseMetadata(ctx, m.licenseMetadataFile)
+	SetProvider(ctx, InstallFilesProvider, installFiles)
+	buildLicenseMetadata(ctx, ctx.licenseMetadataFile)
 
-	if m.moduleInfoJSON != nil {
+	if ctx.moduleInfoJSON != nil {
 		var installed InstallPaths
-		installed = append(installed, m.katiInstalls.InstallPaths()...)
-		installed = append(installed, m.katiSymlinks.InstallPaths()...)
-		installed = append(installed, m.katiInitRcInstalls.InstallPaths()...)
-		installed = append(installed, m.katiVintfInstalls.InstallPaths()...)
+		installed = append(installed, ctx.katiInstalls.InstallPaths()...)
+		installed = append(installed, ctx.katiSymlinks.InstallPaths()...)
+		installed = append(installed, ctx.katiInitRcInstalls.InstallPaths()...)
+		installed = append(installed, ctx.katiVintfInstalls.InstallPaths()...)
 		installedStrings := installed.Strings()
 
 		var targetRequired, hostRequired []string
@@ -1969,41 +1976,49 @@
 		}
 
 		var data []string
-		for _, d := range m.testData {
+		for _, d := range ctx.testData {
 			data = append(data, d.ToRelativeInstallPath())
 		}
 
-		if m.moduleInfoJSON.Uninstallable {
+		if ctx.moduleInfoJSON.Uninstallable {
 			installedStrings = nil
-			if len(m.moduleInfoJSON.CompatibilitySuites) == 1 && m.moduleInfoJSON.CompatibilitySuites[0] == "null-suite" {
-				m.moduleInfoJSON.CompatibilitySuites = nil
-				m.moduleInfoJSON.TestConfig = nil
-				m.moduleInfoJSON.AutoTestConfig = nil
+			if len(ctx.moduleInfoJSON.CompatibilitySuites) == 1 && ctx.moduleInfoJSON.CompatibilitySuites[0] == "null-suite" {
+				ctx.moduleInfoJSON.CompatibilitySuites = nil
+				ctx.moduleInfoJSON.TestConfig = nil
+				ctx.moduleInfoJSON.AutoTestConfig = nil
 				data = nil
 			}
 		}
 
-		m.moduleInfoJSON.core = CoreModuleInfoJSON{
-			RegisterName:       m.moduleInfoRegisterName(ctx, m.moduleInfoJSON.SubName),
+		ctx.moduleInfoJSON.core = CoreModuleInfoJSON{
+			RegisterName:       m.moduleInfoRegisterName(ctx, ctx.moduleInfoJSON.SubName),
 			Path:               []string{ctx.ModuleDir()},
 			Installed:          installedStrings,
-			ModuleName:         m.BaseModuleName() + m.moduleInfoJSON.SubName,
+			ModuleName:         m.BaseModuleName() + ctx.moduleInfoJSON.SubName,
 			SupportedVariants:  []string{m.moduleInfoVariant(ctx)},
 			TargetDependencies: targetRequired,
 			HostDependencies:   hostRequired,
 			Data:               data,
-			Required:           m.RequiredModuleNames(),
+			Required:           append(m.RequiredModuleNames(ctx), m.VintfFragmentModuleNames(ctx)...),
 		}
-		SetProvider(ctx, ModuleInfoJSONProvider, m.moduleInfoJSON)
+		SetProvider(ctx, ModuleInfoJSONProvider, ctx.moduleInfoJSON)
 	}
 
 	m.buildParams = ctx.buildParams
 	m.ruleParams = ctx.ruleParams
 	m.variables = ctx.variables
 
-	if m.outputFiles.DefaultOutputFiles != nil || m.outputFiles.TaggedOutputFiles != nil {
-		SetProvider(ctx, OutputFilesProvider, m.outputFiles)
+	outputFiles := ctx.GetOutputFiles()
+	if outputFiles.DefaultOutputFiles != nil || outputFiles.TaggedOutputFiles != nil {
+		SetProvider(ctx, OutputFilesProvider, outputFiles)
 	}
+
+	if len(ctx.phonies) > 0 {
+		SetProvider(ctx, ModulePhonyProvider, ModulePhonyInfo{
+			Phonies: ctx.phonies,
+		})
+	}
+	buildComplianceMetadataProvider(ctx, m)
 }
 
 func SetJarJarPrefixHandler(handler func(ModuleContext)) {
@@ -2087,11 +2102,61 @@
 	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
+}
+
+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
+	}
+
+	return nil
+}
+
 type extraFilesZip struct {
 	zip Path
 	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
+}
+
+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
+	}
+
+	return nil
+}
+
 type katiInstalls []katiInstall
 
 // BuiltInstalled returns the katiInstalls in the form used by $(call copy-many-files) in Make, a
@@ -2141,17 +2206,23 @@
 	return proptools.Bool(m.commonProperties.Native_bridge_supported)
 }
 
-type ConfigAndErrorContext interface {
+type ConfigContext interface {
+	Config() Config
+}
+
+type ConfigurableEvaluatorContext interface {
+	OtherModuleProviderContext
 	Config() Config
 	OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{})
+	HasMutatorFinished(mutatorName string) bool
 }
 
 type configurationEvalutor struct {
-	ctx ConfigAndErrorContext
+	ctx ConfigurableEvaluatorContext
 	m   Module
 }
 
-func (m *ModuleBase) ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator {
+func (m *ModuleBase) ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator {
 	return configurationEvalutor{
 		ctx: ctx,
 		m:   m.module,
@@ -2165,6 +2236,12 @@
 func (e configurationEvalutor) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue {
 	ctx := e.ctx
 	m := e.m
+
+	if !ctx.HasMutatorFinished("defaults") {
+		ctx.OtherModulePropertyErrorf(m, property, "Cannot evaluate configurable property before the defaults mutator has run")
+		return proptools.ConfigurableValueUndefined()
+	}
+
 	switch condition.FunctionName() {
 	case "release_flag":
 		if condition.NumArgs() != 1 {
@@ -2194,6 +2271,9 @@
 		switch variable {
 		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())
 		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))
@@ -2208,7 +2288,20 @@
 		variable := condition.Arg(1)
 		if n, ok := ctx.Config().productVariables.VendorVars[namespace]; ok {
 			if v, ok := n[variable]; ok {
-				return proptools.ConfigurableValueString(v)
+				ty := ""
+				if namespaces, ok := ctx.Config().productVariables.VendorVarTypes[namespace]; ok {
+					ty = namespaces[variable]
+				}
+				switch ty {
+				case "":
+					// strings are the default, we don't bother writing them to the soong variables json file
+					return proptools.ConfigurableValueString(v)
+				case "bool":
+					return proptools.ConfigurableValueBool(v == "true")
+				default:
+					panic("unhandled soong config variable type: " + ty)
+				}
+
 			}
 		}
 		return proptools.ConfigurableValueUndefined()
@@ -2345,7 +2438,7 @@
 	// The name of the module.
 	moduleName string
 
-	// The tag that will be passed to the module's OutputFileProducer.OutputFiles(tag) method.
+	// The tag that will be used to get the specific output file(s).
 	tag string
 }
 
@@ -2399,14 +2492,7 @@
 	Srcs() Paths
 }
 
-// A module that implements OutputFileProducer can be referenced from any property that is tagged with `android:"path"`
-// using the ":module" syntax or ":module{.tag}" syntax and provides a list of output files to be used as if they were
-// listed in the property.
-type OutputFileProducer interface {
-	OutputFiles(tag string) (Paths, error)
-}
-
-// OutputFilesForModule returns the paths from an OutputFileProducer with the given tag.  On error, including if the
+// 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 {
 	paths, err := outputFilesForModule(ctx, module, tag)
@@ -2417,7 +2503,7 @@
 	return paths
 }
 
-// OutputFileForModule returns the path from an OutputFileProducer with the given tag.  On error, including if the
+// 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 {
 	paths, err := outputFilesForModule(ctx, module, tag)
@@ -2454,24 +2540,17 @@
 
 func outputFilesForModule(ctx PathContext, module blueprint.Module, tag string) (Paths, error) {
 	outputFilesFromProvider, err := outputFilesForModuleFromProvider(ctx, module, tag)
-	if outputFilesFromProvider != nil || err != nil {
+	if outputFilesFromProvider != nil || err != OutputFilesProviderNotSet {
 		return outputFilesFromProvider, err
 	}
-	if outputFileProducer, ok := module.(OutputFileProducer); ok {
-		paths, err := outputFileProducer.OutputFiles(tag)
-		if err != nil {
-			return nil, fmt.Errorf("failed to get output file from module %q at tag %q: %s",
-				pathContextName(ctx, module), tag, err.Error())
-		}
-		return paths, nil
-	} else if sourceFileProducer, ok := module.(SourceFileProducer); ok {
+	if sourceFileProducer, ok := module.(SourceFileProducer); ok {
 		if tag != "" {
-			return nil, fmt.Errorf("module %q is a SourceFileProducer, not an OutputFileProducer, and so does not support tag %q", pathContextName(ctx, module), tag)
+			return nil, fmt.Errorf("module %q is a SourceFileProducer, which does not support tag %q", pathContextName(ctx, module), tag)
 		}
 		paths := sourceFileProducer.Srcs()
 		return paths, nil
 	} else {
-		return nil, fmt.Errorf("module %q is not an OutputFileProducer or SourceFileProducer", pathContextName(ctx, module))
+		return nil, fmt.Errorf("module %q is not a SourceFileProducer or having valid output file for tag %q", pathContextName(ctx, module), tag)
 	}
 }
 
@@ -2479,34 +2558,51 @@
 // *inter-module-communication*.
 // If mctx module is the same as the param module the output files are obtained
 // from outputFiles property of module base, to avoid both setting and
-// reading OutputFilesProvider before  GenerateBuildActions is finished. Also
-// only empty-string-tag is supported in this case.
+// 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) {
-	// TODO: support OutputFilesProvider for singletons
-	mctx, ok := ctx.(ModuleContext)
-	if !ok {
-		return nil, nil
+	var outputFiles OutputFilesInfo
+	fromProperty := false
+
+	type OutputFilesProviderModuleContext interface {
+		OtherModuleProviderContext
+		Module() Module
+		GetOutputFiles() OutputFilesInfo
 	}
-	if mctx.Module() != module {
-		if outputFilesProvider, ok := OtherModuleProvider(mctx, module, OutputFilesProvider); ok {
-			if tag == "" {
-				return outputFilesProvider.DefaultOutputFiles, nil
-			} else if taggedOutputFiles, hasTag := outputFilesProvider.TaggedOutputFiles[tag]; hasTag {
-				return taggedOutputFiles, nil
-			} else {
-				return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-			}
-		}
-	} else {
-		if tag == "" {
-			return mctx.Module().base().outputFiles.DefaultOutputFiles, nil
+
+	if mctx, isMctx := ctx.(OutputFilesProviderModuleContext); isMctx {
+		if 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)
+	} else {
+		return nil, fmt.Errorf("unsupported context %q in method outputFilesForModuleFromProvider", reflect.TypeOf(ctx))
+	}
+
+	if outputFiles.isEmpty() {
+		return nil, OutputFilesProviderNotSet
+	}
+
+	if tag == "" {
+		return outputFiles.DefaultOutputFiles, nil
+	} else if taggedOutputFiles, hasTag := outputFiles.TaggedOutputFiles[tag]; hasTag {
+		return taggedOutputFiles, nil
+	} else {
+		if fromProperty {
 			return nil, fmt.Errorf("unsupported tag %q for module getting its own output files", tag)
+		} else {
+			return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 		}
 	}
-	// TODO: Add a check for param module not having OutputFilesProvider set
-	return nil, nil
+}
+
+func (o OutputFilesInfo) isEmpty() bool {
+	return o.DefaultOutputFiles == nil && o.TaggedOutputFiles == nil
 }
 
 type OutputFilesInfo struct {
@@ -2519,6 +2615,9 @@
 
 var OutputFilesProvider = blueprint.NewProvider[OutputFilesInfo]()
 
+// This is used to mark the case where OutputFilesProvider is not set on some modules.
+var OutputFilesProviderNotSet = fmt.Errorf("No output files from provider")
+
 // Modules can implement HostToolProvider and return a valid OptionalPath from HostToolPath() to
 // specify that they can be used as a tool by a genrule module.
 type HostToolProvider interface {
@@ -2530,8 +2629,6 @@
 
 func init() {
 	RegisterParallelSingletonType("buildtarget", BuildTargetSingleton)
-	RegisterParallelSingletonType("soongconfigtrace", soongConfigTraceSingletonFunc)
-	FinalDepsMutators(registerSoongConfigTraceMutator)
 }
 
 func BuildTargetSingleton() Singleton {
@@ -2583,17 +2680,15 @@
 	modulesInDir := make(map[string]Paths)
 
 	ctx.VisitAllModules(func(module Module) {
-		blueprintDir := module.base().blueprintDir
-		installTarget := module.base().installTarget
-		checkbuildTarget := module.base().checkbuildTarget
+		info := OtherModuleProviderOrDefault(ctx, module, FinalModuleBuildTargetsProvider)
 
-		if checkbuildTarget != nil {
-			checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
-			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], checkbuildTarget)
+		if info.CheckbuildTarget != nil {
+			checkbuildDeps = append(checkbuildDeps, info.CheckbuildTarget)
+			modulesInDir[info.BlueprintDir] = append(modulesInDir[info.BlueprintDir], info.CheckbuildTarget)
 		}
 
-		if installTarget != nil {
-			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], installTarget)
+		if info.InstallTarget != nil {
+			modulesInDir[info.BlueprintDir] = append(modulesInDir[info.BlueprintDir], info.InstallTarget)
 		}
 	})
 
@@ -2628,7 +2723,7 @@
 	ctx.VisitAllModules(func(module Module) {
 		if module.Enabled(ctx) {
 			key := osAndCross{os: module.Target().Os, hostCross: module.Target().HostCross}
-			osDeps[key] = append(osDeps[key], module.base().checkbuildFiles...)
+			osDeps[key] = append(osDeps[key], OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider).CheckbuildFiles...)
 		}
 	})
 
@@ -2663,7 +2758,7 @@
 
 // Collect information for opening IDE project files in java/jdeps.go.
 type IDEInfo interface {
-	IDEInfo(ideInfo *IdeInfo)
+	IDEInfo(ctx BaseModuleContext, ideInfo *IdeInfo)
 	BaseModuleName() string
 }
 
@@ -2675,7 +2770,9 @@
 	IDECustomizedModuleName() string
 }
 
+// Collect information for opening IDE project files in java/jdeps.go.
 type IdeInfo struct {
+	BaseModuleName    string   `json:"-"`
 	Deps              []string `json:"dependencies,omitempty"`
 	Srcs              []string `json:"srcs,omitempty"`
 	Aidl_include_dirs []string `json:"aidl_include_dirs,omitempty"`
@@ -2689,58 +2786,32 @@
 	Libs              []string `json:"libs,omitempty"`
 }
 
+// Merge merges two IdeInfos and produces a new one, leaving the origional unchanged
+func (i IdeInfo) Merge(other IdeInfo) IdeInfo {
+	return IdeInfo{
+		Deps:              mergeStringLists(i.Deps, other.Deps),
+		Srcs:              mergeStringLists(i.Srcs, other.Srcs),
+		Aidl_include_dirs: mergeStringLists(i.Aidl_include_dirs, other.Aidl_include_dirs),
+		Jarjar_rules:      mergeStringLists(i.Jarjar_rules, other.Jarjar_rules),
+		Jars:              mergeStringLists(i.Jars, other.Jars),
+		Classes:           mergeStringLists(i.Classes, other.Classes),
+		Installed_paths:   mergeStringLists(i.Installed_paths, other.Installed_paths),
+		SrcJars:           mergeStringLists(i.SrcJars, other.SrcJars),
+		Paths:             mergeStringLists(i.Paths, other.Paths),
+		Static_libs:       mergeStringLists(i.Static_libs, other.Static_libs),
+		Libs:              mergeStringLists(i.Libs, other.Libs),
+	}
+}
+
+// mergeStringLists appends the two string lists together and returns a new string list,
+// leaving the originals unchanged. Duplicate strings will be deduplicated.
+func mergeStringLists(a, b []string) []string {
+	return FirstUniqueStrings(Concat(a, b))
+}
+
+var IdeInfoProviderKey = blueprint.NewProvider[IdeInfo]()
+
 func CheckBlueprintSyntax(ctx BaseModuleContext, filename string, contents string) []error {
 	bpctx := ctx.blueprintBaseModuleContext()
 	return blueprint.CheckBlueprintSyntax(bpctx.ModuleFactories(), filename, contents)
 }
-
-func registerSoongConfigTraceMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("soongconfigtrace", soongConfigTraceMutator).Parallel()
-}
-
-// soongConfigTraceMutator accumulates recorded soong_config trace from children. Also it normalizes
-// SoongConfigTrace to make it consistent.
-func soongConfigTraceMutator(ctx BottomUpMutatorContext) {
-	trace := &ctx.Module().base().commonProperties.SoongConfigTrace
-	ctx.VisitDirectDeps(func(m Module) {
-		childTrace := &m.base().commonProperties.SoongConfigTrace
-		trace.Bools = append(trace.Bools, childTrace.Bools...)
-		trace.Strings = append(trace.Strings, childTrace.Strings...)
-		trace.IsSets = append(trace.IsSets, childTrace.IsSets...)
-	})
-	trace.Bools = SortedUniqueStrings(trace.Bools)
-	trace.Strings = SortedUniqueStrings(trace.Strings)
-	trace.IsSets = SortedUniqueStrings(trace.IsSets)
-
-	ctx.Module().base().commonProperties.SoongConfigTraceHash = trace.hash()
-}
-
-// soongConfigTraceSingleton writes a map from each module's config hash value to trace data.
-func soongConfigTraceSingletonFunc() Singleton {
-	return &soongConfigTraceSingleton{}
-}
-
-type soongConfigTraceSingleton struct {
-}
-
-func (s *soongConfigTraceSingleton) GenerateBuildActions(ctx SingletonContext) {
-	outFile := PathForOutput(ctx, "soong_config_trace.json")
-
-	traces := make(map[string]*soongConfigTrace)
-	ctx.VisitAllModules(func(module Module) {
-		trace := &module.base().commonProperties.SoongConfigTrace
-		if !trace.isEmpty() {
-			hash := module.base().commonProperties.SoongConfigTraceHash
-			traces[hash] = trace
-		}
-	})
-
-	j, err := json.Marshal(traces)
-	if err != nil {
-		ctx.Errorf("json marshal to %q failed: %#v", outFile, err)
-		return
-	}
-
-	WriteFileRule(ctx, outFile, string(j))
-	ctx.Phony("soong_config_trace", outFile)
-}
diff --git a/android/module_context.go b/android/module_context.go
index 591e270..2bf2a8f 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -85,7 +85,9 @@
 type ModuleContext interface {
 	BaseModuleContext
 
-	blueprintModuleContext() blueprint.ModuleContext
+	// BlueprintModuleContext returns the blueprint.ModuleContext that the ModuleContext wraps.  It may only be
+	// used by the golang module types that need to call into the bootstrap module types.
+	BlueprintModuleContext() blueprint.ModuleContext
 
 	// Deprecated: use ModuleContext.Build instead.
 	ModuleBuild(pctx PackageContext, params ModuleBuildParams)
@@ -107,68 +109,79 @@
 	// InstallExecutable creates a rule to copy srcPath to name in the installPath directory,
 	// with the given additional dependencies.  The file is marked executable after copying.
 	//
-	// The installed file will be returned by FilesToInstall(), and the PackagingSpec for the
-	// installed file will be returned by PackagingSpecs() on this module or by
-	// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
-	// for which IsInstallDepNeeded returns true.
+	// The installed file can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
+	// for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module
+	// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
+	// dependency tags for which IsInstallDepNeeded returns true.
 	InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath
 
 	// InstallFile creates a rule to copy srcPath to name in the installPath directory,
 	// with the given additional dependencies.
 	//
+	// The installed file can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
+	// for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module
+	// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
+	// dependency tags for which IsInstallDepNeeded returns true.
+	InstallFile(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath
+
+	// InstallFileWithoutCheckbuild creates a rule to copy srcPath to name in the installPath directory,
+	// with the given additional dependencies, but does not add the file to the list of files to build
+	// during `m checkbuild`.
+	//
 	// The installed file will be returned by FilesToInstall(), and the PackagingSpec for the
 	// installed file will be returned by PackagingSpecs() on this module or by
 	// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
 	// for which IsInstallDepNeeded returns true.
-	InstallFile(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath
+	InstallFileWithoutCheckbuild(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath
 
 	// InstallFileWithExtraFilesZip creates a rule to copy srcPath to name in the installPath
 	// directory, and also unzip a zip file containing extra files to install into the same
 	// directory.
 	//
-	// The installed file will be returned by FilesToInstall(), and the PackagingSpec for the
-	// installed file will be returned by PackagingSpecs() on this module or by
-	// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
-	// for which IsInstallDepNeeded returns true.
+	// The installed file can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
+	// for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module
+	// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
+	// dependency tags for which IsInstallDepNeeded returns true.
 	InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...InstallPath) InstallPath
 
 	// InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath
 	// directory.
 	//
-	// The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the
-	// installed file will be returned by PackagingSpecs() on this module or by
-	// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
-	// for which IsInstallDepNeeded returns true.
+	// The installed symlink can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
+	// for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module
+	// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
+	// dependency tags for which IsInstallDepNeeded returns true.
 	InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath
 
 	// InstallAbsoluteSymlink creates a rule to create an absolute symlink from src srcPath to name
 	// in the installPath directory.
 	//
-	// The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the
-	// installed file will be returned by PackagingSpecs() on this module or by
-	// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
-	// for which IsInstallDepNeeded returns true.
+	// The installed symlink can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
+	// for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module
+	// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
+	// dependency tags for which IsInstallDepNeeded returns true.
 	InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath
 
 	// InstallTestData creates rules to install test data (e.g. data files used during a test) into
 	// the installPath directory.
 	//
-	// The installed files will be returned by FilesToInstall(), and the PackagingSpec for the
-	// installed files will be returned by PackagingSpecs() on this module or by
-	// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
-	// for which IsInstallDepNeeded returns true.
+	// The installed files can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
+	// for the installed files can be accessed by InstallFilesInfo.PackagingSpecs on this module
+	// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
+	// dependency tags for which IsInstallDepNeeded returns true.
 	InstallTestData(installPath InstallPath, data []DataPath) InstallPaths
 
 	// PackageFile creates a PackagingSpec as if InstallFile was called, but without creating
 	// the rule to copy the file.  This is useful to define how a module would be packaged
 	// without installing it into the global installation directories.
 	//
-	// The created PackagingSpec for the will be returned by PackagingSpecs() on this module or by
-	// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
-	// for which IsInstallDepNeeded returns true.
+	// The created PackagingSpec can be accessed by InstallFilesInfo.PackagingSpecs on this module
+	// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
+	// dependency tags for which IsInstallDepNeeded returns true.
 	PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec
 
-	CheckbuildFile(srcPath Path)
+	CheckbuildFile(srcPaths ...Path)
+	UncheckedModule()
 
 	InstallInData() bool
 	InstallInTestcases() bool
@@ -183,12 +196,11 @@
 	InstallInVendor() bool
 	InstallForceOS() (*OsType, *ArchType)
 
-	RequiredModuleNames() []string
+	RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
 
 	ModuleSubDir() string
-	SoongConfigTraceHash() string
 
 	Variable(pctx PackageContext, name, value string)
 	Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule
@@ -216,19 +228,58 @@
 	// SetOutputFiles stores the outputFiles to outputFiles property, which is used
 	// to set the OutputFilesProvider later.
 	SetOutputFiles(outputFiles Paths, tag string)
+
+	GetOutputFiles() OutputFilesInfo
+
+	// SetLicenseInstallMap stores the set of dependency module:location mappings for files in an
+	// apex container for use when generation the license metadata file.
+	SetLicenseInstallMap(installMap []string)
+
+	// ComplianceMetadataInfo returns a ComplianceMetadataInfo instance for different module types to dump metadata,
+	// which usually happens in GenerateAndroidBuildActions() of a module type.
+	// See android.ModuleBase.complianceMetadataInfo
+	ComplianceMetadataInfo() *ComplianceMetadataInfo
+
+	// Get the information about the containers this module belongs to.
+	getContainersInfo() ContainersInfo
+	setContainersInfo(info ContainersInfo)
+
+	setAconfigPaths(paths Paths)
 }
 
 type moduleContext struct {
 	bp blueprint.ModuleContext
 	baseModuleContext
-	packagingSpecs  []PackagingSpec
-	installFiles    InstallPaths
-	checkbuildFiles Paths
-	module          Module
-	phonies         map[string]Paths
+	packagingSpecs   []PackagingSpec
+	installFiles     InstallPaths
+	checkbuildFiles  Paths
+	checkbuildTarget Path
+	uncheckedModule  bool
+	module           Module
+	phonies          map[string]Paths
+	// outputFiles stores the output of a module by tag and is used to set
+	// the OutputFilesProvider in GenerateBuildActions
+	outputFiles OutputFilesInfo
 
-	katiInstalls []katiInstall
-	katiSymlinks []katiInstall
+	TransitiveInstallFiles *DepSet[InstallPath]
+
+	// set of dependency module:location mappings used to populate the license metadata for
+	// apex containers.
+	licenseInstallMap []string
+
+	// The path to the generated license metadata file for the module.
+	licenseMetadataFile WritablePath
+
+	katiInstalls katiInstalls
+	katiSymlinks katiInstalls
+	// katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are
+	// allowed to have duplicates across modules and variants.
+	katiInitRcInstalls           katiInstalls
+	katiVintfInstalls            katiInstalls
+	initRcPaths                  Paths
+	vintfFragmentsPaths          Paths
+	installedInitRcPaths         InstallPaths
+	installedVintfFragmentsPaths InstallPaths
 
 	testData []DataPath
 
@@ -236,6 +287,21 @@
 	buildParams []BuildParams
 	ruleParams  map[blueprint.Rule]blueprint.RuleParams
 	variables   map[string]string
+
+	// moduleInfoJSON can be filled out by GenerateAndroidBuildActions to write a JSON file that will
+	// be included in the final module-info.json produced by Make.
+	moduleInfoJSON *ModuleInfoJSON
+
+	// containersInfo stores the information about the containers and the information of the
+	// apexes the module belongs to.
+	containersInfo ContainersInfo
+
+	// Merged Aconfig files for all transitive deps.
+	aconfigFilePaths Paths
+
+	// complianceMetadataInfo is for different module types to dump metadata.
+	// See android.ModuleContext interface.
+	complianceMetadataInfo *ComplianceMetadataInfo
 }
 
 var _ ModuleContext = &moduleContext{}
@@ -357,7 +423,7 @@
 }
 
 func (m *moduleContext) Phony(name string, deps ...Path) {
-	addPhony(m.config, name, deps...)
+	m.phonies[name] = append(m.phonies[name], deps...)
 }
 
 func (m *moduleContext) GetMissingDependencies() []string {
@@ -377,10 +443,6 @@
 	return m.bp.ModuleSubDir()
 }
 
-func (m *moduleContext) SoongConfigTraceHash() string {
-	return m.module.base().commonProperties.SoongConfigTraceHash
-}
-
 func (m *moduleContext) InstallInData() bool {
 	return m.module.InstallInData()
 }
@@ -465,17 +527,22 @@
 
 func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
 	deps ...InstallPath) InstallPath {
-	return m.installFile(installPath, name, srcPath, deps, false, true, nil)
+	return m.installFile(installPath, name, srcPath, deps, false, true, true, nil)
+}
+
+func (m *moduleContext) InstallFileWithoutCheckbuild(installPath InstallPath, name string, srcPath Path,
+	deps ...InstallPath) InstallPath {
+	return m.installFile(installPath, name, srcPath, deps, false, true, false, nil)
 }
 
 func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path,
 	deps ...InstallPath) InstallPath {
-	return m.installFile(installPath, name, srcPath, deps, true, true, nil)
+	return m.installFile(installPath, name, srcPath, deps, true, true, true, nil)
 }
 
 func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path,
 	extraZip Path, deps ...InstallPath) InstallPath {
-	return m.installFile(installPath, name, srcPath, deps, false, true, &extraFilesZip{
+	return m.installFile(installPath, name, srcPath, deps, false, true, true, &extraFilesZip{
 		zip: extraZip,
 		dir: installPath,
 	})
@@ -487,11 +554,16 @@
 }
 
 func (m *moduleContext) getAconfigPaths() *Paths {
-	return &m.module.base().aconfigFilePaths
+	return &m.aconfigFilePaths
+}
+
+func (m *moduleContext) setAconfigPaths(paths Paths) {
+	m.aconfigFilePaths = paths
 }
 
 func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec {
 	licenseFiles := m.Module().EffectiveLicenseFiles()
+	overrides := CopyOf(m.Module().base().commonProperties.Overrides)
 	spec := PackagingSpec{
 		relPathInPackage:      Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
 		srcPath:               srcPath,
@@ -502,13 +574,15 @@
 		skipInstall:           m.skipInstall(),
 		aconfigPaths:          m.getAconfigPaths(),
 		archType:              m.target.Arch.ArchType,
+		overrides:             &overrides,
+		owner:                 m.ModuleName(),
 	}
 	m.packagingSpecs = append(m.packagingSpecs, spec)
 	return spec
 }
 
 func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []InstallPath,
-	executable bool, hooks bool, extraZip *extraFilesZip) InstallPath {
+	executable bool, hooks bool, checkbuild bool, extraZip *extraFilesZip) InstallPath {
 
 	fullInstallPath := installPath.Join(m, name)
 	if hooks {
@@ -516,9 +590,9 @@
 	}
 
 	if m.requiresFullInstall() {
-		deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList())...)
-		deps = append(deps, m.module.base().installedInitRcPaths...)
-		deps = append(deps, m.module.base().installedVintfFragmentsPaths...)
+		deps = append(deps, InstallPaths(m.TransitiveInstallFiles.ToList())...)
+		deps = append(deps, m.installedInitRcPaths...)
+		deps = append(deps, m.installedVintfFragmentsPaths...)
 
 		var implicitDeps, orderOnlyDeps Paths
 
@@ -575,7 +649,9 @@
 
 	m.packageFile(fullInstallPath, srcPath, executable)
 
-	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
+	if checkbuild {
+		m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
+	}
 
 	return fullInstallPath
 }
@@ -616,9 +692,9 @@
 		}
 
 		m.installFiles = append(m.installFiles, fullInstallPath)
-		m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
 	}
 
+	overrides := CopyOf(m.Module().base().commonProperties.Overrides)
 	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
 		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
 		srcPath:          nil,
@@ -628,6 +704,8 @@
 		skipInstall:      m.skipInstall(),
 		aconfigPaths:     m.getAconfigPaths(),
 		archType:         m.target.Arch.ArchType,
+		overrides:        &overrides,
+		owner:            m.ModuleName(),
 	})
 
 	return fullInstallPath
@@ -663,6 +741,7 @@
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
 
+	overrides := CopyOf(m.Module().base().commonProperties.Overrides)
 	m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
 		relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
 		srcPath:          nil,
@@ -672,6 +751,8 @@
 		skipInstall:      m.skipInstall(),
 		aconfigPaths:     m.getAconfigPaths(),
 		archType:         m.target.Arch.ArchType,
+		overrides:        &overrides,
+		owner:            m.ModuleName(),
 	})
 
 	return fullInstallPath
@@ -683,52 +764,73 @@
 	ret := make(InstallPaths, 0, len(data))
 	for _, d := range data {
 		relPath := d.ToRelativeInstallPath()
-		installed := m.installFile(installPath, relPath, d.SrcPath, nil, false, false, nil)
+		installed := m.installFile(installPath, relPath, d.SrcPath, nil, false, false, true, nil)
 		ret = append(ret, installed)
 	}
 
 	return ret
 }
 
-func (m *moduleContext) CheckbuildFile(srcPath Path) {
-	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
+// CheckbuildFile specifies the output files that should be built by checkbuild.
+func (m *moduleContext) CheckbuildFile(srcPaths ...Path) {
+	m.checkbuildFiles = append(m.checkbuildFiles, srcPaths...)
 }
 
-func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext {
+// UncheckedModule marks the current module has having no files that should be built by checkbuild.
+func (m *moduleContext) UncheckedModule() {
+	m.uncheckedModule = true
+}
+
+func (m *moduleContext) BlueprintModuleContext() blueprint.ModuleContext {
 	return m.bp
 }
 
 func (m *moduleContext) LicenseMetadataFile() Path {
-	return m.module.base().licenseMetadataFile
+	return m.licenseMetadataFile
 }
 
 func (m *moduleContext) ModuleInfoJSON() *ModuleInfoJSON {
-	if moduleInfoJSON := m.module.base().moduleInfoJSON; moduleInfoJSON != nil {
+	if moduleInfoJSON := m.moduleInfoJSON; moduleInfoJSON != nil {
 		return moduleInfoJSON
 	}
 	moduleInfoJSON := &ModuleInfoJSON{}
-	m.module.base().moduleInfoJSON = moduleInfoJSON
+	m.moduleInfoJSON = moduleInfoJSON
 	return moduleInfoJSON
 }
 
 func (m *moduleContext) SetOutputFiles(outputFiles Paths, tag string) {
 	if tag == "" {
-		if len(m.module.base().outputFiles.DefaultOutputFiles) > 0 {
+		if len(m.outputFiles.DefaultOutputFiles) > 0 {
 			m.ModuleErrorf("Module %s default OutputFiles cannot be overwritten", m.ModuleName())
 		}
-		m.module.base().outputFiles.DefaultOutputFiles = outputFiles
+		m.outputFiles.DefaultOutputFiles = outputFiles
 	} else {
-		if m.module.base().outputFiles.TaggedOutputFiles == nil {
-			m.module.base().outputFiles.TaggedOutputFiles = make(map[string]Paths)
+		if m.outputFiles.TaggedOutputFiles == nil {
+			m.outputFiles.TaggedOutputFiles = make(map[string]Paths)
 		}
-		if _, exists := m.module.base().outputFiles.TaggedOutputFiles[tag]; exists {
+		if _, exists := m.outputFiles.TaggedOutputFiles[tag]; exists {
 			m.ModuleErrorf("Module %s OutputFiles at tag %s cannot be overwritten", m.ModuleName(), tag)
 		} else {
-			m.module.base().outputFiles.TaggedOutputFiles[tag] = outputFiles
+			m.outputFiles.TaggedOutputFiles[tag] = outputFiles
 		}
 	}
 }
 
+func (m *moduleContext) GetOutputFiles() OutputFilesInfo {
+	return m.outputFiles
+}
+
+func (m *moduleContext) SetLicenseInstallMap(installMap []string) {
+	m.licenseInstallMap = append(m.licenseInstallMap, installMap...)
+}
+
+func (m *moduleContext) ComplianceMetadataInfo() *ComplianceMetadataInfo {
+	if m.complianceMetadataInfo == nil {
+		m.complianceMetadataInfo = NewComplianceMetadataInfo()
+	}
+	return m.complianceMetadataInfo
+}
+
 // Returns a list of paths expanded from globs and modules referenced using ":module" syntax.  The property must
 // be tagged with `android:"path" to support automatic source module dependency resolution.
 //
@@ -755,8 +857,8 @@
 	return OptionalPath{}
 }
 
-func (m *moduleContext) RequiredModuleNames() []string {
-	return m.module.RequiredModuleNames()
+func (m *moduleContext) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string {
+	return m.module.RequiredModuleNames(ctx)
 }
 
 func (m *moduleContext) HostRequiredModuleNames() []string {
@@ -766,3 +868,11 @@
 func (m *moduleContext) TargetRequiredModuleNames() []string {
 	return m.module.TargetRequiredModuleNames()
 }
+
+func (m *moduleContext) getContainersInfo() ContainersInfo {
+	return m.containersInfo
+}
+
+func (m *moduleContext) setContainersInfo(info ContainersInfo) {
+	m.containersInfo = info
+}
diff --git a/android/module_test.go b/android/module_test.go
index 1f3db5c..d64e3a5 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -746,7 +746,6 @@
 			foo := result.ModuleForTests("foo", "").Module().base()
 
 			AssertDeepEquals(t, "foo ", tc.expectedProps, foo.propertiesWithValues())
-
 		})
 	}
 }
@@ -935,31 +934,54 @@
 	}
 }
 
-type fakeBlueprintModule struct{}
-
-func (fakeBlueprintModule) Name() string { return "foo" }
-
-func (fakeBlueprintModule) GenerateBuildActions(blueprint.ModuleContext) {}
-
 type sourceProducerTestModule struct {
-	fakeBlueprintModule
-	source Path
+	ModuleBase
+	props struct {
+		// A represents the source file
+		A string
+	}
 }
 
-func (s sourceProducerTestModule) Srcs() Paths { return Paths{s.source} }
-
-type outputFileProducerTestModule struct {
-	fakeBlueprintModule
-	output map[string]Path
-	error  map[string]error
+func sourceProducerTestModuleFactory() Module {
+	module := &sourceProducerTestModule{}
+	module.AddProperties(&module.props)
+	InitAndroidModule(module)
+	return module
 }
 
-func (o outputFileProducerTestModule) OutputFiles(tag string) (Paths, error) {
-	return PathsIfNonNil(o.output[tag]), o.error[tag]
+func (s sourceProducerTestModule) GenerateAndroidBuildActions(ModuleContext) {}
+
+func (s sourceProducerTestModule) Srcs() Paths { return PathsForTesting(s.props.A) }
+
+type outputFilesTestModule struct {
+	ModuleBase
+	props struct {
+		// A represents the tag
+		A string
+		// B represents the output file for tag A
+		B string
+	}
+}
+
+func outputFilesTestModuleFactory() Module {
+	module := &outputFilesTestModule{}
+	module.AddProperties(&module.props)
+	InitAndroidModule(module)
+	return module
+}
+
+func (o outputFilesTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	if o.props.A != "" || o.props.B != "" {
+		ctx.SetOutputFiles(PathsForTesting(o.props.B), o.props.A)
+	}
+	// This is to simulate the case that some module uses an object to set its
+	// OutputFilesProvider, but the object itself is empty.
+	ctx.SetOutputFiles(Paths{}, "missing")
 }
 
 type pathContextAddMissingDependenciesWrapper struct {
 	PathContext
+	OtherModuleProviderContext
 	missingDeps []string
 }
 
@@ -970,52 +992,91 @@
 	return module.Name()
 }
 
+func (p *pathContextAddMissingDependenciesWrapper) Module() Module { return nil }
+
+func (p *pathContextAddMissingDependenciesWrapper) GetOutputFiles() OutputFilesInfo {
+	return OutputFilesInfo{}
+}
+
 func TestOutputFileForModule(t *testing.T) {
 	testcases := []struct {
 		name        string
-		module      blueprint.Module
+		bp          string
 		tag         string
-		env         map[string]string
-		config      func(*config)
 		expected    string
 		missingDeps []string
+		env         map[string]string
+		config      func(*config)
 	}{
 		{
-			name:     "SourceFileProducer",
-			module:   &sourceProducerTestModule{source: PathForTesting("foo.txt")},
-			expected: "foo.txt",
+			name: "SourceFileProducer",
+			bp: `spt_module {
+					name: "test_module",
+					a: "spt.txt",
+				}
+			`,
+			tag:      "",
+			expected: "spt.txt",
 		},
 		{
-			name:     "OutputFileProducer",
-			module:   &outputFileProducerTestModule{output: map[string]Path{"": PathForTesting("foo.txt")}},
-			expected: "foo.txt",
+			name: "OutputFileProviderEmptyStringTag",
+			bp: `oft_module {
+					name: "test_module",
+					a: "",
+					b: "empty.txt",
+				}
+		`,
+			tag:      "",
+			expected: "empty.txt",
 		},
 		{
-			name:     "OutputFileProducer_tag",
-			module:   &outputFileProducerTestModule{output: map[string]Path{"foo": PathForTesting("foo.txt")}},
+			name: "OutputFileProviderTag",
+			bp: `oft_module {
+					name: "test_module",
+					a: "foo",
+					b: "foo.txt",
+				}
+			`,
 			tag:      "foo",
 			expected: "foo.txt",
 		},
 		{
-			name: "OutputFileProducer_AllowMissingDependencies",
+			name: "OutputFileAllowMissingDependencies",
+			bp: `oft_module {
+				name: "test_module",
+			}
+		`,
+			tag:         "missing",
+			expected:    "missing_output_file/test_module",
+			missingDeps: []string{"test_module"},
 			config: func(config *config) {
 				config.TestProductVariables.Allow_missing_dependencies = boolPtr(true)
 			},
-			module:      &outputFileProducerTestModule{},
-			missingDeps: []string{"foo"},
-			expected:    "missing_output_file/foo",
 		},
 	}
+
 	for _, tt := range testcases {
-		config := TestConfig(buildDir, tt.env, "", nil)
-		if tt.config != nil {
-			tt.config(config.config)
-		}
-		ctx := &pathContextAddMissingDependenciesWrapper{
-			PathContext: PathContextForTesting(config),
-		}
-		got := OutputFileForModule(ctx, tt.module, tt.tag)
-		AssertPathRelativeToTopEquals(t, "expected source path", tt.expected, got)
-		AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
+		t.Run(tt.name, func(t *testing.T) {
+			result := GroupFixturePreparers(
+				PrepareForTestWithDefaults,
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("spt_module", sourceProducerTestModuleFactory)
+					ctx.RegisterModuleType("oft_module", outputFilesTestModuleFactory)
+				}),
+				FixtureWithRootAndroidBp(tt.bp),
+			).RunTest(t)
+
+			config := TestConfig(buildDir, tt.env, tt.bp, nil)
+			if tt.config != nil {
+				tt.config(config.config)
+			}
+			ctx := &pathContextAddMissingDependenciesWrapper{
+				PathContext:                PathContextForTesting(config),
+				OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(),
+			}
+			got := OutputFileForModule(ctx, result.ModuleForTests("test_module", "").Module(), tt.tag)
+			AssertPathRelativeToTopEquals(t, "expected output path", tt.expected, got)
+			AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
+		})
 	}
 }
diff --git a/android/mutator.go b/android/mutator.go
index 440b906..9404945 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -148,9 +148,9 @@
 }
 
 func registerArchMutator(ctx RegisterMutatorsContext) {
-	ctx.BottomUpBlueprint("os", osMutator).Parallel()
-	ctx.BottomUp("image", imageMutator).Parallel()
-	ctx.BottomUpBlueprint("arch", archMutator).Parallel()
+	ctx.Transition("os", &osTransitionMutator{})
+	ctx.Transition("image", &imageTransitionMutator{})
+	ctx.Transition("arch", &archTransitionMutator{})
 }
 
 var preDeps = []RegisterMutatorFunc{
@@ -193,16 +193,16 @@
 	// 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
 }
 
 type TopDownMutator func(TopDownMutatorContext)
 
 type TopDownMutatorContext interface {
 	BaseMutatorContext
-
-	// 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
 }
 
 type topDownMutatorContext struct {
@@ -400,6 +400,12 @@
 	Config() Config
 
 	DeviceConfig() DeviceConfig
+
+	// IsAddingDependency returns true if the transition is being called while adding a dependency
+	// after the transition mutator has already run, or false if it is being called when the transition
+	// mutator is running.  This should be used sparingly, all uses will have to be removed in order
+	// to support creating variants on demand.
+	IsAddingDependency() bool
 }
 
 type OutgoingTransitionContext interface {
@@ -510,6 +516,9 @@
 }
 
 func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string {
+	if a.finalPhase {
+		panic("TransitionMutator not allowed in FinalDepsMutators")
+	}
 	if m, ok := ctx.Module().(Module); ok {
 		moduleContext := m.base().baseModuleContextFactory(ctx)
 		return a.mutator.Split(&moduleContext)
@@ -574,6 +583,10 @@
 	return DeviceConfig{c.bp.Config().(Config).deviceConfig}
 }
 
+func (c *incomingTransitionContextImpl) IsAddingDependency() bool {
+	return c.bp.IsAddingDependency()
+}
+
 func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
 	return c.bp.Provider(provider)
 }
@@ -729,6 +742,14 @@
 	b.Module().base().commonProperties.DebugName = name
 }
 
+func (b *bottomUpMutatorContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module {
+	return b.bp.CreateModule(factory, name, props...)
+}
+
+func (b *bottomUpMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
+	return createModule(b, factory, "_bottomUpMutatorModule", props...)
+}
+
 func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module {
 	if b.baseModuleContext.checkedMissingDeps() {
 		panic("Adding deps not allowed after checking for missing deps")
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 21eebd2..b3ef00f 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -81,6 +81,40 @@
 	AssertDeepEquals(t, "foo missing deps", []string{"added_missing_dep", "regular_missing_dep"}, foo.missingDeps)
 }
 
+type testTransitionMutator struct {
+	split              func(ctx BaseModuleContext) []string
+	outgoingTransition func(ctx OutgoingTransitionContext, sourceVariation string) string
+	incomingTransition func(ctx IncomingTransitionContext, incomingVariation string) string
+	mutate             func(ctx BottomUpMutatorContext, variation string)
+}
+
+func (t *testTransitionMutator) Split(ctx BaseModuleContext) []string {
+	if t.split != nil {
+		return t.split(ctx)
+	}
+	return []string{""}
+}
+
+func (t *testTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
+	if t.outgoingTransition != nil {
+		return t.outgoingTransition(ctx, sourceVariation)
+	}
+	return sourceVariation
+}
+
+func (t *testTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	if t.incomingTransition != nil {
+		return t.incomingTransition(ctx, incomingVariation)
+	}
+	return incomingVariation
+}
+
+func (t *testTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+	if t.mutate != nil {
+		t.mutate(ctx, variation)
+	}
+}
+
 func TestModuleString(t *testing.T) {
 	bp := `
 		test {
@@ -94,9 +128,11 @@
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 
 			ctx.PreArchMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("pre_arch", func(ctx BottomUpMutatorContext) {
-					moduleStrings = append(moduleStrings, ctx.Module().String())
-					ctx.CreateVariations("a", "b")
+				ctx.Transition("pre_arch", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						moduleStrings = append(moduleStrings, ctx.Module().String())
+						return []string{"a", "b"}
+					},
 				})
 				ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) {
 					moduleStrings = append(moduleStrings, ctx.Module().String())
@@ -105,16 +141,23 @@
 			})
 
 			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) {
-					moduleStrings = append(moduleStrings, ctx.Module().String())
-					ctx.CreateVariations("c", "d")
+				ctx.Transition("pre_deps", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						moduleStrings = append(moduleStrings, ctx.Module().String())
+						return []string{"c", "d"}
+					},
 				})
 			})
 
 			ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("post_deps", func(ctx BottomUpMutatorContext) {
-					moduleStrings = append(moduleStrings, ctx.Module().String())
-					ctx.CreateLocalVariations("e", "f")
+				ctx.Transition("post_deps", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						moduleStrings = append(moduleStrings, ctx.Module().String())
+						return []string{"e", "f"}
+					},
+					outgoingTransition: func(ctx OutgoingTransitionContext, sourceVariation string) string {
+						return ""
+					},
 				})
 				ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) {
 					moduleStrings = append(moduleStrings, ctx.Module().String())
@@ -138,15 +181,15 @@
 		"foo{pre_arch:b}",
 		"foo{pre_arch:a}",
 
-		// After rename_top_down.
-		"foo_renamed1{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.
-		"foo_renamed1{pre_arch:a,pre_deps:c}",
-		"foo_renamed1{pre_arch:a,pre_deps:d}",
-		"foo_renamed1{pre_arch:b,pre_deps:c}",
+		// 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}",
 
 		// After post_deps.
 		"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}",
@@ -202,8 +245,10 @@
 						ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1")
 					}
 				})
-				ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) {
-					ctx.CreateLocalVariations("a", "b")
+				ctx.Transition("variant", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						return []string{"a", "b"}
+					},
 				})
 			})
 
@@ -243,27 +288,20 @@
 }
 
 func TestNoCreateVariationsInFinalDeps(t *testing.T) {
-	checkErr := func() {
-		if err := recover(); err == nil || !strings.Contains(fmt.Sprintf("%s", err), "not allowed in FinalDepsMutators") {
-			panic("Expected FinalDepsMutators consistency check to fail")
-		}
-	}
-
 	GroupFixturePreparers(
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) {
-					defer checkErr()
-					ctx.CreateVariations("a", "b")
-				})
-				ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) {
-					defer checkErr()
-					ctx.CreateLocalVariations("a", "b")
+				ctx.Transition("vars", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						return []string{"a", "b"}
+					},
 				})
 			})
 
 			ctx.RegisterModuleType("test", mutatorTestModuleFactory)
 		}),
 		FixtureWithRootAndroidBp(`test {name: "foo"}`),
-	).RunTest(t)
+	).
+		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern("not allowed in FinalDepsMutators")).
+		RunTest(t)
 }
diff --git a/android/namespace.go b/android/namespace.go
index ebf85a1..3b9ae3a 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -156,6 +156,9 @@
 			return fmt.Errorf("a namespace must be the first module in the file")
 		}
 	}
+	if (namespace.exportToKati) {
+		r.rootNamespace.visibleNamespaces = append(r.rootNamespace.visibleNamespaces, namespace)
+	}
 	r.sortedNamespaces.add(namespace)
 
 	r.namespacesByDir.Store(namespace.Path, namespace)
diff --git a/android/neverallow.go b/android/neverallow.go
index 62c5e59..b89d150 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -60,6 +60,7 @@
 	AddNeverAllowRules(createCcStubsRule())
 	AddNeverAllowRules(createJavaExcludeStaticLibsRule())
 	AddNeverAllowRules(createProhibitHeaderOnlyRule())
+	AddNeverAllowRules(createLimitNdkExportRule()...)
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -182,6 +183,7 @@
 		"packages/modules/SdkExtensions/derive_sdk",
 		// These are for apps and shouldn't be used by non-SDK variant modules.
 		"prebuilts/ndk",
+		"frameworks/native/libs/binder/ndk",
 		"tools/test/graphicsbenchmark/apps/sample_app",
 		"tools/test/graphicsbenchmark/functional_tests/java",
 		"vendor/xts/gts-tests/hostsidetests/gamedevicecert/apps/javatests",
@@ -212,7 +214,7 @@
 
 func createCcStubsRule() Rule {
 	ccStubsImplementationInstallableProjectsAllowedList := []string{
-		"packages/modules/Virtualization/vm_payload",
+		"packages/modules/Virtualization/libs/libvm_payload",
 	}
 
 	return NeverAllow().
@@ -237,6 +239,7 @@
 			Without("name", "init_first_stage").
 			Without("name", "init_first_stage.microdroid").
 			With("install_in_root", "true").
+			NotModuleType("prebuilt_root").
 			Because("install_in_root is only for init_first_stage."),
 	}
 }
@@ -265,6 +268,22 @@
 		Because("headers_only can only be used for generating framework-minus-apex headers for non-updatable modules")
 }
 
+func createLimitNdkExportRule() []Rule {
+	reason := "If the headers you're trying to export are meant to be a part of the NDK, they should be exposed by an ndk_headers module. If the headers shouldn't be a part of the NDK, the headers should instead be exposed from a separate `cc_library_headers` which consumers depend on."
+	// DO NOT ADD HERE - please consult danalbert@
+	// b/357711733
+	return []Rule{
+		NeverAllow().
+			NotIn("frameworks/native/libs/binder/ndk").
+			ModuleType("ndk_library").
+			WithMatcher("export_header_libs", isSetMatcherInstance).Because(reason),
+		NeverAllow().ModuleType("ndk_library").WithMatcher("export_generated_headers", isSetMatcherInstance).Because(reason),
+		NeverAllow().ModuleType("ndk_library").WithMatcher("export_include_dirs", isSetMatcherInstance).Because(reason),
+		NeverAllow().ModuleType("ndk_library").WithMatcher("export_shared_lib_headers", isSetMatcherInstance).Because(reason),
+		NeverAllow().ModuleType("ndk_library").WithMatcher("export_static_lib_headers", isSetMatcherInstance).Because(reason),
+	}
+}
+
 func neverallowMutator(ctx BottomUpMutatorContext) {
 	m, ok := ctx.Module().(Module)
 	if !ok {
@@ -286,7 +305,7 @@
 			continue
 		}
 
-		if !n.appliesToProperties(properties) {
+		if !n.appliesToProperties(ctx, properties) {
 			continue
 		}
 
@@ -603,9 +622,9 @@
 	return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
 }
 
-func (r *rule) appliesToProperties(properties []interface{}) bool {
-	includeProps := hasAllProperties(properties, r.props)
-	excludeProps := hasAnyProperty(properties, r.unlessProps)
+func (r *rule) appliesToProperties(ctx BottomUpMutatorContext, properties []interface{}) bool {
+	includeProps := hasAllProperties(ctx, properties, r.props)
+	excludeProps := hasAnyProperty(ctx, properties, r.unlessProps)
 	return includeProps && !excludeProps
 }
 
@@ -643,25 +662,25 @@
 	return names
 }
 
-func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
+func hasAnyProperty(ctx BottomUpMutatorContext, properties []interface{}, props []ruleProperty) bool {
 	for _, v := range props {
-		if hasProperty(properties, v) {
+		if hasProperty(ctx, properties, v) {
 			return true
 		}
 	}
 	return false
 }
 
-func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
+func hasAllProperties(ctx BottomUpMutatorContext, properties []interface{}, props []ruleProperty) bool {
 	for _, v := range props {
-		if !hasProperty(properties, v) {
+		if !hasProperty(ctx, properties, v) {
 			return false
 		}
 	}
 	return true
 }
 
-func hasProperty(properties []interface{}, prop ruleProperty) bool {
+func hasProperty(ctx BottomUpMutatorContext, properties []interface{}, prop ruleProperty) bool {
 	for _, propertyStruct := range properties {
 		propertiesValue := reflect.ValueOf(propertyStruct).Elem()
 		for _, v := range prop.fields {
@@ -678,14 +697,14 @@
 			return prop.matcher.Test(value)
 		}
 
-		if matchValue(propertiesValue, check) {
+		if matchValue(ctx, propertiesValue, check) {
 			return true
 		}
 	}
 	return false
 }
 
-func matchValue(value reflect.Value, check func(string) bool) bool {
+func matchValue(ctx BottomUpMutatorContext, value reflect.Value, check func(string) bool) bool {
 	if !value.IsValid() {
 		return false
 	}
@@ -697,19 +716,26 @@
 		value = value.Elem()
 	}
 
-	switch value.Kind() {
-	case reflect.String:
-		return check(value.String())
-	case reflect.Bool:
-		return check(strconv.FormatBool(value.Bool()))
-	case reflect.Int:
-		return check(strconv.FormatInt(value.Int(), 10))
-	case reflect.Slice:
-		slice, ok := value.Interface().([]string)
-		if !ok {
-			panic("Can only handle slice of string")
+	switch v := value.Interface().(type) {
+	case string:
+		return check(v)
+	case bool:
+		return check(strconv.FormatBool(v))
+	case int:
+		return check(strconv.FormatInt((int64)(v), 10))
+	case []string:
+		for _, v := range v {
+			if check(v) {
+				return true
+			}
 		}
-		for _, v := range slice {
+		return false
+	case proptools.Configurable[string]:
+		return check(v.GetOrDefault(ctx, ""))
+	case proptools.Configurable[bool]:
+		return check(strconv.FormatBool(v.GetOrDefault(ctx, false)))
+	case proptools.Configurable[[]string]:
+		for _, v := range v.GetOrDefault(ctx, nil) {
 			if check(v) {
 				return true
 			}
diff --git a/android/notices.go b/android/notices.go
index b9c1682..3c41d92 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -36,10 +36,22 @@
 	return SortedUniqueStrings(dirs)
 }
 
-func modulesLicenseMetadata(ctx BuilderContext, modules ...Module) Paths {
+type BuilderAndOtherModuleProviderContext interface {
+	BuilderContext
+	OtherModuleProviderContext
+}
+
+func modulesLicenseMetadata(ctx OtherModuleProviderContext, modules ...Module) Paths {
 	result := make(Paths, 0, len(modules))
+	mctx, isMctx := ctx.(ModuleContext)
 	for _, module := range modules {
-		if mf := module.base().licenseMetadataFile; mf != nil {
+		var mf Path
+		if isMctx && mctx.Module() == module {
+			mf = mctx.LicenseMetadataFile()
+		} else {
+			mf = OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider).LicenseMetadataFile
+		}
+		if mf != nil {
 			result = append(result, mf)
 		}
 	}
@@ -48,7 +60,7 @@
 
 // buildNoticeOutputFromLicenseMetadata writes out a notice file.
 func buildNoticeOutputFromLicenseMetadata(
-	ctx BuilderContext, tool, ruleName string, outputFile WritablePath,
+	ctx BuilderAndOtherModuleProviderContext, tool, ruleName string, outputFile WritablePath,
 	libraryName string, stripPrefix []string, modules ...Module) {
 	depsFile := outputFile.ReplaceExtension(ctx, strings.TrimPrefix(outputFile.Ext()+".d", "."))
 	rule := NewRuleBuilder(pctx, ctx)
@@ -84,7 +96,7 @@
 // on the license metadata files for the input `modules` defaulting to the
 // current context module if none given.
 func BuildNoticeTextOutputFromLicenseMetadata(
-	ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string,
+	ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string,
 	stripPrefix []string, modules ...Module) {
 	buildNoticeOutputFromLicenseMetadata(ctx, "textnotice", "text_notice_"+ruleName,
 		outputFile, libraryName, stripPrefix, modules...)
@@ -94,7 +106,7 @@
 // on the license metadata files for the input `modules` defaulting to the
 // current context module if none given.
 func BuildNoticeHtmlOutputFromLicenseMetadata(
-	ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string,
+	ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string,
 	stripPrefix []string, modules ...Module) {
 	buildNoticeOutputFromLicenseMetadata(ctx, "htmlnotice", "html_notice_"+ruleName,
 		outputFile, libraryName, stripPrefix, modules...)
@@ -104,7 +116,7 @@
 // on the license metadata files for the input `modules` defaulting to the
 // current context module if none given.
 func BuildNoticeXmlOutputFromLicenseMetadata(
-	ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string,
+	ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string,
 	stripPrefix []string, modules ...Module) {
 	buildNoticeOutputFromLicenseMetadata(ctx, "xmlnotice", "xml_notice_"+ruleName,
 		outputFile, libraryName, stripPrefix, modules...)
diff --git a/android/packaging.go b/android/packaging.go
index ae412e1..0909936 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -15,8 +15,12 @@
 package android
 
 import (
+	"bytes"
+	"encoding/gob"
+	"errors"
 	"fmt"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -55,6 +59,42 @@
 
 	// ArchType of the module which produced this packaging spec
 	archType ArchType
+
+	// List of module names that this packaging spec overrides
+	overrides *[]string
+
+	// Name of the module where this packaging spec is output of
+	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
+	}
+
+	return w.Bytes(), nil
+}
+
+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
 }
 
 func (p *PackagingSpec) Equals(other *PackagingSpec) bool {
@@ -324,7 +364,10 @@
 }
 
 func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec {
-	m := make(map[string]PackagingSpec)
+	// all packaging specs gathered from the dep.
+	var all []PackagingSpec
+	// list of module names overridden
+	var overridden []string
 
 	var arches []ArchType
 	for _, target := range getSupportedTargets(ctx) {
@@ -345,7 +388,8 @@
 		if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
 			return
 		}
-		for _, ps := range child.TransitivePackagingSpecs() {
+		for _, ps := range OtherModuleProviderOrDefault(
+			ctx, child, InstallFilesProvider).TransitivePackagingSpecs.ToList() {
 			if !filterArch(ps) {
 				continue
 			}
@@ -355,17 +399,33 @@
 					continue
 				}
 			}
-			dstPath := ps.relPathInPackage
-			if existingPs, ok := m[dstPath]; ok {
-				if !existingPs.Equals(&ps) {
-					ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps)
-				}
-				continue
+			all = append(all, ps)
+			if ps.overrides != nil {
+				overridden = append(overridden, *ps.overrides...)
 			}
-
-			m[dstPath] = ps
 		}
 	})
+
+	// all minus packaging specs that are overridden
+	var filtered []PackagingSpec
+	for _, ps := range all {
+		if ps.owner != "" && InList(ps.owner, overridden) {
+			continue
+		}
+		filtered = append(filtered, ps)
+	}
+
+	m := make(map[string]PackagingSpec)
+	for _, ps := range filtered {
+		dstPath := ps.relPathInPackage
+		if existingPs, ok := m[dstPath]; ok {
+			if !existingPs.Equals(&ps) {
+				ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps)
+			}
+			continue
+		}
+		m[dstPath] = ps
+	}
 	return m
 }
 
@@ -377,31 +437,59 @@
 // CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
 // entries into the specified directory.
 func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) {
-	if len(specs) == 0 {
+	dirsToSpecs := make(map[WritablePath]map[string]PackagingSpec)
+	dirsToSpecs[dir] = specs
+	return p.CopySpecsToDirs(ctx, builder, dirsToSpecs)
+}
+
+// CopySpecsToDirs is a helper that will add commands to the rule builder to copy the PackagingSpec
+// entries into corresponding directories.
+func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec) (entries []string) {
+	empty := true
+	for _, specs := range dirsToSpecs {
+		if len(specs) > 0 {
+			empty = false
+			break
+		}
+	}
+	if empty {
 		return entries
 	}
+
 	seenDir := make(map[string]bool)
 	preparerPath := PathForModuleOut(ctx, "preparer.sh")
 	cmd := builder.Command().Tool(preparerPath)
 	var sb strings.Builder
 	sb.WriteString("set -e\n")
-	for _, k := range SortedKeys(specs) {
-		ps := specs[k]
-		destPath := filepath.Join(dir.String(), ps.relPathInPackage)
-		destDir := filepath.Dir(destPath)
-		entries = append(entries, ps.relPathInPackage)
-		if _, ok := seenDir[destDir]; !ok {
-			seenDir[destDir] = true
-			sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir))
-		}
-		if ps.symlinkTarget == "" {
-			cmd.Implicit(ps.srcPath)
-			sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath))
-		} else {
-			sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath))
-		}
-		if ps.executable {
-			sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath))
+
+	dirs := make([]WritablePath, 0, len(dirsToSpecs))
+	for dir, _ := range dirsToSpecs {
+		dirs = append(dirs, dir)
+	}
+	sort.Slice(dirs, func(i, j int) bool {
+		return dirs[i].String() < dirs[j].String()
+	})
+
+	for _, dir := range dirs {
+		specs := dirsToSpecs[dir]
+		for _, k := range SortedKeys(specs) {
+			ps := specs[k]
+			destPath := filepath.Join(dir.String(), ps.relPathInPackage)
+			destDir := filepath.Dir(destPath)
+			entries = append(entries, ps.relPathInPackage)
+			if _, ok := seenDir[destDir]; !ok {
+				seenDir[destDir] = true
+				sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir))
+			}
+			if ps.symlinkTarget == "" {
+				cmd.Implicit(ps.srcPath)
+				sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath))
+			} else {
+				sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath))
+			}
+			if ps.executable {
+				sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath))
+			}
 		}
 	}
 
diff --git a/android/packaging_test.go b/android/packaging_test.go
index 19b46fe..f5b1020 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -28,6 +28,7 @@
 	props struct {
 		Deps         []string
 		Skip_install *bool
+		Overrides    []string
 	}
 }
 
@@ -117,6 +118,7 @@
 	}
 
 	result := GroupFixturePreparers(
+		PrepareForTestWithDefaults,
 		PrepareForTestWithArchMutator,
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.RegisterModuleType("component", componentTestModuleFactory)
@@ -650,3 +652,64 @@
 		runPackagingTest(t, config, bp, tc.expected)
 	}
 }
+
+func TestOverrides(t *testing.T) {
+	bpTemplate := `
+		component {
+			name: "foo",
+			deps: ["bar"],
+		}
+
+		component {
+			name: "bar",
+		}
+
+		component {
+			name: "bar_override",
+			overrides: ["bar"],
+		}
+
+		component {
+			name: "baz",
+			deps: ["bar_override"],
+		}
+
+		package_module {
+			name: "package",
+			deps: %DEPS%,
+		}
+	`
+	testcases := []struct {
+		deps     []string
+		expected []string
+	}{
+		{
+			deps:     []string{"foo"},
+			expected: []string{"lib64/foo", "lib64/bar"},
+		},
+		{
+			deps:     []string{"foo", "bar_override"},
+			expected: []string{"lib64/foo", "lib64/bar_override"},
+		},
+		{
+			deps:     []string{"foo", "bar", "bar_override"},
+			expected: []string{"lib64/foo", "lib64/bar_override"},
+		},
+		{
+			deps:     []string{"bar", "bar_override"},
+			expected: []string{"lib64/bar_override"},
+		},
+		{
+			deps:     []string{"foo", "baz"},
+			expected: []string{"lib64/foo", "lib64/baz", "lib64/bar_override"},
+		},
+	}
+	for _, tc := range testcases {
+		config := testConfig{
+			multiTarget:                true,
+			depsCollectFirstTargetOnly: false,
+		}
+		bp := strings.Replace(bpTemplate, "%DEPS%", `["`+strings.Join(tc.deps, `", "`)+`"]`, -1)
+		runPackagingTest(t, config, bp, tc.expected)
+	}
+}
diff --git a/android/paths.go b/android/paths.go
index 6f8f7f2..28656c3 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -15,6 +15,9 @@
 package android
 
 import (
+	"bytes"
+	"encoding/gob"
+	"errors"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -24,7 +27,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
 )
 
@@ -89,8 +91,10 @@
 // the Path methods that rely on module dependencies having been resolved.
 type ModuleWithDepsPathContext interface {
 	EarlyModulePathContext
+	OtherModuleProviderContext
 	VisitDirectDepsBlueprint(visit func(blueprint.Module))
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
+	HasMutatorFinished(mutatorName string) bool
 }
 
 // ModuleMissingDepsPathContext is a subset of *ModuleContext methods required by
@@ -234,6 +238,9 @@
 	// directory, and OutputPath.Join("foo").Rel() would return "foo".
 	Rel() string
 
+	// WithoutRel returns a new Path with no relative path, i.e. Rel() will return the same value as Base().
+	WithoutRel() Path
+
 	// RelativeToTop returns a new path relative to the top, it is provided solely for use in tests.
 	//
 	// It is guaranteed to always return the same type as it is called on, e.g. if called on an
@@ -242,13 +249,13 @@
 	// A standard build has the following structure:
 	//   ../top/
 	//          out/ - make install files go here.
-	//          out/soong - this is the soongOutDir passed to NewTestConfig()
+	//          out/soong - this is the outDir passed to NewTestConfig()
 	//          ... - the source files
 	//
 	// This function converts a path so that it appears relative to the ../top/ directory, i.e.
-	// * Make install paths, which have the pattern "soongOutDir/../<path>" are converted into the top
+	// * Make install paths, which have the pattern "outDir/../<path>" are converted into the top
 	//   relative path "out/<path>"
-	// * Soong install paths and other writable paths, which have the pattern "soongOutDir/<path>" are
+	// * Soong install paths and other writable paths, which have the pattern "outDir/soong/<path>" are
 	//   converted into the top relative path "out/soong/<path>".
 	// * Source paths are already relative to the top.
 	// * Phony paths are not relative to anything.
@@ -258,8 +265,9 @@
 }
 
 const (
-	OutDir      = "out"
-	OutSoongDir = OutDir + "/soong"
+	testOutDir         = "out"
+	testOutSoongSubDir = "/soong"
+	TestOutSoongDir    = testOutDir + testOutSoongSubDir
 )
 
 // WritablePath is a type of path that can be used as an output for build rules.
@@ -460,8 +468,8 @@
 //   - glob, relative to the local module directory, resolves as filepath(s), relative to the local
 //     source directory.
 //   - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
-//     or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
-//     filepath.
+//     or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated
+//     source filepath.
 //
 // Properties passed as the paths argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
@@ -488,8 +496,8 @@
 //   - glob, relative to the local module directory, resolves as filepath(s), relative to the local
 //     source directory. Not valid in excludes.
 //   - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
-//     or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
-//     filepath.
+//     or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated
+//     source filepath.
 //
 // excluding the items (similarly resolved
 // Properties passed as the paths argument must have been annotated with struct tag
@@ -547,13 +555,6 @@
 	return ret
 }
 
-// PathForGoBinary returns the path to the installed location of a bootstrap_go_binary module.
-func PathForGoBinary(ctx PathContext, goBinary bootstrap.GoBinaryTool) Path {
-	goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin")
-	rel := Rel(ctx, goBinaryInstallDir.String(), goBinary.InstallPath())
-	return goBinaryInstallDir.Join(ctx, rel)
-}
-
 // Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax.
 // 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.
@@ -565,10 +566,6 @@
 	if aModule, ok := module.(Module); ok && !aModule.Enabled(ctx) {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
-	if goBinary, ok := module.(bootstrap.GoBinaryTool); ok && tag == "" {
-		goBinaryPath := PathForGoBinary(ctx, goBinary)
-		return Paths{goBinaryPath}, nil
-	}
 	outputFiles, err := outputFilesForModule(ctx, module, tag)
 	if outputFiles != nil && err == nil {
 		return outputFiles, nil
@@ -617,8 +614,8 @@
 //   - glob, relative to the local module directory, resolves as filepath(s), relative to the local
 //     source directory. Not valid in excludes.
 //   - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
-//     or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
-//     filepath.
+//     or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated
+//     source filepath.
 //
 // and a list of the module names of missing module dependencies are returned as the second return.
 // Properties passed as the paths argument must have been annotated with struct tag
@@ -1068,6 +1065,28 @@
 	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
+	}
+
+	return w.Bytes(), nil
+}
+
+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
+}
+
 func (p basePath) Ext() string {
 	return filepath.Ext(p.path)
 }
@@ -1093,8 +1112,8 @@
 	return p
 }
 
-func (p basePath) RelativeToTop() Path {
-	ensureTestOnly()
+func (p basePath) withoutRel() basePath {
+	p.rel = filepath.Base(p.path)
 	return p
 }
 
@@ -1110,6 +1129,11 @@
 	return p
 }
 
+func (p SourcePath) RelativeToTop() Path {
+	ensureTestOnly()
+	return p
+}
+
 // safePathForSource is for paths that we expect are safe -- only for use by go
 // code that is embedding ninja variables in paths
 func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
@@ -1145,6 +1169,31 @@
 	return ret, nil
 }
 
+// pathForSourceRelaxed creates a SourcePath from pathComponents, but does not check that it exists.
+// It differs from pathForSource in that the path is allowed to exist outside of the PathContext.
+func pathForSourceRelaxed(ctx PathContext, pathComponents ...string) (SourcePath, error) {
+	p := filepath.Join(pathComponents...)
+	ret := SourcePath{basePath{p, ""}}
+
+	abs, err := filepath.Abs(ret.String())
+	if err != nil {
+		return ret, err
+	}
+	buildroot, err := filepath.Abs(ctx.Config().outDir)
+	if err != nil {
+		return ret, err
+	}
+	if strings.HasPrefix(abs, buildroot) {
+		return ret, fmt.Errorf("source path %s is in output", abs)
+	}
+
+	if pathtools.IsGlob(ret.String()) {
+		return ret, fmt.Errorf("path may not contain a glob: %s", ret.String())
+	}
+
+	return ret, nil
+}
+
 // existsWithDependencies returns true if the path exists, and adds appropriate dependencies to rerun if the
 // path does not exist.
 func existsWithDependencies(ctx PathGlobContext, path SourcePath) (exists bool, err error) {
@@ -1193,11 +1242,13 @@
 // PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput,
 // the path is relative to the root of the output folder, not the out/soong folder.
 func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path {
-	p, err := validatePath(pathComponents...)
+	path, err := validatePath(pathComponents...)
 	if err != nil {
 		reportPathError(ctx, err)
 	}
-	return basePath{path: filepath.Join(ctx.Config().OutDir(), p)}
+	fullPath := filepath.Join(ctx.Config().OutDir(), path)
+	path = fullPath[len(fullPath)-len(path):]
+	return OutputPath{basePath{path, ""}, ctx.Config().OutDir(), fullPath}
 }
 
 // MaybeExistentPathForSource joins the provided path components and validates that the result
@@ -1215,6 +1266,31 @@
 	return path
 }
 
+// PathForSourceRelaxed joins the provided path components.  Unlike PathForSource,
+// the result is allowed to exist outside of the source dir.
+// On error, it will return a usable, but invalid SourcePath, and report a ModuleError.
+func PathForSourceRelaxed(ctx PathContext, pathComponents ...string) SourcePath {
+	path, err := pathForSourceRelaxed(ctx, pathComponents...)
+	if err != nil {
+		reportPathError(ctx, err)
+	}
+
+	if modCtx, ok := ctx.(ModuleMissingDepsPathContext); ok && ctx.Config().AllowMissingDependencies() {
+		exists, err := existsWithDependencies(modCtx, path)
+		if err != nil {
+			reportPathError(ctx, err)
+		}
+		if !exists {
+			modCtx.AddMissingDependencies([]string{path.String()})
+		}
+	} else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
+		ReportPathErrorf(ctx, "%s: %s", path, err.Error())
+	} else if !exists {
+		ReportPathErrorf(ctx, "source path %s does not exist", path)
+	}
+	return path
+}
+
 // ExistentPathForSource returns an OptionalPath with the SourcePath, rooted from SrcDir, *not*
 // rooted from the module's local source directory, if the path exists, or an empty OptionalPath if
 // it doesn't exist. Dependencies are added so that the ninja file will be regenerated if the state
@@ -1250,6 +1326,11 @@
 	return p.path
 }
 
+func (p SourcePath) WithoutRel() Path {
+	p.basePath = p.basePath.withoutRel()
+	return p
+}
+
 // Join creates a new SourcePath with paths... joined with the current path. The
 // provided paths... may not use '..' to escape from the current path.
 func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
@@ -1300,25 +1381,47 @@
 type OutputPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.SoongOutDir()
-	soongOutDir string
+	// The base out directory for this path, either Config.SoongOutDir() or Config.OutDir()
+	outDir string
 
 	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
+	}
+
+	return w.Bytes(), nil
+}
+
+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
+}
+
 func (p OutputPath) withRel(rel string) OutputPath {
 	p.basePath = p.basePath.withRel(rel)
 	p.fullPath = filepath.Join(p.fullPath, rel)
 	return p
 }
 
-func (p OutputPath) WithoutRel() OutputPath {
-	p.basePath.rel = filepath.Base(p.basePath.path)
+func (p OutputPath) WithoutRel() Path {
+	p.basePath = p.basePath.withoutRel()
 	return p
 }
 
 func (p OutputPath) getSoongOutDir() string {
-	return p.soongOutDir
+	return p.outDir
 }
 
 func (p OutputPath) RelativeToTop() Path {
@@ -1326,8 +1429,13 @@
 }
 
 func (p OutputPath) outputPathRelativeToTop() OutputPath {
-	p.fullPath = StringPathRelativeToTop(p.soongOutDir, p.fullPath)
-	p.soongOutDir = OutSoongDir
+	p.fullPath = StringPathRelativeToTop(p.outDir, p.fullPath)
+	if strings.HasSuffix(p.outDir, testOutSoongSubDir) {
+		p.outDir = TestOutSoongDir
+	} else {
+		// Handle the PathForArbitraryOutput case
+		p.outDir = testOutDir
+	}
 	return p
 }
 
@@ -1344,6 +1452,11 @@
 	basePath
 }
 
+func (t toolDepPath) WithoutRel() Path {
+	t.basePath = t.basePath.withoutRel()
+	return t
+}
+
 func (t toolDepPath) RelativeToTop() Path {
 	ensureTestOnly()
 	return t
@@ -1373,7 +1486,7 @@
 	return OutputPath{basePath{path, ""}, ctx.Config().soongOutDir, fullPath}
 }
 
-// PathsForOutput returns Paths rooted from soongOutDir
+// PathsForOutput returns Paths rooted from outDir
 func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
 	ret := make(WritablePaths, len(paths))
 	for i, path := range paths {
@@ -1557,11 +1670,10 @@
 	ModuleName() string
 	ModuleDir() string
 	ModuleSubDir() string
-	SoongConfigTraceHash() string
 }
 
 func pathForModuleOut(ctx ModuleOutPathContext) OutputPath {
-	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), ctx.SoongConfigTraceHash())
+	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
 }
 
 // PathForModuleOut returns a Path representing the paths... under the module's
@@ -1694,6 +1806,32 @@
 	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
+	}
+
+	return w.Bytes(), nil
+}
+
+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
+}
+
 // Will panic if called from outside a test environment.
 func ensureTestOnly() {
 	if PrefixInList(os.Args, "-test.") {
@@ -1705,14 +1843,19 @@
 func (p InstallPath) RelativeToTop() Path {
 	ensureTestOnly()
 	if p.makePath {
-		p.soongOutDir = OutDir
+		p.soongOutDir = testOutDir
 	} else {
-		p.soongOutDir = OutSoongDir
+		p.soongOutDir = TestOutSoongDir
 	}
 	p.fullPath = filepath.Join(p.soongOutDir, p.path)
 	return p
 }
 
+func (p InstallPath) WithoutRel() Path {
+	p.basePath = p.basePath.withoutRel()
+	return p
+}
+
 func (p InstallPath) getSoongOutDir() string {
 	return p.soongOutDir
 }
@@ -2033,6 +2176,11 @@
 	return p
 }
 
+func (p PhonyPath) WithoutRel() Path {
+	p.basePath = p.basePath.withoutRel()
+	return p
+}
+
 func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
 	panic("Not implemented")
 }
@@ -2049,6 +2197,11 @@
 	return p
 }
 
+func (p testPath) WithoutRel() Path {
+	p.basePath = p.basePath.withoutRel()
+	return p
+}
+
 func (p testPath) String() string {
 	return p.path
 }
diff --git a/android/paths_test.go b/android/paths_test.go
index 93b9b9a..941f0ca 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -1183,9 +1183,6 @@
 		Outs   []string
 		Tagged []string
 	}
-
-	outs   Paths
-	tagged Paths
 }
 
 func pathForModuleSrcOutputFileProviderModuleFactory() Module {
@@ -1196,24 +1193,17 @@
 }
 
 func (p *pathForModuleSrcOutputFileProviderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	var outs, taggedOuts Paths
 	for _, out := range p.props.Outs {
-		p.outs = append(p.outs, PathForModuleOut(ctx, out))
+		outs = append(outs, PathForModuleOut(ctx, out))
 	}
 
 	for _, tagged := range p.props.Tagged {
-		p.tagged = append(p.tagged, PathForModuleOut(ctx, tagged))
+		taggedOuts = append(taggedOuts, PathForModuleOut(ctx, tagged))
 	}
-}
 
-func (p *pathForModuleSrcOutputFileProviderModule) OutputFiles(tag string) (Paths, error) {
-	switch tag {
-	case "":
-		return p.outs, nil
-	case ".tagged":
-		return p.tagged, nil
-	default:
-		return nil, fmt.Errorf("unsupported tag %q", tag)
-	}
+	ctx.SetOutputFiles(outs, "")
+	ctx.SetOutputFiles(taggedOuts, ".tagged")
 }
 
 type pathForModuleSrcTestCase struct {
diff --git a/android/phony.go b/android/phony.go
index 814a9e3..f8db88d 100644
--- a/android/phony.go
+++ b/android/phony.go
@@ -26,14 +26,20 @@
 
 var phonyMapLock sync.Mutex
 
-func getPhonyMap(config Config) phonyMap {
+type ModulePhonyInfo struct {
+	Phonies map[string]Paths
+}
+
+var ModulePhonyProvider = blueprint.NewProvider[ModulePhonyInfo]()
+
+func getSingletonPhonyMap(config Config) phonyMap {
 	return config.Once(phonyMapOnceKey, func() interface{} {
 		return make(phonyMap)
 	}).(phonyMap)
 }
 
-func addPhony(config Config, name string, deps ...Path) {
-	phonyMap := getPhonyMap(config)
+func addSingletonPhony(config Config, name string, deps ...Path) {
+	phonyMap := getSingletonPhonyMap(config)
 	phonyMapLock.Lock()
 	defer phonyMapLock.Unlock()
 	phonyMap[name] = append(phonyMap[name], deps...)
@@ -47,7 +53,15 @@
 var _ SingletonMakeVarsProvider = (*phonySingleton)(nil)
 
 func (p *phonySingleton) GenerateBuildActions(ctx SingletonContext) {
-	p.phonyMap = getPhonyMap(ctx.Config())
+	p.phonyMap = getSingletonPhonyMap(ctx.Config())
+	ctx.VisitAllModules(func(m Module) {
+		if info, ok := OtherModuleProvider(ctx, m, ModulePhonyProvider); ok {
+			for k, v := range info.Phonies {
+				p.phonyMap[k] = append(p.phonyMap[k], v...)
+			}
+		}
+	})
+
 	p.phonyList = SortedKeys(p.phonyMap)
 	for _, phony := range p.phonyList {
 		p.phonyMap[phony] = SortedUniquePaths(p.phonyMap[phony])
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 921fb3c..fd5a6ea 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -61,7 +61,7 @@
 type UserSuppliedPrebuiltProperties struct {
 	// When prefer is set to true the prebuilt will be used instead of any source module with
 	// a matching name.
-	Prefer *bool `android:"arch_variant"`
+	Prefer proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 
 	// When specified this names a Soong config variable that controls the prefer property.
 	//
@@ -148,11 +148,7 @@
 }
 
 func (p *Prebuilt) ForcePrefer() {
-	p.properties.Prefer = proptools.BoolPtr(true)
-}
-
-func (p *Prebuilt) Prefer() bool {
-	return proptools.Bool(p.properties.Prefer)
+	p.properties.Prefer = NewSimpleConfigurable(true)
 }
 
 // SingleSourcePathFromSupplier invokes the supplied supplier for the current module in the
@@ -248,6 +244,8 @@
 	p.srcsPropertyName = srcsPropertyName
 }
 
+// InitPrebuiltModule is the same as InitPrebuiltModuleWithSrcSupplier, but uses the
+// provided list of strings property as the source provider.
 func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) {
 	if srcs == nil {
 		panic(fmt.Errorf("srcs must not be nil"))
@@ -260,6 +258,20 @@
 	InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs")
 }
 
+// InitConfigurablePrebuiltModule is the same as InitPrebuiltModule, but uses a
+// Configurable list of strings property instead of a regular list of strings.
+func InitConfigurablePrebuiltModule(module PrebuiltInterface, srcs *proptools.Configurable[[]string]) {
+	if srcs == nil {
+		panic(fmt.Errorf("srcs must not be nil"))
+	}
+
+	srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
+		return srcs.GetOrDefault(ctx, nil)
+	}
+
+	InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs")
+}
+
 func InitSingleSourcePrebuiltModule(module PrebuiltInterface, srcProps interface{}, srcField string) {
 	srcPropsValue := reflect.ValueOf(srcProps).Elem()
 	srcStructField, _ := srcPropsValue.Type().FieldByName(srcField)
@@ -738,7 +750,7 @@
 	}
 
 	// TODO: use p.Properties.Name and ctx.ModuleDir to override preference
-	return Bool(p.properties.Prefer)
+	return p.properties.Prefer.GetOrDefault(ctx, false)
 }
 
 func (p *Prebuilt) SourceExists() bool {
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index d775ac3..5e4af0b 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -15,7 +15,6 @@
 package android
 
 import (
-	"fmt"
 	"testing"
 
 	"github.com/google/blueprint"
@@ -494,7 +493,6 @@
 	properties struct {
 		Srcs []string `android:"path,arch_variant"`
 	}
-	src Path
 }
 
 func newPrebuiltModule() Module {
@@ -510,24 +508,17 @@
 }
 
 func (p *prebuiltModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	var src Path
 	if len(p.properties.Srcs) >= 1 {
-		p.src = p.prebuilt.SingleSourcePath(ctx)
+		src = p.prebuilt.SingleSourcePath(ctx)
 	}
+	ctx.SetOutputFiles(Paths{src}, "")
 }
 
 func (p *prebuiltModule) Prebuilt() *Prebuilt {
 	return &p.prebuilt
 }
 
-func (p *prebuiltModule) OutputFiles(tag string) (Paths, error) {
-	switch tag {
-	case "":
-		return Paths{p.src}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 type sourceModuleProperties struct {
 	Deps []string `android:"path,arch_variant"`
 }
@@ -583,11 +574,7 @@
 
 func TestPrebuiltErrorCannotListBothSourceAndPrebuiltInContributions(t *testing.T) {
 	selectMainlineModuleContritbutions := GroupFixturePreparers(
-		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_apex_contributions",
-			}
-		}),
+		PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_apex_contributions"),
 	)
 	testPrebuiltErrorWithFixture(t, `Found duplicate variations of the same module in apex_contributions: foo and prebuilt_foo. Please remove one of these`, `
 		source {
diff --git a/android/product_config.go b/android/product_config.go
new file mode 100644
index 0000000..ce3acc9
--- /dev/null
+++ b/android/product_config.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/proptools"
+)
+
+func init() {
+	ctx := InitRegistrationContext
+	ctx.RegisterModuleType("product_config", productConfigFactory)
+}
+
+type productConfigModule struct {
+	ModuleBase
+}
+
+func (p *productConfigModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	if ctx.ModuleName() != "product_config" || ctx.ModuleDir() != "build/soong" {
+		ctx.ModuleErrorf("There can only be one product_config module in build/soong")
+		return
+	}
+	outputFilePath := PathForModuleOut(ctx, p.Name()+".json").OutputPath
+
+	// DeviceProduct can be null so calling ctx.Config().DeviceProduct() may cause null dereference
+	targetProduct := proptools.String(ctx.Config().config.productVariables.DeviceProduct)
+	if targetProduct != "" {
+		targetProduct += "."
+	}
+
+	coverageSuffix := ctx.Config().CoverageSuffix()
+	soongVariablesPath := PathForOutput(ctx, "soong."+targetProduct+coverageSuffix+"variables")
+	extraVariablesPath := PathForOutput(ctx, "soong."+targetProduct+coverageSuffix+"extra.variables")
+
+	rule := NewRuleBuilder(pctx, ctx)
+	rule.Command().BuiltTool("merge_json").
+		Output(outputFilePath).
+		Input(soongVariablesPath).
+		Input(extraVariablesPath).
+		rule.Build("product_config.json", "building product_config.json")
+
+	ctx.SetOutputFiles(Paths{outputFilePath}, "")
+}
+
+// product_config module exports product variables and extra variables as a JSON file.
+func productConfigFactory() Module {
+	module := &productConfigModule{}
+	InitAndroidModule(module)
+	return module
+}
diff --git a/android/product_config_to_bp.go b/android/product_config_to_bp.go
new file mode 100644
index 0000000..680328f
--- /dev/null
+++ b/android/product_config_to_bp.go
@@ -0,0 +1,35 @@
+// 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/provider.go b/android/provider.go
index 3b9c5d2..5ded4cc 100644
--- a/android/provider.go
+++ b/android/provider.go
@@ -14,6 +14,8 @@
 var _ OtherModuleProviderContext = ModuleContext(nil)
 var _ OtherModuleProviderContext = BottomUpMutatorContext(nil)
 var _ OtherModuleProviderContext = TopDownMutatorContext(nil)
+var _ OtherModuleProviderContext = SingletonContext(nil)
+var _ OtherModuleProviderContext = (*TestContext)(nil)
 
 // OtherModuleProvider reads the provider for the given module.  If the provider has been set the value is
 // returned and the boolean is true.  If it has not been set the zero value of the provider's type  is returned
@@ -30,6 +32,11 @@
 	return value.(K), ok
 }
 
+func OtherModuleProviderOrDefault[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) K {
+	value, _ := OtherModuleProvider(ctx, module, provider)
+	return value
+}
+
 // ModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
 // TopDownMutatorContext for use in ModuleProvider.
 type ModuleProviderContext interface {
@@ -56,26 +63,6 @@
 	return value.(K), ok
 }
 
-type SingletonModuleProviderContext interface {
-	moduleProvider(blueprint.Module, blueprint.AnyProviderKey) (any, bool)
-}
-
-var _ SingletonModuleProviderContext = SingletonContext(nil)
-var _ SingletonModuleProviderContext = (*TestContext)(nil)
-
-// SingletonModuleProvider wraps blueprint.SingletonModuleProvider to provide a type-safe method to retrieve the value
-// of the given provider from a module using a SingletonContext.  If the provider has not been set the first return
-// value will be the zero value of the provider's type, and the second return value will be false.  If the provider has
-// been set the second return value will be true.
-func SingletonModuleProvider[K any](ctx SingletonModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
-	value, ok := ctx.moduleProvider(module, provider)
-	if !ok {
-		var k K
-		return k, false
-	}
-	return value.(K), ok
-}
-
 // SetProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
 // TopDownMutatorContext for use in SetProvider.
 type SetProviderContext interface {
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 85e29bd..18bbcab 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -58,6 +58,7 @@
 	sboxInputs       bool
 	sboxManifestPath WritablePath
 	missingDeps      []string
+	args             map[string]string
 }
 
 // NewRuleBuilder returns a newly created RuleBuilder.
@@ -78,6 +79,17 @@
 	return rb
 }
 
+// Set the phony_output argument.
+// This causes the output files to be ignored.
+// If the output isn't created, it's not treated as an error.
+// The build rule is run every time whether or not the output is created.
+func (rb *RuleBuilder) SetPhonyOutput() {
+	if rb.args == nil {
+		rb.args = make(map[string]string)
+	}
+	rb.args["phony_output"] = "true"
+}
+
 // RuleBuilderInstall is a tuple of install from and to locations.
 type RuleBuilderInstall struct {
 	From Path
@@ -451,6 +463,8 @@
 	r.build(name, desc, true)
 }
 
+var sandboxEnvOnceKey = NewOnceKey("sandbox_environment_variables")
+
 func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) {
 	name = ninjaNameEscape(name)
 
@@ -542,6 +556,12 @@
 					To:   proto.String(r.sboxPathForInputRel(input)),
 				})
 			}
+			for _, input := range r.OrderOnlys() {
+				command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
+					From: proto.String(input.String()),
+					To:   proto.String(r.sboxPathForInputRel(input)),
+				})
+			}
 
 			// If using rsp files copy them and their contents into the sbox directory with
 			// the appropriate path mappings.
@@ -562,6 +582,44 @@
 				})
 			}
 
+			// Only allow the build to access certain environment variables
+			command.DontInheritEnv = proto.Bool(true)
+			command.Env = r.ctx.Config().Once(sandboxEnvOnceKey, func() interface{} {
+				// The list of allowed variables was found by running builds of all
+				// genrules and seeing what failed
+				var result []*sbox_proto.EnvironmentVariable
+				inheritedVars := []string{
+					"PATH",
+					"JAVA_HOME",
+					"TMPDIR",
+					// Allow RBE variables because the art tests invoke RBE manually
+					"RBE_log_dir",
+					"RBE_platform",
+					"RBE_server_address",
+					// TODO: RBE_exec_root is set to the absolute path to the root of the source
+					// tree, which we don't want sandboxed actions to find. Remap it to ".".
+					"RBE_exec_root",
+				}
+				for _, v := range inheritedVars {
+					result = append(result, &sbox_proto.EnvironmentVariable{
+						Name: proto.String(v),
+						State: &sbox_proto.EnvironmentVariable_Inherit{
+							Inherit: true,
+						},
+					})
+				}
+				// Set OUT_DIR to the relative path of the sandboxed out directory.
+				// Otherwise, OUT_DIR will be inherited from the rest of the build,
+				// which will allow scripts to escape the sandbox if OUT_DIR is an
+				// absolute path.
+				result = append(result, &sbox_proto.EnvironmentVariable{
+					Name: proto.String("OUT_DIR"),
+					State: &sbox_proto.EnvironmentVariable_Value{
+						Value: sboxOutSubDir,
+					},
+				})
+				return result
+			}).([]*sbox_proto.EnvironmentVariable)
 			command.Chdir = proto.Bool(true)
 		}
 
@@ -726,6 +784,12 @@
 		commandString = proptools.NinjaEscape(commandString)
 	}
 
+	args_vars := make([]string, len(r.args))
+	i := 0
+	for k, _ := range r.args {
+		args_vars[i] = k
+		i++
+	}
 	r.ctx.Build(r.pctx, BuildParams{
 		Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{
 			Command:        commandString,
@@ -734,7 +798,7 @@
 			Rspfile:        proptools.NinjaEscape(rspFile),
 			RspfileContent: rspFileContent,
 			Pool:           pool,
-		}),
+		}, args_vars...),
 		Inputs:          rspFileInputs,
 		Implicits:       inputs,
 		OrderOnly:       r.OrderOnlys(),
@@ -744,6 +808,7 @@
 		Depfile:         depFile,
 		Deps:            depFormat,
 		Description:     desc,
+		Args:            r.args,
 	})
 }
 
diff --git a/android/sbom.go b/android/sbom.go
new file mode 100644
index 0000000..2a5499e
--- /dev/null
+++ b/android/sbom.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 android
+
+import (
+	"io"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/blueprint"
+)
+
+var (
+	// Command line tool to generate SBOM in Soong
+	genSbom = pctx.HostBinToolVariable("genSbom", "gen_sbom")
+
+	// Command to generate SBOM in Soong.
+	genSbomRule = pctx.AndroidStaticRule("genSbomRule", blueprint.RuleParams{
+		Command:     "rm -rf $out && ${genSbom} --output_file ${out} --metadata ${in} --product_out ${productOut} --soong_out ${soongOut} --build_version \"$$(cat ${buildFingerprintFile})\" --product_mfr \"${productManufacturer}\" --json",
+		CommandDeps: []string{"${genSbom}"},
+	}, "productOut", "soongOut", "buildFingerprintFile", "productManufacturer")
+)
+
+func init() {
+	RegisterSbomSingleton(InitRegistrationContext)
+}
+
+func RegisterSbomSingleton(ctx RegistrationContext) {
+	ctx.RegisterParallelSingletonType("sbom_singleton", sbomSingletonFactory)
+}
+
+// sbomSingleton is used to generate build actions of generating SBOM of products.
+type sbomSingleton struct {
+	sbomFile OutputPath
+}
+
+func sbomSingletonFactory() Singleton {
+	return &sbomSingleton{}
+}
+
+// Generates SBOM of products
+func (this *sbomSingleton) GenerateBuildActions(ctx SingletonContext) {
+	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))
+	}
+	prodVars := ctx.Config().productVariables
+	buildFingerprintFile := PathForArbitraryOutput(ctx, "target", "product", String(prodVars.DeviceName), "build_fingerprint.txt")
+	implicits = append(implicits, buildFingerprintFile)
+
+	// Add installed_files.stamp as implicit input, which depends on all installed files of the product.
+	installedFilesStamp := PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "installed_files.stamp")
+	implicits = append(implicits, installedFilesStamp)
+
+	metadataDb := PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "compliance-metadata.db")
+	this.sbomFile = PathForOutput(ctx, "sbom", ctx.Config().DeviceProduct(), "sbom.spdx.json")
+	ctx.Build(pctx, BuildParams{
+		Rule:      genSbomRule,
+		Input:     metadataDb,
+		Implicits: implicits,
+		Output:    this.sbomFile,
+		Args: map[string]string{
+			"productOut":           filepath.Join(ctx.Config().OutDir(), "target", "product", String(prodVars.DeviceName)),
+			"soongOut":             ctx.Config().soongOutDir,
+			"buildFingerprintFile": buildFingerprintFile.String(),
+			"productManufacturer":  ctx.Config().ProductVariables().ProductManufacturer,
+		},
+	})
+
+	if !ctx.Config().UnbundledBuildApps() {
+		// When building SBOM of products, phony rule "sbom" is for generating product SBOM in Soong.
+		ctx.Build(pctx, BuildParams{
+			Rule:   blueprint.Phony,
+			Inputs: []Path{this.sbomFile},
+			Output: PathForPhony(ctx, "sbom"),
+		})
+	}
+}
+
+func (this *sbomSingleton) MakeVars(ctx MakeVarsContext) {
+	// When building SBOM of products
+	if !ctx.Config().UnbundledBuildApps() {
+		ctx.DistForGoalWithFilename("droid", this.sbomFile, "sbom/sbom.spdx.json")
+	}
+}
diff --git a/android/sdk.go b/android/sdk.go
index 121470d..ab9a91c 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -513,6 +513,9 @@
 	// SupportedLinkages returns the names of the linkage variants supported by this module.
 	SupportedLinkages() []string
 
+	// DisablesStrip returns true if the stripping needs to be disabled for this module.
+	DisablesStrip() bool
+
 	// ArePrebuiltsRequired returns true if prebuilts are required in the sdk snapshot, false
 	// otherwise.
 	ArePrebuiltsRequired() bool
@@ -618,6 +621,9 @@
 	// The names of linkage variants supported by this module.
 	SupportedLinkageNames []string
 
+	// StripDisabled returns true if the stripping needs to be disabled for this module.
+	StripDisabled bool
+
 	// When set to true BpPropertyNotRequired indicates that the member type does not require the
 	// property to be specifiable in an Android.bp file.
 	BpPropertyNotRequired bool
@@ -689,6 +695,10 @@
 	return b.SupportedLinkageNames
 }
 
+func (b *SdkMemberTypeBase) DisablesStrip() bool {
+	return b.StripDisabled
+}
+
 // registeredModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports
 // modules.
 var registeredModuleExportsMemberTypes = &sdkRegistry{}
@@ -803,7 +813,6 @@
 
 // SdkMemberContext provides access to information common to a specific member.
 type SdkMemberContext interface {
-
 	// SdkModuleContext returns the module context of the sdk common os variant which is creating the
 	// snapshot.
 	//
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 01b55d0..a9b88fb 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -93,6 +93,15 @@
 	}
 }
 
+func ToSdkKind(s string) SdkKind {
+	for kind := SdkNone; kind <= SdkPrivate; kind++ {
+		if s == kind.String() {
+			return kind
+		}
+	}
+	return SdkInvalid
+}
+
 func (k SdkKind) DefaultJavaLibraryName() string {
 	switch k {
 	case SdkPublic:
diff --git a/android/selects_test.go b/android/selects_test.go
index 3093deb..90d7091 100644
--- a/android/selects_test.go
+++ b/android/selects_test.go
@@ -25,12 +25,14 @@
 
 func TestSelects(t *testing.T) {
 	testCases := []struct {
-		name          string
-		bp            string
-		provider      selectsTestProvider
-		providers     map[string]selectsTestProvider
-		vendorVars    map[string]map[string]string
-		expectedError string
+		name           string
+		bp             string
+		fs             MockFS
+		provider       selectsTestProvider
+		providers      map[string]selectsTestProvider
+		vendorVars     map[string]map[string]string
+		vendorVarTypes map[string]map[string]string
+		expectedError  string
 	}{
 		{
 			name: "basic string list",
@@ -97,6 +99,26 @@
 			},
 		},
 		{
+			name: "Expression in select",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": "foo" + "bar",
+					default: "baz",
+				}),
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("foobar"),
+			},
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "a",
+				},
+			},
+		},
+		{
 			name: "paths with module references",
 			bp: `
 			my_module_type {
@@ -111,20 +133,6 @@
 			expectedError: `"foo" depends on undefined module "c"`,
 		},
 		{
-			name: "Differing types",
-			bp: `
-			my_module_type {
-				name: "foo",
-				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
-					"a": "a.cpp",
-					"b": true,
-					default: "c.cpp",
-				}),
-			}
-			`,
-			expectedError: `Android.bp:8:5: Found select statement with differing types "string" and "bool" in its cases`,
-		},
-		{
 			name: "Select type doesn't match property type",
 			bp: `
 			my_module_type {
@@ -136,7 +144,7 @@
 				}),
 			}
 			`,
-			expectedError: `can't assign bool value to string property "my_string\[0\]"`,
+			expectedError: `can't assign bool value to string property`,
 		},
 		{
 			name: "String list non-default",
@@ -584,6 +592,31 @@
 			},
 		},
 		{
+			name: "Select on boolean soong config variable",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					true: "t",
+					false: "f",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "true",
+				},
+			},
+			vendorVarTypes: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "bool",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("t"),
+			},
+		},
+		{
 			name: "Select on boolean false",
 			bp: `
 			my_module_type {
@@ -799,10 +832,216 @@
 				my_string_list: &[]string{"a.cpp", "c.cpp", "foo.cpp"},
 			},
 		},
+		{
+			name: "Arch variant bool",
+			bp: `
+			my_variable = ["b.cpp"]
+			my_module_type {
+				name: "foo",
+				arch_variant_configurable_bool: false,
+				target: {
+					bionic_arm64: {
+						enabled: true,
+					},
+				},
+			}
+			`,
+			provider: selectsTestProvider{
+				arch_variant_configurable_bool: proptools.BoolPtr(false),
+			},
+		},
+		{
+			name: "Simple string binding",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					any @ my_binding: "hello " + my_binding,
+					default: "goodbye",
+				})
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "world!",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("hello world!"),
+			},
+		},
+		{
+			name: "Any branch with binding not taken",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					any @ my_binding: "hello " + my_binding,
+					default: "goodbye",
+				})
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("goodbye"),
+			},
+		},
+		{
+			name: "Any branch without binding",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					any: "hello",
+					default: "goodbye",
+				})
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "world!",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("hello"),
+			},
+		},
+		{
+			name: "Binding conflicts with file-level variable",
+			bp: `
+			my_binding = "asdf"
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					any @ my_binding: "hello",
+					default: "goodbye",
+				})
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "world!",
+				},
+			},
+			expectedError: "variable already set in inherited scope, previous assignment",
+		},
+		{
+			name: "Binding in combination with file-level variable",
+			bp: `
+			my_var = " there "
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					any @ my_binding: "hello" + my_var + my_binding,
+					default: "goodbye",
+				})
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "world!",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("hello there world!"),
+			},
+		},
+		{
+			name: "Bindings in subdirectory inherits variable",
+			fs: map[string][]byte{
+				"Android.bp": []byte(`
+my_var = "abcd"
+`),
+				"directoryB/Android.bp": []byte(`
+my_module_type {
+	name: "foo",
+	my_string: select(soong_config_variable("my_namespace", "variable_a"), {
+		any @ my_binding: my_var + my_binding,
+		default: "",
+	}),
+}
+`),
+			},
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"variable_a": "e",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("abcde"),
+			},
+		},
+		{
+			name: "Cannot modify variable after referenced by select",
+			bp: `
+my_var = "foo"
+my_module_type {
+	name: "foo",
+	my_string: select(soong_config_variable("my_namespace", "variable_a"), {
+		"a": my_var,
+		default: "",
+	}),
+}
+my_var += "bar"
+`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"variable_a": "b", // notably not the value that causes my_var to be referenced
+				},
+			},
+			expectedError: `modified variable "my_var" with \+= after referencing`,
+		},
+		{
+			name: "Cannot shadow variable with binding",
+			bp: `
+my_var = "foo"
+my_module_type {
+	name: "foo",
+	my_string: select(soong_config_variable("my_namespace", "variable_a"), {
+		any @ my_var: my_var,
+		default: "",
+	}),
+}
+`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"variable_a": "a",
+				},
+			},
+			expectedError: `variable already set in inherited scope, previous assignment:`,
+		},
+		{
+			name: "Basic string list postprocessor",
+			bp: `
+my_defaults {
+	name: "defaults_a",
+	my_string_list: ["a", "b", "c"],
+	string_list_postprocessor_add_to_elements: "1",
+}
+my_defaults {
+	name: "defaults_b",
+	my_string_list: ["d", "e", "f"],
+	string_list_postprocessor_add_to_elements: "2",
+}
+my_module_type {
+	name: "foo",
+	defaults: ["defaults_a", "defaults_b"],
+}
+`,
+			provider: selectsTestProvider{
+				my_string_list: &[]string{"d2", "e2", "f2", "a1", "b1", "c1"},
+			},
+		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			fs := tc.fs
+			if fs == nil {
+				fs = make(MockFS)
+			}
+			if tc.bp != "" {
+				fs["Android.bp"] = []byte(tc.bp)
+			}
 			fixtures := GroupFixturePreparers(
 				PrepareForTestWithDefaults,
 				PrepareForTestWithArchMutator,
@@ -813,12 +1052,14 @@
 				}),
 				FixtureModifyProductVariables(func(variables FixtureProductVariables) {
 					variables.VendorVars = tc.vendorVars
+					variables.VendorVarTypes = tc.vendorVarTypes
 				}),
+				FixtureMergeMockFs(fs),
 			)
 			if tc.expectedError != "" {
 				fixtures = fixtures.ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(tc.expectedError))
 			}
-			result := fixtures.RunTestWithBp(t, tc.bp)
+			result := fixtures.RunTest(t)
 
 			if tc.expectedError == "" {
 				if len(tc.providers) == 0 {
@@ -846,6 +1087,7 @@
 	my_string_list                 *[]string
 	my_paths                       *[]string
 	replacing_string_list          *[]string
+	arch_variant_configurable_bool *bool
 	my_nonconfigurable_bool        *bool
 	my_nonconfigurable_string      *string
 	my_nonconfigurable_string_list []string
@@ -870,6 +1112,7 @@
     my_string_list: %s,
     my_paths: %s,
 	replacing_string_list %s,
+	arch_variant_configurable_bool %v
 	my_nonconfigurable_bool: %v,
 	my_nonconfigurable_string: %s,
 	my_nonconfigurable_string_list: %s,
@@ -879,6 +1122,7 @@
 		p.my_string_list,
 		p.my_paths,
 		p.replacing_string_list,
+		p.arch_variant_configurable_bool,
 		p.my_nonconfigurable_bool,
 		myNonconfigurableStringStr,
 		p.my_nonconfigurable_string_list,
@@ -893,6 +1137,7 @@
 	My_string_list                 proptools.Configurable[[]string]
 	My_paths                       proptools.Configurable[[]string] `android:"path"`
 	Replacing_string_list          proptools.Configurable[[]string] `android:"replace_instead_of_append,arch_variant"`
+	Arch_variant_configurable_bool proptools.Configurable[bool]     `android:"replace_instead_of_append,arch_variant"`
 	My_nonconfigurable_bool        *bool
 	My_nonconfigurable_string      *string
 	My_nonconfigurable_string_list []string
@@ -923,6 +1168,7 @@
 		my_string_list:                 optionalToPtr(p.properties.My_string_list.Get(ctx)),
 		my_paths:                       optionalToPtr(p.properties.My_paths.Get(ctx)),
 		replacing_string_list:          optionalToPtr(p.properties.Replacing_string_list.Get(ctx)),
+		arch_variant_configurable_bool: optionalToPtr(p.properties.Arch_variant_configurable_bool.Get(ctx)),
 		my_nonconfigurable_bool:        p.properties.My_nonconfigurable_bool,
 		my_nonconfigurable_string:      p.properties.My_nonconfigurable_string,
 		my_nonconfigurable_string_list: p.properties.My_nonconfigurable_string_list,
@@ -937,9 +1183,15 @@
 	return m
 }
 
+type selectsMockDefaultsProperties struct {
+	String_list_postprocessor_add_to_elements string
+}
+
 type selectsMockModuleDefaults struct {
 	ModuleBase
 	DefaultsModuleBase
+	myProperties       selectsMockModuleProperties
+	defaultsProperties selectsMockDefaultsProperties
 }
 
 func (d *selectsMockModuleDefaults) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -949,10 +1201,22 @@
 	module := &selectsMockModuleDefaults{}
 
 	module.AddProperties(
-		&selectsMockModuleProperties{},
+		&module.myProperties,
+		&module.defaultsProperties,
 	)
 
 	InitDefaultsModule(module)
 
+	AddLoadHook(module, func(lhc LoadHookContext) {
+		if module.defaultsProperties.String_list_postprocessor_add_to_elements != "" {
+			module.myProperties.My_string_list.AddPostProcessor(func(x []string) []string {
+				for i := range x {
+					x[i] = x[i] + module.defaultsProperties.String_list_postprocessor_add_to_elements
+				}
+				return x
+			})
+		}
+	})
+
 	return module
 }
diff --git a/android/singleton.go b/android/singleton.go
index d364384..913bf6a 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -35,7 +35,7 @@
 	// Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context.
 	ModuleVariantsFromName(referer Module, name string) []Module
 
-	moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
+	otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
 	Errorf(format string, args ...interface{})
@@ -90,6 +90,10 @@
 
 	// OtherModulePropertyErrorf reports an error on the line number of the given property of the given module
 	OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{})
+
+	// HasMutatorFinished returns true if the given mutator has finished running.
+	// It will panic if given an invalid mutator name.
+	HasMutatorFinished(mutatorName string) bool
 }
 
 type singletonAdaptor struct {
@@ -177,7 +181,7 @@
 }
 
 func (s *singletonContextAdaptor) Phony(name string, deps ...Path) {
-	addPhony(s.Config(), name, deps...)
+	addSingletonPhony(s.Config(), name, deps...)
 }
 
 func (s *singletonContextAdaptor) SetOutDir(pctx PackageContext, value string) {
@@ -279,10 +283,14 @@
 	return result
 }
 
-func (s *singletonContextAdaptor) moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
+func (s *singletonContextAdaptor) otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
 	return s.SingletonContext.ModuleProvider(module, provider)
 }
 
 func (s *singletonContextAdaptor) OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) {
 	s.blueprintSingletonContext().OtherModulePropertyErrorf(module, property, format, args...)
 }
+
+func (s *singletonContextAdaptor) HasMutatorFinished(mutatorName string) bool {
+	return s.blueprintSingletonContext().HasMutatorFinished(mutatorName)
+}
diff --git a/android/singleton_module.go b/android/singleton_module.go
index 2351738..43028e8 100644
--- a/android/singleton_module.go
+++ b/android/singleton_module.go
@@ -68,7 +68,7 @@
 func (smb *SingletonModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
 	smb.lock.Lock()
 	if smb.variant != "" {
-		ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only  have one variant", smb.variant)
+		ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only have one variant", smb.variant)
 	}
 	smb.variant = ctx.ModuleSubDir()
 	smb.lock.Unlock()
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
index 3b1bf39..3b8c6b2 100644
--- a/android/singleton_module_test.go
+++ b/android/singleton_module_test.go
@@ -96,12 +96,6 @@
 	}
 }
 
-func testVariantSingletonModuleMutator(ctx BottomUpMutatorContext) {
-	if _, ok := ctx.Module().(*testSingletonModule); ok {
-		ctx.CreateVariations("a", "b")
-	}
-}
-
 func TestVariantSingletonModule(t *testing.T) {
 	if testing.Short() {
 		t.Skip("test fails with data race enabled")
@@ -116,7 +110,11 @@
 		prepareForSingletonModuleTest,
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
 			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
+				ctx.Transition("test_singleton_module_mutator", &testTransitionMutator{
+					split: func(ctx BaseModuleContext) []string {
+						return []string{"a", "b"}
+					},
+				})
 			})
 		}),
 	).
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 38db929..e0b1d7c 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -463,57 +463,6 @@
 	}).(map[string]blueprint.ModuleFactory)
 }
 
-// tracingConfig is a wrapper to soongconfig.SoongConfig which records all accesses to SoongConfig.
-type tracingConfig struct {
-	config    soongconfig.SoongConfig
-	boolSet   map[string]bool
-	stringSet map[string]string
-	isSetSet  map[string]bool
-}
-
-func (c *tracingConfig) Bool(name string) bool {
-	c.boolSet[name] = c.config.Bool(name)
-	return c.boolSet[name]
-}
-
-func (c *tracingConfig) String(name string) string {
-	c.stringSet[name] = c.config.String(name)
-	return c.stringSet[name]
-}
-
-func (c *tracingConfig) IsSet(name string) bool {
-	c.isSetSet[name] = c.config.IsSet(name)
-	return c.isSetSet[name]
-}
-
-func (c *tracingConfig) getTrace() soongConfigTrace {
-	ret := soongConfigTrace{}
-
-	for k, v := range c.boolSet {
-		ret.Bools = append(ret.Bools, fmt.Sprintf("%q:%t", k, v))
-	}
-	for k, v := range c.stringSet {
-		ret.Strings = append(ret.Strings, fmt.Sprintf("%q:%q", k, v))
-	}
-	for k, v := range c.isSetSet {
-		ret.IsSets = append(ret.IsSets, fmt.Sprintf("%q:%t", k, v))
-	}
-
-	return ret
-}
-
-func newTracingConfig(config soongconfig.SoongConfig) *tracingConfig {
-	c := tracingConfig{
-		config:    config,
-		boolSet:   make(map[string]bool),
-		stringSet: make(map[string]string),
-		isSetSet:  make(map[string]bool),
-	}
-	return &c
-}
-
-var _ soongconfig.SoongConfig = (*tracingConfig)(nil)
-
 // configModuleFactory takes an existing soongConfigModuleFactory and a
 // ModuleType to create a new ModuleFactory that uses a custom loadhook.
 func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType) blueprint.ModuleFactory {
@@ -561,8 +510,8 @@
 		// conditional on Soong config variables by reading the product
 		// config variables from Make.
 		AddLoadHook(module, func(ctx LoadHookContext) {
-			tracingConfig := newTracingConfig(ctx.Config().VendorConfig(moduleType.ConfigNamespace))
-			newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, tracingConfig)
+			config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
+			newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
 			if err != nil {
 				ctx.ModuleErrorf("%s", err)
 				return
@@ -570,8 +519,6 @@
 			for _, ps := range newProps {
 				ctx.AppendProperties(ps)
 			}
-
-			module.(Module).base().commonProperties.SoongConfigTrace = tracingConfig.getTrace()
 		})
 		return module, props
 	}
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index a6b2c51..04aafde 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"path/filepath"
 	"testing"
 )
 
@@ -506,197 +505,3 @@
 		})
 	}
 }
-
-func TestSoongConfigModuleTrace(t *testing.T) {
-	bp := `
-		soong_config_module_type {
-			name: "acme_test",
-			module_type: "test",
-			config_namespace: "acme",
-			variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
-			bool_variables: ["feature2", "unused_feature", "always_true"],
-			value_variables: ["size", "unused_size"],
-			properties: ["cflags", "srcs", "defaults"],
-		}
-
-		soong_config_module_type {
-			name: "acme_test_defaults",
-			module_type: "test_defaults",
-			config_namespace: "acme",
-			variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
-			bool_variables: ["feature2", "unused_feature", "always_true"],
-			value_variables: ["size", "unused_size"],
-			properties: ["cflags", "srcs", "defaults"],
-		}
-
-		soong_config_string_variable {
-			name: "board",
-			values: ["soc_a", "soc_b", "soc_c"],
-		}
-
-		soong_config_string_variable {
-			name: "unused_string_var",
-			values: ["a", "b"],
-		}
-
-		soong_config_bool_variable {
-			name: "feature1",
-		}
-
-		soong_config_bool_variable {
-			name: "FEATURE3",
-		}
-
-		test_defaults {
-			name: "test_defaults",
-			cflags: ["DEFAULT"],
-		}
-
-		test {
-			name: "normal",
-			defaults: ["test_defaults"],
-		}
-
-		acme_test {
-			name: "board_1",
-			defaults: ["test_defaults"],
-			soong_config_variables: {
-				board: {
-					soc_a: {
-						cflags: ["-DSOC_A"],
-					},
-				},
-			},
-		}
-
-		acme_test {
-			name: "board_2",
-			defaults: ["test_defaults"],
-			soong_config_variables: {
-				board: {
-					soc_a: {
-						cflags: ["-DSOC_A"],
-					},
-				},
-			},
-		}
-
-		acme_test {
-			name: "size",
-			defaults: ["test_defaults"],
-			soong_config_variables: {
-				size: {
-					cflags: ["-DSIZE=%s"],
-				},
-			},
-		}
-
-		acme_test {
-			name: "board_and_size",
-			defaults: ["test_defaults"],
-			soong_config_variables: {
-				board: {
-					soc_a: {
-						cflags: ["-DSOC_A"],
-					},
-				},
-				size: {
-					cflags: ["-DSIZE=%s"],
-				},
-			},
-		}
-
-		acme_test_defaults {
-			name: "board_defaults",
-			soong_config_variables: {
-				board: {
-					soc_a: {
-						cflags: ["-DSOC_A"],
-					},
-				},
-			},
-		}
-
-		acme_test_defaults {
-			name: "size_defaults",
-			soong_config_variables: {
-				size: {
-					cflags: ["-DSIZE=%s"],
-				},
-			},
-		}
-
-		test {
-			name: "board_and_size_with_defaults",
-			defaults: ["board_defaults", "size_defaults"],
-		}
-    `
-
-	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
-		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
-			variables.VendorVars = vars
-		})
-	}
-
-	preparer := fixtureForVendorVars(map[string]map[string]string{
-		"acme": {
-			"board":    "soc_a",
-			"size":     "42",
-			"feature1": "true",
-			"feature2": "false",
-			// FEATURE3 unset
-			"unused_feature":    "true", // unused
-			"unused_size":       "1",    // unused
-			"unused_string_var": "a",    // unused
-			"always_true":       "true",
-		},
-	})
-
-	t.Run("soong config trace hash", func(t *testing.T) {
-		result := GroupFixturePreparers(
-			preparer,
-			PrepareForTestWithDefaults,
-			PrepareForTestWithSoongConfigModuleBuildComponents,
-			prepareForSoongConfigTestModule,
-			FixtureRegisterWithContext(func(ctx RegistrationContext) {
-				ctx.FinalDepsMutators(registerSoongConfigTraceMutator)
-			}),
-			FixtureWithRootAndroidBp(bp),
-		).RunTest(t)
-
-		// Hashes of modules not using soong config should be empty
-		normal := result.ModuleForTests("normal", "").Module().(*soongConfigTestModule)
-		AssertDeepEquals(t, "normal hash", normal.base().commonProperties.SoongConfigTraceHash, "")
-		AssertDeepEquals(t, "normal hash out", normal.outputPath.RelativeToTop().String(), "out/soong/.intermediates/normal/test")
-
-		board1 := result.ModuleForTests("board_1", "").Module().(*soongConfigTestModule)
-		board2 := result.ModuleForTests("board_2", "").Module().(*soongConfigTestModule)
-		size := result.ModuleForTests("size", "").Module().(*soongConfigTestModule)
-
-		// Trace mutator sets soong config trace hash correctly
-		board1Hash := board1.base().commonProperties.SoongConfigTrace.hash()
-		board1Output := board1.outputPath.RelativeToTop().String()
-		AssertDeepEquals(t, "board hash calc", board1Hash, board1.base().commonProperties.SoongConfigTraceHash)
-		AssertDeepEquals(t, "board hash path", board1Output, filepath.Join("out/soong/.intermediates/board_1", board1Hash, "test"))
-
-		sizeHash := size.base().commonProperties.SoongConfigTrace.hash()
-		sizeOutput := size.outputPath.RelativeToTop().String()
-		AssertDeepEquals(t, "size hash calc", sizeHash, size.base().commonProperties.SoongConfigTraceHash)
-		AssertDeepEquals(t, "size hash path", sizeOutput, filepath.Join("out/soong/.intermediates/size", sizeHash, "test"))
-
-		// Trace should be identical for modules using the same set of variables
-		AssertDeepEquals(t, "board trace", board1.base().commonProperties.SoongConfigTrace, board2.base().commonProperties.SoongConfigTrace)
-		AssertDeepEquals(t, "board hash", board1.base().commonProperties.SoongConfigTraceHash, board2.base().commonProperties.SoongConfigTraceHash)
-
-		// Trace hash should be different for different sets of soong variables
-		AssertBoolEquals(t, "board hash not equal to size hash", board1.base().commonProperties.SoongConfigTraceHash == size.commonProperties.SoongConfigTraceHash, false)
-
-		boardSize := result.ModuleForTests("board_and_size", "").Module().(*soongConfigTestModule)
-		boardSizeDefaults := result.ModuleForTests("board_and_size_with_defaults", "").Module()
-
-		// Trace should propagate
-		AssertDeepEquals(t, "board_size hash calc", boardSize.base().commonProperties.SoongConfigTrace.hash(), boardSize.base().commonProperties.SoongConfigTraceHash)
-		AssertDeepEquals(t, "board_size trace", boardSize.base().commonProperties.SoongConfigTrace, boardSizeDefaults.base().commonProperties.SoongConfigTrace)
-		AssertDeepEquals(t, "board_size hash", boardSize.base().commonProperties.SoongConfigTraceHash, boardSizeDefaults.base().commonProperties.SoongConfigTraceHash)
-	})
-}
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index 8fe1ff1..5a6df26 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -9,7 +9,6 @@
         "blueprint",
         "blueprint-parser",
         "blueprint-proptools",
-        "soong-bazel",
         "soong-starlark-format",
     ],
     srcs: [
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 87af774..f6046d0 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -824,11 +824,16 @@
 			}
 			field.Set(newField)
 		case reflect.Struct:
-			fieldName = append(fieldName, propStruct.Type().Field(i).Name)
-			if err := s.printfIntoPropertyRecursive(fieldName, field, configValues); err != nil {
-				return err
+			if proptools.IsConfigurable(field.Type()) {
+				fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+				return fmt.Errorf("soong_config_variables.%s.%s: list variables are not supported on configurable properties", s.variable, strings.Join(fieldName, "."))
+			} else {
+				fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+				if err := s.printfIntoPropertyRecursive(fieldName, field, configValues); err != nil {
+					return err
+				}
+				fieldName = fieldName[:len(fieldName)-1]
 			}
-			fieldName = fieldName[:len(fieldName)-1]
 		default:
 			fieldName = append(fieldName, propStruct.Type().Field(i).Name)
 			return fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, strings.Join(fieldName, "."), kind)
diff --git a/android/team_proto/Android.bp b/android/team_proto/Android.bp
index 7e2a4c1..5faaaf1 100644
--- a/android/team_proto/Android.bp
+++ b/android/team_proto/Android.bp
@@ -40,4 +40,8 @@
     proto: {
         canonical_path_from_root: false,
     },
+    visibility: [
+        "//build/soong:__subpackages__",
+        "//tools/asuite/team_build_scripts",
+    ],
 }
diff --git a/android/test_suites.go b/android/test_suites.go
index ff75f26..936d2b6 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -47,7 +47,8 @@
 					files[testSuite] = make(map[string]InstallPaths)
 				}
 				name := ctx.ModuleName(m)
-				files[testSuite][name] = append(files[testSuite][name], tsm.FilesToInstall()...)
+				files[testSuite][name] = append(files[testSuite][name],
+					OtherModuleProviderOrDefault(ctx, tsm, InstallFilesProvider).InstallFiles...)
 			}
 		}
 	})
diff --git a/android/testing.go b/android/testing.go
index 6fb2997..196b22e 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -126,6 +126,10 @@
 	ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
 })
 
+var PrepareForTestVintfFragmentModules = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	registerVintfFragmentComponents(ctx)
+})
+
 // Test fixture preparer that will register most java build components.
 //
 // Singletons and mutators should only be added here if they are needed for a majority of java
@@ -149,6 +153,7 @@
 	PrepareForTestWithPackageModule,
 	PrepareForTestWithPrebuilts,
 	PrepareForTestWithVisibility,
+	PrepareForTestVintfFragmentModules,
 )
 
 // Prepares an integration test with all build components from the android package.
@@ -174,6 +179,16 @@
 	config.TestAllowNonExistentPaths = false
 })
 
+// PrepareForTestWithBuildFlag returns a FixturePreparer that sets the given flag to the given value.
+func PrepareForTestWithBuildFlag(flag, value string) FixturePreparer {
+	return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+		if variables.BuildFlags == nil {
+			variables.BuildFlags = make(map[string]string)
+		}
+		variables.BuildFlags[flag] = value
+	})
+}
+
 func NewTestArchContext(config Config) *TestContext {
 	ctx := NewTestContext(config)
 	ctx.preDeps = append(ctx.preDeps, registerArchMutator)
@@ -202,7 +217,7 @@
 	ctx.PreArchMutators(f)
 }
 
-func (ctx *TestContext) moduleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
+func (ctx *TestContext) otherModuleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
 	return ctx.Context.ModuleProvider(m, p)
 }
 
@@ -220,10 +235,14 @@
 
 func (ctx *TestContext) OtherModuleProviderAdaptor() OtherModuleProviderContext {
 	return NewOtherModuleProviderAdaptor(func(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
-		return ctx.moduleProvider(module, provider)
+		return ctx.otherModuleProvider(module, provider)
 	})
 }
 
+func (ctx *TestContext) OtherModulePropertyErrorf(module Module, property string, fmt_ string, args ...interface{}) {
+	panic(fmt.Sprintf(fmt_, args...))
+}
+
 // registeredComponentOrder defines the order in which a sortableComponent type is registered at
 // runtime and provides support for reordering the components registered for a test in the same
 // way.
@@ -818,15 +837,15 @@
 // containing at most one instance of the temporary build directory at the start of the path while
 // this assumes that there can be any number at any position.
 func normalizeStringRelativeToTop(config Config, s string) string {
-	// The soongOutDir usually looks something like: /tmp/testFoo2345/001
+	// The outDir usually looks something like: /tmp/testFoo2345/001
 	//
-	// Replace any usage of the soongOutDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
+	// Replace any usage of the outDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
 	// "out/soong".
 	outSoongDir := filepath.Clean(config.soongOutDir)
 	re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`)
 	s = re.ReplaceAllString(s, "out/soong")
 
-	// Replace any usage of the soongOutDir/.. with out, e.g. replace "/tmp/testFoo2345" with
+	// Replace any usage of the outDir/.. with out, e.g. replace "/tmp/testFoo2345" with
 	// "out". This must come after the previous replacement otherwise this would replace
 	// "/tmp/testFoo2345/001" with "out/001" instead of "out/soong".
 	outDir := filepath.Dir(outSoongDir)
@@ -1014,28 +1033,21 @@
 	return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests())
 }
 
-// OutputFiles first checks if module base outputFiles property has any output
+// OutputFiles checks if module base outputFiles property has any output
 // files can be used to return.
-// If not, it calls OutputFileProducer.OutputFiles on the
-// encapsulated module, exits the test immediately if there is an error and
+// Exits the test immediately if there is an error and
 // otherwise returns the result of calling Paths.RelativeToTop
 // on the returned Paths.
-func (m TestingModule) OutputFiles(t *testing.T, tag string) Paths {
-	// TODO: add non-empty-string tag case and remove OutputFileProducer part
-	if tag == "" && m.module.base().outputFiles.DefaultOutputFiles != nil {
-		return m.module.base().outputFiles.DefaultOutputFiles.RelativeToTop()
+func (m TestingModule) OutputFiles(ctx *TestContext, t *testing.T, tag string) Paths {
+	outputFiles := OtherModuleProviderOrDefault(ctx.OtherModuleProviderAdaptor(), m.Module(), OutputFilesProvider)
+	if tag == "" && outputFiles.DefaultOutputFiles != nil {
+		return outputFiles.DefaultOutputFiles.RelativeToTop()
+	} else if taggedOutputFiles, hasTag := outputFiles.TaggedOutputFiles[tag]; hasTag {
+		return taggedOutputFiles.RelativeToTop()
 	}
 
-	producer, ok := m.module.(OutputFileProducer)
-	if !ok {
-		t.Fatalf("%q must implement OutputFileProducer\n", m.module.Name())
-	}
-	paths, err := producer.OutputFiles(tag)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	return paths.RelativeToTop()
+	t.Fatal(fmt.Errorf("No test output file has been set for tag %q", tag))
+	return nil
 }
 
 // TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual
@@ -1237,8 +1249,14 @@
 	}
 
 	if isRel {
-		// The path is in the soong out dir so indicate that in the relative path.
-		return filepath.Join("out/soong", rel)
+		if strings.HasSuffix(soongOutDir, testOutSoongSubDir) {
+			// The path is in the soong out dir so indicate that in the relative path.
+			return filepath.Join(TestOutSoongDir, rel)
+		} else {
+			// Handle the PathForArbitraryOutput case
+			return filepath.Join(testOutDir, rel)
+
+		}
 	}
 
 	// Check to see if the path is relative to the top level out dir.
@@ -1308,7 +1326,15 @@
 	return ctx.ctx.Config()
 }
 
-func PanickingConfigAndErrorContext(ctx *TestContext) ConfigAndErrorContext {
+func (ctx *panickingConfigAndErrorContext) HasMutatorFinished(mutatorName string) bool {
+	return ctx.ctx.HasMutatorFinished(mutatorName)
+}
+
+func (ctx *panickingConfigAndErrorContext) otherModuleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
+	return ctx.ctx.otherModuleProvider(m, p)
+}
+
+func PanickingConfigAndErrorContext(ctx *TestContext) ConfigurableEvaluatorContext {
 	return &panickingConfigAndErrorContext{
 		ctx: ctx,
 	}
diff --git a/android/util.go b/android/util.go
index e21e66b..2d269b7 100644
--- a/android/util.go
+++ b/android/util.go
@@ -177,6 +177,41 @@
 	return m
 }
 
+// PrettyConcat returns the formatted concatenated string suitable for displaying user-facing
+// messages.
+func PrettyConcat(list []string, quote bool, lastSep string) string {
+	if len(list) == 0 {
+		return ""
+	}
+
+	quoteStr := func(v string) string {
+		if !quote {
+			return v
+		}
+		return fmt.Sprintf("%q", v)
+	}
+
+	if len(list) == 1 {
+		return quoteStr(list[0])
+	}
+
+	var sb strings.Builder
+	for i, val := range list {
+		if i > 0 {
+			sb.WriteString(", ")
+		}
+		if i == len(list)-1 {
+			sb.WriteString(lastSep)
+			if lastSep != "" {
+				sb.WriteString(" ")
+			}
+		}
+		sb.WriteString(quoteStr(val))
+	}
+
+	return sb.String()
+}
+
 // ListSetDifference checks if the two lists contain the same elements. It returns
 // a boolean which is true if there is a difference, and then returns lists of elements
 // that are in l1 but not l2, and l2 but not l1.
@@ -201,6 +236,12 @@
 	return listsDiffer, diff1, diff2
 }
 
+// Returns true if the two lists have common elements.
+func HasIntersection[T comparable](l1, l2 []T) bool {
+	_, a, b := ListSetDifference(l1, l2)
+	return len(a)+len(b) < len(setFromList(l1))+len(setFromList(l2))
+}
+
 // Returns true if the given string s is prefixed with any string in the given prefix list.
 func HasAnyPrefix(s string, prefixList []string) bool {
 	for _, prefix := range prefixList {
diff --git a/android/util_test.go b/android/util_test.go
index 8e73d83..b76ffcf 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -818,3 +818,100 @@
 		})
 	}
 }
+
+var hasIntersectionTestCases = []struct {
+	name     string
+	l1       []string
+	l2       []string
+	expected bool
+}{
+	{
+		name:     "empty",
+		l1:       []string{"a", "b", "c"},
+		l2:       []string{},
+		expected: false,
+	},
+	{
+		name:     "both empty",
+		l1:       []string{},
+		l2:       []string{},
+		expected: false,
+	},
+	{
+		name:     "identical",
+		l1:       []string{"a", "b", "c"},
+		l2:       []string{"a", "b", "c"},
+		expected: true,
+	},
+	{
+		name:     "duplicates",
+		l1:       []string{"a", "a", "a"},
+		l2:       []string{"a", "b", "c"},
+		expected: true,
+	},
+	{
+		name:     "duplicates with no intersection",
+		l1:       []string{"d", "d", "d", "d"},
+		l2:       []string{"a", "b", "c"},
+		expected: false,
+	},
+}
+
+func TestHasIntersection(t *testing.T) {
+	for _, testCase := range hasIntersectionTestCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			hasIntersection := HasIntersection(testCase.l1, testCase.l2)
+			if !reflect.DeepEqual(hasIntersection, testCase.expected) {
+				t.Errorf("expected %#v, got %#v", testCase.expected, hasIntersection)
+			}
+		})
+	}
+}
+
+var prettyConcatTestCases = []struct {
+	name          string
+	list          []string
+	quote         bool
+	lastSeparator string
+	expected      string
+}{
+	{
+		name:          "empty",
+		list:          []string{},
+		quote:         false,
+		lastSeparator: "and",
+		expected:      ``,
+	},
+	{
+		name:          "single",
+		list:          []string{"a"},
+		quote:         true,
+		lastSeparator: "and",
+		expected:      `"a"`,
+	},
+	{
+		name:          "with separator",
+		list:          []string{"a", "b", "c"},
+		quote:         true,
+		lastSeparator: "or",
+		expected:      `"a", "b", or "c"`,
+	},
+	{
+		name:          "without separator",
+		list:          []string{"a", "b", "c"},
+		quote:         false,
+		lastSeparator: "",
+		expected:      `a, b, c`,
+	},
+}
+
+func TestPrettyConcat(t *testing.T) {
+	for _, testCase := range prettyConcatTestCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			concatString := PrettyConcat(testCase.list, testCase.quote, testCase.lastSeparator)
+			if !reflect.DeepEqual(concatString, testCase.expected) {
+				t.Errorf("expected %#v, got %#v", testCase.expected, concatString)
+			}
+		})
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index 2500140..e0d512d 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -121,6 +121,7 @@
 		// are used for dogfooding and performance testing, and should be as similar to user builds
 		// as possible.
 		Debuggable struct {
+			Apk             *string
 			Cflags          []string
 			Cppflags        []string
 			Init_rc         []string
@@ -132,9 +133,11 @@
 				Keep_symbols                 *bool
 				Keep_symbols_and_debug_frame *bool
 			}
-			Static_libs       []string
-			Whole_static_libs []string
-			Shared_libs       []string
+			Static_libs         []string
+			Exclude_static_libs []string
+			Whole_static_libs   []string
+			Shared_libs         []string
+			Jni_libs            []string
 
 			Cmdline []string
 
@@ -185,10 +188,10 @@
 		// release_aidl_use_unfrozen is "true" when a device can
 		// use the unfrozen versions of AIDL interfaces.
 		Release_aidl_use_unfrozen struct {
-			Cflags          []string
-			Cmd             *string
-			Required        []string
-			Vintf_fragments []string
+			Cflags                 []string
+			Cmd                    *string
+			Required               []string
+			Vintf_fragment_modules []string
 		}
 	} `android:"arch_variant"`
 }
@@ -199,11 +202,12 @@
 	// Suffix to add to generated Makefiles
 	Make_suffix *string `json:",omitempty"`
 
-	BuildId             *string `json:",omitempty"`
-	BuildNumberFile     *string `json:",omitempty"`
-	BuildHostnameFile   *string `json:",omitempty"`
-	BuildThumbprintFile *string `json:",omitempty"`
-	DisplayBuildNumber  *bool   `json:",omitempty"`
+	BuildId              *string `json:",omitempty"`
+	BuildFingerprintFile *string `json:",omitempty"`
+	BuildNumberFile      *string `json:",omitempty"`
+	BuildHostnameFile    *string `json:",omitempty"`
+	BuildThumbprintFile  *string `json:",omitempty"`
+	DisplayBuildNumber   *bool   `json:",omitempty"`
 
 	Platform_display_version_name          *string  `json:",omitempty"`
 	Platform_version_name                  *string  `json:",omitempty"`
@@ -236,8 +240,6 @@
 
 	VendorApiLevel *string `json:",omitempty"`
 
-	RecoverySnapshotVersion *string `json:",omitempty"`
-
 	DeviceSecondaryArch        *string  `json:",omitempty"`
 	DeviceSecondaryArchVariant *string  `json:",omitempty"`
 	DeviceSecondaryCpuVariant  *string  `json:",omitempty"`
@@ -273,8 +275,10 @@
 	AAPTPreferredConfig *string  `json:",omitempty"`
 	AAPTPrebuiltDPI     []string `json:",omitempty"`
 
-	DefaultAppCertificate           *string `json:",omitempty"`
-	MainlineSepolicyDevCertificates *string `json:",omitempty"`
+	DefaultAppCertificate           *string  `json:",omitempty"`
+	ExtraOtaKeys                    []string `json:",omitempty"`
+	ExtraOtaRecoveryKeys            []string `json:",omitempty"`
+	MainlineSepolicyDevCertificates *string  `json:",omitempty"`
 
 	AppsDefaultVersionName *string `json:",omitempty"`
 
@@ -291,6 +295,7 @@
 	HostStaticBinaries           *bool    `json:",omitempty"`
 	Binder32bit                  *bool    `json:",omitempty"`
 	UseGoma                      *bool    `json:",omitempty"`
+	UseABFS                      *bool    `json:",omitempty"`
 	UseRBE                       *bool    `json:",omitempty"`
 	UseRBEJAVAC                  *bool    `json:",omitempty"`
 	UseRBER8                     *bool    `json:",omitempty"`
@@ -368,20 +373,6 @@
 
 	PgoAdditionalProfileDirs []string `json:",omitempty"`
 
-	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
-
-	DirectedVendorSnapshot bool            `json:",omitempty"`
-	VendorSnapshotModules  map[string]bool `json:",omitempty"`
-
-	DirectedRecoverySnapshot bool            `json:",omitempty"`
-	RecoverySnapshotModules  map[string]bool `json:",omitempty"`
-
-	VendorSnapshotDirsIncluded   []string `json:",omitempty"`
-	VendorSnapshotDirsExcluded   []string `json:",omitempty"`
-	RecoverySnapshotDirsExcluded []string `json:",omitempty"`
-	RecoverySnapshotDirsIncluded []string `json:",omitempty"`
-	HostFakeSnapshotEnabled      bool     `json:",omitempty"`
-
 	MultitreeUpdateMeta bool `json:",omitempty"`
 
 	BoardVendorSepolicyDirs      []string `json:",omitempty"`
@@ -390,6 +381,7 @@
 	SystemExtPrivateSepolicyDirs []string `json:",omitempty"`
 	BoardSepolicyM4Defs          []string `json:",omitempty"`
 
+	BoardPlatform           *string `json:",omitempty"`
 	BoardSepolicyVers       *string `json:",omitempty"`
 	PlatformSepolicyVersion *string `json:",omitempty"`
 
@@ -398,7 +390,8 @@
 
 	PlatformSepolicyCompatVersions []string `json:",omitempty"`
 
-	VendorVars map[string]map[string]string `json:",omitempty"`
+	VendorVars     map[string]map[string]string `json:",omitempty"`
+	VendorVarTypes map[string]map[string]string `json:",omitempty"`
 
 	Ndk_abis *bool `json:",omitempty"`
 
@@ -430,6 +423,9 @@
 
 	TargetFSConfigGen []string `json:",omitempty"`
 
+	UseSoongSystemImage            *bool   `json:",omitempty"`
+	ProductSoongDefinedSystemImage *string `json:",omitempty"`
+
 	EnforceProductPartitionInterface *bool `json:",omitempty"`
 
 	EnforceInterPartitionJavaSdkLibrary *bool    `json:",omitempty"`
@@ -453,11 +449,11 @@
 	GenruleSandboxing                   *bool    `json:",omitempty"`
 	BuildBrokenEnforceSyspropOwner      bool     `json:",omitempty"`
 	BuildBrokenTrebleSyspropNeverallow  bool     `json:",omitempty"`
-	BuildBrokenUsesSoongPython2Modules  bool     `json:",omitempty"`
 	BuildBrokenVendorPropertyNamespace  bool     `json:",omitempty"`
 	BuildBrokenIncorrectPartitionImages bool     `json:",omitempty"`
 	BuildBrokenInputDirModules          []string `json:",omitempty"`
 	BuildBrokenDontCheckSystemSdk       bool     `json:",omitempty"`
+	BuildBrokenDupSysprop               bool     `json:",omitempty"`
 
 	BuildWarningBadOptionalUsesLibsAllowlist []string `json:",omitempty"`
 
@@ -513,6 +509,20 @@
 	BoardUseVbmetaDigestInFingerprint *bool `json:",omitempty"`
 
 	OemProperties []string `json:",omitempty"`
+
+	ArtTargetIncludeDebugBuild *bool `json:",omitempty"`
+
+	SystemPropFiles    []string `json:",omitempty"`
+	SystemExtPropFiles []string `json:",omitempty"`
+	ProductPropFiles   []string `json:",omitempty"`
+	OdmPropFiles       []string `json:",omitempty"`
+
+	EnableUffdGc *string `json:",omitempty"`
+
+	BoardAvbEnable                         *bool    `json:",omitempty"`
+	BoardAvbSystemAddHashtreeFooterArgs    []string `json:",omitempty"`
+	DeviceFrameworkCompatibilityMatrixFile []string `json:",omitempty"`
+	DeviceProductCompatibilityMatrixFile   []string `json:",omitempty"`
 }
 
 type PartitionQualifiedVariablesType struct {
diff --git a/android/variable_test.go b/android/variable_test.go
index 928bca6..73dc052 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -199,9 +199,7 @@
 			ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct {
 				Foo []string
 			}{}))
-			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("variable", VariableMutator).Parallel()
-			})
+			registerVariableBuildComponents(ctx)
 		}),
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
@@ -210,14 +208,14 @@
 var testProductVariableDefaultsProperties = struct {
 	Product_variables struct {
 		Eng struct {
-			Foo []string
+			Foo []string `android:"arch_variant"`
 			Bar []string
-		}
-	}
+		} `android:"arch_variant"`
+	} `android:"arch_variant"`
 }{}
 
 type productVariablesDefaultsTestProperties struct {
-	Foo []string
+	Foo []string `android:"arch_variant"`
 }
 
 type productVariablesDefaultsTestProperties2 struct {
@@ -242,7 +240,7 @@
 	module := &productVariablesDefaultsTestModule{}
 	module.AddProperties(&module.properties)
 	module.variableProperties = testProductVariableDefaultsProperties
-	InitAndroidModule(module)
+	InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
 	InitDefaultableModule(module)
 	return module
 }
@@ -324,3 +322,46 @@
 		})
 	}
 }
+
+// Test a defaults module that supports more product variable properties than the target module.
+func TestProductVariablesArch(t *testing.T) {
+	bp := `
+		test {
+			name: "foo",
+			arch: {
+				arm: {
+					product_variables: {
+						eng: {
+							foo: ["arm"],
+						},
+					},
+				},
+				arm64: {
+					product_variables: {
+						eng: {
+							foo: ["arm64"],
+						},
+					},
+				},
+			},
+			foo: ["module"],
+		}
+	`
+
+	result := GroupFixturePreparers(
+		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.Eng = boolPtr(true)
+		}),
+		PrepareForTestWithArchMutator,
+		PrepareForTestWithVariables,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory)
+		}),
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	foo := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*productVariablesDefaultsTestModule)
+
+	want := []string{"module", "arm64"}
+	AssertDeepEquals(t, "foo", want, foo.properties.Foo)
+}
diff --git a/android/vintf_fragment.go b/android/vintf_fragment.go
new file mode 100644
index 0000000..329eac9
--- /dev/null
+++ b/android/vintf_fragment.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 android
+
+type vintfFragmentProperties struct {
+	// Vintf fragment XML file.
+	Src string `android:"path"`
+}
+
+type vintfFragmentModule struct {
+	ModuleBase
+
+	properties vintfFragmentProperties
+
+	installDirPath InstallPath
+	outputFilePath OutputPath
+}
+
+func init() {
+	registerVintfFragmentComponents(InitRegistrationContext)
+}
+
+func registerVintfFragmentComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("vintf_fragment", vintfLibraryFactory)
+}
+
+// vintf_fragment module processes vintf fragment file and installs under etc/vintf/manifest.
+// Vintf fragment files formerly listed in vintf_fragment property would be transformed into
+// this module type.
+func vintfLibraryFactory() Module {
+	m := &vintfFragmentModule{}
+	m.AddProperties(
+		&m.properties,
+	)
+	InitAndroidArchModule(m, DeviceSupported, MultilibFirst)
+
+	return m
+}
+
+func (m *vintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	builder := NewRuleBuilder(pctx, ctx)
+	srcVintfFragment := PathForModuleSrc(ctx, m.properties.Src)
+	processedVintfFragment := PathForModuleOut(ctx, srcVintfFragment.Base())
+
+	// Process vintf fragment source file with assemble_vintf tool
+	builder.Command().
+		Flag("VINTF_IGNORE_TARGET_FCM_VERSION=true").
+		BuiltTool("assemble_vintf").
+		FlagWithInput("-i ", srcVintfFragment).
+		FlagWithOutput("-o ", processedVintfFragment)
+
+	builder.Build("assemble_vintf", "Process vintf fragment "+processedVintfFragment.String())
+
+	m.installDirPath = PathForModuleInstall(ctx, "etc", "vintf", "manifest")
+	m.outputFilePath = processedVintfFragment.OutputPath
+
+	ctx.InstallFile(m.installDirPath, processedVintfFragment.Base(), processedVintfFragment)
+}
+
+// Make this module visible to AndroidMK so it can be referenced from modules defined from Android.mk files
+func (m *vintfFragmentModule) AndroidMkEntries() []AndroidMkEntries {
+	return []AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: OptionalPathForPath(m.outputFilePath),
+		ExtraEntries: []AndroidMkExtraEntriesFunc{
+			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", m.installDirPath.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", m.outputFilePath.Base())
+			},
+		},
+	}}
+}
diff --git a/android/vintf_fragment_test.go b/android/vintf_fragment_test.go
new file mode 100644
index 0000000..8be534c
--- /dev/null
+++ b/android/vintf_fragment_test.go
@@ -0,0 +1,36 @@
+// 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 (
+	"strings"
+	"testing"
+)
+
+func TestVintfManifestBuildAction(t *testing.T) {
+	bp := `
+	vintf_fragment {
+		name: "test_vintf_fragment",
+		src: "test_vintf_file",
+	}
+	`
+
+	testResult := PrepareForTestWithAndroidBuildComponents.RunTestWithBp(t, bp)
+
+	vintfFragmentBuild := testResult.TestContext.ModuleForTests("test_vintf_fragment", "android_arm64_armv8-a").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 89c0adc..61f2200 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -283,7 +283,7 @@
 
 // This must be registered after the deps have been resolved.
 func RegisterVisibilityRuleEnforcer(ctx RegisterMutatorsContext) {
-	ctx.TopDown("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
+	ctx.BottomUp("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
 }
 
 // Checks the per-module visibility rule lists before defaults expansion.
@@ -507,7 +507,7 @@
 	return true, pkg, name
 }
 
-func visibilityRuleEnforcer(ctx TopDownMutatorContext) {
+func visibilityRuleEnforcer(ctx BottomUpMutatorContext) {
 	qualified := createVisibilityModuleReference(ctx.ModuleName(), ctx.ModuleDir(), ctx.Module())
 
 	// Visit all the dependencies making sure that this module has access to them all.
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 373e883..a2486fd 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -46,6 +46,9 @@
 
 	outputBaseName string
 	outputFile     android.OptionalPath
+
+	// TODO(b/357908583): Temp field, remove this once we support Android Mk providers
+	installFile android.InstallPath
 }
 
 type remapProperties struct {
@@ -234,14 +237,18 @@
 
 	s.outputBaseName = name
 	s.outputFile = android.OptionalPathForPath(outputZipFile)
-	ctx.InstallFile(android.PathForModuleInstall(ctx, "sdk-repo"), name+".zip", outputZipFile)
+	installPath := android.PathForModuleInstall(ctx, "sdk-repo")
+	name = name + ".zip"
+	ctx.InstallFile(installPath, name, outputZipFile)
+	// TODO(b/357908583): Temp field, remove this once we support Android Mk providers
+	s.installFile = installPath.Join(ctx, name)
 }
 
 func (s *sdkRepoHost) AndroidMk() android.AndroidMkData {
 	return android.AndroidMkData{
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
 			fmt.Fprintln(w, ".PHONY:", name, "sdk_repo", "sdk-repo-"+name)
-			fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", strings.Join(s.FilesToInstall().Strings(), " "))
+			fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", s.installFile.String())
 
 			fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-FILE_NAME_TAG_PLACEHOLDER.zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName)
 		},
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 8a8bb2e..18c97b5 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -341,9 +341,6 @@
 
 		firstOperand := v.Args[0]
 		secondOperand := v.Args[1]
-		if firstOperand.Type() != bpparser.StringType {
-			return "global", value, nil
-		}
 
 		if _, ok := firstOperand.(*bpparser.Operator); ok {
 			return "global", value, nil
@@ -783,6 +780,13 @@
 	return fmt.Errorf("Currently LOCAL_PROTOC_FLAGS only support with value '--proto_path=$(LOCAL_PATH)/...'")
 }
 
+func exportCflags(ctx variableAssignmentContext) error {
+	// The Soong replacement for EXPORT_CFLAGS doesn't need the same extra escaped quotes that were present in Make
+	ctx.mkvalue = ctx.mkvalue.Clone()
+	ctx.mkvalue.ReplaceLiteral(`\"`, `"`)
+	return includeVariableNow(bpVariable{"export_cflags", bpparser.ListType}, ctx)
+}
+
 func proguardEnabled(ctx variableAssignmentContext) error {
 	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
 	if err != nil {
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index 2e8810f..6fb20dc 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -493,7 +493,6 @@
 				Name:      name,
 				NamePos:   pos,
 				Value:     value,
-				OrigValue: value,
 				EqualsPos: pos,
 				Assigner:  "+=",
 			}
@@ -506,7 +505,6 @@
 				Name:      name,
 				NamePos:   pos,
 				Value:     value,
-				OrigValue: value,
 				EqualsPos: pos,
 				Assigner:  "=",
 			}
diff --git a/androidmk/androidmk/values.go b/androidmk/androidmk/values.go
index 9618142..701c708 100644
--- a/androidmk/androidmk/values.go
+++ b/androidmk/androidmk/values.go
@@ -81,7 +81,7 @@
 			}
 			tmp := &bpparser.Variable{
 				Name:  name,
-				Value: &bpparser.String{},
+				Type_: bpparser.StringType,
 			}
 
 			if tmp.Name == "TOP" {
@@ -150,7 +150,7 @@
 					}
 					listOfListValues = append(listOfListValues, &bpparser.Variable{
 						Name:  name,
-						Value: &bpparser.List{},
+						Type_: bpparser.ListType,
 					})
 					listValue = &bpparser.List{}
 				}
@@ -215,7 +215,7 @@
 			}
 			return &bpparser.Variable{
 				Name:  name,
-				Value: &bpparser.Bool{},
+				Type_: bpparser.BoolType,
 			}, nil
 		} else {
 			return nil, fmt.Errorf("non-const bool expression %s", ms.Dump())
diff --git a/androidmk/parser/ast.go b/androidmk/parser/ast.go
index d5d1354..c3d198f 100644
--- a/androidmk/parser/ast.go
+++ b/androidmk/parser/ast.go
@@ -84,6 +84,7 @@
 	Prerequisites *MakeString
 	RecipePos     Pos
 	Recipe        string
+	RecipeEndPos  Pos
 }
 
 func (x *Rule) Dump() string {
@@ -95,7 +96,7 @@
 }
 
 func (x *Rule) Pos() Pos { return x.Target.Pos() }
-func (x *Rule) End() Pos { return Pos(int(x.RecipePos) + len(x.Recipe)) }
+func (x *Rule) End() Pos { return x.RecipeEndPos }
 
 type Variable struct {
 	Name *MakeString
diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go
index 8a20bb0..f2477db 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -448,6 +448,7 @@
 			Prerequisites: prerequisites,
 			Recipe:        recipe,
 			RecipePos:     recipePos,
+			RecipeEndPos:  p.pos(),
 		})
 	}
 }
diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go
index db3313d..e238f8b 100644
--- a/androidmk/parser/parser_test.go
+++ b/androidmk/parser/parser_test.go
@@ -86,20 +86,19 @@
 	},
 	{
 		name: "Blank line in rule's command",
-		in:   `all:
+		in: `all:
 	echo first line
 
 	echo second line`,
 		out: []Node{
 			&Rule{
-				Target: SimpleMakeString("all", NoPos),
-				RecipePos: NoPos,
-				Recipe: "echo first line\necho second line",
+				Target:        SimpleMakeString("all", NoPos),
+				RecipePos:     NoPos,
+				Recipe:        "echo first line\necho second line",
 				Prerequisites: SimpleMakeString("", NoPos),
 			},
 		},
 	},
-
 }
 
 func TestParse(t *testing.T) {
@@ -125,3 +124,25 @@
 		})
 	}
 }
+
+func TestRuleEnd(t *testing.T) {
+	name := "ruleEndTest"
+	in := `all:
+ifeq (A, A)
+	echo foo
+	echo foo
+	echo foo
+	echo foo
+endif
+	echo bar
+`
+	p := NewParser(name, bytes.NewBufferString(in))
+	got, errs := p.Parse()
+	if len(errs) != 0 {
+		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())
+	}
+}
diff --git a/apex/Android.bp b/apex/Android.bp
index abae9e2..4848513 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -15,7 +15,6 @@
         "soong-cc",
         "soong-filesystem",
         "soong-java",
-        "soong-multitree",
         "soong-provenance",
         "soong-python",
         "soong-rust",
@@ -37,9 +36,12 @@
         "apex_test.go",
         "bootclasspath_fragment_test.go",
         "classpath_element_test.go",
+        "container_test.go",
         "dexpreopt_bootjars_test.go",
         "platform_bootclasspath_test.go",
         "systemserver_classpath_fragment_test.go",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/apex/aconfig_test.go b/apex/aconfig_test.go
index 14c0b63..bb811f5 100644
--- a/apex/aconfig_test.go
+++ b/apex/aconfig_test.go
@@ -74,6 +74,8 @@
 					apex_available: [
 						"myapex",
 					],
+					sdk_version: "none",
+					system_modules: "none",
 				}`,
 		},
 		{
@@ -122,6 +124,8 @@
 					apex_available: [
 						"myapex",
 					],
+					sdk_version: "none",
+					system_modules: "none",
 				}`,
 		},
 		{
@@ -345,6 +349,8 @@
 					apex_available: [
 						"myapex",
 					],
+					sdk_version: "none",
+					system_modules: "none",
 				}`,
 			expectedError: `.*my_java_library_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`,
 		},
@@ -392,6 +398,8 @@
 					apex_available: [
 						"myapex",
 					],
+					sdk_version: "none",
+					system_modules: "none",
 				}`,
 			expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`,
 		},
@@ -693,6 +701,8 @@
 					apex_available: [
 						"myapex",
 					],
+					sdk_version: "none",
+					system_modules: "none",
 				}`,
 			expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`,
 		},
@@ -769,6 +779,8 @@
 					apex_available: [
 						"myapex",
 					],
+					sdk_version: "none",
+					system_modules: "none",
 				}`,
 		},
 	}
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 619be8d..933682a 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -136,6 +136,11 @@
 		fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", filepath.Join(modulePath, fi.stem()))
 		fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", fi.builtFile.String()+":"+filepath.Join(modulePath, fi.stem()))
 		fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
+		if fi.checkbuildTarget != nil {
+			fmt.Fprintln(w, "LOCAL_CHECKED_MODULE :=", fi.checkbuildTarget.String())
+		} else {
+			fmt.Fprintln(w, "LOCAL_CHECKED_MODULE :=", fi.builtFile.String())
+		}
 		fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake())
 		if fi.module != nil {
 			// This apexFile's module comes from Soong
@@ -218,7 +223,7 @@
 	var required []string
 	var targetRequired []string
 	var hostRequired []string
-	required = append(required, a.RequiredModuleNames()...)
+	required = append(required, a.required...)
 	targetRequired = append(targetRequired, a.TargetRequiredModuleNames()...)
 	hostRequired = append(hostRequired, a.HostRequiredModuleNames()...)
 	for _, fi := range a.filesInfo {
diff --git a/apex/apex.go b/apex/apex.go
index c19732e..d3c1ed0 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -32,7 +32,6 @@
 	prebuilt_etc "android/soong/etc"
 	"android/soong/filesystem"
 	"android/soong/java"
-	"android/soong/multitree"
 	"android/soong/rust"
 	"android/soong/sh"
 )
@@ -50,17 +49,11 @@
 	ctx.RegisterModuleType("override_apex", OverrideApexFactory)
 	ctx.RegisterModuleType("apex_set", apexSetFactory)
 
-	ctx.PreArchMutators(registerPreArchMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
 }
 
-func registerPreArchMutators(ctx android.RegisterMutatorsContext) {
-	ctx.TopDown("prebuilt_apex_module_creator", prebuiltApexModuleCreatorMutator).Parallel()
-}
-
 func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
 	ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
 }
 
@@ -86,7 +79,7 @@
 
 	// AndroidManifest.xml file used for the zip container of this APEX bundle. If unspecified,
 	// a default one is automatically generated.
-	AndroidManifest *string `android:"path"`
+	AndroidManifest proptools.Configurable[string] `android:"path,replace_instead_of_append"`
 
 	// Determines the file contexts file for setting the security contexts to files in this APEX
 	// bundle. For platform APEXes, this should points to a file under /system/sepolicy Default:
@@ -104,7 +97,7 @@
 	// path_or_glob is a path or glob pattern for a file or set of files,
 	// uid/gid are numerial values of user ID and group ID, mode is octal value
 	// for the file mode, and cap is hexadecimal value for the capability.
-	Canned_fs_config *string `android:"path"`
+	Canned_fs_config proptools.Configurable[string] `android:"path,replace_instead_of_append"`
 
 	ApexNativeDependencies
 
@@ -117,7 +110,8 @@
 	Bootclasspath_fragments []string
 
 	// List of systemserverclasspath fragments that are embedded inside this APEX bundle.
-	Systemserverclasspath_fragments []string
+	Systemserverclasspath_fragments        proptools.Configurable[[]string]
+	ResolvedSystemserverclasspathFragments []string `blueprint:"mutated"`
 
 	// List of java libraries that are embedded inside this APEX bundle.
 	Java_libs []string
@@ -157,10 +151,6 @@
 	// Default: true.
 	Installable *bool
 
-	// If set true, VNDK libs are considered as stable libs and are not included in this APEX.
-	// Should be only used in non-system apexes (e.g. vendor: true). Default is false.
-	Use_vndk_as_stable *bool
-
 	// The type of filesystem to use. Either 'ext4', 'f2fs' or 'erofs'. Default 'ext4'.
 	Payload_fs_type *string
 
@@ -168,10 +158,6 @@
 	// Default is false.
 	Ignore_system_library_special_case *bool
 
-	// Whenever apex_payload.img of the APEX should include dm-verity hashtree.
-	// Default value is true.
-	Generate_hashtree *bool
-
 	// Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only
 	// used in tests.
 	Test_only_unsigned_payload *bool
@@ -213,6 +199,50 @@
 
 type ApexNativeDependencies struct {
 	// List of native libraries that are embedded inside this APEX.
+	Native_shared_libs proptools.Configurable[[]string]
+
+	// List of JNI libraries that are embedded inside this APEX.
+	Jni_libs []string
+
+	// List of rust dyn libraries that are embedded inside this APEX.
+	Rust_dyn_libs []string
+
+	// List of native executables that are embedded inside this APEX.
+	Binaries proptools.Configurable[[]string]
+
+	// List of native tests that are embedded inside this APEX.
+	Tests []string
+
+	// List of filesystem images that are embedded inside this APEX bundle.
+	Filesystems []string
+
+	// List of prebuilt_etcs that are embedded inside this APEX bundle.
+	Prebuilts proptools.Configurable[[]string]
+
+	// List of native libraries to exclude from this APEX.
+	Exclude_native_shared_libs []string
+
+	// List of JNI libraries to exclude from this APEX.
+	Exclude_jni_libs []string
+
+	// List of rust dyn libraries to exclude from this APEX.
+	Exclude_rust_dyn_libs []string
+
+	// List of native executables to exclude from this APEX.
+	Exclude_binaries []string
+
+	// List of native tests to exclude from this APEX.
+	Exclude_tests []string
+
+	// List of filesystem images to exclude from this APEX bundle.
+	Exclude_filesystems []string
+
+	// List of prebuilt_etcs to exclude from this APEX bundle.
+	Exclude_prebuilts []string
+}
+
+type ResolvedApexNativeDependencies struct {
+	// List of native libraries that are embedded inside this APEX.
 	Native_shared_libs []string
 
 	// List of JNI libraries that are embedded inside this APEX.
@@ -256,14 +286,14 @@
 }
 
 // Merge combines another ApexNativeDependencies into this one
-func (a *ApexNativeDependencies) Merge(b ApexNativeDependencies) {
-	a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs...)
+func (a *ResolvedApexNativeDependencies) Merge(ctx android.BaseMutatorContext, 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.Rust_dyn_libs = append(a.Rust_dyn_libs, b.Rust_dyn_libs...)
-	a.Binaries = append(a.Binaries, b.Binaries...)
+	a.Binaries = append(a.Binaries, b.Binaries.GetOrDefault(ctx, nil)...)
 	a.Tests = append(a.Tests, b.Tests...)
 	a.Filesystems = append(a.Filesystems, b.Filesystems...)
-	a.Prebuilts = append(a.Prebuilts, b.Prebuilts...)
+	a.Prebuilts = append(a.Prebuilts, b.Prebuilts.GetOrDefault(ctx, nil)...)
 
 	a.Exclude_native_shared_libs = append(a.Exclude_native_shared_libs, b.Exclude_native_shared_libs...)
 	a.Exclude_jni_libs = append(a.Exclude_jni_libs, b.Exclude_jni_libs...)
@@ -339,10 +369,10 @@
 // base apex.
 type overridableProperties struct {
 	// List of APKs that are embedded inside this APEX.
-	Apps []string
+	Apps proptools.Configurable[[]string]
 
 	// List of prebuilt files that are embedded inside this APEX bundle.
-	Prebuilts []string
+	Prebuilts proptools.Configurable[[]string]
 
 	// List of BPF programs inside this APEX bundle.
 	Bpfs []string
@@ -394,7 +424,6 @@
 	android.ModuleBase
 	android.DefaultableModuleBase
 	android.OverridableModuleBase
-	multitree.ExportableModuleBase
 
 	// Properties
 	properties            apexBundleProperties
@@ -489,6 +518,9 @@
 	javaApisUsedByModuleFile     android.ModuleOutPath
 
 	aconfigFiles []android.Path
+
+	// Required modules, filled out during GenerateAndroidBuildActions and used in AndroidMk
+	required []string
 }
 
 // apexFileClass represents a type of file that can be included in APEX.
@@ -530,6 +562,8 @@
 	customStem string
 	symlinks   []string // additional symlinks
 
+	checkbuildTarget android.Path
+
 	// Info for Android.mk Module name of `module` in AndroidMk. Note the generated AndroidMk
 	// module for apexFile is named something like <AndroidMk module name>.<apex name>[<apex
 	// suffix>]
@@ -565,9 +599,12 @@
 		module:              module,
 	}
 	if module != nil {
+		if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok {
+			ret.checkbuildTarget = installFilesInfo.CheckbuildTarget
+		}
 		ret.moduleDir = ctx.OtherModuleDir(module)
 		ret.partition = module.PartitionTag(ctx.DeviceConfig())
-		ret.requiredModuleNames = module.RequiredModuleNames()
+		ret.requiredModuleNames = module.RequiredModuleNames(ctx)
 		ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
 		ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
 		ret.multilib = module.Target().Arch.ArchType.Multilib
@@ -652,6 +689,8 @@
 	// If not-nil and an APEX is a member of an SDK then dependencies of that APEX with this tag will
 	// also be added as exported members of that SDK.
 	memberType android.SdkMemberType
+
+	installable bool
 }
 
 func (d *dependencyTag) SdkMemberType(_ android.Module) android.SdkMemberType {
@@ -670,18 +709,23 @@
 	return !d.sourceOnly
 }
 
+func (d *dependencyTag) InstallDepNeeded() bool {
+	return d.installable
+}
+
 var _ android.ReplaceSourceWithPrebuilt = &dependencyTag{}
 var _ android.SdkMemberDependencyTag = &dependencyTag{}
 
 var (
-	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}
-	sscpfTag        = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true, memberType: java.SystemServerClasspathFragmentSdkMemberType}
+	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}
+	// The dexpreopt artifacts of apex system server jars are installed onto system image.
+	sscpfTag        = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true, memberType: java.SystemServerClasspathFragmentSdkMemberType, installable: true}
 	compatConfigTag = &dependencyTag{name: "compatConfig", payload: true, sourceOnly: true, memberType: java.CompatConfigSdkMemberType}
 	javaLibTag      = &dependencyTag{name: "javaLib", payload: true}
 	jniLibTag       = &dependencyTag{name: "jniLib", payload: true}
@@ -695,13 +739,12 @@
 )
 
 // TODO(jiyong): shorten this function signature
-func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) {
+func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ResolvedApexNativeDependencies, target android.Target, imageVariation string) {
 	binVariations := target.Variations()
 	libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
 	rustLibVariations := append(
 		target.Variations(), []blueprint.Variation{
 			{Mutator: "rust_libraries", Variation: "dylib"},
-			{Mutator: "link", Variation: ""},
 		}...,
 	)
 
@@ -745,9 +788,9 @@
 
 	prefix := android.CoreVariation
 	if a.SocSpecific() || a.DeviceSpecific() {
-		prefix = cc.VendorVariation
+		prefix = android.VendorVariation
 	} else if a.ProductSpecific() {
-		prefix = cc.ProductVariation
+		prefix = android.ProductVariation
 	}
 
 	return prefix, ""
@@ -777,25 +820,24 @@
 		}
 	}
 	for i, target := range targets {
-		var deps ApexNativeDependencies
+		var deps ResolvedApexNativeDependencies
 
 		// Add native modules targeting both ABIs. When multilib.* is omitted for
 		// native_shared_libs/jni_libs/tests, it implies multilib.both
-		deps.Merge(a.properties.Multilib.Both)
-		deps.Merge(ApexNativeDependencies{
+		deps.Merge(ctx, a.properties.Multilib.Both)
+		deps.Merge(ctx, ApexNativeDependencies{
 			Native_shared_libs: a.properties.Native_shared_libs,
 			Tests:              a.properties.Tests,
 			Jni_libs:           a.properties.Jni_libs,
-			Binaries:           nil,
 		})
 
 		// Add native modules targeting the first ABI When multilib.* is omitted for
 		// binaries, it implies multilib.first
 		isPrimaryAbi := i == 0
 		if isPrimaryAbi {
-			deps.Merge(a.properties.Multilib.First)
-			deps.Merge(ApexNativeDependencies{
-				Native_shared_libs: nil,
+			deps.Merge(ctx, a.properties.Multilib.First)
+			deps.Merge(ctx, ApexNativeDependencies{
+				Native_shared_libs: proptools.NewConfigurable[[]string](nil, nil),
 				Tests:              nil,
 				Jni_libs:           nil,
 				Binaries:           a.properties.Binaries,
@@ -805,27 +847,27 @@
 		// Add native modules targeting either 32-bit or 64-bit ABI
 		switch target.Arch.ArchType.Multilib {
 		case "lib32":
-			deps.Merge(a.properties.Multilib.Lib32)
-			deps.Merge(a.properties.Multilib.Prefer32)
+			deps.Merge(ctx, a.properties.Multilib.Lib32)
+			deps.Merge(ctx, a.properties.Multilib.Prefer32)
 		case "lib64":
-			deps.Merge(a.properties.Multilib.Lib64)
+			deps.Merge(ctx, a.properties.Multilib.Lib64)
 			if !has32BitTarget {
-				deps.Merge(a.properties.Multilib.Prefer32)
+				deps.Merge(ctx, a.properties.Multilib.Prefer32)
 			}
 		}
 
 		// Add native modules targeting a specific arch variant
 		switch target.Arch.ArchType {
 		case android.Arm:
-			deps.Merge(a.archProperties.Arch.Arm.ApexNativeDependencies)
+			deps.Merge(ctx, a.archProperties.Arch.Arm.ApexNativeDependencies)
 		case android.Arm64:
-			deps.Merge(a.archProperties.Arch.Arm64.ApexNativeDependencies)
+			deps.Merge(ctx, a.archProperties.Arch.Arm64.ApexNativeDependencies)
 		case android.Riscv64:
-			deps.Merge(a.archProperties.Arch.Riscv64.ApexNativeDependencies)
+			deps.Merge(ctx, a.archProperties.Arch.Riscv64.ApexNativeDependencies)
 		case android.X86:
-			deps.Merge(a.archProperties.Arch.X86.ApexNativeDependencies)
+			deps.Merge(ctx, a.archProperties.Arch.X86.ApexNativeDependencies)
 		case android.X86_64:
-			deps.Merge(a.archProperties.Arch.X86_64.ApexNativeDependencies)
+			deps.Merge(ctx, a.archProperties.Arch.X86_64.ApexNativeDependencies)
 		default:
 			panic(fmt.Errorf("unsupported arch %v\n", ctx.Arch().ArchType))
 		}
@@ -839,11 +881,13 @@
 		}
 	}
 
+	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.Systemserverclasspath_fragments...)
+	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.ResolvedSystemserverclasspathFragments...)
 	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
 	ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
@@ -856,9 +900,9 @@
 	}
 
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
-	ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
+	ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps.GetOrDefault(ctx, nil)...)
 	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.overridableProperties.Bpfs...)
-	if prebuilts := a.overridableProperties.Prebuilts; len(prebuilts) > 0 {
+	if prebuilts := a.overridableProperties.Prebuilts.GetOrDefault(ctx, nil); len(prebuilts) > 0 {
 		// For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
 		// regardless of the TARGET_PREFER_* setting. See b/144532908
 		arches := ctx.DeviceConfig().Arches()
@@ -947,24 +991,6 @@
 		return
 	}
 
-	// Special casing for APEXes on non-system (e.g., vendor, odm, etc.) partitions. They are
-	// provided with a property named use_vndk_as_stable, which when set to true doesn't collect
-	// VNDK libraries as transitive dependencies. This option is useful for reducing the size of
-	// the non-system APEXes because the VNDK libraries won't be included (and duped) in the
-	// APEX, but shared across APEXes via the VNDK APEX.
-	useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
-	if proptools.Bool(a.properties.Use_vndk_as_stable) {
-		if !useVndk {
-			mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
-		}
-		if a.minSdkVersionValue(mctx) != "" {
-			mctx.PropertyErrorf("use_vndk_as_stable", "not supported when min_sdk_version is set")
-		}
-		if mctx.Failed() {
-			return
-		}
-	}
-
 	continueApexDepsWalk := func(child, parent android.Module) bool {
 		am, ok := child.(android.ApexModule)
 		if !ok || !am.CanHaveApexVariants() {
@@ -982,10 +1008,6 @@
 			return false
 		}
 
-		if useVndk && child.Name() == "libbinder" {
-			mctx.ModuleErrorf("Module %s in the vendor APEX %s should not use libbinder. Use libbinder_ndk instead.", parent.Name(), a.Name())
-		}
-
 		// By default, all the transitive dependencies are collected, unless filtered out
 		// above.
 		return true
@@ -1040,6 +1062,8 @@
 		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),
 	}
 	mctx.WalkDeps(func(child, parent android.Module) bool {
 		if !continueApexDepsWalk(child, parent) {
@@ -1051,7 +1075,7 @@
 
 	if a.dynamic_common_lib_apex() {
 		android.SetProvider(mctx, DCLAInfoProvider, DCLAInfo{
-			ProvidedLibs: a.properties.Native_shared_libs,
+			ProvidedLibs: a.properties.Native_shared_libs.GetOrDefault(mctx, nil),
 		})
 	}
 }
@@ -1168,6 +1192,7 @@
 		"test_com.android.os.statsd",
 		"test_com.android.permission",
 		"test_com.android.wifi",
+		"test_imgdiag_com.android.art",
 		"test_jitzygote_com.android.art",
 		// go/keep-sorted end
 	}
@@ -1366,27 +1391,6 @@
 	return true
 }
 
-var _ android.OutputFileProducer = (*apexBundle)(nil)
-
-// Implements android.OutputFileProducer
-func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "", android.DefaultDistTag:
-		// This is the default dist path.
-		return android.Paths{a.outputFile}, nil
-	case imageApexSuffix:
-		// uncompressed one
-		if a.outputApexFile != nil {
-			return android.Paths{a.outputApexFile}, nil
-		}
-		fallthrough
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
-var _ multitree.Exportable = (*apexBundle)(nil)
-
 func (a *apexBundle) Exportable() bool {
 	return true
 }
@@ -1400,7 +1404,7 @@
 var _ cc.Coverage = (*apexBundle)(nil)
 
 // Implements cc.Coverage
-func (a *apexBundle) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (a *apexBundle) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
@@ -1462,11 +1466,6 @@
 	return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
 }
 
-// See the generate_hashtree property
-func (a *apexBundle) shouldGenerateHashtree() bool {
-	return proptools.BoolDefault(a.properties.Generate_hashtree, true)
-}
-
 // See the test_only_unsigned_payload property
 func (a *apexBundle) testOnlyShouldSkipPayloadSign() bool {
 	return proptools.Bool(a.properties.Test_only_unsigned_payload)
@@ -1526,11 +1525,10 @@
 		imageVariation := a.getImageVariation()
 		for _, target := range ctx.MultiTargets() {
 			if target.Arch.ArchType.Multilib == "lib64" {
-				addDependenciesForNativeModules(ctx, ApexNativeDependencies{
+				addDependenciesForNativeModules(ctx, ResolvedApexNativeDependencies{
 					Native_shared_libs: []string{"libclang_rt.hwasan"},
 					Tests:              nil,
 					Jni_libs:           nil,
-					Binaries:           nil,
 				}, target, imageVariation)
 				break
 			}
@@ -1679,12 +1677,10 @@
 	if sdkLib, ok := module.(*java.SdkLibrary); ok {
 		for _, install := range sdkLib.BuiltInstalledForApex() {
 			af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
-			install.PackageFile(ctx)
 		}
 	} else if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
 		for _, install := range dexpreopter.DexpreoptBuiltInstalledForApex() {
 			af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
-			install.PackageFile(ctx)
 		}
 	}
 	return af
@@ -1925,8 +1921,6 @@
 
 	// visitor skips these from this list of module names
 	unwantedTransitiveDeps []string
-
-	aconfigFiles []android.Path
 }
 
 func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) {
@@ -1993,7 +1987,6 @@
 				fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
 				fi.isJniLib = isJniLib
 				vctx.filesInfo = append(vctx.filesInfo, fi)
-				addAconfigFiles(vctx, ctx, child)
 				// Collect the list of stub-providing libs except:
 				// - VNDK libs are only for vendors
 				// - bootstrap bionic libs are treated as provided by system
@@ -2005,7 +1998,6 @@
 				fi := apexFileForRustLibrary(ctx, ch)
 				fi.isJniLib = isJniLib
 				vctx.filesInfo = append(vctx.filesInfo, fi)
-				addAconfigFiles(vctx, ctx, child)
 				return true // track transitive dependencies
 			default:
 				ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
@@ -2014,11 +2006,9 @@
 			switch ch := child.(type) {
 			case *cc.Module:
 				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
-				addAconfigFiles(vctx, ctx, child)
 				return true // track transitive dependencies
 			case *rust.Module:
 				vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, ch))
-				addAconfigFiles(vctx, ctx, child)
 				return true // track transitive dependencies
 			default:
 				ctx.PropertyErrorf("binaries",
@@ -2058,7 +2048,6 @@
 					return false
 				}
 				vctx.filesInfo = append(vctx.filesInfo, af)
-				addAconfigFiles(vctx, ctx, child)
 				return true // track transitive dependencies
 			default:
 				ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
@@ -2067,14 +2056,11 @@
 			switch ap := child.(type) {
 			case *java.AndroidApp:
 				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
-				addAconfigFiles(vctx, ctx, child)
 				return true // track transitive dependencies
 			case *java.AndroidAppImport:
 				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
-				addAconfigFiles(vctx, ctx, child)
 			case *java.AndroidTestHelperApp:
 				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
-				addAconfigFiles(vctx, ctx, child)
 			case *java.AndroidAppSet:
 				appDir := "app"
 				if ap.Privileged() {
@@ -2088,7 +2074,6 @@
 				af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap)
 				af.certificate = java.PresignedCertificate
 				vctx.filesInfo = append(vctx.filesInfo, af)
-				addAconfigFiles(vctx, ctx, child)
 			default:
 				ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
 			}
@@ -2120,7 +2105,6 @@
 				for _, etcFile := range filesToCopy {
 					vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile))
 				}
-				addAconfigFiles(vctx, ctx, child)
 			} else {
 				ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
 			}
@@ -2132,20 +2116,9 @@
 			}
 		case testTag:
 			if ccTest, ok := child.(*cc.Module); ok {
-				if ccTest.IsTestPerSrcAllTestsVariation() {
-					// Multiple-output test module (where `test_per_src: true`).
-					//
-					// `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
-					// We do not add this variation to `filesInfo`, as it has no output;
-					// however, we do add the other variations of this module as indirect
-					// dependencies (see below).
-				} else {
-					// Single-output test module (where `test_per_src: false`).
-					af := apexFileForExecutable(ctx, ccTest)
-					af.class = nativeTest
-					vctx.filesInfo = append(vctx.filesInfo, af)
-					addAconfigFiles(vctx, ctx, child)
-				}
+				af := apexFileForExecutable(ctx, ccTest)
+				af.class = nativeTest
+				vctx.filesInfo = append(vctx.filesInfo, af)
 				return true // track transitive dependencies
 			} else {
 				ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
@@ -2225,26 +2198,15 @@
 			}
 
 			vctx.filesInfo = append(vctx.filesInfo, af)
-			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		} else if rm, ok := child.(*rust.Module); ok {
+			if !android.IsDepInSameApex(ctx, am, am) {
+				return false
+			}
+
 			af := apexFileForRustLibrary(ctx, rm)
 			af.transitiveDep = true
 			vctx.filesInfo = append(vctx.filesInfo, af)
-			addAconfigFiles(vctx, ctx, child)
-			return true // track transitive dependencies
-		}
-	} else if cc.IsTestPerSrcDepTag(depTag) {
-		if ch, ok := child.(*cc.Module); ok {
-			af := apexFileForExecutable(ctx, ch)
-			// Handle modules created as `test_per_src` variations of a single test module:
-			// use the name of the generated test binary (`fileToCopy`) instead of the name
-			// of the original test module (`depName`, shared by all `test_per_src`
-			// variations of that module).
-			af.androidMkModuleName = filepath.Base(af.builtFile.String())
-			// these are not considered transitive dep
-			af.transitiveDep = false
-			vctx.filesInfo = append(vctx.filesInfo, af)
 			return true // track transitive dependencies
 		}
 	} else if cc.IsHeaderDepTag(depTag) {
@@ -2260,10 +2222,13 @@
 		}
 	} else if rust.IsDylibDepTag(depTag) {
 		if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
+			if !android.IsDepInSameApex(ctx, am, am) {
+				return false
+			}
+
 			af := apexFileForRustLibrary(ctx, rustm)
 			af.transitiveDep = true
 			vctx.filesInfo = append(vctx.filesInfo, af)
-			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		}
 	} else if rust.IsRlibDepTag(depTag) {
@@ -2282,7 +2247,6 @@
 				return false
 			}
 			vctx.filesInfo = append(vctx.filesInfo, af)
-			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		default:
 			ctx.PropertyErrorf("bootclasspath_fragments",
@@ -2297,7 +2261,6 @@
 			if profileAf := apexFileForJavaModuleProfile(ctx, child.(javaModule)); profileAf != nil {
 				vctx.filesInfo = append(vctx.filesInfo, *profileAf)
 			}
-			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		default:
 			ctx.PropertyErrorf("systemserverclasspath_fragments",
@@ -2315,19 +2278,6 @@
 	return false
 }
 
-func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) {
-	if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigPropagatingProviderKey); ok {
-		if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil {
-			vctx.aconfigFiles = append(vctx.aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...)
-		}
-	}
-
-	validationFlag := ctx.DeviceConfig().AconfigContainerValidation()
-	if validationFlag == "error" || validationFlag == "warning" {
-		android.VerifyAconfigBuildMode(ctx, ctx.ModuleName(), module, validationFlag == "error")
-	}
-}
-
 func (a *apexBundle) shouldCheckDuplicate(ctx android.ModuleContext) bool {
 	// TODO(b/263308293) remove this
 	if a.properties.IsCoverageVariant {
@@ -2409,13 +2359,16 @@
 	// 3) some fields in apexBundle struct are configured
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = vctx.filesInfo
-	a.aconfigFiles = android.FirstUniquePaths(vctx.aconfigFiles)
 
 	a.setPayloadFsType(ctx)
 	a.setSystemLibLink(ctx)
 	a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
 
 	////////////////////////////////////////////////////////////////////////////////////////////
+	// 3.a) some artifacts are generated from the collected files
+	a.filesInfo = append(a.filesInfo, a.buildAconfigFiles(ctx)...)
+
+	////////////////////////////////////////////////////////////////////////////////////////////
 	// 4) generate the build rules to create the APEX. This is done in builder.go.
 	a.buildManifest(ctx, vctx.provideNativeLibs, vctx.requireNativeLibs)
 	a.buildApex(ctx)
@@ -2426,6 +2379,11 @@
 	a.provideApexExportsInfo(ctx)
 
 	a.providePrebuiltInfo(ctx)
+
+	a.required = a.RequiredModuleNames(ctx)
+	a.required = append(a.required, a.VintfFragmentModuleNames(ctx)...)
+
+	a.setOutputFiles(ctx)
 }
 
 // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@@ -2454,6 +2412,18 @@
 	})
 }
 
+// Set output files to outputFiles property, which is later used to set the
+// OutputFilesProvider
+func (a *apexBundle) setOutputFiles(ctx android.ModuleContext) {
+	// default dist path
+	ctx.SetOutputFiles(android.Paths{a.outputFile}, "")
+	ctx.SetOutputFiles(android.Paths{a.outputFile}, android.DefaultDistTag)
+	// uncompressed one
+	if a.outputApexFile != nil {
+		ctx.SetOutputFiles(android.Paths{a.outputApexFile}, imageApexSuffix)
+	}
+}
+
 // 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 {
@@ -2548,7 +2518,6 @@
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 	android.InitOverridableModule(module, &module.overridableProperties.Overrides)
-	multitree.InitExportableModule(module)
 	return module
 }
 
@@ -2713,12 +2682,12 @@
 		if a.minSdkVersionValue(ctx) == "" {
 			ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
 		}
+		if a.minSdkVersion(ctx).IsCurrent() {
+			ctx.PropertyErrorf("updatable", "updatable APEXes should not set min_sdk_version to current. Please use a finalized API level or a recognized in-development codename")
+		}
 		if a.UsePlatformApis() {
 			ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs")
 		}
-		if proptools.Bool(a.properties.Use_vndk_as_stable) {
-			ctx.PropertyErrorf("use_vndk_as_stable", "updatable APEXes can't use external VNDK libs")
-		}
 		if a.FutureUpdatable() {
 			ctx.PropertyErrorf("future_updatable", "Already updatable. Remove `future_updatable: true:`")
 		}
@@ -2771,6 +2740,12 @@
 		return
 	}
 
+	// Temporarily bypass /product APEXes with a specific prefix.
+	// TODO: b/352818241 - Remove this after APEX availability is enforced for /product APEXes.
+	if a.ProductSpecific() && strings.HasPrefix(a.ApexVariationName(), "com.sdv.") {
+		return
+	}
+
 	// Coverage build adds additional dependencies for the coverage-only runtime libraries.
 	// Requiring them and their transitive depencies with apex_available is not right
 	// because they just add noise.
@@ -2805,13 +2780,24 @@
 			return false
 		}
 
-		if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) {
+		if to.AvailableFor(apexName) {
 			return true
 		}
+
+		// Let's give some hint for apex_available
+		hint := fmt.Sprintf("%q", apexName)
+
+		if strings.HasPrefix(apexName, "com.") && !strings.HasPrefix(apexName, "com.android.") && strings.Count(apexName, ".") >= 2 {
+			// In case of a partner APEX, prefix format might be an option.
+			components := strings.Split(apexName, ".")
+			components[len(components)-1] = "*"
+			hint += fmt.Sprintf(" or %q", strings.Join(components, "."))
+		}
+
 		ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'."+
 			"\n\nDependency path:%s\n\n"+
-			"Consider adding %q to 'apex_available' property of %q",
-			fromName, toName, ctx.GetPathString(true), apexName, toName)
+			"Consider adding %s to 'apex_available' property of %q",
+			fromName, toName, ctx.GetPathString(true), hint, toName)
 		// Visit this module's dependencies to check and report any issues with their availability.
 		return true
 	})
@@ -2848,78 +2834,10 @@
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
-func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) {
+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.Systemserverclasspath_fragments...)
-}
-
-var (
-	apexAvailBaseline        = makeApexAvailableBaseline()
-	inverseApexAvailBaseline = invertApexBaseline(apexAvailBaseline)
-)
-
-func baselineApexAvailable(apex, moduleName string) bool {
-	key := apex
-	moduleName = normalizeModuleName(moduleName)
-
-	if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
-		return true
-	}
-
-	key = android.AvailableToAnyApex
-	if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
-		return true
-	}
-
-	return false
-}
-
-func normalizeModuleName(moduleName string) string {
-	// Prebuilt modules (e.g. java_import, etc.) have "prebuilt_" prefix added by the build
-	// system. Trim the prefix for the check since they are confusing
-	moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName)
-	if strings.HasPrefix(moduleName, "libclang_rt.") {
-		// This module has many arch variants that depend on the product being built.
-		// We don't want to list them all
-		moduleName = "libclang_rt"
-	}
-	if strings.HasPrefix(moduleName, "androidx.") {
-		// TODO(b/156996905) Set apex_available/min_sdk_version for androidx support libraries
-		moduleName = "androidx"
-	}
-	return moduleName
-}
-
-// Transform the map of apex -> modules to module -> apexes.
-func invertApexBaseline(m map[string][]string) map[string][]string {
-	r := make(map[string][]string)
-	for apex, modules := range m {
-		for _, module := range modules {
-			r[module] = append(r[module], apex)
-		}
-	}
-	return r
-}
-
-// Retrieve the baseline of apexes to which the supplied module belongs.
-func BaselineApexAvailable(moduleName string) []string {
-	return inverseApexAvailBaseline[normalizeModuleName(moduleName)]
-}
-
-// This is a map from apex to modules, which overrides the apex_available setting for that
-// particular module to make it available for the apex regardless of its setting.
-// TODO(b/147364041): remove this
-func makeApexAvailableBaseline() map[string][]string {
-	// The "Module separator"s below are employed to minimize merge conflicts.
-	m := make(map[string][]string)
-	//
-	// Module separator
-	//
-	m["com.android.runtime"] = []string{
-		"libz",
-	}
-	return m
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.ResolvedSystemserverclasspathFragments...)
 }
 
 func init() {
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index e6ebff2..f405cb2 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -46,6 +46,9 @@
 		Command: "cat $out.rsp | xargs cat" +
 			// Only track non-external dependencies, i.e. those that end up in the binary
 			" | grep -v '(external)'" +
+			// Allowlist androidx deps
+			" | grep -v '^androidx\\.'" +
+			" | grep -v '^prebuilt_androidx\\.'" +
 			// Ignore comments in any of the files
 			" | grep -v '^#'" +
 			" | sort -u -f >$out",
@@ -85,7 +88,7 @@
 	updatableFlatLists := android.Paths{}
 	ctx.VisitAllModules(func(module android.Module) {
 		if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok {
-			apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
+			apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
 			if path := binaryInfo.FlatListPath(); path != nil {
 				if binaryInfo.Updatable() || apexInfo.Updatable {
 					updatableFlatLists = append(updatableFlatLists, path)
@@ -152,7 +155,7 @@
 	prebuiltInfos := []android.PrebuiltInfo{}
 
 	ctx.VisitAllModules(func(m android.Module) {
-		prebuiltInfo, exists := android.SingletonModuleProvider(ctx, m, android.PrebuiltInfoProvider)
+		prebuiltInfo, exists := android.OtherModuleProvider(ctx, m, android.PrebuiltInfoProvider)
 		// Use prebuiltInfoProvider to filter out non apex soong modules.
 		// Use HideFromMake to filter out the unselected variants of a specific apex.
 		if exists && !m.IsHideFromMake() {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 9a31447..394f4ae 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -267,6 +267,7 @@
 }
 
 func ensureMatches(t *testing.T, result string, expectedRex string) {
+	t.Helper()
 	ok, err := regexp.MatchString(expectedRex, result)
 	if err != nil {
 		t.Fatalf("regexp failure trying to match %s against `%s` expression: %s", result, expectedRex, err)
@@ -277,6 +278,14 @@
 	}
 }
 
+func ensureListContainsMatch(t *testing.T, result []string, expectedRex string) {
+	t.Helper()
+	p := regexp.MustCompile(expectedRex)
+	if android.IndexListPred(func(s string) bool { return p.MatchString(s) }, result) == -1 {
+		t.Errorf("%q is not found in %v", expectedRex, result)
+	}
+}
+
 func ensureListContains(t *testing.T, result []string, expected string) {
 	t.Helper()
 	if !android.InList(expected, result) {
@@ -384,7 +393,7 @@
 			symlink_preferred_arch: true,
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex", "com.android.gki.*" ],
+			apex_available: [ "myapex" ],
 		}
 
 		rust_binary {
@@ -432,14 +441,6 @@
 			apex_available: ["myapex"],
 		}
 
-		apex {
-			name: "com.android.gki.fake",
-			binaries: ["foo"],
-			key: "myapex.key",
-			file_contexts: ":myapex-file_contexts",
-			updatable: false,
-		}
-
 		cc_library_shared {
 			name: "mylib2",
 			srcs: ["mylib.cpp"],
@@ -914,7 +915,7 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
-			shared_libs: ["mylib2", "mylib3"],
+			shared_libs: ["mylib2", "mylib3", "my_prebuilt_platform_lib", "my_prebuilt_platform_stub_only_lib"],
 			system_shared_libs: [],
 			stl: "none",
 			apex_available: [ "myapex" ],
@@ -927,6 +928,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			stubs: {
+				symbol_file: "mylib2.map.txt",
 				versions: ["1", "2", "3"],
 			},
 		}
@@ -938,6 +940,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			stubs: {
+				symbol_file: "mylib3.map.txt",
 				versions: ["10", "11", "12"],
 			},
 			apex_available: [ "myapex" ],
@@ -951,6 +954,24 @@
 			apex_available: [ "myapex" ],
 		}
 
+		cc_prebuilt_library_shared {
+			name: "my_prebuilt_platform_lib",
+			stubs: {
+				symbol_file: "my_prebuilt_platform_lib.map.txt",
+				versions: ["1", "2", "3"],
+			},
+			srcs: ["foo.so"],
+		}
+
+		// Similar to my_prebuilt_platform_lib, but this library only provides stubs, i.e. srcs is empty
+		cc_prebuilt_library_shared {
+			name: "my_prebuilt_platform_stub_only_lib",
+			stubs: {
+				symbol_file: "my_prebuilt_platform_stub_only_lib.map.txt",
+				versions: ["1", "2", "3"],
+			}
+		}
+
 		rust_binary {
 			name: "foo.rust",
 			srcs: ["foo.rs"],
@@ -1030,6 +1051,20 @@
 
 	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
 	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so")
+
+	// Ensure that mylib is linking with the latest version of stubs for my_prebuilt_platform_lib
+	ensureContains(t, mylibLdFlags, "my_prebuilt_platform_lib/android_arm64_armv8-a_shared_current/my_prebuilt_platform_lib.so")
+	// ... and not linking to the non-stub (impl) variant of my_prebuilt_platform_lib
+	ensureNotContains(t, mylibLdFlags, "my_prebuilt_platform_lib/android_arm64_armv8-a_shared/my_prebuilt_platform_lib.so")
+	// Ensure that genstub for platform-provided lib is invoked with --systemapi
+	ensureContains(t, ctx.ModuleForTests("my_prebuilt_platform_lib", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
+
+	// Ensure that mylib is linking with the latest version of stubs for my_prebuilt_platform_lib
+	ensureContains(t, mylibLdFlags, "my_prebuilt_platform_stub_only_lib/android_arm64_armv8-a_shared_current/my_prebuilt_platform_stub_only_lib.so")
+	// ... and not linking to the non-stub (impl) variant of my_prebuilt_platform_lib
+	ensureNotContains(t, mylibLdFlags, "my_prebuilt_platform_stub_only_lib/android_arm64_armv8-a_shared/my_prebuilt_platform_stub_only_lib.so")
+	// Ensure that genstub for platform-provided lib is invoked with --systemapi
+	ensureContains(t, ctx.ModuleForTests("my_prebuilt_platform_stub_only_lib", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi")
 }
 
 func TestApexShouldNotEmbedStubVariant(t *testing.T) {
@@ -1164,6 +1199,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			stubs: {
+				symbol_file: "mylib2.map.txt",
 				versions: ["28", "29", "30", "current"],
 			},
 			min_sdk_version: "28",
@@ -1176,6 +1212,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			stubs: {
+				symbol_file: "mylib3.map.txt",
 				versions: ["28", "29", "30", "current"],
 			},
 			apex_available: [ "myapex" ],
@@ -3648,7 +3685,7 @@
 }
 
 func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) {
-	deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Description("deapex")
+	deapexer := ctx.ModuleForTests(moduleName, variant).Description("deapex")
 	outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1)
 	if deapexer.Output != nil {
 		outputs = append(outputs, deapexer.Output.String())
@@ -4848,200 +4885,6 @@
 func (ctx moduleErrorfTestCtx) ModuleErrorf(format string, args ...interface{}) {
 }
 
-// These tests verify that the prebuilt_apex/deapexer to java_import wiring allows for the
-// propagation of paths to dex implementation jars from the former to the latter.
-func TestPrebuiltExportDexImplementationJars(t *testing.T) {
-	transform := android.NullFixturePreparer
-
-	checkDexJarBuildPath := func(t *testing.T, ctx *android.TestContext, name string) {
-		t.Helper()
-		// Make sure the import has been given the correct path to the dex jar.
-		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
-		dexJarBuildPath := p.DexJarBuildPath(moduleErrorfTestCtx{}).PathOrNil()
-		stem := android.RemoveOptionalPrebuiltPrefix(name)
-		android.AssertStringEquals(t, "DexJarBuildPath should be apex-related path.",
-			".intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar",
-			android.NormalizePathForTesting(dexJarBuildPath))
-	}
-
-	checkDexJarInstallPath := func(t *testing.T, ctx *android.TestContext, name string) {
-		t.Helper()
-		// Make sure the import has been given the correct path to the dex jar.
-		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
-		dexJarBuildPath := p.DexJarInstallPath()
-		stem := android.RemoveOptionalPrebuiltPrefix(name)
-		android.AssertStringEquals(t, "DexJarInstallPath should be apex-related path.",
-			"target/product/test_device/apex/myapex/javalib/"+stem+".jar",
-			android.NormalizePathForTesting(dexJarBuildPath))
-	}
-
-	ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) {
-		t.Helper()
-		// Make sure that an apex variant is not created for the source module.
-		android.AssertArrayString(t, "Check if there is no source variant",
-			[]string{"android_common"},
-			ctx.ModuleVariantsForTests(name))
-	}
-
-	t.Run("prebuilt only", func(t *testing.T) {
-		bp := `
-		prebuilt_apex {
-			name: "myapex",
-			arch: {
-				arm64: {
-					src: "myapex-arm64.apex",
-				},
-				arm: {
-					src: "myapex-arm.apex",
-				},
-			},
-			exported_java_libs: ["libfoo", "libbar"],
-		}
-
-		java_import {
-			name: "libfoo",
-			jars: ["libfoo.jar"],
-		}
-
-		java_sdk_library_import {
-			name: "libbar",
-			public: {
-				jars: ["libbar.jar"],
-			},
-		}
-	`
-
-		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-
-		deapexerName := deapexerModuleName("prebuilt_myapex")
-		android.AssertStringEquals(t, "APEX module name from deapexer name", "prebuilt_myapex", apexModuleName(deapexerName))
-
-		// Make sure that the deapexer has the correct input APEX.
-		deapexer := ctx.ModuleForTests(deapexerName, "android_common")
-		rule := deapexer.Rule("deapexer")
-		if expected, actual := []string{"myapex-arm64.apex"}, android.NormalizePathsForTesting(rule.Implicits); !reflect.DeepEqual(expected, actual) {
-			t.Errorf("expected: %q, found: %q", expected, actual)
-		}
-
-		// Make sure that the prebuilt_apex has the correct input APEX.
-		prebuiltApex := ctx.ModuleForTests("myapex", "android_common_myapex")
-		rule = prebuiltApex.Rule("android/soong/android.Cp")
-		if expected, actual := "myapex-arm64.apex", android.NormalizePathForTesting(rule.Input); !reflect.DeepEqual(expected, actual) {
-			t.Errorf("expected: %q, found: %q", expected, actual)
-		}
-
-		checkDexJarBuildPath(t, ctx, "libfoo")
-		checkDexJarInstallPath(t, ctx, "libfoo")
-
-		checkDexJarBuildPath(t, ctx, "libbar")
-		checkDexJarInstallPath(t, ctx, "libbar")
-	})
-
-	t.Run("prebuilt with source preferred", func(t *testing.T) {
-
-		bp := `
-		prebuilt_apex {
-			name: "myapex",
-			arch: {
-				arm64: {
-					src: "myapex-arm64.apex",
-				},
-				arm: {
-					src: "myapex-arm.apex",
-				},
-			},
-			exported_java_libs: ["libfoo", "libbar"],
-		}
-
-		java_import {
-			name: "libfoo",
-			jars: ["libfoo.jar"],
-		}
-
-		java_library {
-			name: "libfoo",
-		}
-
-		java_sdk_library_import {
-			name: "libbar",
-			public: {
-				jars: ["libbar.jar"],
-			},
-		}
-
-		java_sdk_library {
-			name: "libbar",
-			srcs: ["foo/bar/MyClass.java"],
-			unsafe_ignore_missing_latest_api: true,
-		}
-	`
-
-		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-
-		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
-		checkDexJarInstallPath(t, ctx, "prebuilt_libfoo")
-		ensureNoSourceVariant(t, ctx, "libfoo")
-
-		checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
-		checkDexJarInstallPath(t, ctx, "prebuilt_libbar")
-		ensureNoSourceVariant(t, ctx, "libbar")
-	})
-
-	t.Run("prebuilt preferred with source", func(t *testing.T) {
-		bp := `
-		prebuilt_apex {
-			name: "myapex",
-			arch: {
-				arm64: {
-					src: "myapex-arm64.apex",
-				},
-				arm: {
-					src: "myapex-arm.apex",
-				},
-			},
-			exported_java_libs: ["libfoo", "libbar"],
-		}
-
-		java_import {
-			name: "libfoo",
-			prefer: true,
-			jars: ["libfoo.jar"],
-		}
-
-		java_library {
-			name: "libfoo",
-		}
-
-		java_sdk_library_import {
-			name: "libbar",
-			prefer: true,
-			public: {
-				jars: ["libbar.jar"],
-			},
-		}
-
-		java_sdk_library {
-			name: "libbar",
-			srcs: ["foo/bar/MyClass.java"],
-			unsafe_ignore_missing_latest_api: true,
-		}
-	`
-
-		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
-
-		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
-		checkDexJarInstallPath(t, ctx, "prebuilt_libfoo")
-		ensureNoSourceVariant(t, ctx, "libfoo")
-
-		checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
-		checkDexJarInstallPath(t, ctx, "prebuilt_libbar")
-		ensureNoSourceVariant(t, ctx, "libbar")
-	})
-}
-
 func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
 	preparer := android.GroupFixturePreparers(
 		java.FixtureConfigureApexBootJars("myapex:libfoo", "myapex:libbar"),
@@ -5064,23 +4907,6 @@
 		}),
 	)
 
-	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
-		t.Helper()
-		s := ctx.ModuleForTests("dex_bootjars", "android_common")
-		foundLibfooJar := false
-		base := stem + ".jar"
-		for _, output := range s.AllOutputs() {
-			if filepath.Base(output) == base {
-				foundLibfooJar = true
-				buildRule := s.Output(output)
-				android.AssertStringEquals(t, "boot dex jar path", bootDexJarPath, buildRule.Input.String())
-			}
-		}
-		if !foundLibfooJar {
-			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().SoongOutDir(), s.AllOutputs()))
-		}
-	}
-
 	checkHiddenAPIIndexFromClassesInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
 		t.Helper()
 		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
@@ -5133,10 +4959,13 @@
 			},
 		}
 
-		java_import {
+		java_sdk_library_import {
 			name: "libfoo",
-			jars: ["libfoo.jar"],
+			public: {
+				jars: ["libfoo.jar"],
+			},
 			apex_available: ["myapex"],
+			shared_library: false,
 			permitted_packages: ["foo"],
 		}
 
@@ -5152,8 +4981,6 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
@@ -5169,18 +4996,10 @@
 		apex_set {
 			name: "myapex",
 			set: "myapex.apks",
-			exported_java_libs: ["myjavalib"],
 			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
 			exported_systemserverclasspath_fragments: ["my-systemserverclasspath-fragment"],
 		}
 
-		java_import {
-			name: "myjavalib",
-			jars: ["myjavalib.jar"],
-			apex_available: ["myapex"],
-			permitted_packages: ["javalib"],
-		}
-
 		prebuilt_bootclasspath_fragment {
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
@@ -5201,13 +5020,17 @@
 			apex_available: ["myapex"],
 		}
 
-		java_import {
+		java_sdk_library_import {
 			name: "libfoo",
-			jars: ["libfoo.jar"],
+			public: {
+				jars: ["libfoo.jar"],
+			},
 			apex_available: ["myapex"],
-			permitted_packages: ["foo"],
+			shared_library: false,
+			permitted_packages: ["libfoo"],
 		}
 
+
 		java_sdk_library_import {
 			name: "libbar",
 			public: {
@@ -5230,8 +5053,6 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
@@ -5292,12 +5113,14 @@
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
+			sdk_version: "core_current",
 		}
 
 		java_library {
 			name: "libfoo",
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
+			sdk_version: "core_current",
 		}
 
 		java_sdk_library_import {
@@ -5383,12 +5206,15 @@
 			},
 		}
 
-		java_import {
+		java_sdk_library_import {
 			name: "libfoo",
 			prefer: true,
-			jars: ["libfoo.jar"],
+			public: {
+				jars: ["libfoo.jar"],
+			},
 			apex_available: ["myapex"],
-			permitted_packages: ["foo"],
+			shared_library: false,
+			permitted_packages: ["libfoo"],
 		}
 
 		java_library {
@@ -5396,6 +5222,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
 			installable: true,
+			sdk_version: "core_current",
 		}
 
 		java_sdk_library_import {
@@ -5419,8 +5246,6 @@
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
@@ -5486,6 +5311,7 @@
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
+			sdk_version: "core_current",
 		}
 
 		java_library {
@@ -5494,6 +5320,7 @@
 			apex_available: ["myapex"],
 			permitted_packages: ["foo"],
 			installable: true,
+			sdk_version: "core_current",
 		}
 
 		java_sdk_library_import {
@@ -5512,12 +5339,11 @@
 			apex_available: ["myapex"],
 			permitted_packages: ["bar"],
 			compile_dex: true,
+			sdk_version: "core_current",
 		}
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
@@ -5629,8 +5455,6 @@
 		)
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer2, fragment)
-		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
-		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
@@ -5728,7 +5552,6 @@
 			updatable: false,
 			tests: [
 				"mytest",
-				"mytests",
 			],
 		}
 
@@ -5771,25 +5594,6 @@
 				"testdata/baz"
 			],
 		}
-
-		cc_test {
-			name: "mytests",
-			gtest: false,
-			srcs: [
-				"mytest1.cpp",
-				"mytest2.cpp",
-				"mytest3.cpp",
-			],
-			test_per_src: true,
-			relative_install_path: "test",
-			system_shared_libs: [],
-			static_executable: true,
-			stl: "none",
-			data: [
-				":fg",
-				":fg2",
-			],
-		}
 	`)
 
 	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
@@ -5803,11 +5607,6 @@
 	ensureContains(t, copyCmds, "image.apex/bin/test/baz")
 	ensureContains(t, copyCmds, "image.apex/bin/test/bar/baz")
 
-	// Ensure that test deps built with `test_per_src` are copied into apex.
-	ensureContains(t, copyCmds, "image.apex/bin/test/mytest1")
-	ensureContains(t, copyCmds, "image.apex/bin/test/mytest2")
-	ensureContains(t, copyCmds, "image.apex/bin/test/mytest3")
-
 	// Ensure the module is correctly translated.
 	bundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, bundle)
@@ -5817,9 +5616,6 @@
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
 	ensureContains(t, androidMk, "LOCAL_MODULE := mytest.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := mytest1.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := mytest2.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := mytest3.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
 }
 
@@ -6134,6 +5930,7 @@
 			name: "TesterHelpAppFoo",
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: [ "myapex" ],
+			sdk_version: "test_current",
 		}
 
 	`)
@@ -6216,6 +6013,87 @@
 		system_shared_libs: [],
 		apex_available: ["otherapex"],
 	}`)
+
+	// 'apex_available' check is bypassed for /product apex with a specific prefix.
+	// TODO: b/352818241 - Remove below two cases after APEX availability is enforced for /product APEXes.
+	testApex(t, `
+	apex {
+		name: "com.sdv.myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+		product_specific: true,
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	apex {
+		name: "com.any.otherapex",
+		key: "otherapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "otherapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["com.any.otherapex"],
+		product_specific: true,
+	}`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.sdv.myapex-file_contexts":    nil,
+			"system/sepolicy/apex/com.any.otherapex-file_contexts": nil,
+		}))
+
+	// 'apex_available' check is not bypassed for non-product apex with a specific prefix.
+	testApexError(t, "requires \"libfoo\" that doesn't list the APEX under 'apex_available'.", `
+	apex {
+		name: "com.sdv.myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	apex {
+		name: "com.any.otherapex",
+		key: "otherapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "otherapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		system_shared_libs: [],
+		apex_available: ["com.any.otherapex"],
+	}`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.sdv.myapex-file_contexts":    nil,
+			"system/sepolicy/apex/com.any.otherapex-file_contexts": nil,
+		}))
 }
 
 func TestApexAvailable_IndirectDep(t *testing.T) {
@@ -6261,6 +6139,91 @@
 		stl: "none",
 		system_shared_libs: [],
 	}`)
+
+	// 'apex_available' check is bypassed for /product apex with a specific prefix.
+	// TODO: b/352818241 - Remove below two cases after APEX availability is enforced for /product APEXes.
+	testApex(t, `
+		apex {
+			name: "com.sdv.myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			updatable: false,
+			product_specific: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libfoo",
+			stl: "none",
+			shared_libs: ["libbar"],
+			system_shared_libs: [],
+			apex_available: ["com.sdv.myapex"],
+			product_specific: true,
+		}
+
+		cc_library {
+			name: "libbar",
+			stl: "none",
+			shared_libs: ["libbaz"],
+			system_shared_libs: [],
+			apex_available: ["com.sdv.myapex"],
+			product_specific: true,
+		}
+
+		cc_library {
+			name: "libbaz",
+			stl: "none",
+			system_shared_libs: [],
+			product_specific: true,
+		}`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.sdv.myapex-file_contexts": nil,
+		}))
+
+	// 'apex_available' check is not bypassed for non-product apex with a specific prefix.
+	testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'.`, `
+		apex {
+			name: "com.sdv.myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libfoo",
+			stl: "none",
+			shared_libs: ["libbar"],
+			system_shared_libs: [],
+			apex_available: ["com.sdv.myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stl: "none",
+			shared_libs: ["libbaz"],
+			system_shared_libs: [],
+			apex_available: ["com.sdv.myapex"],
+		}
+
+		cc_library {
+			name: "libbaz",
+			stl: "none",
+			system_shared_libs: [],
+		}`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.sdv.myapex-file_contexts": nil,
+		}))
 }
 
 func TestApexAvailable_IndirectStaticDep(t *testing.T) {
@@ -6758,6 +6721,99 @@
 	}
 }
 
+func TestApexAvailable_PrefixMatch(t *testing.T) {
+
+	for _, tc := range []struct {
+		name          string
+		apexAvailable string
+		expectedError string
+	}{
+		{
+			name:          "prefix matches correctly",
+			apexAvailable: "com.foo.*",
+		},
+		{
+			name:          "prefix doesn't match",
+			apexAvailable: "com.bar.*",
+			expectedError: `Consider .* "com.foo\.\*"`,
+		},
+		{
+			name:          "short prefix",
+			apexAvailable: "com.*",
+			expectedError: "requires two or more components",
+		},
+		{
+			name:          "wildcard not in the end",
+			apexAvailable: "com.*.foo",
+			expectedError: "should end with .*",
+		},
+		{
+			name:          "wildcard in the middle",
+			apexAvailable: "com.foo*.*",
+			expectedError: "not allowed in the middle",
+		},
+		{
+			name:          "hint with prefix pattern",
+			apexAvailable: "//apex_available:platform",
+			expectedError: "Consider adding \"com.foo.bar\" or \"com.foo.*\"",
+		},
+	} {
+		t.Run(tc.name, func(t *testing.T) {
+			errorHandler := android.FixtureExpectsNoErrors
+			if tc.expectedError != "" {
+				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedError)
+			}
+			context := android.GroupFixturePreparers(
+				prepareForApexTest,
+				android.FixtureMergeMockFs(android.MockFS{
+					"system/sepolicy/apex/com.foo.bar-file_contexts": nil,
+				}),
+			).ExtendWithErrorHandler(errorHandler)
+
+			context.RunTestWithBp(t, `
+				apex {
+					name: "com.foo.bar",
+					key: "myapex.key",
+					native_shared_libs: ["libfoo"],
+					updatable: false,
+				}
+
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+
+				cc_library {
+					name: "libfoo",
+					stl: "none",
+					system_shared_libs: [],
+					apex_available: ["`+tc.apexAvailable+`"],
+				}`)
+		})
+	}
+	testApexError(t, `Consider adding "com.foo" to`, `
+		apex {
+			name: "com.foo", // too short for a partner apex
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libfoo",
+			stl: "none",
+			system_shared_libs: [],
+		}
+	`)
+}
+
 func TestOverrideApex(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -7054,7 +7110,6 @@
 	module := ctx.ModuleForTests("myapex", "android_common_myapex")
 	args := module.Rule("apexRule").Args
 	ensureContains(t, args["opt_flags"], "--manifest_json "+module.Output("apex_manifest.json").Output.String())
-	ensureNotContains(t, args["opt_flags"], "--no_hashtree")
 
 	// The copies of the libraries in the apex should have one more dependency than
 	// the ones outside the apex, namely the unwinder. Ideally we should check
@@ -7125,6 +7180,46 @@
 	ensureMatches(t, contents, "<library\\n\\s+name=\\\"foo\\\"\\n\\s+file=\\\"/apex/myapex/javalib/foo.jar\\\"")
 }
 
+func TestJavaSDKLibraryOverrideApexes(t *testing.T) {
+	ctx := testApex(t, `
+		override_apex {
+			name: "mycompanyapex",
+			base: "myapex",
+		}
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["foo"],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			apex_available: [ "myapex" ],
+		}
+
+		prebuilt_apis {
+			name: "sdk",
+			api_dirs: ["100"],
+		}
+	`, withFiles(filesForSdkLibrary))
+
+	// Permission XML should point to the activated path of impl jar of java_sdk_library.
+	// Since override variants (com.mycompany.android.foo) are installed in the same package as the overridden variant
+	// (com.android.foo), the filepath should not contain override apex name.
+	sdkLibrary := ctx.ModuleForTests("foo.xml", "android_common_mycompanyapex").Output("foo.xml")
+	contents := android.ContentFromFileRuleForTests(t, ctx, sdkLibrary)
+	ensureMatches(t, contents, "<library\\n\\s+name=\\\"foo\\\"\\n\\s+file=\\\"/apex/myapex/javalib/foo.jar\\\"")
+}
+
 func TestJavaSDKLibrary_WithinApex(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -7152,7 +7247,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["a.java"],
-			libs: ["foo"],
+			libs: ["foo.impl"],
 			apex_available: ["myapex"],
 			sdk_version: "none",
 			system_modules: "none",
@@ -7205,7 +7300,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["a.java"],
-			libs: ["foo"],
+			libs: ["foo.stubs"],
 			sdk_version: "none",
 			system_modules: "none",
 		}
@@ -7259,7 +7354,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["a.java"],
-			libs: ["foo"],
+			libs: ["foo.impl"],
 			apex_available: ["myapex"],
 			sdk_version: "none",
 			system_modules: "none",
@@ -7603,7 +7698,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			sdk_version: "none",
 			system_modules: "none",
-			libs: ["myotherjar"],
+			static_libs: ["myotherjar"],
 			apex_available: [
 				"myapex",
 				"myapex.updatable",
@@ -7932,9 +8027,9 @@
 	ctx := testApex(t, bp, prepareForTestWithSantitizeHwaddress)
 
 	// Check that the extractor produces the correct output file from the correct input file.
-	extractorOutput := "out/soong/.intermediates/prebuilt_myapex.apex.extractor/android_common/extracted/myapex.hwasan.apks"
+	extractorOutput := "out/soong/.intermediates/myapex/android_common_myapex/extracted/myapex.hwasan.apks"
 
-	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("myapex", "android_common_myapex")
 	extractedApex := m.Output(extractorOutput)
 
 	android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings())
@@ -7959,10 +8054,10 @@
 		}
 	`)
 
-	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("myapex", "android_common_myapex")
 
 	// Check that the extractor produces the correct apks file from the input module
-	extractorOutput := "out/soong/.intermediates/prebuilt_myapex.apex.extractor/android_common/extracted/myapex.apks"
+	extractorOutput := "out/soong/.intermediates/myapex/android_common_myapex/extracted/myapex.apks"
 	extractedApex := m.Output(extractorOutput)
 
 	android.AssertArrayString(t, "extractor input", []string{"myapex.apks"}, extractedApex.Inputs.Strings())
@@ -8024,189 +8119,6 @@
 	return result.TestContext
 }
 
-func TestDuplicateDeapexersFromPrebuiltApexes(t *testing.T) {
-	preparers := android.GroupFixturePreparers(
-		java.PrepareForTestWithJavaDefaultModules,
-		prepareForTestWithBootclasspathFragment,
-		dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:libfoo"),
-		PrepareForTestWithApexBuildComponents,
-	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
-			"Multiple installable prebuilt APEXes provide ambiguous deapexers: prebuilt_com.android.art and prebuilt_com.mycompany.android.art"))
-
-	bpBase := `
-		apex_set {
-			name: "com.android.art",
-			installable: true,
-			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
-			set: "myapex.apks",
-		}
-
-		apex_set {
-			name: "com.mycompany.android.art",
-			apex_name: "com.android.art",
-			installable: true,
-			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
-			set: "company-myapex.apks",
-		}
-
-		prebuilt_bootclasspath_fragment {
-			name: "art-bootclasspath-fragment",
-			apex_available: ["com.android.art"],
-			hidden_api: {
-				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
-				metadata: "my-bootclasspath-fragment/metadata.csv",
-				index: "my-bootclasspath-fragment/index.csv",
-				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
-				all_flags: "my-bootclasspath-fragment/all-flags.csv",
-			},
-			%s
-		}
-	`
-
-	t.Run("java_import", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
-			java_import {
-				name: "libfoo",
-				jars: ["libfoo.jar"],
-				apex_available: ["com.android.art"],
-			}
-		`)
-	})
-
-	t.Run("java_sdk_library_import", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
-			java_sdk_library_import {
-				name: "libfoo",
-				public: {
-					jars: ["libbar.jar"],
-				},
-				shared_library: false,
-				apex_available: ["com.android.art"],
-			}
-		`)
-	})
-
-	t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `
-			image_name: "art",
-			contents: ["libfoo"],
-		`)+`
-			java_sdk_library_import {
-				name: "libfoo",
-				public: {
-					jars: ["libbar.jar"],
-				},
-				shared_library: false,
-				apex_available: ["com.android.art"],
-			}
-		`)
-	})
-}
-
-func TestDuplicateButEquivalentDeapexersFromPrebuiltApexes(t *testing.T) {
-	preparers := android.GroupFixturePreparers(
-		java.PrepareForTestWithJavaDefaultModules,
-		PrepareForTestWithApexBuildComponents,
-	)
-
-	errCtx := moduleErrorfTestCtx{}
-
-	bpBase := `
-		apex_set {
-			name: "com.android.myapex",
-			installable: true,
-			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
-			set: "myapex.apks",
-		}
-
-		apex_set {
-			name: "com.android.myapex_compressed",
-			apex_name: "com.android.myapex",
-			installable: true,
-			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
-			set: "myapex_compressed.apks",
-		}
-
-		prebuilt_bootclasspath_fragment {
-			name: "my-bootclasspath-fragment",
-			apex_available: [
-				"com.android.myapex",
-				"com.android.myapex_compressed",
-			],
-			hidden_api: {
-				annotation_flags: "annotation-flags.csv",
-				metadata: "metadata.csv",
-				index: "index.csv",
-				signature_patterns: "signature_patterns.csv",
-			},
-			%s
-		}
-	`
-
-	t.Run("java_import", func(t *testing.T) {
-		result := preparers.RunTestWithBp(t,
-			fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
-			java_import {
-				name: "libfoo",
-				jars: ["libfoo.jar"],
-				apex_available: [
-					"com.android.myapex",
-					"com.android.myapex_compressed",
-				],
-			}
-		`)
-
-		module := result.Module("libfoo", "android_common_com.android.myapex")
-		usesLibraryDep := module.(java.UsesLibraryDependency)
-		android.AssertPathRelativeToTopEquals(t, "dex jar path",
-			"out/soong/.intermediates/prebuilt_com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar",
-			usesLibraryDep.DexJarBuildPath(errCtx).Path())
-	})
-
-	t.Run("java_sdk_library_import", func(t *testing.T) {
-		result := preparers.RunTestWithBp(t,
-			fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
-			java_sdk_library_import {
-				name: "libfoo",
-				public: {
-					jars: ["libbar.jar"],
-				},
-				apex_available: [
-					"com.android.myapex",
-					"com.android.myapex_compressed",
-				],
-				compile_dex: true,
-			}
-		`)
-
-		module := result.Module("libfoo", "android_common_com.android.myapex")
-		usesLibraryDep := module.(java.UsesLibraryDependency)
-		android.AssertPathRelativeToTopEquals(t, "dex jar path",
-			"out/soong/.intermediates/prebuilt_com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar",
-			usesLibraryDep.DexJarBuildPath(errCtx).Path())
-	})
-
-	t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `
-			image_name: "art",
-			contents: ["libfoo"],
-		`)+`
-			java_sdk_library_import {
-				name: "libfoo",
-				public: {
-					jars: ["libbar.jar"],
-				},
-				apex_available: [
-					"com.android.myapex",
-					"com.android.myapex_compressed",
-				],
-				compile_dex: true,
-			}
-		`)
-	})
-}
-
 func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
 	testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, `
 		apex {
@@ -8238,60 +8150,6 @@
 	`)
 }
 
-func Test_use_vndk_as_stable_shouldnt_be_used_for_updatable_vendor_apexes(t *testing.T) {
-	testApexError(t, `"myapex" .*: use_vndk_as_stable: updatable APEXes can't use external VNDK libs`, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			updatable: true,
-			use_vndk_as_stable: true,
-			soc_specific: true,
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-	`)
-}
-
-func Test_use_vndk_as_stable_shouldnt_be_used_with_min_sdk_version(t *testing.T) {
-	testApexError(t, `"myapex" .*: use_vndk_as_stable: not supported when min_sdk_version is set`, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			updatable: false,
-			min_sdk_version: "29",
-			use_vndk_as_stable: true,
-			vendor: true,
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-	`)
-}
-
-func Test_use_vndk_as_stable_shouldnt_be_used_for_non_vendor_apexes(t *testing.T) {
-	testApexError(t, `"myapex" .*: use_vndk_as_stable: not supported for system/system_ext APEXes`, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			updatable: false,
-			use_vndk_as_stable: true,
-		}
-
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-	`)
-}
-
 func TestUpdatable_should_not_set_generate_classpaths_proto(t *testing.T) {
 	testApexError(t, `"mysystemserverclasspathfragment" .* it must not set generate_classpaths_proto to false`, `
 		apex {
@@ -8318,6 +8176,7 @@
 			apex_available: [
 				"myapex",
 			],
+			sdk_version: "current",
 		}
 
 		systemserverclasspath_fragment {
@@ -8371,12 +8230,16 @@
 				},
 			}
 
-			java_import {
-				name: "libfoo",
+		java_sdk_library_import {
+			name: "libfoo",
+			prefer: true,
+			public: {
 				jars: ["libfoo.jar"],
-				apex_available: ["myapex"],
-				permitted_packages: ["libfoo"],
-			}
+			},
+			apex_available: ["myapex"],
+			shared_library: false,
+			permitted_packages: ["libfoo"],
+		}
 		`, "", preparer, fragment)
 	})
 }
@@ -8756,7 +8619,7 @@
 		}),
 	)
 
-	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("myapex", "android_common_myapex")
 
 	// Check extract_apks tool parameters.
 	extractedApex := m.Output("extracted/myapex.apks")
@@ -8797,7 +8660,7 @@
 		}),
 	)
 
-	m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common")
+	m := ctx.ModuleForTests("myapex", "android_common_myapex")
 
 	// Check extract_apks tool parameters. No native bridge arch expected
 	extractedApex := m.Output("extracted/myapex.apks")
@@ -9035,6 +8898,30 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.capex\n")
 }
 
+func TestApexSet_ShouldRespectCompressedApexFlag(t *testing.T) {
+	for _, compressionEnabled := range []bool{true, false} {
+		t.Run(fmt.Sprintf("compressionEnabled=%v", compressionEnabled), func(t *testing.T) {
+			ctx := testApex(t, `
+				apex_set {
+					name: "com.company.android.myapex",
+					apex_name: "com.android.myapex",
+					set: "company-myapex.apks",
+				}
+			`, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.CompressedApex = proptools.BoolPtr(compressionEnabled)
+			}),
+			)
+
+			build := ctx.ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex").Output("com.company.android.myapex.apex")
+			if compressionEnabled {
+				ensureEquals(t, build.Rule.String(), "android/soong/android.Cp")
+			} else {
+				ensureEquals(t, build.Rule.String(), "android/apex.decompressApex")
+			}
+		})
+	}
+}
+
 func TestPreferredPrebuiltSharedLibDep(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -9336,6 +9223,7 @@
 			srcs: ["mybootclasspathlib.java"],
 			apex_available: ["myapex"],
 			compile_dex: true,
+			sdk_version: "current",
 		}
 
 		systemserverclasspath_fragment {
@@ -9459,42 +9347,6 @@
 	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n")
 }
 
-func TestAndroidMk_DexpreoptBuiltInstalledForApex_Prebuilt(t *testing.T) {
-	ctx := testApex(t, `
-		prebuilt_apex {
-			name: "myapex",
-			arch: {
-				arm64: {
-					src: "myapex-arm64.apex",
-				},
-				arm: {
-					src: "myapex-arm.apex",
-				},
-			},
-			exported_java_libs: ["foo"],
-		}
-
-		java_import {
-			name: "foo",
-			jars: ["foo.jar"],
-			apex_available: ["myapex"],
-		}
-	`,
-		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
-	)
-
-	prebuilt := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
-	entriesList := android.AndroidMkEntriesForTest(t, ctx, prebuilt)
-	mainModuleEntries := entriesList[0]
-	android.AssertArrayString(t,
-		"LOCAL_REQUIRED_MODULES",
-		mainModuleEntries.EntryMap["LOCAL_REQUIRED_MODULES"],
-		[]string{
-			"foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex",
-			"foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex",
-		})
-}
-
 func TestAndroidMk_RequiredModules(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -9651,6 +9503,7 @@
 				unsafe_ignore_missing_latest_api: true,
 				min_sdk_version: "31",
 				static_libs: ["util"],
+				sdk_version: "core_current",
 			}
 
 			java_library {
@@ -9659,6 +9512,7 @@
 				apex_available: ["myapex"],
 				min_sdk_version: "31",
 				static_libs: ["another_util"],
+				sdk_version: "core_current",
 			}
 
 			java_library {
@@ -9666,6 +9520,7 @@
                 srcs: ["a.java"],
 				min_sdk_version: "31",
 				apex_available: ["myapex"],
+				sdk_version: "core_current",
 			}
 		`)
 	})
@@ -9721,7 +9576,7 @@
 	})
 
 	t.Run("bootclasspath_fragment jar must set min_sdk_version", func(t *testing.T) {
-		preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "mybootclasspathlib".*must set min_sdk_version`)).
+		preparer.
 			RunTestWithBp(t, `
 				apex {
 					name: "myapex",
@@ -9752,6 +9607,8 @@
 					apex_available: ["myapex"],
 					compile_dex: true,
 					unsafe_ignore_missing_latest_api: true,
+					sdk_version: "current",
+					min_sdk_version: "30",
 				}
 		`)
 	})
@@ -9997,6 +9854,56 @@
 	}
 }
 
+func TestApexLintBcpFragmentSdkLibDeps(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			bootclasspath_fragments: ["mybootclasspathfragment"],
+			min_sdk_version: "29",
+			java_libs: [
+				"jacocoagent",
+			],
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		bootclasspath_fragment {
+			name: "mybootclasspathfragment",
+			contents: ["foo"],
+			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+		java_sdk_library {
+			name: "foo",
+			srcs: ["MyClass.java"],
+			apex_available: [ "myapex" ],
+			sdk_version: "current",
+			min_sdk_version: "29",
+			compile_dex: true,
+		}
+		`
+	fs := android.MockFS{
+		"lint-baseline.xml": nil,
+	}
+
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.PrepareForTestWithJacocoInstrumentation,
+		java.FixtureWithLastReleaseApis("foo"),
+		android.FixtureMergeMockFs(fs),
+	).RunTestWithBp(t, bp)
+
+	myapex := result.ModuleForTests("myapex", "android_common_myapex")
+	lintReportInputs := strings.Join(myapex.Output("lint-report-xml.zip").Inputs.Strings(), " ")
+	android.AssertStringDoesContain(t,
+		"myapex lint report expected to contain that of the sdk library impl lib as an input",
+		lintReportInputs, "foo.impl")
+}
+
 // updatable apexes should propagate updatable=true to its apps
 func TestUpdatableApexEnforcesAppUpdatability(t *testing.T) {
 	bp := `
@@ -10060,208 +9967,6 @@
 	}
 }
 
-func TestApexBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
-	bp := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["libbaz"],
-			binaries: ["binfoo"],
-			min_sdk_version: "29",
-		}
-		apex_key {
-			name: "myapex.key",
-		}
-		cc_binary {
-			name: "binfoo",
-			shared_libs: ["libbar", "libbaz", "libqux",],
-			apex_available: ["myapex"],
-			min_sdk_version: "29",
-			recovery_available: false,
-		}
-		cc_library {
-			name: "libbar",
-			srcs: ["libbar.cc"],
-			stubs: {
-				symbol_file: "libbar.map.txt",
-				versions: [
-					"29",
-				],
-			},
-		}
-		cc_library {
-			name: "libbaz",
-			srcs: ["libbaz.cc"],
-			apex_available: ["myapex"],
-			min_sdk_version: "29",
-			stubs: {
-				symbol_file: "libbaz.map.txt",
-				versions: [
-					"29",
-				],
-			},
-		}
-		cc_api_library {
-			name: "libbar",
-			src: "libbar_stub.so",
-			min_sdk_version: "29",
-			variants: ["apex.29"],
-		}
-		cc_api_variant {
-			name: "libbar",
-			variant: "apex",
-			version: "29",
-			src: "libbar_apex_29.so",
-		}
-		cc_api_library {
-			name: "libbaz",
-			src: "libbaz_stub.so",
-			min_sdk_version: "29",
-			variants: ["apex.29"],
-		}
-		cc_api_variant {
-			name: "libbaz",
-			variant: "apex",
-			version: "29",
-			src: "libbaz_apex_29.so",
-		}
-		cc_api_library {
-			name: "libqux",
-			src: "libqux_stub.so",
-			min_sdk_version: "29",
-			variants: ["apex.29"],
-		}
-		cc_api_variant {
-			name: "libqux",
-			variant: "apex",
-			version: "29",
-			src: "libqux_apex_29.so",
-		}
-		api_imports {
-			name: "api_imports",
-			apex_shared_libs: [
-				"libbar",
-				"libbaz",
-				"libqux",
-			],
-		}
-		`
-	result := testApex(t, bp)
-
-	hasDep := func(m android.Module, wantDep android.Module) bool {
-		t.Helper()
-		var found bool
-		result.VisitDirectDeps(m, func(dep blueprint.Module) {
-			if dep == wantDep {
-				found = true
-			}
-		})
-		return found
-	}
-
-	// Library defines stubs and cc_api_library should be used with cc_api_library
-	binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Module()
-	libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
-	libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
-
-	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant))
-	android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant))
-
-	binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Rule("ld").Args["libFlags"]
-	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so")
-
-	// Library defined in the same APEX should be linked with original definition instead of cc_api_library
-	libbazApexVariant := result.ModuleForTests("libbaz", "android_arm64_armv8-a_shared_apex29").Module()
-	libbazApiImportCoreVariant := result.ModuleForTests("libbaz.apiimport", "android_arm64_armv8-a_shared").Module()
-	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even from same APEX", true, hasDep(binfooApexVariant, libbazApiImportCoreVariant))
-	android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbazApexVariant))
-
-	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbaz.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbaz.apiimport.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbaz.apex.29.apiimport.so")
-
-	// cc_api_library defined without original library should be linked with cc_api_library
-	libquxApiImportApexVariant := result.ModuleForTests("libqux.apiimport", "android_arm64_armv8-a_shared").Module()
-	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even original library definition does not exist", true, hasDep(binfooApexVariant, libquxApiImportApexVariant))
-	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libqux.apex.29.apiimport.so")
-}
-
-func TestPlatformBinaryBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
-	bp := `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["libbar"],
-			min_sdk_version: "29",
-		}
-		apex_key {
-			name: "myapex.key",
-		}
-		cc_binary {
-			name: "binfoo",
-			shared_libs: ["libbar"],
-			recovery_available: false,
-		}
-		cc_library {
-			name: "libbar",
-			srcs: ["libbar.cc"],
-			apex_available: ["myapex"],
-			min_sdk_version: "29",
-			stubs: {
-				symbol_file: "libbar.map.txt",
-				versions: [
-					"29",
-				],
-			},
-		}
-		cc_api_library {
-			name: "libbar",
-			src: "libbar_stub.so",
-			variants: ["apex.29"],
-		}
-		cc_api_variant {
-			name: "libbar",
-			variant: "apex",
-			version: "29",
-			src: "libbar_apex_29.so",
-		}
-		api_imports {
-			name: "api_imports",
-			apex_shared_libs: [
-				"libbar",
-			],
-		}
-		`
-
-	result := testApex(t, bp)
-
-	hasDep := func(m android.Module, wantDep android.Module) bool {
-		t.Helper()
-		var found bool
-		result.VisitDirectDeps(m, func(dep blueprint.Module) {
-			if dep == wantDep {
-				found = true
-			}
-		})
-		return found
-	}
-
-	// Library defines stubs and cc_api_library should be used with cc_api_library
-	binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Module()
-	libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
-	libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
-
-	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant))
-	android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant))
-
-	binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
-	android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so")
-	android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so")
-}
-
 func TestTrimmedApex(t *testing.T) {
 	bp := `
 		apex {
@@ -10300,21 +10005,6 @@
 			apex_available: ["myapex","mydcla"],
 			min_sdk_version: "29",
 		}
-		cc_api_library {
-			name: "libc",
-			src: "libc.so",
-			min_sdk_version: "29",
-			recovery_available: true,
-			vendor_available: true,
-			product_available: true,
-		}
-		api_imports {
-			name: "api_imports",
-			shared_libs: [
-				"libc",
-			],
-			header_libs: [],
-		}
 		`
 	ctx := testApex(t, bp)
 	module := ctx.ModuleForTests("myapex", "android_common_myapex")
@@ -10586,14 +10276,14 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 8 {
-		t.Fatalf("Expected 5 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 12 {
+		t.Fatalf("Expected 12 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
-	ensureMatches(t, copyCmds[4], "^cp -f .*/aconfig_flags.pb .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[5], "^cp -f .*/package.map .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[6], "^cp -f .*/flag.map .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[7], "^cp -f .*/flag.val .*/image.apex/etc$")
+	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")
 
 	inputs := []string{
 		"my_aconfig_declarations_foo/intermediate.pb",
@@ -10721,14 +10411,14 @@
 	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) != 16 {
+		t.Fatalf("Expected 16 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
-	ensureMatches(t, copyCmds[8], "^cp -f .*/aconfig_flags.pb .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[9], "^cp -f .*/package.map .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[10], "^cp -f .*/flag.map .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[11], "^cp -f .*/flag.val .*/image.apex/etc$")
+	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")
 
 	inputs := []string{
 		"my_aconfig_declarations_foo/intermediate.pb",
@@ -10889,14 +10579,14 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 32 {
-		t.Fatalf("Expected 28 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 36 {
+		t.Fatalf("Expected 36 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
-	ensureMatches(t, copyCmds[28], "^cp -f .*/aconfig_flags.pb .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[29], "^cp -f .*/package.map .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[30], "^cp -f .*/flag.map .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[31], "^cp -f .*/flag.val .*/image.apex/etc$")
+	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")
 
 	inputs := []string{
 		"my_aconfig_declarations_foo/intermediate.pb",
@@ -11236,12 +10926,12 @@
 		{
 			desc:                      "Prebuilt apex prebuilt_com.android.foo is selected, profile should come from .prof deapexed from the prebuilt",
 			selectedApexContributions: "foo.prebuilt.contributions",
-			expectedBootJar:           "out/soong/.intermediates/prebuilt_com.android.foo.deapexer/android_common/deapexer/javalib/framework-foo.jar",
+			expectedBootJar:           "out/soong/.intermediates/prebuilt_com.android.foo/android_common_com.android.foo/deapexer/javalib/framework-foo.jar",
 		},
 		{
 			desc:                      "Prebuilt apex prebuilt_com.android.foo.v2 is selected, profile should come from .prof deapexed from the prebuilt",
 			selectedApexContributions: "foo.prebuilt.v2.contributions",
-			expectedBootJar:           "out/soong/.intermediates/prebuilt_com.android.foo.v2.deapexer/android_common/deapexer/javalib/framework-foo.jar",
+			expectedBootJar:           "out/soong/.intermediates/com.android.foo.v2/android_common_com.android.foo/deapexer/javalib/framework-foo.jar",
 		},
 	}
 
@@ -11270,11 +10960,7 @@
 				fs["platform/Test.java"] = nil
 			}),
 
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions,
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions),
 		)
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
 		checkBootDexJarPath(t, ctx, "framework-foo", tc.expectedBootJar)
@@ -11413,11 +11099,7 @@
 			android.FixtureMergeMockFs(map[string][]byte{
 				"system/sepolicy/apex/com.android.foo-file_contexts": nil,
 			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions,
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions),
 		)
 		if tc.expectedError != "" {
 			preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(tc.expectedError))
@@ -11533,11 +11215,7 @@
 			android.FixtureMergeMockFs(map[string][]byte{
 				"system/sepolicy/apex/com.android.adservices-file_contexts": nil,
 			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions,
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions),
 		)
 		ctx := testApex(t, bp, preparer)
 
@@ -11570,9 +11248,6 @@
 		prepareForApexTest,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 	).RunTestWithBp(t, `
 		java_library {
 			name: "baz-java-lib",
@@ -11813,3 +11488,245 @@
 
 	java.CheckModuleHasDependency(t, res.TestContext, "myoverrideapex", "android_common_myoverrideapex_myoverrideapex", "foo")
 }
+
+func TestUpdatableApexMinSdkVersionCurrent(t *testing.T) {
+	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",
+			key: "myapex.key",
+			updatable: true,
+			min_sdk_version: "current",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+}
+
+func TestPrebuiltStubNoinstall(t *testing.T) {
+	testFunc := func(t *testing.T, expectLibfooOnSystemLib bool, fs android.MockFS) {
+		result := android.GroupFixturePreparers(
+			prepareForApexTest,
+			android.PrepareForTestWithAndroidMk,
+			android.PrepareForTestWithMakevars,
+			android.FixtureMergeMockFs(fs),
+		).RunTest(t)
+
+		ldRule := result.ModuleForTests("installedlib", "android_arm64_armv8-a_shared").Rule("ld")
+		android.AssertStringDoesContain(t, "", ldRule.Args["libFlags"], "android_arm64_armv8-a_shared_current/libfoo.so")
+
+		installRules := result.InstallMakeRulesForTesting(t)
+
+		var installedlibRule *android.InstallMakeRule
+		for i, rule := range installRules {
+			if rule.Target == "out/target/product/test_device/system/lib/installedlib.so" {
+				if installedlibRule != nil {
+					t.Errorf("Duplicate install rules for %s", rule.Target)
+				}
+				installedlibRule = &installRules[i]
+			}
+		}
+		if installedlibRule == nil {
+			t.Errorf("No install rule found for installedlib")
+			return
+		}
+
+		if expectLibfooOnSystemLib {
+			android.AssertStringListContains(t,
+				"installedlib doesn't have install dependency on libfoo impl",
+				installedlibRule.OrderOnlyDeps,
+				"out/target/product/test_device/system/lib/libfoo.so")
+		} else {
+			android.AssertStringListDoesNotContain(t,
+				"installedlib has install dependency on libfoo stub",
+				installedlibRule.Deps,
+				"out/target/product/test_device/system/lib/libfoo.so")
+			android.AssertStringListDoesNotContain(t,
+				"installedlib has order-only install dependency on libfoo stub",
+				installedlibRule.OrderOnlyDeps,
+				"out/target/product/test_device/system/lib/libfoo.so")
+		}
+	}
+
+	prebuiltLibfooBp := []byte(`
+		cc_prebuilt_library {
+			name: "libfoo",
+			prefer: true,
+			srcs: ["libfoo.so"],
+			stubs: {
+				versions: ["1"],
+			},
+			apex_available: ["apexfoo"],
+		}
+	`)
+
+	apexfooBp := []byte(`
+		apex {
+			name: "apexfoo",
+			key: "apexfoo.key",
+			native_shared_libs: ["libfoo"],
+			updatable: false,
+			compile_multilib: "both",
+		}
+		apex_key {
+			name: "apexfoo.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	installedlibBp := []byte(`
+		cc_library {
+			name: "installedlib",
+			shared_libs: ["libfoo"],
+		}
+	`)
+
+	t.Run("prebuilt stub (without source): no install", func(t *testing.T) {
+		testFunc(
+			t,
+			/*expectLibfooOnSystemLib=*/ false,
+			android.MockFS{
+				"prebuilts/module_sdk/art/current/Android.bp": prebuiltLibfooBp,
+				"apexfoo/Android.bp":                          apexfooBp,
+				"system/sepolicy/apex/apexfoo-file_contexts":  nil,
+				"Android.bp": installedlibBp,
+			},
+		)
+	})
+
+	disabledSourceLibfooBp := []byte(`
+		cc_library {
+			name: "libfoo",
+			enabled: false,
+			stubs: {
+				versions: ["1"],
+			},
+			apex_available: ["apexfoo"],
+		}
+	`)
+
+	t.Run("prebuilt stub (with disabled source): no install", func(t *testing.T) {
+		testFunc(
+			t,
+			/*expectLibfooOnSystemLib=*/ false,
+			android.MockFS{
+				"prebuilts/module_sdk/art/current/Android.bp": prebuiltLibfooBp,
+				"impl/Android.bp":                            disabledSourceLibfooBp,
+				"apexfoo/Android.bp":                         apexfooBp,
+				"system/sepolicy/apex/apexfoo-file_contexts": nil,
+				"Android.bp":                                 installedlibBp,
+			},
+		)
+	})
+}
+
+func TestSdkLibraryTransitiveClassLoaderContext(t *testing.T) {
+	// 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.
+	android.GroupFixturePreparers(
+		prepareForApexTest,
+		android.PrepareForIntegrationTestWithAndroid,
+		PrepareForTestWithApexBuildComponents,
+		android.FixtureModifyEnv(func(env map[string]string) {
+			env["DISABLE_CONTAINER_CHECK"] = "true"
+		}),
+		withFiles(filesForSdkLibrary),
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/com.android.foo30-file_contexts": nil,
+		}),
+	).RunTestWithBp(t, `
+		apex {
+		name: "com.android.foo30",
+		key: "myapex.key",
+		updatable: true,
+		bootclasspath_fragments: [
+			"foo-bootclasspath-fragment",
+		],
+		java_libs: [
+			"bar",
+		],
+		apps: [
+			"bar-app",
+		],
+		min_sdk_version: "30",
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		bootclasspath_fragment {
+			name: "foo-bootclasspath-fragment",
+			contents: [
+				"framework-foo",
+			],
+			apex_available: [
+				"com.android.foo30",
+			],
+			hidden_api: {
+				split_packages: ["*"]
+			},
+		}
+
+		java_sdk_library {
+			name: "framework-foo",
+			srcs: [
+				"A.java"
+			],
+			unsafe_ignore_missing_latest_api: true,
+			apex_available: [
+				"com.android.foo30",
+			],
+			compile_dex: true,
+			sdk_version: "core_current",
+			shared_library: false,
+		}
+
+		java_library {
+			name: "bar",
+			srcs: [
+				"A.java"
+			],
+			libs: [
+				"framework-foo.impl",
+			],
+			apex_available: [
+				"com.android.foo30",
+			],
+			sdk_version: "core_current",
+		}
+
+		java_library {
+			name: "baz",
+			srcs: [
+				"A.java"
+			],
+			libs: [
+				"bar",
+			],
+			sdk_version: "core_current",
+		}
+
+		android_app {
+			name: "bar-app",
+			srcs: [
+				"A.java"
+			],
+			libs: [
+				"baz",
+				"framework-foo.impl",
+			],
+			apex_available: [
+				"com.android.foo30",
+			],
+			sdk_version: "core_current",
+			min_sdk_version: "30",
+			manifest: "AndroidManifest.xml",
+		}
+       `)
+}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 533f937..e44d3f5 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -53,11 +53,7 @@
 		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
 		java.FixtureConfigureApexBootJars("someapex:foo", "someapex:bar"),
 		prepareForTestWithArtApex,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo", "baz"),
 	).RunTestWithBp(t, `
@@ -108,6 +104,7 @@
 			test: {
 				enabled: true,
 			},
+			sdk_version: "core_current",
 		}
 
 		java_library {
@@ -156,7 +153,7 @@
 
 	// Check stub dex paths exported by art.
 	artFragment := result.Module("art-bootclasspath-fragment", "android_common")
-	artInfo, _ := android.SingletonModuleProvider(result, artFragment, java.HiddenAPIInfoProvider)
+	artInfo, _ := android.OtherModuleProvider(result, artFragment, java.HiddenAPIInfoProvider)
 
 	bazPublicStubs := "out/soong/.intermediates/baz.stubs.exportable/android_common/dex/baz.stubs.exportable.jar"
 	bazSystemStubs := "out/soong/.intermediates/baz.stubs.exportable.system/android_common/dex/baz.stubs.exportable.system.jar"
@@ -169,7 +166,7 @@
 
 	// Check stub dex paths exported by other.
 	otherFragment := result.Module("other-bootclasspath-fragment", "android_common")
-	otherInfo, _ := android.SingletonModuleProvider(result, otherFragment, java.HiddenAPIInfoProvider)
+	otherInfo, _ := android.OtherModuleProvider(result, otherFragment, java.HiddenAPIInfoProvider)
 
 	fooPublicStubs := "out/soong/.intermediates/foo.stubs.exportable/android_common/dex/foo.stubs.exportable.jar"
 	fooSystemStubs := "out/soong/.intermediates/foo.stubs.exportable.system/android_common/dex/foo.stubs.exportable.system.jar"
@@ -401,11 +398,20 @@
 
 			// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
 			addPrebuilt(true, "foo", "bar"),
+			android.FixtureMergeMockFs(android.MockFS{
+				"apex_contributions/Android.bp": []byte(`
+				apex_contributions {
+					name: "prebuilt_art_contributions",
+					contents: ["prebuilt_com.android.art"],
+					api_domain: "com.android.art",
+				}
+			`)}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
 
 			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 		).RunTest(t)
 
-		ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common", []string{
+		ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common_com.android.art", []string{
 			"etc/boot-image.prof",
 			"javalib/bar.jar",
 			"javalib/foo.jar",
@@ -498,6 +504,7 @@
 		java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
 		dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"),
 		java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
 	)
 
 	bp := `
@@ -555,6 +562,12 @@
 			src: "com.mycompany.android.art.apex",
 			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 		}
+	
+		apex_contributions {
+			name: "prebuilt_art_contributions",
+			contents: ["prebuilt_com.android.art"],
+			api_domain: "com.android.art",
+		}
 	`
 
 	t.Run("disabled alternative APEX", func(t *testing.T) {
@@ -564,27 +577,18 @@
 			`all_apex_contributions`,
 			`dex2oatd`,
 			`prebuilt_art-bootclasspath-fragment`,
-			`prebuilt_com.android.art.apex.selector`,
-			`prebuilt_com.android.art.deapexer`,
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{
 			`all_apex_contributions`,
 			`dex2oatd`,
 			`prebuilt_bar`,
-			`prebuilt_com.android.art.deapexer`,
 			`prebuilt_foo`,
 		})
 
 		module := result.ModuleForTests("dex_bootjars", "android_common")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
-
-	t.Run("enabled alternative APEX", func(t *testing.T) {
-		preparers.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
-			"Multiple installable prebuilt APEXes provide ambiguous deapexers: prebuilt_com.android.art and prebuilt_com.mycompany.android.art")).
-			RunTestWithBp(t, fmt.Sprintf(bp, ""))
-	})
 }
 
 // checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the
@@ -692,7 +696,7 @@
 	// Make sure that the fragment provides the hidden API encoded dex jars to the APEX.
 	fragment := result.Module("mybootclasspathfragment", "android_common_apex10000")
 
-	info, _ := android.SingletonModuleProvider(result, fragment, java.BootclasspathFragmentApexContentInfoProvider)
+	info, _ := android.OtherModuleProvider(result, fragment, java.BootclasspathFragmentApexContentInfoProvider)
 
 	checkFragmentExportedDexJar := func(name string, expectedDexJar string) {
 		module := result.Module(name, "android_common_apex10000")
@@ -731,11 +735,7 @@
 
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo", "quuz"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "com.android.art",
@@ -757,6 +757,7 @@
 			],
 			srcs: ["b.java"],
 			compile_dex: true,
+			sdk_version: "core_current",
 		}
 
 		java_sdk_library {
@@ -841,6 +842,7 @@
 	`)
 
 	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+		"all_apex_contributions",
 		"art-bootclasspath-fragment",
 		"bar",
 		"dex2oatd",
@@ -930,6 +932,7 @@
 			],
 			srcs: ["b.java"],
 			compile_dex: true,
+			sdk_version: "core_current",
 		}
 
 		java_library {
@@ -1011,6 +1014,7 @@
 	`)
 
 	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+		"all_apex_contributions",
 		"android-non-updatable.stubs",
 		"android-non-updatable.stubs.module_lib",
 		"android-non-updatable.stubs.system",
@@ -1101,6 +1105,7 @@
 			],
 			srcs: ["b.java"],
 			compile_dex: true,
+			sdk_version: "core_current",
 		}
 
 		java_library {
@@ -1182,6 +1187,7 @@
 	`)
 
 	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+		"all_apex_contributions",
 		"android-non-updatable.stubs",
 		"android-non-updatable.stubs.system",
 		"android-non-updatable.stubs.test",
@@ -1253,6 +1259,7 @@
 			],
 			srcs: ["b.java"],
 			compile_dex: true,
+			sdk_version: "core_current",
 		}
 
 		java_library {
@@ -1334,6 +1341,7 @@
 	`)
 
 	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
+		"all_apex_contributions",
 		"art-bootclasspath-fragment",
 		"bar",
 		"dex2oatd",
diff --git a/apex/builder.go b/apex/builder.go
index 763ce4d..a851d12 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -83,6 +83,7 @@
 	pctx.HostBinToolVariable("assemble_vintf", "assemble_vintf")
 	pctx.HostBinToolVariable("apex_elf_checker", "apex_elf_checker")
 	pctx.HostBinToolVariable("aconfig", "aconfig")
+	pctx.HostBinToolVariable("host_apex_verifier", "host_apex_verifier")
 }
 
 type createStorageStruct struct {
@@ -249,6 +250,13 @@
 		Description: "run apex_linkerconfig_validation",
 	}, "image_dir")
 
+	apexHostVerifierRule = pctx.StaticRule("apexHostVerifierRule", blueprint.RuleParams{
+		Command: `${host_apex_verifier} --deapexer=${deapexer} --debugfs=${debugfs_static} ` +
+			`--fsckerofs=${fsck_erofs} --apex=${in} && touch ${out}`,
+		CommandDeps: []string{"${host_apex_verifier}", "${deapexer}", "${debugfs_static}", "${fsck_erofs}"},
+		Description: "run host_apex_verifier",
+	})
+
 	assembleVintfRule = pctx.StaticRule("assembleVintfRule", blueprint.RuleParams{
 		Command:     `rm -f $out && VINTF_IGNORE_TARGET_FCM_VERSION=true ${assemble_vintf} -i $in -o $out`,
 		CommandDeps: []string{"${assemble_vintf}"},
@@ -262,6 +270,58 @@
 	}, "tool_path", "unwanted")
 )
 
+func (a *apexBundle) buildAconfigFiles(ctx android.ModuleContext) []apexFile {
+	var aconfigFiles android.Paths
+	for _, file := range a.filesInfo {
+		if file.module == nil {
+			continue
+		}
+		if dep, ok := android.OtherModuleProvider(ctx, file.module, android.AconfigPropagatingProviderKey); ok {
+			if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil {
+				aconfigFiles = append(aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...)
+			}
+		}
+
+		validationFlag := ctx.DeviceConfig().AconfigContainerValidation()
+		if validationFlag == "error" || validationFlag == "warning" {
+			android.VerifyAconfigBuildMode(ctx, ctx.ModuleName(), file.module, validationFlag == "error")
+		}
+	}
+	aconfigFiles = android.FirstUniquePaths(aconfigFiles)
+
+	var files []apexFile
+	if len(aconfigFiles) > 0 {
+		apexAconfigFile := android.PathForModuleOut(ctx, "aconfig_flags.pb")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        aconfig.AllDeclarationsRule,
+			Inputs:      aconfigFiles,
+			Output:      apexAconfigFile,
+			Description: "combine_aconfig_declarations",
+			Args: map[string]string{
+				"cache_files": android.JoinPathsWithPrefix(aconfigFiles, "--cache "),
+			},
+		})
+		files = append(files, newApexFile(ctx, apexAconfigFile, "aconfig_flags", "etc", etc, nil))
+
+		for _, info := range createStorageInfo {
+			outputFile := android.PathForModuleOut(ctx, info.Output_file)
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        aconfig.CreateStorageRule,
+				Inputs:      aconfigFiles,
+				Output:      outputFile,
+				Description: info.Desc,
+				Args: map[string]string{
+					"container":   ctx.ModuleName(),
+					"file_type":   info.File_type,
+					"cache_files": android.JoinPathsWithPrefix(aconfigFiles, "--cache "),
+				},
+			})
+			files = append(files, newApexFile(ctx, outputFile, info.File_type, "etc", etc, nil))
+		}
+	}
+	return files
+}
+
 // buildManifest creates buile rules to modify the input apex_manifest.json to add information
 // gathered by the build system such as provided/required native libraries. Two output files having
 // different formats are generated. a.manifestJsonOut is JSON format for Q devices, and
@@ -595,7 +655,7 @@
 	if len(installMapSet) > 0 {
 		var installs []string
 		installs = append(installs, android.SortedKeys(installMapSet)...)
-		a.SetLicenseInstallMap(installs)
+		ctx.SetLicenseInstallMap(installs)
 	}
 
 	////////////////////////////////////////////////////////////////////////////////////////////
@@ -644,48 +704,10 @@
 	outHostBinDir := ctx.Config().HostToolPath(ctx, "").String()
 	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
 
-	defaultReadOnlyFiles := []string{"apex_manifest.json", "apex_manifest.pb"}
-	aconfigDest := imageDir.Join(ctx, "etc").String()
-	if len(a.aconfigFiles) > 0 {
-		apexAconfigFile := android.PathForModuleOut(ctx, "aconfig_flags.pb")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        aconfig.AllDeclarationsRule,
-			Inputs:      a.aconfigFiles,
-			Output:      apexAconfigFile,
-			Description: "combine_aconfig_declarations",
-			Args: map[string]string{
-				"cache_files": android.JoinPathsWithPrefix(a.aconfigFiles, "--cache "),
-			},
-		})
-
-		copyCommands = append(copyCommands, "cp -f "+apexAconfigFile.String()+" "+aconfigDest)
-		implicitInputs = append(implicitInputs, apexAconfigFile)
-		defaultReadOnlyFiles = append(defaultReadOnlyFiles, "etc/"+apexAconfigFile.Base())
-
-		for _, info := range createStorageInfo {
-			outputFile := android.PathForModuleOut(ctx, info.Output_file)
-			ctx.Build(pctx, android.BuildParams{
-				Rule:        aconfig.CreateStorageRule,
-				Inputs:      a.aconfigFiles,
-				Output:      outputFile,
-				Description: info.Desc,
-				Args: map[string]string{
-					"container":   ctx.ModuleName(),
-					"file_type":   info.File_type,
-					"cache_files": android.JoinPathsWithPrefix(a.aconfigFiles, "--cache "),
-				},
-			})
-
-			copyCommands = append(copyCommands, "cp -f "+outputFile.String()+" "+aconfigDest)
-			implicitInputs = append(implicitInputs, outputFile)
-			defaultReadOnlyFiles = append(defaultReadOnlyFiles, "etc/"+outputFile.Base())
-		}
-	}
-
 	////////////////////////////////////////////////////////////////////////////////////
 	// Step 2: create canned_fs_config which encodes filemode,uid,gid of each files
 	// in this APEX. The file will be used by apexer in later steps.
-	cannedFsConfig := a.buildCannedFsConfig(ctx, defaultReadOnlyFiles)
+	cannedFsConfig := a.buildCannedFsConfig(ctx)
 	implicitInputs = append(implicitInputs, cannedFsConfig)
 
 	////////////////////////////////////////////////////////////////////////////////////
@@ -704,8 +726,9 @@
 		optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName)
 	}
 
-	if a.properties.AndroidManifest != nil {
-		androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest))
+	androidManifest := a.properties.AndroidManifest.GetOrDefault(ctx, "")
+	if androidManifest != "" {
+		androidManifestFile := android.PathForModuleSrc(ctx, androidManifest)
 
 		if a.testApex {
 			androidManifestFile = markManifestTestOnly(ctx, androidManifestFile)
@@ -766,18 +789,6 @@
 	implicitInputs = append(implicitInputs, noticeAssetPath)
 	optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String()))
 
-	// Apexes which are supposed to be installed in builtin dirs(/system, etc)
-	// don't need hashtree for activation. Therefore, by removing hashtree from
-	// apex bundle (filesystem image in it, to be specific), we can save storage.
-	needHashTree := moduleMinSdkVersion.LessThanOrEqualTo(android.SdkVersion_Android10) ||
-		a.shouldGenerateHashtree()
-	if ctx.Config().ApexCompressionEnabled() && a.isCompressable() {
-		needHashTree = true
-	}
-	if !needHashTree {
-		optFlags = append(optFlags, "--no_hashtree")
-	}
-
 	if a.testOnlyShouldSkipPayloadSign() {
 		optFlags = append(optFlags, "--unsigned_payload")
 	}
@@ -951,6 +962,9 @@
 		validations = append(validations,
 			runApexElfCheckerUnwanted(ctx, unsignedOutputFile.OutputPath, a.properties.Unwanted_transitive_deps))
 	}
+	if !a.testApex && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
+		validations = append(validations, runApexHostVerifier(ctx, unsignedOutputFile.OutputPath))
+	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
 		Description: "signapk",
@@ -1138,8 +1152,8 @@
 	a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build())
 }
 
-func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext, defaultReadOnlyFiles []string) android.OutputPath {
-	var readOnlyPaths = defaultReadOnlyFiles
+func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath {
+	var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
 	var executablePaths []string // this also includes dirs
 	var appSetDirs []string
 	appSetFiles := make(map[string]android.Path)
@@ -1195,8 +1209,9 @@
 	}
 	// Custom fs_config is "appended" to the last so that entries from the file are preferred
 	// over default ones set above.
-	if a.properties.Canned_fs_config != nil {
-		cmd.Text("cat").Input(android.PathForModuleSrc(ctx, *a.properties.Canned_fs_config))
+	customFsConfig := a.properties.Canned_fs_config.GetOrDefault(ctx, "")
+	if customFsConfig != "" {
+		cmd.Text("cat").Input(android.PathForModuleSrc(ctx, customFsConfig))
 	}
 	cmd.Text(")").FlagWithOutput("> ", cannedFsConfig)
 	builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName()))
@@ -1244,3 +1259,13 @@
 	})
 	return timestamp
 }
+
+func runApexHostVerifier(ctx android.ModuleContext, apexFile android.OutputPath) android.Path {
+	timestamp := android.PathForModuleOut(ctx, "host_apex_verifier.timestamp")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   apexHostVerifierRule,
+		Input:  apexFile,
+		Output: timestamp,
+	})
+	return timestamp
+}
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index b9a9198..9e1ac94 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -92,6 +92,7 @@
 			],
 			srcs: ["b.java"],
 			installable: true,
+			sdk_version: "core_current",
 		}
 
 		java_library {
diff --git a/apex/container_test.go b/apex/container_test.go
new file mode 100644
index 0000000..d28b1a6
--- /dev/null
+++ b/apex/container_test.go
@@ -0,0 +1,337 @@
+// 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 apex
+
+import (
+	"android/soong/android"
+	"android/soong/java"
+	"fmt"
+	"testing"
+)
+
+var checkContainerMatch = func(t *testing.T, name string, container string, expected bool, actual bool) {
+	errorMessage := fmt.Sprintf("module %s container %s value differ", name, container)
+	android.AssertBoolEquals(t, errorMessage, expected, actual)
+}
+
+func TestApexDepsContainers(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("mybootclasspathlib", "bar"),
+	).RunTestWithBp(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			bootclasspath_fragments: [
+				"mybootclasspathfragment",
+			],
+			updatable: true,
+			min_sdk_version: "30",
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		bootclasspath_fragment {
+			name: "mybootclasspathfragment",
+			contents: [
+				"mybootclasspathlib",
+			],
+			apex_available: [
+				"myapex",
+			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+		java_sdk_library {
+			name: "mybootclasspathlib",
+			srcs: [
+				"mybootclasspathlib.java",
+			],
+			apex_available: [
+				"myapex",
+			],
+			compile_dex: true,
+			static_libs: [
+				"food",
+				"baz",
+			],
+			libs: [
+				"bar.stubs",
+			],
+			min_sdk_version: "30",
+			sdk_version: "current",
+		}
+		java_library {
+			name: "food",
+			srcs:[
+				"A.java",
+			],
+			apex_available: [
+				"myapex",
+			],
+			min_sdk_version: "30",
+			sdk_version: "core_current",
+		}
+		java_sdk_library {
+			name: "bar",
+			srcs:[
+				"A.java",
+			],
+			min_sdk_version: "30",
+			sdk_version: "core_current",
+		}
+		java_library {
+			name: "baz",
+			srcs:[
+				"A.java",
+			],
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+			min_sdk_version: "30",
+			sdk_version: "core_current",
+		}
+	`)
+	testcases := []struct {
+		moduleName        string
+		variant           string
+		isSystemContainer bool
+		isApexContainer   bool
+	}{
+		{
+			moduleName:        "mybootclasspathlib",
+			variant:           "android_common_myapex",
+			isSystemContainer: true,
+			isApexContainer:   true,
+		},
+		{
+			moduleName:        "mybootclasspathlib.impl",
+			variant:           "android_common_apex30",
+			isSystemContainer: true,
+			isApexContainer:   true,
+		},
+		{
+			moduleName:        "mybootclasspathlib.stubs",
+			variant:           "android_common",
+			isSystemContainer: true,
+			isApexContainer:   false,
+		},
+		{
+			moduleName:        "food",
+			variant:           "android_common_apex30",
+			isSystemContainer: true,
+			isApexContainer:   true,
+		},
+		{
+			moduleName:        "bar",
+			variant:           "android_common",
+			isSystemContainer: true,
+			isApexContainer:   false,
+		},
+		{
+			moduleName:        "baz",
+			variant:           "android_common_apex30",
+			isSystemContainer: true,
+			isApexContainer:   true,
+		},
+	}
+
+	for _, c := range testcases {
+		m := result.ModuleForTests(c.moduleName, c.variant)
+		containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider)
+		belongingContainers := containers.BelongingContainers()
+		checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers))
+		checkContainerMatch(t, c.moduleName, "apex", c.isApexContainer, android.InList(android.ApexContainer, belongingContainers))
+	}
+}
+
+func TestNonUpdatableApexDepsContainers(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("mybootclasspathlib", "bar"),
+	).RunTestWithBp(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			bootclasspath_fragments: [
+				"mybootclasspathfragment",
+			],
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		bootclasspath_fragment {
+			name: "mybootclasspathfragment",
+			contents: [
+				"mybootclasspathlib",
+			],
+			apex_available: [
+				"myapex",
+			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+		java_sdk_library {
+			name: "mybootclasspathlib",
+			srcs: [
+				"mybootclasspathlib.java",
+			],
+			apex_available: [
+				"myapex",
+			],
+			compile_dex: true,
+			static_libs: [
+				"food",
+			],
+			libs: [
+				"bar.stubs",
+			],
+			sdk_version: "current",
+		}
+		java_library {
+			name: "food",
+			srcs:[
+				"A.java",
+			],
+			apex_available: [
+				"myapex",
+			],
+			sdk_version: "core_current",
+		}
+		java_sdk_library {
+			name: "bar",
+			srcs:[
+				"A.java",
+			],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+	`)
+	testcases := []struct {
+		moduleName        string
+		variant           string
+		isSystemContainer bool
+		isApexContainer   bool
+	}{
+		{
+			moduleName:        "mybootclasspathlib",
+			variant:           "android_common_myapex",
+			isSystemContainer: true,
+			isApexContainer:   true,
+		},
+		{
+			moduleName:        "mybootclasspathlib.impl",
+			variant:           "android_common_apex10000",
+			isSystemContainer: true,
+			isApexContainer:   true,
+		},
+		{
+			moduleName:        "mybootclasspathlib.stubs",
+			variant:           "android_common",
+			isSystemContainer: true,
+			isApexContainer:   false,
+		},
+		{
+			moduleName:        "food",
+			variant:           "android_common_apex10000",
+			isSystemContainer: true,
+			isApexContainer:   true,
+		},
+		{
+			moduleName:        "bar",
+			variant:           "android_common",
+			isSystemContainer: true,
+			isApexContainer:   false,
+		},
+	}
+
+	for _, c := range testcases {
+		m := result.ModuleForTests(c.moduleName, c.variant)
+		containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider)
+		belongingContainers := containers.BelongingContainers()
+		checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers))
+		checkContainerMatch(t, c.moduleName, "apex", c.isApexContainer, android.InList(android.ApexContainer, belongingContainers))
+	}
+}
+
+func TestUpdatableAndNonUpdatableApexesIdenticalMinSdkVersion(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/myapex_non_updatable-file_contexts": nil,
+			"system/sepolicy/apex/myapex_updatable-file_contexts":     nil,
+		}),
+	).RunTestWithBp(t, `
+		apex {
+			name: "myapex_non_updatable",
+			key: "myapex_non_updatable.key",
+			java_libs: [
+				"foo",
+			],
+			updatable: false,
+			min_sdk_version: "30",
+		}
+		apex_key {
+			name: "myapex_non_updatable.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		apex {
+			name: "myapex_updatable",
+			key: "myapex_updatable.key",
+			java_libs: [
+				"foo",
+			],
+			updatable: true,
+			min_sdk_version: "30",
+		}
+		apex_key {
+			name: "myapex_updatable.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs:[
+				"A.java",
+			],
+			apex_available: [
+				"myapex_non_updatable",
+				"myapex_updatable",
+			],
+			min_sdk_version: "30",
+			sdk_version: "current",
+		}
+	`)
+
+	fooApexVariant := result.ModuleForTests("foo", "android_common_apex30")
+	containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), fooApexVariant.Module(), android.ContainersInfoProvider)
+	belongingContainers := containers.BelongingContainers()
+	checkContainerMatch(t, "foo", "system", true, android.InList(android.SystemContainer, belongingContainers))
+	checkContainerMatch(t, "foo", "apex", true, android.InList(android.ApexContainer, belongingContainers))
+}
diff --git a/apex/deapexer.go b/apex/deapexer.go
index a673108..3b8233d 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -15,37 +15,9 @@
 package apex
 
 import (
-	"strings"
-
 	"android/soong/android"
 )
 
-// Contains 'deapexer' a private module type used by 'prebuilt_apex' to make dex files contained
-// within a .apex file referenced by `prebuilt_apex` available for use by their associated
-// `java_import` modules.
-//
-// An 'apex' module references `java_library` modules from which .dex files are obtained that are
-// stored in the resulting `.apex` file. The resulting `.apex` file is then made available as a
-// prebuilt by referencing it from a `prebuilt_apex`. For each such `java_library` that is used by
-// modules outside the `.apex` file a `java_import` prebuilt is made available referencing a jar
-// that contains the Java classes.
-//
-// When building a Java module type, e.g. `java_module` or `android_app` against such prebuilts the
-// `java_import` provides the classes jar  (jar containing `.class` files) against which the
-// module's `.java` files are compiled. That classes jar usually contains only stub classes. The
-// resulting classes jar is converted into a dex jar (jar containing `.dex` files). Then if
-// necessary the dex jar is further processed by `dexpreopt` to produce an optimized form of the
-// library specific to the current Android version. This process requires access to implementation
-// dex jars for each `java_import`. The `java_import` will obtain the implementation dex jar from
-// the `.apex` file in the associated `prebuilt_apex`.
-//
-// This is intentionally not registered by name as it is not intended to be used from within an
-// `Android.bp` file.
-
-// DeapexerProperties specifies the properties supported by the deapexer module.
-//
-// As these are never intended to be supplied in a .bp file they use a different naming convention
-// to make it clear that they are different.
 type DeapexerProperties struct {
 	// List of common modules that may need access to files exported by this module.
 	//
@@ -72,46 +44,9 @@
 	Selected_apex *string `android:"path" blueprint:"mutated"`
 }
 
-type Deapexer struct {
-	android.ModuleBase
-
-	properties             DeapexerProperties
-	selectedApexProperties SelectedApexProperties
-
-	inputApex android.Path
-}
-
-// Returns the name of the deapexer module corresponding to an APEX module with the given name.
-func deapexerModuleName(apexModuleName string) string {
-	return apexModuleName + ".deapexer"
-}
-
-// Returns the name of the APEX module corresponding to an deapexer module with
-// the given name. This reverses deapexerModuleName.
-func apexModuleName(deapexerModuleName string) string {
-	return strings.TrimSuffix(deapexerModuleName, ".deapexer")
-}
-
-func privateDeapexerFactory() android.Module {
-	module := &Deapexer{}
-	module.AddProperties(&module.properties, &module.selectedApexProperties)
-	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-	return module
-}
-
-func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// Add dependencies from the java modules to which this exports files from the `.apex` file onto
-	// this module so that they can access the `DeapexerInfo` object that this provides.
-	// TODO: b/308174306 - Once all the mainline modules have been flagged, drop this dependency edge
-	for _, lib := range p.properties.CommonModules {
-		dep := prebuiltApexExportedModuleName(ctx, lib)
-		ctx.AddReverseDependency(ctx.Module(), android.DeapexerTag, dep)
-	}
-}
-
-func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	p.inputApex = android.OptionalPathForModuleSrc(ctx, p.selectedApexProperties.Selected_apex).Path()
-
+// deapex creates the build rules to deapex a prebuilt .apex file
+// it returns a pointer to a DeapexerInfo object
+func deapex(ctx android.ModuleContext, apexFile android.Path, deapexerProps DeapexerProperties) *android.DeapexerInfo {
 	// Create and remember the directory into which the .apex file's contents will be unpacked.
 	deapexerOutput := android.PathForModuleOut(ctx, "deapexer")
 
@@ -119,7 +54,7 @@
 
 	// Create mappings from apex relative path to the extracted file's path.
 	exportedPaths := make(android.Paths, 0, len(exports))
-	for _, path := range p.properties.ExportedFiles {
+	for _, path := range deapexerProps.ExportedFiles {
 		// Populate the exports that this makes available.
 		extractedPath := deapexerOutput.Join(ctx, path)
 		exports[path] = extractedPath
@@ -131,9 +66,8 @@
 	// apex relative path to extracted file path available for other modules.
 	if len(exports) > 0 {
 		// Make the information available for other modules.
-		di := android.NewDeapexerInfo(apexModuleName(ctx.ModuleName()), exports, p.properties.CommonModules)
-		di.AddDexpreoptProfileGuidedExportedModuleNames(p.properties.DexpreoptProfileGuidedModules...)
-		android.SetProvider(ctx, android.DeapexerProvider, di)
+		di := android.NewDeapexerInfo(ctx.ModuleName(), exports, deapexerProps.CommonModules)
+		di.AddDexpreoptProfileGuidedExportedModuleNames(deapexerProps.DexpreoptProfileGuidedModules...)
 
 		// Create a sorted list of the files that this exports.
 		exportedPaths = android.SortedUniquePaths(exportedPaths)
@@ -147,11 +81,13 @@
 			BuiltTool("deapexer").
 			BuiltTool("debugfs").
 			BuiltTool("fsck.erofs").
-			Input(p.inputApex).
+			Input(apexFile).
 			Text(deapexerOutput.String())
 		for _, p := range exportedPaths {
 			command.Output(p.(android.WritablePath))
 		}
-		builder.Build("deapexer", "deapex "+apexModuleName(ctx.ModuleName()))
+		builder.Build("deapexer", "deapex "+ctx.ModuleName())
+		return &di
 	}
+	return nil
 }
diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go
index 7a17f50..4feade8 100644
--- a/apex/dexpreopt_bootjars_test.go
+++ b/apex/dexpreopt_bootjars_test.go
@@ -127,16 +127,29 @@
 			src: "com.android.art-arm.apex",
 			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
 		}
+
+		apex_contributions {
+			name: "prebuilt_art_contributions",
+			contents: ["prebuilt_com.android.art"],
+			api_domain: "com.android.art",
+		}
 	`
 
-	result := android.GroupFixturePreparers(
+	fixture := android.GroupFixturePreparers(
 		java.PrepareForTestWithDexpreopt,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo"),
 		java.FixtureConfigureBootJars("com.android.art:core-oj", "platform:foo", "system_ext:bar", "platform:baz"),
 		PrepareForTestWithApexBuildComponents,
 		prepareForTestWithArtApex,
-	).RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt))
+	)
+	if preferPrebuilt {
+		fixture = android.GroupFixturePreparers(
+			fixture,
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
+		)
+	}
+	result := fixture.RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt))
 
 	dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")
 	rule := dexBootJars.Output(ruleFile)
@@ -200,7 +213,7 @@
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar",
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar",
 		"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
-		"out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
+		"out/soong/.intermediates/prebuilt_com.android.art/android_common_com.android.art/deapexer/etc/boot-image.prof",
 		"out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof",
 		"out/soong/dexpreopt/uffd_gc_flag.txt",
 	}
@@ -384,12 +397,12 @@
 		{
 			desc:                         "Prebuilt apex prebuilt_com.android.art is selected, profile should come from .prof deapexed from the prebuilt",
 			selectedArtApexContributions: "art.prebuilt.contributions",
-			expectedProfile:              "out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
+			expectedProfile:              "out/soong/.intermediates/prebuilt_com.android.art/android_common_com.android.art/deapexer/etc/boot-image.prof",
 		},
 		{
 			desc:                         "Prebuilt apex prebuilt_com.android.art.v2 is selected, profile should come from .prof deapexed from the prebuilt",
 			selectedArtApexContributions: "art.prebuilt.v2.contributions",
-			expectedProfile:              "out/soong/.intermediates/prebuilt_com.android.art.v2.deapexer/android_common/deapexer/etc/boot-image.prof",
+			expectedProfile:              "out/soong/.intermediates/com.android.art.v2/android_common_com.android.art/deapexer/etc/boot-image.prof",
 		},
 	}
 	for _, tc := range testCases {
@@ -399,11 +412,7 @@
 			java.FixtureConfigureBootJars("com.android.art:core-oj"),
 			PrepareForTestWithApexBuildComponents,
 			prepareForTestWithArtApex,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ART": tc.selectedArtApexContributions,
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", tc.selectedArtApexContributions),
 		).RunTestWithBp(t, bp)
 
 		dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")
@@ -413,3 +422,106 @@
 		android.AssertStringListContains(t, tc.desc, inputs, tc.expectedProfile)
 	}
 }
+
+// Check that dexpreopt works with Google mainline prebuilts even in workspaces where source is missing
+func TestDexpreoptWithMainlinePrebuiltNoSource(t *testing.T) {
+	bp := `
+		// Platform.
+
+		platform_bootclasspath {
+			name: "platform-bootclasspath",
+			fragments: [
+				{
+					apex: "com.android.art",
+					module: "art-bootclasspath-fragment",
+				},
+			],
+		}
+
+		// Source AOSP ART apex
+		java_library {
+			name: "core-oj",
+			srcs: ["core-oj.java"],
+			installable: true,
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			image_name: "art",
+			contents: ["core-oj"],
+			apex_available: [
+				"com.android.art",
+			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		apex_key {
+			name: "com.android.art.key",
+			public_key: "com.android.art.avbpubkey",
+			private_key: "com.android.art.pem",
+		}
+
+		apex {
+			name: "com.android.art",
+			key: "com.android.art.key",
+			bootclasspath_fragments: ["art-bootclasspath-fragment"],
+			updatable: false,
+		}
+
+
+		// Prebuilt Google ART APEX.
+
+		java_import {
+			name: "core-oj",
+			jars: ["core-oj.jar"],
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			image_name: "art",
+			contents: ["core-oj"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		prebuilt_apex {
+			name: "com.google.android.art",
+			apex_name: "com.android.art",
+			src: "com.android.art-arm.apex",
+			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
+		}
+
+		apex_contributions {
+			name: "art.prebuilt.contributions",
+			api_domain: "com.android.art",
+			contents: ["prebuilt_com.google.android.art"],
+		}
+	`
+	res := android.GroupFixturePreparers(
+		java.PrepareForTestWithDexpreopt,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureConfigureBootJars("com.android.art:core-oj"),
+		PrepareForTestWithApexBuildComponents,
+		prepareForTestWithArtApex,
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "art.prebuilt.contributions"),
+	).RunTestWithBp(t, bp)
+	if !java.CheckModuleHasDependency(t, res.TestContext, "dex_bootjars", "android_common", "prebuilt_com.google.android.art") {
+		t.Errorf("Expected dexpreopt to use prebuilt apex")
+	}
+}
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 4a20cf0..9c2d899 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -154,7 +154,7 @@
 	).RunTest(t)
 
 	pbcp := result.Module("platform-bootclasspath", "android_common")
-	info, _ := android.SingletonModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
+	info, _ := android.OtherModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
 
 	for _, category := range java.HiddenAPIFlagFileCategories {
 		name := category.PropertyName()
@@ -236,7 +236,7 @@
 	)
 
 	pbcp := result.Module("myplatform-bootclasspath", "android_common")
-	info, _ := android.SingletonModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
+	info, _ := android.OtherModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
 
 	android.AssertArrayString(t, "stub flags", []string{"prebuilt-stub-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
 	android.AssertArrayString(t, "all flags", []string{"prebuilt-all-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
@@ -254,11 +254,7 @@
 		java.FixtureWithLastReleaseApis("foo"),
 		java.PrepareForTestWithDexpreopt,
 		dexpreopt.FixtureDisableDexpreoptBootImages(false),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "com.android.art",
@@ -297,6 +293,7 @@
 			],
 			srcs: ["b.java"],
 			installable: true,
+			sdk_version: "core_current",
 		}
 
 		// Add a java_import that is not preferred and so won't have an appropriate apex variant created
@@ -429,10 +426,9 @@
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
 		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
+
 		java.FixtureWithPrebuiltApis(map[string][]string{
 			"current": {},
 			"30":      {"foo"},
@@ -796,6 +792,128 @@
 		`)
 }
 
+// Skip bcp_fragment content validation of source apexes if prebuilts are active.
+func TestNonBootJarInPrebuilts(t *testing.T) {
+	testCases := []struct {
+		description               string
+		selectedApexContributions string
+		expectedError             string
+	}{
+		{
+			description:               "source is active",
+			selectedApexContributions: "",
+			expectedError:             "in contents must also be declared in PRODUCT_APEX_BOOT_JARS",
+		},
+		{
+			description:               "prebuilts are active",
+			selectedApexContributions: "myapex.prebuilt.contributions",
+			expectedError:             "", // skip content validation of source bcp fragment
+		},
+	}
+	bp := `
+// Source
+apex {
+	name: "myapex",
+	key: "myapex.key",
+	bootclasspath_fragments: ["apex-fragment"],
+	updatable: false,
+	min_sdk_version: "29",
+}
+
+override_apex {
+	name: "myapex.override", // overrides the min_sdk_version, thereby creating different variants of its transitive deps
+	base: "myapex",
+	min_sdk_version: "34",
+}
+
+apex_key {
+	name: "myapex.key",
+	public_key: "testkey.avbpubkey",
+	private_key: "testkey.pem",
+}
+
+java_library {
+	name: "foo",
+	srcs: ["b.java"],
+	installable: true,
+	apex_available: ["myapex"],
+	permitted_packages: ["foo"],
+	min_sdk_version: "29",
+}
+
+java_library {
+	name: "bar",
+	srcs: ["b.java"],
+	installable: true,
+	apex_available: ["myapex"],
+	permitted_packages: ["bar"],
+	min_sdk_version: "29",
+}
+
+bootclasspath_fragment {
+	name: "apex-fragment",
+	contents: ["foo", "bar"],
+	apex_available:[ "myapex" ],
+	hidden_api: {
+		split_packages: ["*"],
+	},
+}
+
+platform_bootclasspath {
+	name: "myplatform-bootclasspath",
+	fragments: [{
+			apex: "myapex",
+			module:"apex-fragment",
+	}],
+}
+
+// prebuilts
+prebuilt_apex {
+	name: "myapex",
+		apex_name: "myapex",
+		src: "myapex.apex",
+		exported_bootclasspath_fragments: ["apex-fragment"],
+	}
+
+	prebuilt_bootclasspath_fragment {
+		name: "apex-fragment",
+		contents: ["foo"],
+		hidden_api: {
+			annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+			metadata: "my-bootclasspath-fragment/metadata.csv",
+			index: "my-bootclasspath-fragment/index.csv",
+			stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+			all_flags: "my-bootclasspath-fragment/all-flags.csv",
+		},
+	}
+	java_import {
+		name: "foo",
+		jars: ["foo.jar"],
+	}
+
+apex_contributions {
+	name: "myapex.prebuilt.contributions",
+	api_domain: "myapex",
+	contents: ["prebuilt_myapex"],
+}
+`
+
+	for _, tc := range testCases {
+		fixture := android.GroupFixturePreparers(
+			prepareForTestWithPlatformBootclasspath,
+			PrepareForTestWithApexBuildComponents,
+			prepareForTestWithMyapex,
+			java.FixtureConfigureApexBootJars("myapex:foo"),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions),
+		)
+		if tc.expectedError != "" {
+			fixture = fixture.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedError))
+		}
+		fixture.RunTestWithBp(t, bp)
+	}
+
+}
+
 // Source and prebuilt apex provide different set of boot jars
 func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) {
 	bp := `
@@ -935,11 +1053,7 @@
 			PrepareForTestWithApexBuildComponents,
 			prepareForTestWithMyapex,
 			java.FixtureConfigureApexBootJars(tc.configuredBootJars...),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ART": "my_apex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "my_apex_contributions"),
 		)
 		if tc.errorExpected {
 			fixture = fixture.ExtendWithErrorHandler(
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 97bf617..9cd5688 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -15,9 +15,6 @@
 package apex
 
 import (
-	"fmt"
-	"io"
-	"path/filepath"
 	"strconv"
 	"strings"
 
@@ -42,6 +39,11 @@
 			CommandDeps: []string{"${extract_apks}"},
 		},
 		"abis", "allow-prereleased", "sdk-version", "skip-sdk-check")
+	decompressApex = pctx.StaticRule("decompressApex", blueprint.RuleParams{
+		Command:     `rm -rf $out && ${deapexer} decompress --copy-if-uncompressed --input ${in} --output ${out}`,
+		CommandDeps: []string{"${deapexer}"},
+		Description: "decompress $out",
+	})
 )
 
 type prebuilt interface {
@@ -65,10 +67,6 @@
 	// fragment for this apex for apexkeys.txt
 	apexKeysPath android.WritablePath
 
-	// A list of apexFile objects created in prebuiltCommon.initApexFilesForAndroidMk which are used
-	// to create make modules in prebuiltCommon.AndroidMkEntries.
-	apexFilesForAndroidMk []apexFile
-
 	// Installed locations of symlinks for backward compatibility.
 	compatSymlinks android.InstallPaths
 
@@ -109,11 +107,6 @@
 	// from PRODUCT_PACKAGES.
 	Overrides []string
 
-	// List of java libraries that are embedded inside this prebuilt APEX bundle and for which this
-	// APEX bundle will create an APEX variant and provide dex implementation jars for use by
-	// dexpreopt and boot jars package check.
-	Exported_java_libs []string
-
 	// List of bootclasspath fragments inside this prebuilt APEX bundle and for which this APEX
 	// bundle will create an APEX variant.
 	Exported_bootclasspath_fragments []string
@@ -197,14 +190,12 @@
 	// If this apex contains a system server jar, then the dexpreopt artifacts should be added as required
 	for _, install := range p.Dexpreopter.DexpreoptBuiltInstalledForApex() {
 		p.requiredModuleNames = append(p.requiredModuleNames, install.FullModuleName())
-		install.PackageFile(ctx)
 	}
 }
 
 // If this prebuilt has system server jar, create the rules to dexpreopt it and install it alongside the prebuilt apex
-func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext) {
-	// If this apex does not export anything, return
-	if !p.hasExportedDeps() {
+func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext, di *android.DeapexerInfo) {
+	if di == nil {
 		return
 	}
 	// If this prebuilt apex has not been selected, return
@@ -213,10 +204,7 @@
 	}
 	// Use apex_name to determine the api domain of this prebuilt apex
 	apexName := p.ApexVariationName()
-	di, err := android.FindDeapexerProviderForModule(ctx)
-	if err != nil {
-		ctx.ModuleErrorf(err.Error())
-	}
+	// TODO: do not compute twice
 	dc := dexpreopt.GetGlobalConfig(ctx)
 	systemServerJarList := dc.AllApexSystemServerJars(ctx)
 
@@ -231,11 +219,6 @@
 }
 
 func (p *prebuiltCommon) addRequiredModules(entries *android.AndroidMkEntries) {
-	for _, fi := range p.apexFilesForAndroidMk {
-		entries.AddStrings("LOCAL_REQUIRED_MODULES", fi.requiredModuleNames...)
-		entries.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", fi.targetRequiredModuleNames...)
-		entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", fi.hostRequiredModuleNames...)
-	}
 	entries.AddStrings("LOCAL_REQUIRED_MODULES", p.requiredModuleNames...)
 }
 
@@ -267,77 +250,11 @@
 		entriesList = append(entriesList, install.ToMakeEntries())
 	}
 
-	// Iterate over the apexFilesForAndroidMk list and create an AndroidMkEntries struct for each
-	// file. This provides similar behavior to that provided in apexBundle.AndroidMk() as it makes the
-	// apex specific variants of the exported java modules available for use from within make.
-	apexName := p.BaseModuleName()
-	for _, fi := range p.apexFilesForAndroidMk {
-		entries := p.createEntriesForApexFile(fi, apexName)
-		entriesList = append(entriesList, entries)
-	}
-
 	return entriesList
 }
 
-// createEntriesForApexFile creates an AndroidMkEntries for the supplied apexFile
-func (p *prebuiltCommon) createEntriesForApexFile(fi apexFile, apexName string) android.AndroidMkEntries {
-	moduleName := fi.androidMkModuleName + "." + apexName
-	entries := android.AndroidMkEntries{
-		Class:        fi.class.nameInMake(),
-		OverrideName: moduleName,
-		OutputFile:   android.OptionalPathForPath(fi.builtFile),
-		Include:      "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", p.installDir.String())
-				entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", filepath.Join(p.installDir.String(), fi.stem()))
-				entries.SetString("LOCAL_SOONG_INSTALL_PAIRS",
-					fi.builtFile.String()+":"+filepath.Join(p.installDir.String(), fi.stem()))
-
-				// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar  Therefore
-				// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
-				// we will have foo.jar.jar
-				entries.SetString("LOCAL_MODULE_STEM", strings.TrimSuffix(fi.stem(), ".jar"))
-				entries.SetString("LOCAL_SOONG_DEX_JAR", fi.builtFile.String())
-				entries.SetString("LOCAL_DEX_PREOPT", "false")
-			},
-		},
-		ExtraFooters: []android.AndroidMkExtraFootersFunc{
-			func(w io.Writer, name, prefix, moduleDir string) {
-				// m <module_name> will build <module_name>.<apex_name> as well.
-				if fi.androidMkModuleName != moduleName {
-					fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName)
-					fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
-				}
-			},
-		},
-	}
-	return entries
-}
-
-// prebuiltApexModuleCreator defines the methods that need to be implemented by prebuilt_apex and
-// apex_set in order to create the modules needed to provide access to the prebuilt .apex file.
-type prebuiltApexModuleCreator interface {
-	createPrebuiltApexModules(ctx android.TopDownMutatorContext)
-}
-
-// prebuiltApexModuleCreatorMutator is the mutator responsible for invoking the
-// prebuiltApexModuleCreator's createPrebuiltApexModules method.
-//
-// It is registered as a pre-arch mutator as it must run after the ComponentDepsMutator because it
-// will need to access dependencies added by that (exported modules) but must run before the
-// DepsMutator so that the deapexer module it creates can add dependencies onto itself from the
-// exported modules.
-func prebuiltApexModuleCreatorMutator(ctx android.TopDownMutatorContext) {
-	module := ctx.Module()
-	if creator, ok := module.(prebuiltApexModuleCreator); ok {
-		creator.createPrebuiltApexModules(ctx)
-	}
-}
-
 func (p *prebuiltCommon) hasExportedDeps() bool {
-	return len(p.prebuiltCommonProperties.Exported_java_libs) > 0 ||
-		len(p.prebuiltCommonProperties.Exported_bootclasspath_fragments) > 0 ||
+	return len(p.prebuiltCommonProperties.Exported_bootclasspath_fragments) > 0 ||
 		len(p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments) > 0
 }
 
@@ -345,11 +262,6 @@
 func (p *prebuiltCommon) prebuiltApexContentsDeps(ctx android.BottomUpMutatorContext) {
 	module := ctx.Module()
 
-	for _, dep := range p.prebuiltCommonProperties.Exported_java_libs {
-		prebuiltDep := android.PrebuiltNameFromSource(dep)
-		ctx.AddDependency(module, exportedJavaLibTag, prebuiltDep)
-	}
-
 	for _, dep := range p.prebuiltCommonProperties.Exported_bootclasspath_fragments {
 		prebuiltDep := android.PrebuiltNameFromSource(dep)
 		ctx.AddDependency(module, exportedBootclasspathFragmentTag, prebuiltDep)
@@ -467,34 +379,6 @@
 	}
 }
 
-// prebuiltApexSelectorModule is a private module type that is only created by the prebuilt_apex
-// module. It selects the apex to use and makes it available for use by prebuilt_apex and the
-// deapexer.
-type prebuiltApexSelectorModule struct {
-	android.ModuleBase
-
-	apexFileProperties ApexFileProperties
-
-	inputApex android.Path
-}
-
-func privateApexSelectorModuleFactory() android.Module {
-	module := &prebuiltApexSelectorModule{}
-	module.AddProperties(
-		&module.apexFileProperties,
-	)
-	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-	return module
-}
-
-func (p *prebuiltApexSelectorModule) Srcs() android.Paths {
-	return android.Paths{p.inputApex}
-}
-
-func (p *prebuiltApexSelectorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	p.inputApex = android.SingleSourcePathFromSupplier(ctx, p.apexFileProperties.prebuiltApexSelector, "src")
-}
-
 type Prebuilt struct {
 	prebuiltCommon
 
@@ -511,7 +395,7 @@
 	// This cannot be marked as `android:"arch_variant"` because the `prebuilt_apex` is only mutated
 	// for android_common. That is so that it will have the same arch variant as, and so be compatible
 	// with, the source `apex` module type that it replaces.
-	Src  *string `android:"path"`
+	Src  proptools.Configurable[string] `android:"path,replace_instead_of_append"`
 	Arch struct {
 		Arm struct {
 			Src *string `android:"path"`
@@ -537,11 +421,11 @@
 // to use methods on it that are specific to the current module.
 //
 // See the ApexFileProperties.Src property.
-func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) []string {
+func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) string {
 	multiTargets := prebuilt.MultiTargets()
 	if len(multiTargets) != 1 {
 		ctx.OtherModuleErrorf(prebuilt, "compile_multilib shouldn't be \"both\" for prebuilt_apex")
-		return nil
+		return ""
 	}
 	var src string
 	switch multiTargets[0].Arch.ArchType {
@@ -561,7 +445,7 @@
 		src = String(p.Arch.X86_64.Src)
 	}
 	if src == "" {
-		src = String(p.Src)
+		src = p.Src.GetOrDefault(ctx, "")
 	}
 
 	if src == "" {
@@ -574,7 +458,7 @@
 		// logic from reporting a more general, less useful message.
 	}
 
-	return []string{src}
+	return src
 }
 
 type PrebuiltProperties struct {
@@ -587,48 +471,23 @@
 	return false
 }
 
-func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{p.outputApex}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 // prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
 func PrebuiltFactory() android.Module {
 	module := &Prebuilt{}
 	module.AddProperties(&module.properties)
-	module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties)
+	module.prebuiltCommon.prebuiltCommonProperties = &module.properties.PrebuiltCommonProperties
+
+	// init the module as a prebuilt
+	// even though this module type has srcs, use `InitPrebuiltModuleWithoutSrcs`, since the existing
+	// InitPrebuiltModule* are not friendly with Sources of Configurable type.
+	// The actual src will be evaluated in GenerateAndroidBuildActions.
+	android.InitPrebuiltModuleWithoutSrcs(module)
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 
 	return module
 }
 
-func createApexSelectorModule(ctx android.TopDownMutatorContext, name string, apexFileProperties *ApexFileProperties) {
-	props := struct {
-		Name *string
-	}{
-		Name: proptools.StringPtr(name),
-	}
-
-	ctx.CreateModule(privateApexSelectorModuleFactory,
-		&props,
-		apexFileProperties,
-	)
-}
-
-// createDeapexerModuleIfNeeded will create a deapexer module if it is needed.
-//
-// A deapexer module is only needed when the prebuilt apex specifies one or more modules in either
-// the `exported_java_libs` or `exported_bootclasspath_fragments` properties as that indicates that
-// the listed modules need access to files from within the prebuilt .apex file.
-func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string) {
-	// Only create the deapexer module if it is needed.
-	if !p.hasExportedDeps() {
-		return
-	}
-
+func (p *prebuiltCommon) getDeapexerPropertiesIfNeeded(ctx android.ModuleContext) DeapexerProperties {
 	// Compute the deapexer properties from the transitive dependencies of this module.
 	commonModules := []string{}
 	dexpreoptProfileGuidedModules := []string{}
@@ -662,7 +521,7 @@
 	})
 
 	// Create properties for deapexer module.
-	deapexerProperties := &DeapexerProperties{
+	deapexerProperties := DeapexerProperties{
 		// Remove any duplicates from the common modules lists as a module may be included via a direct
 		// dependency as well as transitive ones.
 		CommonModules:                 android.SortedUniqueStrings(commonModules),
@@ -671,22 +530,7 @@
 
 	// Populate the exported files property in a fixed order.
 	deapexerProperties.ExportedFiles = android.SortedUniqueStrings(exportedFiles)
-
-	props := struct {
-		Name          *string
-		Selected_apex *string
-	}{
-		Name:          proptools.StringPtr(deapexerName),
-		Selected_apex: proptools.StringPtr(apexFileSource),
-	}
-	ctx.CreateModule(privateDeapexerFactory,
-		&props,
-		deapexerProperties,
-	)
-}
-
-func apexSelectorModuleName(baseModuleName string) string {
-	return baseModuleName + ".apex.selector"
+	return deapexerProperties
 }
 
 func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name string) string {
@@ -728,97 +572,50 @@
 var _ android.RequiresFilesFromPrebuiltApexTag = exportedDependencyTag{}
 
 var (
-	exportedJavaLibTag                       = exportedDependencyTag{name: "exported_java_libs"}
 	exportedBootclasspathFragmentTag         = exportedDependencyTag{name: "exported_bootclasspath_fragments"}
 	exportedSystemserverclasspathFragmentTag = exportedDependencyTag{name: "exported_systemserverclasspath_fragments"}
 )
 
-var _ prebuiltApexModuleCreator = (*Prebuilt)(nil)
-
-// createPrebuiltApexModules creates modules necessary to export files from the prebuilt apex to the
-// build.
-//
-// If this needs to make files from within a `.apex` file available for use by other Soong modules,
-// e.g. make dex implementation jars available for java_import modules listed in exported_java_libs,
-// it does so as follows:
-//
-//  1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
-//     makes them available for use by other modules, at both Soong and ninja levels.
-//
-//  2. It adds a dependency onto those modules and creates an apex specific variant similar to what
-//     an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
-//     dexpreopt, will work the same way from source and prebuilt.
-//
-//  3. The `deapexer` module adds a dependency from the modules that require the exported files onto
-//     itself so that they can retrieve the file paths to those files.
-//
-// It also creates a child module `selector` that is responsible for selecting the appropriate
-// input apex for both the prebuilt_apex and the deapexer. That is needed for a couple of reasons:
-//
-//  1. To dedup the selection logic so it only runs in one module.
-//
-//  2. To allow the deapexer to be wired up to a different source for the input apex, e.g. an
-//     `apex_set`.
-//
-//     prebuilt_apex
-//     /      |      \
-//     /         |         \
-//     V            V            V
-//     selector  <---  deapexer  <---  exported java lib
-func (p *Prebuilt) createPrebuiltApexModules(ctx android.TopDownMutatorContext) {
-	apexSelectorModuleName := apexSelectorModuleName(p.Name())
-	createApexSelectorModule(ctx, apexSelectorModuleName, &p.properties.ApexFileProperties)
-
-	apexFileSource := ":" + apexSelectorModuleName
-	p.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(p.Name()), apexFileSource)
-
-	// Add a source reference to retrieve the selected apex from the selector module.
-	p.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
-}
-
 func (p *Prebuilt) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
 	p.prebuiltApexContentsDeps(ctx)
 }
 
-func (p *prebuiltCommon) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if p.hasExportedDeps() {
-		// Create a dependency from the prebuilt apex (prebuilt_apex/apex_set) to the internal deapexer module
-		// The deapexer will return a provider that will be bubbled up to the rdeps of apexes (e.g. dex_bootjars)
-		ctx.AddDependency(ctx.Module(), android.DeapexerTag, deapexerModuleName(p.Name()))
-	}
-}
-
 var _ ApexInfoMutator = (*Prebuilt)(nil)
 
 func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
 	p.apexInfoMutator(mctx)
 }
 
+// creates the build rules to deapex the prebuilt, and returns a deapexerInfo
+func (p *prebuiltCommon) getDeapexerInfo(ctx android.ModuleContext, apexFile android.Path) *android.DeapexerInfo {
+	if !p.hasExportedDeps() {
+		// nothing to do
+		return nil
+	}
+	deapexerProps := p.getDeapexerPropertiesIfNeeded(ctx)
+	return deapex(ctx, apexFile, deapexerProps)
+}
+
 // Set a provider containing information about the jars and .prof provided by the apex
 // Apexes built from prebuilts retrieve this information by visiting its internal deapexer module
 // Used by dex_bootjars to generate the boot image
-func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext) {
-	if !p.hasExportedDeps() {
-		// nothing to do
+func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext, di *android.DeapexerInfo) {
+	if di == nil {
 		return
 	}
-	if di, err := android.FindDeapexerProviderForModule(ctx); err == nil {
-		javaModuleToDexPath := map[string]android.Path{}
-		for _, commonModule := range di.GetExportedModuleNames() {
-			if dex := di.PrebuiltExportPath(java.ApexRootRelativePathToJavaLib(commonModule)); dex != nil {
-				javaModuleToDexPath[commonModule] = dex
-			}
+	javaModuleToDexPath := map[string]android.Path{}
+	for _, commonModule := range di.GetExportedModuleNames() {
+		if dex := di.PrebuiltExportPath(java.ApexRootRelativePathToJavaLib(commonModule)); dex != nil {
+			javaModuleToDexPath[commonModule] = dex
 		}
-
-		exports := android.ApexExportsInfo{
-			ApexName:                      p.ApexVariationName(),
-			ProfilePathOnHost:             di.PrebuiltExportPath(java.ProfileInstallPathInApex),
-			LibraryNameToDexJarPathOnHost: javaModuleToDexPath,
-		}
-		android.SetProvider(ctx, android.ApexExportsInfoProvider, exports)
-	} else {
-		ctx.ModuleErrorf(err.Error())
 	}
+
+	exports := android.ApexExportsInfo{
+		ApexName:                      p.ApexVariationName(),
+		ProfilePathOnHost:             di.PrebuiltExportPath(java.ProfileInstallPathInApex),
+		LibraryNameToDexJarPathOnHost: javaModuleToDexPath,
+	}
+	android.SetProvider(ctx, android.ApexExportsInfoProvider, exports)
 }
 
 // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@@ -854,7 +651,7 @@
 
 	p.apexKeysPath = writeApexKeys(ctx, p)
 	// TODO(jungjw): Check the key validity.
-	p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path()
+	p.inputApex = android.PathForModuleSrc(ctx, p.properties.prebuiltApexSelector(ctx, ctx.Module()))
 	p.installDir = android.PathForModuleInstall(ctx, "apex")
 	p.installFilename = p.InstallFilename()
 	if !strings.HasSuffix(p.installFilename, imageApexSuffix) {
@@ -872,11 +669,13 @@
 		return
 	}
 
+	deapexerInfo := p.getDeapexerInfo(ctx, p.inputApex)
+
 	// dexpreopt any system server jars if present
-	p.dexpreoptSystemServerJars(ctx)
+	p.dexpreoptSystemServerJars(ctx, deapexerInfo)
 
 	// provide info used for generating the boot image
-	p.provideApexExportsInfo(ctx)
+	p.provideApexExportsInfo(ctx, deapexerInfo)
 
 	p.providePrebuiltInfo(ctx)
 
@@ -894,6 +693,8 @@
 		p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks...)
 		p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile)
 	}
+
+	ctx.SetOutputFiles(android.Paths{p.outputApex}, "")
 }
 
 func (p *Prebuilt) ProvenanceMetaDataFile() android.OutputPath {
@@ -910,26 +711,11 @@
 	extractedApex android.WritablePath
 }
 
-func privateApexExtractorModuleFactory() android.Module {
-	module := &prebuiltApexExtractorModule{}
-	module.AddProperties(
-		&module.properties,
-	)
-	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-	return module
-}
-
-func (p *prebuiltApexExtractorModule) Srcs() android.Paths {
-	return android.Paths{p.extractedApex}
-}
-
-func (p *prebuiltApexExtractorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	srcsSupplier := func(ctx android.BaseModuleContext, prebuilt android.Module) []string {
-		return p.properties.prebuiltSrcs(ctx)
-	}
+// extract registers the build actions to extract an apex from .apks file
+// returns the path of the extracted apex
+func extract(ctx android.ModuleContext, apexSet android.Path, prerelease *bool) android.Path {
 	defaultAllowPrerelease := ctx.Config().IsEnvTrue("SOONG_ALLOW_PRERELEASE_APEXES")
-	apexSet := android.SingleSourcePathFromSupplier(ctx, srcsSupplier, "set")
-	p.extractedApex = android.PathForModuleOut(ctx, "extracted", apexSet.Base())
+	extractedApex := android.PathForModuleOut(ctx, "extracted", apexSet.Base())
 	// Filter out NativeBridge archs (b/260115309)
 	abis := java.SupportedAbis(ctx, true)
 	ctx.Build(pctx,
@@ -937,14 +723,16 @@
 			Rule:        extractMatchingApex,
 			Description: "Extract an apex from an apex set",
 			Inputs:      android.Paths{apexSet},
-			Output:      p.extractedApex,
+			Output:      extractedApex,
 			Args: map[string]string{
 				"abis":              strings.Join(abis, ","),
-				"allow-prereleased": strconv.FormatBool(proptools.BoolDefault(p.properties.Prerelease, defaultAllowPrerelease)),
+				"allow-prereleased": strconv.FormatBool(proptools.BoolDefault(prerelease, defaultAllowPrerelease)),
 				"sdk-version":       ctx.Config().PlatformSdkVersion().String(),
 				"skip-sdk-check":    strconv.FormatBool(ctx.Config().IsEnvTrue("SOONG_SKIP_APPSET_SDK_CHECK")),
 			},
-		})
+		},
+	)
+	return extractedApex
 }
 
 type ApexSet struct {
@@ -1009,61 +797,22 @@
 	return false
 }
 
-func (a *ApexSet) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{a.outputApex}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 // prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
 func apexSetFactory() android.Module {
 	module := &ApexSet{}
 	module.AddProperties(&module.properties)
-	module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties)
+	module.prebuiltCommon.prebuiltCommonProperties = &module.properties.PrebuiltCommonProperties
+
+	// init the module as a prebuilt
+	// even though this module type has srcs, use `InitPrebuiltModuleWithoutSrcs`, since the existing
+	// InitPrebuiltModule* are not friendly with Sources of Configurable type.
+	// The actual src will be evaluated in GenerateAndroidBuildActions.
+	android.InitPrebuiltModuleWithoutSrcs(module)
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 
 	return module
 }
 
-func createApexExtractorModule(ctx android.TopDownMutatorContext, name string, apexExtractorProperties *ApexExtractorProperties) {
-	props := struct {
-		Name *string
-	}{
-		Name: proptools.StringPtr(name),
-	}
-
-	ctx.CreateModule(privateApexExtractorModuleFactory,
-		&props,
-		apexExtractorProperties,
-	)
-}
-
-func apexExtractorModuleName(baseModuleName string) string {
-	return baseModuleName + ".apex.extractor"
-}
-
-var _ prebuiltApexModuleCreator = (*ApexSet)(nil)
-
-// createPrebuiltApexModules creates modules necessary to export files from the apex set to other
-// modules.
-//
-// This effectively does for apex_set what Prebuilt.createPrebuiltApexModules does for a
-// prebuilt_apex except that instead of creating a selector module which selects one .apex file
-// from those provided this creates an extractor module which extracts the appropriate .apex file
-// from the zip file containing them.
-func (a *ApexSet) createPrebuiltApexModules(ctx android.TopDownMutatorContext) {
-	apexExtractorModuleName := apexExtractorModuleName(a.Name())
-	createApexExtractorModule(ctx, apexExtractorModuleName, &a.properties.ApexExtractorProperties)
-
-	apexFileSource := ":" + apexExtractorModuleName
-	a.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(a.Name()), apexFileSource)
-
-	// After passing the arch specific src properties to the creating the apex selector module
-	a.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
-}
-
 func (a *ApexSet) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
 	a.prebuiltApexContentsDeps(ctx)
 }
@@ -1086,11 +835,25 @@
 		ctx.ModuleErrorf("filename should end in %s or %s for apex_set", imageApexSuffix, imageCapexSuffix)
 	}
 
-	inputApex := android.OptionalPathForModuleSrc(ctx, a.prebuiltCommonProperties.Selected_apex).Path()
+	var apexSet android.Path
+	if srcs := a.properties.prebuiltSrcs(ctx); len(srcs) == 1 {
+		apexSet = android.PathForModuleSrc(ctx, srcs[0])
+	} else {
+		ctx.ModuleErrorf("Expected exactly one source apex_set file, found %v\n", srcs)
+	}
+
+	extractedApex := extract(ctx, apexSet, a.properties.Prerelease)
+
 	a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
+
+	// Build the output APEX. If compression is not enabled, make sure the output is not compressed even if the input is compressed
+	buildRule := android.Cp
+	if !ctx.Config().ApexCompressionEnabled() {
+		buildRule = decompressApex
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.Cp,
-		Input:  inputApex,
+		Rule:   buildRule,
+		Input:  extractedApex,
 		Output: a.outputApex,
 	})
 
@@ -1099,11 +862,13 @@
 		return
 	}
 
+	deapexerInfo := a.getDeapexerInfo(ctx, extractedApex)
+
 	// dexpreopt any system server jars if present
-	a.dexpreoptSystemServerJars(ctx)
+	a.dexpreoptSystemServerJars(ctx, deapexerInfo)
 
 	// provide info used for generating the boot image
-	a.provideApexExportsInfo(ctx)
+	a.provideApexExportsInfo(ctx, deapexerInfo)
 
 	a.providePrebuiltInfo(ctx)
 
@@ -1121,6 +886,8 @@
 	for _, overridden := range a.prebuiltCommonProperties.Overrides {
 		a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
 	}
+
+	ctx.SetOutputFiles(android.Paths{a.outputApex}, "")
 }
 
 type systemExtContext struct {
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 452a43e..acb3649 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -80,6 +80,7 @@
 			apex_available: [
 				"myapex",
 			],
+			sdk_version: "core_current",
 		}
 
 		systemserverclasspath_fragment {
@@ -276,8 +277,6 @@
 	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
 		`all_apex_contributions`,
 		`dex2oatd`,
-		`prebuilt_myapex.apex.selector`,
-		`prebuilt_myapex.deapexer`,
 		`prebuilt_mysystemserverclasspathfragment`,
 	})
 
@@ -285,10 +284,9 @@
 		`all_apex_contributions`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
-		`prebuilt_myapex.deapexer`,
 	})
 
-	ensureExactDeapexedContents(t, ctx, "prebuilt_myapex", "android_common", []string{
+	ensureExactDeapexedContents(t, ctx, "myapex", "android_common_myapex", []string{
 		"javalib/foo.jar",
 		"javalib/bar.jar",
 		"javalib/bar.jar.prof",
@@ -350,6 +348,7 @@
 			apex_available: [
 				"myapex",
 			],
+			sdk_version: "core_current",
 		}
 
 		systemserverclasspath_fragment {
@@ -437,10 +436,9 @@
 		`all_apex_contributions`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
-		`prebuilt_myapex.deapexer`,
 	})
 
-	ensureExactDeapexedContents(t, ctx, "prebuilt_myapex", "android_common", []string{
+	ensureExactDeapexedContents(t, ctx, "myapex", "android_common_myapex", []string{
 		"javalib/foo.jar",
 		"javalib/bar.jar",
 		"javalib/bar.jar.prof",
diff --git a/apex/vndk.go b/apex/vndk.go
index 781aa3c..3ececc5 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -54,30 +54,6 @@
 	Vndk_version *string
 }
 
-func apexVndkMutator(mctx android.TopDownMutatorContext) {
-	if ab, ok := mctx.Module().(*apexBundle); ok && ab.vndkApex {
-		if ab.IsNativeBridgeSupported() {
-			mctx.PropertyErrorf("native_bridge_supported", "%q doesn't support native bridge binary.", mctx.ModuleType())
-		}
-
-		vndkVersion := ab.vndkVersion()
-		if vndkVersion != "" {
-			apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
-			if err != nil {
-				mctx.PropertyErrorf("vndk_version", "%s", err.Error())
-				return
-			}
-
-			targets := mctx.MultiTargets()
-			if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) {
-				// Disable VNDK APEXes for VNDK versions less than the minimum supported API
-				// level for the primary architecture.
-				ab.Disable()
-			}
-		}
-	}
-}
-
 func apexVndkDepsMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*cc.Module); ok && cc.IsForVndkApex(mctx, m) {
 		vndkVersion := m.VndkVersion()
@@ -93,8 +69,27 @@
 			mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApexName)
 		}
 	} else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex {
-		vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current")
-		mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion, mctx)...)
+		if a.IsNativeBridgeSupported() {
+			mctx.PropertyErrorf("native_bridge_supported", "%q doesn't support native bridge binary.", mctx.ModuleType())
+		}
+
+		vndkVersion := a.vndkVersion()
+		if vndkVersion != "" {
+			apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
+			if err != nil {
+				mctx.PropertyErrorf("vndk_version", "%s", err.Error())
+				return
+			}
+
+			targets := mctx.MultiTargets()
+			if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) {
+				// Disable VNDK APEXes for VNDK versions less than the minimum supported API
+				// level for the primary architecture.
+				a.Disable()
+			} else {
+				mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion, mctx)...)
+			}
+		}
 	}
 }
 
diff --git a/bazel/Android.bp b/bazel/Android.bp
index 4709f5c..f8273a8 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -6,22 +6,17 @@
     name: "soong-bazel",
     pkgPath: "android/soong/bazel",
     srcs: [
-        "aquery.go",
-        "bazel_proxy.go",
         "configurability.go",
-        "constants.go",
         "properties.go",
         "testing.go",
     ],
     testSrcs: [
-        "aquery_test.go",
         "properties_test.go",
     ],
     pluginFor: [
         "soong_build",
     ],
     deps: [
-        "bazel_analysis_v2_proto",
         "blueprint",
     ],
 }
diff --git a/bazel/aquery.go b/bazel/aquery.go
deleted file mode 100644
index 35942bc..0000000
--- a/bazel/aquery.go
+++ /dev/null
@@ -1,768 +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 (
-	"crypto/sha256"
-	"encoding/base64"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"path/filepath"
-	"reflect"
-	"sort"
-	"strings"
-	"sync"
-
-	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
-
-	"github.com/google/blueprint/metrics"
-	"github.com/google/blueprint/proptools"
-	"google.golang.org/protobuf/proto"
-)
-
-type artifactId int
-type depsetId int
-type pathFragmentId int
-
-// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
-type KeyValuePair struct {
-	Key   string
-	Value string
-}
-
-// AqueryDepset is a depset definition from Bazel's aquery response. This is
-// akin to the `depSetOfFiles` in the response proto, except:
-//   - direct artifacts are enumerated by full path instead of by ID
-//   - it has a hash of the depset contents, instead of an int ID (for determinism)
-//
-// A depset is a data structure for efficient transitive handling of artifact
-// paths. A single depset consists of one or more artifact paths and one or
-// more "child" depsets.
-type AqueryDepset struct {
-	ContentHash            string
-	DirectArtifacts        []string
-	TransitiveDepSetHashes []string
-}
-
-// BuildStatement contains information to register a build statement corresponding (one to one)
-// with a Bazel action from Bazel's action graph.
-type BuildStatement struct {
-	Command      string
-	Depfile      *string
-	OutputPaths  []string
-	SymlinkPaths []string
-	Env          []*analysis_v2_proto.KeyValuePair
-	Mnemonic     string
-
-	// Inputs of this build statement, either as unexpanded depsets or expanded
-	// input paths. There should be no overlap between these fields; an input
-	// path should either be included as part of an unexpanded depset or a raw
-	// input path string, but not both.
-	InputDepsetHashes []string
-	InputPaths        []string
-	FileContents      string
-	// If ShouldRunInSbox is true, Soong will use sbox to created an isolated environment
-	// and run the mixed build action there
-	ShouldRunInSbox bool
-	// A list of files to add as implicit deps to the outputs of this BuildStatement.
-	// Unlike most properties in BuildStatement, these paths must be relative to the root of
-	// the whole out/ folder, instead of relative to ctx.Config().BazelContext.OutputBase()
-	ImplicitDeps []string
-	IsExecutable bool
-}
-
-// A helper type for aquery processing which facilitates retrieval of path IDs from their
-// less readable Bazel structures (depset and path fragment).
-type aqueryArtifactHandler struct {
-	// Maps depset id to AqueryDepset, a representation of depset which is
-	// post-processed for middleman artifact handling, unhandled artifact
-	// dropping, content hashing, etc.
-	depsetIdToAqueryDepset map[depsetId]AqueryDepset
-	emptyDepsetIds         map[depsetId]struct{}
-	// Maps content hash to AqueryDepset.
-	depsetHashToAqueryDepset map[string]AqueryDepset
-
-	// depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
-	// may be an expensive operation.
-	depsetHashToArtifactPathsCache sync.Map
-	// Maps artifact ids to fully expanded paths.
-	artifactIdToPath map[artifactId]string
-}
-
-// The tokens should be substituted with the value specified here, instead of the
-// one returned in 'substitutions' of TemplateExpand action.
-var templateActionOverriddenTokens = map[string]string{
-	// Uses "python3" for %python_binary% instead of the value returned by aquery
-	// which is "py3wrapper.sh". See removePy3wrapperScript.
-	"%python_binary%": "python3",
-}
-
-const (
-	middlemanMnemonic = "Middleman"
-	// The file name of py3wrapper.sh, which is used by py_binary targets.
-	py3wrapperFileName = "/py3wrapper.sh"
-)
-
-func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
-	m := map[K]V{}
-	for _, v := range values {
-		m[keyFn(v)] = v
-	}
-	return m
-}
-
-func newAqueryHandler(aqueryResult *analysis_v2_proto.ActionGraphContainer) (*aqueryArtifactHandler, error) {
-	pathFragments := indexBy(aqueryResult.PathFragments, func(pf *analysis_v2_proto.PathFragment) pathFragmentId {
-		return pathFragmentId(pf.Id)
-	})
-
-	artifactIdToPath := make(map[artifactId]string, len(aqueryResult.Artifacts))
-	for _, artifact := range aqueryResult.Artifacts {
-		artifactPath, err := expandPathFragment(pathFragmentId(artifact.PathFragmentId), pathFragments)
-		if err != nil {
-			return nil, err
-		}
-		if artifact.IsTreeArtifact &&
-			!strings.HasPrefix(artifactPath, "bazel-out/io_bazel_rules_go/") &&
-			!strings.HasPrefix(artifactPath, "bazel-out/rules_java_builtin/") {
-			// Since we're using ninja as an executor, we can't use tree artifacts. Ninja only
-			// considers a file/directory "dirty" when it's mtime changes. Directories' mtimes will
-			// only change when a file in the directory is added/removed, but not when files in
-			// the directory are changed, or when files in subdirectories are changed/added/removed.
-			// Bazel handles this by walking the directory and generating a hash for it after the
-			// action runs, which we would have to do as well if we wanted to support these
-			// artifacts in mixed builds.
-			//
-			// However, there are some bazel built-in rules that use tree artifacts. Allow those,
-			// but keep in mind that they'll have incrementality issues.
-			return nil, fmt.Errorf("tree artifacts are currently not supported in mixed builds: " + artifactPath)
-		}
-		artifactIdToPath[artifactId(artifact.Id)] = artifactPath
-	}
-
-	// Map middleman artifact ContentHash to input artifact depset ID.
-	// Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example,
-	// if we find a middleman action which has inputs [foo, bar], and output [baz_middleman], then,
-	// for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
-	// that action instead.
-	middlemanIdToDepsetIds := map[artifactId][]uint32{}
-	for _, actionEntry := range aqueryResult.Actions {
-		if actionEntry.Mnemonic == middlemanMnemonic {
-			for _, outputId := range actionEntry.OutputIds {
-				middlemanIdToDepsetIds[artifactId(outputId)] = actionEntry.InputDepSetIds
-			}
-		}
-	}
-
-	depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d *analysis_v2_proto.DepSetOfFiles) depsetId {
-		return depsetId(d.Id)
-	})
-
-	aqueryHandler := aqueryArtifactHandler{
-		depsetIdToAqueryDepset:         map[depsetId]AqueryDepset{},
-		depsetHashToAqueryDepset:       map[string]AqueryDepset{},
-		depsetHashToArtifactPathsCache: sync.Map{},
-		emptyDepsetIds:                 make(map[depsetId]struct{}, 0),
-		artifactIdToPath:               artifactIdToPath,
-	}
-
-	// Validate and adjust aqueryResult.DepSetOfFiles values.
-	for _, depset := range aqueryResult.DepSetOfFiles {
-		_, err := aqueryHandler.populateDepsetMaps(depset, middlemanIdToDepsetIds, depsetIdToDepset)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return &aqueryHandler, nil
-}
-
-// Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
-// depset.
-func (a *aqueryArtifactHandler) populateDepsetMaps(depset *analysis_v2_proto.DepSetOfFiles, middlemanIdToDepsetIds map[artifactId][]uint32, depsetIdToDepset map[depsetId]*analysis_v2_proto.DepSetOfFiles) (*AqueryDepset, error) {
-	if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depsetId(depset.Id)]; containsDepset {
-		return &aqueryDepset, nil
-	}
-	transitiveDepsetIds := depset.TransitiveDepSetIds
-	directArtifactPaths := make([]string, 0, len(depset.DirectArtifactIds))
-	for _, id := range depset.DirectArtifactIds {
-		aId := artifactId(id)
-		path, pathExists := a.artifactIdToPath[aId]
-		if !pathExists {
-			return nil, fmt.Errorf("undefined input artifactId %d", aId)
-		}
-		// Filter out any inputs which are universally dropped, and swap middleman
-		// artifacts with their corresponding depsets.
-		if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[aId]; isMiddleman {
-			// Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
-			transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
-		} else if strings.HasSuffix(path, py3wrapperFileName) ||
-			strings.HasPrefix(path, "../bazel_tools") {
-			continue
-			// Drop these artifacts.
-			// See go/python-binary-host-mixed-build for more details.
-			// 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
-			// TemplateExpandAction handles everything necessary to launch a Pythin application.
-			// 2) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
-			// containing depset to always be considered newer than their outputs.
-		} else {
-			directArtifactPaths = append(directArtifactPaths, path)
-		}
-	}
-
-	childDepsetHashes := make([]string, 0, len(transitiveDepsetIds))
-	for _, id := range transitiveDepsetIds {
-		childDepsetId := depsetId(id)
-		childDepset, exists := depsetIdToDepset[childDepsetId]
-		if !exists {
-			if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
-				continue
-			} else {
-				return nil, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
-			}
-		}
-		if childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset); err != nil {
-			return nil, err
-		} else if childAqueryDepset == nil {
-			continue
-		} else {
-			childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
-		}
-	}
-	if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
-		a.emptyDepsetIds[depsetId(depset.Id)] = struct{}{}
-		return nil, nil
-	}
-	aqueryDepset := AqueryDepset{
-		ContentHash:            depsetContentHash(directArtifactPaths, childDepsetHashes),
-		DirectArtifacts:        directArtifactPaths,
-		TransitiveDepSetHashes: childDepsetHashes,
-	}
-	a.depsetIdToAqueryDepset[depsetId(depset.Id)] = aqueryDepset
-	a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
-	return &aqueryDepset, nil
-}
-
-// getInputPaths flattens the depsets of the given IDs and returns all transitive
-// input paths contained in these depsets.
-// This is a potentially expensive operation, and should not be invoked except
-// for actions which need specialized input handling.
-func (a *aqueryArtifactHandler) getInputPaths(depsetIds []uint32) ([]string, error) {
-	var inputPaths []string
-
-	for _, id := range depsetIds {
-		inputDepSetId := depsetId(id)
-		depset := a.depsetIdToAqueryDepset[inputDepSetId]
-		inputArtifacts, err := a.artifactPathsFromDepsetHash(depset.ContentHash)
-		if err != nil {
-			return nil, err
-		}
-		for _, inputPath := range inputArtifacts {
-			inputPaths = append(inputPaths, inputPath)
-		}
-	}
-
-	return inputPaths, nil
-}
-
-func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) ([]string, error) {
-	if result, exists := a.depsetHashToArtifactPathsCache.Load(depsetHash); exists {
-		return result.([]string), nil
-	}
-	if depset, exists := a.depsetHashToAqueryDepset[depsetHash]; exists {
-		result := depset.DirectArtifacts
-		for _, childHash := range depset.TransitiveDepSetHashes {
-			childArtifactIds, err := a.artifactPathsFromDepsetHash(childHash)
-			if err != nil {
-				return nil, err
-			}
-			result = append(result, childArtifactIds...)
-		}
-		a.depsetHashToArtifactPathsCache.Store(depsetHash, result)
-		return result, nil
-	} else {
-		return nil, fmt.Errorf("undefined input depset hash %s", depsetHash)
-	}
-}
-
-// AqueryBuildStatements returns a slice of BuildStatements and a slice of AqueryDepset
-// which should be registered (and output to a ninja file) to correspond with Bazel's
-// action graph, as described by the given action graph json proto.
-// BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
-// are one-to-one with Bazel's depSetOfFiles objects.
-func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHandler) ([]*BuildStatement, []AqueryDepset, error) {
-	aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
-	err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var aqueryHandler *aqueryArtifactHandler
-	{
-		eventHandler.Begin("init_handler")
-		defer eventHandler.End("init_handler")
-		aqueryHandler, err = newAqueryHandler(aqueryProto)
-		if err != nil {
-			return nil, nil, err
-		}
-	}
-
-	// allocate both length and capacity so each goroutine can write to an index independently without
-	// any need for synchronization for slice access.
-	buildStatements := make([]*BuildStatement, len(aqueryProto.Actions))
-	{
-		eventHandler.Begin("build_statements")
-		defer eventHandler.End("build_statements")
-		wg := sync.WaitGroup{}
-		var errOnce sync.Once
-		id2targets := make(map[uint32]string, len(aqueryProto.Targets))
-		for _, t := range aqueryProto.Targets {
-			id2targets[t.GetId()] = t.GetLabel()
-		}
-		for i, actionEntry := range aqueryProto.Actions {
-			wg.Add(1)
-			go func(i int, actionEntry *analysis_v2_proto.Action) {
-				if strings.HasPrefix(id2targets[actionEntry.TargetId], "@bazel_tools//") {
-					// bazel_tools are removed depsets in `populateDepsetMaps()` so skipping
-					// conversion to build statements as well
-					buildStatements[i] = nil
-				} else if buildStatement, aErr := aqueryHandler.actionToBuildStatement(actionEntry); aErr != nil {
-					errOnce.Do(func() {
-						aErr = fmt.Errorf("%s: [%s] [%s]", aErr.Error(), actionEntry.GetMnemonic(), id2targets[actionEntry.TargetId])
-						err = aErr
-					})
-				} else {
-					// set build statement at an index rather than appending such that each goroutine does not
-					// impact other goroutines
-					buildStatements[i] = buildStatement
-				}
-				wg.Done()
-			}(i, actionEntry)
-		}
-		wg.Wait()
-	}
-	if err != nil {
-		return nil, nil, err
-	}
-
-	depsetsByHash := map[string]AqueryDepset{}
-	depsets := make([]AqueryDepset, 0, len(aqueryHandler.depsetIdToAqueryDepset))
-	{
-		eventHandler.Begin("depsets")
-		defer eventHandler.End("depsets")
-		for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
-			if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
-				// Two depsets collide on hash. Ensure that their contents are identical.
-				if !reflect.DeepEqual(aqueryDepset, prevEntry) {
-					return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
-				}
-			} else {
-				depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
-				depsets = append(depsets, aqueryDepset)
-			}
-		}
-	}
-
-	eventHandler.Do("build_statement_sort", func() {
-		// Build Statements and depsets must be sorted by their content hash to
-		// preserve determinism between builds (this will result in consistent ninja file
-		// output). Note they are not sorted by their original IDs nor their Bazel ordering,
-		// as Bazel gives nondeterministic ordering / identifiers in aquery responses.
-		sort.Slice(buildStatements, func(i, j int) bool {
-			// Sort all nil statements to the end of the slice
-			if buildStatements[i] == nil {
-				return false
-			} else if buildStatements[j] == nil {
-				return true
-			}
-			//For build statements, compare output lists. In Bazel, each output file
-			// may only have one action which generates it, so this will provide
-			// a deterministic ordering.
-			outputs_i := buildStatements[i].OutputPaths
-			outputs_j := buildStatements[j].OutputPaths
-			if len(outputs_i) != len(outputs_j) {
-				return len(outputs_i) < len(outputs_j)
-			}
-			if len(outputs_i) == 0 {
-				// No outputs for these actions, so compare commands.
-				return buildStatements[i].Command < buildStatements[j].Command
-			}
-			// There may be multiple outputs, but the output ordering is deterministic.
-			return outputs_i[0] < outputs_j[0]
-		})
-	})
-	eventHandler.Do("depset_sort", func() {
-		sort.Slice(depsets, func(i, j int) bool {
-			return depsets[i].ContentHash < depsets[j].ContentHash
-		})
-	})
-	return buildStatements, depsets, nil
-}
-
-// depsetContentHash computes and returns a SHA256 checksum of the contents of
-// the given depset. This content hash may serve as the depset's identifier.
-// Using a content hash for an identifier is superior for determinism. (For example,
-// using an integer identifier which depends on the order in which the depsets are
-// created would result in nondeterministic depset IDs.)
-func depsetContentHash(directPaths []string, transitiveDepsetHashes []string) string {
-	h := sha256.New()
-	// Use newline as delimiter, as paths cannot contain newline.
-	h.Write([]byte(strings.Join(directPaths, "\n")))
-	h.Write([]byte(strings.Join(transitiveDepsetHashes, "")))
-	fullHash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
-	return fullHash
-}
-
-func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []uint32) ([]string, error) {
-	var hashes []string
-	for _, id := range inputDepsetIds {
-		dId := depsetId(id)
-		if aqueryDepset, exists := a.depsetIdToAqueryDepset[dId]; !exists {
-			if _, empty := a.emptyDepsetIds[dId]; !empty {
-				return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", dId)
-			}
-		} else {
-			hashes = append(hashes, aqueryDepset.ContentHash)
-		}
-	}
-	return hashes, nil
-}
-
-// escapes the args received from aquery and creates a command string
-func commandString(actionEntry *analysis_v2_proto.Action) string {
-	argsEscaped := make([]string, len(actionEntry.Arguments))
-	for i, arg := range actionEntry.Arguments {
-		if arg == "" {
-			// If this is an empty string, add ''
-			// And not
-			// 1. (literal empty)
-			// 2. `''\'''\'''` (escaped version of '')
-			//
-			// If we had used (1), then this would appear as a whitespace when we strings.Join
-			argsEscaped[i] = "''"
-		} else {
-			argsEscaped[i] = proptools.ShellEscapeIncludingSpaces(arg)
-		}
-	}
-	return strings.Join(argsEscaped, " ")
-}
-
-func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
-	command := commandString(actionEntry)
-	inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
-	if err != nil {
-		return nil, err
-	}
-	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
-	if err != nil {
-		return nil, err
-	}
-
-	buildStatement := &BuildStatement{
-		Command:           command,
-		Depfile:           depfile,
-		OutputPaths:       outputPaths,
-		InputDepsetHashes: inputDepsetHashes,
-		Env:               actionEntry.EnvironmentVariables,
-		Mnemonic:          actionEntry.Mnemonic,
-	}
-	if buildStatement.Mnemonic == "GoToolchainBinaryBuild" {
-		// Unlike b's execution root, mixed build execution root contains a symlink to prebuilts/go
-		// This causes issues for `GOCACHE=$(mktemp -d) go build ...`
-		// To prevent this, sandbox this action in mixed builds as well
-		buildStatement.ShouldRunInSbox = true
-	}
-	return buildStatement, nil
-}
-
-func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
-	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
-	if err != nil {
-		return nil, err
-	}
-	if len(outputPaths) != 1 {
-		return nil, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
-	}
-	expandedTemplateContent := expandTemplateContent(actionEntry)
-	// The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
-	// and the new line characters (\n) are also changed to \\n which avoids some Ninja escape on \n, which might
-	// change \n to space and mess up the format of Python programs.
-	// sed is used to convert \\n back to \n before saving to output file.
-	// See go/python-binary-host-mixed-build for more details.
-	command := fmt.Sprintf(`/bin/bash -c 'echo "%[1]s" | sed "s/\\\\n/\\n/g" > %[2]s && chmod a+x %[2]s'`,
-		escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
-	inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
-	if err != nil {
-		return nil, err
-	}
-
-	buildStatement := &BuildStatement{
-		Command:           command,
-		Depfile:           depfile,
-		OutputPaths:       outputPaths,
-		InputDepsetHashes: inputDepsetHashes,
-		Env:               actionEntry.EnvironmentVariables,
-		Mnemonic:          actionEntry.Mnemonic,
-	}
-	return buildStatement, nil
-}
-
-func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
-	outputPaths, _, err := a.getOutputPaths(actionEntry)
-	var depsetHashes []string
-	if err == nil {
-		depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
-	}
-	if err != nil {
-		return nil, err
-	}
-	return &BuildStatement{
-		Depfile:           nil,
-		OutputPaths:       outputPaths,
-		Env:               actionEntry.EnvironmentVariables,
-		Mnemonic:          actionEntry.Mnemonic,
-		InputDepsetHashes: depsetHashes,
-		FileContents:      actionEntry.FileContents,
-		IsExecutable:      actionEntry.IsExecutable,
-	}, nil
-}
-
-func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
-	outputPaths, _, err := a.getOutputPaths(actionEntry)
-	if err != nil {
-		return nil, err
-	}
-	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
-	if err != nil {
-		return nil, err
-	}
-	if len(inputPaths) != 1 || len(outputPaths) != 1 {
-		return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
-	}
-	// The actual command is generated in bazelSingleton.GenerateBuildActions
-	return &BuildStatement{
-		Depfile:     nil,
-		OutputPaths: outputPaths,
-		Env:         actionEntry.EnvironmentVariables,
-		Mnemonic:    actionEntry.Mnemonic,
-		InputPaths:  inputPaths,
-	}, nil
-}
-
-type bazelSandwichJson struct {
-	Target         string   `json:"target"`
-	DependOnTarget *bool    `json:"depend_on_target,omitempty"`
-	ImplicitDeps   []string `json:"implicit_deps"`
-}
-
-func (a *aqueryArtifactHandler) unresolvedSymlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
-	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
-	if err != nil {
-		return nil, err
-	}
-	if len(actionEntry.InputDepSetIds) != 0 || len(outputPaths) != 1 {
-		return nil, fmt.Errorf("expected 0 inputs and 1 output to symlink action, got: input %q, output %q", actionEntry.InputDepSetIds, outputPaths)
-	}
-	target := actionEntry.UnresolvedSymlinkTarget
-	if target == "" {
-		return nil, fmt.Errorf("expected an unresolved_symlink_target, but didn't get one")
-	}
-	if filepath.Clean(target) != target {
-		return nil, fmt.Errorf("expected %q, got %q", filepath.Clean(target), target)
-	}
-	if strings.HasPrefix(target, "/") {
-		return nil, fmt.Errorf("no absolute symlinks allowed: %s", target)
-	}
-
-	out := outputPaths[0]
-	outDir := filepath.Dir(out)
-	var implicitDeps []string
-	if strings.HasPrefix(target, "bazel_sandwich:") {
-		j := bazelSandwichJson{}
-		err := json.Unmarshal([]byte(target[len("bazel_sandwich:"):]), &j)
-		if err != nil {
-			return nil, err
-		}
-		if proptools.BoolDefault(j.DependOnTarget, true) {
-			implicitDeps = append(implicitDeps, j.Target)
-		}
-		implicitDeps = append(implicitDeps, j.ImplicitDeps...)
-		dotDotsToReachCwd := ""
-		if outDir != "." {
-			dotDotsToReachCwd = strings.Repeat("../", strings.Count(outDir, "/")+1)
-		}
-		target = proptools.ShellEscapeIncludingSpaces(j.Target)
-		target = "{DOTDOTS_TO_OUTPUT_ROOT}" + dotDotsToReachCwd + target
-	} else {
-		target = proptools.ShellEscapeIncludingSpaces(target)
-	}
-
-	outDir = proptools.ShellEscapeIncludingSpaces(outDir)
-	out = proptools.ShellEscapeIncludingSpaces(out)
-	// Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
-	command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, target)
-	symlinkPaths := outputPaths[:]
-
-	buildStatement := &BuildStatement{
-		Command:      command,
-		Depfile:      depfile,
-		OutputPaths:  outputPaths,
-		Env:          actionEntry.EnvironmentVariables,
-		Mnemonic:     actionEntry.Mnemonic,
-		SymlinkPaths: symlinkPaths,
-		ImplicitDeps: implicitDeps,
-	}
-	return buildStatement, nil
-}
-
-func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
-	outputPaths, depfile, err := a.getOutputPaths(actionEntry)
-	if err != nil {
-		return nil, err
-	}
-
-	inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
-	if err != nil {
-		return nil, err
-	}
-	if len(inputPaths) != 1 || len(outputPaths) != 1 {
-		return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
-	}
-	out := outputPaths[0]
-	outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
-	out = proptools.ShellEscapeIncludingSpaces(out)
-	in := filepath.Join("$PWD", proptools.ShellEscapeIncludingSpaces(inputPaths[0]))
-	// Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
-	command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
-	symlinkPaths := outputPaths[:]
-
-	buildStatement := &BuildStatement{
-		Command:      command,
-		Depfile:      depfile,
-		OutputPaths:  outputPaths,
-		InputPaths:   inputPaths,
-		Env:          actionEntry.EnvironmentVariables,
-		Mnemonic:     actionEntry.Mnemonic,
-		SymlinkPaths: symlinkPaths,
-	}
-	return buildStatement, nil
-}
-
-func (a *aqueryArtifactHandler) getOutputPaths(actionEntry *analysis_v2_proto.Action) (outputPaths []string, depfile *string, err error) {
-	for _, outputId := range actionEntry.OutputIds {
-		outputPath, exists := a.artifactIdToPath[artifactId(outputId)]
-		if !exists {
-			err = fmt.Errorf("undefined outputId %d", outputId)
-			return
-		}
-		ext := filepath.Ext(outputPath)
-		if ext == ".d" {
-			if depfile != nil {
-				err = fmt.Errorf("found multiple potential depfiles %q, %q", *depfile, outputPath)
-				return
-			} else {
-				depfile = &outputPath
-			}
-		} else {
-			outputPaths = append(outputPaths, outputPath)
-		}
-	}
-	return
-}
-
-// expandTemplateContent substitutes the tokens in a template.
-func expandTemplateContent(actionEntry *analysis_v2_proto.Action) string {
-	replacerString := make([]string, len(actionEntry.Substitutions)*2)
-	for i, pair := range actionEntry.Substitutions {
-		value := pair.Value
-		if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
-			value = val
-		}
-		replacerString[i*2] = pair.Key
-		replacerString[i*2+1] = value
-	}
-	replacer := strings.NewReplacer(replacerString...)
-	return replacer.Replace(actionEntry.TemplateContent)
-}
-
-// \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
-var commandLineArgumentReplacer = strings.NewReplacer(
-	`\`, `\\`,
-	`$`, `\$`,
-	"`", "\\`",
-	`"`, `\"`,
-	"\n", "\\n",
-	`'`, `'"'"'`,
-)
-
-func escapeCommandlineArgument(str string) string {
-	return commandLineArgumentReplacer.Replace(str)
-}
-
-func (a *aqueryArtifactHandler) actionToBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
-	switch actionEntry.Mnemonic {
-	// Middleman actions are not handled like other actions; they are handled separately as a
-	// preparatory step so that their inputs may be relayed to actions depending on middleman
-	// artifacts.
-	case middlemanMnemonic:
-		return nil, nil
-	// PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
-	case "PythonZipper":
-		return nil, nil
-	// Skip "Fail" actions, which are placeholder actions designed to always fail.
-	case "Fail":
-		return nil, nil
-	case "BaselineCoverage":
-		return nil, nil
-	case "Symlink", "SolibSymlink", "ExecutableSymlink":
-		return a.symlinkActionBuildStatement(actionEntry)
-	case "TemplateExpand":
-		if len(actionEntry.Arguments) < 1 {
-			return a.templateExpandActionBuildStatement(actionEntry)
-		}
-	case "FileWrite", "SourceSymlinkManifest", "RepoMappingManifest":
-		return a.fileWriteActionBuildStatement(actionEntry)
-	case "SymlinkTree":
-		return a.symlinkTreeActionBuildStatement(actionEntry)
-	case "UnresolvedSymlink":
-		return a.unresolvedSymlinkActionBuildStatement(actionEntry)
-	}
-
-	if len(actionEntry.Arguments) < 1 {
-		return nil, errors.New("received action with no command")
-	}
-	return a.normalActionBuildStatement(actionEntry)
-
-}
-
-func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]*analysis_v2_proto.PathFragment) (string, error) {
-	var labels []string
-	currId := id
-	// Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
-	for currId > 0 {
-		currFragment, ok := pathFragmentsMap[currId]
-		if !ok {
-			return "", fmt.Errorf("undefined path fragment id %d", currId)
-		}
-		labels = append([]string{currFragment.Label}, labels...)
-		parentId := pathFragmentId(currFragment.ParentId)
-		if currId == parentId {
-			return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
-		}
-		currId = parentId
-	}
-	return filepath.Join(labels...), nil
-}
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
deleted file mode 100644
index cbd2791..0000000
--- a/bazel/aquery_test.go
+++ /dev/null
@@ -1,1411 +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 (
-	"encoding/json"
-	"fmt"
-	"reflect"
-	"sort"
-	"testing"
-
-	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
-
-	"github.com/google/blueprint/metrics"
-	"google.golang.org/protobuf/proto"
-)
-
-func TestAqueryMultiArchGenrule(t *testing.T) {
-	// This input string is retrieved from a real build of bionic-related genrules.
-	const inputString = `
-{
- "Artifacts": [
-   { "Id": 1, "path_fragment_id": 1 },
-   { "Id": 2, "path_fragment_id": 6 },
-   { "Id": 3, "path_fragment_id": 8 },
-   { "Id": 4, "path_fragment_id": 12 },
-   { "Id": 5, "path_fragment_id": 19 },
-   { "Id": 6, "path_fragment_id": 20 },
-   { "Id": 7, "path_fragment_id": 21 }],
- "Actions": [{
-   "target_id": 1,
-   "action_key": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
-   "Mnemonic": "Genrule",
-   "configuration_id": 1,
-   "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
-   "environment_variables": [{
-     "Key": "PATH",
-     "Value": "/bin:/usr/bin:/usr/local/bin"
-   }],
-   "input_dep_set_ids": [1],
-   "output_ids": [4],
-   "primary_output_id": 4
- }, {
-   "target_id": 2,
-   "action_key": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
-   "Mnemonic": "Genrule",
-   "configuration_id": 1,
-   "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
-   "environment_variables": [{
-     "Key": "PATH",
-     "Value": "/bin:/usr/bin:/usr/local/bin"
-   }],
-   "input_dep_set_ids": [2],
-   "output_ids": [5],
-   "primary_output_id": 5
- }, {
-   "target_id": 3,
-   "action_key": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
-   "Mnemonic": "Genrule",
-   "configuration_id": 1,
-   "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
-   "environment_variables": [{
-     "Key": "PATH",
-     "Value": "/bin:/usr/bin:/usr/local/bin"
-   }],
-   "input_dep_set_ids": [3],
-   "output_ids": [6],
-   "primary_output_id": 6
- }, {
-   "target_id": 4,
-   "action_key": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
-   "Mnemonic": "Genrule",
-   "configuration_id": 1,
-   "Arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
-   "environment_variables": [{
-     "Key": "PATH",
-     "Value": "/bin:/usr/bin:/usr/local/bin"
-   }],
-   "input_dep_set_ids": [4],
-   "output_ids": [7],
-   "primary_output_id": 7
- }],
- "Targets": [
-   { "Id": 1, "Label": "@sourceroot//bionic/libc:syscalls-arm", "rule_class_id": 1 },
-   { "Id": 2, "Label": "@sourceroot//bionic/libc:syscalls-x86", "rule_class_id": 1 },
-   { "Id": 3, "Label": "@sourceroot//bionic/libc:syscalls-x86_64", "rule_class_id": 1 },
-   { "Id": 4, "Label": "@sourceroot//bionic/libc:syscalls-arm64", "rule_class_id": 1 }],
- "dep_set_of_files": [
-   { "Id": 1, "direct_artifact_ids": [1, 2, 3] },
-   { "Id": 2, "direct_artifact_ids": [1, 2, 3] },
-   { "Id": 3, "direct_artifact_ids": [1, 2, 3] },
-   { "Id": 4, "direct_artifact_ids": [1, 2, 3] }],
- "Configuration": [{
-   "Id": 1,
-   "Mnemonic": "k8-fastbuild",
-   "platform_name": "k8",
-   "Checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
- }],
- "rule_classes": [{ "Id": 1, "Name": "genrule"}],
- "path_fragments": [
-   { "Id": 5, "Label": ".." },
-   { "Id": 4, "Label": "sourceroot", "parent_id": 5 },
-   { "Id": 3, "Label": "bionic", "parent_id": 4 },
-   { "Id": 2, "Label": "libc", "parent_id": 3 },
-   { "Id": 1, "Label": "SYSCALLS.TXT", "parent_id": 2 },
-   { "Id": 7, "Label": "tools", "parent_id": 2 },
-   { "Id": 6, "Label": "gensyscalls.py", "parent_id": 7 },
-   { "Id": 11, "Label": "bazel_tools", "parent_id": 5 },
-   { "Id": 10, "Label": "tools", "parent_id": 11 },
-   { "Id": 9, "Label": "genrule", "parent_id": 10 },
-   { "Id": 8, "Label": "genrule-setup.sh", "parent_id": 9 },
-   { "Id": 18, "Label": "bazel-out" },
-   { "Id": 17, "Label": "sourceroot", "parent_id": 18 },
-   { "Id": 16, "Label": "k8-fastbuild", "parent_id": 17 },
-   { "Id": 15, "Label": "bin", "parent_id": 16 },
-   { "Id": 14, "Label": "bionic", "parent_id": 15 },
-   { "Id": 13, "Label": "libc", "parent_id": 14 },
-   { "Id": 12, "Label": "syscalls-arm.S", "parent_id": 13 },
-   { "Id": 19, "Label": "syscalls-x86.S", "parent_id": 13 },
-   { "Id": 20, "Label": "syscalls-x86_64.S", "parent_id": 13 },
-   { "Id": 21, "Label": "syscalls-arm64.S", "parent_id": 13 }]
-}
-`
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
-	var expectedBuildStatements []*BuildStatement
-	for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
-		expectedBuildStatements = append(expectedBuildStatements,
-			&BuildStatement{
-				Command: fmt.Sprintf(
-					"/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
-					arch, arch),
-				OutputPaths: []string{
-					fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
-				},
-				Env: []*analysis_v2_proto.KeyValuePair{
-					{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
-				},
-				Mnemonic: "Genrule",
-			})
-	}
-	assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
-
-	expectedFlattenedInputs := []string{
-		"../sourceroot/bionic/libc/SYSCALLS.TXT",
-		"../sourceroot/bionic/libc/tools/gensyscalls.py",
-	}
-	// In this example, each depset should have the same expected inputs.
-	for _, actualDepset := range actualDepsets {
-		actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
-		if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
-			t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
-		}
-	}
-}
-
-func TestInvalidOutputId(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 },
-   { "id": 2, "path_fragment_id": 2 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "action_x",
-   "mnemonic": "X",
-   "arguments": ["touch", "foo"],
-   "input_dep_set_ids": [1],
-   "output_ids": [3],
-   "primary_output_id": 3
- }],
- "dep_set_of_files": [
-   { "id": 1, "direct_artifact_ids": [1, 2] }],
- "path_fragments": [
-   { "id": 1, "label": "one" },
-   { "id": 2, "label": "two" }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
-	assertError(t, err, "undefined outputId 3: [X] []")
-}
-
-func TestInvalidInputDepsetIdFromAction(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 },
-   { "id": 2, "path_fragment_id": 2 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "action_x",
-   "mnemonic": "X",
-   "arguments": ["touch", "foo"],
-   "input_dep_set_ids": [2],
-   "output_ids": [1],
-   "primary_output_id": 1
- }],
- "targets": [{
-   "id": 1,
-   "label": "target_x"
- }],
- "dep_set_of_files": [
-   { "id": 1, "direct_artifact_ids": [1, 2] }],
- "path_fragments": [
-   { "id": 1, "label": "one" },
-   { "id": 2, "label": "two" }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
-	assertError(t, err, "undefined (not even empty) input depsetId 2: [X] [target_x]")
-}
-
-func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 },
-   { "id": 2, "path_fragment_id": 2 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "x",
-   "arguments": ["touch", "foo"],
-   "input_dep_set_ids": [1],
-   "output_ids": [1],
-   "primary_output_id": 1
- }],
- "dep_set_of_files": [
-   { "id": 1, "direct_artifact_ids": [1, 2], "transitive_dep_set_ids": [42] }],
- "path_fragments": [
-   { "id": 1, "label": "one"},
-   { "id": 2, "label": "two" }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
-	assertError(t, err, "undefined input depsetId 42 (referenced by depsetId 1)")
-}
-
-func TestInvalidInputArtifactId(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 },
-   { "id": 2, "path_fragment_id": 2 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "x",
-   "arguments": ["touch", "foo"],
-   "input_dep_set_ids": [1],
-   "output_ids": [1],
-   "primary_output_id": 1
- }],
- "dep_set_of_files": [
-   { "id": 1, "direct_artifact_ids": [1, 3] }],
- "path_fragments": [
-   { "id": 1, "label": "one" },
-   { "id": 2, "label": "two" }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
-	assertError(t, err, "undefined input artifactId 3")
-}
-
-func TestInvalidPathFragmentId(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 },
-   { "id": 2, "path_fragment_id": 2 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "x",
-   "arguments": ["touch", "foo"],
-   "input_dep_set_ids": [1],
-   "output_ids": [1],
-   "primary_output_id": 1
- }],
- "dep_set_of_files": [
-    { "id": 1, "direct_artifact_ids": [1, 2] }],
- "path_fragments": [
-   {  "id": 1, "label": "one" },
-   {  "id": 2, "label": "two", "parent_id": 3 }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
-	assertError(t, err, "undefined path fragment id 3")
-}
-
-func TestDepfiles(t *testing.T) {
-	const inputString = `
-{
-  "artifacts": [
-    { "id": 1, "path_fragment_id": 1 },
-    { "id": 2, "path_fragment_id": 2 },
-    { "id": 3, "path_fragment_id": 3 }],
-  "actions": [{
-    "target_Id": 1,
-    "action_Key": "x",
-    "mnemonic": "x",
-    "arguments": ["touch", "foo"],
-    "input_dep_set_ids": [1],
-    "output_ids": [2, 3],
-    "primary_output_id": 2
-  }],
-  "dep_set_of_files": [
-    { "id": 1, "direct_Artifact_Ids": [1, 2, 3] }],
-  "path_fragments": [
-    { "id": 1, "label": "one" },
-    { "id": 2, "label": "two" },
-    { "id": 3, "label": "two.d" }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-	if expected := 1; len(actual) != expected {
-		t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
-		return
-	}
-
-	bs := actual[0]
-	expectedDepfile := "two.d"
-	if bs.Depfile == nil {
-		t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
-	} else if *bs.Depfile != expectedDepfile {
-		t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
-	}
-}
-
-func TestMultipleDepfiles(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 },
-   { "id": 2, "path_fragment_id": 2 },
-   { "id": 3, "path_fragment_id": 3 },
-   { "id": 4, "path_fragment_id": 4 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "action_x",
-   "mnemonic": "X",
-   "arguments": ["touch", "foo"],
-   "input_dep_set_ids": [1],
-   "output_ids": [2,3,4],
-   "primary_output_id": 2
- }],
- "dep_set_of_files": [{
-   "id": 1,
-   "direct_artifact_ids": [1, 2, 3, 4]
- }],
- "path_fragments": [
-   { "id": 1, "label": "one" },
-   { "id": 2, "label": "two" },
-   { "id": 3, "label": "two.d" },
-   { "id": 4, "label": "other.d" }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
-	assertError(t, err, `found multiple potential depfiles "two.d", "other.d": [X] []`)
-}
-
-func TestTransitiveInputDepsets(t *testing.T) {
-	// The input aquery for this test comes from a proof-of-concept starlark rule which registers
-	// a single action with many inputs given via a deep depset.
-	const inputString = `
-{
- "artifacts": [
-  { "id": 1, "path_fragment_id": 1 },
-  { "id": 2, "path_fragment_id": 7 },
-  { "id": 3, "path_fragment_id": 8 },
-  { "id": 4, "path_fragment_id": 9 },
-  { "id": 5, "path_fragment_id": 10 },
-  { "id": 6, "path_fragment_id": 11 },
-  { "id": 7, "path_fragment_id": 12 },
-  { "id": 8, "path_fragment_id": 13 },
-  { "id": 9, "path_fragment_id": 14 },
-  { "id": 10, "path_fragment_id": 15 },
-  { "id": 11, "path_fragment_id": 16 },
-  { "id": 12, "path_fragment_id": 17 },
-  { "id": 13, "path_fragment_id": 18 },
-  { "id": 14, "path_fragment_id": 19 },
-  { "id": 15, "path_fragment_id": 20 },
-  { "id": 16, "path_fragment_id": 21 },
-  { "id": 17, "path_fragment_id": 22 },
-  { "id": 18, "path_fragment_id": 23 },
-  { "id": 19, "path_fragment_id": 24 },
-  { "id": 20, "path_fragment_id": 25 },
-  { "id": 21, "path_fragment_id": 26 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
-   "mnemonic": "Action",
-   "configuration_id": 1,
-   "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
-   "input_dep_set_ids": [1],
-   "output_ids": [21],
-   "primary_output_id": 21
- }],
- "dep_set_of_files": [
-   { "id": 3, "direct_artifact_ids": [1, 2, 3, 4, 5] },
-   { "id": 4, "direct_artifact_ids": [6, 7, 8, 9, 10] },
-   { "id": 2, "transitive_dep_set_ids": [3, 4], "direct_artifact_ids": [11, 12, 13, 14, 15] },
-   { "id": 5, "direct_artifact_ids": [16, 17, 18, 19] },
-   { "id": 1, "transitive_dep_set_ids": [2, 5], "direct_artifact_ids": [20] }],
- "path_fragments": [
-   { "id": 6, "label": "bazel-out" },
-   { "id": 5, "label": "sourceroot", "parent_id": 6 },
-   { "id": 4, "label": "k8-fastbuild", "parent_id": 5 },
-   { "id": 3, "label": "bin", "parent_id": 4 },
-   { "id": 2, "label": "testpkg", "parent_id": 3 },
-   { "id": 1, "label": "test_1", "parent_id": 2 },
-   { "id": 7, "label": "test_2", "parent_id": 2 },
-   { "id": 8, "label": "test_3", "parent_id": 2 },
-   { "id": 9, "label": "test_4", "parent_id": 2 },
-   { "id": 10, "label": "test_5", "parent_id": 2 },
-   { "id": 11, "label": "test_6", "parent_id": 2 },
-   { "id": 12, "label": "test_7", "parent_id": 2 },
-	 { "id": 13, "label": "test_8", "parent_id": 2 },
-   { "id": 14, "label": "test_9", "parent_id": 2 },
-   { "id": 15, "label": "test_10", "parent_id": 2 },
-   { "id": 16, "label": "test_11", "parent_id": 2 },
-   { "id": 17, "label": "test_12", "parent_id": 2 },
-   { "id": 18, "label": "test_13", "parent_id": 2 },
-   { "id": 19, "label": "test_14", "parent_id": 2 },
-   { "id": 20, "label": "test_15", "parent_id": 2 },
-   { "id": 21, "label": "test_16", "parent_id": 2 },
-   { "id": 22, "label": "test_17", "parent_id": 2 },
-   { "id": 23, "label": "test_18", "parent_id": 2 },
-   { "id": 24, "label": "test_19", "parent_id": 2 },
-   { "id": 25, "label": "test_root", "parent_id": 2 },
-   { "id": 26,"label": "test_out", "parent_id": 2 }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
-
-	expectedBuildStatements := []*BuildStatement{
-		&BuildStatement{
-			Command:      "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
-			OutputPaths:  []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
-			Mnemonic:     "Action",
-			SymlinkPaths: []string{},
-		},
-	}
-	assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
-
-	// Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
-	// are given via a deep depset, but the depset is flattened when returned as a
-	// BuildStatement slice.
-	var expectedFlattenedInputs []string
-	for i := 1; i < 20; i++ {
-		expectedFlattenedInputs = append(expectedFlattenedInputs, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
-	}
-	expectedFlattenedInputs = append(expectedFlattenedInputs, "bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root")
-
-	actualDepsetHashes := actualbuildStatements[0].InputDepsetHashes
-	actualFlattenedInputs := flattenDepsets(actualDepsetHashes, actualDepsets)
-	if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
-		t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
-	}
-}
-
-func TestSymlinkTree(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 },
-   { "id": 2, "path_fragment_id": 2 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "SymlinkTree",
-   "configuration_id": 1,
-   "input_dep_set_ids": [1],
-   "output_ids": [2],
-   "primary_output_id": 2,
-   "execution_platform": "//build/bazel/platforms:linux_x86_64"
- }],
- "path_fragments": [
-   { "id": 1, "label": "foo.manifest" },
-   { "id": 2, "label": "foo.runfiles/MANIFEST" }],
- "dep_set_of_files": [
-   { "id": 1, "direct_artifact_ids": [1] }]
-}
-`
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-	assertBuildStatements(t, []*BuildStatement{
-		&BuildStatement{
-			Command:      "",
-			OutputPaths:  []string{"foo.runfiles/MANIFEST"},
-			Mnemonic:     "SymlinkTree",
-			InputPaths:   []string{"foo.manifest"},
-			SymlinkPaths: []string{},
-		},
-	}, actual)
-}
-
-func TestBazelToolsRemovalFromInputDepsets(t *testing.T) {
-	const inputString = `{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 10 },
-   { "id": 2, "path_fragment_id": 20 },
-   { "id": 3, "path_fragment_id": 30 },
-   { "id": 4, "path_fragment_id": 40 }],
- "dep_set_of_files": [{
-   "id": 1111,
-   "direct_artifact_ids": [3 , 4]
- }, {
-   "id": 2222,
-   "direct_artifact_ids": [3]
- }],
- "actions": [{
-   "target_id": 100,
-   "action_key": "x",
-   "input_dep_set_ids": [1111, 2222],
-   "mnemonic": "x",
-   "arguments": ["bogus", "command"],
-   "output_ids": [2],
-   "primary_output_id": 1
- }],
- "path_fragments": [
-   { "id": 10, "label": "input" },
-   { "id": 20, "label": "output" },
-   { "id": 30, "label": "dep1", "parent_id": 50 },
-   { "id": 40, "label": "dep2", "parent_id": 60 },
-   { "id": 50, "label": "bazel_tools", "parent_id": 60 },
-   { "id": 60, "label": ".."}
- ]
-}`
-	/* depsets
-	       1111  2222
-	       /  \   |
-	../dep2    ../bazel_tools/dep1
-	*/
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if len(actualDepsets) != 1 {
-		t.Errorf("expected 1 depset but found %#v", actualDepsets)
-		return
-	}
-	dep2Found := false
-	for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
-		if dep == "../bazel_tools/dep1" {
-			t.Errorf("dependency %s expected to be removed but still exists", dep)
-		} else if dep == "../dep2" {
-			dep2Found = true
-		}
-	}
-	if !dep2Found {
-		t.Errorf("dependency ../dep2 expected but not found")
-	}
-
-	expectedBuildStatement := &BuildStatement{
-		Command:      "bogus command",
-		OutputPaths:  []string{"output"},
-		Mnemonic:     "x",
-		SymlinkPaths: []string{},
-	}
-	buildStatementFound := false
-	for _, actualBuildStatement := range actualBuildStatements {
-		if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
-			buildStatementFound = true
-			break
-		}
-	}
-	if !buildStatementFound {
-		t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
-		return
-	}
-}
-
-func TestBazelToolsRemovalFromTargets(t *testing.T) {
-	const inputString = `{
- "artifacts": [{ "id": 1, "path_fragment_id": 10 }],
- "targets": [
-   { "id": 100, "label": "targetX" },
-   { "id": 200, "label": "@bazel_tools//tool_y" }
-],
- "actions": [{
-   "target_id": 100,
-   "action_key": "actionX",
-   "arguments": ["bogus", "command"],
-   "mnemonic" : "x",
-   "output_ids": [1]
- }, {
-   "target_id": 200,
-   "action_key": "y"
- }],
- "path_fragments": [{ "id": 10, "label": "outputX"}]
-}`
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if len(actualDepsets) != 0 {
-		t.Errorf("expected 0 depset but found %#v", actualDepsets)
-		return
-	}
-	expectedBuildStatement := &BuildStatement{
-		Command:      "bogus command",
-		OutputPaths:  []string{"outputX"},
-		Mnemonic:     "x",
-		SymlinkPaths: []string{},
-	}
-	buildStatementFound := false
-	for _, actualBuildStatement := range actualBuildStatements {
-		if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
-			buildStatementFound = true
-			break
-		}
-	}
-	if !buildStatementFound {
-		t.Errorf("expected but missing %#v in %#v build statements", expectedBuildStatement, len(actualBuildStatements))
-		return
-	}
-}
-
-func TestBazelToolsRemovalFromTransitiveInputDepsets(t *testing.T) {
-	const inputString = `{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 10 },
-   { "id": 2, "path_fragment_id": 20 },
-   { "id": 3, "path_fragment_id": 30 }],
- "dep_set_of_files": [{
-   "id": 1111,
-   "transitive_dep_set_ids": [2222]
- }, {
-   "id": 2222,
-   "direct_artifact_ids": [3]
- }, {
-   "id": 3333,
-   "direct_artifact_ids": [3]
- }, {
-   "id": 4444,
-   "transitive_dep_set_ids": [3333]
- }],
- "actions": [{
-   "target_id": 100,
-   "action_key": "x",
-   "input_dep_set_ids": [1111, 4444],
-   "mnemonic": "x",
-   "arguments": ["bogus", "command"],
-   "output_ids": [2],
-   "primary_output_id": 1
- }],
- "path_fragments": [
-   { "id": 10, "label": "input" },
-   { "id": 20, "label": "output" },
-   { "id": 30, "label": "dep", "parent_id": 50 },
-   { "id": 50, "label": "bazel_tools", "parent_id": 60 },
-   { "id": 60, "label": ".."}
- ]
-}`
-	/* depsets
-	    1111    4444
-	     ||      ||
-	    2222    3333
-	      |      |
-	../bazel_tools/dep
-	Note: in dep_set_of_files:
-	  1111 appears BEFORE its dependency,2222 while
-	  4444 appears AFTER its dependency 3333
-	and this test shows that that order doesn't affect empty depset pruning
-	*/
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if len(actualDepsets) != 0 {
-		t.Errorf("expected 0 depsets but found %#v", actualDepsets)
-		return
-	}
-
-	expectedBuildStatement := &BuildStatement{
-		Command:     "bogus command",
-		OutputPaths: []string{"output"},
-		Mnemonic:    "x",
-	}
-	buildStatementFound := false
-	for _, actualBuildStatement := range actualBuildStatements {
-		if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
-			buildStatementFound = true
-			break
-		}
-	}
-	if !buildStatementFound {
-		t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
-		return
-	}
-}
-
-func TestMiddlemenAction(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 },
-   { "id": 2, "path_fragment_id": 2 },
-   { "id": 3, "path_fragment_id": 3 },
-   { "id": 4, "path_fragment_id": 4 },
-   { "id": 5, "path_fragment_id": 5 },
-   { "id": 6, "path_fragment_id": 6 }],
- "path_fragments": [
-   { "id": 1, "label": "middleinput_one" },
-   { "id": 2, "label": "middleinput_two" },
-   { "id": 3, "label": "middleman_artifact" },
-   { "id": 4, "label": "maininput_one" },
-   { "id": 5, "label": "maininput_two" },
-   { "id": 6, "label": "output" }],
- "dep_set_of_files": [
-   { "id": 1, "direct_artifact_ids": [1, 2] },
-   { "id": 2, "direct_artifact_ids": [3, 4, 5] }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "Middleman",
-   "arguments": ["touch", "foo"],
-   "input_dep_set_ids": [1],
-   "output_ids": [3],
-   "primary_output_id": 3
- }, {
-   "target_id": 2,
-   "action_key": "y",
-   "mnemonic": "Main action",
-   "arguments": ["touch", "foo"],
-   "input_dep_set_ids": [2],
-   "output_ids": [6],
-   "primary_output_id": 6
- }]
-}`
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actualBuildStatements, actualDepsets, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-	if expected := 2; len(actualBuildStatements) != expected {
-		t.Fatalf("Expected %d build statements, got %d %#v", expected, len(actualBuildStatements), actualBuildStatements)
-		return
-	}
-
-	expectedDepsetFiles := [][]string{
-		{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"},
-		{"middleinput_one", "middleinput_two"},
-	}
-	assertFlattenedDepsets(t, actualDepsets, expectedDepsetFiles)
-
-	bs := actualBuildStatements[0]
-	if len(bs.InputPaths) > 0 {
-		t.Errorf("Expected main action raw inputs to be empty, but got %q", bs.InputPaths)
-	}
-
-	expectedOutputs := []string{"output"}
-	if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
-		t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
-	}
-
-	expectedFlattenedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
-	actualFlattenedInputs := flattenDepsets(bs.InputDepsetHashes, actualDepsets)
-
-	if !reflect.DeepEqual(actualFlattenedInputs, expectedFlattenedInputs) {
-		t.Errorf("Expected flattened inputs %v, but got %v", expectedFlattenedInputs, actualFlattenedInputs)
-	}
-
-	bs = actualBuildStatements[1]
-	if bs != nil {
-		t.Errorf("Expected nil action for skipped")
-	}
-}
-
-// Returns the contents of given depsets in concatenated post order.
-func flattenDepsets(depsetHashesToFlatten []string, allDepsets []AqueryDepset) []string {
-	depsetsByHash := map[string]AqueryDepset{}
-	for _, depset := range allDepsets {
-		depsetsByHash[depset.ContentHash] = depset
-	}
-	var result []string
-	for _, depsetId := range depsetHashesToFlatten {
-		result = append(result, flattenDepset(depsetId, depsetsByHash)...)
-	}
-	return result
-}
-
-// Returns the contents of a given depset in post order.
-func flattenDepset(depsetHashToFlatten string, allDepsets map[string]AqueryDepset) []string {
-	depset := allDepsets[depsetHashToFlatten]
-	var result []string
-	for _, depsetId := range depset.TransitiveDepSetHashes {
-		result = append(result, flattenDepset(depsetId, allDepsets)...)
-	}
-	result = append(result, depset.DirectArtifacts...)
-	return result
-}
-
-func assertFlattenedDepsets(t *testing.T, actualDepsets []AqueryDepset, expectedDepsetFiles [][]string) {
-	t.Helper()
-	if len(actualDepsets) != len(expectedDepsetFiles) {
-		t.Errorf("Expected %d depsets, but got %d depsets", len(expectedDepsetFiles), len(actualDepsets))
-	}
-	for i, actualDepset := range actualDepsets {
-		actualFlattenedInputs := flattenDepsets([]string{actualDepset.ContentHash}, actualDepsets)
-		if !reflect.DeepEqual(actualFlattenedInputs, expectedDepsetFiles[i]) {
-			t.Errorf("Expected depset files: %v, but got %v", expectedDepsetFiles[i], actualFlattenedInputs)
-		}
-	}
-}
-
-func TestSimpleSymlink(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 3 },
-   { "id": 2, "path_fragment_id": 5 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "Symlink",
-   "input_dep_set_ids": [1],
-   "output_ids": [2],
-   "primary_output_id": 2
- }],
- "dep_set_of_files": [
-   { "id": 1, "direct_artifact_ids": [1] }],
- "path_fragments": [
-   { "id": 1, "label": "one" },
-   { "id": 2, "label": "file_subdir", "parent_id": 1 },
-   { "id": 3, "label": "file", "parent_id": 2 },
-   { "id": 4, "label": "symlink_subdir", "parent_id": 1 },
-   { "id": 5, "label": "symlink", "parent_id": 4 }]
-}`
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-
-	expectedBuildStatements := []*BuildStatement{
-		&BuildStatement{
-			Command: "mkdir -p one/symlink_subdir && " +
-				"rm -f one/symlink_subdir/symlink && " +
-				"ln -sf $PWD/one/file_subdir/file one/symlink_subdir/symlink",
-			InputPaths:   []string{"one/file_subdir/file"},
-			OutputPaths:  []string{"one/symlink_subdir/symlink"},
-			SymlinkPaths: []string{"one/symlink_subdir/symlink"},
-			Mnemonic:     "Symlink",
-		},
-	}
-	assertBuildStatements(t, actual, expectedBuildStatements)
-}
-
-func TestSymlinkQuotesPaths(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 3 },
-   { "id": 2, "path_fragment_id": 5 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "SolibSymlink",
-   "input_dep_set_ids": [1],
-   "output_ids": [2],
-   "primary_output_id": 2
- }],
- "dep_set_of_files": [
-   { "id": 1, "direct_artifact_ids": [1] }],
- "path_fragments": [
-   { "id": 1, "label": "one" },
-   { "id": 2, "label": "file subdir", "parent_id": 1 },
-   { "id": 3, "label": "file", "parent_id": 2 },
-   { "id": 4, "label": "symlink subdir", "parent_id": 1 },
-   { "id": 5, "label": "symlink", "parent_id": 4 }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-
-	expectedBuildStatements := []*BuildStatement{
-		&BuildStatement{
-			Command: "mkdir -p 'one/symlink subdir' && " +
-				"rm -f 'one/symlink subdir/symlink' && " +
-				"ln -sf $PWD/'one/file subdir/file' 'one/symlink subdir/symlink'",
-			InputPaths:   []string{"one/file subdir/file"},
-			OutputPaths:  []string{"one/symlink subdir/symlink"},
-			SymlinkPaths: []string{"one/symlink subdir/symlink"},
-			Mnemonic:     "SolibSymlink",
-		},
-	}
-	assertBuildStatements(t, expectedBuildStatements, actual)
-}
-
-func TestSymlinkMultipleInputs(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 },
-   { "id": 2, "path_fragment_id": 2 },
-   { "id": 3, "path_fragment_id": 3 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "action_x",
-   "mnemonic": "Symlink",
-   "input_dep_set_ids": [1],
-   "output_ids": [3],
-   "primary_output_id": 3
- }],
- "dep_set_of_files": [{ "id": 1, "direct_artifact_ids": [1,2] }],
- "path_fragments": [
-   { "id": 1, "label": "file" },
-   { "id": 2, "label": "other_file" },
-   { "id": 3, "label": "symlink" }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
-	assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]: [Symlink] []`)
-}
-
-func TestSymlinkMultipleOutputs(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 },
-   { "id": 3, "path_fragment_id": 3 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "Symlink",
-   "input_dep_set_ids": [1],
-   "output_ids": [2,3],
-   "primary_output_id": 2
- }],
- "dep_set_of_files": [
-   { "id": 1, "direct_artifact_ids": [1] }],
- "path_fragments": [
-   { "id": 1, "label": "file" },
-   { "id": 2, "label": "symlink" },
-   { "id": 3,  "label": "other_symlink" }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
-	assertError(t, err, "undefined outputId 2: [Symlink] []")
-}
-
-func TestTemplateExpandActionSubstitutions(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [{
-   "id": 1,
-   "path_fragment_id": 1
- }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "TemplateExpand",
-   "configuration_id": 1,
-   "output_ids": [1],
-   "primary_output_id": 1,
-   "execution_platform": "//build/bazel/platforms:linux_x86_64",
-   "template_content": "Test template substitutions: %token1%, %python_binary%",
-   "substitutions": [
-     { "key": "%token1%", "value": "abcd" },
-     { "key": "%python_binary%", "value": "python3" }]
- }],
- "path_fragments": [
-   { "id": 1, "label": "template_file" }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-
-	expectedBuildStatements := []*BuildStatement{
-		&BuildStatement{
-			Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
-				"chmod a+x template_file'",
-			OutputPaths:  []string{"template_file"},
-			Mnemonic:     "TemplateExpand",
-			SymlinkPaths: []string{},
-		},
-	}
-	assertBuildStatements(t, expectedBuildStatements, actual)
-}
-
-func TestTemplateExpandActionNoOutput(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "TemplateExpand",
-   "configuration_id": 1,
-   "primary_output_id": 1,
-   "execution_platform": "//build/bazel/platforms:linux_x86_64",
-   "templateContent": "Test template substitutions: %token1%, %python_binary%",
-   "substitutions": [
-     { "key": "%token1%", "value": "abcd" },
-     { "key": "%python_binary%", "value": "python3" }]
- }],
- "path_fragments": [
-   { "id": 1, "label": "template_file" }]
-}`
-
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	_, _, err = AqueryBuildStatements(data, &metrics.EventHandler{})
-	assertError(t, err, `Expect 1 output to template expand action, got: output []: [TemplateExpand] []`)
-}
-
-func TestFileWrite(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "FileWrite",
-   "configuration_id": 1,
-   "output_ids": [1],
-   "primary_output_id": 1,
-   "execution_platform": "//build/bazel/platforms:linux_x86_64",
-   "file_contents": "file data\n"
- }],
- "path_fragments": [
-   { "id": 1, "label": "foo.manifest" }]
-}
-`
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-	assertBuildStatements(t, []*BuildStatement{
-		&BuildStatement{
-			OutputPaths:  []string{"foo.manifest"},
-			Mnemonic:     "FileWrite",
-			FileContents: "file data\n",
-			SymlinkPaths: []string{},
-		},
-	}, actual)
-}
-
-func TestSourceSymlinkManifest(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 }],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "SourceSymlinkManifest",
-   "configuration_id": 1,
-   "output_ids": [1],
-   "primary_output_id": 1,
-   "execution_platform": "//build/bazel/platforms:linux_x86_64",
-   "file_contents": "symlink target\n"
- }],
- "path_fragments": [
-   { "id": 1, "label": "foo.manifest" }]
-}
-`
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-	assertBuildStatements(t, []*BuildStatement{
-		&BuildStatement{
-			OutputPaths:  []string{"foo.manifest"},
-			Mnemonic:     "SourceSymlinkManifest",
-			SymlinkPaths: []string{},
-		},
-	}, actual)
-}
-
-func TestUnresolvedSymlink(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 }
- ],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "UnresolvedSymlink",
-   "configuration_id": 1,
-   "output_ids": [1],
-   "primary_output_id": 1,
-   "execution_platform": "//build/bazel/platforms:linux_x86_64",
-   "unresolved_symlink_target": "symlink/target"
- }],
- "path_fragments": [
-   { "id": 1, "label": "path/to/symlink" }
- ]
-}
-`
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-	assertBuildStatements(t, []*BuildStatement{{
-		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf symlink/target path/to/symlink",
-		OutputPaths:  []string{"path/to/symlink"},
-		Mnemonic:     "UnresolvedSymlink",
-		SymlinkPaths: []string{"path/to/symlink"},
-	}}, actual)
-}
-
-func TestUnresolvedSymlinkBazelSandwich(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 }
- ],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "UnresolvedSymlink",
-   "configuration_id": 1,
-   "output_ids": [1],
-   "primary_output_id": 1,
-   "execution_platform": "//build/bazel/platforms:linux_x86_64",
-   "unresolved_symlink_target": "bazel_sandwich:{\"target\":\"target/product/emulator_x86_64/system\"}"
- }],
- "path_fragments": [
-   { "id": 1, "label": "path/to/symlink" }
- ]
-}
-`
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-	assertBuildStatements(t, []*BuildStatement{{
-		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink",
-		OutputPaths:  []string{"path/to/symlink"},
-		Mnemonic:     "UnresolvedSymlink",
-		SymlinkPaths: []string{"path/to/symlink"},
-		ImplicitDeps: []string{"target/product/emulator_x86_64/system"},
-	}}, actual)
-}
-
-func TestUnresolvedSymlinkBazelSandwichWithAlternativeDeps(t *testing.T) {
-	const inputString = `
-{
- "artifacts": [
-   { "id": 1, "path_fragment_id": 1 }
- ],
- "actions": [{
-   "target_id": 1,
-   "action_key": "x",
-   "mnemonic": "UnresolvedSymlink",
-   "configuration_id": 1,
-   "output_ids": [1],
-   "primary_output_id": 1,
-   "execution_platform": "//build/bazel/platforms:linux_x86_64",
-   "unresolved_symlink_target": "bazel_sandwich:{\"depend_on_target\":false,\"implicit_deps\":[\"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp\"],\"target\":\"target/product/emulator_x86_64/system\"}"
- }],
- "path_fragments": [
-   { "id": 1, "label": "path/to/symlink" }
- ]
-}
-`
-	data, err := JsonToActionGraphContainer(inputString)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	actual, _, err := AqueryBuildStatements(data, &metrics.EventHandler{})
-	if err != nil {
-		t.Errorf("Unexpected error %q", err)
-		return
-	}
-	assertBuildStatements(t, []*BuildStatement{{
-		Command:      "mkdir -p path/to && rm -f path/to/symlink && ln -sf {DOTDOTS_TO_OUTPUT_ROOT}../../target/product/emulator_x86_64/system path/to/symlink",
-		OutputPaths:  []string{"path/to/symlink"},
-		Mnemonic:     "UnresolvedSymlink",
-		SymlinkPaths: []string{"path/to/symlink"},
-		// Note that the target of the symlink, target/product/emulator_x86_64/system, is not listed here
-		ImplicitDeps: []string{"target/product/emulator_x86_64/obj/PACKAGING/systemimage_intermediates/staging_dir.stamp"},
-	}}, actual)
-}
-
-func assertError(t *testing.T, err error, expected string) {
-	t.Helper()
-	if err == nil {
-		t.Errorf("expected error '%s', but got no error", expected)
-	} else if err.Error() != expected {
-		t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
-	}
-}
-
-// Asserts that the given actual build statements match the given expected build statements.
-// Build statement equivalence is determined using buildStatementEquals.
-func assertBuildStatements(t *testing.T, expected []*BuildStatement, actual []*BuildStatement) {
-	t.Helper()
-	if len(expected) != len(actual) {
-		t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
-			len(expected), len(actual), expected, actual)
-		return
-	}
-	type compareFn = func(i int, j int) bool
-	byCommand := func(slice []*BuildStatement) compareFn {
-		return func(i int, j int) bool {
-			if slice[i] == nil {
-				return false
-			} else if slice[j] == nil {
-				return false
-			}
-			return slice[i].Command < slice[j].Command
-		}
-	}
-	sort.SliceStable(expected, byCommand(expected))
-	sort.SliceStable(actual, byCommand(actual))
-	for i, actualStatement := range actual {
-		expectedStatement := expected[i]
-		if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
-			t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
-				differingField, actualStatement, expectedStatement)
-			return
-		}
-	}
-}
-
-func buildStatementEquals(first *BuildStatement, second *BuildStatement) string {
-	if (first == nil) != (second == nil) {
-		return "Nil"
-	}
-	if first.Mnemonic != second.Mnemonic {
-		return "Mnemonic"
-	}
-	if first.Command != second.Command {
-		return "Command"
-	}
-	// Ordering is significant for environment variables.
-	if !reflect.DeepEqual(first.Env, second.Env) {
-		return "Env"
-	}
-	// Ordering is irrelevant for input and output paths, so compare sets.
-	if !reflect.DeepEqual(sortedStrings(first.InputPaths), sortedStrings(second.InputPaths)) {
-		return "InputPaths"
-	}
-	if !reflect.DeepEqual(sortedStrings(first.OutputPaths), sortedStrings(second.OutputPaths)) {
-		return "OutputPaths"
-	}
-	if !reflect.DeepEqual(sortedStrings(first.SymlinkPaths), sortedStrings(second.SymlinkPaths)) {
-		return "SymlinkPaths"
-	}
-	if !reflect.DeepEqual(sortedStrings(first.ImplicitDeps), sortedStrings(second.ImplicitDeps)) {
-		return "ImplicitDeps"
-	}
-	if first.Depfile != second.Depfile {
-		return "Depfile"
-	}
-	return ""
-}
-
-func sortedStrings(stringSlice []string) []string {
-	sorted := make([]string, len(stringSlice))
-	copy(sorted, stringSlice)
-	sort.Strings(sorted)
-	return sorted
-}
-
-// Transform the json format to ActionGraphContainer
-func JsonToActionGraphContainer(inputString string) ([]byte, error) {
-	var aqueryProtoResult analysis_v2_proto.ActionGraphContainer
-	err := json.Unmarshal([]byte(inputString), &aqueryProtoResult)
-	if err != nil {
-		return []byte(""), err
-	}
-	data, _ := proto.Marshal(&aqueryProtoResult)
-	return data, err
-}
diff --git a/bazel/bazel_proxy.go b/bazel/bazel_proxy.go
deleted file mode 100644
index 229818d..0000000
--- a/bazel/bazel_proxy.go
+++ /dev/null
@@ -1,237 +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 bazel
-
-import (
-	"bytes"
-	"encoding/gob"
-	"fmt"
-	"net"
-	os_lib "os"
-	"os/exec"
-	"path/filepath"
-	"strings"
-	"time"
-)
-
-// Logs events of ProxyServer.
-type ServerLogger interface {
-	Fatal(v ...interface{})
-	Fatalf(format string, v ...interface{})
-	Println(v ...interface{})
-}
-
-// CmdRequest is a request to the Bazel Proxy server.
-type CmdRequest struct {
-	// Args to the Bazel command.
-	Argv []string
-	// Environment variables to pass to the Bazel invocation. Strings should be of
-	// the form "KEY=VALUE".
-	Env []string
-}
-
-// CmdResponse is a response from the Bazel Proxy server.
-type CmdResponse struct {
-	Stdout      string
-	Stderr      string
-	ErrorString string
-}
-
-// ProxyClient is a client which can issue Bazel commands to the Bazel
-// proxy server. Requests are issued (and responses received) via a unix socket.
-// See ProxyServer for more details.
-type ProxyClient struct {
-	outDir string
-}
-
-// ProxyServer is a server which runs as a background goroutine. Each
-// request to the server describes a Bazel command which the server should run.
-// The server then issues the Bazel command, and returns a response describing
-// the stdout/stderr of the command.
-// Client-server communication is done via a unix socket under the output
-// directory.
-// The server is intended to circumvent sandboxing for subprocesses of the
-// build. The build orchestrator (soong_ui) can launch a server to exist outside
-// of sandboxing, and sandboxed processes (such as soong_build) can issue
-// bazel commands through this socket tunnel. This allows a sandboxed process
-// to issue bazel requests to a bazel that resides outside of sandbox. This
-// is particularly useful to maintain a persistent Bazel server which lives
-// past the duration of a single build.
-// The ProxyServer will only live as long as soong_ui does; the
-// underlying Bazel server will live past the duration of the build.
-type ProxyServer struct {
-	logger          ServerLogger
-	outDir          string
-	workspaceDir    string
-	bazeliskVersion string
-	// The server goroutine will listen on this channel and stop handling requests
-	// once it is written to.
-	done chan struct{}
-}
-
-// NewProxyClient is a constructor for a ProxyClient.
-func NewProxyClient(outDir string) *ProxyClient {
-	return &ProxyClient{
-		outDir: outDir,
-	}
-}
-
-func unixSocketPath(outDir string) string {
-	return filepath.Join(outDir, "bazelsocket.sock")
-}
-
-// IssueCommand issues a request to the Bazel Proxy Server to issue a Bazel
-// request. Returns a response describing the output from the Bazel process
-// (if the Bazel process had an error, then the response will include an error).
-// Returns an error if there was an issue with the connection to the Bazel Proxy
-// server.
-func (b *ProxyClient) IssueCommand(req CmdRequest) (CmdResponse, error) {
-	var resp CmdResponse
-	var err error
-	// Check for connections every 1 second. This is chosen to be a relatively
-	// short timeout, because the proxy server should accept requests quite
-	// quickly.
-	d := net.Dialer{Timeout: 1 * time.Second}
-	var conn net.Conn
-	conn, err = d.Dial("unix", unixSocketPath(b.outDir))
-	if err != nil {
-		return resp, err
-	}
-	defer conn.Close()
-
-	enc := gob.NewEncoder(conn)
-	if err = enc.Encode(req); err != nil {
-		return resp, err
-	}
-	dec := gob.NewDecoder(conn)
-	err = dec.Decode(&resp)
-	return resp, err
-}
-
-// NewProxyServer is a constructor for a ProxyServer.
-func NewProxyServer(logger ServerLogger, outDir string, workspaceDir string, bazeliskVersion string) *ProxyServer {
-	if len(bazeliskVersion) > 0 {
-		logger.Println("** Using Bazelisk for this build, due to env var USE_BAZEL_VERSION=" + bazeliskVersion + " **")
-	}
-
-	return &ProxyServer{
-		logger:          logger,
-		outDir:          outDir,
-		workspaceDir:    workspaceDir,
-		done:            make(chan struct{}),
-		bazeliskVersion: bazeliskVersion,
-	}
-}
-
-func ExecBazel(bazelPath string, workspaceDir string, request CmdRequest) (stdout []byte, stderr []byte, cmdErr error) {
-	bazelCmd := exec.Command(bazelPath, request.Argv...)
-	bazelCmd.Dir = workspaceDir
-	bazelCmd.Env = request.Env
-
-	stderrBuffer := &bytes.Buffer{}
-	bazelCmd.Stderr = stderrBuffer
-
-	if output, err := bazelCmd.Output(); err != nil {
-		cmdErr = fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
-			err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderrBuffer)
-	} else {
-		stdout = output
-	}
-	stderr = stderrBuffer.Bytes()
-	return
-}
-
-func (b *ProxyServer) handleRequest(conn net.Conn) error {
-	defer conn.Close()
-
-	dec := gob.NewDecoder(conn)
-	var req CmdRequest
-	if err := dec.Decode(&req); err != nil {
-		return fmt.Errorf("Error decoding request: %s", err)
-	}
-
-	if len(b.bazeliskVersion) > 0 {
-		req.Env = append(req.Env, "USE_BAZEL_VERSION="+b.bazeliskVersion)
-	}
-	stdout, stderr, cmdErr := ExecBazel("./build/bazel/bin/bazel", b.workspaceDir, req)
-	errorString := ""
-	if cmdErr != nil {
-		errorString = cmdErr.Error()
-	}
-
-	resp := CmdResponse{string(stdout), string(stderr), errorString}
-	enc := gob.NewEncoder(conn)
-	if err := enc.Encode(&resp); err != nil {
-		return fmt.Errorf("Error encoding response: %s", err)
-	}
-	return nil
-}
-
-func (b *ProxyServer) listenUntilClosed(listener net.Listener) error {
-	for {
-		// Check for connections every 1 second. This is a blocking operation, so
-		// if the server is closed, the goroutine will not fully close until this
-		// deadline is reached. Thus, this deadline is short (but not too short
-		// so that the routine churns).
-		listener.(*net.UnixListener).SetDeadline(time.Now().Add(time.Second))
-		conn, err := listener.Accept()
-
-		select {
-		case <-b.done:
-			return nil
-		default:
-		}
-
-		if err != nil {
-			if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
-				// Timeout is normal and expected while waiting for client to establish
-				// a connection.
-				continue
-			} else {
-				b.logger.Fatalf("Listener error: %s", err)
-			}
-		}
-
-		err = b.handleRequest(conn)
-		if err != nil {
-			b.logger.Fatal(err)
-		}
-	}
-}
-
-// Start initializes the server unix socket and (in a separate goroutine)
-// handles requests on the socket until the server is closed. Returns an error
-// if a failure occurs during initialization. Will log any post-initialization
-// errors to the server's logger.
-func (b *ProxyServer) Start() error {
-	unixSocketAddr := unixSocketPath(b.outDir)
-	if err := os_lib.RemoveAll(unixSocketAddr); err != nil {
-		return fmt.Errorf("couldn't remove socket '%s': %s", unixSocketAddr, err)
-	}
-	listener, err := net.Listen("unix", unixSocketAddr)
-
-	if err != nil {
-		return fmt.Errorf("error listening on socket '%s': %s", unixSocketAddr, err)
-	}
-
-	go b.listenUntilClosed(listener)
-	return nil
-}
-
-// Close shuts down the server. This will stop the server from listening for
-// additional requests.
-func (b *ProxyServer) Close() {
-	b.done <- struct{}{}
-}
diff --git a/bazel/constants.go b/bazel/constants.go
deleted file mode 100644
index b10f256..0000000
--- a/bazel/constants.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package bazel
-
-type RunName string
-
-// Below is a list bazel execution run names used through out the
-// Platform Build systems. Each run name represents an unique key
-// to query the bazel metrics.
-const (
-	// Perform a bazel build of the phony root to generate symlink forests
-	// for dependencies of the bazel build.
-	BazelBuildPhonyRootRunName = RunName("bazel-build-phony-root")
-
-	// Perform aquery of the bazel build root to retrieve action information.
-	AqueryBuildRootRunName = RunName("aquery-buildroot")
-
-	// Perform cquery of the Bazel build root and its dependencies.
-	CqueryBuildRootRunName = RunName("cquery-buildroot")
-
-	// Run bazel as a ninja executer
-	BazelNinjaExecRunName = RunName("bazel-ninja-exec")
-
-	SoongInjectionDirName = "soong_injection"
-
-	GeneratedBazelFileWarning = "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT."
-)
-
-// String returns the name of the run.
-func (c RunName) String() string {
-	return string(c)
-}
diff --git a/bazel/cquery/Android.bp b/bazel/cquery/Android.bp
deleted file mode 100644
index 74f7721..0000000
--- a/bazel/cquery/Android.bp
+++ /dev/null
@@ -1,17 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
-    name: "soong-cquery",
-    pkgPath: "android/soong/bazel/cquery",
-    srcs: [
-        "request_type.go",
-    ],
-    pluginFor: [
-        "soong_build",
-    ],
-    testSrcs: [
-        "request_type_test.go",
-    ],
-}
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
deleted file mode 100644
index 791c6bc..0000000
--- a/bazel/cquery/request_type.go
+++ /dev/null
@@ -1,426 +0,0 @@
-package cquery
-
-import (
-	"encoding/json"
-	"fmt"
-	"strings"
-)
-
-var (
-	GetOutputFiles      = &getOutputFilesRequestType{}
-	GetCcInfo           = &getCcInfoType{}
-	GetApexInfo         = &getApexInfoType{}
-	GetCcUnstrippedInfo = &getCcUnstrippedInfoType{}
-	GetPrebuiltFileInfo = &getPrebuiltFileInfo{}
-)
-
-type CcAndroidMkInfo struct {
-	LocalStaticLibs      []string
-	LocalWholeStaticLibs []string
-	LocalSharedLibs      []string
-}
-
-type CcInfo struct {
-	CcAndroidMkInfo
-	OutputFiles          []string
-	CcObjectFiles        []string
-	CcSharedLibraryFiles []string
-	CcStaticLibraryFiles []string
-	Includes             []string
-	SystemIncludes       []string
-	Headers              []string
-	// Archives owned by the current target (not by its dependencies). These will
-	// be a subset of OutputFiles. (or static libraries, this will be equal to OutputFiles,
-	// but general cc_library will also have dynamic libraries in output files).
-	RootStaticArchives []string
-	// Dynamic libraries (.so files) created by the current target. These will
-	// be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles,
-	// but general cc_library will also have dynamic libraries in output files).
-	RootDynamicLibraries []string
-	TidyFiles            []string
-	TocFile              string
-	UnstrippedOutput     string
-	AbiDiffFiles         []string
-}
-
-type getOutputFilesRequestType struct{}
-
-// Name returns a string name for this request type. Such request type names must be unique,
-// and must only consist of alphanumeric characters.
-func (g getOutputFilesRequestType) Name() string {
-	return "getOutputFiles"
-}
-
-// StarlarkFunctionBody returns a starlark function body to process this request type.
-// The returned string is the body of a Starlark function which obtains
-// all request-relevant information about a target and returns a string containing
-// this information.
-// The function should have the following properties:
-//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
-//   - The return value must be a string.
-//   - The function body should not be indented outside of its own scope.
-func (g getOutputFilesRequestType) StarlarkFunctionBody() string {
-	return "return ', '.join([f.path for f in target.files.to_list()])"
-}
-
-// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
-// The given rawString must correspond to the string output which was created by evaluating the
-// Starlark given in StarlarkFunctionBody.
-func (g getOutputFilesRequestType) ParseResult(rawString string) []string {
-	return splitOrEmpty(rawString, ", ")
-}
-
-type getCcInfoType struct{}
-
-// Name returns a string name for this request type. Such request type names must be unique,
-// and must only consist of alphanumeric characters.
-func (g getCcInfoType) Name() string {
-	return "getCcInfo"
-}
-
-// StarlarkFunctionBody returns a starlark function body to process this request type.
-// The returned string is the body of a Starlark function which obtains
-// all request-relevant information about a target and returns a string containing
-// this information.
-// The function should have the following properties:
-//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
-//   - The return value must be a string.
-//   - The function body should not be indented outside of its own scope.
-func (g getCcInfoType) StarlarkFunctionBody() string {
-	return `
-outputFiles = [f.path for f in target.files.to_list()]
-p = providers(target)
-cc_info = p.get("CcInfo")
-if not cc_info:
-  fail("%s did not provide CcInfo" % id_string)
-
-includes = cc_info.compilation_context.includes.to_list()
-system_includes = cc_info.compilation_context.system_includes.to_list()
-headers = [f.path for f in cc_info.compilation_context.headers.to_list()]
-
-ccObjectFiles = []
-staticLibraries = []
-rootStaticArchives = []
-linker_inputs = cc_info.linking_context.linker_inputs.to_list()
-
-static_info_tag = "//build/bazel/rules/cc:cc_library_static.bzl%CcStaticLibraryInfo"
-if static_info_tag in p:
-  static_info = p[static_info_tag]
-  ccObjectFiles = [f.path for f in static_info.objects]
-  rootStaticArchives = [static_info.root_static_archive.path]
-else:
-  for linker_input in linker_inputs:
-    for library in linker_input.libraries:
-      for object in library.objects:
-        ccObjectFiles += [object.path]
-      if library.static_library:
-        staticLibraries.append(library.static_library.path)
-        if linker_input.owner == target.label:
-          rootStaticArchives.append(library.static_library.path)
-
-sharedLibraries = []
-rootSharedLibraries = []
-
-shared_info_tag = "//build/bazel/rules/cc:cc_library_shared.bzl%CcSharedLibraryOutputInfo"
-stubs_tag = "//build/bazel/rules/cc:cc_stub_library.bzl%CcStubInfo"
-unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
-unstripped = ""
-
-if shared_info_tag in p:
-  shared_info = p[shared_info_tag]
-  path = shared_info.output_file.path
-  sharedLibraries.append(path)
-  rootSharedLibraries += [path]
-  unstripped = path
-  if unstripped_tag in p:
-    unstripped = p[unstripped_tag].unstripped.path
-elif stubs_tag in p:
-  rootSharedLibraries.extend([f.path for f in target.files.to_list()])
-else:
-  for linker_input in linker_inputs:
-    for library in linker_input.libraries:
-      if library.dynamic_library:
-        path = library.dynamic_library.path
-        sharedLibraries.append(path)
-        if linker_input.owner == target.label:
-          rootSharedLibraries.append(path)
-
-toc_file = ""
-toc_file_tag = "//build/bazel/rules/cc:generate_toc.bzl%CcTocInfo"
-if toc_file_tag in p:
-  toc_file = p[toc_file_tag].toc.path
-else:
-  # NOTE: It's OK if there's no ToC, as Soong just uses it for optimization
-  pass
-
-tidy_files = []
-clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
-if clang_tidy_info:
-  tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
-
-abi_diff_files = []
-abi_diff_info = p.get("//build/bazel/rules/abi:abi_dump.bzl%AbiDiffInfo")
-if abi_diff_info:
-  abi_diff_files = [f.path for f in abi_diff_info.diff_files.to_list()]
-
-local_static_libs = []
-local_whole_static_libs = []
-local_shared_libs = []
-androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
-if androidmk_tag in p:
-    androidmk_info = p[androidmk_tag]
-    local_static_libs = androidmk_info.local_static_libs
-    local_whole_static_libs = androidmk_info.local_whole_static_libs
-    local_shared_libs = androidmk_info.local_shared_libs
-
-return json.encode({
-    "OutputFiles": outputFiles,
-    "CcObjectFiles": ccObjectFiles,
-    "CcSharedLibraryFiles": sharedLibraries,
-    "CcStaticLibraryFiles": staticLibraries,
-    "Includes": includes,
-    "SystemIncludes": system_includes,
-    "Headers": headers,
-    "RootStaticArchives": rootStaticArchives,
-    "RootDynamicLibraries": rootSharedLibraries,
-    "TidyFiles": [t for t in tidy_files],
-    "TocFile": toc_file,
-    "UnstrippedOutput": unstripped,
-    "AbiDiffFiles": abi_diff_files,
-    "LocalStaticLibs": [l for l in local_static_libs],
-    "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
-    "LocalSharedLibs": [l for l in local_shared_libs],
-})`
-
-}
-
-// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
-// The given rawString must correspond to the string output which was created by evaluating the
-// Starlark given in StarlarkFunctionBody.
-func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) {
-	var ccInfo CcInfo
-	if err := parseJson(rawString, &ccInfo); err != nil {
-		return ccInfo, err
-	}
-	return ccInfo, nil
-}
-
-// Query Bazel for the artifacts generated by the apex modules.
-type getApexInfoType struct{}
-
-// Name returns a string name for this request type. Such request type names must be unique,
-// and must only consist of alphanumeric characters.
-func (g getApexInfoType) Name() string {
-	return "getApexInfo"
-}
-
-// StarlarkFunctionBody returns a starlark function body to process this request type.
-// The returned string is the body of a Starlark function which obtains
-// all request-relevant information about a target and returns a string containing
-// this information. The function should have the following properties:
-//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
-//   - The return value must be a string.
-//   - The function body should not be indented outside of its own scope.
-func (g getApexInfoType) StarlarkFunctionBody() string {
-	return `
-info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexInfo")
-if not info:
-  fail("%s did not provide ApexInfo" % id_string)
-bundle_key_info = info.bundle_key_info
-container_key_info = info.container_key_info
-
-signed_compressed_output = "" # no .capex if the apex is not compressible, cannot be None as it needs to be json encoded.
-if info.signed_compressed_output:
-    signed_compressed_output = info.signed_compressed_output.path
-
-mk_info = providers(target).get("//build/bazel/rules/apex:apex_info.bzl%ApexMkInfo")
-if not mk_info:
-  fail("%s did not provide ApexMkInfo" % id_string)
-
-tidy_files = []
-clang_tidy_info = providers(target).get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
-if clang_tidy_info:
-    tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
-
-return json.encode({
-    "signed_output": info.signed_output.path,
-    "signed_compressed_output": signed_compressed_output,
-    "unsigned_output": info.unsigned_output.path,
-    "provides_native_libs": [str(lib) for lib in info.provides_native_libs],
-    "requires_native_libs": [str(lib) for lib in info.requires_native_libs],
-    "bundle_key_info": [bundle_key_info.public_key.path, bundle_key_info.private_key.path],
-    "container_key_info": [container_key_info.pem.path, container_key_info.pk8.path, container_key_info.key_name],
-    "package_name": info.package_name,
-    "symbols_used_by_apex": info.symbols_used_by_apex.path,
-    "java_symbols_used_by_apex": info.java_symbols_used_by_apex.path,
-    "backing_libs": info.backing_libs.path,
-    "bundle_file": info.base_with_config_zip.path,
-    "installed_files": info.installed_files.path,
-    "make_modules_to_install": mk_info.make_modules_to_install,
-    "files_info": mk_info.files_info,
-    "tidy_files": [t for t in tidy_files],
-})`
-}
-
-type ApexInfo struct {
-	// From the ApexInfo provider
-	SignedOutput           string   `json:"signed_output"`
-	SignedCompressedOutput string   `json:"signed_compressed_output"`
-	UnsignedOutput         string   `json:"unsigned_output"`
-	ProvidesLibs           []string `json:"provides_native_libs"`
-	RequiresLibs           []string `json:"requires_native_libs"`
-	BundleKeyInfo          []string `json:"bundle_key_info"`
-	ContainerKeyInfo       []string `json:"container_key_info"`
-	PackageName            string   `json:"package_name"`
-	SymbolsUsedByApex      string   `json:"symbols_used_by_apex"`
-	JavaSymbolsUsedByApex  string   `json:"java_symbols_used_by_apex"`
-	BackingLibs            string   `json:"backing_libs"`
-	BundleFile             string   `json:"bundle_file"`
-	InstalledFiles         string   `json:"installed_files"`
-	TidyFiles              []string `json:"tidy_files"`
-
-	// From the ApexMkInfo provider
-	MakeModulesToInstall []string            `json:"make_modules_to_install"`
-	PayloadFilesInfo     []map[string]string `json:"files_info"`
-}
-
-// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
-// The given rawString must correspond to the string output which was created by evaluating the
-// Starlark given in StarlarkFunctionBody.
-func (g getApexInfoType) ParseResult(rawString string) (ApexInfo, error) {
-	var info ApexInfo
-	err := parseJson(rawString, &info)
-	return info, err
-}
-
-// getCcUnstrippedInfoType implements cqueryRequest interface. It handles the
-// interaction with `bazel cquery` to retrieve CcUnstrippedInfo provided
-// by the` cc_binary` and `cc_shared_library` rules.
-type getCcUnstrippedInfoType struct{}
-
-func (g getCcUnstrippedInfoType) Name() string {
-	return "getCcUnstrippedInfo"
-}
-
-func (g getCcUnstrippedInfoType) StarlarkFunctionBody() string {
-	return `
-p = providers(target)
-output_path = target.files.to_list()[0].path
-
-unstripped = output_path
-unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
-if unstripped_tag in p:
-    unstripped_info = p[unstripped_tag]
-    unstripped = unstripped_info.unstripped[0].files.to_list()[0].path
-
-local_static_libs = []
-local_whole_static_libs = []
-local_shared_libs = []
-androidmk_tag = "//build/bazel/rules/cc:cc_library_common.bzl%CcAndroidMkInfo"
-if androidmk_tag in p:
-    androidmk_info = p[androidmk_tag]
-    local_static_libs = androidmk_info.local_static_libs
-    local_whole_static_libs = androidmk_info.local_whole_static_libs
-    local_shared_libs = androidmk_info.local_shared_libs
-
-tidy_files = []
-clang_tidy_info = p.get("//build/bazel/rules/cc:clang_tidy.bzl%ClangTidyInfo")
-if clang_tidy_info:
-    tidy_files = [v.path for v in clang_tidy_info.transitive_tidy_files.to_list()]
-
-return json.encode({
-    "OutputFile":  output_path,
-    "UnstrippedOutput": unstripped,
-    "LocalStaticLibs": [l for l in local_static_libs],
-    "LocalWholeStaticLibs": [l for l in local_whole_static_libs],
-    "LocalSharedLibs": [l for l in local_shared_libs],
-    "TidyFiles": [t for t in tidy_files],
-})
-`
-}
-
-// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
-// The given rawString must correspond to the string output which was created by evaluating the
-// Starlark given in StarlarkFunctionBody.
-func (g getCcUnstrippedInfoType) ParseResult(rawString string) (CcUnstrippedInfo, error) {
-	var info CcUnstrippedInfo
-	err := parseJson(rawString, &info)
-	return info, err
-}
-
-type CcUnstrippedInfo struct {
-	CcAndroidMkInfo
-	OutputFile       string
-	UnstrippedOutput string
-	TidyFiles        []string
-}
-
-// splitOrEmpty is a modification of strings.Split() that returns an empty list
-// if the given string is empty.
-func splitOrEmpty(s string, sep string) []string {
-	if len(s) < 1 {
-		return []string{}
-	} else {
-		return strings.Split(s, sep)
-	}
-}
-
-// parseJson decodes json string into the fields of the receiver.
-// Unknown attribute name causes panic.
-func parseJson(jsonString string, info interface{}) error {
-	decoder := json.NewDecoder(strings.NewReader(jsonString))
-	decoder.DisallowUnknownFields() //useful to detect typos, e.g. in unit tests
-	err := decoder.Decode(info)
-	if err != nil {
-		return fmt.Errorf("cannot parse cquery result '%s': %s", jsonString, err)
-	}
-	return nil
-}
-
-type getPrebuiltFileInfo struct{}
-
-// Name returns a string name for this request type. Such request type names must be unique,
-// and must only consist of alphanumeric characters.
-func (g getPrebuiltFileInfo) Name() string {
-	return "getPrebuiltFileInfo"
-}
-
-// StarlarkFunctionBody returns a starlark function body to process this request type.
-// The returned string is the body of a Starlark function which obtains
-// all request-relevant information about a target and returns a string containing
-// this information.
-// The function should have the following properties:
-//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
-//   - The return value must be a string.
-//   - The function body should not be indented outside of its own scope.
-func (g getPrebuiltFileInfo) StarlarkFunctionBody() string {
-	return `
-p = providers(target)
-prebuilt_file_info = p.get("//build/bazel/rules:prebuilt_file.bzl%PrebuiltFileInfo")
-if not prebuilt_file_info:
-  fail("%s did not provide PrebuiltFileInfo" % id_string)
-
-return json.encode({
-	"Src": prebuilt_file_info.src.path,
-	"Dir": prebuilt_file_info.dir,
-	"Filename": prebuilt_file_info.filename,
-	"Installable": prebuilt_file_info.installable,
-})`
-}
-
-type PrebuiltFileInfo struct {
-	// TODO: b/207489266 - Fully support all properties in prebuilt_file
-	Src         string
-	Dir         string
-	Filename    string
-	Installable bool
-}
-
-// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
-// The given rawString must correspond to the string output which was created by evaluating the
-// Starlark given in StarlarkFunctionBody.
-func (g getPrebuiltFileInfo) ParseResult(rawString string) (PrebuiltFileInfo, error) {
-	var info PrebuiltFileInfo
-	err := parseJson(rawString, &info)
-	return info, err
-}
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
deleted file mode 100644
index e772bb7..0000000
--- a/bazel/cquery/request_type_test.go
+++ /dev/null
@@ -1,281 +0,0 @@
-package cquery
-
-import (
-	"encoding/json"
-	"reflect"
-	"strings"
-	"testing"
-)
-
-func TestGetOutputFilesParseResults(t *testing.T) {
-	t.Parallel()
-	testCases := []struct {
-		description    string
-		input          string
-		expectedOutput []string
-	}{
-		{
-			description:    "no result",
-			input:          "",
-			expectedOutput: []string{},
-		},
-		{
-			description:    "one result",
-			input:          "test",
-			expectedOutput: []string{"test"},
-		},
-		{
-			description:    "splits on comma with space",
-			input:          "foo, bar",
-			expectedOutput: []string{"foo", "bar"},
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.description, func(t *testing.T) {
-			actualOutput := GetOutputFiles.ParseResult(tc.input)
-			if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-				t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
-			}
-		})
-	}
-}
-
-func TestGetCcInfoParseResults(t *testing.T) {
-	t.Parallel()
-	testCases := []struct {
-		description    string
-		inputCcInfo    CcInfo
-		expectedOutput CcInfo
-	}{
-		{
-			description:    "no result",
-			inputCcInfo:    CcInfo{},
-			expectedOutput: CcInfo{},
-		},
-		{
-			description: "all items set",
-			inputCcInfo: CcInfo{
-				OutputFiles:          []string{"out1", "out2"},
-				CcObjectFiles:        []string{"object1", "object2"},
-				CcSharedLibraryFiles: []string{"shared_lib1", "shared_lib2"},
-				CcStaticLibraryFiles: []string{"static_lib1", "static_lib2"},
-				Includes:             []string{".", "dir/subdir"},
-				SystemIncludes:       []string{"system/dir", "system/other/dir"},
-				Headers:              []string{"dir/subdir/hdr.h"},
-				RootStaticArchives:   []string{"rootstaticarchive1"},
-				RootDynamicLibraries: []string{"rootdynamiclibrary1"},
-				TocFile:              "lib.so.toc",
-			},
-			expectedOutput: CcInfo{
-				OutputFiles:          []string{"out1", "out2"},
-				CcObjectFiles:        []string{"object1", "object2"},
-				CcSharedLibraryFiles: []string{"shared_lib1", "shared_lib2"},
-				CcStaticLibraryFiles: []string{"static_lib1", "static_lib2"},
-				Includes:             []string{".", "dir/subdir"},
-				SystemIncludes:       []string{"system/dir", "system/other/dir"},
-				Headers:              []string{"dir/subdir/hdr.h"},
-				RootStaticArchives:   []string{"rootstaticarchive1"},
-				RootDynamicLibraries: []string{"rootdynamiclibrary1"},
-				TocFile:              "lib.so.toc",
-			},
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.description, func(t *testing.T) {
-			jsonInput, _ := json.Marshal(tc.inputCcInfo)
-			actualOutput, err := GetCcInfo.ParseResult(string(jsonInput))
-			if err != nil {
-				t.Errorf("error parsing result: %q", err)
-			} else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-				t.Errorf("expected %#v\n!= actual %#v", tc.expectedOutput, actualOutput)
-			}
-		})
-	}
-}
-
-func TestGetCcInfoParseResultsError(t *testing.T) {
-	t.Parallel()
-	testCases := []struct {
-		description   string
-		input         string
-		expectedError string
-	}{
-		{
-			description:   "not json",
-			input:         ``,
-			expectedError: `cannot parse cquery result '': EOF`,
-		},
-		{
-			description: "invalid field",
-			input: `{
-	"toc_file": "dir/file.so.toc"
-}`,
-			expectedError: `json: unknown field "toc_file"`,
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.description, func(t *testing.T) {
-			_, err := GetCcInfo.ParseResult(tc.input)
-			if !strings.Contains(err.Error(), tc.expectedError) {
-				t.Errorf("expected string %q in error message, got %q", tc.expectedError, err)
-			}
-		})
-	}
-}
-
-func TestGetApexInfoParseResults(t *testing.T) {
-	t.Parallel()
-	testCases := []struct {
-		description    string
-		input          string
-		expectedOutput ApexInfo
-	}{
-		{
-			description:    "no result",
-			input:          "{}",
-			expectedOutput: ApexInfo{},
-		},
-		{
-			description: "one result",
-			input: `{
-	"signed_output":"my.apex",
-	"unsigned_output":"my.apex.unsigned",
-	"requires_native_libs":["//bionic/libc:libc","//bionic/libdl:libdl"],
-	"bundle_key_info":["foo.pem", "foo.privkey"],
-	"container_key_info":["foo.x509.pem", "foo.pk8", "foo"],
-	"package_name":"package.name",
-	"symbols_used_by_apex": "path/to/my.apex_using.txt",
-	"backing_libs":"path/to/backing.txt",
-	"bundle_file": "dir/bundlefile.zip",
-	"installed_files":"path/to/installed-files.txt",
-	"provides_native_libs":[],
-	"make_modules_to_install": ["foo","bar"]
-}`,
-			expectedOutput: ApexInfo{
-				// ApexInfo
-				SignedOutput:      "my.apex",
-				UnsignedOutput:    "my.apex.unsigned",
-				RequiresLibs:      []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
-				ProvidesLibs:      []string{},
-				BundleKeyInfo:     []string{"foo.pem", "foo.privkey"},
-				ContainerKeyInfo:  []string{"foo.x509.pem", "foo.pk8", "foo"},
-				PackageName:       "package.name",
-				SymbolsUsedByApex: "path/to/my.apex_using.txt",
-				BackingLibs:       "path/to/backing.txt",
-				BundleFile:        "dir/bundlefile.zip",
-				InstalledFiles:    "path/to/installed-files.txt",
-
-				// ApexMkInfo
-				MakeModulesToInstall: []string{"foo", "bar"},
-			},
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.description, func(t *testing.T) {
-			actualOutput, err := GetApexInfo.ParseResult(tc.input)
-			if err != nil {
-				t.Errorf("Unexpected error %q", err)
-			}
-			if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-				t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
-			}
-		})
-	}
-}
-
-func TestGetApexInfoParseResultsError(t *testing.T) {
-	t.Parallel()
-	testCases := []struct {
-		description   string
-		input         string
-		expectedError string
-	}{
-		{
-			description:   "not json",
-			input:         ``,
-			expectedError: `cannot parse cquery result '': EOF`,
-		},
-		{
-			description: "invalid field",
-			input: `{
-	"fake_field": "path/to/file"
-}`,
-			expectedError: `json: unknown field "fake_field"`,
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.description, func(t *testing.T) {
-			_, err := GetApexInfo.ParseResult(tc.input)
-			if !strings.Contains(err.Error(), tc.expectedError) {
-				t.Errorf("expected string %q in error message, got %q", tc.expectedError, err)
-			}
-		})
-	}
-}
-
-func TestGetCcUnstrippedParseResults(t *testing.T) {
-	t.Parallel()
-	testCases := []struct {
-		description    string
-		input          string
-		expectedOutput CcUnstrippedInfo
-	}{
-		{
-			description:    "no result",
-			input:          "{}",
-			expectedOutput: CcUnstrippedInfo{},
-		},
-		{
-			description: "one result",
-			input:       `{"OutputFile":"myapp", "UnstrippedOutput":"myapp_unstripped"}`,
-			expectedOutput: CcUnstrippedInfo{
-				OutputFile:       "myapp",
-				UnstrippedOutput: "myapp_unstripped",
-			},
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.description, func(t *testing.T) {
-			actualOutput, err := GetCcUnstrippedInfo.ParseResult(tc.input)
-			if err != nil {
-				t.Errorf("Unexpected error %q", err)
-			}
-			if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-				t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
-			}
-		})
-	}
-}
-
-func TestGetCcUnstrippedParseResultsErrors(t *testing.T) {
-	t.Parallel()
-	testCases := []struct {
-		description   string
-		input         string
-		expectedError string
-	}{
-		{
-			description:   "not json",
-			input:         ``,
-			expectedError: `cannot parse cquery result '': EOF`,
-		},
-		{
-			description: "invalid field",
-			input: `{
-	"fake_field": "path/to/file"
-}`,
-			expectedError: `json: unknown field "fake_field"`,
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.description, func(t *testing.T) {
-			_, err := GetCcUnstrippedInfo.ParseResult(tc.input)
-			if !strings.Contains(err.Error(), tc.expectedError) {
-				t.Errorf("expected string %q in error message, got %q", tc.expectedError, err)
-			}
-		})
-	}
-}
diff --git a/bin/afind b/bin/afind
new file mode 100755
index 0000000..f5b8319
--- /dev/null
+++ b/bin/afind
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# prevent glob expansion in this script
+set -f
+
+dir=${1:-.}
+
+shift
+
+args=( $@ )
+if [[ ${#args[@]} -eq 0 ]] ; then
+    args=( -print )
+fi
+
+find "$dir" -name .repo -prune -o -name .git -prune -o -name out -prune -o ${args[@]}
+
+exit $?
diff --git a/bin/aninja b/bin/aninja
index cceb794..5cb5a55 100755
--- a/bin/aninja
+++ b/bin/aninja
@@ -20,6 +20,19 @@
 require_top
 require_lunch
 
+case $(uname -s) in
+    Darwin)
+        host_arch=darwin-x86
+        ;;
+    Linux)
+        host_arch=linux-x86
+        ;;
+    *)
+        >&2 echo Unknown host $(uname -s)
+        exit 1
+        ;;
+esac
+
 cd $(gettop)
-prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-${TARGET_PRODUCT}.ninja "$@"
+prebuilts/build-tools/${host_arch}/bin/ninja -f $(getoutdir)/combined-${TARGET_PRODUCT}.ninja "$@"
 
diff --git a/bin/dirmods b/bin/dirmods
index 52d935a..c8976d5 100755
--- a/bin/dirmods
+++ b/bin/dirmods
@@ -32,18 +32,29 @@
 def main():
     parser = argparse.ArgumentParser(description=__doc__)
     parser.add_argument('path')
+    parser.add_argument('--no-recurse', '-n', action='store_true',
+                        help='Do not include modules defined in subdirs of path')
     args = parser.parse_args()
+    should_recurse = not args.no_recurse
 
     d = os.path.normpath(args.path)
-    prefix = d + '/'
+    # Fix absolute path to be relative to build top
+    if os.path.isabs(d):
+        base = os.environ.get('ANDROID_BUILD_TOP')
+        if base:
+            base = os.path.normpath(base) + os.path.sep
+            if d.startswith(base):
+                d = d[len(base):]
+
+    prefix = d + os.path.sep
 
     module_info = modinfo.ReadModuleInfo()
 
     results = set()
     for m in module_info.values():
-        for path in m.get(u'path', []):
-            if path == d or path.startswith(prefix):
-                name = m.get(u'module_name')
+        for path in m.get('path', []):
+            if path == d or (should_recurse and path.startswith(prefix)):
+                name = m.get('module_name')
                 if name:
                     results.add(name)
 
diff --git a/bin/installmod b/bin/installmod
index 1d0d836..1ad5b84 100755
--- a/bin/installmod
+++ b/bin/installmod
@@ -28,7 +28,6 @@
     return 1
 fi
 
-local _path
 _path=$(outmod ${@:$#:1})
 if [ $? -ne 0 ]; then
     return 1
@@ -39,7 +38,7 @@
     echo "Module '$1' does not produce a file ending with .apk (try 'refreshmod' if there have been build changes?)" >&2
     return 1
 fi
-local serial_device=""
+serial_device=""
 if [[ "$1" == "-s" ]]; then
     if [[ $# -le 2 ]]; then
         echo "-s requires an argument" >&2
@@ -48,7 +47,7 @@
     serial_device="-s $2"
     shift 2
 fi
-local length=$(( $# - 1 ))
+length=$(( $# - 1 ))
 echo adb $serial_device install ${@:1:$length} $_path
 adb $serial_device install ${@:1:$length} $_path
 
diff --git a/bin/soongdbg b/bin/soongdbg
index bfdbbde..98d31eb 100755
--- a/bin/soongdbg
+++ b/bin/soongdbg
@@ -32,11 +32,13 @@
                 dep.rdeps.add(node)
                 node.dep_tags.setdefault(dep, list()).append(d)
 
-    def find_paths(self, id1, id2):
+    def find_paths(self, id1, id2, tag_filter):
         # Throws KeyError if one of the names isn't found
         def recurse(node1, node2, visited):
             result = set()
             for dep in node1.rdeps:
+                if not matches_tag(dep, node1, tag_filter):
+                    continue
                 if dep == node2:
                     result.add(node2)
                 if dep not in visited:
@@ -214,6 +216,8 @@
                         help="jq query for each module metadata")
     parser.add_argument("--deptags", action="store_true",
                         help="show dependency tags (makes the graph much more complex)")
+    parser.add_argument("--tag", action="append", default=[],
+                        help="Limit output to these dependency tags.")
 
     group = parser.add_argument_group("output formats",
                                       "If no format is provided, a dot file will be written to"
@@ -259,13 +263,21 @@
         sys.stdout.write(text)
 
 
-def get_deps(nodes, root, maxdepth, reverse):
+def matches_tag(node, dep, tag_filter):
+    if not tag_filter:
+        return True
+    return not tag_filter.isdisjoint([t.tag_type for t in node.dep_tags[dep]])
+
+
+def get_deps(nodes, root, maxdepth, reverse, tag_filter):
     if root in nodes:
         return
     nodes.add(root)
     if maxdepth != 0:
         for dep in (root.rdeps if reverse else root.deps):
-            get_deps(nodes, dep, maxdepth-1, reverse)
+            if not matches_tag(root, dep, tag_filter):
+                continue
+            get_deps(nodes, dep, maxdepth-1, reverse, tag_filter)
 
 
 def new_module_formatter(args):
@@ -302,7 +314,7 @@
 
     def run(self, args):
         graph = load_graph()
-        print_nodes(args, graph.find_paths(args.module[0], args.module[1]),
+        print_nodes(args, graph.find_paths(args.module[0], args.module[1], set(args.tag)),
                     new_module_formatter(args))
 
 
@@ -328,7 +340,7 @@
                 sys.stderr.write(f"error: Can't find root: {id}\n")
                 err = True
                 continue
-            get_deps(nodes, root, args.depth, args.reverse)
+            get_deps(nodes, root, args.depth, args.reverse, set(args.tag))
         if err:
             sys.exit(1)
         print_nodes(args, nodes, new_module_formatter(args))
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index b72b6d3..8ecea98 100644
--- a/bloaty/bloaty.go
+++ b/bloaty/bloaty.go
@@ -88,7 +88,7 @@
 		if !m.ExportedToMake() {
 			return
 		}
-		filePaths, ok := android.SingletonModuleProvider(ctx, m, fileSizeMeasurerKey)
+		filePaths, ok := android.OtherModuleProvider(ctx, m, fileSizeMeasurerKey)
 		if !ok {
 			return
 		}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index ce00b5b..8679821 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -56,6 +56,7 @@
 )
 
 func registerBpfBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("bpf_defaults", defaultsFactory)
 	ctx.RegisterModuleType("bpf", BpfFactory)
 }
 
@@ -77,10 +78,16 @@
 	// the C/C++ module.
 	Cflags []string
 
-	// directories (relative to the root of the source tree) that will
-	// be added to the include paths using -I.
+	// list of directories relative to the root of the source tree that
+	// will be added to the include paths using -I.
+	// 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
 
+	// list of directories relative to the Blueprint file that will be
+	// added to the include path using -I.
+	Local_include_dirs []string
 	// optional subdirectory under which this module is installed into.
 	Sub_dir string
 
@@ -94,7 +101,7 @@
 
 type bpf struct {
 	android.ModuleBase
-
+	android.DefaultableModuleBase
 	properties BpfProperties
 
 	objs android.Paths
@@ -104,6 +111,14 @@
 
 func (bpf *bpf) ImageMutatorBegin(ctx android.BaseModuleContext) {}
 
+func (bpf *bpf) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+	return proptools.Bool(bpf.properties.Vendor)
+}
+
+func (bpf *bpf) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (bpf *bpf) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return !proptools.Bool(bpf.properties.Vendor)
 }
@@ -125,9 +140,6 @@
 }
 
 func (bpf *bpf) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	if proptools.Bool(bpf.properties.Vendor) {
-		return []string{"vendor"}
-	}
 	return nil
 }
 
@@ -143,24 +155,32 @@
 		"-no-canonical-prefixes",
 
 		"-O2",
+		"-Wall",
+		"-Werror",
+		"-Wextra",
+
 		"-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.
 		"-isystem bionic/libc/kernel/uapi/asm-arm64",
 		"-isystem bionic/libc/kernel/android/uapi",
-		"-I       packages/modules/Connectivity/staticlibs/native/bpf_headers/include/bpf",
+		"-I       packages/modules/Connectivity/bpf/headers/include",
 		// TODO(b/149785767): only give access to specific file with AID_* constants
 		"-I       system/core/libcutils/include",
 		"-I " + ctx.ModuleDir(),
 	}
 
+	for _, dir := range android.PathsForModuleSrc(ctx, bpf.properties.Local_include_dirs) {
+		cflags = append(cflags, "-I "+dir.String())
+	}
+
 	for _, dir := range android.PathsForSource(ctx, bpf.properties.Include_dirs) {
 		cflags = append(cflags, "-I "+dir.String())
 	}
 
 	cflags = append(cflags, bpf.properties.Cflags...)
 
-	if proptools.Bool(bpf.properties.Btf) {
+	if proptools.BoolDefault(bpf.properties.Btf, true) {
 		cflags = append(cflags, "-g")
 		if runtime.GOOS != "darwin" {
 			cflags = append(cflags, "-fdebug-prefix-map=/proc/self/cwd=")
@@ -185,7 +205,7 @@
 			},
 		})
 
-		if proptools.Bool(bpf.properties.Btf) {
+		if proptools.BoolDefault(bpf.properties.Btf, true) {
 			objStripped := android.ObjPathWithExt(ctx, "", src, "o")
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   stripRule,
@@ -255,6 +275,26 @@
 	}
 }
 
+type Defaults struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+}
+
+func defaultsFactory() android.Module {
+	return DefaultsFactory()
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+	module := &Defaults{}
+
+	module.AddProperties(props...)
+	module.AddProperties(&BpfProperties{})
+
+	android.InitDefaultsModule(module)
+
+	return module
+}
+
 func (bpf *bpf) SubDir() string {
 	return bpf.properties.Sub_dir
 }
@@ -265,5 +305,7 @@
 	module.AddProperties(&module.properties)
 
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+
 	return module
 }
diff --git a/bpf/libbpf/Android.bp b/bpf/libbpf/Android.bp
new file mode 100644
index 0000000..f0ba90f
--- /dev/null
+++ b/bpf/libbpf/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-libbpf",
+    pkgPath: "android/soong/bpf/libbpf",
+    deps: [
+        "blueprint",
+        "blueprint-proptools",
+        "soong-android",
+        "soong-cc",
+        "soong-cc-config",
+    ],
+    srcs: [
+        "libbpf_prog.go",
+    ],
+    testSrcs: [
+        "libbpf_prog_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/bpf/libbpf/libbpf_prog.go b/bpf/libbpf/libbpf_prog.go
new file mode 100644
index 0000000..ac61510
--- /dev/null
+++ b/bpf/libbpf/libbpf_prog.go
@@ -0,0 +1,310 @@
+// 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 libbpf_prog
+
+import (
+	"fmt"
+	"io"
+	"runtime"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/genrule"
+
+	"github.com/google/blueprint"
+)
+
+type libbpfProgDepType struct {
+	blueprint.BaseDependencyTag
+}
+
+func init() {
+	registerLibbpfProgBuildComponents(android.InitRegistrationContext)
+	pctx.Import("android/soong/cc/config")
+	pctx.StaticVariable("relPwd", cc.PwdPrefix())
+}
+
+var (
+	pctx = android.NewPackageContext("android/soong/bpf/libbpf_prog")
+
+	libbpfProgCcRule = pctx.AndroidStaticRule("libbpfProgCcRule",
+		blueprint.RuleParams{
+			Depfile:     "${out}.d",
+			Deps:        blueprint.DepsGCC,
+			Command:     "$relPwd $ccCmd --target=bpf -c $cFlags -MD -MF ${out}.d -o $out $in",
+			CommandDeps: []string{"$ccCmd"},
+		},
+		"ccCmd", "cFlags")
+
+	libbpfProgStripRule = pctx.AndroidStaticRule("libbpfProgStripRule",
+		blueprint.RuleParams{
+			Command: `$stripCmd --strip-unneeded --remove-section=.rel.BTF ` +
+				`--remove-section=.rel.BTF.ext --remove-section=.BTF.ext $in -o $out`,
+			CommandDeps: []string{"$stripCmd"},
+		},
+		"stripCmd")
+
+	libbpfProgDepTag = libbpfProgDepType{}
+)
+
+func registerLibbpfProgBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("libbpf_defaults", defaultsFactory)
+	ctx.RegisterModuleType("libbpf_prog", LibbpfProgFactory)
+}
+
+var PrepareForTestWithLibbpfProg = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(registerLibbpfProgBuildComponents),
+	android.FixtureAddFile("libbpf_headers/Foo.h", nil),
+	android.FixtureAddFile("libbpf_headers/Android.bp", []byte(`
+		genrule {
+			name: "libbpf_headers",
+			out: ["foo.h",],
+		}
+	`)),
+	genrule.PrepareForTestWithGenRuleBuildComponents,
+)
+
+type LibbpfProgProperties struct {
+	// source paths to the files.
+	Srcs []string `android:"path"`
+
+	// additional cflags that should be used to build the libbpf variant of
+	// the C/C++ module.
+	Cflags []string `android:"arch_variant"`
+
+	// list of directories relative to the Blueprint file that will
+	// be added to the include path using -I
+	Local_include_dirs []string `android:"arch_variant"`
+
+	Header_libs []string `android:"arch_variant"`
+
+	// optional subdirectory under which this module is installed into.
+	Relative_install_path string
+}
+
+type libbpfProg struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	properties LibbpfProgProperties
+	objs       android.Paths
+}
+
+var _ android.ImageInterface = (*libbpfProg)(nil)
+
+func (libbpf *libbpfProg) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (libbpf *libbpfProg) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (libbpf *libbpfProg) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (libbpf *libbpfProg) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return true
+}
+
+func (libbpf *libbpfProg) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (libbpf *libbpfProg) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (libbpf *libbpfProg) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (libbpf *libbpfProg) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (libbpf *libbpfProg) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	return nil
+}
+
+func (libbpf *libbpfProg) SetImageVariation(ctx android.BaseModuleContext, variation string) {
+}
+
+func (libbpf *libbpfProg) DepsMutator(ctx android.BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), libbpfProgDepTag, "libbpf_headers")
+	ctx.AddVariationDependencies(nil, cc.HeaderDepTag(), libbpf.properties.Header_libs...)
+}
+
+func (libbpf *libbpfProg) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var cFlagsDeps android.Paths
+	cflags := []string{
+		"-nostdlibinc",
+
+		// Make paths in deps files relative
+		"-no-canonical-prefixes",
+
+		"-O2",
+		"-Wall",
+		"-Werror",
+		"-Wextra",
+
+		"-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.
+		"-isystem bionic/libc/kernel/uapi/asm-arm64",
+		"-isystem bionic/libc/kernel/android/uapi",
+		"-I " + ctx.ModuleDir(),
+		"-g", //Libbpf builds require BTF data
+	}
+
+	if runtime.GOOS != "darwin" {
+		cflags = append(cflags, "-fdebug-prefix-map=/proc/self/cwd=")
+	}
+
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		depTag := ctx.OtherModuleDependencyTag(dep)
+		if depTag == libbpfProgDepTag {
+			if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
+				cFlagsDeps = append(cFlagsDeps, genRule.GeneratedDeps()...)
+				dirs := genRule.GeneratedHeaderDirs()
+				for _, dir := range dirs {
+					cflags = append(cflags, "-I "+dir.String())
+				}
+			} else {
+				depName := ctx.OtherModuleName(dep)
+				ctx.ModuleErrorf("module %q is not a genrule", depName)
+			}
+		} else if depTag == cc.HeaderDepTag() {
+			depExporterInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
+			for _, dir := range depExporterInfo.IncludeDirs {
+				cflags = append(cflags, "-I "+dir.String())
+			}
+		}
+	})
+
+	for _, dir := range android.PathsForModuleSrc(ctx, libbpf.properties.Local_include_dirs) {
+		cflags = append(cflags, "-I "+dir.String())
+	}
+
+	cflags = append(cflags, libbpf.properties.Cflags...)
+
+	srcs := android.PathsForModuleSrc(ctx, libbpf.properties.Srcs)
+
+	for _, src := range srcs {
+		if strings.ContainsRune(src.Base(), '_') {
+			ctx.ModuleErrorf("invalid character '_' in source name")
+		}
+		obj := android.ObjPathWithExt(ctx, "unstripped", src, "o")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:      libbpfProgCcRule,
+			Input:     src,
+			Implicits: cFlagsDeps,
+			Output:    obj,
+			Args: map[string]string{
+				"cFlags": strings.Join(cflags, " "),
+				"ccCmd":  "${config.ClangBin}/clang",
+			},
+		})
+
+		objStripped := android.ObjPathWithExt(ctx, "", src, "o")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   libbpfProgStripRule,
+			Input:  obj,
+			Output: objStripped,
+			Args: map[string]string{
+				"stripCmd": "${config.ClangBin}/llvm-strip",
+			},
+		})
+		libbpf.objs = append(libbpf.objs, objStripped.WithoutRel())
+	}
+
+	installDir := android.PathForModuleInstall(ctx, "etc", "bpf/libbpf")
+	if len(libbpf.properties.Relative_install_path) > 0 {
+		installDir = installDir.Join(ctx, libbpf.properties.Relative_install_path)
+	}
+	for _, obj := range libbpf.objs {
+		ctx.PackageFile(installDir, obj.Base(), obj)
+	}
+
+	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
+
+	ctx.SetOutputFiles(libbpf.objs, "")
+}
+
+func (libbpf *libbpfProg) AndroidMk() android.AndroidMkData {
+	return android.AndroidMkData{
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			var names []string
+			fmt.Fprintln(w)
+			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+			fmt.Fprintln(w)
+			var localModulePath string
+			localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf/libbpf"
+			if len(libbpf.properties.Relative_install_path) > 0 {
+				localModulePath += "/" + libbpf.properties.Relative_install_path
+			}
+			for _, obj := range libbpf.objs {
+				objName := name + "_" + obj.Base()
+				names = append(names, objName)
+				fmt.Fprintln(w, "include $(CLEAR_VARS)", " # libbpf.libbpf.obj")
+				fmt.Fprintln(w, "LOCAL_MODULE := ", objName)
+				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String())
+				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", obj.Base())
+				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC")
+				fmt.Fprintln(w, localModulePath)
+				// AconfigUpdateAndroidMkData may have added elements to Extra.  Process them here.
+				for _, extra := range data.Extra {
+					extra(w, nil)
+				}
+				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+				fmt.Fprintln(w)
+			}
+			fmt.Fprintln(w, "include $(CLEAR_VARS)", " # libbpf.libbpf")
+			fmt.Fprintln(w, "LOCAL_MODULE := ", name)
+			android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", names)
+			fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
+		},
+	}
+}
+
+type Defaults struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+}
+
+func defaultsFactory() android.Module {
+	return DefaultsFactory()
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+	module := &Defaults{}
+
+	module.AddProperties(props...)
+	module.AddProperties(&LibbpfProgProperties{})
+
+	android.InitDefaultsModule(module)
+
+	return module
+}
+
+func LibbpfProgFactory() android.Module {
+	module := &libbpfProg{}
+
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+
+	return module
+}
diff --git a/bpf/libbpf/libbpf_prog_test.go b/bpf/libbpf/libbpf_prog_test.go
new file mode 100644
index 0000000..f4f5167
--- /dev/null
+++ b/bpf/libbpf/libbpf_prog_test.go
@@ -0,0 +1,69 @@
+// 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 libbpf_prog
+
+import (
+	"os"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+func TestMain(m *testing.M) {
+	os.Exit(m.Run())
+}
+
+var prepareForLibbpfProgTest = android.GroupFixturePreparers(
+	cc.PrepareForTestWithCcDefaultModules,
+	android.FixtureMergeMockFs(
+		map[string][]byte{
+			"bpf.c":              nil,
+			"bpf_invalid_name.c": nil,
+			"BpfTest.cpp":        nil,
+		},
+	),
+	PrepareForTestWithLibbpfProg,
+)
+
+func TestLibbpfProgDataDependency(t *testing.T) {
+	bp := `
+		libbpf_prog {
+			name: "bpf.o",
+			srcs: ["bpf.c"],
+		}
+
+		cc_test {
+			name: "vts_test_binary_bpf_module",
+			srcs: ["BpfTest.cpp"],
+			data: [":bpf.o"],
+			gtest: false,
+		}
+	`
+
+	prepareForLibbpfProgTest.RunTestWithBp(t, bp)
+}
+
+func TestLibbpfProgSourceName(t *testing.T) {
+	bp := `
+		libbpf_prog {
+			name: "bpf_invalid_name.o",
+			srcs: ["bpf_invalid_name.c"],
+		}
+	`
+	prepareForLibbpfProgTest.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		`invalid character '_' in source name`)).
+		RunTestWithBp(t, bp)
+}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index ddaa98a..9163ab7 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -286,7 +286,7 @@
 }
 
 func parse(name string, r io.Reader) (*parser.File, error) {
-	tree, errs := parser.Parse(name, r, parser.NewScope(nil))
+	tree, errs := parser.Parse(name, r)
 	if errs != nil {
 		s := "parse error: "
 		for _, err := range errs {
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index b5b49b1..f487d3c 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -46,7 +46,7 @@
 	}
 	`,
 		printListOfStrings(local_include_dirs), printListOfStrings(export_include_dirs))
-	tree, errs := parser.Parse("", strings.NewReader(input), parser.NewScope(nil))
+	tree, errs := parser.Parse("", strings.NewReader(input))
 	if len(errs) > 0 {
 		errs = append([]error{fmt.Errorf("failed to parse:\n%s", input)}, errs...)
 	}
@@ -167,7 +167,7 @@
 		return fixer, err
 	}
 
-	tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
+	tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in))
 	if errs != nil {
 		return fixer, err
 	}
diff --git a/bpfix/cmd_lib/bpfix.go b/bpfix/cmd_lib/bpfix.go
index 1106d4a..41430f8 100644
--- a/bpfix/cmd_lib/bpfix.go
+++ b/bpfix/cmd_lib/bpfix.go
@@ -66,7 +66,7 @@
 		return err
 	}
 	r := bytes.NewBuffer(append([]byte(nil), src...))
-	file, errs := parser.Parse(filename, r, parser.NewScope(nil))
+	file, errs := parser.Parse(filename, r)
 	if len(errs) > 0 {
 		for _, err := range errs {
 			fmt.Fprintln(os.Stderr, err)
diff --git a/build_kzip.bash b/build_kzip.bash
index 4c42048..850aeda 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -40,6 +40,7 @@
   merge_zips
   xref_cxx
   xref_java
+  xref_kotlin
   # TODO: b/286390153 - reenable rust
   # xref_rust
 )
diff --git a/cc/Android.bp b/cc/Android.bp
index 3bbcaa9..3688c8a 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -16,7 +16,6 @@
         "soong-etc",
         "soong-fuzz",
         "soong-genrule",
-        "soong-multitree",
         "soong-testing",
         "soong-tradefed",
     ],
@@ -65,7 +64,6 @@
         "library.go",
         "library_headers.go",
         "library_sdk_member.go",
-        "library_stub.go",
         "native_bridge_sdk_trait.go",
         "object.go",
         "test.go",
@@ -73,7 +71,6 @@
         "ndk_abi.go",
         "ndk_headers.go",
         "ndk_library.go",
-        "ndk_prebuilt.go",
         "ndk_sysroot.go",
 
         "llndk_library.go",
@@ -119,4 +116,6 @@
         "cmake_module_cc.txt",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/cc/TEST_MAPPING b/cc/TEST_MAPPING
new file mode 100644
index 0000000..be2809d
--- /dev/null
+++ b/cc/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "bionic"
+    }
+  ]
+}
diff --git a/cc/afdo.go b/cc/afdo.go
index 00b2245..14d105e 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -47,6 +47,10 @@
 	if ctx.Config().Eng() {
 		afdo.Properties.Afdo = false
 	}
+	// Disable for native coverage builds.
+	if ctx.DeviceConfig().NativeCoverageEnabled() {
+		afdo.Properties.Afdo = false
+	}
 }
 
 // afdoEnabled returns true for binaries and shared libraries
@@ -76,6 +80,8 @@
 	}
 
 	if afdo.Properties.Afdo || afdo.Properties.AfdoDep {
+		// Emit additional debug info for AutoFDO
+		flags.Local.CFlags = append([]string{"-fdebug-info-for-profiling"}, flags.Local.CFlags...)
 		// We use `-funique-internal-linkage-names` to associate profiles to the right internal
 		// functions. This option should be used before generating a profile. Because a profile
 		// generated for a binary without unique names doesn't work well building a binary with
@@ -176,6 +182,9 @@
 
 func (a *afdoTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
 	if m, ok := ctx.Module().(*Module); ok && m.afdo != nil {
+		if !m.Enabled(ctx) {
+			return
+		}
 		if variation == "" {
 			// The empty variation is either a module that has enabled AFDO for itself, or the non-AFDO
 			// variant of a dependency.
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 143e86f..6966f76 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -21,7 +21,6 @@
 	"strings"
 
 	"android/soong/android"
-	"android/soong/multitree"
 )
 
 var (
@@ -351,9 +350,6 @@
 	ctx.subAndroidMk(entries, test.testDecorator)
 
 	entries.Class = "NATIVE_TESTS"
-	if Bool(test.Properties.Test_per_src) {
-		entries.SubName = "_" + String(test.binaryDecorator.Properties.Stem)
-	}
 	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())
@@ -454,10 +450,6 @@
 	})
 }
 
-func (c *ndkPrebuiltStlLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "SHARED_LIBRARIES"
-}
-
 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 {
@@ -486,34 +478,6 @@
 	androidMkWritePrebuiltOptions(p.baseLinker, entries)
 }
 
-func (a *apiLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "SHARED_LIBRARIES"
-	entries.SubName += multitree.GetApiImportSuffix()
-
-	entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		a.libraryDecorator.androidMkWriteExportedFlags(entries)
-		src := *a.properties.Src
-		path, file := filepath.Split(src)
-		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)
-		entries.SetString("LOCAL_MODULE_PATH", path)
-		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-		entries.SetString("LOCAL_SOONG_TOC", a.toc().String())
-	})
-}
-
-func (a *apiHeadersDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "HEADER_LIBRARIES"
-	entries.SubName += multitree.GetApiImportSuffix()
-
-	entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		a.libraryDecorator.androidMkWriteExportedFlags(entries)
-		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-	})
-}
-
 func androidMkWritePrebuiltOptions(linker *baseLinker, entries *android.AndroidMkEntries) {
 	allow := linker.Properties.Allow_undefined_symbols
 	if allow != nil {
diff --git a/cc/binary.go b/cc/binary.go
index 3ff35de..2ac9a45 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -451,7 +451,7 @@
 }
 
 func (binary *binaryDecorator) strippedAllOutputFilePath() android.Path {
-	panic("Not implemented.")
+	return nil
 }
 
 func (binary *binaryDecorator) setSymlinkList(ctx ModuleContext) {
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index 71e0cd8..4063714 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -132,7 +132,7 @@
 
 	if ccModule.linker != nil {
 		specifiedDeps := specifiedDeps{}
-		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(specifiedDeps)
+		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx.SdkModuleContext(), ccModule, specifiedDeps)
 
 		p.SharedLibs = specifiedDeps.sharedLibs
 		p.SystemSharedLibs = specifiedDeps.systemSharedLibs
diff --git a/cc/builder.go b/cc/builder.go
index 8719d4f..cd535c1 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -22,6 +22,7 @@
 	"fmt"
 	"path/filepath"
 	"runtime"
+	"slices"
 	"strconv"
 	"strings"
 
@@ -156,11 +157,17 @@
 		"args")
 
 	// Rule to invoke `strip` (to discard symbols and data from object files) on darwin architecture.
-	darwinStrip = pctx.AndroidStaticRule("darwinStrip",
-		blueprint.RuleParams{
-			Command:     "${config.MacStripPath} -u -r -o $out $in",
-			CommandDeps: []string{"${config.MacStripPath}"},
-		})
+	darwinStrip = func() blueprint.Rule {
+		if runtime.GOOS == "darwin" {
+			return pctx.AndroidStaticRule("darwinStrip",
+				blueprint.RuleParams{
+					Command:     "${config.MacStripPath} -u -r -o $out $in",
+					CommandDeps: []string{"${config.MacStripPath}"},
+				})
+		} else {
+			return nil
+		}
+	}()
 
 	// b/132822437: objcopy uses a file descriptor per .o file when called on .a files, which runs the system out of
 	// file descriptors on darwin.  Limit concurrent calls to 5 on darwin.
@@ -174,11 +181,17 @@
 		}
 	}()
 
-	darwinLipo = pctx.AndroidStaticRule("darwinLipo",
-		blueprint.RuleParams{
-			Command:     "${config.MacLipoPath} -create -output $out $in",
-			CommandDeps: []string{"${config.MacLipoPath}"},
-		})
+	darwinLipo = func() blueprint.Rule {
+		if runtime.GOOS == "darwin" {
+			return pctx.AndroidStaticRule("darwinLipo",
+				blueprint.RuleParams{
+					Command:     "${config.MacLipoPath} -create -output $out $in",
+					CommandDeps: []string{"${config.MacLipoPath}"},
+				})
+		} else {
+			return nil
+		}
+	}()
 
 	_ = pctx.SourcePathVariable("archiveRepackPath", "build/soong/scripts/archive_repack.sh")
 
@@ -798,9 +811,12 @@
 // Generate a Rust staticlib from a list of rlibDeps. Returns nil if TransformRlibstoStaticlib is nil or rlibDeps is empty.
 func generateRustStaticlib(ctx android.ModuleContext, rlibDeps []RustRlibDep) android.Path {
 	if TransformRlibstoStaticlib == nil && len(rlibDeps) > 0 {
-		// This should only be reachable if a module defines static_rlibs and
+		// This should only be reachable if a module defines Rust deps in static_libs and
 		// soong-rust hasn't been loaded alongside soong-cc (e.g. in soong-cc tests).
-		panic(fmt.Errorf("TransformRlibstoStaticlib is not set and static_rlibs is defined in %s", ctx.ModuleName()))
+		panic(fmt.Errorf(
+			"TransformRlibstoStaticlib is not set and rust deps are defined in static_libs for %s",
+			ctx.ModuleName()))
+
 	} else if len(rlibDeps) == 0 {
 		return nil
 	}
@@ -829,6 +845,7 @@
 func genRustStaticlibSrcFile(crateNames []string) string {
 	lines := []string{
 		"// @Soong generated Source",
+		"#![no_std]", // pre-emptively set no_std to support both std and no_std.
 	}
 	for _, crate := range crateNames {
 		lines = append(lines, fmt.Sprintf("extern crate %s;", crate))
@@ -894,6 +911,16 @@
 		"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags,
 		"crtEnd":        strings.Join(crtEnd.Strings(), " "),
 	}
+
+	// On Windows, we always generate a PDB file
+	// --strip-debug is needed to also keep COFF symbols which are needed when
+	// we patch binaries with symbol_inject.
+	if ctx.Windows() {
+		pdb := outputFile.ReplaceExtension(ctx, "pdb")
+		args["ldFlags"] = args["ldFlags"] + " -Wl,--strip-debug -Wl,--pdb=" + pdb.String() + " "
+		implicitOutputs = append(slices.Clone(implicitOutputs), pdb)
+	}
+
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
 		rule = ldRE
 		args["implicitOutputs"] = strings.Join(implicitOutputs.Strings(), ",")
diff --git a/cc/cc.go b/cc/cc.go
index 7ea726e..e47e3fc 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,7 +34,6 @@
 	"android/soong/cc/config"
 	"android/soong/fuzz"
 	"android/soong/genrule"
-	"android/soong/multitree"
 )
 
 func init() {
@@ -48,11 +47,10 @@
 	ctx.RegisterModuleType("cc_defaults", defaultsFactory)
 
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("sdk", sdkMutator).Parallel()
+		ctx.Transition("sdk", &sdkTransitionMutator{})
 		ctx.BottomUp("llndk", llndkMutator).Parallel()
-		ctx.BottomUp("link", LinkageMutator).Parallel()
-		ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel()
-		ctx.BottomUp("version", versionMutator).Parallel()
+		ctx.Transition("link", &linkageTransitionMutator{})
+		ctx.Transition("version", &versionTransitionMutator{})
 		ctx.BottomUp("begin", BeginMutator).Parallel()
 	})
 
@@ -61,10 +59,10 @@
 			san.registerMutators(ctx)
 		}
 
-		ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
+		ctx.BottomUp("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
 		ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
 
-		ctx.TopDown("fuzz_deps", fuzzMutatorDeps)
+		ctx.BottomUp("fuzz_deps", fuzzMutatorDeps)
 
 		ctx.Transition("coverage", &coverageTransitionMutator{})
 
@@ -75,7 +73,7 @@
 		ctx.Transition("lto", &ltoTransitionMutator{})
 
 		ctx.BottomUp("check_linktype", checkLinkTypeMutator).Parallel()
-		ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
+		ctx.BottomUp("double_loadable", checkDoubleLoadableLibraries).Parallel()
 	})
 
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
@@ -99,7 +97,6 @@
 	StaticLibs, LateStaticLibs, WholeStaticLibs []string
 	HeaderLibs                                  []string
 	RuntimeLibs                                 []string
-	Rlibs                                       []string
 
 	// UnexportedStaticLibs are static libraries that are also passed to -Wl,--exclude-libs= to
 	// prevent automatically exporting symbols.
@@ -361,6 +358,8 @@
 	Recovery_available *bool
 
 	// Used by imageMutator, set by ImageMutatorBegin()
+	VendorVariantNeeded        bool `blueprint:"mutated"`
+	ProductVariantNeeded       bool `blueprint:"mutated"`
 	CoreVariantNeeded          bool `blueprint:"mutated"`
 	RamdiskVariantNeeded       bool `blueprint:"mutated"`
 	VendorRamdiskVariantNeeded bool `blueprint:"mutated"`
@@ -613,7 +612,7 @@
 	coverageOutputFilePath() android.OptionalPath
 
 	// Get the deps that have been explicitly specified in the properties.
-	linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps
+	linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps
 
 	moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON)
 }
@@ -744,11 +743,6 @@
 	return d.Kind == staticLibraryDependency
 }
 
-// rlib returns true if the libraryDependencyTag is tagging an rlib dependency.
-func (d libraryDependencyTag) rlib() bool {
-	return d.Kind == rlibLibraryDependency
-}
-
 func (d libraryDependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
 	if d.shared() {
 		return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
@@ -803,7 +797,6 @@
 	dataLibDepTag         = dependencyTag{name: "data lib"}
 	dataBinDepTag         = dependencyTag{name: "data bin"}
 	runtimeDepTag         = installDependencyTag{name: "runtime lib"}
-	testPerSrcDepTag      = dependencyTag{name: "test_per_src"}
 	stubImplDepTag        = dependencyTag{name: "stub_impl"}
 	JniFuzzLibTag         = dependencyTag{name: "jni_fuzz_lib_tag"}
 	FdoProfileTag         = dependencyTag{name: "fdo_profile"}
@@ -830,11 +823,6 @@
 	return depTag == runtimeDepTag
 }
 
-func IsTestPerSrcDepTag(depTag blueprint.DependencyTag) bool {
-	ccDepTag, ok := depTag.(dependencyTag)
-	return ok && ccDepTag == testPerSrcDepTag
-}
-
 // Module contains the properties and members used by all C/C++ module types, and implements
 // the blueprint.Module interface.  It delegates to compiler, linker, and installer interfaces
 // to construct the output file.  Behavior can be customized with a Customizer, or "decorator",
@@ -915,19 +903,19 @@
 	hideApexVariantFromMake bool
 
 	logtagsPaths android.Paths
+
+	WholeRustStaticlib bool
+
+	hasAidl         bool
+	hasLex          bool
+	hasProto        bool
+	hasRenderscript bool
+	hasSysprop      bool
+	hasWinMsg       bool
+	hasYacc         bool
 }
 
 func (c *Module) AddJSONData(d *map[string]interface{}) {
-	var hasAidl, hasLex, hasProto, hasRenderscript, hasSysprop, hasWinMsg, hasYacc bool
-	if b, ok := c.compiler.(*baseCompiler); ok {
-		hasAidl = b.hasSrcExt(".aidl")
-		hasLex = b.hasSrcExt(".l") || b.hasSrcExt(".ll")
-		hasProto = b.hasSrcExt(".proto")
-		hasRenderscript = b.hasSrcExt(".rscript") || b.hasSrcExt(".fs")
-		hasSysprop = b.hasSrcExt(".sysprop")
-		hasWinMsg = b.hasSrcExt(".mc")
-		hasYacc = b.hasSrcExt(".y") || b.hasSrcExt(".yy")
-	}
 	c.AndroidModuleBase().AddJSONData(d)
 	(*d)["Cc"] = map[string]interface{}{
 		"SdkVersion":             c.SdkVersion(),
@@ -957,14 +945,14 @@
 		"IsVendorPublicLibrary":  c.IsVendorPublicLibrary(),
 		"ApexSdkVersion":         c.apexSdkVersion,
 		"TestFor":                c.TestFor(),
-		"AidlSrcs":               hasAidl,
-		"LexSrcs":                hasLex,
-		"ProtoSrcs":              hasProto,
-		"RenderscriptSrcs":       hasRenderscript,
-		"SyspropSrcs":            hasSysprop,
-		"WinMsgSrcs":             hasWinMsg,
-		"YaccSrsc":               hasYacc,
-		"OnlyCSrcs":              !(hasAidl || hasLex || hasProto || hasRenderscript || hasSysprop || hasWinMsg || hasYacc),
+		"AidlSrcs":               c.hasAidl,
+		"LexSrcs":                c.hasLex,
+		"ProtoSrcs":              c.hasProto,
+		"RenderscriptSrcs":       c.hasRenderscript,
+		"SyspropSrcs":            c.hasSysprop,
+		"WinMsgSrcs":             c.hasWinMsg,
+		"YaccSrsc":               c.hasYacc,
+		"OnlyCSrcs":              !(c.hasAidl || c.hasLex || c.hasProto || c.hasRenderscript || c.hasSysprop || c.hasWinMsg || c.hasYacc),
 		"OptimizeForSize":        c.OptimizeForSize(),
 	}
 }
@@ -981,8 +969,8 @@
 	return c.Properties.HideFromMake
 }
 
-func (c *Module) RequiredModuleNames() []string {
-	required := android.CopyOf(c.ModuleBase.RequiredModuleNames())
+func (c *Module) RequiredModuleNames(ctx android.ConfigurableEvaluatorContext) []string {
+	required := android.CopyOf(c.ModuleBase.RequiredModuleNames(ctx))
 	if c.ImageVariation().Variation == android.CoreVariation {
 		required = append(required, c.Properties.Target.Platform.Required...)
 		required = removeListFromList(required, c.Properties.Target.Platform.Exclude_required)
@@ -1037,13 +1025,6 @@
 	return ""
 }
 
-func (c *Module) NdkPrebuiltStl() bool {
-	if _, ok := c.linker.(*ndkPrebuiltStlLinker); ok {
-		return true
-	}
-	return false
-}
-
 func (c *Module) StubDecorator() bool {
 	if _, ok := c.linker.(*stubDecorator); ok {
 		return true
@@ -1097,16 +1078,6 @@
 	return false
 }
 
-func (c *Module) IsNdkPrebuiltStl() bool {
-	if c.linker == nil {
-		return false
-	}
-	if _, ok := c.linker.(*ndkPrebuiltStlLinker); ok {
-		return true
-	}
-	return false
-}
-
 func (c *Module) RlibStd() bool {
 	panic(fmt.Errorf("RlibStd called on non-Rust module: %q", c.BaseModuleName()))
 }
@@ -1190,6 +1161,16 @@
 	panic(fmt.Errorf("BuildSharedVariant called on non-library module: %q", c.BaseModuleName()))
 }
 
+func (c *Module) BuildRlibVariant() bool {
+	// cc modules can never build rlib variants
+	return false
+}
+
+func (c *Module) IsRustFFI() bool {
+	// cc modules are not Rust modules
+	return false
+}
+
 func (c *Module) Module() android.Module {
 	return c
 }
@@ -1378,17 +1359,11 @@
 }
 
 func (c *Module) isCfi() bool {
-	if sanitize := c.sanitize; sanitize != nil {
-		return Bool(sanitize.Properties.SanitizeMutated.Cfi)
-	}
-	return false
+	return c.sanitize.isSanitizerEnabled(cfi)
 }
 
 func (c *Module) isFuzzer() bool {
-	if sanitize := c.sanitize; sanitize != nil {
-		return Bool(sanitize.Properties.SanitizeMutated.Fuzzer)
-	}
-	return false
+	return c.sanitize.isSanitizerEnabled(Fuzzer)
 }
 
 func (c *Module) isNDKStubLibrary() bool {
@@ -1778,11 +1753,6 @@
 	return nil
 }
 
-func (c *Module) IsTestPerSrcAllTestsVariation() bool {
-	test, ok := c.linker.(testPerSrc)
-	return ok && test.isAllTestsVariation()
-}
-
 func (c *Module) DataPaths() []android.DataPath {
 	if p, ok := c.installer.(interface {
 		dataPaths() []android.DataPath
@@ -1877,8 +1847,10 @@
 		"libdl":         true,
 		"libz":          true,
 		// art apex
+		// TODO(b/234351700): Remove this when com.android.art.debug is gone.
 		"libandroidio":    true,
 		"libdexfile":      true,
+		"libdexfiled":     true, // com.android.art.debug only
 		"libnativebridge": true,
 		"libnativehelper": true,
 		"libnativeloader": true,
@@ -1935,16 +1907,6 @@
 		TopLevelTarget: c.testModule,
 	})
 
-	// Handle the case of a test module split by `test_per_src` mutator.
-	//
-	// The `test_per_src` mutator adds an extra variation named "", depending on all the other
-	// `test_per_src` variations of the test module. Set `outputFile` to an empty path for this
-	// module and return early, as this module does not produce an output file per se.
-	if c.IsTestPerSrcAllTestsVariation() {
-		c.outputFile = android.OptionalPath{}
-		return
-	}
-
 	c.Properties.SubName = GetSubnameProperty(actx, c)
 	apexInfo, _ := android.ModuleProvider(actx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
@@ -2117,8 +2079,68 @@
 		if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
 			moduleInfoJSON.Uninstallable = true
 		}
-
 	}
+
+	buildComplianceMetadataInfo(ctx, c, deps)
+
+	if b, ok := c.compiler.(*baseCompiler); ok {
+		c.hasAidl = b.hasSrcExt(ctx, ".aidl")
+		c.hasLex = b.hasSrcExt(ctx, ".l") || b.hasSrcExt(ctx, ".ll")
+		c.hasProto = b.hasSrcExt(ctx, ".proto")
+		c.hasRenderscript = b.hasSrcExt(ctx, ".rscript") || b.hasSrcExt(ctx, ".fs")
+		c.hasSysprop = b.hasSrcExt(ctx, ".sysprop")
+		c.hasWinMsg = b.hasSrcExt(ctx, ".mc")
+		c.hasYacc = b.hasSrcExt(ctx, ".y") || b.hasSrcExt(ctx, ".yy")
+	}
+
+	c.setOutputFiles(ctx)
+}
+
+func (c *Module) setOutputFiles(ctx ModuleContext) {
+	if c.outputFile.Valid() {
+		ctx.SetOutputFiles(android.Paths{c.outputFile.Path()}, "")
+	} else {
+		ctx.SetOutputFiles(android.Paths{}, "")
+	}
+	if c.linker != nil {
+		ctx.SetOutputFiles(android.PathsIfNonNil(c.linker.unstrippedOutputFilePath()), "unstripped")
+		ctx.SetOutputFiles(android.PathsIfNonNil(c.linker.strippedAllOutputFilePath()), "stripped_all")
+	}
+}
+
+func buildComplianceMetadataInfo(ctx ModuleContext, c *Module, deps PathDeps) {
+	// Dump metadata that can not be done in android/compliance-metadata.go
+	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
+	complianceMetadataInfo.SetStringValue(android.ComplianceMetadataProp.IS_STATIC_LIB, strconv.FormatBool(ctx.static()))
+	complianceMetadataInfo.SetStringValue(android.ComplianceMetadataProp.BUILT_FILES, c.outputFile.String())
+
+	// Static deps
+	staticDeps := ctx.GetDirectDepsWithTag(StaticDepTag(false))
+	staticDepNames := make([]string, 0, len(staticDeps))
+	for _, dep := range staticDeps {
+		staticDepNames = append(staticDepNames, dep.Name())
+	}
+
+	staticDepPaths := make([]string, 0, len(deps.StaticLibs))
+	for _, dep := range deps.StaticLibs {
+		staticDepPaths = append(staticDepPaths, dep.String())
+	}
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEPS, android.FirstUniqueStrings(staticDepNames))
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEP_FILES, android.FirstUniqueStrings(staticDepPaths))
+
+	// Whole static deps
+	wholeStaticDeps := ctx.GetDirectDepsWithTag(StaticDepTag(true))
+	wholeStaticDepNames := make([]string, 0, len(wholeStaticDeps))
+	for _, dep := range wholeStaticDeps {
+		wholeStaticDepNames = append(wholeStaticDepNames, dep.Name())
+	}
+
+	wholeStaticDepPaths := make([]string, 0, len(deps.WholeStaticLibs))
+	for _, dep := range deps.WholeStaticLibs {
+		wholeStaticDepPaths = append(wholeStaticDepPaths, dep.String())
+	}
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.WHOLE_STATIC_DEPS, android.FirstUniqueStrings(wholeStaticDepNames))
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.WHOLE_STATIC_DEP_FILES, android.FirstUniqueStrings(wholeStaticDepPaths))
 }
 
 func (c *Module) maybeUnhideFromMake() {
@@ -2228,7 +2250,6 @@
 
 	deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
 	deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
-	deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
 	deps.LateStaticLibs = android.LastUniqueStrings(deps.LateStaticLibs)
 	deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
 	deps.LateSharedLibs = android.LastUniqueStrings(deps.LateSharedLibs)
@@ -2339,24 +2360,6 @@
 	}
 }
 
-func GetApiImports(c LinkableInterface, actx android.BottomUpMutatorContext) multitree.ApiImportInfo {
-	apiImportInfo := multitree.ApiImportInfo{}
-
-	if c.Device() {
-		var apiImportModule []blueprint.Module
-		if actx.OtherModuleExists("api_imports") {
-			apiImportModule = actx.AddDependency(c, nil, "api_imports")
-			if len(apiImportModule) > 0 && apiImportModule[0] != nil {
-				apiInfo, _ := android.OtherModuleProvider(actx, apiImportModule[0], multitree.ApiImportsProvider)
-				apiImportInfo = apiInfo
-				android.SetProvider(actx, multitree.ApiImportsProvider, apiInfo)
-			}
-		}
-	}
-
-	return apiImportInfo
-}
-
 func GetReplaceModuleName(lib string, replaceMap map[string]string) string {
 	if snapshot, ok := replaceMap[lib]; ok {
 		return snapshot
@@ -2426,11 +2429,6 @@
 			// NDK Variant
 			return true
 		}
-
-		if c.isImportedApiLibrary() {
-			// API Library should depend on API headers
-			return true
-		}
 	}
 
 	return false
@@ -2450,19 +2448,10 @@
 	ctx.ctx = ctx
 
 	deps := c.deps(ctx)
-	apiImportInfo := GetApiImports(c, actx)
 
 	apiNdkLibs := []string{}
 	apiLateNdkLibs := []string{}
 
-	if c.shouldUseApiSurface() {
-		deps.SharedLibs, apiNdkLibs = rewriteLibsForApiImports(c, deps.SharedLibs, apiImportInfo.SharedLibs, ctx.Config())
-		deps.LateSharedLibs, apiLateNdkLibs = rewriteLibsForApiImports(c, deps.LateSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
-		deps.SystemSharedLibs, _ = rewriteLibsForApiImports(c, deps.SystemSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
-		deps.ReexportHeaderLibHeaders, _ = rewriteLibsForApiImports(c, deps.ReexportHeaderLibHeaders, apiImportInfo.SharedLibs, ctx.Config())
-		deps.ReexportSharedLibHeaders, _ = rewriteLibsForApiImports(c, deps.ReexportSharedLibHeaders, apiImportInfo.SharedLibs, ctx.Config())
-	}
-
 	c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
 
 	variantNdkLibs := []string{}
@@ -2496,15 +2485,16 @@
 			depTag.reexportFlags = true
 		}
 
-		// Check header lib replacement from API surface first, and then check again with VSDK
-		if c.shouldUseApiSurface() {
-			lib = GetReplaceModuleName(lib, apiImportInfo.HeaderLibs)
-		}
-
 		if c.isNDKStubLibrary() {
-			// ndk_headers do not have any variations
-			actx.AddFarVariationDependencies([]blueprint.Variation{}, depTag, lib)
-		} else if c.IsStubs() && !c.isImportedApiLibrary() {
+			variationExists := actx.OtherModuleDependencyVariantExists(nil, lib)
+			if variationExists {
+				actx.AddVariationDependencies(nil, depTag, lib)
+			} else {
+				// dependencies to ndk_headers fall here as ndk_headers do not have
+				// any variants.
+				actx.AddFarVariationDependencies([]blueprint.Variation{}, depTag, lib)
+			}
+		} else if c.IsStubs() {
 			actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
 				depTag, lib)
 		} else {
@@ -2526,7 +2516,7 @@
 	if c.ImageVariation().Variation == android.CoreVariation && c.Device() &&
 		c.Target().NativeBridge == android.NativeBridgeDisabled {
 		actx.AddVariationDependencies(
-			[]blueprint.Variation{{Mutator: "image", Variation: VendorVariation}},
+			[]blueprint.Variation{{Mutator: "image", Variation: android.VendorVariation}},
 			llndkHeaderLibTag,
 			deps.LlndkHeaderLibs...)
 	}
@@ -2540,28 +2530,20 @@
 	}
 
 	for _, lib := range deps.StaticLibs {
+		// Some dependencies listed in static_libs might actually be rust_ffi rlib variants.
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency}
+
 		if inList(lib, deps.ReexportStaticLibHeaders) {
 			depTag.reexportFlags = true
 		}
 		if inList(lib, deps.ExcludeLibsForApex) {
 			depTag.excludeInApex = true
 		}
-
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
 		}, depTag, lib)
 	}
 
-	for _, lib := range deps.Rlibs {
-		depTag := libraryDependencyTag{Kind: rlibLibraryDependency}
-		actx.AddVariationDependencies([]blueprint.Variation{
-			{Mutator: "link", Variation: ""},
-			{Mutator: "rust_libraries", Variation: "rlib"},
-			{Mutator: "rust_stdlinkage", Variation: "rlib-std"},
-		}, depTag, lib)
-	}
-
 	// staticUnwinderDep is treated as staticDep for Q apexes
 	// so that native libraries/binaries are linked with static unwinder
 	// because Q libc doesn't have unwinder APIs
@@ -2588,22 +2570,12 @@
 		}
 
 		name, version := StubsLibNameAndVersion(lib)
-		if apiLibraryName, ok := apiImportInfo.SharedLibs[name]; ok && !ctx.OtherModuleExists(name) {
-			name = apiLibraryName
-		}
 		sharedLibNames = append(sharedLibNames, name)
 
 		variations := []blueprint.Variation{
 			{Mutator: "link", Variation: "shared"},
 		}
-
-		if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) {
-			AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
-		}
-
-		if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok {
-			AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, apiLibraryName, version, false)
-		}
+		AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
 	}
 
 	for _, lib := range deps.LateStaticLibs {
@@ -2698,7 +2670,6 @@
 		)
 	}
 
-	updateImportedLibraryDependency(ctx)
 }
 
 func BeginMutator(ctx android.BottomUpMutatorContext) {
@@ -2727,10 +2698,6 @@
 		return
 	}
 
-	// TODO(b/244244438) : Remove this once all variants are implemented
-	if ccFrom, ok := from.(*Module); ok && ccFrom.isImportedApiLibrary() {
-		return
-	}
 	if from.SdkVersion() == "" {
 		// Platform code can link to anything
 		return
@@ -2748,19 +2715,11 @@
 		return
 	}
 	if c, ok := to.(*Module); ok {
-		if c.NdkPrebuiltStl() {
-			// These are allowed, but they don't set sdk_version
-			return
-		}
 		if c.StubDecorator() {
 			// These aren't real libraries, but are the stub shared libraries that are included in
 			// the NDK.
 			return
 		}
-		if c.isImportedApiLibrary() {
-			// Imported library from the API surface is a stub library built against interface definition.
-			return
-		}
 	}
 
 	if strings.HasPrefix(ctx.ModuleName(), "libclang_rt.") && to.Module().Name() == "libc++" {
@@ -2841,7 +2800,7 @@
 // If a library has a vendor variant and is a (transitive) dependency of an LLNDK library,
 // it is subject to be double loaded. Such lib should be explicitly marked as double_loadable: true
 // or as vndk-sp (vndk: { enabled: true, support_system_process: true}).
-func checkDoubleLoadableLibraries(ctx android.TopDownMutatorContext) {
+func checkDoubleLoadableLibraries(ctx android.BottomUpMutatorContext) {
 	check := func(child, parent android.Module) bool {
 		to, ok := child.(*Module)
 		if !ok {
@@ -2936,47 +2895,6 @@
 
 	skipModuleList := map[string]bool{}
 
-	var apiImportInfo multitree.ApiImportInfo
-	hasApiImportInfo := false
-
-	ctx.VisitDirectDeps(func(dep android.Module) {
-		if dep.Name() == "api_imports" {
-			apiImportInfo, _ = android.OtherModuleProvider(ctx, dep, multitree.ApiImportsProvider)
-			hasApiImportInfo = true
-		}
-	})
-
-	if hasApiImportInfo {
-		targetStubModuleList := map[string]string{}
-		targetOrigModuleList := map[string]string{}
-
-		// Search for dependency which both original module and API imported library with APEX stub exists
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok {
-				targetStubModuleList[apiLibrary] = depName
-			}
-		})
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if origLibrary, ok := targetStubModuleList[depName]; ok {
-				targetOrigModuleList[origLibrary] = depName
-			}
-		})
-
-		// Decide which library should be used between original and API imported library
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if apiLibrary, ok := targetOrigModuleList[depName]; ok {
-				if ShouldUseStubForApex(ctx, dep) {
-					skipModuleList[depName] = true
-				} else {
-					skipModuleList[apiLibrary] = true
-				}
-			}
-		})
-	}
-
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
@@ -3049,7 +2967,7 @@
 		}
 
 		if dep.Target().Os != ctx.Os() {
-			ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
+			ctx.ModuleErrorf("OS mismatch between %q (%s) and %q (%s)", ctx.ModuleName(), ctx.Os().Name, depName, dep.Target().Os.Name)
 			return
 		}
 		if dep.Target().Arch.ArchType != ctx.Arch().ArchType {
@@ -3149,78 +3067,86 @@
 					panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
 				}
 
-			case libDepTag.rlib():
-				rlibDep := RustRlibDep{LibPath: linkFile.Path(), CrateName: ccDep.CrateName(), LinkDirs: ccDep.ExportedCrateLinkDirs()}
-				depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, rlibDep)
-				depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, rlibDep)
-				depPaths.IncludeDirs = append(depPaths.IncludeDirs, depExporterInfo.IncludeDirs...)
-				depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, depExporterInfo.IncludeDirs...)
-
 			case libDepTag.static():
-				staticLibraryInfo, isStaticLib := android.OtherModuleProvider(ctx, dep, StaticLibraryInfoProvider)
-				if !isStaticLib {
-					if !ctx.Config().AllowMissingDependencies() {
-						ctx.ModuleErrorf("module %q is not a static library", depName)
-					} else {
-						ctx.AddMissingDependencies([]string{depName})
-					}
-					return
-				}
+				if ccDep.RustLibraryInterface() {
+					rlibDep := RustRlibDep{LibPath: linkFile.Path(), CrateName: ccDep.CrateName(), LinkDirs: ccDep.ExportedCrateLinkDirs()}
+					depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, rlibDep)
+					depPaths.IncludeDirs = append(depPaths.IncludeDirs, depExporterInfo.IncludeDirs...)
+					if libDepTag.wholeStatic {
+						depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, depExporterInfo.IncludeDirs...)
+						depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, rlibDep)
 
-				// Stubs lib doesn't link to the static lib dependencies. Don't set
-				// linkFile, depFile, and ptr.
-				if c.IsStubs() {
-					break
-				}
-
-				linkFile = android.OptionalPathForPath(staticLibraryInfo.StaticLibrary)
-				if libDepTag.wholeStatic {
-					ptr = &depPaths.WholeStaticLibs
-					if len(staticLibraryInfo.Objects.objFiles) > 0 {
-						depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLibraryInfo.Objects)
-					} else {
-						// This case normally catches prebuilt static
-						// libraries, but it can also occur when
-						// AllowMissingDependencies is on and the
-						// dependencies has no sources of its own
-						// but has a whole_static_libs dependency
-						// on a missing library.  We want to depend
-						// on the .a file so that there is something
-						// in the dependency tree that contains the
-						// error rule for the missing transitive
-						// dependency.
-						depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
+						// If whole_static, track this as we want to make sure that in a final linkage for a shared library,
+						// exported functions from the rust generated staticlib still exported.
+						if c.CcLibrary() && c.Shared() {
+							c.WholeRustStaticlib = true
+						}
 					}
-					depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts,
-						staticLibraryInfo.WholeStaticLibsFromPrebuilts...)
+
 				} else {
-					switch libDepTag.Order {
-					case earlyLibraryDependency:
-						panic(fmt.Errorf("early static libs not suppported"))
-					case normalLibraryDependency:
-						// static dependencies will be handled separately so they can be ordered
-						// using transitive dependencies.
-						ptr = nil
-						directStaticDeps = append(directStaticDeps, staticLibraryInfo)
-					case lateLibraryDependency:
-						ptr = &depPaths.LateStaticLibs
-					default:
-						panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
+					staticLibraryInfo, isStaticLib := android.OtherModuleProvider(ctx, dep, StaticLibraryInfoProvider)
+					if !isStaticLib {
+						if !ctx.Config().AllowMissingDependencies() {
+							ctx.ModuleErrorf("module %q is not a static library", depName)
+						} else {
+							ctx.AddMissingDependencies([]string{depName})
+						}
+						return
 					}
-				}
 
-				// We re-export the Rust static_rlibs so rlib dependencies don't need to be redeclared by cc_library_static dependents.
-				// E.g. libfoo (cc_library_static) depends on libfoo.ffi (a rust_ffi rlib), libbar depending on libfoo shouldn't have to also add libfoo.ffi to static_rlibs.
-				depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, depExporterInfo.RustRlibDeps...)
-				depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...)
+					// Stubs lib doesn't link to the static lib dependencies. Don't set
+					// linkFile, depFile, and ptr.
+					if c.IsStubs() {
+						break
+					}
 
-				if libDepTag.unexportedSymbols {
-					depPaths.LdFlags = append(depPaths.LdFlags,
-						"-Wl,--exclude-libs="+staticLibraryInfo.StaticLibrary.Base())
+					linkFile = android.OptionalPathForPath(staticLibraryInfo.StaticLibrary)
+					if libDepTag.wholeStatic {
+						ptr = &depPaths.WholeStaticLibs
+						if len(staticLibraryInfo.Objects.objFiles) > 0 {
+							depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLibraryInfo.Objects)
+						} else {
+							// This case normally catches prebuilt static
+							// libraries, but it can also occur when
+							// AllowMissingDependencies is on and the
+							// dependencies has no sources of its own
+							// but has a whole_static_libs dependency
+							// on a missing library.  We want to depend
+							// on the .a file so that there is something
+							// in the dependency tree that contains the
+							// error rule for the missing transitive
+							// dependency.
+							depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
+						}
+						depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts,
+							staticLibraryInfo.WholeStaticLibsFromPrebuilts...)
+					} else {
+						switch libDepTag.Order {
+						case earlyLibraryDependency:
+							panic(fmt.Errorf("early static libs not supported"))
+						case normalLibraryDependency:
+							// static dependencies will be handled separately so they can be ordered
+							// using transitive dependencies.
+							ptr = nil
+							directStaticDeps = append(directStaticDeps, staticLibraryInfo)
+						case lateLibraryDependency:
+							ptr = &depPaths.LateStaticLibs
+						default:
+							panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
+						}
+					}
+
+					// Collect any exported Rust rlib deps from static libraries which have been included as whole_static_libs
+					depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...)
+
+					if libDepTag.unexportedSymbols {
+						depPaths.LdFlags = append(depPaths.LdFlags,
+							"-Wl,--exclude-libs="+staticLibraryInfo.StaticLibrary.Base())
+					}
 				}
 			}
 
-			if libDepTag.static() && !libDepTag.wholeStatic {
+			if libDepTag.static() && !libDepTag.wholeStatic && !ccDep.RustLibraryInterface() {
 				if !ccDep.CcLibraryInterface() || !ccDep.Static() {
 					ctx.ModuleErrorf("module %q not a static library", depName)
 					return
@@ -3307,12 +3233,14 @@
 				c.Properties.AndroidMkSharedLibs = append(
 					c.Properties.AndroidMkSharedLibs, makeLibName)
 			case libDepTag.static():
-				if libDepTag.wholeStatic {
-					c.Properties.AndroidMkWholeStaticLibs = append(
-						c.Properties.AndroidMkWholeStaticLibs, makeLibName)
-				} else {
-					c.Properties.AndroidMkStaticLibs = append(
-						c.Properties.AndroidMkStaticLibs, makeLibName)
+				if !ccDep.RustLibraryInterface() {
+					if libDepTag.wholeStatic {
+						c.Properties.AndroidMkWholeStaticLibs = append(
+							c.Properties.AndroidMkWholeStaticLibs, makeLibName)
+					} else {
+						c.Properties.AndroidMkStaticLibs = append(
+							c.Properties.AndroidMkStaticLibs, makeLibName)
+					}
 				}
 			}
 		} else if !c.IsStubs() {
@@ -3395,17 +3323,7 @@
 		// bootstrap modules, always link to non-stub variant
 		isNotInPlatform := dep.(android.ApexModule).NotInPlatform()
 
-		isApexImportedApiLibrary := false
-
-		if cc, ok := dep.(*Module); ok {
-			if apiLibrary, ok := cc.linker.(*apiLibraryDecorator); ok {
-				if apiLibrary.hasApexStubs() {
-					isApexImportedApiLibrary = true
-				}
-			}
-		}
-
-		useStubs = (isNotInPlatform || isApexImportedApiLibrary) && !bootstrap
+		useStubs = isNotInPlatform && !bootstrap
 
 		if useStubs {
 			// Another exception: if this module is a test for an APEX, then
@@ -3430,7 +3348,7 @@
 			// 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 !isApexImportedApiLibrary && android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
+			if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
 				useStubs = false
 			}
 		}
@@ -3593,28 +3511,6 @@
 	return c.outputFile
 }
 
-func (c *Module) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		if c.outputFile.Valid() {
-			return android.Paths{c.outputFile.Path()}, nil
-		}
-		return android.Paths{}, nil
-	case "unstripped":
-		if c.linker != nil {
-			return android.PathsIfNonNil(c.linker.unstrippedOutputFilePath()), nil
-		}
-		return nil, nil
-	case "stripped_all":
-		if c.linker != nil {
-			return android.PathsIfNonNil(c.linker.strippedAllOutputFilePath()), nil
-		}
-		return nil, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (c *Module) static() bool {
 	if static, ok := c.linker.(interface {
 		static() bool
@@ -3738,14 +3634,12 @@
 }
 
 // Overrides ApexModule.IsInstallabeToApex()
-// Only shared/runtime libraries and "test_per_src" tests are installable to APEX.
+// Only shared/runtime libraries .
 func (c *Module) IsInstallableToApex() bool {
 	if lib := c.library; lib != nil {
 		// Stub libs and prebuilt libs in a versioned SDK are not
 		// installable to APEX even though they are shared libs.
 		return lib.shared() && !lib.buildStubs()
-	} else if _, ok := c.linker.(testPerSrc); ok {
-		return true
 	}
 	return false
 }
@@ -3935,7 +3829,6 @@
 	headerLibrary
 	testBin // testBinary already declared
 	ndkLibrary
-	ndkPrebuiltStl
 )
 
 func (c *Module) typ() moduleType {
@@ -3974,8 +3867,6 @@
 		return sharedLibrary
 	} else if c.isNDKStubLibrary() {
 		return ndkLibrary
-	} else if c.IsNdkPrebuiltStl() {
-		return ndkPrebuiltStl
 	}
 	return unknownType
 }
@@ -4041,11 +3932,6 @@
 	return c.Properties.IsSdkVariant
 }
 
-func (c *Module) isImportedApiLibrary() bool {
-	_, ok := c.linker.(*apiLibraryDecorator)
-	return ok
-}
-
 func kytheExtractAllFactory() android.Singleton {
 	return &kytheExtractAllSingleton{}
 }
@@ -4087,6 +3973,13 @@
 	return c.ModuleBase.BaseModuleName()
 }
 
+func (c *Module) stubsSymbolFilePath() android.Path {
+	if library, ok := c.linker.(*libraryDecorator); ok {
+		return library.stubsSymbolFilePath
+	}
+	return android.OptionalPath{}.Path()
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var BoolPtr = proptools.BoolPtr
diff --git a/cc/cc_test.go b/cc/cc_test.go
index c2bb25a..3f3347b 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -49,17 +49,30 @@
 
 func registerTestMutators(ctx android.RegistrationContext) {
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("apex", testApexMutator).Parallel()
+		ctx.Transition("apex", &testApexTransitionMutator{})
 	})
 }
 
-func testApexMutator(mctx android.BottomUpMutatorContext) {
-	modules := mctx.CreateVariations(apexVariationName)
+type testApexTransitionMutator struct{}
+
+func (t *testApexTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	return []string{apexVariationName}
+}
+
+func (t *testApexTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (t *testApexTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	return incomingVariation
+}
+
+func (t *testApexTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
 	apexInfo := android.ApexInfo{
 		ApexVariationName: apexVariationName,
 		MinSdkVersion:     android.ApiLevelForTest(apexVersion),
 	}
-	mctx.SetVariationProvider(modules[0], android.ApexInfoProvider, apexInfo)
+	android.SetProvider(ctx, android.ApexInfoProvider, apexInfo)
 }
 
 // testCcWithConfig runs tests using the prepareForCcTest
@@ -300,13 +313,9 @@
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 
 	ctx := testCcWithConfig(t, config)
-	module := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon").Module()
-	testBinary := module.(*Module).linker.(*testBinary)
-	outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
-	if err != nil {
-		t.Errorf("Expected cc_test to produce output files, error: %s", err)
-		return
-	}
+	testingModule := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon")
+	testBinary := testingModule.Module().(*Module).linker.(*testBinary)
+	outputFiles := testingModule.OutputFiles(ctx, t, "")
 	if len(outputFiles) != 1 {
 		t.Errorf("expected exactly one output file. output files: [%s]", outputFiles)
 		return
@@ -356,12 +365,10 @@
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 
 	ctx := testCcWithConfig(t, config)
-	module := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon").Module()
+	testingModule := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon")
+	module := testingModule.Module()
 	testBinary := module.(*Module).linker.(*testBinary)
-	outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
-	if err != nil {
-		t.Fatalf("Expected cc_test to produce output files, error: %s", err)
-	}
+	outputFiles := testingModule.OutputFiles(ctx, t, "")
 	if len(outputFiles) != 1 {
 		t.Fatalf("expected exactly one output file. output files: [%s]", outputFiles)
 	}
@@ -857,7 +864,7 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	staticLibInfo, _ := android.SingletonModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
+	staticLibInfo, _ := android.OtherModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
 	actual := android.Paths(staticLibInfo.TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
 	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
 
@@ -893,7 +900,7 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	staticLibInfo, _ := android.SingletonModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
+	staticLibInfo, _ := android.OtherModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
 	actual := android.Paths(staticLibInfo.TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
 	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b"})
 
@@ -933,7 +940,7 @@
 
 	cc_prebuilt_library_shared {
 		name: "libllndkprebuilt",
-		stubs: { versions: ["1", "2"] },
+		stubs: { versions: ["1", "2"] , symbol_file: "libllndkprebuilt.map.txt" },
 		llndk: {
 			symbol_file: "libllndkprebuilt.map.txt",
 		},
@@ -1005,7 +1012,7 @@
 	checkExportedIncludeDirs := func(module, variant string, expectedSystemDirs []string, expectedDirs ...string) {
 		t.Helper()
 		m := result.ModuleForTests(module, variant).Module()
-		f, _ := android.SingletonModuleProvider(result, m, FlagExporterInfoProvider)
+		f, _ := android.OtherModuleProvider(result, m, FlagExporterInfoProvider)
 		android.AssertPathsRelativeToTopEquals(t, "exported include dirs for "+module+"["+variant+"]",
 			expectedDirs, f.IncludeDirs)
 		android.AssertPathsRelativeToTopEquals(t, "exported include dirs for "+module+"["+variant+"]",
@@ -1033,7 +1040,7 @@
 			}
 		}
 		vendorModule := result.ModuleForTests(module, vendorVariant).Module()
-		vendorInfo, _ := android.SingletonModuleProvider(result, vendorModule, FlagExporterInfoProvider)
+		vendorInfo, _ := android.OtherModuleProvider(result, vendorModule, FlagExporterInfoProvider)
 		vendorDirs := android.Concat(vendorInfo.IncludeDirs, vendorInfo.SystemIncludeDirs)
 		android.AssertStringEquals(t, module+" has different exported include dirs for vendor variant and ABI check",
 			android.JoinPathsWithPrefix(vendorDirs, "-I"), abiCheckFlags)
@@ -1407,12 +1414,10 @@
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 
 	ctx := testCcWithConfig(t, config)
-	module := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon").Module()
+	testingModule := ctx.ModuleForTests("main_test", "android_arm_armv7-a-neon")
+	module := testingModule.Module()
 	testBinary := module.(*Module).linker.(*testBinary)
-	outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
-	if err != nil {
-		t.Fatalf("Expected cc_test to produce output files, error: %s", err)
-	}
+	outputFiles := testingModule.OutputFiles(ctx, t, "")
 	if len(outputFiles) != 1 {
 		t.Errorf("expected exactly one output file. output files: [%s]", outputFiles)
 	}
@@ -2460,7 +2465,7 @@
 
 	checkIncludeDirs := func(t *testing.T, ctx *android.TestContext, module android.Module, checkers ...exportedChecker) {
 		t.Helper()
-		exported, _ := android.SingletonModuleProvider(ctx, module, FlagExporterInfoProvider)
+		exported, _ := android.OtherModuleProvider(ctx, module, FlagExporterInfoProvider)
 		name := module.Name()
 
 		for _, checker := range checkers {
@@ -2768,7 +2773,7 @@
 		"external/foo/libarm",
 		"external/foo/lib32",
 		"external/foo/libandroid_arm",
-		"defaults/cc/common/ndk_libc++_shared",
+		"defaults/cc/common/ndk_libc++_shared_include_dirs",
 	}
 
 	conly := []string{"-fPIC", "${config.CommonGlobalConlyflags}"}
@@ -2908,8 +2913,6 @@
 				PrepareForIntegrationTestWithCc,
 				android.FixtureAddTextFile("external/foo/Android.bp", bp),
 			).RunTest(t)
-			// Use the arm variant instead of the arm64 variant so that it gets headers from
-			// ndk_libandroid_support to test LateStaticLibs.
 			cflags := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_sdk_static").Output("obj/external/foo/foo.o").Args["cFlags"]
 
 			var includes []string
@@ -3120,12 +3123,8 @@
  `
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
 	ctx := testCcWithConfig(t, config)
-	module := ctx.ModuleForTests("test_lib", "android_arm_armv7-a-neon_shared").Module()
-	outputFile, err := module.(android.OutputFileProducer).OutputFiles("stripped_all")
-	if err != nil {
-		t.Errorf("Expected cc_library to produce output files, error: %s", err)
-		return
-	}
+	testingModule := ctx.ModuleForTests("test_lib", "android_arm_armv7-a-neon_shared")
+	outputFile := testingModule.OutputFiles(ctx, t, "stripped_all")
 	if !strings.HasSuffix(outputFile.Strings()[0], "/stripped_all/test_lib.so") {
 		t.Errorf("Unexpected output file: %s", outputFile.Strings()[0])
 		return
@@ -3208,12 +3207,7 @@
 
 	ctx = android.GroupFixturePreparers(
 		prepareForCcTest,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			if variables.BuildFlags == nil {
-				variables.BuildFlags = make(map[string]string)
-			}
-			variables.BuildFlags["RELEASE_BOARD_API_LEVEL_FROZEN"] = "true"
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_BOARD_API_LEVEL_FROZEN", "true"),
 	).RunTestWithBp(t, bp)
 	testSdkVersionFlag("libfoo", "30")
 	testSdkVersionFlag("libbar", "29")
diff --git a/cc/cc_test_only_property_test.go b/cc/cc_test_only_property_test.go
index c14f34e..972e86b 100644
--- a/cc/cc_test_only_property_test.go
+++ b/cc/cc_test_only_property_test.go
@@ -78,38 +78,6 @@
 	}
 }
 
-func TestTestOnlyValueWithTestPerSrcProp(t *testing.T) {
-	t.Parallel()
-	ctx := android.GroupFixturePreparers(
-		prepareForCcTest,
-	).RunTestWithBp(t, `
-                // These should be test-only
-                cc_test { name: "cc-test",
-                          gtest: false,
-                          test_per_src: true,
-                          srcs: ["foo_test.cpp"],
-                          test_options: { unit_test: false, },
-                         }
-	`)
-
-	// Ensure all variation of test-per-src tests are marked test-only.
-	ctx.VisitAllModules(func(m blueprint.Module) {
-		testOnly := false
-		if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok {
-			if provider.TestOnly {
-				testOnly = true
-			}
-		}
-		if module, ok := m.(*Module); ok {
-			if testModule, ok := module.installer.(*testBinary); ok {
-				if !testOnly && *testModule.Properties.Test_per_src {
-					t.Errorf("%v is not test-only but should be", m)
-				}
-			}
-		}
-	})
-}
-
 func TestTestOnlyInTeamsProto(t *testing.T) {
 	t.Parallel()
 	ctx := android.GroupFixturePreparers(
diff --git a/cc/ccdeps.go b/cc/ccdeps.go
index d30abba..469fe31 100644
--- a/cc/ccdeps.go
+++ b/cc/ccdeps.go
@@ -85,9 +85,8 @@
 	moduleDeps := ccDeps{}
 	moduleInfos := map[string]ccIdeInfo{}
 
-	// Track which projects have already had CMakeLists.txt generated to keep the first
-	// variant for each project.
-	seenProjects := map[string]bool{}
+	// Track if best variant (device arch match) has been found.
+	bestVariantFound := map[string]bool{}
 
 	pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
 	moduleDeps.C_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cClang)
@@ -96,7 +95,7 @@
 	ctx.VisitAllModules(func(module android.Module) {
 		if ccModule, ok := module.(*Module); ok {
 			if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
-				generateCLionProjectData(ctx, compiledModule, ccModule, seenProjects, moduleInfos)
+				generateCLionProjectData(ctx, compiledModule, ccModule, bestVariantFound, moduleInfos)
 			}
 		}
 	})
@@ -180,26 +179,30 @@
 }
 
 func generateCLionProjectData(ctx android.SingletonContext, compiledModule CompiledInterface,
-	ccModule *Module, seenProjects map[string]bool, moduleInfos map[string]ccIdeInfo) {
+	ccModule *Module, bestVariantFound map[string]bool, moduleInfos map[string]ccIdeInfo) {
+	moduleName := ccModule.ModuleBase.Name()
 	srcs := compiledModule.Srcs()
+
+	// Skip if best variant has already been found.
+	if bestVariantFound[moduleName] {
+		return
+	}
+
+	// Skip if sources are empty.
 	if len(srcs) == 0 {
 		return
 	}
 
-	// Only keep the DeviceArch variant module.
-	if ctx.DeviceConfig().DeviceArch() != ccModule.ModuleBase.Arch().ArchType.Name {
+	// Check if device arch matches, in which case this is the best variant and takes precedence.
+	if ccModule.Device() && ccModule.ModuleBase.Arch().ArchType.Name == ctx.DeviceConfig().DeviceArch() {
+		bestVariantFound[moduleName] = true
+	} else if _, ok := moduleInfos[moduleName]; ok {
+		// Skip because this isn't the best variant and a previous one has already been added.
+		// Heuristically, ones that appear first are likely to be more relevant.
 		return
 	}
 
-	clionProjectLocation := getCMakeListsForModule(ccModule, ctx)
-	if seenProjects[clionProjectLocation] {
-		return
-	}
-
-	seenProjects[clionProjectLocation] = true
-
-	name := ccModule.ModuleBase.Name()
-	dpInfo := moduleInfos[name]
+	dpInfo := ccIdeInfo{}
 
 	dpInfo.Path = append(dpInfo.Path, path.Dir(ctx.BlueprintFile(ccModule)))
 	dpInfo.Srcs = append(dpInfo.Srcs, srcs.Strings()...)
@@ -216,9 +219,9 @@
 	dpInfo.Local_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CppFlags)
 	dpInfo.System_include_flags = parseCompilerCCParameters(ctx, ccModule.flags.SystemIncludeFlags)
 
-	dpInfo.Module_name = name
+	dpInfo.Module_name = moduleName
 
-	moduleInfos[name] = dpInfo
+	moduleInfos[moduleName] = dpInfo
 }
 
 type Deal struct {
diff --git a/cc/check.go b/cc/check.go
index e3af3b2..fa1926d 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 == "-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" {
 			if !ctx.Config().IsEnvTrue("ANDROID_TEMPORARILY_ALLOW_WEVERYTHING") {
 				ctx.PropertyErrorf(prop, "-Weverything is not allowed in Android.bp files.  "+
diff --git a/cc/cmake_ext_add_aidl_library.txt b/cc/cmake_ext_add_aidl_library.txt
index af5bdf6..d5c134e 100644
--- a/cc/cmake_ext_add_aidl_library.txt
+++ b/cc/cmake_ext_add_aidl_library.txt
@@ -1,3 +1,12 @@
+if ("${CMAKE_HOST_SYSTEM_PROCESSOR}" MATCHES "^(arm|aarch)")
+    set(PREBUILTS_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/prebuilts/host/linux_musl-arm64/bin")
+else()
+    set(PREBUILTS_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/prebuilts/host/linux-x86/bin")
+endif()
+if (NOT AIDL_BIN)
+    find_program(AIDL_BIN aidl REQUIRED HINTS "${PREBUILTS_BIN_DIR}")
+endif()
+
 function(add_aidl_library NAME LANG AIDLROOT SOURCES AIDLFLAGS)
     if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20")
         cmake_policy(SET CMP0116 NEW)
@@ -25,7 +34,7 @@
         endif()
 
         set(DEPFILE_ARG)
-        if (NOT ${CMAKE_GENERATOR} MATCHES "Unix Makefiles")
+        if (NOT ${CMAKE_GENERATOR} STREQUAL "Unix Makefiles")
             set(DEPFILE_ARG DEPFILE "${GEN_SOURCE}.d")
         endif()
 
@@ -57,7 +66,7 @@
         "${GEN_DIR}/include"
     )
 
-    if (${LANG} MATCHES "ndk")
+    if (${LANG} STREQUAL "ndk")
         set(BINDER_LIB_NAME "libbinder_ndk_sdk")
     else()
         set(BINDER_LIB_NAME "libbinder_sdk")
diff --git a/cc/cmake_main.txt b/cc/cmake_main.txt
index e9177d6..eeabf53 100644
--- a/cc/cmake_main.txt
+++ b/cc/cmake_main.txt
@@ -6,16 +6,12 @@
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
 include(AddAidlLibrary)
 include(AppendCxxFlagsIfSupported)
+include(FindThreads)
 
 if (NOT ANDROID_BUILD_TOP)
     set(ANDROID_BUILD_TOP "${CMAKE_CURRENT_SOURCE_DIR}")
 endif()
 
-set(PREBUILTS_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/prebuilts/host/linux-x86/bin")
-if (NOT AIDL_BIN)
-    find_program(AIDL_BIN aidl REQUIRED HINTS "${PREBUILTS_BIN_DIR}")
-endif()
-
 <<cflagsList .M.Name "_CFLAGS" .M.Properties.Cflags .M.Properties.Unportable_flags .M.Properties.Cflags_ignored>>
 
 <<range .Pprop.SystemPackages ->>
@@ -25,6 +21,7 @@
 add_subdirectory("${ANDROID_BUILD_TOP}/<<.>>" "<<.>>/build" EXCLUDE_FROM_ALL)
 <<end>>
 add_compile_options(${<<.M.Name>>_CFLAGS})
+link_libraries(${CMAKE_THREAD_LIBS_INIT})
 <<range $moduleDir, $value := .ModuleDirs ->>
 add_subdirectory(<<$moduleDir>>)
 <<end>>
diff --git a/cc/cmake_module_cc.txt b/cc/cmake_module_cc.txt
index 0dc45ae..0f6e62f 100644
--- a/cc/cmake_module_cc.txt
+++ b/cc/cmake_module_cc.txt
@@ -2,10 +2,10 @@
 <<$includeDirs := getIncludeDirs .Ctx .M>>
 <<$cflags := getCflagsProperty .Ctx .M>>
 <<$deps := mapLibraries .Ctx .M (concat5
-(getLinkerProperties .M).Whole_static_libs
-(getLinkerProperties .M).Static_libs
-(getLinkerProperties .M).Shared_libs
-(getLinkerProperties .M).Header_libs
+(getWholeStaticLibsProperty .Ctx .M)
+(getStaticLibsProperty .Ctx .M)
+(getSharedLibsProperty .Ctx .M)
+(getHeaderLibsProperty .Ctx .M)
 (getExtraLibs .M)
 ) .Pprop.LibraryMapping>>
 <<$moduleType := getModuleType .M>>
diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go
index b4d1268..61fa46d 100644
--- a/cc/cmake_snapshot.go
+++ b/cc/cmake_snapshot.go
@@ -15,7 +15,6 @@
 package cc
 
 import (
-	"android/soong/android"
 	"bytes"
 	_ "embed"
 	"fmt"
@@ -25,6 +24,8 @@
 	"strings"
 	"text/template"
 
+	"android/soong/android"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -50,8 +51,10 @@
 var cmakeExtAppendFlags string
 
 var defaultUnportableFlags []string = []string{
+	"-Wno-c99-designator",
 	"-Wno-class-memaccess",
 	"-Wno-exit-time-destructors",
+	"-Winconsistent-missing-override",
 	"-Wno-inconsistent-missing-override",
 	"-Wreorder-init-list",
 	"-Wno-reorder-init-list",
@@ -61,8 +64,19 @@
 }
 
 var ignoredSystemLibs []string = []string{
+	"crtbegin_dynamic",
+	"crtend_android",
+	"libc",
 	"libc++",
 	"libc++_static",
+	"libc++demangle",
+	"libc_musl",
+	"libc_musl_crtbegin_so",
+	"libc_musl_crtbegin_static",
+	"libc_musl_crtend",
+	"libc_musl_crtend_so",
+	"libdl",
+	"libm",
 	"prebuilt_libclang_rt.builtins",
 	"prebuilt_libclang_rt.ubsan_minimal",
 }
@@ -83,8 +97,14 @@
 }
 
 type CmakeSnapshotProperties struct {
-	// Modules to add to the snapshot package. Their dependencies are pulled in automatically.
-	Modules []string
+	// Host modules to add to the snapshot package. Their dependencies are pulled in automatically.
+	Modules_host []string
+
+	// System modules to add to the snapshot package. Their dependencies are pulled in automatically.
+	Modules_system []string
+
+	// Vendor modules to add to the snapshot package. Their dependencies are pulled in automatically.
+	Modules_vendor []string
 
 	// Host prebuilts to bundle with the snapshot. These are tools needed to build outside Android.
 	Prebuilts []string
@@ -141,11 +161,7 @@
 			return list.String()
 		},
 		"toStrings": func(files android.Paths) []string {
-			strings := make([]string, len(files))
-			for idx, file := range files {
-				strings[idx] = file.String()
-			}
-			return strings
+			return files.Strings()
 		},
 		"concat5": func(list1 []string, list2 []string, list3 []string, list4 []string, list5 []string) []string {
 			return append(append(append(append(list1, list2...), list3...), list4...), list5...)
@@ -188,12 +204,28 @@
 			return m.compiler.baseCompilerProps()
 		},
 		"getCflagsProperty": func(ctx android.ModuleContext, m *Module) []string {
-			cflags := m.compiler.baseCompilerProps().Cflags
-			return cflags.GetOrDefault(ctx, nil)
+			prop := m.compiler.baseCompilerProps().Cflags
+			return prop.GetOrDefault(ctx, nil)
 		},
 		"getLinkerProperties": func(m *Module) BaseLinkerProperties {
 			return m.linker.baseLinkerProps()
 		},
+		"getWholeStaticLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
+			prop := m.linker.baseLinkerProps().Whole_static_libs
+			return prop.GetOrDefault(ctx, nil)
+		},
+		"getStaticLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
+			prop := m.linker.baseLinkerProps().Static_libs
+			return prop.GetOrDefault(ctx, nil)
+		},
+		"getSharedLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
+			prop := m.linker.baseLinkerProps().Shared_libs
+			return prop.GetOrDefault(ctx, nil)
+		},
+		"getHeaderLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
+			prop := m.linker.baseLinkerProps().Header_libs
+			return prop.GetOrDefault(ctx, nil)
+		},
 		"getExtraLibs":   getExtraLibs,
 		"getIncludeDirs": getIncludeDirs,
 		"mapLibraries": func(ctx android.ModuleContext, m *Module, libs []string, mapping map[string]LibraryMappingProperty) []string {
@@ -266,12 +298,19 @@
 }
 
 func (m *CmakeSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
-	variations := []blueprint.Variation{
-		{"os", "linux_glibc"},
-		{"arch", "x86_64"},
+	deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations()
+	deviceSystemVariations := append(deviceVariations, blueprint.Variation{"image", ""})
+	deviceVendorVariations := append(deviceVariations, blueprint.Variation{"image", "vendor"})
+	hostVariations := ctx.Config().BuildOSTarget.Variations()
+
+	ctx.AddVariationDependencies(hostVariations, cmakeSnapshotModuleTag, m.Properties.Modules_host...)
+	ctx.AddVariationDependencies(deviceSystemVariations, cmakeSnapshotModuleTag, m.Properties.Modules_system...)
+	ctx.AddVariationDependencies(deviceVendorVariations, cmakeSnapshotModuleTag, m.Properties.Modules_vendor...)
+
+	if len(m.Properties.Prebuilts) > 0 {
+		prebuilts := append(m.Properties.Prebuilts, "libc++")
+		ctx.AddVariationDependencies(hostVariations, cmakeSnapshotPrebuiltTag, prebuilts...)
 	}
-	ctx.AddVariationDependencies(variations, cmakeSnapshotModuleTag, m.Properties.Modules...)
-	ctx.AddVariationDependencies(variations, cmakeSnapshotPrebuiltTag, m.Properties.Prebuilts...)
 }
 
 func (m *CmakeSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -324,8 +363,11 @@
 		if slices.Contains(ignoredSystemLibs, moduleName) {
 			return false // system libs built in-tree for Android
 		}
+		if dep.IsPrebuilt() {
+			return false // prebuilts are not supported
+		}
 		if dep.compiler == nil {
-			return false // unsupported module type (e.g. prebuilt)
+			return false // unsupported module type
 		}
 		isAidlModule := dep.compiler.baseCompilerProps().AidlInterface.Lang != ""
 
@@ -389,7 +431,8 @@
 
 	// Merging CMakeLists.txt contents for every module directory
 	var makefilesList android.Paths
-	for moduleDir, fragments := range moduleDirs {
+	for _, moduleDir := range android.SortedKeys(moduleDirs) {
+		fragments := moduleDirs[moduleDir]
 		moduleCmakePath := android.PathForModuleGen(ctx, moduleDir, "CMakeLists.txt")
 		makefilesList = append(makefilesList, moduleCmakePath)
 		sort.Strings(fragments)
@@ -429,8 +472,9 @@
 	// Packaging all sources into the zip file
 	if m.Properties.Include_sources {
 		var sourcesList android.Paths
-		for _, file := range sourceFiles {
-			sourcesList = append(sourcesList, file)
+		for _, file := range android.SortedKeys(sourceFiles) {
+			path := sourceFiles[file]
+			sourcesList = append(sourcesList, path)
 		}
 
 		sourcesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_sources.rsp")
@@ -448,7 +492,8 @@
 		var prebuiltsList android.Paths
 
 		ctx.VisitDirectDepsWithTag(cmakeSnapshotPrebuiltTag, func(dep android.Module) {
-			for _, file := range dep.FilesToInstall() {
+			for _, file := range android.OtherModuleProviderOrDefault(
+				ctx, dep, android.InstallFilesProvider).InstallFiles {
 				prebuiltsList = append(prebuiltsList, file)
 			}
 		})
@@ -462,15 +507,8 @@
 
 	// Finish generating the final zip file
 	zipRule.Build(m.zipPath.String(), "archiving "+ctx.ModuleName())
-}
 
-func (m *CmakeSnapshot) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{m.zipPath}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
+	ctx.SetOutputFiles(android.Paths{m.zipPath}, "")
 }
 
 func (m *CmakeSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/cc/cmake_snapshot_test.go b/cc/cmake_snapshot_test.go
index 8fca6c1..b6f4369 100644
--- a/cc/cmake_snapshot_test.go
+++ b/cc/cmake_snapshot_test.go
@@ -38,7 +38,9 @@
 	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
 		cc_cmake_snapshot {
 			name: "foo",
-			modules: [],
+			modules_host: [],
+			modules_system: [],
+			modules_vendor: [],
 			prebuilts: ["libc++"],
 			include_sources: true,
 		}`)
@@ -65,7 +67,7 @@
 	result := android.GroupFixturePreparers(PrepareForIntegrationTestWithCc, xtra).RunTestWithBp(t, `
 		cc_cmake_snapshot {
 			name: "foo",
-			modules: [
+			modules_system: [
 				"foo_binary",
 			],
 			include_sources: true,
@@ -99,7 +101,7 @@
 
 		cc_cmake_snapshot {
 			name: "foo",
-			modules: [],
+			modules_system: [],
 			prebuilts: ["libc++"],
 			include_sources: true,
 		}`)
diff --git a/cc/compdb.go b/cc/compdb.go
index da28183..b33f490 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -85,23 +85,24 @@
 	if err != nil {
 		log.Fatalf("Could not create file %s: %s", compDBFile, err)
 	}
-	defer f.Close()
+	defer func() {
+		if err := f.Close(); err != nil {
+			log.Fatalf("Could not close file %s: %s", compDBFile, err)
+		}
+	}()
 
 	v := make([]compDbEntry, 0, len(m))
-
 	for _, value := range m {
 		v = append(v, value)
 	}
-	var dat []byte
+
+	w := json.NewEncoder(f)
 	if outputCompdbDebugInfo {
-		dat, err = json.MarshalIndent(v, "", " ")
-	} else {
-		dat, err = json.Marshal(v)
+		w.SetIndent("", " ")
 	}
-	if err != nil {
-		log.Fatalf("Failed to marshal: %s", err)
+	if err := w.Encode(v); err != nil {
+		log.Fatalf("Failed to encode: %s", err)
 	}
-	f.Write(dat)
 
 	if finalLinkDir := ctx.Config().Getenv(envVariableCompdbLink); finalLinkDir != "" {
 		finalLinkPath := filepath.Join(finalLinkDir, compdbFilename)
diff --git a/cc/compiler.go b/cc/compiler.go
index d8446fb..396ec88 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -37,7 +37,7 @@
 	// list of source files used to compile the C/C++ module.  May be .c, .cpp, or .S files.
 	// srcs may reference the outputs of other modules that produce source files like genrule
 	// or filegroup using the syntax ":module".
-	Srcs []string `android:"path,arch_variant"`
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	// list of source files that should not be compiled with clang-tidy.
 	Tidy_disabled_srcs []string `android:"path,arch_variant"`
@@ -47,13 +47,13 @@
 
 	// list of source files that should not be used to build the C/C++ module.
 	// This is most useful in the arch/multilib variants to remove non-common files
-	Exclude_srcs []string `android:"path,arch_variant"`
+	Exclude_srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	// list of module-specific flags that will be used for C and C++ compiles.
 	Cflags proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of module-specific flags that will be used for C++ compiles
-	Cppflags []string `android:"arch_variant"`
+	Cppflags proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of module-specific flags that will be used for C compiles
 	Conlyflags []string `android:"arch_variant"`
@@ -229,7 +229,7 @@
 	} `android:"arch_variant"`
 
 	// Stores the original list of source files before being cleared by library reuse
-	OriginalSrcs []string `blueprint:"mutated"`
+	OriginalSrcs proptools.Configurable[[]string] `blueprint:"mutated"`
 
 	// Build and link with OpenMP
 	Openmp *bool `android:"arch_variant"`
@@ -300,7 +300,7 @@
 	deps.AidlLibs = append(deps.AidlLibs, compiler.Properties.Aidl.Libs...)
 
 	android.ProtoDeps(ctx, &compiler.Proto)
-	if compiler.hasSrcExt(".proto") {
+	if compiler.hasSrcExt(ctx, ".proto") {
 		deps = protoDeps(ctx, deps, &compiler.Proto, Bool(compiler.Properties.Proto.Static))
 	}
 
@@ -363,12 +363,15 @@
 	tc := ctx.toolchain()
 	modulePath := ctx.ModuleDir()
 
-	compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, compiler.Properties.Srcs, compiler.Properties.Exclude_srcs)
+	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)
 	CheckBadCompilerFlags(ctx, "cflags", cflags)
-	CheckBadCompilerFlags(ctx, "cppflags", compiler.Properties.Cppflags)
+	CheckBadCompilerFlags(ctx, "cppflags", cppflags)
 	CheckBadCompilerFlags(ctx, "conlyflags", compiler.Properties.Conlyflags)
 	CheckBadCompilerFlags(ctx, "asflags", compiler.Properties.Asflags)
 	CheckBadCompilerFlags(ctx, "vendor.cflags", compiler.Properties.Target.Vendor.Cflags)
@@ -381,7 +384,7 @@
 	esc := proptools.NinjaAndShellEscapeList
 
 	flags.Local.CFlags = append(flags.Local.CFlags, esc(cflags)...)
-	flags.Local.CppFlags = append(flags.Local.CppFlags, esc(compiler.Properties.Cppflags)...)
+	flags.Local.CppFlags = append(flags.Local.CppFlags, esc(cppflags)...)
 	flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, esc(compiler.Properties.Conlyflags)...)
 	flags.Local.AsFlags = append(flags.Local.AsFlags, esc(compiler.Properties.Asflags)...)
 	flags.Local.YasmFlags = append(flags.Local.YasmFlags, esc(compiler.Properties.Asflags)...)
@@ -539,12 +542,10 @@
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "${config.ExternalCflags}")
 	}
 
-	if tc.Bionic() {
-		if Bool(compiler.Properties.Rtti) {
-			flags.Local.CppFlags = append(flags.Local.CppFlags, "-frtti")
-		} else {
-			flags.Local.CppFlags = append(flags.Local.CppFlags, "-fno-rtti")
-		}
+	if Bool(compiler.Properties.Rtti) {
+		flags.Local.CppFlags = append(flags.Local.CppFlags, "-frtti")
+	} else {
+		flags.Local.CppFlags = append(flags.Local.CppFlags, "-fno-rtti")
 	}
 
 	flags.Global.AsFlags = append(flags.Global.AsFlags, "${config.CommonGlobalAsflags}")
@@ -604,11 +605,11 @@
 		flags.Global.CFlags = append(flags.Global.CFlags, "-DANDROID_STRICT")
 	}
 
-	if compiler.hasSrcExt(".proto") {
+	if compiler.hasSrcExt(ctx, ".proto") {
 		flags = protoFlags(ctx, flags, &compiler.Proto)
 	}
 
-	if compiler.hasSrcExt(".y") || compiler.hasSrcExt(".yy") {
+	if compiler.hasSrcExt(ctx, ".y") || compiler.hasSrcExt(ctx, ".yy") {
 		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "yacc", ctx.ModuleDir()).String())
 	}
@@ -618,7 +619,7 @@
 		ctx.ModuleErrorf("aidl.libs and (aidl.include_dirs or aidl.local_include_dirs) can't be set at the same time. For aidl headers, please only use aidl.libs prop")
 	}
 
-	if compiler.hasAidl(deps) {
+	if compiler.hasAidl(ctx, deps) {
 		flags.aidlFlags = append(flags.aidlFlags, compiler.Properties.Aidl.Flags...)
 		if len(compiler.Properties.Aidl.Local_include_dirs) > 0 {
 			localAidlIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Aidl.Local_include_dirs)
@@ -647,7 +648,7 @@
 		}
 		flags.aidlFlags = append(flags.aidlFlags, "--min_sdk_version="+aidlMinSdkVersion)
 
-		if compiler.hasSrcExt(".aidl") {
+		if compiler.hasSrcExt(ctx, ".aidl") {
 			flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 				"-I"+android.PathForModuleGen(ctx, "aidl").String())
 		}
@@ -657,16 +658,16 @@
 		}
 	}
 
-	if compiler.hasSrcExt(".rscript") || compiler.hasSrcExt(".fs") {
+	if compiler.hasSrcExt(ctx, ".rscript") || compiler.hasSrcExt(ctx, ".fs") {
 		flags = rsFlags(ctx, flags, &compiler.Properties)
 	}
 
-	if compiler.hasSrcExt(".sysprop") {
+	if compiler.hasSrcExt(ctx, ".sysprop") {
 		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "sysprop", "include").String())
 	}
 
-	if len(compiler.Properties.Srcs) > 0 {
+	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)
@@ -707,18 +708,18 @@
 	return flags
 }
 
-func (compiler *baseCompiler) hasSrcExt(ext string) bool {
+func (compiler *baseCompiler) hasSrcExt(ctx BaseModuleContext, ext string) bool {
 	for _, src := range compiler.srcsBeforeGen {
 		if src.Ext() == ext {
 			return true
 		}
 	}
-	for _, src := range compiler.Properties.Srcs {
+	for _, src := range compiler.Properties.Srcs.GetOrDefault(ctx, nil) {
 		if filepath.Ext(src) == ext {
 			return true
 		}
 	}
-	for _, src := range compiler.Properties.OriginalSrcs {
+	for _, src := range compiler.Properties.OriginalSrcs.GetOrDefault(ctx, nil) {
 		if filepath.Ext(src) == ext {
 			return true
 		}
@@ -746,8 +747,8 @@
 	return nil
 }
 
-func (compiler *baseCompiler) hasAidl(deps PathDeps) bool {
-	return len(deps.AidlLibraryInfos) > 0 || compiler.hasSrcExt(".aidl")
+func (compiler *baseCompiler) hasAidl(ctx BaseModuleContext, deps PathDeps) bool {
+	return len(deps.AidlLibraryInfos) > 0 || compiler.hasSrcExt(ctx, ".aidl")
 }
 
 func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
@@ -796,24 +797,21 @@
 	// be added to the include path using -I
 	Local_include_dirs []string `android:"arch_variant,variant_prepend"`
 
-	// list of Rust static libraries.
-	Static_rlibs []string `android:"arch_variant,variant_prepend"`
-
 	// list of static libraries that provide headers for this binding.
-	Static_libs []string `android:"arch_variant,variant_prepend"`
+	Static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of shared libraries that provide headers for this binding.
-	Shared_libs []string `android:"arch_variant"`
+	Shared_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// List of libraries which export include paths required for this module
-	Header_libs []string `android:"arch_variant,variant_prepend"`
+	Header_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of clang flags required to correctly interpret the headers.
 	Cflags proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of c++ specific clang flags required to correctly interpret the headers.
 	// This is provided primarily to make sure cppflags defined in cc_defaults are pulled in.
-	Cppflags []string `android:"arch_variant"`
+	Cppflags proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// C standard version to use. Can be a specific version (such as "gnu11"),
 	// "experimental" (which will use draft versions like C1x when available),
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 289409f..f514db6 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -35,4 +35,8 @@
     testSrcs: [
         "tidy_test.go",
     ],
+    visibility: [
+        "//build/soong:__subpackages__",
+        "//prebuilts/clang/host/linux-x86/soong",
+    ],
 }
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index beb68e1..0dcf2cf 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -41,11 +41,18 @@
 		"armv8-2a-dotprod": []string{
 			"-march=armv8.2-a+dotprod",
 		},
+		// On ARMv9 and later, Pointer Authentication Codes (PAC) are mandatory,
+		// so -fstack-protector is unnecessary.
 		"armv9-a": []string{
 			"-march=armv8.2-a+dotprod",
 			"-mbranch-protection=standard",
 			"-fno-stack-protector",
 		},
+		"armv9-2a": []string{
+			"-march=armv9.2-a",
+			"-mbranch-protection=standard",
+			"-fno-stack-protector",
+		},
 	}
 
 	arm64Ldflags = []string{
@@ -111,11 +118,9 @@
 
 	pctx.StaticVariable("Arm64Cppflags", strings.Join(arm64Cppflags, " "))
 
-	pctx.StaticVariable("Arm64Armv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
-	pctx.StaticVariable("Arm64Armv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " "))
-	pctx.StaticVariable("Arm64Armv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
-	pctx.StaticVariable("Arm64Armv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
-	pctx.StaticVariable("Arm64Armv9ACflags", strings.Join(arm64ArchVariantCflags["armv9-a"], " "))
+	for variant, cflags := range arm64ArchVariantCflags {
+		pctx.StaticVariable("Arm64"+variant+"VariantCflags", strings.Join(cflags, " "))
+	}
 
 	pctx.StaticVariable("Arm64CortexA53Cflags", strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
 	pctx.StaticVariable("Arm64CortexA55Cflags", strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
@@ -127,14 +132,6 @@
 }
 
 var (
-	arm64ArchVariantCflagsVar = map[string]string{
-		"armv8-a":            "${config.Arm64Armv8ACflags}",
-		"armv8-a-branchprot": "${config.Arm64Armv8ABranchProtCflags}",
-		"armv8-2a":           "${config.Arm64Armv82ACflags}",
-		"armv8-2a-dotprod":   "${config.Arm64Armv82ADotprodCflags}",
-		"armv9-a":            "${config.Arm64Armv9ACflags}",
-	}
-
 	arm64CpuVariantCflagsVar = map[string]string{
 		"cortex-a53": "${config.Arm64CortexA53Cflags}",
 		"cortex-a55": "${config.Arm64CortexA55Cflags}",
@@ -204,18 +201,12 @@
 }
 
 func arm64ToolchainFactory(arch android.Arch) Toolchain {
-	switch arch.ArchVariant {
-	case "armv8-a":
-	case "armv8-a-branchprot":
-	case "armv8-2a":
-	case "armv8-2a-dotprod":
-	case "armv9-a":
-		// Nothing extra for armv8-a/armv8-2a
-	default:
-		panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
+	// Error now rather than having a confusing Ninja error
+	if _, ok := arm64ArchVariantCflags[arch.ArchVariant]; !ok {
+		panic(fmt.Sprintf("Unknown ARM64 architecture version: %q", arch.ArchVariant))
 	}
 
-	toolchainCflags := []string{arm64ArchVariantCflagsVar[arch.ArchVariant]}
+	toolchainCflags := []string{"${config.Arm64" + arch.ArchVariant + "VariantCflags}"}
 	toolchainCflags = append(toolchainCflags,
 		variantOrDefault(arm64CpuVariantCflagsVar, arch.CpuVariant))
 
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
index 438e0e6..a19b0ed 100644
--- a/cc/config/arm64_linux_host.go
+++ b/cc/config/arm64_linux_host.go
@@ -87,8 +87,8 @@
 }
 
 func linuxBionicArm64ToolchainFactory(arch android.Arch) Toolchain {
-	archVariant := "armv8-a" // for host, default to armv8-a
-	toolchainCflags := []string{arm64ArchVariantCflagsVar[archVariant]}
+	// for host, default to armv8-a
+	toolchainCflags := []string{"-march=armv8-a"}
 
 	// We don't specify CPU architecture for host. Conservatively assume
 	// the host CPU needs the fix
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 4856669..1783f49 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"os/exec"
 	"path/filepath"
+	"runtime"
 	"strings"
 	"sync"
 
@@ -28,6 +29,7 @@
 	darwinCflags = []string{
 		"-fPIC",
 		"-funwind-tables",
+		"-fno-omit-frame-pointer",
 
 		"-isysroot ${macSdkRoot}",
 		"-mmacosx-version-min=${macMinVersion}",
@@ -73,31 +75,33 @@
 )
 
 func init() {
-	pctx.VariableFunc("macSdkRoot", func(ctx android.PackageVarContext) string {
-		return getMacTools(ctx).sdkRoot
-	})
-	pctx.StaticVariable("macMinVersion", "10.14")
-	pctx.VariableFunc("MacArPath", func(ctx android.PackageVarContext) string {
-		return getMacTools(ctx).arPath
-	})
+	if runtime.GOOS == "darwin" {
+		pctx.VariableFunc("macSdkRoot", func(ctx android.PackageVarContext) string {
+			return getMacTools(ctx).sdkRoot
+		})
+		pctx.StaticVariable("macMinVersion", "10.14")
+		pctx.VariableFunc("MacArPath", func(ctx android.PackageVarContext) string {
+			return getMacTools(ctx).arPath
+		})
 
-	pctx.VariableFunc("MacLipoPath", func(ctx android.PackageVarContext) string {
-		return getMacTools(ctx).lipoPath
-	})
+		pctx.VariableFunc("MacLipoPath", func(ctx android.PackageVarContext) string {
+			return getMacTools(ctx).lipoPath
+		})
 
-	pctx.VariableFunc("MacStripPath", func(ctx android.PackageVarContext) string {
-		return getMacTools(ctx).stripPath
-	})
+		pctx.VariableFunc("MacStripPath", func(ctx android.PackageVarContext) string {
+			return getMacTools(ctx).stripPath
+		})
 
-	pctx.VariableFunc("MacToolPath", func(ctx android.PackageVarContext) string {
-		return getMacTools(ctx).toolPath
-	})
+		pctx.VariableFunc("MacToolPath", func(ctx android.PackageVarContext) string {
+			return getMacTools(ctx).toolPath
+		})
 
-	pctx.StaticVariable("DarwinCflags", strings.Join(darwinCflags, " "))
-	pctx.StaticVariable("DarwinLdflags", strings.Join(darwinLdflags, " "))
-	pctx.StaticVariable("DarwinLldflags", strings.Join(darwinLdflags, " "))
+		pctx.StaticVariable("DarwinCflags", strings.Join(darwinCflags, " "))
+		pctx.StaticVariable("DarwinLdflags", strings.Join(darwinLdflags, " "))
+		pctx.StaticVariable("DarwinLldflags", strings.Join(darwinLdflags, " "))
 
-	pctx.StaticVariable("DarwinYasmFlags", "-f macho -m amd64")
+		pctx.StaticVariable("DarwinYasmFlags", "-f macho -m amd64")
+	}
 }
 
 func MacStripPath(ctx android.PathContext) string {
diff --git a/cc/config/global.go b/cc/config/global.go
index 62a4765..9d3de6d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -136,11 +136,6 @@
 		// displaying logs in web browsers.
 		"-fmessage-length=0",
 
-		// Disable C++17 "relaxed template template argument matching" as a workaround for
-		// our out-dated libcxx.
-		// http://b/341084395
-		"-fno-relaxed-template-template-args",
-
 		// Using simple template names reduces the size of debug builds.
 		"-gsimple-template-names",
 
@@ -149,9 +144,6 @@
 
 		// Make paths in deps files relative.
 		"-no-canonical-prefixes",
-
-		// http://b/315250603 temporarily disabled
-		"-Wno-error=format",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -181,9 +173,6 @@
 		"-Werror=sequence-point",
 		"-Werror=format-security",
 		"-nostdlibinc",
-
-		// Emit additional debug info for AutoFDO
-		"-fdebug-info-for-profiling",
 	}
 
 	commonGlobalLldflags = []string{
@@ -286,7 +275,7 @@
 		"-Wno-zero-as-null-pointer-constant",        // http://b/68236239
 		"-Wno-deprecated-anon-enum-enum-conversion", // http://b/153746485
 		"-Wno-deprecated-enum-enum-conversion",
-		"-Wno-pessimizing-move", // http://b/154270751
+		"-Wno-error=pessimizing-move", // http://b/154270751
 		// New warnings to be fixed after clang-r399163
 		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
 		// New warnings to be fixed after clang-r428724
@@ -297,20 +286,14 @@
 		// New warnings to be fixed after clang-r468909
 		"-Wno-error=deprecated-builtins", // http://b/241601211
 		"-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=single-bit-bitfield-constant-conversion", // http://b/243965903
-		"-Wno-error=enum-constexpr-conversion",               // http://b/243964282
+		"-Wno-error=enum-constexpr-conversion", // http://b/243964282
 		// New warnings to be fixed after clang-r522817
 		"-Wno-error=invalid-offsetof",
 		"-Wno-error=thread-safety-reference-return",
 
-		// Irrelevant on Android because _we_ don't use exceptions, but causes
-		// lots of build noise because libcxx/libcxxabi do. This can probably
-		// go away when we're on a new enough libc++, but has to be global
-		// until then because it causes warnings in the _callers_, not the
-		// project itself.
-		"-Wno-deprecated-dynamic-exception-spec",
-
 		// Allow using VLA CXX extension.
 		"-Wno-vla-cxx-extension",
 	}
@@ -350,6 +333,9 @@
 
 		"-Wno-unused",
 		"-Wno-deprecated",
+
+		// http://b/315250603 temporarily disabled
+		"-Wno-error=format",
 	}
 
 	// Similar to noOverrideGlobalCflags, but applies only to third-party code
@@ -376,6 +362,7 @@
 		"-Wno-unqualified-std-cast-call",
 		"-Wno-array-parameter",
 		"-Wno-gnu-offsetof-extensions",
+		"-Wno-pessimizing-move",
 		// TODO: Enable this warning http://b/315245071
 		"-Wno-fortify-source",
 	}
@@ -397,8 +384,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r522817"
-	ClangDefaultShortVersion = "18"
+	ClangDefaultVersion      = "clang-r530567"
+	ClangDefaultShortVersion = "19"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index 724676a..6a5293f 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -29,14 +29,10 @@
 		// This is already the driver's Android default, but duplicated here (and
 		// below) for ease of experimentation with additional extensions.
 		"-march=rv64gcv_zba_zbb_zbs",
-		// TODO: move to driver (https://github.com/google/android-riscv64/issues/111)
-		"-mno-strict-align",
 		// TODO: remove when qemu V works (https://gitlab.com/qemu-project/qemu/-/issues/1976)
 		// (Note that we'll probably want to wait for berberis to be good enough
 		// that most people don't care about qemu's V performance either!)
 		"-mno-implicit-float",
-		// TODO: remove when clang default changed (https://github.com/google/android-riscv64/issues/124)
-		"-mllvm -jump-is-expensive=false",
 	}
 
 	riscv64ArchVariantCflags = map[string][]string{}
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index 515cb21..ddc86c2 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -25,6 +25,8 @@
 
 		"-fPIC",
 
+		"-fno-omit-frame-pointer",
+
 		"-U_FORTIFY_SOURCE",
 		"-D_FORTIFY_SOURCE=2",
 		"-fstack-protector-strong",
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 7f22377..287967c 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -26,6 +26,8 @@
 
 		"-fPIC",
 
+		"-fno-omit-frame-pointer",
+
 		"-U_FORTIFY_SOURCE",
 		"-D_FORTIFY_SOURCE=2",
 		"-fstack-protector",
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 1e61b01..a4d43b9 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -43,6 +43,12 @@
 		"-mno-ms-bitfields",
 
 		"--sysroot ${WindowsGccRoot}/${WindowsGccTriple}",
+
+		// Windows flags to generate PDB
+		"-g",
+		"-gcodeview",
+
+		"-fno-omit-frame-pointer",
 	}
 
 	windowsIncludeFlags = []string{
diff --git a/cc/coverage.go b/cc/coverage.go
index f6092e4..a7618dd 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -23,26 +23,26 @@
 )
 
 var (
- 	clangCoverageHostLdFlags = []string{
- 		"-Wl,--no-as-needed",
- 		"-Wl,--wrap,open",
- 	}
- 	clangContinuousCoverageFlags = []string{
- 		"-mllvm",
- 		"-runtime-counter-relocation",
- 	}
- 	clangCoverageCFlags = []string{
- 		"-Wno-frame-larger-than=",
- 	}
- 	clangCoverageCommonFlags = []string{
- 		"-fcoverage-mapping",
- 		"-Wno-pass-failed",
- 		"-D__ANDROID_CLANG_COVERAGE__",
- 	}
- 	clangCoverageHWASanFlags = []string{
- 		"-mllvm",
- 		"-hwasan-globals=0",
- 	}
+	clangCoverageHostLdFlags = []string{
+		"-Wl,--no-as-needed",
+		"-Wl,--wrap,open",
+	}
+	clangContinuousCoverageFlags = []string{
+		"-mllvm",
+		"-runtime-counter-relocation",
+	}
+	clangCoverageCFlags = []string{
+		"-Wno-frame-larger-than=",
+	}
+	clangCoverageCommonFlags = []string{
+		"-fcoverage-mapping",
+		"-Wno-pass-failed",
+		"-D__ANDROID_CLANG_COVERAGE__",
+	}
+	clangCoverageHWASanFlags = []string{
+		"-mllvm",
+		"-hwasan-globals=0",
+	}
 )
 
 const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
@@ -247,9 +247,19 @@
 	return properties
 }
 
+type IsNativeCoverageNeededContext interface {
+	Config() android.Config
+	DeviceConfig() android.DeviceConfig
+	Device() bool
+}
+
+var _ IsNativeCoverageNeededContext = android.IncomingTransitionContext(nil)
+var _ IsNativeCoverageNeededContext = android.BaseModuleContext(nil)
+var _ IsNativeCoverageNeededContext = android.BottomUpMutatorContext(nil)
+
 type UseCoverage interface {
 	android.Module
-	IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool
+	IsNativeCoverageNeeded(ctx IsNativeCoverageNeededContext) bool
 }
 
 // Coverage is an interface for non-CC modules to implement to be mutated for coverage
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 92f2c5e..3f21bc6 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -57,7 +57,7 @@
 	return []interface{}{&fuzzer.Properties}
 }
 
-func fuzzMutatorDeps(mctx android.TopDownMutatorContext) {
+func fuzzMutatorDeps(mctx android.BottomUpMutatorContext) {
 	currentModule, ok := mctx.Module().(*Module)
 	if !ok {
 		return
@@ -373,7 +373,7 @@
 		}
 
 		if targetFramework == fuzz.AFL {
-			fuzzBin.baseCompiler.Properties.Srcs = append(fuzzBin.baseCompiler.Properties.Srcs, ":aflpp_driver", ":afl-compiler-rt")
+			fuzzBin.baseCompiler.Properties.Srcs.AppendSimpleValue([]string{":aflpp_driver", ":afl-compiler-rt"})
 			module.fuzzer.Properties.FuzzFramework = fuzz.AFL
 		}
 	})
diff --git a/cc/genrule.go b/cc/genrule.go
index cabf787..fe3b127 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -79,6 +79,14 @@
 
 func (g *GenruleExtraProperties) ImageMutatorBegin(ctx android.BaseModuleContext) {}
 
+func (g *GenruleExtraProperties) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+	return Bool(g.Vendor_available) || Bool(g.Odm_available) || ctx.SocSpecific() || ctx.DeviceSpecific()
+}
+
+func (g *GenruleExtraProperties) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+	return Bool(g.Product_available) || ctx.ProductSpecific()
+}
+
 func (g *GenruleExtraProperties) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return !(ctx.SocSpecific() || ctx.DeviceSpecific() || ctx.ProductSpecific())
 }
@@ -102,18 +110,7 @@
 }
 
 func (g *GenruleExtraProperties) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	var variants []string
-	vendorVariantRequired := Bool(g.Vendor_available) || Bool(g.Odm_available) || ctx.SocSpecific() || ctx.DeviceSpecific()
-	productVariantRequired := Bool(g.Product_available) || ctx.ProductSpecific()
-
-	if vendorVariantRequired {
-		variants = append(variants, VendorVariation)
-	}
-	if productVariantRequired {
-		variants = append(variants, ProductVariation)
-	}
-
-	return variants
+	return nil
 }
 
 func (g *GenruleExtraProperties) SetImageVariation(ctx android.BaseModuleContext, variation string) {
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index b3d5116..9a8049b 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -236,7 +236,7 @@
 	}
 	`
 	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, bp)
-	gen_32bit := result.ModuleForTests("gen", "android_arm_armv7-a-neon").OutputFiles(t, "")
+	gen_32bit := result.ModuleForTests("gen", "android_arm_armv7-a-neon").OutputFiles(result.TestContext, t, "")
 	android.AssertPathsEndWith(t,
 		"genrule_out",
 		[]string{
@@ -245,7 +245,7 @@
 		gen_32bit,
 	)
 
-	gen_64bit := result.ModuleForTests("gen", "android_arm64_armv8-a").OutputFiles(t, "")
+	gen_64bit := result.ModuleForTests("gen", "android_arm64_armv8-a").OutputFiles(result.TestContext, t, "")
 	android.AssertPathsEndWith(t,
 		"genrule_out",
 		[]string{
diff --git a/cc/image.go b/cc/image.go
index 48a9174..7594a08 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -39,18 +39,10 @@
 )
 
 const (
-	// VendorVariation is the variant name used for /vendor code that does not
-	// compile against the VNDK.
-	VendorVariation = "vendor"
-
 	// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
 	// against the VNDK.
 	VendorVariationPrefix = "vendor."
 
-	// ProductVariation is the variant name used for /product code that does not
-	// compile against the VNDK.
-	ProductVariation = "product"
-
 	// ProductVariationPrefix is the variant prefix used for /product code that compiles
 	// against the VNDK.
 	ProductVariationPrefix = "product."
@@ -117,12 +109,12 @@
 
 // Returns true if the module is "product" variant. Usually these modules are installed in /product
 func (c *Module) InProduct() bool {
-	return c.Properties.ImageVariation == ProductVariation
+	return c.Properties.ImageVariation == android.ProductVariation
 }
 
 // Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
 func (c *Module) InVendor() bool {
-	return c.Properties.ImageVariation == VendorVariation
+	return c.Properties.ImageVariation == android.VendorVariation
 }
 
 // Returns true if the module is "vendor" or "product" variant. This replaces previous UseVndk usages
@@ -207,6 +199,12 @@
 
 	// SetCoreVariantNeeded sets whether the Core Variant is needed.
 	SetCoreVariantNeeded(b bool)
+
+	// SetProductVariantNeeded sets whether the Product Variant is needed.
+	SetProductVariantNeeded(b bool)
+
+	// SetVendorVariantNeeded sets whether the Vendor Variant is needed.
+	SetVendorVariantNeeded(b bool)
 }
 
 var _ ImageMutatableModule = (*Module)(nil)
@@ -267,6 +265,14 @@
 	m.Properties.CoreVariantNeeded = b
 }
 
+func (m *Module) SetProductVariantNeeded(b bool) {
+	m.Properties.ProductVariantNeeded = b
+}
+
+func (m *Module) SetVendorVariantNeeded(b bool) {
+	m.Properties.VendorVariantNeeded = b
+}
+
 func (m *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
 	if snapshot, ok := m.linker.(SnapshotInterface); ok {
 		return snapshot.Version()
@@ -319,41 +325,34 @@
 		}
 	}
 
+	var vendorVariantNeeded bool = false
+	var productVariantNeeded bool = false
 	var coreVariantNeeded bool = false
 	var ramdiskVariantNeeded bool = false
 	var vendorRamdiskVariantNeeded bool = false
 	var recoveryVariantNeeded bool = false
 
-	var vendorVariants []string
-	var productVariants []string
-
-	needVndkVersionVendorVariantForLlndk := false
-
 	if m.NeedsLlndkVariants() {
 		// This is an LLNDK library.  The implementation of the library will be on /system,
 		// and vendor and product variants will be created with LLNDK stubs.
 		// The LLNDK libraries need vendor variants even if there is no VNDK.
 		coreVariantNeeded = true
-		vendorVariants = append(vendorVariants, "")
-		productVariants = append(productVariants, "")
-		// Generate vendor variants for boardVndkVersion only if the VNDK snapshot does not
-		// provide the LLNDK stub libraries.
-		if needVndkVersionVendorVariantForLlndk {
-			vendorVariants = append(vendorVariants, "")
-		}
+		vendorVariantNeeded = true
+		productVariantNeeded = true
+
 	} else if m.NeedsVendorPublicLibraryVariants() {
 		// A vendor public library has the implementation on /vendor, with stub variants
 		// for system and product.
 		coreVariantNeeded = true
-		vendorVariants = append(vendorVariants, "")
-		productVariants = append(productVariants, "")
+		vendorVariantNeeded = true
+		productVariantNeeded = true
 	} else if m.IsSnapshotPrebuilt() {
 		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
 		// PRODUCT_EXTRA_VNDK_VERSIONS.
 		if m.InstallInRecovery() {
 			recoveryVariantNeeded = true
 		} else {
-			vendorVariants = append(vendorVariants, m.SnapshotVersion(mctx))
+			m.AppendExtraVariant(VendorVariationPrefix + m.SnapshotVersion(mctx))
 		}
 	} else if m.HasNonSystemVariants() {
 		// This will be available to /system unless it is product_specific
@@ -364,16 +363,16 @@
 		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
 		// PLATFORM_VNDK_VERSION.
 		if m.HasVendorVariant() {
-			vendorVariants = append(vendorVariants, "")
+			vendorVariantNeeded = true
 		}
 
 		// product_available modules are available to /product.
 		if m.HasProductVariant() {
-			productVariants = append(productVariants, "")
+			productVariantNeeded = true
 		}
 	} else if vendorSpecific && m.SdkVersion() == "" {
 		// This will be available in /vendor (or /odm) only
-		vendorVariants = append(vendorVariants, "")
+		vendorVariantNeeded = true
 	} else {
 		// This is either in /system (or similar: /data), or is a
 		// module built with the NDK. Modules built with the NDK
@@ -384,7 +383,7 @@
 	if coreVariantNeeded && productSpecific && m.SdkVersion() == "" {
 		// The module has "product_specific: true" that does not create core variant.
 		coreVariantNeeded = false
-		productVariants = append(productVariants, "")
+		productVariantNeeded = true
 	}
 
 	if m.RamdiskAvailable() {
@@ -414,36 +413,32 @@
 		coreVariantNeeded = false
 	}
 
-	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
-		if variant == "" {
-			m.AppendExtraVariant(VendorVariation)
-		} else {
-			m.AppendExtraVariant(VendorVariationPrefix + variant)
-		}
-	}
-
-	for _, variant := range android.FirstUniqueStrings(productVariants) {
-		if variant == "" {
-			m.AppendExtraVariant(ProductVariation)
-		} else {
-			m.AppendExtraVariant(ProductVariationPrefix + variant)
-		}
-	}
-
 	m.SetRamdiskVariantNeeded(ramdiskVariantNeeded)
 	m.SetVendorRamdiskVariantNeeded(vendorRamdiskVariantNeeded)
 	m.SetRecoveryVariantNeeded(recoveryVariantNeeded)
 	m.SetCoreVariantNeeded(coreVariantNeeded)
+	m.SetProductVariantNeeded(productVariantNeeded)
+	m.SetVendorVariantNeeded(vendorVariantNeeded)
 
 	// Disable the module if no variants are needed.
 	if !ramdiskVariantNeeded &&
 		!recoveryVariantNeeded &&
 		!coreVariantNeeded &&
+		!productVariantNeeded &&
+		!vendorVariantNeeded &&
 		len(m.ExtraVariants()) == 0 {
 		m.Disable()
 	}
 }
 
+func (c *Module) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.VendorVariantNeeded
+}
+
+func (c *Module) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.ProductVariantNeeded
+}
+
 func (c *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return c.Properties.CoreVariantNeeded
 }
@@ -470,11 +465,8 @@
 
 func squashVendorSrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
-		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
-			lib.baseCompiler.Properties.Target.Vendor.Srcs...)
-
-		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
-			lib.baseCompiler.Properties.Target.Vendor.Exclude_srcs...)
+		lib.baseCompiler.Properties.Srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Vendor.Srcs)
+		lib.baseCompiler.Properties.Exclude_srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Vendor.Exclude_srcs)
 
 		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
 			lib.baseCompiler.Properties.Target.Vendor.Exclude_generated_sources...)
@@ -487,11 +479,8 @@
 
 func squashProductSrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
-		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
-			lib.baseCompiler.Properties.Target.Product.Srcs...)
-
-		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
-			lib.baseCompiler.Properties.Target.Product.Exclude_srcs...)
+		lib.baseCompiler.Properties.Srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Product.Srcs)
+		lib.baseCompiler.Properties.Exclude_srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Product.Exclude_srcs)
 
 		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
 			lib.baseCompiler.Properties.Target.Product.Exclude_generated_sources...)
@@ -504,11 +493,8 @@
 
 func squashRecoverySrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
-		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
-			lib.baseCompiler.Properties.Target.Recovery.Srcs...)
-
-		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
-			lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs...)
+		lib.baseCompiler.Properties.Srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Recovery.Srcs)
+		lib.baseCompiler.Properties.Exclude_srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs)
 
 		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
 			lib.baseCompiler.Properties.Target.Recovery.Exclude_generated_sources...)
@@ -517,13 +503,13 @@
 
 func squashVendorRamdiskSrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
-		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs, lib.baseCompiler.Properties.Target.Vendor_ramdisk.Exclude_srcs...)
+		lib.baseCompiler.Properties.Exclude_srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Vendor_ramdisk.Exclude_srcs)
 	}
 }
 
 func squashRamdiskSrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
-		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs, lib.baseCompiler.Properties.Target.Ramdisk.Exclude_srcs...)
+		lib.baseCompiler.Properties.Exclude_srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Ramdisk.Exclude_srcs)
 	}
 }
 
@@ -537,15 +523,15 @@
 	} else if variant == android.RecoveryVariation {
 		c.MakeAsPlatform()
 		squashRecoverySrcs(c)
-	} else if strings.HasPrefix(variant, VendorVariation) {
-		c.Properties.ImageVariation = VendorVariation
+	} else if strings.HasPrefix(variant, android.VendorVariation) {
+		c.Properties.ImageVariation = android.VendorVariation
 
 		if strings.HasPrefix(variant, VendorVariationPrefix) {
 			c.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
 		}
 		squashVendorSrcs(c)
-	} else if strings.HasPrefix(variant, ProductVariation) {
-		c.Properties.ImageVariation = ProductVariation
+	} else if strings.HasPrefix(variant, android.ProductVariation) {
+		c.Properties.ImageVariation = android.ProductVariation
 		if strings.HasPrefix(variant, ProductVariationPrefix) {
 			c.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
 		}
diff --git a/cc/libbuildversion/Android.bp b/cc/libbuildversion/Android.bp
index b105a30..c1f2c10 100644
--- a/cc/libbuildversion/Android.bp
+++ b/cc/libbuildversion/Android.bp
@@ -20,4 +20,5 @@
         "//apex_available:anyapex",
     ],
     vendor_available: true,
+    visibility: ["//visibility:public"],
 }
diff --git a/cc/library.go b/cc/library.go
index e49f50c..c4ec88c 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -19,6 +19,7 @@
 	"io"
 	"path/filepath"
 	"regexp"
+	"slices"
 	"strconv"
 	"strings"
 	"sync"
@@ -63,7 +64,7 @@
 	Stubs struct {
 		// Relative path to the symbol map. The symbol map provides the list of
 		// symbols that are exported for stubs variant of this library.
-		Symbol_file *string `android:"path"`
+		Symbol_file *string `android:"path,arch_variant"`
 
 		// List versions to generate stubs libs for. The version name "current" is always
 		// implicitly added.
@@ -74,7 +75,7 @@
 		// implementation is made available by some other means, e.g. in a Microdroid
 		// virtual machine.
 		Implementation_installable *bool
-	}
+	} `android:"arch_variant"`
 
 	// set the name of the output
 	Stem *string `android:"arch_variant"`
@@ -117,7 +118,7 @@
 
 	// If this is an LLNDK library, properties to describe the LLNDK stubs.  Will be copied from
 	// the module pointed to by llndk_stubs if it is set.
-	Llndk llndkLibraryProperties
+	Llndk llndkLibraryProperties `android:"arch_variant"`
 
 	// If this is a vendor public library, properties to describe the vendor public library stubs.
 	Vendor_public_library vendorPublicLibraryProperties
@@ -141,7 +142,7 @@
 // Use `StaticProperties` or `SharedProperties`, depending on which variant is needed.
 // `StaticOrSharedProperties` exists only to avoid duplication.
 type StaticOrSharedProperties struct {
-	Srcs []string `android:"path,arch_variant"`
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	Tidy_disabled_srcs []string `android:"path,arch_variant"`
 
@@ -151,11 +152,11 @@
 
 	Cflags proptools.Configurable[[]string] `android:"arch_variant"`
 
-	Enabled            *bool    `android:"arch_variant"`
-	Whole_static_libs  []string `android:"arch_variant"`
-	Static_libs        []string `android:"arch_variant"`
-	Shared_libs        []string `android:"arch_variant"`
-	System_shared_libs []string `android:"arch_variant"`
+	Enabled            *bool                            `android:"arch_variant"`
+	Whole_static_libs  proptools.Configurable[[]string] `android:"arch_variant"`
+	Static_libs        proptools.Configurable[[]string] `android:"arch_variant"`
+	Shared_libs        proptools.Configurable[[]string] `android:"arch_variant"`
+	System_shared_libs []string                         `android:"arch_variant"`
 
 	Export_shared_lib_headers []string `android:"arch_variant"`
 	Export_static_lib_headers []string `android:"arch_variant"`
@@ -196,6 +197,9 @@
 	// using -isystem for this module and any module that links against this module.
 	Export_system_include_dirs []string `android:"arch_variant,variant_prepend"`
 
+	// list of plain cc flags to be used for any module that links against this module.
+	Export_cflags []string  `android:"arch_variant"`
+
 	Target struct {
 		Vendor, Product struct {
 			// list of exported include directories, like
@@ -306,6 +310,10 @@
 	f.systemDirs = append(f.systemDirs, android.PathsForModuleSrc(ctx, f.Properties.Export_system_include_dirs)...)
 }
 
+func (f *flagExporter) exportExtraFlags(ctx ModuleContext) {
+	f.flags = append(f.flags, f.Properties.Export_cflags...)
+}
+
 // exportIncludesAsSystem registers the include directories and system include directories to be
 // exported transitively both as system include directories to modules depending on this module.
 func (f *flagExporter) exportIncludesAsSystem(ctx ModuleContext) {
@@ -427,6 +435,9 @@
 	*baseInstaller
 
 	apiListCoverageXmlPath android.ModuleOutPath
+
+	// Path to the file containing the APIs exported by this library
+	stubsSymbolFilePath android.Path
 }
 
 // linkerProps returns the list of properties structs relevant for this library. (For example, if
@@ -590,52 +601,20 @@
 		return objs
 	}
 	if library.buildStubs() {
-		symbolFile := String(library.Properties.Stubs.Symbol_file)
-		if symbolFile != "" && !strings.HasSuffix(symbolFile, ".map.txt") {
-			ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile)
-			return Objects{}
-		}
-		// b/239274367 --apex and --systemapi filters symbols tagged with # apex and #
-		// systemapi, respectively. The former is for symbols defined in platform libraries
-		// and the latter is for symbols defined in APEXes.
-		// A single library can contain either # apex or # systemapi, but not both.
-		// The stub generator (ndkstubgen) is additive, so passing _both_ of these to it should be a no-op.
-		// 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() {
-			flag = "--apex"
-		} else {
-			flag = "--systemapi"
-		}
-		// b/184712170, unless the lib is an NDK library, exclude all public symbols from
-		// the stub so that it is mandated that all symbols are explicitly marked with
-		// either apex or systemapi.
-		if !ctx.Module().(*Module).IsNdk(ctx.Config()) {
-			flag = flag + " --no-ndk"
-		}
-		nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
-			android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag)
-		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
-		library.versionScriptPath = android.OptionalPathForPath(
-			nativeAbiResult.versionScript)
-
-		// Parse symbol file to get API list for coverage
-		if library.stubsVersion() == "current" && ctx.PrimaryArch() && !ctx.inRecovery() && !ctx.inProduct() && !ctx.inVendor() {
-			library.apiListCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile)
-		}
-
-		return objs
+		return library.compileModuleLibApiStubs(ctx, flags, deps)
 	}
 
+	srcs := library.baseCompiler.Properties.Srcs.GetOrDefault(ctx, nil)
+	staticSrcs := library.StaticProperties.Static.Srcs.GetOrDefault(ctx, nil)
+	sharedSrcs := library.SharedProperties.Shared.Srcs.GetOrDefault(ctx, nil)
 	if !library.buildShared() && !library.buildStatic() {
-		if len(library.baseCompiler.Properties.Srcs) > 0 {
+		if len(srcs) > 0 {
 			ctx.PropertyErrorf("srcs", "cc_library_headers must not have any srcs")
 		}
-		if len(library.StaticProperties.Static.Srcs) > 0 {
+		if len(staticSrcs) > 0 {
 			ctx.PropertyErrorf("static.srcs", "cc_library_headers must not have any srcs")
 		}
-		if len(library.SharedProperties.Shared.Srcs) > 0 {
+		if len(sharedSrcs) > 0 {
 			ctx.PropertyErrorf("shared.srcs", "cc_library_headers must not have any srcs")
 		}
 		return Objects{}
@@ -646,8 +625,8 @@
 		for _, dir := range dirs {
 			flags.SAbiFlags = append(flags.SAbiFlags, "-I"+dir)
 		}
-		totalLength := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) +
-			len(library.SharedProperties.Shared.Srcs) + len(library.StaticProperties.Static.Srcs)
+		totalLength := len(srcs) + len(deps.GeneratedSources) +
+			len(sharedSrcs) + len(staticSrcs)
 		if totalLength > 0 {
 			flags.SAbiDump = true
 		}
@@ -657,13 +636,13 @@
 	buildFlags := flagsToBuilderFlags(flags)
 
 	if library.static() {
-		srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs)
+		srcs := android.PathsForModuleSrc(ctx, staticSrcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, srcs,
 			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_disabled_srcs),
 			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_timeout_srcs),
 			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	} else if library.shared() {
-		srcs := android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Srcs)
+		srcs := android.PathsForModuleSrc(ctx, sharedSrcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary, srcs,
 			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_disabled_srcs),
 			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_timeout_srcs),
@@ -673,6 +652,61 @@
 	return objs
 }
 
+// Compile stubs for the API surface between platform and apex
+// This method will be used by source and prebuilt cc module types.
+func (library *libraryDecorator) compileModuleLibApiStubs(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+	// TODO (b/275273834): Make this a hard error when the symbol files have been added to module sdk.
+	if library.Properties.Stubs.Symbol_file == nil {
+		return Objects{}
+	}
+	symbolFile := String(library.Properties.Stubs.Symbol_file)
+	library.stubsSymbolFilePath = android.PathForModuleSrc(ctx, symbolFile)
+	// b/239274367 --apex and --systemapi filters symbols tagged with # apex and #
+	// systemapi, respectively. The former is for symbols defined in platform libraries
+	// and the latter is for symbols defined in APEXes.
+	// A single library can contain either # apex or # systemapi, but not both.
+	// The stub generator (ndkstubgen) is additive, so passing _both_ of these to it should be a no-op.
+	// 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() {
+		flag = "--apex"
+	} else {
+		flag = "--systemapi"
+	}
+	// b/184712170, unless the lib is an NDK library, exclude all public symbols from
+	// the stub so that it is mandated that all symbols are explicitly marked with
+	// either apex or systemapi.
+	if !ctx.Module().(*Module).IsNdk(ctx.Config()) &&
+		// the symbol files of libclang libs are autogenerated and do not contain systemapi tags
+		// TODO (spandandas): Update mapfile.py to include #systemapi tag on all symbols
+		!strings.Contains(ctx.ModuleName(), "libclang_rt") {
+		flag = flag + " --no-ndk"
+	}
+	// TODO(b/361303067): Remove this special case if bionic/ projects are added to ART development branches.
+	if isBionic(ctx.baseModuleName()) {
+		// set the flags explicitly for bionic libs.
+		// this is necessary for development in minimal branches which does not contain bionic/*.
+		// In such minimal branches, e.g. on the prebuilt libc stubs
+		// 1. IsNdk will return false (since the ndk_library definition for libc does not exist)
+		// 2. NotInPlatform will return true (since the source com.android.runtime does not exist)
+		flag = "--apex"
+	}
+	nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
+		android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag)
+	objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
+
+	library.versionScriptPath = android.OptionalPathForPath(
+		nativeAbiResult.versionScript)
+
+	// Parse symbol file to get API list for coverage
+	if library.stubsVersion() == "current" && ctx.PrimaryArch() && !ctx.inRecovery() && !ctx.inProduct() && !ctx.inVendor() {
+		library.apiListCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile)
+	}
+
+	return objs
+}
+
 type libraryInterface interface {
 	versionedInterface
 
@@ -711,7 +745,7 @@
 	setStubsVersion(string)
 	stubsVersion() string
 
-	stubsVersions(ctx android.BaseMutatorContext) []string
+	stubsVersions(ctx android.BaseModuleContext) []string
 	setAllStubsVersions([]string)
 	allStubsVersions() []string
 
@@ -829,9 +863,9 @@
 
 	if library.static() {
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs,
-			library.StaticProperties.Static.Whole_static_libs...)
-		deps.StaticLibs = append(deps.StaticLibs, library.StaticProperties.Static.Static_libs...)
-		deps.SharedLibs = append(deps.SharedLibs, library.StaticProperties.Static.Shared_libs...)
+			library.StaticProperties.Static.Whole_static_libs.GetOrDefault(ctx, nil)...)
+		deps.StaticLibs = append(deps.StaticLibs, library.StaticProperties.Static.Static_libs.GetOrDefault(ctx, nil)...)
+		deps.SharedLibs = append(deps.SharedLibs, library.StaticProperties.Static.Shared_libs.GetOrDefault(ctx, nil)...)
 
 		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.StaticProperties.Static.Export_shared_lib_headers...)
 		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
@@ -844,9 +878,9 @@
 		if library.baseLinker.Properties.crtPadSegment() {
 			deps.CrtEnd = append(deps.CrtEnd, ctx.toolchain().CrtPadSegmentSharedLibrary()...)
 		}
-		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...)
-		deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...)
-		deps.SharedLibs = append(deps.SharedLibs, library.SharedProperties.Shared.Shared_libs...)
+		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs.GetOrDefault(ctx, nil)...)
+		deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs.GetOrDefault(ctx, nil)...)
+		deps.SharedLibs = append(deps.SharedLibs, library.SharedProperties.Shared.Shared_libs.GetOrDefault(ctx, nil)...)
 
 		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.SharedProperties.Shared.Export_shared_lib_headers...)
 		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.SharedProperties.Shared.Export_static_lib_headers...)
@@ -892,8 +926,8 @@
 	return deps
 }
 
-func (library *libraryDecorator) linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps {
-	specifiedDeps = library.baseLinker.linkerSpecifiedDeps(specifiedDeps)
+func (library *libraryDecorator) linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
+	specifiedDeps = library.baseLinker.linkerSpecifiedDeps(ctx, module, specifiedDeps)
 	var properties StaticOrSharedProperties
 	if library.static() {
 		properties = library.StaticProperties.Static
@@ -901,7 +935,8 @@
 		properties = library.SharedProperties.Shared
 	}
 
-	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, properties.Shared_libs...)
+	eval := module.ConfigurableEvaluator(ctx)
+	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, properties.Shared_libs.GetOrDefault(eval, nil)...)
 
 	// Must distinguish nil and [] in system_shared_libs - ensure that [] in
 	// either input list doesn't come out as nil.
@@ -1135,8 +1170,12 @@
 	linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 
-	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
-		deps.StaticLibs = append(deps.StaticLibs, generatedLib)
+	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil && !library.buildStubs() {
+		if ctx.Module().(*Module).WholeRustStaticlib {
+			deps.WholeStaticLibs = append(deps.WholeStaticLibs, generatedLib)
+		} else {
+			deps.StaticLibs = append(deps.StaticLibs, generatedLib)
+		}
 	}
 
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
@@ -1169,12 +1208,17 @@
 	return unstrippedOutputFile
 }
 
-func addStubDependencyProviders(ctx ModuleContext) {
+// Visits the stub variants of the library and returns a struct containing the stub .so paths
+func addStubDependencyProviders(ctx ModuleContext) []SharedStubLibrary {
+	stubsInfo := []SharedStubLibrary{}
 	stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
 	if len(stubs) > 0 {
-		var stubsInfo []SharedStubLibrary
 		for _, stub := range stubs {
-			stubInfo, _ := android.OtherModuleProvider(ctx, stub, SharedLibraryInfoProvider)
+			stubInfo, ok := android.OtherModuleProvider(ctx, stub, SharedLibraryInfoProvider)
+			// TODO (b/275273834): Make this a hard error when the symbol files have been added to module sdk.
+			if !ok {
+				continue
+			}
 			flagInfo, _ := android.OtherModuleProvider(ctx, stub, FlagExporterInfoProvider)
 			stubsInfo = append(stubsInfo, SharedStubLibrary{
 				Version:           moduleLibraryInterface(stub).stubsVersion(),
@@ -1182,11 +1226,14 @@
 				FlagExporterInfo:  flagInfo,
 			})
 		}
-		android.SetProvider(ctx, SharedLibraryStubsProvider, SharedLibraryStubsInfo{
-			SharedStubLibraries: stubsInfo,
-			IsLLNDK:             ctx.IsLlndk(),
-		})
+		if len(stubsInfo) > 0 {
+			android.SetProvider(ctx, SharedLibraryStubsProvider, SharedLibraryStubsInfo{
+				SharedStubLibraries: stubsInfo,
+				IsLLNDK:             ctx.IsLlndk(),
+			})
+		}
 	}
+	return stubsInfo
 }
 
 func (library *libraryDecorator) unstrippedOutputFilePath() android.Path {
@@ -1224,14 +1271,6 @@
 func (library *libraryDecorator) llndkIncludeDirsForAbiCheck(ctx ModuleContext, deps PathDeps) []string {
 	var includeDirs, systemIncludeDirs []string
 
-	// The ABI checker does not need the preprocess which adds macro guards to function declarations.
-	preprocessedDirs := android.PathsForModuleSrc(ctx, library.Properties.Llndk.Export_preprocessed_headers).Strings()
-	if Bool(library.Properties.Llndk.Export_headers_as_system) {
-		systemIncludeDirs = append(systemIncludeDirs, preprocessedDirs...)
-	} else {
-		includeDirs = append(includeDirs, preprocessedDirs...)
-	}
-
 	if library.Properties.Llndk.Override_export_include_dirs != nil {
 		includeDirs = append(includeDirs, android.PathsForModuleSrc(
 			ctx, library.Properties.Llndk.Override_export_include_dirs).Strings()...)
@@ -1539,25 +1578,6 @@
 	}
 }
 
-func processLLNDKHeaders(ctx ModuleContext, srcHeaderDir string, outDir android.ModuleGenPath) (timestamp android.Path, installPaths android.WritablePaths) {
-	srcDir := android.PathForModuleSrc(ctx, srcHeaderDir)
-	srcFiles := ctx.GlobFiles(filepath.Join(srcDir.String(), "**/*.h"), nil)
-
-	for _, header := range srcFiles {
-		headerDir := filepath.Dir(header.String())
-		relHeaderDir, err := filepath.Rel(srcDir.String(), headerDir)
-		if err != nil {
-			ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s",
-				srcDir.String(), headerDir, err)
-			continue
-		}
-
-		installPaths = append(installPaths, outDir.Join(ctx, relHeaderDir, header.Base()))
-	}
-
-	return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths), installPaths
-}
-
 // link registers actions to link this library, and sets various fields
 // on this library to reflect information that should be exported up the build
 // tree (for example, exported flags and include paths).
@@ -1565,26 +1585,6 @@
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
 	if ctx.IsLlndk() {
-		if len(library.Properties.Llndk.Export_preprocessed_headers) > 0 {
-			// This is the vendor variant of an LLNDK library with preprocessed headers.
-			genHeaderOutDir := android.PathForModuleGen(ctx, "include")
-
-			var timestampFiles android.Paths
-			for _, dir := range library.Properties.Llndk.Export_preprocessed_headers {
-				timestampFile, installPaths := processLLNDKHeaders(ctx, dir, genHeaderOutDir)
-				timestampFiles = append(timestampFiles, timestampFile)
-				library.addExportedGeneratedHeaders(installPaths.Paths()...)
-			}
-
-			if Bool(library.Properties.Llndk.Export_headers_as_system) {
-				library.reexportSystemDirs(genHeaderOutDir)
-			} else {
-				library.reexportDirs(genHeaderOutDir)
-			}
-
-			library.reexportDeps(timestampFiles...)
-		}
-
 		// override the module's export_include_dirs with llndk.override_export_include_dirs
 		// if it is set.
 		if override := library.Properties.Llndk.Override_export_include_dirs; override != nil {
@@ -1630,6 +1630,7 @@
 
 	// Export include paths and flags to be propagated up the tree.
 	library.exportIncludes(ctx)
+	library.exportExtraFlags(ctx)
 	library.reexportDirs(deps.ReexportedDirs...)
 	library.reexportSystemDirs(deps.ReexportedSystemDirs...)
 	library.reexportFlags(deps.ReexportedFlags...)
@@ -1642,8 +1643,8 @@
 
 	// Optionally export aidl headers.
 	if Bool(library.Properties.Aidl.Export_aidl_headers) {
-		if library.baseCompiler.hasAidl(deps) {
-			if library.baseCompiler.hasSrcExt(".aidl") {
+		if library.baseCompiler.hasAidl(ctx, deps) {
+			if library.baseCompiler.hasSrcExt(ctx, ".aidl") {
 				dir := android.PathForModuleGen(ctx, "aidl")
 				library.reexportDirs(dir)
 			}
@@ -1659,7 +1660,7 @@
 
 	// Optionally export proto headers.
 	if Bool(library.Properties.Proto.Export_proto_headers) {
-		if library.baseCompiler.hasSrcExt(".proto") {
+		if library.baseCompiler.hasSrcExt(ctx, ".proto") {
 			var includes android.Paths
 			if flags.proto.CanonicalPathFromRoot {
 				includes = append(includes, flags.proto.SubDir)
@@ -1673,7 +1674,7 @@
 	}
 
 	// If the library is sysprop_library, expose either public or internal header selectively.
-	if library.baseCompiler.hasSrcExt(".sysprop") {
+	if library.baseCompiler.hasSrcExt(ctx, ".sysprop") {
 		dir := android.PathForModuleGen(ctx, "sysprop", "include")
 		if library.Properties.Sysprop.Platform != nil {
 			isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
@@ -1899,7 +1900,7 @@
 	return BoolDefault(library.Properties.Stubs.Implementation_installable, true)
 }
 
-func (library *libraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
+func (library *libraryDecorator) stubsVersions(ctx android.BaseModuleContext) []string {
 	if !library.hasStubsVariants() {
 		return nil
 	}
@@ -2060,46 +2061,48 @@
 
 // connects a shared library to a static library in order to reuse its .o files to avoid
 // compiling source files twice.
-func reuseStaticLibrary(mctx android.BottomUpMutatorContext, static, shared *Module) {
-	if staticCompiler, ok := static.compiler.(*libraryDecorator); ok {
-		sharedCompiler := shared.compiler.(*libraryDecorator)
+func reuseStaticLibrary(ctx android.BottomUpMutatorContext, shared *Module) {
+	if sharedCompiler, ok := shared.compiler.(*libraryDecorator); ok {
 
 		// Check libraries in addition to cflags, since libraries may be exporting different
 		// include directories.
-		if len(staticCompiler.StaticProperties.Static.Cflags.GetOrDefault(mctx, nil)) == 0 &&
-			len(sharedCompiler.SharedProperties.Shared.Cflags.GetOrDefault(mctx, nil)) == 0 &&
-			len(staticCompiler.StaticProperties.Static.Whole_static_libs) == 0 &&
-			len(sharedCompiler.SharedProperties.Shared.Whole_static_libs) == 0 &&
-			len(staticCompiler.StaticProperties.Static.Static_libs) == 0 &&
-			len(sharedCompiler.SharedProperties.Shared.Static_libs) == 0 &&
-			len(staticCompiler.StaticProperties.Static.Shared_libs) == 0 &&
-			len(sharedCompiler.SharedProperties.Shared.Shared_libs) == 0 &&
+		if len(sharedCompiler.StaticProperties.Static.Cflags.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Cflags.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.StaticProperties.Static.Whole_static_libs.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Whole_static_libs.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.StaticProperties.Static.Static_libs.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Static_libs.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.StaticProperties.Static.Shared_libs.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Shared_libs.GetOrDefault(ctx, nil)) == 0 &&
 			// Compare System_shared_libs properties with nil because empty lists are
 			// semantically significant for them.
-			staticCompiler.StaticProperties.Static.System_shared_libs == nil &&
+			sharedCompiler.StaticProperties.Static.System_shared_libs == nil &&
 			sharedCompiler.SharedProperties.Shared.System_shared_libs == nil {
 
-			mctx.AddInterVariantDependency(reuseObjTag, shared, static)
+			// TODO: namespaces?
+			ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, reuseObjTag, ctx.ModuleName())
 			sharedCompiler.baseCompiler.Properties.OriginalSrcs =
 				sharedCompiler.baseCompiler.Properties.Srcs
-			sharedCompiler.baseCompiler.Properties.Srcs = nil
+			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
-		mctx.AddInterVariantDependency(staticVariantTag, shared, static)
+		ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, staticVariantTag, ctx.ModuleName())
 	}
 }
 
-// LinkageMutator adds "static" or "shared" variants for modules depending
+// linkageTransitionMutator adds "static" or "shared" variants for modules depending
 // on whether the module can be built as a static library or a shared library.
-func LinkageMutator(mctx android.BottomUpMutatorContext) {
+type linkageTransitionMutator struct{}
+
+func (linkageTransitionMutator) Split(ctx android.BaseModuleContext) []string {
 	ccPrebuilt := false
-	if m, ok := mctx.Module().(*Module); ok && m.linker != nil {
+	if m, ok := ctx.Module().(*Module); ok && m.linker != nil {
 		_, ccPrebuilt = m.linker.(prebuiltLibraryInterface)
 	}
 	if ccPrebuilt {
-		library := mctx.Module().(*Module).linker.(prebuiltLibraryInterface)
+		library := ctx.Module().(*Module).linker.(prebuiltLibraryInterface)
 
 		// Differentiate between header only and building an actual static/shared library
 		buildStatic := library.buildStatic()
@@ -2108,70 +2111,118 @@
 			// Always create both the static and shared variants for prebuilt libraries, and then disable the one
 			// that is not being used.  This allows them to share the name of a cc_library module, which requires that
 			// all the variants of the cc_library also exist on the prebuilt.
-			modules := mctx.CreateLocalVariations("static", "shared")
-			static := modules[0].(*Module)
-			shared := modules[1].(*Module)
-
-			static.linker.(prebuiltLibraryInterface).setStatic()
-			shared.linker.(prebuiltLibraryInterface).setShared()
-
-			if buildShared {
-				mctx.AliasVariation("shared")
-			} else if buildStatic {
-				mctx.AliasVariation("static")
-			}
-
-			if !buildStatic {
-				static.linker.(prebuiltLibraryInterface).disablePrebuilt()
-			}
-			if !buildShared {
-				shared.linker.(prebuiltLibraryInterface).disablePrebuilt()
-			}
+			return []string{"static", "shared"}
 		} else {
 			// Header only
 		}
-
-	} else if library, ok := mctx.Module().(LinkableInterface); ok && (library.CcLibraryInterface() || library.RustLibraryInterface()) {
+	} else if library, ok := ctx.Module().(LinkableInterface); ok && (library.CcLibraryInterface() || library.RustLibraryInterface()) {
 		// Non-cc.Modules may need an empty variant for their mutators.
 		variations := []string{}
 		if library.NonCcVariants() {
 			variations = append(variations, "")
 		}
 		isLLNDK := false
-		if m, ok := mctx.Module().(*Module); ok {
+		if m, ok := ctx.Module().(*Module); ok {
 			isLLNDK = m.IsLlndk()
 		}
 		buildStatic := library.BuildStaticVariant() && !isLLNDK
 		buildShared := library.BuildSharedVariant()
 		if buildStatic && buildShared {
-			variations := append([]string{"static", "shared"}, variations...)
-
-			modules := mctx.CreateLocalVariations(variations...)
-			static := modules[0].(LinkableInterface)
-			shared := modules[1].(LinkableInterface)
-
-			static.SetStatic()
-			shared.SetShared()
-
-			if _, ok := library.(*Module); ok {
-				reuseStaticLibrary(mctx, static.(*Module), shared.(*Module))
-			}
-			mctx.AliasVariation("shared")
+			variations = append([]string{"static", "shared"}, variations...)
+			return variations
 		} else if buildStatic {
-			variations := append([]string{"static"}, variations...)
-
-			modules := mctx.CreateLocalVariations(variations...)
-			modules[0].(LinkableInterface).SetStatic()
-			mctx.AliasVariation("static")
+			variations = append([]string{"static"}, variations...)
 		} else if buildShared {
-			variations := append([]string{"shared"}, variations...)
+			variations = append([]string{"shared"}, variations...)
+		}
 
-			modules := mctx.CreateLocalVariations(variations...)
-			modules[0].(LinkableInterface).SetShared()
-			mctx.AliasVariation("shared")
-		} else if len(variations) > 0 {
-			mctx.CreateLocalVariations(variations...)
-			mctx.AliasVariation(variations[0])
+		if len(variations) > 0 {
+			return variations
+		}
+	}
+	return []string{""}
+}
+
+func (linkageTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return ""
+}
+
+func (linkageTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	ccPrebuilt := false
+	if m, ok := ctx.Module().(*Module); ok && m.linker != nil {
+		_, ccPrebuilt = m.linker.(prebuiltLibraryInterface)
+	}
+	if ccPrebuilt {
+		if incomingVariation != "" {
+			return incomingVariation
+		}
+		library := ctx.Module().(*Module).linker.(prebuiltLibraryInterface)
+		if library.buildShared() {
+			return "shared"
+		} else if library.buildStatic() {
+			return "static"
+		}
+		return ""
+	} else if library, ok := ctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() {
+		isLLNDK := false
+		if m, ok := ctx.Module().(*Module); ok {
+			isLLNDK = m.IsLlndk()
+		}
+		buildStatic := library.BuildStaticVariant() && !isLLNDK
+		buildShared := library.BuildSharedVariant()
+		if library.BuildRlibVariant() && library.IsRustFFI() && !buildStatic && (incomingVariation == "static" || incomingVariation == "") {
+			// Rust modules do not build static libs, but rlibs are used as if they
+			// were via `static_libs`. Thus we need to alias the BuildRlibVariant
+			// to "static" for Rust FFI libraries.
+			return ""
+		}
+		if incomingVariation != "" {
+			return incomingVariation
+		}
+		if buildShared {
+			return "shared"
+		} else if buildStatic {
+			return "static"
+		}
+		return ""
+	}
+	return ""
+}
+
+func (linkageTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	ccPrebuilt := false
+	if m, ok := ctx.Module().(*Module); ok && m.linker != nil {
+		_, ccPrebuilt = m.linker.(prebuiltLibraryInterface)
+	}
+	if ccPrebuilt {
+		library := ctx.Module().(*Module).linker.(prebuiltLibraryInterface)
+		if variation == "static" {
+			library.setStatic()
+			if !library.buildStatic() {
+				library.disablePrebuilt()
+			}
+		} else if variation == "shared" {
+			library.setShared()
+			if !library.buildShared() {
+				library.disablePrebuilt()
+			}
+		}
+	} else if library, ok := ctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() {
+		if variation == "static" {
+			library.SetStatic()
+		} else if variation == "shared" {
+			library.SetShared()
+			var isLLNDK bool
+			if m, ok := ctx.Module().(*Module); ok {
+				isLLNDK = m.IsLlndk()
+			}
+			buildStatic := library.BuildStaticVariant() && !isLLNDK
+			buildShared := library.BuildSharedVariant()
+			if buildStatic && buildShared {
+				if _, ok := library.(*Module); ok {
+					reuseStaticLibrary(ctx, library.(*Module))
+				}
+			}
 		}
 	}
 }
@@ -2195,60 +2246,14 @@
 	}
 }
 
-func createVersionVariations(mctx android.BottomUpMutatorContext, versions []string) {
-	// "" is for the non-stubs (implementation) variant for system modules, or the LLNDK variant
-	// for LLNDK modules.
-	variants := append(android.CopyOf(versions), "")
-
-	m := mctx.Module().(*Module)
-	isLLNDK := m.IsLlndk()
-	isVendorPublicLibrary := m.IsVendorPublicLibrary()
-	isImportedApiLibrary := m.isImportedApiLibrary()
-
-	modules := mctx.CreateLocalVariations(variants...)
-	for i, m := range modules {
-
-		if variants[i] != "" || isLLNDK || isVendorPublicLibrary || isImportedApiLibrary {
-			// A stubs or LLNDK stubs variant.
-			c := m.(*Module)
-			c.sanitize = nil
-			c.stl = nil
-			c.Properties.PreventInstall = true
-			lib := moduleLibraryInterface(m)
-			isLatest := i == (len(versions) - 1)
-			lib.setBuildStubs(isLatest)
-
-			if variants[i] != "" {
-				// A non-LLNDK stubs module is hidden from make and has a dependency from the
-				// implementation module to the stubs module.
-				c.Properties.HideFromMake = true
-				lib.setStubsVersion(variants[i])
-				mctx.AddInterVariantDependency(stubImplDepTag, modules[len(modules)-1], modules[i])
-			}
-		}
-	}
-	mctx.AliasVariation("")
-	latestVersion := ""
-	if len(versions) > 0 {
-		latestVersion = versions[len(versions)-1]
-	}
-	mctx.CreateAliasVariation("latest", latestVersion)
-}
-
-func createPerApiVersionVariations(mctx android.BottomUpMutatorContext, minSdkVersion string) {
+func perApiVersionVariations(mctx android.BaseModuleContext, minSdkVersion string) []string {
 	from, err := nativeApiLevelFromUser(mctx, minSdkVersion)
 	if err != nil {
 		mctx.PropertyErrorf("min_sdk_version", err.Error())
-		return
+		return []string{""}
 	}
 
-	versionStrs := ndkLibraryVersions(mctx, from)
-	modules := mctx.CreateLocalVariations(versionStrs...)
-
-	for i, module := range modules {
-		module.(*Module).Properties.Sdk_version = StringPtr(versionStrs[i])
-		module.(*Module).Properties.Min_sdk_version = StringPtr(versionStrs[i])
-	}
+	return ndkLibraryVersions(mctx, from)
 }
 
 func canBeOrLinkAgainstVersionVariants(module interface {
@@ -2278,7 +2283,7 @@
 }
 
 // setStubsVersions normalizes the versions in the Stubs.Versions property into MutatedProperties.AllStubsVersions.
-func setStubsVersions(mctx android.BottomUpMutatorContext, library libraryInterface, module *Module) {
+func setStubsVersions(mctx android.BaseModuleContext, library libraryInterface, module *Module) {
 	if !library.buildShared() || !canBeVersionVariant(module) {
 		return
 	}
@@ -2291,25 +2296,97 @@
 	library.setAllStubsVersions(versions)
 }
 
-// versionMutator splits a module into the mandatory non-stubs variant
+// versionTransitionMutator splits a module into the mandatory non-stubs variant
 // (which is unnamed) and zero or more stubs variants.
-func versionMutator(mctx android.BottomUpMutatorContext) {
-	if mctx.Os() != android.Android {
-		return
+type versionTransitionMutator struct{}
+
+func (versionTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	if ctx.Os() != android.Android {
+		return []string{""}
 	}
 
-	m, ok := mctx.Module().(*Module)
-	if library := moduleLibraryInterface(mctx.Module()); library != nil && canBeVersionVariant(m) {
-		setStubsVersions(mctx, library, m)
+	m, ok := ctx.Module().(*Module)
+	if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) {
+		setStubsVersions(ctx, library, m)
 
-		createVersionVariations(mctx, library.allStubsVersions())
-		return
+		return append(slices.Clone(library.allStubsVersions()), "")
+	} else if ok && m.SplitPerApiLevel() && m.IsSdkVariant() {
+		return perApiVersionVariations(ctx, m.MinSdkVersion())
 	}
 
-	if ok {
-		if m.SplitPerApiLevel() && m.IsSdkVariant() {
-			createPerApiVersionVariations(mctx, m.MinSdkVersion())
+	return []string{""}
+}
+
+func (versionTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return ""
+}
+
+func (versionTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if ctx.Os() != android.Android {
+		return ""
+	}
+	m, ok := ctx.Module().(*Module)
+	if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) {
+		if incomingVariation == "latest" {
+			latestVersion := ""
+			versions := library.allStubsVersions()
+			if len(versions) > 0 {
+				latestVersion = versions[len(versions)-1]
+			}
+			return latestVersion
 		}
+		return incomingVariation
+	} else if ok && m.SplitPerApiLevel() && m.IsSdkVariant() {
+		// If this module only has variants with versions and the incoming dependency doesn't specify which one
+		// is needed then assume the latest version.
+		if incomingVariation == "" {
+			return android.FutureApiLevel.String()
+		}
+		return incomingVariation
+	}
+
+	return ""
+}
+
+func (versionTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	// Optimization: return early if this module can't be affected.
+	if ctx.Os() != android.Android {
+		return
+	}
+
+	m, ok := ctx.Module().(*Module)
+	if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) {
+		isLLNDK := m.IsLlndk()
+		isVendorPublicLibrary := m.IsVendorPublicLibrary()
+
+		if variation != "" || isLLNDK || isVendorPublicLibrary {
+			// A stubs or LLNDK stubs variant.
+			if m.sanitize != nil {
+				m.sanitize.Properties.ForceDisable = true
+			}
+			if m.stl != nil {
+				m.stl.Properties.Stl = StringPtr("none")
+			}
+			m.Properties.PreventInstall = true
+			lib := moduleLibraryInterface(m)
+			allStubsVersions := library.allStubsVersions()
+			isLatest := len(allStubsVersions) > 0 && variation == allStubsVersions[len(allStubsVersions)-1]
+			lib.setBuildStubs(isLatest)
+		}
+		if variation != "" {
+			// A non-LLNDK stubs module is hidden from make
+			library.setStubsVersion(variation)
+			m.Properties.HideFromMake = true
+		} else {
+			// A non-LLNDK implementation module has a dependency to all stubs versions
+			for _, version := range library.allStubsVersions() {
+				ctx.AddVariationDependencies([]blueprint.Variation{{"version", version}},
+					stubImplDepTag, ctx.ModuleName())
+			}
+		}
+	} else if ok && m.SplitPerApiLevel() && m.IsSdkVariant() {
+		m.Properties.Sdk_version = StringPtr(variation)
+		m.Properties.Min_sdk_version = StringPtr(variation)
 	}
 }
 
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index a65b1ba..af3658d 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -31,6 +31,7 @@
 		SupportsSdk:           true,
 		HostOsDependent:       true,
 		SupportedLinkageNames: []string{"shared"},
+		StripDisabled:         true,
 	},
 	prebuiltModuleType: "cc_prebuilt_library_shared",
 }
@@ -405,6 +406,9 @@
 	if len(libInfo.StubsVersions) > 0 {
 		stubsSet := outputProperties.AddPropertySet("stubs")
 		stubsSet.AddProperty("versions", libInfo.StubsVersions)
+		// The symbol file will be copied next to the Android.bp file
+		stubsSet.AddProperty("symbol_file", libInfo.StubsSymbolFilePath.Base())
+		builder.CopyToSnapshot(libInfo.StubsSymbolFilePath, libInfo.StubsSymbolFilePath.Base())
 	}
 }
 
@@ -480,6 +484,9 @@
 	// is written to does not vary by arch so cannot be android specific.
 	StubsVersions []string `sdk:"ignored-on-host"`
 
+	// The symbol file containing the APIs exported by this library.
+	StubsSymbolFilePath android.Path `sdk:"ignored-on-host"`
+
 	// Value of SanitizeProperties.Sanitize. Several - but not all - of these
 	// affect the expanded variants. All are propagated to avoid entangling the
 	// sanitizer logic with the snapshot generation.
@@ -536,7 +543,7 @@
 	p.ExportedFlags = exportedInfo.Flags
 	if ccModule.linker != nil {
 		specifiedDeps := specifiedDeps{}
-		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(specifiedDeps)
+		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx.SdkModuleContext(), ccModule, specifiedDeps)
 
 		if lib := ccModule.library; lib != nil {
 			if !lib.hasStubsVariants() {
@@ -548,6 +555,11 @@
 				// the versioned stub libs are retained in the prebuilt tree; currently only
 				// the stub corresponding to ccModule.StubsVersion() is.
 				p.StubsVersions = lib.allStubsVersions()
+				if lib.buildStubs() && ccModule.stubsSymbolFilePath() == nil {
+					ctx.ModuleErrorf("Could not determine symbol_file")
+				} else {
+					p.StubsSymbolFilePath = ccModule.stubsSymbolFilePath()
+				}
 			}
 		}
 		p.SystemSharedLibs = specifiedDeps.systemSharedLibs
diff --git a/cc/library_stub.go b/cc/library_stub.go
deleted file mode 100644
index 9643ec2..0000000
--- a/cc/library_stub.go
+++ /dev/null
@@ -1,515 +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 cc
-
-import (
-	"regexp"
-	"strings"
-
-	"android/soong/android"
-	"android/soong/multitree"
-
-	"github.com/google/blueprint/proptools"
-)
-
-var (
-	ndkVariantRegex  = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)")
-	stubVariantRegex = regexp.MustCompile("apex\\.([a-zA-Z0-9]+)")
-)
-
-func init() {
-	RegisterLibraryStubBuildComponents(android.InitRegistrationContext)
-}
-
-func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("cc_api_library", CcApiLibraryFactory)
-	ctx.RegisterModuleType("cc_api_headers", CcApiHeadersFactory)
-	ctx.RegisterModuleType("cc_api_variant", CcApiVariantFactory)
-}
-
-func updateImportedLibraryDependency(ctx android.BottomUpMutatorContext) {
-	m, ok := ctx.Module().(*Module)
-	if !ok {
-		return
-	}
-
-	apiLibrary, ok := m.linker.(*apiLibraryDecorator)
-	if !ok {
-		return
-	}
-
-	if m.InVendorOrProduct() && apiLibrary.hasLLNDKStubs() {
-		// Add LLNDK variant dependency
-		if inList("llndk", apiLibrary.properties.Variants) {
-			variantName := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
-			ctx.AddDependency(m, nil, variantName)
-		}
-	} else if m.IsSdkVariant() {
-		// Add NDK variant dependencies
-		targetVariant := "ndk." + m.StubsVersion()
-		if inList(targetVariant, apiLibrary.properties.Variants) {
-			variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
-			ctx.AddDependency(m, nil, variantName)
-		}
-	} else if m.IsStubs() {
-		targetVariant := "apex." + m.StubsVersion()
-		if inList(targetVariant, apiLibrary.properties.Variants) {
-			variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
-			ctx.AddDependency(m, nil, variantName)
-		}
-	}
-}
-
-// 'cc_api_library' is a module type which is from the exported API surface
-// with C shared library type. The module will replace original module, and
-// offer a link to the module that generates shared library object from the
-// map file.
-type apiLibraryProperties struct {
-	Src      *string `android:"arch_variant"`
-	Variants []string
-}
-
-type apiLibraryDecorator struct {
-	*libraryDecorator
-	properties apiLibraryProperties
-}
-
-func CcApiLibraryFactory() android.Module {
-	module, decorator := NewLibrary(android.DeviceSupported)
-	apiLibraryDecorator := &apiLibraryDecorator{
-		libraryDecorator: decorator,
-	}
-	apiLibraryDecorator.BuildOnlyShared()
-
-	module.stl = nil
-	module.sanitize = nil
-	decorator.disableStripping()
-
-	module.compiler = nil
-	module.linker = apiLibraryDecorator
-	module.installer = nil
-	module.library = apiLibraryDecorator
-	module.AddProperties(&module.Properties, &apiLibraryDecorator.properties)
-
-	// Prevent default system libs (libc, libm, and libdl) from being linked
-	if apiLibraryDecorator.baseLinker.Properties.System_shared_libs == nil {
-		apiLibraryDecorator.baseLinker.Properties.System_shared_libs = []string{}
-	}
-
-	apiLibraryDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
-	apiLibraryDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
-
-	module.Init()
-
-	return module
-}
-
-func (d *apiLibraryDecorator) Name(basename string) string {
-	return basename + multitree.GetApiImportSuffix()
-}
-
-// Export include dirs without checking for existence.
-// The directories are not guaranteed to exist during Soong analysis.
-func (d *apiLibraryDecorator) exportIncludes(ctx ModuleContext) {
-	exporterProps := d.flagExporter.Properties
-	for _, dir := range exporterProps.Export_include_dirs.GetOrDefault(ctx, nil) {
-		d.dirs = append(d.dirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
-	}
-	// system headers
-	for _, dir := range exporterProps.Export_system_include_dirs {
-		d.systemDirs = append(d.systemDirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
-	}
-}
-
-func (d *apiLibraryDecorator) linkerInit(ctx BaseModuleContext) {
-	d.baseLinker.linkerInit(ctx)
-
-	if d.hasNDKStubs() {
-		// Set SDK version of module as current
-		ctx.Module().(*Module).Properties.Sdk_version = StringPtr("current")
-
-		// Add NDK stub as NDK known libs
-		name := ctx.ModuleName()
-
-		ndkKnownLibsLock.Lock()
-		ndkKnownLibs := getNDKKnownLibs(ctx.Config())
-		if !inList(name, *ndkKnownLibs) {
-			*ndkKnownLibs = append(*ndkKnownLibs, name)
-		}
-		ndkKnownLibsLock.Unlock()
-	}
-}
-
-func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
-	m, _ := ctx.Module().(*Module)
-
-	var in android.Path
-
-	// src might not exist during the beginning of soong analysis in Multi-tree
-	if src := String(d.properties.Src); src != "" {
-		in = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), src)
-	}
-
-	libName := m.BaseModuleName() + multitree.GetApiImportSuffix()
-
-	load_cc_variant := func(apiVariantModule string) {
-		var mod android.Module
-
-		ctx.VisitDirectDeps(func(depMod android.Module) {
-			if depMod.Name() == apiVariantModule {
-				mod = depMod
-				libName = apiVariantModule
-			}
-		})
-
-		if mod != nil {
-			variantMod, ok := mod.(*CcApiVariant)
-			if ok {
-				in = variantMod.Src()
-
-				// Copy LLDNK properties to cc_api_library module
-				exportIncludeDirs := append(d.libraryDecorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil),
-					variantMod.exportProperties.Export_include_dirs...)
-				d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](
-					nil,
-					[]proptools.ConfigurableCase[[]string]{
-						proptools.NewConfigurableCase[[]string](nil, &exportIncludeDirs),
-					},
-				)
-
-				// Export headers as system include dirs if specified. Mostly for libc
-				if Bool(variantMod.exportProperties.Export_headers_as_system) {
-					d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
-						d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
-						d.libraryDecorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil)...)
-					d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](nil, nil)
-				}
-			}
-		}
-	}
-
-	if m.InVendorOrProduct() && d.hasLLNDKStubs() {
-		// LLNDK variant
-		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "llndk", ""))
-	} else if m.IsSdkVariant() {
-		// NDK Variant
-		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion()))
-	} else if m.IsStubs() {
-		// APEX Variant
-		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "apex", m.StubsVersion()))
-	}
-
-	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
-	d.exportIncludes(ctx)
-	d.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
-	d.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
-	d.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
-	d.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
-	d.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
-
-	if in == nil {
-		ctx.PropertyErrorf("src", "Unable to locate source property")
-		return nil
-	}
-
-	// Make the _compilation_ of rdeps have an order-only dep on cc_api_library.src (an .so file)
-	// The .so file itself has an order-only dependency on the headers contributed by this library.
-	// Creating this dependency ensures that the headers are assembled before compilation of rdeps begins.
-	d.libraryDecorator.reexportDeps(in)
-	d.libraryDecorator.flagExporter.setProvider(ctx)
-
-	d.unstrippedOutputFile = in
-	libName += flags.Toolchain.ShlibSuffix()
-
-	tocFile := android.PathForModuleOut(ctx, libName+".toc")
-	d.tocFile = android.OptionalPathForPath(tocFile)
-	TransformSharedObjectToToc(ctx, in, tocFile)
-
-	outputFile := android.PathForModuleOut(ctx, libName)
-
-	// TODO(b/270485584) This copies with a new name, just to avoid conflict with prebuilts.
-	// We can just use original input if there is any way to avoid name conflict without copy.
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.Cp,
-		Description: "API surface imported library",
-		Input:       in,
-		Output:      outputFile,
-		Args: map[string]string{
-			"cpFlags": "-L",
-		},
-	})
-
-	android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
-		SharedLibrary: outputFile,
-		Target:        ctx.Target(),
-
-		TableOfContents: d.tocFile,
-	})
-
-	d.shareStubs(ctx)
-
-	return outputFile
-}
-
-// Share additional information about stub libraries with provider
-func (d *apiLibraryDecorator) shareStubs(ctx ModuleContext) {
-	stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
-	if len(stubs) > 0 {
-		var stubsInfo []SharedStubLibrary
-		for _, stub := range stubs {
-			stubInfo, _ := android.OtherModuleProvider(ctx, stub, SharedLibraryInfoProvider)
-			flagInfo, _ := android.OtherModuleProvider(ctx, stub, FlagExporterInfoProvider)
-			stubsInfo = append(stubsInfo, SharedStubLibrary{
-				Version:           moduleLibraryInterface(stub).stubsVersion(),
-				SharedLibraryInfo: stubInfo,
-				FlagExporterInfo:  flagInfo,
-			})
-		}
-		android.SetProvider(ctx, SharedLibraryStubsProvider, SharedLibraryStubsInfo{
-			SharedStubLibraries: stubsInfo,
-
-			IsLLNDK: ctx.IsLlndk(),
-		})
-	}
-}
-
-func (d *apiLibraryDecorator) availableFor(what string) bool {
-	// Stub from API surface should be available for any APEX.
-	return true
-}
-
-func (d *apiLibraryDecorator) hasApexStubs() bool {
-	for _, variant := range d.properties.Variants {
-		if strings.HasPrefix(variant, "apex") {
-			return true
-		}
-	}
-	return false
-}
-
-func (d *apiLibraryDecorator) hasStubsVariants() bool {
-	return d.hasApexStubs()
-}
-
-func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
-	m, ok := ctx.Module().(*Module)
-
-	if !ok {
-		return nil
-	}
-
-	// TODO(b/244244438) Create more version information for NDK and APEX variations
-	// NDK variants
-	if m.IsSdkVariant() {
-		// TODO(b/249193999) Do not check if module has NDK stubs once all NDK cc_api_library contains ndk variant of cc_api_variant.
-		if d.hasNDKStubs() {
-			return d.getNdkVersions()
-		}
-	}
-
-	if d.hasLLNDKStubs() && m.InVendorOrProduct() {
-		// LLNDK libraries only need a single stubs variant.
-		return []string{android.FutureApiLevel.String()}
-	}
-
-	stubsVersions := d.getStubVersions()
-
-	if len(stubsVersions) != 0 {
-		return stubsVersions
-	}
-
-	if m.MinSdkVersion() == "" {
-		return nil
-	}
-
-	firstVersion, err := nativeApiLevelFromUser(ctx,
-		m.MinSdkVersion())
-
-	if err != nil {
-		return nil
-	}
-
-	return ndkLibraryVersions(ctx, firstVersion)
-}
-
-func (d *apiLibraryDecorator) hasLLNDKStubs() bool {
-	return inList("llndk", d.properties.Variants)
-}
-
-func (d *apiLibraryDecorator) hasNDKStubs() bool {
-	for _, variant := range d.properties.Variants {
-		if ndkVariantRegex.MatchString(variant) {
-			return true
-		}
-	}
-	return false
-}
-
-func (d *apiLibraryDecorator) getNdkVersions() []string {
-	ndkVersions := []string{}
-
-	for _, variant := range d.properties.Variants {
-		if match := ndkVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
-			ndkVersions = append(ndkVersions, match[1])
-		}
-	}
-
-	return ndkVersions
-}
-
-func (d *apiLibraryDecorator) getStubVersions() []string {
-	stubVersions := []string{}
-
-	for _, variant := range d.properties.Variants {
-		if match := stubVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
-			stubVersions = append(stubVersions, match[1])
-		}
-	}
-
-	return stubVersions
-}
-
-// 'cc_api_headers' is similar with 'cc_api_library', but which replaces
-// header libraries. The module will replace any dependencies to existing
-// original header libraries.
-type apiHeadersDecorator struct {
-	*libraryDecorator
-}
-
-func CcApiHeadersFactory() android.Module {
-	module, decorator := NewLibrary(android.DeviceSupported)
-	apiHeadersDecorator := &apiHeadersDecorator{
-		libraryDecorator: decorator,
-	}
-	apiHeadersDecorator.HeaderOnly()
-
-	module.stl = nil
-	module.sanitize = nil
-	decorator.disableStripping()
-
-	module.compiler = nil
-	module.linker = apiHeadersDecorator
-	module.installer = nil
-
-	// Prevent default system libs (libc, libm, and libdl) from being linked
-	if apiHeadersDecorator.baseLinker.Properties.System_shared_libs == nil {
-		apiHeadersDecorator.baseLinker.Properties.System_shared_libs = []string{}
-	}
-
-	apiHeadersDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
-	apiHeadersDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
-
-	module.Init()
-
-	return module
-}
-
-func (d *apiHeadersDecorator) Name(basename string) string {
-	return basename + multitree.GetApiImportSuffix()
-}
-
-func (d *apiHeadersDecorator) availableFor(what string) bool {
-	// Stub from API surface should be available for any APEX.
-	return true
-}
-
-type ccApiexportProperties struct {
-	Src     *string `android:"arch_variant"`
-	Variant *string
-	Version *string
-}
-
-type variantExporterProperties struct {
-	// Header directory to export
-	Export_include_dirs []string `android:"arch_variant"`
-
-	// Export all headers as system include
-	Export_headers_as_system *bool
-}
-
-type CcApiVariant struct {
-	android.ModuleBase
-
-	properties       ccApiexportProperties
-	exportProperties variantExporterProperties
-
-	src android.Path
-}
-
-var _ android.Module = (*CcApiVariant)(nil)
-var _ android.ImageInterface = (*CcApiVariant)(nil)
-
-func CcApiVariantFactory() android.Module {
-	module := &CcApiVariant{}
-
-	module.AddProperties(&module.properties)
-	module.AddProperties(&module.exportProperties)
-
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
-	return module
-}
-
-func (v *CcApiVariant) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// No need to build
-
-	if String(v.properties.Src) == "" {
-		ctx.PropertyErrorf("src", "src is a required property")
-	}
-
-	// Skip the existence check of the stub prebuilt file.
-	// The file is not guaranteed to exist during Soong analysis.
-	// Build orchestrator will be responsible for creating a connected ninja graph.
-	v.src = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), String(v.properties.Src))
-}
-
-func (v *CcApiVariant) Name() string {
-	version := String(v.properties.Version)
-	return BuildApiVariantName(v.BaseModuleName(), *v.properties.Variant, version)
-}
-
-func (v *CcApiVariant) Src() android.Path {
-	return v.src
-}
-
-func BuildApiVariantName(baseName string, variant string, version string) string {
-	names := []string{baseName, variant}
-	if version != "" {
-		names = append(names, version)
-	}
-
-	return strings.Join(names[:], ".") + multitree.GetApiImportSuffix()
-}
-
-// Implement ImageInterface to generate image variants
-func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext) {}
-func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
-	return inList(String(v.properties.Variant), []string{"ndk", "apex"})
-}
-func (v *CcApiVariant) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool       { return false }
-func (v *CcApiVariant) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
-func (v *CcApiVariant) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool  { return false }
-func (v *CcApiVariant) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool      { return false }
-func (v *CcApiVariant) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	var variations []string
-
-	if String(v.properties.Variant) == "llndk" {
-		variations = append(variations, VendorVariation)
-		variations = append(variations, ProductVariation)
-	}
-
-	return variations
-}
-func (v *CcApiVariant) SetImageVariation(ctx android.BaseModuleContext, variation string) {
-}
diff --git a/cc/linkable.go b/cc/linkable.go
index 2309fe8..1672366 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -94,12 +94,16 @@
 	SelectedStl() string
 
 	BuildStaticVariant() bool
+	BuildRlibVariant() bool
 	BuildSharedVariant() bool
 	SetStatic()
 	SetShared()
 	IsPrebuilt() bool
 	Toc() android.OptionalPath
 
+	// IsRustFFI returns true if this is a Rust FFI library.
+	IsRustFFI() bool
+
 	// IsFuzzModule returns true if this a *_fuzz module.
 	IsFuzzModule() bool
 
diff --git a/cc/linker.go b/cc/linker.go
index 1675df6..1efacad 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -37,19 +37,16 @@
 	// in their entirety.  For static library modules, all of the .o files from the intermediate
 	// directory of the dependency will be linked into this modules .a file.  For a shared library,
 	// the dependency's .a file will be linked into this module using -Wl,--whole-archive.
-	Whole_static_libs []string `android:"arch_variant,variant_prepend"`
-
-	// list of Rust libs that should be statically linked into this module.
-	Static_rlibs []string `android:"arch_variant"`
+	Whole_static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of modules that should be statically linked into this module.
-	Static_libs []string `android:"arch_variant,variant_prepend"`
+	Static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of modules that should be dynamically linked into this module.
-	Shared_libs []string `android:"arch_variant"`
+	Shared_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of modules that should only provide headers for this module.
-	Header_libs []string `android:"arch_variant,variant_prepend"`
+	Header_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of module-specific flags that will be used for all link steps
 	Ldflags []string `android:"arch_variant"`
@@ -127,10 +124,6 @@
 			// variant of the C/C++ module.
 			Header_libs []string
 
-			// list of Rust libs that should be statically linked to build vendor or product
-			// variant.
-			Static_rlibs []string
-
 			// list of shared libs that should not be used to build vendor or
 			// product variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -159,10 +152,6 @@
 			// variant of the C/C++ module.
 			Static_libs []string
 
-			// list of Rust libs that should be statically linked to build the recovery
-			// variant.
-			Static_rlibs []string
-
 			// list of shared libs that should not be used to build
 			// the recovery variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -184,10 +173,6 @@
 			// variant of the C/C++ module.
 			Static_libs []string
 
-			// list of Rust libs that should be statically linked to build the ramdisk
-			// variant.
-			Static_rlibs []string
-
 			// list of shared libs that should not be used to build
 			// the ramdisk variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -205,10 +190,6 @@
 			// the vendor ramdisk variant of the C/C++ module.
 			Exclude_shared_libs []string
 
-			// list of Rust libs that should be statically linked to build the vendor ramdisk
-			// variant.
-			Static_rlibs []string
-
 			// list of static libs that should not be used to build
 			// the vendor ramdisk variant of the C/C++ module.
 			Exclude_static_libs []string
@@ -224,10 +205,6 @@
 			// variants.
 			Shared_libs []string
 
-			// list of Rust libs that should be statically linked to build the vendor ramdisk
-			// variant.
-			Static_rlibs []string
-
 			// list of ehader libs that only should be used to build platform variant of
 			// the C/C++ module.
 			Header_libs []string
@@ -319,11 +296,10 @@
 }
 
 func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...)
-	deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs...)
-	deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Static_libs...)
-	deps.Rlibs = append(deps.Rlibs, linker.Properties.Static_rlibs...)
-	deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Shared_libs...)
+	deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs.GetOrDefault(ctx, nil)...)
+	deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs.GetOrDefault(ctx, nil)...)
+	deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Static_libs.GetOrDefault(ctx, nil)...)
+	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...)
@@ -366,7 +342,6 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor.Exclude_runtime_libs)
-		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Vendor.Static_rlibs...)
 	}
 
 	if ctx.inProduct() {
@@ -380,7 +355,6 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Product.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Product.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Product.Exclude_runtime_libs)
-		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Product.Static_rlibs...)
 	}
 
 	if ctx.inRecovery() {
@@ -394,7 +368,6 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Recovery.Exclude_runtime_libs)
-		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Recovery.Static_rlibs...)
 	}
 
 	if ctx.inRamdisk() {
@@ -405,7 +378,6 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Ramdisk.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Ramdisk.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Ramdisk.Exclude_runtime_libs)
-		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Ramdisk.Static_rlibs...)
 	}
 
 	if ctx.inVendorRamdisk() {
@@ -415,7 +387,6 @@
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_runtime_libs)
-		deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Vendor_ramdisk.Static_rlibs...)
 	}
 
 	if !ctx.useSdk() {
@@ -674,8 +645,9 @@
 	panic(fmt.Errorf("baseLinker doesn't know how to link"))
 }
 
-func (linker *baseLinker) linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps {
-	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, linker.Properties.Shared_libs...)
+func (linker *baseLinker) linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
+	eval := module.ConfigurableEvaluator(ctx)
+	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, linker.Properties.Shared_libs.GetOrDefault(eval, nil)...)
 
 	// Must distinguish nil and [] in system_shared_libs - ensure that [] in
 	// either input list doesn't come out as nil.
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 632c76d..c7950f9 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -30,16 +30,12 @@
 type llndkLibraryProperties struct {
 	// Relative path to the symbol map.
 	// An example file can be seen here: TODO(danalbert): Make an example.
-	Symbol_file *string
+	Symbol_file *string `android:"path,arch_variant"`
 
 	// Whether to export any headers as -isystem instead of -I. Mainly for use by
 	// bionic/libc.
 	Export_headers_as_system *bool
 
-	// Which headers to process with versioner. This really only handles
-	// bionic/libc/include right now.
-	Export_preprocessed_headers []string
-
 	// Whether the system library uses symbol versions.
 	Unversioned *bool
 
@@ -184,10 +180,6 @@
 	return ""
 }
 
-func (txt *llndkLibrariesTxtModule) OutputFiles(tag string) (android.Paths, error) {
-	return android.Paths{txt.outputFile}, nil
-}
-
 func llndkMutator(mctx android.BottomUpMutatorContext) {
 	m, ok := mctx.Module().(*Module)
 	if !ok {
diff --git a/cc/lto.go b/cc/lto.go
index 4444152..f3af7d2 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.Config().Eng() {
+		if Bool(lto.Properties.Lto_O0) || ctx.optimizeForSize() || ctx.Config().Eng() {
 			ltoLdFlags = append(ltoLdFlags, "-Wl,--lto-O0")
 		}
 
diff --git a/cc/makevars.go b/cc/makevars.go
index 9d29aff..c9352a4 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -138,10 +138,8 @@
 	ctx.Strict("CLANG_COVERAGE_HWASAN_FLAGS", strings.Join(clangCoverageHWASanFlags, " "))
 
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(asanCflags, " "))
-	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_LDFLAGS", strings.Join(asanLdflags, " "))
 
 	ctx.Strict("HWADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(hwasanCflags, " "))
-	ctx.Strict("HWADDRESS_SANITIZER_GLOBAL_OPTIONS", strings.Join(hwasanGlobalOptions, ","))
 
 	ctx.Strict("CFI_EXTRA_CFLAGS", strings.Join(cfiCflags, " "))
 	ctx.Strict("CFI_EXTRA_ASFLAGS", strings.Join(cfiAsflags, " "))
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index 5beeab1..2706261 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -46,7 +46,7 @@
 
 		if m, ok := module.(*Module); ok {
 			if installer, ok := m.installer.(*stubDecorator); ok {
-				if canDumpAbi(ctx.Config()) {
+				if installer.hasAbiDump {
 					depPaths = append(depPaths, installer.abiDumpPath)
 				}
 			}
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 57a3b3a..7481954 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -18,19 +18,11 @@
 	"path/filepath"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
 var (
-	versionBionicHeaders = pctx.AndroidStaticRule("versionBionicHeaders",
-		blueprint.RuleParams{
-			// The `&& touch $out` isn't really necessary, but Blueprint won't
-			// let us have only implicit outputs.
-			Command:     "$versionerCmd -o $outDir $srcDir $depsPath && touch $out",
-			CommandDeps: []string{"$versionerCmd"},
-		},
-		"depsPath", "srcDir", "outDir")
-
 	preprocessNdkHeader = pctx.AndroidStaticRule("preprocessNdkHeader",
 		blueprint.RuleParams{
 			Command:     "$preprocessor -o $out $in",
@@ -39,12 +31,8 @@
 		"preprocessor")
 )
 
-func init() {
-	pctx.SourcePathVariable("versionerCmd", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/versioner")
-}
-
 // Returns the NDK base include path for use with sdk_version current. Usable with -I.
-func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath {
+func getCurrentIncludePath(ctx android.PathContext) android.OutputPath {
 	return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
 }
 
@@ -73,6 +61,13 @@
 
 	// Path to the NOTICE file associated with the headers.
 	License *string `android:"path"`
+
+	// Set to true if the headers installed by this module should skip
+	// verification. This step ensures that each header is self-contained (can
+	// be #included alone) and is valid C. This should not be disabled except in
+	// rare cases. Outside bionic and external, if you're using this option
+	// you've probably made a mistake.
+	Skip_verification *bool
 }
 
 type headerModule struct {
@@ -159,126 +154,6 @@
 	return module
 }
 
-type versionedHeaderProperties struct {
-	// Base directory of the headers being installed. As an example:
-	//
-	// versioned_ndk_headers {
-	//     name: "foo",
-	//     from: "include",
-	//     to: "",
-	// }
-	//
-	// Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead
-	// "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h.
-	From *string
-
-	// Install path within the sysroot. This is relative to usr/include.
-	To *string
-
-	// Path to the NOTICE file associated with the headers.
-	License *string
-}
-
-// Like ndk_headers, but preprocesses the headers with the bionic versioner:
-// https://android.googlesource.com/platform/bionic/+/main/tools/versioner/README.md.
-//
-// Unlike ndk_headers, we don't operate on a list of sources but rather a whole directory, the
-// module does not have the srcs property, and operates on a full directory (the `from` property).
-//
-// Note that this is really only built to handle bionic/libc/include.
-type versionedHeaderModule struct {
-	android.ModuleBase
-
-	properties versionedHeaderProperties
-
-	srcPaths     android.Paths
-	installPaths android.Paths
-	licensePath  android.Path
-}
-
-// Return the glob pattern to find all .h files beneath `dir`
-func headerGlobPattern(dir string) string {
-	return filepath.Join(dir, "**", "*.h")
-}
-
-func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if String(m.properties.License) == "" {
-		ctx.PropertyErrorf("license", "field is required")
-	}
-
-	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
-
-	fromSrcPath := android.PathForModuleSrc(ctx, String(m.properties.From))
-	toOutputPath := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
-	m.srcPaths = ctx.GlobFiles(headerGlobPattern(fromSrcPath.String()), nil)
-	var installPaths []android.WritablePath
-	for _, header := range m.srcPaths {
-		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), String(m.properties.To))
-		installPath := installDir.Join(ctx, header.Base())
-		installPaths = append(installPaths, installPath)
-		m.installPaths = append(m.installPaths, installPath)
-	}
-
-	if len(m.installPaths) == 0 {
-		ctx.ModuleErrorf("glob %q matched zero files", String(m.properties.From))
-	}
-
-	processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, m.srcPaths, installPaths)
-}
-
-func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path,
-	srcPaths android.Paths, installPaths []android.WritablePath) android.Path {
-	// The versioner depends on a dependencies directory to simplify determining include paths
-	// when parsing headers. This directory contains architecture specific directories as well
-	// as a common directory, each of which contains symlinks to the actually directories to
-	// be included.
-	//
-	// ctx.Glob doesn't follow symlinks, so we need to do this ourselves so we correctly
-	// depend on these headers.
-	// TODO(http://b/35673191): Update the versioner to use a --sysroot.
-	depsPath := android.PathForSource(ctx, "bionic/libc/versioner-dependencies")
-	depsGlob := ctx.Glob(filepath.Join(depsPath.String(), "**/*"), nil)
-	for i, path := range depsGlob {
-		if ctx.IsSymlink(path) {
-			dest := ctx.Readlink(path)
-			// Additional .. to account for the symlink itself.
-			depsGlob[i] = android.PathForSource(
-				ctx, filepath.Clean(filepath.Join(path.String(), "..", dest)))
-		}
-	}
-
-	timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:            versionBionicHeaders,
-		Description:     "versioner preprocess " + srcDir.Rel(),
-		Output:          timestampFile,
-		Implicits:       append(srcPaths, depsGlob...),
-		ImplicitOutputs: installPaths,
-		Args: map[string]string{
-			"depsPath": depsPath.String(),
-			"srcDir":   srcDir.String(),
-			"outDir":   outDir.String(),
-		},
-	})
-
-	return timestampFile
-}
-
-// versioned_ndk_headers preprocesses the headers with the bionic versioner:
-// https://android.googlesource.com/platform/bionic/+/main/tools/versioner/README.md.
-// Unlike the ndk_headers soong module, versioned_ndk_headers operates on a
-// directory level specified in `from` property. This is only used to process
-// the bionic/libc/include directory.
-func VersionedNdkHeadersFactory() android.Module {
-	module := &versionedHeaderModule{}
-
-	module.AddProperties(&module.properties)
-
-	android.InitAndroidModule(module)
-
-	return module
-}
-
 // preprocessed_ndk_header {
 //
 //	name: "foo",
@@ -309,6 +184,13 @@
 
 	// Path to the NOTICE file associated with the headers.
 	License *string
+
+	// Set to true if the headers installed by this module should skip
+	// verification. This step ensures that each header is self-contained (can
+	// be #included alone) and is valid C. This should not be disabled except in
+	// rare cases. Outside bionic and external, if you're using this option
+	// you've probably made a mistake.
+	Skip_verification *bool
 }
 
 type preprocessedHeadersModule struct {
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index f326068..01551ab 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -103,8 +103,17 @@
 	// https://github.com/android-ndk/ndk/issues/265.
 	Unversioned_until *string
 
-	// Headers presented by this library to the Public API Surface
+	// DO NOT USE THIS
+	// NDK libraries should not export their headers. Headers belonging to NDK
+	// libraries should be added to the NDK with an ndk_headers module.
 	Export_header_libs []string
+
+	// Do not add other export_* properties without consulting with danalbert@.
+	// Consumers of ndk_library modules should emulate the typical NDK build
+	// behavior as closely as possible (that is, all NDK APIs are exposed to
+	// builds via --sysroot). Export behaviors used in Soong will not be present
+	// for app developers as they don't use Soong, and reliance on these export
+	// behaviors can mask issues with the NDK sysroot.
 }
 
 type stubDecorator struct {
@@ -116,6 +125,7 @@
 	parsedCoverageXmlPath android.ModuleOutPath
 	installPath           android.Path
 	abiDumpPath           android.OutputPath
+	hasAbiDump            bool
 	abiDiffPaths          android.Paths
 
 	apiLevel         android.ApiLevel
@@ -133,7 +143,7 @@
 	return strings.TrimSuffix(name, ndkLibrarySuffix)
 }
 
-func ndkLibraryVersions(ctx android.BaseMutatorContext, from android.ApiLevel) []string {
+func ndkLibraryVersions(ctx android.BaseModuleContext, from android.ApiLevel) []string {
 	var versions []android.ApiLevel
 	versionStrs := []string{}
 	for _, version := range ctx.Config().AllSupportedApiLevels() {
@@ -147,7 +157,7 @@
 	return versionStrs
 }
 
-func (this *stubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
+func (this *stubDecorator) stubsVersions(ctx android.BaseModuleContext) []string {
 	if !ctx.Module().Enabled(ctx) {
 		return nil
 	}
@@ -321,12 +331,27 @@
 }
 
 // Feature flag.
-func canDumpAbi(config android.Config) bool {
+func (this *stubDecorator) canDumpAbi(ctx ModuleContext) bool {
 	if runtime.GOOS == "darwin" {
 		return false
 	}
+	if strings.HasPrefix(ctx.ModuleDir(), "bionic/") {
+		// Bionic has enough uncommon implementation details like ifuncs and asm
+		// code that the ABI tracking here has a ton of false positives. That's
+		// causing pretty extreme friction for development there, so disabling
+		// it until the workflow can be improved.
+		//
+		// http://b/358653811
+		return false
+	}
+
+	if this.apiLevel.IsCurrent() {
+		// "current" (AKA 10000) is not tracked.
+		return false
+	}
+
 	// http://b/156513478
-	return config.ReleaseNdkAbiMonitored()
+	return ctx.Config().ReleaseNdkAbiMonitored()
 }
 
 // Feature flag to disable diffing against prebuilts.
@@ -339,6 +364,7 @@
 	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
 		this.apiLevel.String(), ctx.Arch().ArchType.String(),
 		this.libraryName(ctx), "abi.stg")
+	this.hasAbiDump = true
 	headersList := getNdkABIHeadersFile(ctx)
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        stg,
@@ -403,41 +429,45 @@
 	// Also ensure that the ABI of the next API level (if there is one) matches
 	// this API level. *New* ABI is allowed, but any changes to APIs that exist
 	// in this API level are disallowed.
-	if !this.apiLevel.IsCurrent() && prebuiltAbiDump.Valid() {
+	if prebuiltAbiDump.Valid() {
 		nextApiLevel := findNextApiLevel(ctx, this.apiLevel)
 		if nextApiLevel == nil {
 			panic(fmt.Errorf("could not determine which API level follows "+
 				"non-current API level %s", this.apiLevel))
 		}
-		nextAbiDiffPath := android.PathForModuleOut(ctx,
-			"abidiff_next.timestamp")
-		nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel)
-		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",
-				},
-			})
+
+		// "current" ABI is not tracked.
+		if !nextApiLevel.IsCurrent() {
+			nextAbiDiffPath := android.PathForModuleOut(ctx,
+				"abidiff_next.timestamp")
+			nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel)
+			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)
 		}
-		this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath)
 	}
 }
 
@@ -460,7 +490,7 @@
 	nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
 	objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
 	c.versionScriptPath = nativeAbiResult.versionScript
-	if canDumpAbi(ctx.Config()) {
+	if c.canDumpAbi(ctx) {
 		c.dumpAbi(ctx, nativeAbiResult.symbolList)
 		if canDiffAbi(ctx.Config()) {
 			c.diffAbi(ctx)
@@ -475,7 +505,8 @@
 // Add a dependency on the header modules of this ndk_library
 func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	return Deps{
-		HeaderLibs: linker.properties.Export_header_libs,
+		ReexportHeaderLibHeaders: linker.properties.Export_header_libs,
+		HeaderLibs:               linker.properties.Export_header_libs,
 	}
 }
 
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
deleted file mode 100644
index f503982..0000000
--- a/cc/ndk_prebuilt.go
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2016 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 (
-	"strings"
-
-	"android/soong/android"
-)
-
-func init() {
-	android.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
-	android.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
-}
-
-// NDK prebuilt libraries.
-//
-// These differ from regular prebuilts in that they aren't stripped and usually aren't installed
-// either (with the exception of the shared STLs, which are installed to the app's directory rather
-// than to the system image).
-
-type ndkPrebuiltStlLinker struct {
-	*libraryDecorator
-}
-
-func (ndk *ndkPrebuiltStlLinker) linkerProps() []interface{} {
-	return append(ndk.libraryDecorator.linkerProps(), &ndk.Properties, &ndk.flagExporter.Properties)
-}
-
-func (*ndkPrebuiltStlLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	// NDK libraries can't have any dependencies
-	return deps
-}
-
-func (*ndkPrebuiltStlLinker) availableFor(what string) bool {
-	// ndk prebuilt objects are available to everywhere
-	return true
-}
-
-// ndk_prebuilt_shared_stl exports a precompiled ndk shared standard template
-// library (stl) library for linking operation. The soong's module name format
-// is ndk_<NAME>.so where the library is located under
-// ./prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/$(HOST_ARCH)/<NAME>.so.
-func NdkPrebuiltSharedStlFactory() android.Module {
-	module, library := NewLibrary(android.DeviceSupported)
-	library.BuildOnlyShared()
-	module.compiler = nil
-	module.linker = &ndkPrebuiltStlLinker{
-		libraryDecorator: library,
-	}
-	module.installer = nil
-	module.Properties.Sdk_version = StringPtr("minimum")
-	module.Properties.AlwaysSdk = true
-	module.stl.Properties.Stl = StringPtr("none")
-	return module.Init()
-}
-
-// ndk_prebuilt_static_stl exports a precompiled ndk static standard template
-// library (stl) library for linking operation. The soong's module name format
-// is ndk_<NAME>.a where the library is located under
-// ./prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/$(HOST_ARCH)/<NAME>.a.
-func NdkPrebuiltStaticStlFactory() android.Module {
-	module, library := NewLibrary(android.DeviceSupported)
-	library.BuildOnlyStatic()
-	module.compiler = nil
-	module.linker = &ndkPrebuiltStlLinker{
-		libraryDecorator: library,
-	}
-	module.installer = nil
-	module.Properties.Sdk_version = StringPtr("minimum")
-	module.Properties.HideFromMake = true
-	module.Properties.AlwaysSdk = true
-	module.Properties.Sdk_version = StringPtr("current")
-	module.stl.Properties.Stl = StringPtr("none")
-	return module.Init()
-}
-
-const (
-	libDir = "current/sources/cxx-stl/llvm-libc++/libs"
-)
-
-func getNdkStlLibDir(ctx android.ModuleContext) android.SourcePath {
-	return android.PathForSource(ctx, ctx.ModuleDir(), libDir).Join(ctx, ctx.Arch().Abi[0])
-}
-
-func (ndk *ndkPrebuiltStlLinker) link(ctx ModuleContext, flags Flags,
-	deps PathDeps, objs Objects) android.Path {
-	// A null build step, but it sets up the output path.
-	if !strings.HasPrefix(ctx.ModuleName(), "ndk_lib") {
-		ctx.ModuleErrorf("NDK prebuilt libraries must have an ndk_lib prefixed name")
-	}
-
-	ndk.libraryDecorator.flagExporter.exportIncludesAsSystem(ctx)
-
-	libName := strings.TrimPrefix(ctx.ModuleName(), "ndk_")
-	libExt := flags.Toolchain.ShlibSuffix()
-	if ndk.static() {
-		libExt = staticLibraryExtension
-	}
-
-	libDir := getNdkStlLibDir(ctx)
-	lib := libDir.Join(ctx, libName+libExt)
-
-	ndk.libraryDecorator.flagExporter.setProvider(ctx)
-
-	if ndk.static() {
-		depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(lib).Build()
-		android.SetProvider(ctx, StaticLibraryInfoProvider, StaticLibraryInfo{
-			StaticLibrary: lib,
-
-			TransitiveStaticLibrariesForOrdering: depSet,
-		})
-	} else {
-		android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
-			SharedLibrary: lib,
-			Target:        ctx.Target(),
-		})
-	}
-
-	return lib
-}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 3c48f68..92da172 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -54,7 +54,22 @@
 
 import (
 	"android/soong/android"
+	"fmt"
+	"path/filepath"
 	"strings"
+
+	"github.com/google/blueprint"
+)
+
+var (
+	verifyCCompat = pctx.AndroidStaticRule("verifyCCompat",
+		blueprint.RuleParams{
+			Command:     "$ccCmd -x c -fsyntax-only $flags $in && touch $out",
+			CommandDeps: []string{"$ccCmd"},
+		},
+		"ccCmd",
+		"flags",
+	)
 )
 
 func init() {
@@ -64,7 +79,6 @@
 func RegisterNdkModuleTypes(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("ndk_headers", NdkHeadersFactory)
 	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
-	ctx.RegisterModuleType("versioned_ndk_headers", VersionedNdkHeadersFactory)
 	ctx.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
 	ctx.RegisterParallelSingletonType("ndk", NdkSingleton)
 }
@@ -103,6 +117,45 @@
 	return android.PathForOutput(ctx, "ndk_abi_headers.txt")
 }
 
+func verifyNdkHeaderIsCCompatible(ctx android.SingletonContext,
+	src android.Path, dest android.Path) android.Path {
+	sysrootInclude := getCurrentIncludePath(ctx)
+	baseOutputDir := android.PathForOutput(ctx, "c-compat-verification")
+	installRelPath, err := filepath.Rel(sysrootInclude.String(), dest.String())
+	if err != nil {
+		ctx.Errorf("filepath.Rel(%q, %q) failed: %s", dest, sysrootInclude, err)
+	}
+	output := baseOutputDir.Join(ctx, installRelPath)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        verifyCCompat,
+		Description: fmt.Sprintf("Verifying C compatibility of %s", src),
+		Output:      output,
+		Input:       dest,
+		// Ensures that all the headers in the sysroot are already installed
+		// before testing any of the headers for C compatibility, and also that
+		// the check will be re-run whenever the sysroot changes. This is
+		// necessary because many of the NDK headers depend on other NDK
+		// headers, but we don't have explicit dependency tracking for that.
+		Implicits: []android.Path{getNdkHeadersTimestampFile(ctx)},
+		Args: map[string]string{
+			"ccCmd": "${config.ClangBin}/clang",
+			"flags": fmt.Sprintf(
+				// Ideally we'd check each ABI, multiple API levels,
+				// fortify/non-fortify, and a handful of other variations. It's
+				// a lot more difficult to do that though, and would eat up more
+				// build time. All the problems we've seen so far that this
+				// check would catch have been in arch-generic and
+				// minSdkVersion-generic code in frameworks though, so this is a
+				// good place to start.
+				"-target aarch64-linux-android%d --sysroot %s",
+				android.FutureApiLevel.FinalOrFutureInt(),
+				getNdkSysrootBase(ctx).String(),
+			),
+		},
+	})
+	return output
+}
+
 func NdkSingleton() android.Singleton {
 	return &ndkSingleton{}
 }
@@ -143,10 +196,17 @@
 
 type ndkSingleton struct{}
 
+type srcDestPair struct {
+	src  android.Path
+	dest android.Path
+}
+
 func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var staticLibInstallPaths android.Paths
 	var headerSrcPaths android.Paths
 	var headerInstallPaths android.Paths
+	var headersToVerify []srcDestPair
+	var headerCCompatVerificationTimestampPaths android.Paths
 	var installPaths android.Paths
 	var licensePaths android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
@@ -157,13 +217,14 @@
 		if m, ok := module.(*headerModule); ok {
 			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
 			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
-			installPaths = append(installPaths, m.installPaths...)
-			licensePaths = append(licensePaths, m.licensePath)
-		}
-
-		if m, ok := module.(*versionedHeaderModule); ok {
-			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
-			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
+			if !Bool(m.properties.Skip_verification) {
+				for i, installPath := range m.installPaths {
+					headersToVerify = append(headersToVerify, srcDestPair{
+						src:  m.srcPaths[i],
+						dest: installPath,
+					})
+				}
+			}
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
@@ -171,6 +232,14 @@
 		if m, ok := module.(*preprocessedHeadersModule); ok {
 			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
 			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
+			if !Bool(m.properties.Skip_verification) {
+				for i, installPath := range m.installPaths {
+					headersToVerify = append(headersToVerify, srcDestPair{
+						src:  m.srcPaths[i],
+						dest: installPath,
+					})
+				}
+			}
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
@@ -223,6 +292,12 @@
 		Implicits: headerInstallPaths,
 	})
 
+	for _, srcDestPair := range headersToVerify {
+		headerCCompatVerificationTimestampPaths = append(
+			headerCCompatVerificationTimestampPaths,
+			verifyNdkHeaderIsCCompatible(ctx, srcDestPair.src, srcDestPair.dest))
+	}
+
 	writeNdkAbiSrcFilter(ctx, headerSrcPaths, getNdkABIHeadersFile(ctx))
 
 	fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
@@ -235,6 +310,6 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:      android.Touch,
 		Output:    getNdkFullTimestampFile(ctx),
-		Implicits: fullDepPaths,
+		Implicits: append(fullDepPaths, headerCCompatVerificationTimestampPaths...),
 	})
 }
diff --git a/cc/object.go b/cc/object.go
index 6c0391f..c89520a 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -19,6 +19,8 @@
 	"strings"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
 )
 
 //
@@ -50,13 +52,13 @@
 
 type ObjectLinkerProperties struct {
 	// list of static library modules that should only provide headers for this module.
-	Static_libs []string `android:"arch_variant,variant_prepend"`
+	Static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of shared library modules should only provide headers for this module.
-	Shared_libs []string `android:"arch_variant,variant_prepend"`
+	Shared_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of modules that should only provide headers for this module.
-	Header_libs []string `android:"arch_variant,variant_prepend"`
+	Header_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of default libraries that will provide headers for this module.  If unset, generally
 	// defaults to libc, libm, and libdl.  Set to [] to prevent using headers from the defaults.
@@ -116,9 +118,9 @@
 func (*objectLinker) linkerInit(ctx BaseModuleContext) {}
 
 func (object *objectLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	deps.HeaderLibs = append(deps.HeaderLibs, object.Properties.Header_libs...)
-	deps.SharedLibs = append(deps.SharedLibs, object.Properties.Shared_libs...)
-	deps.StaticLibs = append(deps.StaticLibs, object.Properties.Static_libs...)
+	deps.HeaderLibs = append(deps.HeaderLibs, object.Properties.Header_libs.GetOrDefault(ctx, nil)...)
+	deps.SharedLibs = append(deps.SharedLibs, object.Properties.Shared_libs.GetOrDefault(ctx, nil)...)
+	deps.StaticLibs = append(deps.StaticLibs, object.Properties.Static_libs.GetOrDefault(ctx, nil)...)
 	deps.ObjFiles = append(deps.ObjFiles, object.Properties.Objs...)
 
 	deps.SystemSharedLibs = object.Properties.System_shared_libs
@@ -201,8 +203,9 @@
 	return outputFile
 }
 
-func (object *objectLinker) linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps {
-	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, object.Properties.Shared_libs...)
+func (object *objectLinker) linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
+	eval := module.ConfigurableEvaluator(ctx)
+	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, object.Properties.Shared_libs.GetOrDefault(eval, nil)...)
 
 	// Must distinguish nil and [] in system_shared_libs - ensure that [] in
 	// either input list doesn't come out as nil.
@@ -220,7 +223,7 @@
 }
 
 func (object *objectLinker) strippedAllOutputFilePath() android.Path {
-	panic("Not implemented.")
+	return nil
 }
 
 func (object *objectLinker) nativeCoverage() bool {
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index e9f790f..299fb51 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -16,6 +16,7 @@
 
 import (
 	"path/filepath"
+	"strings"
 
 	"github.com/google/blueprint/proptools"
 
@@ -48,7 +49,7 @@
 	Source_module_name *string
 
 	// a prebuilt library or binary. Can reference a genrule module that generates an executable file.
-	Srcs []string `android:"path,arch_variant"`
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	Sanitized Sanitized `android:"arch_variant"`
 
@@ -75,10 +76,6 @@
 	return &p.Prebuilt
 }
 
-func (p *prebuiltLinker) PrebuiltSrcs() []string {
-	return p.properties.Srcs
-}
-
 type prebuiltLibraryInterface interface {
 	libraryInterface
 	prebuiltLinkerInterface
@@ -99,10 +96,6 @@
 	return p.libraryDecorator.linkerDeps(ctx, deps)
 }
 
-func (p *prebuiltLibraryLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	return flags
-}
-
 func (p *prebuiltLibraryLinker) linkerProps() []interface{} {
 	return p.libraryDecorator.linkerProps()
 }
@@ -121,6 +114,30 @@
 
 	// TODO(ccross): verify shared library dependencies
 	srcs := p.prebuiltSrcs(ctx)
+	stubInfo := addStubDependencyProviders(ctx)
+
+	// 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)
+	}
+
 	if len(srcs) > 0 {
 		if len(srcs) > 1 {
 			ctx.PropertyErrorf("srcs", "multiple prebuilt source files")
@@ -205,19 +222,18 @@
 				TableOfContents: p.tocFile,
 			})
 
-			// TODO(b/220898484): Mainline module sdk prebuilts of stub libraries use a stub
-			// library as their source and must not be installed, but other prebuilts like
-			// libclang_rt.* libraries set `stubs` property because they are LLNDK libraries,
-			// but use an implementation library as their source and need to be installed.
-			// This discrepancy should be resolved without the prefix hack below.
-			isModuleSdkPrebuilts := android.HasAnyPrefix(ctx.ModuleDir(), []string{
-				"prebuilts/runtime/mainline/", "prebuilts/module_sdk/"})
-			if p.hasStubsVariants() && !p.buildStubs() && !ctx.Host() && isModuleSdkPrebuilts {
-				ctx.Module().MakeUninstallable()
-			}
-
 			return outputFile
 		}
+	} else if p.shared() && len(stubInfo) > 0 {
+		// This is a prebuilt which does not have any implementation (nil `srcs`), but provides APIs.
+		// Provide the latest (i.e. `current`) stubs to reverse dependencies.
+		latestStub := stubInfo[len(stubInfo)-1].SharedLibraryInfo.SharedLibrary
+		android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
+			SharedLibrary: latestStub,
+			Target:        ctx.Target(),
+		})
+
+		return latestStub
 	}
 
 	if p.header() {
@@ -237,14 +253,14 @@
 
 func (p *prebuiltLibraryLinker) prebuiltSrcs(ctx android.BaseModuleContext) []string {
 	sanitize := ctx.Module().(*Module).sanitize
-	srcs := p.properties.Srcs
+	srcs := p.properties.Srcs.GetOrDefault(ctx, nil)
 	srcs = append(srcs, srcsForSanitizer(sanitize, p.properties.Sanitized)...)
 	if p.static() {
-		srcs = append(srcs, p.libraryDecorator.StaticProperties.Static.Srcs...)
+		srcs = append(srcs, p.libraryDecorator.StaticProperties.Static.Srcs.GetOrDefault(ctx, nil)...)
 		srcs = append(srcs, srcsForSanitizer(sanitize, p.libraryDecorator.StaticProperties.Static.Sanitized)...)
 	}
 	if p.shared() {
-		srcs = append(srcs, p.libraryDecorator.SharedProperties.Shared.Srcs...)
+		srcs = append(srcs, p.libraryDecorator.SharedProperties.Shared.Srcs.GetOrDefault(ctx, nil)...)
 		srcs = append(srcs, srcsForSanitizer(sanitize, p.libraryDecorator.SharedProperties.Shared.Sanitized)...)
 	}
 	return srcs
@@ -259,7 +275,7 @@
 }
 
 func (p *prebuiltLibraryLinker) disablePrebuilt() {
-	p.properties.Srcs = nil
+	p.properties.Srcs = proptools.NewConfigurable[[]string](nil, nil)
 	p.properties.Sanitized.None.Srcs = nil
 	p.properties.Sanitized.Address.Srcs = nil
 	p.properties.Sanitized.Hwaddress.Srcs = nil
@@ -272,11 +288,11 @@
 
 func NewPrebuiltLibrary(hod android.HostOrDeviceSupported, srcsProperty string) (*Module, *libraryDecorator) {
 	module, library := NewLibrary(hod)
-	module.compiler = nil
 
 	prebuilt := &prebuiltLibraryLinker{
 		libraryDecorator: library,
 	}
+	module.compiler = prebuilt
 	module.linker = prebuilt
 	module.library = prebuilt
 
@@ -295,6 +311,13 @@
 	return module, library
 }
 
+func (p *prebuiltLibraryLinker) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+	if p.buildStubs() && p.stubsVersion() != "" {
+		return p.compileModuleLibApiStubs(ctx, flags, deps)
+	}
+	return Objects{}
+}
+
 // cc_prebuilt_library installs a precompiled shared library that are
 // listed in the srcs property in the device's directory.
 func PrebuiltLibraryFactory() android.Module {
@@ -427,7 +450,7 @@
 func (p *prebuiltBinaryLinker) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 	// TODO(ccross): verify shared library dependencies
-	if len(p.properties.Srcs) > 0 {
+	if len(p.properties.Srcs.GetOrDefault(ctx, nil)) > 0 {
 		fileName := p.getStem(ctx) + flags.Toolchain.ExecutableSuffix()
 		in := p.Prebuilt.SingleSourcePath(ctx)
 		outputFile := android.PathForModuleOut(ctx, fileName)
@@ -511,7 +534,7 @@
 
 	module.AddProperties(&prebuilt.properties)
 
-	android.InitPrebuiltModule(module, &prebuilt.properties.Srcs)
+	android.InitConfigurablePrebuiltModule(module, &prebuilt.properties.Srcs)
 	return module, binary
 }
 
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 71b7e43..acbbabc 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -385,112 +385,6 @@
 	assertString(t, static2.OutputFile().Path().Base(), "libf.hwasan.a")
 }
 
-func TestPrebuiltStubNoinstall(t *testing.T) {
-	testFunc := func(t *testing.T, expectLibfooOnSystemLib bool, fs android.MockFS) {
-		result := android.GroupFixturePreparers(
-			prepareForPrebuiltTest,
-			android.PrepareForTestWithMakevars,
-			android.FixtureMergeMockFs(fs),
-		).RunTest(t)
-
-		ldRule := result.ModuleForTests("installedlib", "android_arm64_armv8-a_shared").Rule("ld")
-		android.AssertStringDoesContain(t, "", ldRule.Args["libFlags"], "android_arm64_armv8-a_shared/libfoo.so")
-
-		installRules := result.InstallMakeRulesForTesting(t)
-		var installedlibRule *android.InstallMakeRule
-		for i, rule := range installRules {
-			if rule.Target == "out/target/product/test_device/system/lib/installedlib.so" {
-				if installedlibRule != nil {
-					t.Errorf("Duplicate install rules for %s", rule.Target)
-				}
-				installedlibRule = &installRules[i]
-			}
-		}
-		if installedlibRule == nil {
-			t.Errorf("No install rule found for installedlib")
-			return
-		}
-
-		if expectLibfooOnSystemLib {
-			android.AssertStringListContains(t,
-				"installedlib doesn't have install dependency on libfoo impl",
-				installedlibRule.OrderOnlyDeps,
-				"out/target/product/test_device/system/lib/libfoo.so")
-		} else {
-			android.AssertStringListDoesNotContain(t,
-				"installedlib has install dependency on libfoo stub",
-				installedlibRule.Deps,
-				"out/target/product/test_device/system/lib/libfoo.so")
-			android.AssertStringListDoesNotContain(t,
-				"installedlib has order-only install dependency on libfoo stub",
-				installedlibRule.OrderOnlyDeps,
-				"out/target/product/test_device/system/lib/libfoo.so")
-		}
-	}
-
-	prebuiltLibfooBp := []byte(`
-		cc_prebuilt_library {
-			name: "libfoo",
-			prefer: true,
-			srcs: ["libfoo.so"],
-			stubs: {
-				versions: ["1"],
-			},
-		}
-	`)
-
-	installedlibBp := []byte(`
-		cc_library {
-			name: "installedlib",
-			shared_libs: ["libfoo"],
-		}
-	`)
-
-	t.Run("prebuilt stub (without source): no install", func(t *testing.T) {
-		testFunc(
-			t,
-			/*expectLibfooOnSystemLib=*/ false,
-			android.MockFS{
-				"prebuilts/module_sdk/art/current/Android.bp": prebuiltLibfooBp,
-				"Android.bp": installedlibBp,
-			},
-		)
-	})
-
-	disabledSourceLibfooBp := []byte(`
-		cc_library {
-			name: "libfoo",
-			enabled: false,
-			stubs: {
-				versions: ["1"],
-			},
-		}
-	`)
-
-	t.Run("prebuilt stub (with disabled source): no install", func(t *testing.T) {
-		testFunc(
-			t,
-			/*expectLibfooOnSystemLib=*/ false,
-			android.MockFS{
-				"prebuilts/module_sdk/art/current/Android.bp": prebuiltLibfooBp,
-				"impl/Android.bp": disabledSourceLibfooBp,
-				"Android.bp":      installedlibBp,
-			},
-		)
-	})
-
-	t.Run("prebuilt impl (with `stubs` property set): install", func(t *testing.T) {
-		testFunc(
-			t,
-			/*expectLibfooOnSystemLib=*/ true,
-			android.MockFS{
-				"impl/Android.bp": prebuiltLibfooBp,
-				"Android.bp":      installedlibBp,
-			},
-		)
-	})
-}
-
 func TestPrebuiltBinaryNoSrcsNoError(t *testing.T) {
 	const bp = `
 cc_prebuilt_binary {
@@ -582,11 +476,7 @@
 			android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 				android.RegisterApexContributionsBuildComponents(ctx)
 			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		)
 		ctx := testPrebuilt(t, fmt.Sprintf(bp, tc.selectedDependencyName), map[string][]byte{
 			"libbar.so": nil,
@@ -680,11 +570,7 @@
 			android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 				android.RegisterApexContributionsBuildComponents(ctx)
 			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		)
 		if tc.expectedErr != "" {
 			preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedErr))
@@ -744,11 +630,7 @@
 		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 			android.RegisterApexContributionsBuildComponents(ctx)
 		}),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 	)
 	ctx := testPrebuilt(t, bp, map[string][]byte{
 		"libbar.so": nil,
diff --git a/cc/proto.go b/cc/proto.go
index 4d72f26..93142b9 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -19,6 +19,8 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+
+	"strings"
 )
 
 const (
@@ -35,13 +37,21 @@
 		srcSuffix = ".c"
 	}
 
+	srcInfix := "pb"
+	for _, value := range flags.proto.Flags {
+		if strings.HasPrefix(value, "--plugin=") && strings.HasSuffix(value, "protoc-gen-grpc-cpp-plugin") {
+			srcInfix = "grpc.pb"
+			break
+		}
+	}
+
 	if flags.proto.CanonicalPathFromRoot {
-		ccFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb"+srcSuffix)
-		headerFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb.h")
+		ccFile = android.GenPathWithExt(ctx, "proto", protoFile, srcInfix+srcSuffix)
+		headerFile = android.GenPathWithExt(ctx, "proto", protoFile, srcInfix+".h")
 	} else {
 		rel := protoFile.Rel()
-		ccFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb"+srcSuffix))
-		headerFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb.h"))
+		ccFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, srcInfix+srcSuffix))
+		headerFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, srcInfix+".h"))
 	}
 
 	protoDeps := flags.proto.Deps
diff --git a/cc/proto_test.go b/cc/proto_test.go
index abcb273..a905ea8 100644
--- a/cc/proto_test.go
+++ b/cc/proto_test.go
@@ -68,4 +68,36 @@
 		}
 	})
 
+	t.Run("grpc-cpp-plugin", func(t *testing.T) {
+		ctx := testCc(t, `
+                cc_binary_host {
+                        name: "protoc-gen-grpc-cpp-plugin",
+                        stl: "none",
+                }
+
+                cc_library_shared {
+                        name: "libgrpc",
+                        srcs: ["a.proto"],
+                        proto: {
+                                plugin: "grpc-cpp-plugin",
+                        },
+                }`)
+
+		buildOS := ctx.Config().BuildOS.String()
+
+		proto := ctx.ModuleForTests("libgrpc", "android_arm_armv7-a-neon_shared").Output("proto/a.grpc.pb.cc")
+		grpcCppPlugin := ctx.ModuleForTests("protoc-gen-grpc-cpp-plugin", buildOS+"_x86_64")
+
+		cmd := proto.RuleParams.Command
+		if w := "--grpc-cpp-plugin_out="; !strings.Contains(cmd, w) {
+			t.Errorf("expected %q in %q", w, cmd)
+		}
+
+		grpcCppPluginPath := grpcCppPlugin.Module().(android.HostToolProvider).HostToolPath().RelativeToTop().String()
+
+		if w := "--plugin=protoc-gen-grpc-cpp-plugin=" + grpcCppPluginPath; !strings.Contains(cmd, w) {
+			t.Errorf("expected %q in %q", w, cmd)
+		}
+	})
+
 }
diff --git a/cc/sanitize.go b/cc/sanitize.go
index d72d7d3..7f52ce1 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -36,7 +36,6 @@
 	asanCflags = []string{
 		"-fno-omit-frame-pointer",
 	}
-	asanLdflags = []string{"-Wl,-u,__asan_preinit"}
 
 	// DO NOT ADD MLLVM FLAGS HERE! ADD THEM BELOW TO hwasanCommonFlags.
 	hwasanCflags = []string{
@@ -80,8 +79,6 @@
 
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
 		"-fno-sanitize-recover=integer,undefined"}
-	hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
-		"export_memory_stats=0", "max_malloc_fill_size=131072", "malloc_fill_byte=0"}
 	memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
 	memtagStackLlvmFlags   = []string{"-dom-tree-reachability-max-bbs-to-explore=128"}
 
@@ -179,7 +176,7 @@
 	switch t {
 	case cfi, Hwasan, Asan, tsan, Fuzzer, scs, Memtag_stack:
 		sanitizer := &sanitizerSplitMutator{t}
-		ctx.TopDown(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
+		ctx.BottomUp(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
 		ctx.Transition(t.variationName(), sanitizer)
 	case Memtag_heap, Memtag_globals, intOverflow:
 		// do nothing
@@ -383,7 +380,19 @@
 	Sanitize        SanitizeUserProps         `android:"arch_variant"`
 	SanitizeMutated sanitizeMutatedProperties `blueprint:"mutated"`
 
-	SanitizerEnabled  bool     `blueprint:"mutated"`
+	// ForceDisable is set by the version mutator to disable sanitization of stubs variants
+	ForceDisable bool `blueprint:"mutated"`
+
+	// SanitizerEnabled is set by begin() if any of the sanitize boolean properties are set after
+	// applying the logic that enables globally enabled sanitizers and disables any unsupported
+	// sanitizers.
+	// TODO(b/349906293): this has some unintuitive behavior.  It is set in begin() before the sanitize
+	//  mutator is run if any of the individual sanitizes  properties are set, and then the individual
+	//  sanitize properties are cleared in the non-sanitized variants, but this value is never cleared.
+	//  That results in SanitizerEnabled being set in variants that have no sanitizers enabled, causing
+	//  some of the sanitizer logic in flags() to be applied to the non-sanitized variant.
+	SanitizerEnabled bool `blueprint:"mutated"`
+
 	MinimalRuntimeDep bool     `blueprint:"mutated"`
 	BuiltinsDep       bool     `blueprint:"mutated"`
 	UbsanRuntimeDep   bool     `blueprint:"mutated"`
@@ -455,6 +464,10 @@
 	s := &sanitize.Properties.SanitizeMutated
 	s.copyUserPropertiesToMutated(&sanitize.Properties.Sanitize)
 
+	if sanitize.Properties.ForceDisable {
+		return
+	}
+
 	// Don't apply sanitizers to NDK code.
 	if ctx.useSdk() {
 		s.Never = BoolPtr(true)
@@ -765,6 +778,10 @@
 }
 
 func (s *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
+	if s.Properties.ForceDisable {
+		return flags
+	}
+
 	if !s.Properties.SanitizerEnabled && !s.Properties.UbsanRuntimeDep {
 		return flags
 	}
@@ -777,16 +794,17 @@
 			flags.RequiredInstructionSet = "arm"
 		}
 		flags.Local.CFlags = append(flags.Local.CFlags, asanCflags...)
-		flags.Local.LdFlags = append(flags.Local.LdFlags, asanLdflags...)
 
 		if Bool(sanProps.Writeonly) {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-instrument-reads=0")
 		}
 
 		if ctx.Host() {
-			// -nodefaultlibs (provided with libc++) prevents the driver from linking
-			// libraries needed with -fsanitize=address. http://b/18650275 (WAI)
-			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-as-needed")
+			if !ctx.Darwin() { // ld64.lld doesn't know about '--no-as-needed'
+				// -nodefaultlibs (provided with libc++) prevents the driver from linking
+				// libraries needed with -fsanitize=address. http://b/18650275 (WAI)
+				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-as-needed")
+			}
 		} else {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-globals=0")
 			if ctx.bootstrap() {
@@ -1104,7 +1122,7 @@
 	if s == nil {
 		return false
 	}
-	if proptools.Bool(s.Properties.SanitizeMutated.Never) {
+	if s.Properties.ForceDisable || proptools.Bool(s.Properties.SanitizeMutated.Never) {
 		return false
 	}
 
@@ -1135,7 +1153,7 @@
 // If an APEX is sanitized or not depends on whether it contains at least one
 // sanitized module. Transition mutators cannot propagate information up the
 // dependency graph this way, so we need an auxiliary mutator to do so.
-func (s *sanitizerSplitMutator) markSanitizableApexesMutator(ctx android.TopDownMutatorContext) {
+func (s *sanitizerSplitMutator) markSanitizableApexesMutator(ctx android.BottomUpMutatorContext) {
 	if sanitizeable, ok := ctx.Module().(Sanitizeable); ok {
 		enabled := sanitizeable.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name())
 		ctx.VisitDirectDeps(func(dep android.Module) {
@@ -1329,7 +1347,7 @@
 }
 
 func (c *Module) SanitizeNever() bool {
-	return Bool(c.sanitize.Properties.SanitizeMutated.Never)
+	return c.sanitize.Properties.ForceDisable || Bool(c.sanitize.Properties.SanitizeMutated.Never)
 }
 
 func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool {
@@ -1337,9 +1355,12 @@
 }
 
 // Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies.
-func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) {
+func sanitizerRuntimeDepsMutator(mctx android.BottomUpMutatorContext) {
 	// Change this to PlatformSanitizable when/if non-cc modules support ubsan sanitizers.
 	if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
+		if c.sanitize.Properties.ForceDisable {
+			return
+		}
 		isSanitizableDependencyTag := c.SanitizableDepTagChecker()
 		mctx.WalkDeps(func(child, parent android.Module) bool {
 			if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
@@ -1350,7 +1371,7 @@
 			if !ok || !d.static() {
 				return false
 			}
-			if d.sanitize != nil {
+			if d.sanitize != nil && !d.sanitize.Properties.ForceDisable {
 				if enableMinimalRuntime(d.sanitize) {
 					// If a static dependency is built with the minimal runtime,
 					// make sure we include the ubsan minimal runtime.
@@ -1385,6 +1406,10 @@
 		if !c.Enabled(mctx) {
 			return
 		}
+		if c.sanitize.Properties.ForceDisable {
+			return
+		}
+
 		var sanitizers []string
 		var diagSanitizers []string
 
@@ -1412,11 +1437,11 @@
 					//"null",
 					//"shift-base",
 					//"signed-integer-overflow",
-					// TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on.
-					// https://llvm.org/PR19302
-					// http://reviews.llvm.org/D6974
-					// "object-size",
 				)
+
+				if mctx.Config().ReleaseBuildObjectSizeSanitizer() {
+					sanitizers = append(sanitizers, "object-size")
+				}
 			}
 			sanitizers = append(sanitizers, sanProps.Misc_undefined...)
 		}
@@ -1899,7 +1924,3 @@
 func (txt *sanitizerLibrariesTxtModule) SubDir() string {
 	return ""
 }
-
-func (txt *sanitizerLibrariesTxtModule) OutputFiles(tag string) (android.Paths, error) {
-	return android.Paths{txt.outputFile}, nil
-}
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 44f38e1..a1cfb5c 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -47,7 +47,7 @@
 `))
 
 type providerInterface interface {
-	android.SingletonModuleProviderContext
+	android.OtherModuleProviderContext
 }
 
 // expectSharedLinkDep verifies that the from module links against the to module as a
@@ -55,7 +55,7 @@
 func expectSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)
+	toInfo, _ := android.OtherModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)
 
 	if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); !android.InList(w, g) {
 		t.Errorf("%s should link against %s, expected %q, got %q",
@@ -68,7 +68,7 @@
 func expectNoSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)
+	toInfo, _ := android.OtherModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)
 
 	if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); android.InList(w, g) {
 		t.Errorf("%s should not link against %s, expected %q, got %q",
@@ -81,7 +81,7 @@
 func expectStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)
+	toInfo, _ := android.OtherModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)
 
 	if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); !android.InList(w, g) {
 		t.Errorf("%s should link against %s, expected %q, got %q",
@@ -95,7 +95,7 @@
 func expectNoStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)
+	toInfo, _ := android.OtherModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)
 
 	if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); android.InList(w, g) {
 		t.Errorf("%s should not link against %s, expected %q, got %q",
@@ -794,47 +794,47 @@
 
 		android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in bin_with_ubsan static libs",
 			strings.Split(binWithUbsan.Rule("ld").Args["libFlags"], " "),
-			minimalRuntime.OutputFiles(t, "")[0].String())
+			minimalRuntime.OutputFiles(result.TestContext, t, "")[0].String())
 
 		android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in bin_depends_ubsan_static static libs",
 			strings.Split(binDependsUbsan.Rule("ld").Args["libFlags"], " "),
-			minimalRuntime.OutputFiles(t, "")[0].String())
+			minimalRuntime.OutputFiles(result.TestContext, t, "")[0].String())
 
 		android.AssertStringListContains(t, "missing libclang_rt.ubsan_minimal in libsharedubsan static libs",
 			strings.Split(libSharedUbsan.Rule("ld").Args["libFlags"], " "),
-			minimalRuntime.OutputFiles(t, "")[0].String())
+			minimalRuntime.OutputFiles(result.TestContext, t, "")[0].String())
 
 		android.AssertStringListDoesNotContain(t, "unexpected libclang_rt.ubsan_minimal in bin_depends_ubsan_shared static libs",
 			strings.Split(binDependsUbsanShared.Rule("ld").Args["libFlags"], " "),
-			minimalRuntime.OutputFiles(t, "")[0].String())
+			minimalRuntime.OutputFiles(result.TestContext, t, "")[0].String())
 
 		android.AssertStringListDoesNotContain(t, "unexpected libclang_rt.ubsan_minimal in bin_no_ubsan static libs",
 			strings.Split(binNoUbsan.Rule("ld").Args["libFlags"], " "),
-			minimalRuntime.OutputFiles(t, "")[0].String())
+			minimalRuntime.OutputFiles(result.TestContext, t, "")[0].String())
 
 		android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in bin_with_ubsan",
 			strings.Split(binWithUbsan.Rule("ld").Args["ldFlags"], " "),
-			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(result.TestContext, t, "")[0].Base())
 
 		android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in bin_depends_ubsan_static static libs",
 			strings.Split(binDependsUbsan.Rule("ld").Args["ldFlags"], " "),
-			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(result.TestContext, t, "")[0].Base())
 
 		android.AssertStringListContains(t, "missing -Wl,--exclude-libs for minimal runtime in libsharedubsan static libs",
 			strings.Split(libSharedUbsan.Rule("ld").Args["ldFlags"], " "),
-			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(result.TestContext, t, "")[0].Base())
 
 		android.AssertStringListDoesNotContain(t, "unexpected -Wl,--exclude-libs for minimal runtime in bin_depends_ubsan_shared static libs",
 			strings.Split(binDependsUbsanShared.Rule("ld").Args["ldFlags"], " "),
-			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(result.TestContext, t, "")[0].Base())
 
 		android.AssertStringListDoesNotContain(t, "unexpected -Wl,--exclude-libs for minimal runtime in bin_no_ubsan static libs",
 			strings.Split(binNoUbsan.Rule("ld").Args["ldFlags"], " "),
-			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(t, "")[0].Base())
+			"-Wl,--exclude-libs="+minimalRuntime.OutputFiles(result.TestContext, t, "")[0].Base())
 
 		android.AssertStringListContains(t, "missing libclang_rt.ubsan_standalone.static in static_bin_with_ubsan_dep static libs",
 			strings.Split(staticBin.Rule("ld").Args["libFlags"], " "),
-			standaloneRuntime.OutputFiles(t, "")[0].String())
+			standaloneRuntime.OutputFiles(result.TestContext, t, "")[0].String())
 
 	}
 
diff --git a/cc/sdk.go b/cc/sdk.go
index 4925ce1..5dd44d8 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -19,12 +19,74 @@
 	"android/soong/genrule"
 )
 
-// sdkMutator sets a creates a platform and an SDK variant for modules
+// sdkTransitionMutator creates a platform and an SDK variant for modules
 // that set sdk_version, and ignores sdk_version for the platform
 // variant.  The SDK variant will be used for embedding in APKs
 // that may be installed on older platforms.  Apexes use their own
 // variants that enforce backwards compatibility.
-func sdkMutator(ctx android.BottomUpMutatorContext) {
+type sdkTransitionMutator struct{}
+
+func (sdkTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	if ctx.Os() != android.Android {
+		return []string{""}
+	}
+
+	switch m := ctx.Module().(type) {
+	case LinkableInterface:
+		if m.AlwaysSdk() {
+			if !m.UseSdk() && !m.SplitPerApiLevel() {
+				ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?")
+			}
+			return []string{"sdk"}
+		} else if m.UseSdk() || m.SplitPerApiLevel() {
+			return []string{"", "sdk"}
+		} else {
+			return []string{""}
+		}
+	case *genrule.Module:
+		if p, ok := m.Extra.(*GenruleExtraProperties); ok {
+			if String(p.Sdk_version) != "" {
+				return []string{"", "sdk"}
+			} else {
+				return []string{""}
+			}
+		}
+	}
+
+	return []string{""}
+}
+
+func (sdkTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (sdkTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if ctx.Os() != android.Android {
+		return ""
+	}
+	switch m := ctx.Module().(type) {
+	case LinkableInterface:
+		if m.AlwaysSdk() {
+			return "sdk"
+		} else if m.UseSdk() || m.SplitPerApiLevel() {
+			return incomingVariation
+		}
+	case *genrule.Module:
+		if p, ok := m.Extra.(*GenruleExtraProperties); ok {
+			if String(p.Sdk_version) != "" {
+				return incomingVariation
+			}
+		}
+	}
+
+	if ctx.IsAddingDependency() {
+		return incomingVariation
+	} else {
+		return ""
+	}
+}
+
+func (sdkTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
 	if ctx.Os() != android.Android {
 		return
 	}
@@ -33,59 +95,45 @@
 	case LinkableInterface:
 		ccModule, isCcModule := ctx.Module().(*Module)
 		if m.AlwaysSdk() {
-			if !m.UseSdk() && !m.SplitPerApiLevel() {
-				ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?")
+			if variation != "sdk" {
+				ctx.ModuleErrorf("tried to create variation %q for module with AlwaysSdk set, expected \"sdk\"", variation)
 			}
-			modules := ctx.CreateVariations("sdk")
-			modules[0].(*Module).Properties.IsSdkVariant = true
+
+			ccModule.Properties.IsSdkVariant = true
 		} else if m.UseSdk() || m.SplitPerApiLevel() {
-			modules := ctx.CreateVariations("", "sdk")
+			if variation == "" {
+				// Clear the sdk_version property for the platform (non-SDK) variant so later code
+				// doesn't get confused by it.
+				ccModule.Properties.Sdk_version = nil
+			} else {
+				// Mark the SDK variant.
+				ccModule.Properties.IsSdkVariant = true
 
-			// Clear the sdk_version property for the platform (non-SDK) variant so later code
-			// doesn't get confused by it.
-			modules[0].(*Module).Properties.Sdk_version = nil
-
-			// Mark the SDK variant.
-			modules[1].(*Module).Properties.IsSdkVariant = true
+				// SDK variant never gets installed because the variant is to be embedded in
+				// APKs, not to be installed to the platform.
+				ccModule.Properties.PreventInstall = true
+			}
 
 			if ctx.Config().UnbundledBuildApps() {
-				// For an unbundled apps build, hide the platform variant from Make
-				// so that other Make modules don't link against it, but against the
-				// SDK variant.
-				modules[0].(*Module).Properties.HideFromMake = true
+				if variation == "" {
+					// For an unbundled apps build, hide the platform variant from Make
+					// so that other Make modules don't link against it, but against the
+					// SDK variant.
+					ccModule.Properties.HideFromMake = true
+				}
 			} else {
-				// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
-				// exposed to Make.
-				modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
+				if variation == "sdk" {
+					// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
+					// exposed to Make.
+					ccModule.Properties.SdkAndPlatformVariantVisibleToMake = true
+				}
 			}
-			// SDK variant never gets installed because the variant is to be embedded in
-			// APKs, not to be installed to the platform.
-			modules[1].(*Module).Properties.PreventInstall = true
-			ctx.AliasVariation("")
 		} else {
 			if isCcModule {
 				// Clear the sdk_version property for modules that don't have an SDK variant so
 				// later code doesn't get confused by it.
 				ccModule.Properties.Sdk_version = nil
 			}
-			ctx.CreateVariations("")
-			ctx.AliasVariation("")
-		}
-	case *genrule.Module:
-		if p, ok := m.Extra.(*GenruleExtraProperties); ok {
-			if String(p.Sdk_version) != "" {
-				ctx.CreateVariations("", "sdk")
-			} else {
-				ctx.CreateVariations("")
-			}
-			ctx.AliasVariation("")
-		}
-	case *CcApiVariant:
-		ccApiVariant, _ := ctx.Module().(*CcApiVariant)
-		if String(ccApiVariant.properties.Variant) == "ndk" {
-			ctx.CreateVariations("sdk")
-		} else {
-			ctx.CreateVariations("")
 		}
 	}
 }
diff --git a/cc/stl.go b/cc/stl.go
index de2066f..8c4ef0b 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -177,7 +177,7 @@
 		} else {
 			deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl, "ndk_libc++abi")
 		}
-		deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
+		deps.StaticLibs = append(deps.StaticLibs, "libunwind")
 	default:
 		panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
 	}
diff --git a/cc/stub_library.go b/cc/stub_library.go
index 47c6cb9..e746a33 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -39,8 +39,9 @@
 }
 
 // Get target file name to be installed from this module
-func getInstalledFileName(m *Module) string {
-	for _, ps := range m.PackagingSpecs() {
+func getInstalledFileName(ctx android.SingletonContext, m *Module) string {
+	for _, ps := range android.OtherModuleProviderOrDefault(
+		ctx, m.Module(), android.InstallFilesProvider).PackagingSpecs {
 		if name := ps.FileName(); name != "" {
 			return name
 		}
@@ -53,14 +54,14 @@
 	ctx.VisitAllModules(func(module android.Module) {
 		if m, ok := module.(*Module); ok {
 			if IsStubTarget(m) {
-				if name := getInstalledFileName(m); name != "" {
+				if name := getInstalledFileName(ctx, m); name != "" {
 					s.stubLibraryMap[name] = true
 					if m.InVendor() {
 						s.stubVendorLibraryMap[name] = true
 					}
 				}
 			}
-			if m.library != nil {
+			if m.library != nil && android.IsModulePreferred(m) {
 				if p := m.library.getAPIListCoverageXMLPath().String(); p != "" {
 					s.apiListCoverageXmlPaths = append(s.apiListCoverageXmlPaths, p)
 				}
diff --git a/cc/test.go b/cc/test.go
index a96af31..f5bb761 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -15,11 +15,9 @@
 package cc
 
 import (
+	"github.com/google/blueprint/proptools"
 	"path/filepath"
 	"strconv"
-	"strings"
-
-	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 	"android/soong/tradefed"
@@ -75,13 +73,9 @@
 }
 
 type TestBinaryProperties struct {
-	// Create a separate binary for each source file.  Useful when there is
-	// global state that can not be torn down and reset between each test suite.
-	Test_per_src *bool
-
 	// Disables the creation of a test-specific directory when used with
 	// relative_install_path. Useful if several tests need to be in the same
-	// directory, but test_per_src doesn't work.
+	// directory.
 	No_named_install_directory *bool
 
 	// list of files or filegroup modules that provide data that should be installed alongside
@@ -174,86 +168,14 @@
 	return module.Init()
 }
 
-type testPerSrc interface {
-	testPerSrc() bool
-	srcs() []string
-	isAllTestsVariation() bool
-	setSrc(string, string)
-	unsetSrc()
-}
-
-func (test *testBinary) testPerSrc() bool {
-	return Bool(test.Properties.Test_per_src)
-}
-
-func (test *testBinary) srcs() []string {
-	return test.baseCompiler.Properties.Srcs
-}
-
 func (test *testBinary) dataPaths() []android.DataPath {
 	return test.data
 }
 
-func (test *testBinary) isAllTestsVariation() bool {
-	stem := test.binaryDecorator.Properties.Stem
-	return stem != nil && *stem == ""
-}
-
-func (test *testBinary) setSrc(name, src string) {
-	test.baseCompiler.Properties.Srcs = []string{src}
-	test.binaryDecorator.Properties.Stem = StringPtr(name)
-}
-
-func (test *testBinary) unsetSrc() {
-	test.baseCompiler.Properties.Srcs = nil
-	test.binaryDecorator.Properties.Stem = StringPtr("")
-}
-
 func (test *testBinary) testBinary() bool {
 	return true
 }
 
-var _ testPerSrc = (*testBinary)(nil)
-
-func TestPerSrcMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok {
-		if test, ok := m.linker.(testPerSrc); ok {
-			numTests := len(test.srcs())
-			if test.testPerSrc() && numTests > 0 {
-				if duplicate, found := android.CheckDuplicate(test.srcs()); found {
-					mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
-					return
-				}
-				testNames := make([]string, numTests)
-				for i, src := range test.srcs() {
-					testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
-				}
-				// In addition to creating one variation per test source file,
-				// create an additional "all tests" variation named "", and have it
-				// depends on all other test_per_src variations. This is useful to
-				// create subsequent dependencies of a given module on all
-				// test_per_src variations created above: by depending on
-				// variation "", that module will transitively depend on all the
-				// other test_per_src variations without the need to know their
-				// name or even their number.
-				testNames = append(testNames, "")
-				tests := mctx.CreateLocalVariations(testNames...)
-				allTests := tests[numTests]
-				allTests.(*Module).linker.(testPerSrc).unsetSrc()
-				// Prevent the "all tests" variation from being installable nor
-				// exporting to Make, as it won't create any output file.
-				allTests.(*Module).Properties.PreventInstall = true
-				allTests.(*Module).Properties.HideFromMake = true
-				for i, src := range test.srcs() {
-					tests[i].(*Module).linker.(testPerSrc).setSrc(testNames[i], src)
-					mctx.AddInterVariantDependency(testPerSrcDepTag, allTests, tests[i])
-				}
-				mctx.AliasVariation("")
-			}
-		}
-	}
-}
-
 type testDecorator struct {
 	LinkerProperties    TestLinkerProperties
 	InstallerProperties TestInstallerProperties
@@ -382,10 +304,6 @@
 	}
 	moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, test.extraTestConfigs.Strings()...)
 
-	if Bool(test.Properties.Test_per_src) {
-		moduleInfoJSON.SubName = "_" + String(test.binaryDecorator.Properties.Stem)
-	}
-
 	moduleInfoJSON.DataDependencies = append(moduleInfoJSON.DataDependencies, test.Properties.Data_bins...)
 
 	if len(test.InstallerProperties.Test_suites) > 0 {
diff --git a/cc/testing.go b/cc/testing.go
index 02f9924..14a6b7a 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -20,7 +20,6 @@
 
 	"android/soong/android"
 	"android/soong/genrule"
-	"android/soong/multitree"
 )
 
 func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
@@ -29,17 +28,12 @@
 	RegisterBinaryBuildComponents(ctx)
 	RegisterLibraryBuildComponents(ctx)
 	RegisterLibraryHeadersBuildComponents(ctx)
-	RegisterLibraryStubBuildComponents(ctx)
-
-	multitree.RegisterApiImportsModule(ctx)
 
 	ctx.RegisterModuleType("prebuilt_build_tool", android.NewPrebuiltBuildTool)
 	ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
 	ctx.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory)
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("cc_genrule", GenRuleFactory)
-	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
-	ctx.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
 	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
 	ctx.RegisterModuleType("ndk_headers", NdkHeadersFactory)
 }
@@ -312,6 +306,25 @@
 			],
 		}
 		cc_library {
+			name: "ndk_libc++_shared",
+			export_include_dirs: ["ndk_libc++_shared_include_dirs"],
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			product_available: true,
+			recovery_available: true,
+			host_supported: false,
+			sdk_version: "minimum",
+			double_loadable: true,
+			apex_available: [
+				"//apex_available:platform",
+				"//apex_available:anyapex",
+			],
+		}
+		cc_library {
 			name: "libc++demangle",
 			no_libcrt: true,
 			nocrt: true,
@@ -397,13 +410,6 @@
 			name: "libprotobuf-cpp-lite",
 		}
 
-		cc_library {
-			name: "ndk_libunwind",
-			sdk_version: "minimum",
-			stl: "none",
-			system_shared_libs: [],
-		}
-
 		ndk_library {
 			name: "libc",
 			first_version: "minimum",
@@ -422,11 +428,6 @@
 			symbol_file: "libdl.map.txt",
 		}
 
-		ndk_prebuilt_shared_stl {
-			name: "ndk_libc++_shared",
-			export_include_dirs: ["ndk_libc++_shared"],
-		}
-
 		cc_library_static {
 			name: "libgoogle-benchmark",
 			sdk_version: "current",
@@ -557,13 +558,6 @@
 
 		RegisterLlndkLibraryTxtType(ctx)
 	}),
-
-	// Additional files needed in tests that disallow non-existent source files.
-	// This includes files that are needed by all, or at least most, instances of a cc module type.
-	android.MockFS{
-		// Needed for ndk_prebuilt_(shared|static)_stl.
-		"defaults/cc/common/current/sources/cxx-stl/llvm-libc++/libs": nil,
-	}.AddToFixture(),
 )
 
 // Preparer that will define default cc modules, e.g. standard prebuilt modules.
@@ -572,17 +566,17 @@
 
 	// Additional files needed in tests that disallow non-existent source.
 	android.MockFS{
-		"defaults/cc/common/libc.map.txt":                nil,
-		"defaults/cc/common/libdl.map.txt":               nil,
-		"defaults/cc/common/libft2.map.txt":              nil,
-		"defaults/cc/common/libm.map.txt":                nil,
-		"defaults/cc/common/ndk_libc++_shared":           nil,
-		"defaults/cc/common/crtbegin_so.c":               nil,
-		"defaults/cc/common/crtbegin.c":                  nil,
-		"defaults/cc/common/crtend_so.c":                 nil,
-		"defaults/cc/common/crtend.c":                    nil,
-		"defaults/cc/common/crtbrand.c":                  nil,
-		"external/compiler-rt/lib/cfi/cfi_blocklist.txt": nil,
+		"defaults/cc/common/libc.map.txt":                   nil,
+		"defaults/cc/common/libdl.map.txt":                  nil,
+		"defaults/cc/common/libft2.map.txt":                 nil,
+		"defaults/cc/common/libm.map.txt":                   nil,
+		"defaults/cc/common/ndk_libc++_shared_include_dirs": nil,
+		"defaults/cc/common/crtbegin_so.c":                  nil,
+		"defaults/cc/common/crtbegin.c":                     nil,
+		"defaults/cc/common/crtend_so.c":                    nil,
+		"defaults/cc/common/crtend.c":                       nil,
+		"defaults/cc/common/crtbrand.c":                     nil,
+		"external/compiler-rt/lib/cfi/cfi_blocklist.txt":    nil,
 
 		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm64.a": nil,
 		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm.a":   nil,
@@ -715,7 +709,7 @@
 func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
 	t.Helper()
 	mod := ctx.ModuleForTests(moduleName, variant)
-	outputFiles := mod.OutputFiles(t, "")
+	outputFiles := mod.OutputFiles(ctx, t, "")
 	if len(outputFiles) != 1 {
 		t.Errorf("%q must have single output\n", moduleName)
 		return
diff --git a/cmd/extract_apks/bundle_proto/Android.bp b/cmd/extract_apks/bundle_proto/Android.bp
index e56c0fb..0abf1e2 100644
--- a/cmd/extract_apks/bundle_proto/Android.bp
+++ b/cmd/extract_apks/bundle_proto/Android.bp
@@ -10,4 +10,8 @@
     proto: {
         canonical_path_from_root: false,
     },
+    visibility: [
+        "//build/soong:__subpackages__",
+        "//tools/mainline:__subpackages__",
+    ],
 }
diff --git a/cmd/release_config/release_config_lib/flag_artifact.go b/cmd/release_config/release_config_lib/flag_artifact.go
index 6cdde7c..51c02d2 100644
--- a/cmd/release_config/release_config_lib/flag_artifact.go
+++ b/cmd/release_config/release_config_lib/flag_artifact.go
@@ -67,7 +67,7 @@
 	if artifactsPath != "" {
 		fas := &rc_proto.FlagArtifacts{}
 		LoadMessage(artifactsPath, fas)
-		for _, fa_pb := range fas.FlagArtifacts {
+		for _, fa_pb := range fas.Flags {
 			fa := &FlagArtifact{}
 			fa.FlagDeclaration = fa_pb.GetFlagDeclaration()
 			if val := fa_pb.GetValue(); val != nil {
@@ -102,7 +102,7 @@
 	if description := fa.FlagDeclaration.GetDescription(); description != "" {
 		ret.Description = proto.String(description)
 	}
-	if workflow := fa.FlagDeclaration.GetWorkflow(); workflow != rc_proto.Workflow_Workflow_Unspecified {
+	if workflow := fa.FlagDeclaration.GetWorkflow(); workflow != rc_proto.Workflow_WORKFLOW_UNSPECIFIED {
 		ret.Workflow = &workflow
 	}
 	if containers := fa.FlagDeclaration.GetContainers(); containers != nil {
@@ -116,20 +116,20 @@
 	if path != "" {
 		LoadMessage(path, ret)
 	} else {
-		ret.FlagDeclarationArtifacts = []*rc_proto.FlagDeclarationArtifact{}
+		ret.FlagDeclarationArtifactList = []*rc_proto.FlagDeclarationArtifact{}
 	}
 	return ret
 }
 
 func (fas *FlagArtifacts) GenerateFlagDeclarationArtifacts(intermediates []*rc_proto.FlagDeclarationArtifacts) *rc_proto.FlagDeclarationArtifacts {
-	ret := &rc_proto.FlagDeclarationArtifacts{FlagDeclarationArtifacts: []*rc_proto.FlagDeclarationArtifact{}}
+	ret := &rc_proto.FlagDeclarationArtifacts{FlagDeclarationArtifactList: []*rc_proto.FlagDeclarationArtifact{}}
 	for _, fa := range *fas {
-		ret.FlagDeclarationArtifacts = append(ret.FlagDeclarationArtifacts, fa.GenerateFlagDeclarationArtifact())
+		ret.FlagDeclarationArtifactList = append(ret.FlagDeclarationArtifactList, fa.GenerateFlagDeclarationArtifact())
 	}
 	for _, fda := range intermediates {
-		ret.FlagDeclarationArtifacts = append(ret.FlagDeclarationArtifacts, fda.FlagDeclarationArtifacts...)
+		ret.FlagDeclarationArtifactList = append(ret.FlagDeclarationArtifactList, fda.FlagDeclarationArtifactList...)
 	}
-	slices.SortFunc(ret.FlagDeclarationArtifacts, func(a, b *rc_proto.FlagDeclarationArtifact) int {
+	slices.SortFunc(ret.FlagDeclarationArtifactList, func(a, b *rc_proto.FlagDeclarationArtifact) int {
 		return cmp.Compare(*a.Name, *b.Name)
 	})
 	return ret
diff --git a/cmd/release_config/release_config_lib/flag_declaration.go b/cmd/release_config/release_config_lib/flag_declaration.go
index 97d4d4c..22001bf 100644
--- a/cmd/release_config/release_config_lib/flag_declaration.go
+++ b/cmd/release_config/release_config_lib/flag_declaration.go
@@ -18,10 +18,21 @@
 	rc_proto "android/soong/cmd/release_config/release_config_proto"
 )
 
+var (
+	// Allowlist: these flags may have duplicate (identical) declarations
+	// without generating an error.  This will be removed once all such
+	// declarations have been fixed.
+	DuplicateDeclarationAllowlist = map[string]bool{}
+)
+
 func FlagDeclarationFactory(protoPath string) (fd *rc_proto.FlagDeclaration) {
 	fd = &rc_proto.FlagDeclaration{}
 	if protoPath != "" {
 		LoadMessage(protoPath, fd)
 	}
+	// If the input didn't specify a value, create one (== UnspecifiedValue).
+	if fd.Value == nil {
+		fd.Value = &rc_proto.Value{Val: &rc_proto.Value_UnspecifiedValue{false}}
+	}
 	return fd
 }
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
index f0ce1bb..ee71336 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -192,6 +192,7 @@
 
 	workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL)
 	myDirsMap := make(map[int]bool)
+	myValueDirsMap := make(map[int]bool)
 	if isBuildPrefix && releasePlatformVersion != nil {
 		if MarshalValue(releasePlatformVersion.Value) != strings.ToUpper(config.Name) {
 			value := FlagValue{
@@ -226,8 +227,18 @@
 			config.PriorStagesMap[priorStage] = true
 		}
 		myDirsMap[contrib.DeclarationIndex] = true
-		if config.AconfigFlagsOnly && len(contrib.FlagValues) > 0 {
-			return fmt.Errorf("%s does not allow build flag overrides", config.Name)
+		// This path *could* provide a value for this release config.
+		myValueDirsMap[contrib.DeclarationIndex] = true
+		if config.AconfigFlagsOnly {
+			// AconfigFlagsOnly allows very very few build flag values, all of them are part of aconfig flags.
+			allowedFlags := map[string]bool{
+				"RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS": true,
+			}
+			for _, fv := range contrib.FlagValues {
+				if !allowedFlags[*fv.proto.Name] {
+					return fmt.Errorf("%s does not allow build flag overrides", config.Name)
+				}
+			}
 		}
 		for _, value := range contrib.FlagValues {
 			name := *value.proto.Name
@@ -235,10 +246,13 @@
 			if !ok {
 				return fmt.Errorf("Setting value for undefined flag %s in %s\n", name, value.path)
 			}
+			// Record that flag declarations from fa.DeclarationIndex were included in this release config.
 			myDirsMap[fa.DeclarationIndex] = true
+			// Do not set myValueDirsMap, since it just records that we *could* provide values here.
 			if fa.DeclarationIndex > contrib.DeclarationIndex {
 				// Setting location is to the left of declaration.
-				return fmt.Errorf("Setting value for flag %s not allowed in %s\n", name, value.path)
+				return fmt.Errorf("Setting value for flag %s (declared in %s) not allowed in %s\n",
+					name, filepath.Dir(configs.ReleaseConfigMaps[fa.DeclarationIndex].path), value.path)
 			}
 			if isRoot && *fa.FlagDeclaration.Workflow != workflowManual {
 				// The "root" release config can only contain workflow: MANUAL flags.
@@ -256,7 +270,7 @@
 	myAconfigValueSets := []string{}
 	myAconfigValueSetsMap := map[string]bool{}
 	for _, v := range strings.Split(releaseAconfigValueSets.Value.GetStringValue(), " ") {
-		if myAconfigValueSetsMap[v] {
+		if v == "" || myAconfigValueSetsMap[v] {
 			continue
 		}
 		myAconfigValueSetsMap[v] = true
@@ -265,10 +279,31 @@
 	releaseAconfigValueSets.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{strings.TrimSpace(strings.Join(myAconfigValueSets, " "))}}
 
 	directories := []string{}
+	valueDirectories := []string{}
+	// These path prefixes are exclusive for a release config.
+	// "A release config shall exist in at most one of these."
+	// If we find a benefit to generalizing this, we can do so at that time.
+	exclusiveDirPrefixes := []string{
+		"build/release",
+		"vendor/google_shared/build/release",
+	}
+	var exclusiveDir string
 	for idx, confDir := range configs.configDirs {
 		if _, ok := myDirsMap[idx]; ok {
 			directories = append(directories, confDir)
 		}
+		if _, ok := myValueDirsMap[idx]; ok {
+			for _, dir := range exclusiveDirPrefixes {
+				if strings.HasPrefix(confDir, dir) {
+					if exclusiveDir != "" && !strings.HasPrefix(exclusiveDir, dir) {
+						return fmt.Errorf("%s is declared in both %s and %s",
+							config.Name, exclusiveDir, confDir)
+					}
+					exclusiveDir = confDir
+				}
+			}
+			valueDirectories = append(valueDirectories, confDir)
+		}
 	}
 
 	// Now build the per-partition artifacts
@@ -282,13 +317,13 @@
 			if _, ok := config.PartitionBuildFlags[container]; !ok {
 				config.PartitionBuildFlags[container] = &rc_proto.FlagArtifacts{}
 			}
-			config.PartitionBuildFlags[container].FlagArtifacts = append(config.PartitionBuildFlags[container].FlagArtifacts, artifact)
+			config.PartitionBuildFlags[container].Flags = append(config.PartitionBuildFlags[container].Flags, artifact)
 		}
 	}
 	config.ReleaseConfigArtifact = &rc_proto.ReleaseConfigArtifact{
 		Name:       proto.String(config.Name),
 		OtherNames: config.OtherNames,
-		FlagArtifacts: func() []*rc_proto.FlagArtifact {
+		Flags: func() []*rc_proto.FlagArtifact {
 			ret := []*rc_proto.FlagArtifact{}
 			flagNames := []string{}
 			for k := range config.FlagArtifacts {
@@ -308,6 +343,7 @@
 		AconfigValueSets: myAconfigValueSets,
 		Inherits:         myInherits,
 		Directories:      directories,
+		ValueDirectories: valueDirectories,
 		PriorStages:      SortedMapKeys(config.PriorStagesMap),
 	}
 
@@ -320,6 +356,23 @@
 	makeVars := make(map[string]string)
 
 	myFlagArtifacts := config.FlagArtifacts.Clone()
+
+	// Add any RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS variables.
+	var extraAconfigReleaseConfigs []string
+	if extraAconfigValueSetsValue, ok := config.FlagArtifacts["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"]; ok {
+		if val := MarshalValue(extraAconfigValueSetsValue.Value); len(val) > 0 {
+			extraAconfigReleaseConfigs = strings.Split(val, " ")
+		}
+	}
+	for _, rcName := range extraAconfigReleaseConfigs {
+		rc, err := configs.GetReleaseConfig(rcName)
+		if err != nil {
+			return err
+		}
+		myFlagArtifacts["RELEASE_ACONFIG_VALUE_SETS_"+rcName] = rc.FlagArtifacts["RELEASE_ACONFIG_VALUE_SETS"]
+		myFlagArtifacts["RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION_"+rcName] = rc.FlagArtifacts["RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION"]
+	}
+
 	// Sort the flags by name first.
 	names := myFlagArtifacts.SortedFlagNames()
 	partitions := make(map[string][]string)
@@ -386,7 +439,7 @@
 func (config *ReleaseConfig) WritePartitionBuildFlags(outDir string) error {
 	var err error
 	for partition, flags := range config.PartitionBuildFlags {
-		slices.SortFunc(flags.FlagArtifacts, func(a, b *rc_proto.FlagArtifact) int {
+		slices.SortFunc(flags.Flags, func(a, b *rc_proto.FlagArtifact) int {
 			return cmp.Compare(*a.FlagDeclaration.Name, *b.FlagDeclaration.Name)
 		})
 		// The json file name must not be modified as this is read from
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index f2e1388..97eb8f1 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -42,6 +42,10 @@
 
 	// Flags declared this directory's flag_declarations/*.textproto
 	FlagDeclarations []rc_proto.FlagDeclaration
+
+	// Potential aconfig and build flag contributions in this map directory.
+	// This is used to detect errors.
+	FlagValueDirs map[string][]string
 }
 
 type ReleaseConfigDirMap map[string]int
@@ -272,6 +276,20 @@
 		configs.Aliases[name] = alias.Target
 	}
 	var err error
+	// Temporarily allowlist duplicate flag declaration files to prevent
+	// more from entering the tree while we work to clean up the duplicates
+	// that already exist.
+	dupFlagFile := filepath.Join(dir, "duplicate_allowlist.txt")
+	data, err := os.ReadFile(dupFlagFile)
+	if err == nil {
+		for _, flag := range strings.Split(string(data), "\n") {
+			flag = strings.TrimSpace(flag)
+			if strings.HasPrefix(flag, "//") || strings.HasPrefix(flag, "#") {
+				continue
+			}
+			DuplicateDeclarationAllowlist[flag] = true
+		}
+	}
 	err = WalkTextprotoFiles(dir, "flag_declarations", func(path string, d fs.DirEntry, err error) error {
 		flagDeclaration := FlagDeclarationFactory(path)
 		// Container must be specified.
@@ -285,14 +303,6 @@
 			}
 		}
 
-		// TODO: once we have namespaces initialized, we can throw an error here.
-		if flagDeclaration.Namespace == nil {
-			flagDeclaration.Namespace = proto.String("android_UNKNOWN")
-		}
-		// If the input didn't specify a value, create one (== UnspecifiedValue).
-		if flagDeclaration.Value == nil {
-			flagDeclaration.Value = &rc_proto.Value{Val: &rc_proto.Value_UnspecifiedValue{false}}
-		}
 		m.FlagDeclarations = append(m.FlagDeclarations, *flagDeclaration)
 		name := *flagDeclaration.Name
 		if name == "RELEASE_ACONFIG_VALUE_SETS" {
@@ -300,8 +310,8 @@
 		}
 		if def, ok := configs.FlagArtifacts[name]; !ok {
 			configs.FlagArtifacts[name] = &FlagArtifact{FlagDeclaration: flagDeclaration, DeclarationIndex: ConfigDirIndex}
-		} else if !proto.Equal(def.FlagDeclaration, flagDeclaration) {
-			return fmt.Errorf("Duplicate definition of %s", *flagDeclaration.Name)
+		} else if !proto.Equal(def.FlagDeclaration, flagDeclaration) || !DuplicateDeclarationAllowlist[name] {
+			return fmt.Errorf("Duplicate definition of %s in %s", *flagDeclaration.Name, path)
 		}
 		// Set the initial value in the flag artifact.
 		configs.FilesUsedMap[path] = true
@@ -317,6 +327,21 @@
 		return err
 	}
 
+	subDirs := func(subdir string) (ret []string) {
+		if flagVersions, err := os.ReadDir(filepath.Join(dir, subdir)); err == nil {
+			for _, e := range flagVersions {
+				if e.IsDir() && validReleaseConfigName(e.Name()) {
+					ret = append(ret, e.Name())
+				}
+			}
+		}
+		return
+	}
+	m.FlagValueDirs = map[string][]string{
+		"aconfig":     subDirs("aconfig"),
+		"flag_values": subDirs("flag_values"),
+	}
+
 	err = WalkTextprotoFiles(dir, "release_configs", func(path string, d fs.DirEntry, err error) error {
 		releaseConfigContribution := &ReleaseConfigContribution{path: path, DeclarationIndex: ConfigDirIndex}
 		LoadMessage(path, &releaseConfigContribution.proto)
@@ -424,6 +449,27 @@
 		}
 	}
 
+	// Look for ignored flagging values.  Gather the entire list to make it easier to fix them.
+	errors := []string{}
+	for _, contrib := range configs.ReleaseConfigMaps {
+		dirName := filepath.Dir(contrib.path)
+		for k, names := range contrib.FlagValueDirs {
+			for _, rcName := range names {
+				if config, err := configs.GetReleaseConfig(rcName); 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",
+							filepath.Join(dirName, k, rcName), dirName, config.Name))
+					}
+				}
+
+			}
+		}
+	}
+	if len(errors) > 0 {
+		return fmt.Errorf("%s", strings.Join(errors, "\n"))
+	}
+
 	releaseConfig, err := configs.GetReleaseConfig(targetRelease)
 	if err != nil {
 		return err
diff --git a/cmd/release_config/release_config_lib/util.go b/cmd/release_config/release_config_lib/util.go
index 9919c70..b149293 100644
--- a/cmd/release_config/release_config_lib/util.go
+++ b/cmd/release_config/release_config_lib/util.go
@@ -31,8 +31,9 @@
 )
 
 var (
-	disableWarnings    bool
-	containerRegexp, _ = regexp.Compile("^[a-z][a-z0-9]*([._][a-z][a-z0-9]*)*$")
+	disableWarnings        bool
+	containerRegexp, _     = regexp.Compile("^[a-z][a-z0-9]*([._][a-z][a-z0-9]*)*$")
+	releaseConfigRegexp, _ = regexp.Compile("^[a-z][a-z0-9]*([._][a-z0-9]*)*$")
 )
 
 type StringList []string
@@ -179,6 +180,10 @@
 	return containerRegexp.MatchString(container)
 }
 
+func validReleaseConfigName(name string) bool {
+	return releaseConfigRegexp.MatchString(name)
+}
+
 // Returns the default value for release config artifacts.
 func GetDefaultOutDir() string {
 	outEnv := os.Getenv("OUT_DIR")
diff --git a/cmd/release_config/release_config_proto/build_flags_common.pb.go b/cmd/release_config/release_config_proto/build_flags_common.pb.go
index 1e927db..f8ad38f 100644
--- a/cmd/release_config/release_config_proto/build_flags_common.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_common.pb.go
@@ -15,7 +15,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: build_flags_common.proto
 
@@ -38,6 +38,8 @@
 type Workflow int32
 
 const (
+	Workflow_WORKFLOW_UNSPECIFIED Workflow = 0
+	// Deprecated.  Use WORKFLOW_UNSPECIFIED instead.
 	Workflow_Workflow_Unspecified Workflow = 0
 	// Boolean value flags that progress from false to true.
 	Workflow_LAUNCH Workflow = 1
@@ -52,12 +54,14 @@
 // Enum value maps for Workflow.
 var (
 	Workflow_name = map[int32]string{
-		0: "Workflow_Unspecified",
+		0: "WORKFLOW_UNSPECIFIED",
+		// Duplicate value: 0: "Workflow_Unspecified",
 		1: "LAUNCH",
 		2: "PREBUILT",
 		3: "MANUAL",
 	}
 	Workflow_value = map[string]int32{
+		"WORKFLOW_UNSPECIFIED": 0,
 		"Workflow_Unspecified": 0,
 		"LAUNCH":               1,
 		"PREBUILT":             2,
@@ -108,15 +112,17 @@
 	0x0a, 0x18, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x63, 0x6f,
 	0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x61, 0x6e, 0x64, 0x72,
 	0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0x4a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b,
-	0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
-	0x5f, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x10, 0x00, 0x12, 0x0a,
-	0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52,
-	0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x41, 0x4e, 0x55,
-	0x41, 0x4c, 0x10, 0x03, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
-	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0x68, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b,
+	0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57,
+	0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18,
+	0x0a, 0x14, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x55, 0x6e, 0x73, 0x70, 0x65,
+	0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e,
+	0x43, 0x48, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x54,
+	0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x1a, 0x02,
+	0x10, 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 (
@@ -133,7 +139,7 @@
 
 var file_build_flags_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_build_flags_common_proto_goTypes = []interface{}{
-	(Workflow)(0), // 0: android.release_config_proto.workflow
+	(Workflow)(0), // 0: android.release_config_proto.Workflow
 }
 var file_build_flags_common_proto_depIdxs = []int32{
 	0, // [0:0] is the sub-list for method output_type
diff --git a/cmd/release_config/release_config_proto/build_flags_common.proto b/cmd/release_config/release_config_proto/build_flags_common.proto
index d5d6101..cd9f1e0 100644
--- a/cmd/release_config/release_config_proto/build_flags_common.proto
+++ b/cmd/release_config/release_config_proto/build_flags_common.proto
@@ -20,7 +20,11 @@
 // This protobuf file defines common messages used in the rest of the build flag
 // protos.
 
-enum workflow {
+enum Workflow {
+  option allow_alias = true;
+  WORKFLOW_UNSPECIFIED = 0;
+
+  // Deprecated.  Use WORKFLOW_UNSPECIFIED instead.
   Workflow_Unspecified = 0;
 
   // Boolean value flags that progress from false to true.
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 c0573ed..7db945a 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
@@ -15,7 +15,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: build_flags_declarations.proto
 
@@ -121,7 +121,7 @@
 	if x != nil && x.Workflow != nil {
 		return *x.Workflow
 	}
-	return Workflow_Workflow_Unspecified
+	return Workflow_WORKFLOW_UNSPECIFIED
 }
 
 func (x *FlagDeclarationArtifact) GetContainers() []string {
@@ -137,7 +137,7 @@
 	unknownFields protoimpl.UnknownFields
 
 	// The artifacts
-	FlagDeclarationArtifacts []*FlagDeclarationArtifact `protobuf:"bytes,1,rep,name=flag_declaration_artifacts,json=flagDeclarationArtifacts" json:"flag_declaration_artifacts,omitempty"`
+	FlagDeclarationArtifactList []*FlagDeclarationArtifact `protobuf:"bytes,1,rep,name=flag_declaration_artifact_list,json=flagDeclarationArtifactList" json:"flag_declaration_artifact_list,omitempty"`
 }
 
 func (x *FlagDeclarationArtifacts) Reset() {
@@ -172,9 +172,9 @@
 	return file_build_flags_declarations_proto_rawDescGZIP(), []int{1}
 }
 
-func (x *FlagDeclarationArtifacts) GetFlagDeclarationArtifacts() []*FlagDeclarationArtifact {
+func (x *FlagDeclarationArtifacts) GetFlagDeclarationArtifactList() []*FlagDeclarationArtifact {
 	if x != nil {
-		return x.FlagDeclarationArtifacts
+		return x.FlagDeclarationArtifactList
 	}
 	return nil
 }
@@ -187,37 +187,37 @@
 	0x12, 0x1c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
 	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18,
 	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x6d,
-	0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8e, 0x02, 0x0a, 0x19, 0x66, 0x6c, 0x61,
-	0x67, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72,
-	0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61,
-	0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e,
-	0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
-	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
-	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65,
-	0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f,
-	0x6e, 0x50, 0x61, 0x74, 0x68, 0x12, 0x43, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
-	0x77, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
-	0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
-	0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1f, 0x0a, 0x0a, 0x63, 0x6f,
-	0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0xce, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
-	0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x4a, 0x04, 0x08, 0x04, 0x10,
-	0x05, 0x4a, 0x06, 0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01, 0x22, 0x93, 0x01, 0x0a, 0x1a, 0x66, 0x6c,
-	0x61, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61,
-	0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x12, 0x75, 0x0a, 0x1a, 0x66, 0x6c, 0x61, 0x67,
-	0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x74,
-	0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x61,
+	0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8c, 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, 0x66, 0x6c, 0x61, 0x67,
-	0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x74,
-	0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x18, 0x66, 0x6c, 0x61, 0x67, 0x44, 0x65, 0x63, 0x6c, 0x61,
-	0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x42,
-	0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
-	0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f,
-	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f,
+	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 (
@@ -234,13 +234,13 @@
 
 var file_build_flags_declarations_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
 var file_build_flags_declarations_proto_goTypes = []interface{}{
-	(*FlagDeclarationArtifact)(nil),  // 0: android.release_config_proto.flag_declaration_artifact
-	(*FlagDeclarationArtifacts)(nil), // 1: android.release_config_proto.flag_declaration_artifacts
-	(Workflow)(0),                    // 2: android.release_config_proto.workflow
+	(*FlagDeclarationArtifact)(nil),  // 0: android.release_config_proto.FlagDeclarationArtifact
+	(*FlagDeclarationArtifacts)(nil), // 1: android.release_config_proto.FlagDeclarationArtifacts
+	(Workflow)(0),                    // 2: android.release_config_proto.Workflow
 }
 var file_build_flags_declarations_proto_depIdxs = []int32{
-	2, // 0: android.release_config_proto.flag_declaration_artifact.workflow:type_name -> android.release_config_proto.workflow
-	0, // 1: android.release_config_proto.flag_declaration_artifacts.flag_declaration_artifacts:type_name -> android.release_config_proto.flag_declaration_artifact
+	2, // 0: android.release_config_proto.FlagDeclarationArtifact.workflow:type_name -> android.release_config_proto.Workflow
+	0, // 1: android.release_config_proto.FlagDeclarationArtifacts.flag_declaration_artifact_list:type_name -> android.release_config_proto.FlagDeclarationArtifact
 	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
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 e0cf099..d755e02 100644
--- a/cmd/release_config/release_config_proto/build_flags_declarations.proto
+++ b/cmd/release_config/release_config_proto/build_flags_declarations.proto
@@ -39,7 +39,7 @@
 //      com.android.mypackage is a valid name while com.android.myPackage,
 //      com.android.1mypackage are invalid
 
-message flag_declaration_artifact {
+message FlagDeclarationArtifact {
   // The name of the flag.
   // See # name for format detail
   optional string name = 1;
@@ -58,7 +58,7 @@
   optional string declaration_path = 5;
 
   // Workflow for this flag.
-  optional workflow workflow = 205;
+  optional Workflow workflow = 205;
 
   // The container for this flag.  This overrides any default container given
   // in the release_config_map message.
@@ -69,7 +69,7 @@
   reserved 207;
 }
 
-message flag_declaration_artifacts {
+message FlagDeclarationArtifacts {
   // The artifacts
-  repeated flag_declaration_artifact flag_declaration_artifacts = 1;
+  repeated FlagDeclarationArtifact flag_declaration_artifact_list = 1;
 }
diff --git a/cmd/release_config/release_config_proto/build_flags_out.pb.go b/cmd/release_config/release_config_proto/build_flags_out.pb.go
index 309ec34..60ba2e1 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
@@ -15,7 +15,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: build_flags_out.proto
 
@@ -163,7 +163,7 @@
 	unknownFields protoimpl.UnknownFields
 
 	// The artifacts
-	FlagArtifacts []*FlagArtifact `protobuf:"bytes,1,rep,name=flag_artifacts,json=flagArtifacts" json:"flag_artifacts,omitempty"`
+	Flags []*FlagArtifact `protobuf:"bytes,1,rep,name=flags" json:"flags,omitempty"`
 }
 
 func (x *FlagArtifacts) Reset() {
@@ -198,9 +198,9 @@
 	return file_build_flags_out_proto_rawDescGZIP(), []int{2}
 }
 
-func (x *FlagArtifacts) GetFlagArtifacts() []*FlagArtifact {
+func (x *FlagArtifacts) GetFlags() []*FlagArtifact {
 	if x != nil {
-		return x.FlagArtifacts
+		return x.Flags
 	}
 	return nil
 }
@@ -217,18 +217,24 @@
 	OtherNames []string `protobuf:"bytes,2,rep,name=other_names,json=otherNames" json:"other_names,omitempty"`
 	// The complete set of build flags in this release config, after all
 	// inheritance and other processing is complete.
-	FlagArtifacts []*FlagArtifact `protobuf:"bytes,3,rep,name=flag_artifacts,json=flagArtifacts" json:"flag_artifacts,omitempty"`
+	Flags []*FlagArtifact `protobuf:"bytes,3,rep,name=flags" json:"flags,omitempty"`
 	// The (complete) list of aconfig_value_sets Soong modules to use.
 	AconfigValueSets []string `protobuf:"bytes,4,rep,name=aconfig_value_sets,json=aconfigValueSets" json:"aconfig_value_sets,omitempty"`
 	// The names of the release_config_artifacts from which we inherited.
 	// Included for reference only.
 	Inherits []string `protobuf:"bytes,5,rep,name=inherits" json:"inherits,omitempty"`
-	// The release config directories used for this config.
+	// The release config directories used for this config.  This includes
+	// directories that provide flag declarations, but do not provide any flag
+	// values specific to this release config.
 	// For example, "build/release".
 	Directories []string `protobuf:"bytes,6,rep,name=directories" json:"directories,omitempty"`
 	// Prior stage(s) for flag advancement (during development).
 	// Once a flag has met criteria in a prior stage, it can advance to this one.
 	PriorStages []string `protobuf:"bytes,7,rep,name=prior_stages,json=priorStages" json:"prior_stages,omitempty"`
+	// The release config directories that contribute directly to this release
+	// 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"`
 }
 
 func (x *ReleaseConfigArtifact) Reset() {
@@ -277,9 +283,9 @@
 	return nil
 }
 
-func (x *ReleaseConfigArtifact) GetFlagArtifacts() []*FlagArtifact {
+func (x *ReleaseConfigArtifact) GetFlags() []*FlagArtifact {
 	if x != nil {
-		return x.FlagArtifacts
+		return x.Flags
 	}
 	return nil
 }
@@ -312,6 +318,13 @@
 	return nil
 }
 
+func (x *ReleaseConfigArtifact) GetValueDirectories() []string {
+	if x != nil {
+		return x.ValueDirectories
+	}
+	return nil
+}
+
 type ReleaseConfigsArtifact struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -386,87 +399,88 @@
 	0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
 	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61,
 	0x67, 0x73, 0x5f, 0x73, 0x72, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x60, 0x0a, 0x0a,
-	0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f,
+	0x54, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f,
 	0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72,
 	0x63, 0x65, 0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01,
 	0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c,
 	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xe8,
-	0x01, 0x0a, 0x0d, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
-	0x12, 0x59, 0x0a, 0x10, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61,
-	0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 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, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x64,
-	0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x66, 0x6c, 0x61, 0x67,
-	0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x05, 0x76,
-	0x61, 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e,
-	0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65,
-	0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x40, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65,
-	0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x6f, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xe6,
+	0x01, 0x0a, 0x0c, 0x46, 0x6c, 0x61, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12,
+	0x58, 0x0a, 0x10, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 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, 0x52, 0x0f, 0x66, 0x6c, 0x61, 0x67, 0x44, 0x65,
+	0x63, 0x6c, 0x61, 0x72, 0x61, 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, 0x40, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x18,
+	0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 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, 0x54, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52,
+	0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x22, 0x61, 0x0a, 0x0d, 0x46, 0x6c, 0x61, 0x67, 0x41,
+	0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x12, 0x40, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67,
+	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, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e,
-	0x74, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x22, 0x64, 0x0a, 0x0e, 0x66, 0x6c, 0x61,
-	0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x12, 0x52, 0x0a, 0x0e, 0x66,
-	0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20,
-	0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65,
-	0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
-	0x52, 0x0d, 0x66, 0x6c, 0x61, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22,
-	0xb1, 0x02, 0x0a, 0x17, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
-	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
-	0x1f, 0x0a, 0x0b, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02,
-	0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73,
-	0x12, 0x52, 0x0a, 0x0e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63,
-	0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	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,
+	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,
+	0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6f,
+	0x74, 0x68, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x05, 0x66, 0x6c, 0x61,
+	0x67, 0x73, 0x18, 0x03, 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, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74,
-	0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x0d, 0x66, 0x6c, 0x61, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66,
-	0x61, 0x63, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09,
-	0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65,
-	0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x05,
-	0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x20,
-	0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73,
-	0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73,
-	0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61,
-	0x67, 0x65, 0x73, 0x22, 0xe8, 0x03, 0x0a, 0x18, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
-	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
-	0x12, 0x5c, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
-	0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
-	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52,
-	0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x69,
+	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, 0x12, 0x2c, 0x0a, 0x12, 0x61,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x73, 0x65, 0x74,
+	0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x68,
+	0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68,
+	0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f,
+	0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65,
+	0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x6f, 0x72,
+	0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70,
+	0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 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,
+	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, 0x35, 0x2e,
+	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, 0x72, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69,
-	0x66, 0x61, 0x63, 0x74, 0x52, 0x13, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x6c, 0x65, 0x61,
-	0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x17, 0x72, 0x65,
-	0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70,
-	0x73, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x50, 0x2e, 0x61, 0x6e,
-	0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61,
-	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66,
-	0x61, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x72,
-	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73,
-	0x4d, 0x61, 0x70, 0x1a, 0x79, 0x0a, 0x19, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79,
-	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
-	0x65, 0x79, 0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x30, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65,
-	0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
-	0x6d, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x33,
-	0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
-	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72,
-	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72,
-	0x6f, 0x74, 0x6f,
+	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,
 }
 
 var (
@@ -483,27 +497,27 @@
 
 var file_build_flags_out_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
 var file_build_flags_out_proto_goTypes = []interface{}{
-	(*Tracepoint)(nil),             // 0: android.release_config_proto.tracepoint
-	(*FlagArtifact)(nil),           // 1: android.release_config_proto.flag_artifact
-	(*FlagArtifacts)(nil),          // 2: android.release_config_proto.flag_artifacts
-	(*ReleaseConfigArtifact)(nil),  // 3: android.release_config_proto.release_config_artifact
-	(*ReleaseConfigsArtifact)(nil), // 4: android.release_config_proto.release_configs_artifact
-	nil,                            // 5: android.release_config_proto.release_configs_artifact.ReleaseConfigMapsMapEntry
-	(*Value)(nil),                  // 6: android.release_config_proto.value
-	(*FlagDeclaration)(nil),        // 7: android.release_config_proto.flag_declaration
-	(*ReleaseConfigMap)(nil),       // 8: android.release_config_proto.release_config_map
+	(*Tracepoint)(nil),             // 0: android.release_config_proto.Tracepoint
+	(*FlagArtifact)(nil),           // 1: android.release_config_proto.FlagArtifact
+	(*FlagArtifacts)(nil),          // 2: android.release_config_proto.FlagArtifacts
+	(*ReleaseConfigArtifact)(nil),  // 3: android.release_config_proto.ReleaseConfigArtifact
+	(*ReleaseConfigsArtifact)(nil), // 4: android.release_config_proto.ReleaseConfigsArtifact
+	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
 }
 var file_build_flags_out_proto_depIdxs = []int32{
-	6,  // 0: android.release_config_proto.tracepoint.value:type_name -> android.release_config_proto.value
-	7,  // 1: android.release_config_proto.flag_artifact.flag_declaration:type_name -> android.release_config_proto.flag_declaration
-	6,  // 2: android.release_config_proto.flag_artifact.value:type_name -> android.release_config_proto.value
-	0,  // 3: android.release_config_proto.flag_artifact.traces:type_name -> android.release_config_proto.tracepoint
-	1,  // 4: android.release_config_proto.flag_artifacts.flag_artifacts:type_name -> android.release_config_proto.flag_artifact
-	1,  // 5: android.release_config_proto.release_config_artifact.flag_artifacts:type_name -> android.release_config_proto.flag_artifact
-	3,  // 6: android.release_config_proto.release_configs_artifact.release_config:type_name -> android.release_config_proto.release_config_artifact
-	3,  // 7: android.release_config_proto.release_configs_artifact.other_release_configs:type_name -> android.release_config_proto.release_config_artifact
-	5,  // 8: android.release_config_proto.release_configs_artifact.release_config_maps_map:type_name -> android.release_config_proto.release_configs_artifact.ReleaseConfigMapsMapEntry
-	8,  // 9: android.release_config_proto.release_configs_artifact.ReleaseConfigMapsMapEntry.value:type_name -> android.release_config_proto.release_config_map
+	6,  // 0: android.release_config_proto.Tracepoint.value:type_name -> android.release_config_proto.Value
+	7,  // 1: android.release_config_proto.FlagArtifact.flag_declaration:type_name -> android.release_config_proto.FlagDeclaration
+	6,  // 2: android.release_config_proto.FlagArtifact.value:type_name -> android.release_config_proto.Value
+	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
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 0cbc157..2ab62d1 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.proto
+++ b/cmd/release_config/release_config_proto/build_flags_out.proto
@@ -39,29 +39,30 @@
 //      com.android.mypackage is a valid name while com.android.myPackage,
 //      com.android.1mypackage are invalid
 
-message tracepoint {
+message Tracepoint {
   // Path to declaration or value file relative to $TOP
   optional string source = 1;
-  optional value value = 201;
+  optional Value value = 201;
 }
 
-message flag_artifact {
+message FlagArtifact {
   // The original declaration
-  optional flag_declaration flag_declaration = 1;
+  optional FlagDeclaration flag_declaration = 1;
 
   // Value for the flag
-  optional value value = 201;
+  optional Value value = 201;
 
   // Trace of where the flag value was assigned.
-  repeated tracepoint traces = 8;
+  repeated Tracepoint traces = 8;
 }
 
-message flag_artifacts {
+message FlagArtifacts {
   // The artifacts
-  repeated flag_artifact flag_artifacts = 1;
+  repeated FlagArtifact flags = 1;
+  reserved "flag_artifacts";
 }
 
-message release_config_artifact {
+message ReleaseConfigArtifact {
   // The name of the release config.
   // See # name for format detail
   optional string name = 1;
@@ -71,7 +72,8 @@
 
   // The complete set of build flags in this release config, after all
   // inheritance and other processing is complete.
-  repeated flag_artifact flag_artifacts = 3;
+  repeated FlagArtifact flags = 3;
+  reserved "flag_artifacts";
 
   // The (complete) list of aconfig_value_sets Soong modules to use.
   repeated string aconfig_value_sets = 4;
@@ -80,23 +82,30 @@
   // Included for reference only.
   repeated string inherits = 5;
 
-  // The release config directories used for this config.
+  // The release config directories used for this config.  This includes
+  // directories that provide flag declarations, but do not provide any flag
+  // values specific to this release config.
   // For example, "build/release".
   repeated string directories = 6;
 
   // Prior stage(s) for flag advancement (during development).
   // Once a flag has met criteria in a prior stage, it can advance to this one.
   repeated string prior_stages = 7;
+
+  // The release config directories that contribute directly to this release
+  // config.  The listed directories contain at least a `release_config` message
+  // for this release config.
+  repeated string value_directories = 8;
 }
 
-message release_configs_artifact {
+message ReleaseConfigsArtifact {
   // The active release config for this build.
-  optional release_config_artifact release_config = 1;
+  optional ReleaseConfigArtifact release_config = 1;
 
   // All other release configs defined for this TARGET_PRODUCT.
-  repeated release_config_artifact other_release_configs = 2;
+  repeated ReleaseConfigArtifact other_release_configs = 2;
 
   // Map of release_config_artifact.directories to release_config_map message.
-  map<string, release_config_map> release_config_maps_map = 3;
+  map<string, ReleaseConfigMap> release_config_maps_map = 3;
 }
 
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 8de340e..d784dee 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
@@ -15,7 +15,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: build_flags_src.proto
 
@@ -232,7 +232,7 @@
 	if x != nil && x.Workflow != nil {
 		return *x.Workflow
 	}
-	return Workflow_Workflow_Unspecified
+	return Workflow_WORKFLOW_UNSPECIFIED
 }
 
 func (x *FlagDeclaration) GetContainers() []string {
@@ -531,7 +531,7 @@
 	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,
-	0xa5, 0x01, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2e, 0x0a, 0x11, 0x75, 0x6e, 0x73,
+	0xa5, 0x01, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2e, 0x0a, 0x11, 0x75, 0x6e, 0x73,
 	0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc8,
 	0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x10, 0x75, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69,
 	0x66, 0x69, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x24, 0x0a, 0x0c, 0x73, 0x74, 0x72,
@@ -541,62 +541,62 @@
 	0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75,
 	0x65, 0x12, 0x1d, 0x0a, 0x08, 0x6f, 0x62, 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x18, 0xcb, 0x01,
 	0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x08, 0x6f, 0x62, 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65,
-	0x42, 0x05, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x96, 0x02, 0x0a, 0x10, 0x66, 0x6c, 0x61, 0x67,
-	0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04,
-	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-	0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x20,
-	0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x42, 0x05, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x95, 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,
+	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,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x43, 0x0a, 0x08,
-	0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
-	0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
-	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x77,
-	0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
-	0x77, 0x12, 0x1f, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18,
-	0xce, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
-	0x72, 0x73, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x06, 0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01,
-	0x22, 0x79, 0x0a, 0x0a, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12,
-	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b,
-	0x0a, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28,
-	0x08, 0x52, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x22, 0xbf, 0x01, 0x0a, 0x0e,
-	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12,
-	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x02,
-	0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x2c,
-	0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f,
-	0x73, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12,
-	0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x6f, 0x6e,
-	0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72,
-	0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
-	0x52, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x22, 0x3b, 0x0a,
-	0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12,
-	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x12, 0x72,
-	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61,
-	0x70, 0x12, 0x45, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x52,
-	0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
-	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
-	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65,
-	0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73,
-	0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43,
-	0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64,
-	0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61,
-	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
-	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	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,
 }
 
 var (
@@ -613,19 +613,19 @@
 
 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.flag_declaration
-	(*FlagValue)(nil),        // 2: android.release_config_proto.flag_value
-	(*ReleaseConfig)(nil),    // 3: android.release_config_proto.release_config
-	(*ReleaseAlias)(nil),     // 4: android.release_config_proto.release_alias
-	(*ReleaseConfigMap)(nil), // 5: android.release_config_proto.release_config_map
-	(Workflow)(0),            // 6: android.release_config_proto.workflow
+	(*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
 }
 var file_build_flags_src_proto_depIdxs = []int32{
-	0, // 0: android.release_config_proto.flag_declaration.value:type_name -> android.release_config_proto.value
-	6, // 1: android.release_config_proto.flag_declaration.workflow:type_name -> android.release_config_proto.workflow
-	0, // 2: android.release_config_proto.flag_value.value:type_name -> android.release_config_proto.value
-	4, // 3: android.release_config_proto.release_config_map.aliases:type_name -> android.release_config_proto.release_alias
+	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
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 4fad478..e1925bc 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.proto
+++ b/cmd/release_config/release_config_proto/build_flags_src.proto
@@ -44,7 +44,7 @@
 //      com.android.mypackage is a valid name while com.android.myPackage,
 //      com.android.1mypackage are invalid
 
-message value {
+message Value {
   oneof val {
     bool unspecified_value = 200;
     string string_value = 201;
@@ -55,7 +55,7 @@
 }
 
 // The proto used in the source tree.
-message flag_declaration {
+message FlagDeclaration {
   // The name of the flag.
   // See # name for format detail
   optional string name = 1;
@@ -71,10 +71,10 @@
   reserved 4;
 
   // Value for the flag
-  optional value value = 201;
+  optional Value value = 201;
 
   // Workflow for this flag.
-  optional workflow workflow = 205;
+  optional Workflow workflow = 205;
 
   // The container for this flag.  This overrides any default container given
   // in the release_config_map message.
@@ -85,13 +85,13 @@
   reserved 207;
 }
 
-message flag_value {
+message FlagValue {
   // Name of the flag.
   // See # name for format detail
   optional string name = 2;
 
   // Value for the flag
-  optional value value = 201;
+  optional Value value = 201;
 
   // If true, the flag is completely removed from the release config as if
   // never declared.
@@ -99,7 +99,7 @@
 }
 
 // This replaces $(call declare-release-config).
-message release_config {
+message ReleaseConfig {
   // The name of the release config.
   // See # name for format detail
   optional string name = 1;
@@ -120,7 +120,7 @@
 }
 
 // Any aliases.  These are used for continuous integration builder config.
-message release_alias {
+message ReleaseAlias {
   // The name of the alias.
   optional string name = 1;
 
@@ -129,9 +129,9 @@
 }
 
 // This provides the data from release_config_map.mk
-message release_config_map {
+message ReleaseConfigMap {
   // Any aliases.
-  repeated release_alias aliases = 1;
+  repeated ReleaseAlias aliases = 1;
 
   // Description of this map and its intended use.
   optional string description = 2;
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index e69a930..f3931a4 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -27,6 +27,7 @@
 	"os"
 	"os/exec"
 	"path/filepath"
+	"regexp"
 	"strconv"
 	"strings"
 	"time"
@@ -51,6 +52,8 @@
 	sandboxDirPlaceholder = "__SBOX_SANDBOX_DIR__"
 )
 
+var envVarNameRegex = regexp.MustCompile("^[a-zA-Z0-9_-]+$")
+
 func init() {
 	flag.StringVar(&sandboxesRoot, "sandbox-path", "",
 		"root of temp directory to put the sandbox into")
@@ -238,6 +241,51 @@
 	return &manifest, nil
 }
 
+func createEnv(command *sbox_proto.Command) ([]string, error) {
+	env := []string{}
+	if command.DontInheritEnv == nil || !*command.DontInheritEnv {
+		env = os.Environ()
+	}
+	for _, envVar := range command.Env {
+		if envVar.Name == nil || !envVarNameRegex.MatchString(*envVar.Name) {
+			name := "nil"
+			if envVar.Name != nil {
+				name = *envVar.Name
+			}
+			return nil, fmt.Errorf("Invalid environment variable name: %q", name)
+		}
+		if envVar.State == nil {
+			return nil, fmt.Errorf("Must set state")
+		}
+		switch state := envVar.State.(type) {
+		case *sbox_proto.EnvironmentVariable_Value:
+			env = append(env, *envVar.Name+"="+state.Value)
+		case *sbox_proto.EnvironmentVariable_Unset:
+			if !state.Unset {
+				return nil, fmt.Errorf("Can't have unset set to false")
+			}
+			prefix := *envVar.Name + "="
+			for i := 0; i < len(env); i++ {
+				if strings.HasPrefix(env[i], prefix) {
+					env = append(env[:i], env[i+1:]...)
+					i--
+				}
+			}
+		case *sbox_proto.EnvironmentVariable_Inherit:
+			if !state.Inherit {
+				return nil, fmt.Errorf("Can't have inherit set to false")
+			}
+			val, ok := os.LookupEnv(*envVar.Name)
+			if ok {
+				env = append(env, *envVar.Name+"="+val)
+			}
+		default:
+			return nil, fmt.Errorf("Unhandled state type")
+		}
+	}
+	return env, nil
+}
+
 // runCommand runs a single command from a manifest.  If the command references the
 // __SBOX_DEPFILE__ placeholder it returns the name of the depfile that was used.
 func runCommand(command *sbox_proto.Command, tempDir string, commandIndex int) (depFile string, err error) {
@@ -313,6 +361,12 @@
 			return "", fmt.Errorf("Failed to update PATH: %w", err)
 		}
 	}
+
+	cmd.Env, err = createEnv(command)
+	if err != nil {
+		return "", err
+	}
+
 	err = cmd.Run()
 
 	if err != nil {
diff --git a/cmd/sbox/sbox_proto/sbox.pb.go b/cmd/sbox/sbox_proto/sbox.pb.go
index 7c84f2c..271039c 100644
--- a/cmd/sbox/sbox_proto/sbox.pb.go
+++ b/cmd/sbox/sbox_proto/sbox.pb.go
@@ -14,8 +14,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.26.0
-// 	protoc        v3.9.1
+// 	protoc-gen-go v1.33.0
+// 	protoc        v3.21.12
 // source: sbox.proto
 
 package sbox_proto
@@ -116,6 +116,13 @@
 	// A list of files that will be copied before the sandboxed command, and whose contents should be
 	// copied as if they were listed in copy_before.
 	RspFiles []*RspFile `protobuf:"bytes,6,rep,name=rsp_files,json=rspFiles" json:"rsp_files,omitempty"`
+	// The environment variables that will be set or unset while running the command.
+	// Also see dont_inherit_env.
+	Env []*EnvironmentVariable `protobuf:"bytes,7,rep,name=env" json:"env,omitempty"`
+	// By default, all environment variables are inherited from the calling process, but may be
+	// replaced or unset by env. If dont_inherit_env is set, no environment variables will be
+	// inherited, and instead only the variables in env will be defined.
+	DontInheritEnv *bool `protobuf:"varint,8,opt,name=dont_inherit_env,json=dontInheritEnv" json:"dont_inherit_env,omitempty"`
 }
 
 func (x *Command) Reset() {
@@ -192,6 +199,129 @@
 	return nil
 }
 
+func (x *Command) GetEnv() []*EnvironmentVariable {
+	if x != nil {
+		return x.Env
+	}
+	return nil
+}
+
+func (x *Command) GetDontInheritEnv() bool {
+	if x != nil && x.DontInheritEnv != nil {
+		return *x.DontInheritEnv
+	}
+	return false
+}
+
+type EnvironmentVariable struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the environment variable
+	Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
+	// Types that are assignable to State:
+	//
+	//	*EnvironmentVariable_Value
+	//	*EnvironmentVariable_Unset
+	//	*EnvironmentVariable_Inherit
+	State isEnvironmentVariable_State `protobuf_oneof:"state"`
+}
+
+func (x *EnvironmentVariable) Reset() {
+	*x = EnvironmentVariable{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_sbox_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *EnvironmentVariable) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*EnvironmentVariable) ProtoMessage() {}
+
+func (x *EnvironmentVariable) ProtoReflect() protoreflect.Message {
+	mi := &file_sbox_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 EnvironmentVariable.ProtoReflect.Descriptor instead.
+func (*EnvironmentVariable) Descriptor() ([]byte, []int) {
+	return file_sbox_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *EnvironmentVariable) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (m *EnvironmentVariable) GetState() isEnvironmentVariable_State {
+	if m != nil {
+		return m.State
+	}
+	return nil
+}
+
+func (x *EnvironmentVariable) GetValue() string {
+	if x, ok := x.GetState().(*EnvironmentVariable_Value); ok {
+		return x.Value
+	}
+	return ""
+}
+
+func (x *EnvironmentVariable) GetUnset() bool {
+	if x, ok := x.GetState().(*EnvironmentVariable_Unset); ok {
+		return x.Unset
+	}
+	return false
+}
+
+func (x *EnvironmentVariable) GetInherit() bool {
+	if x, ok := x.GetState().(*EnvironmentVariable_Inherit); ok {
+		return x.Inherit
+	}
+	return false
+}
+
+type isEnvironmentVariable_State interface {
+	isEnvironmentVariable_State()
+}
+
+type EnvironmentVariable_Value struct {
+	// The value to set the environment variable to.
+	Value string `protobuf:"bytes,2,opt,name=value,oneof"`
+}
+
+type EnvironmentVariable_Unset struct {
+	// This environment variable should be unset in the command.
+	Unset bool `protobuf:"varint,3,opt,name=unset,oneof"`
+}
+
+type EnvironmentVariable_Inherit struct {
+	// This environment variable should be inherited from the parent process.
+	// Can be combined with dont_inherit_env to only inherit certain environment
+	// variables.
+	Inherit bool `protobuf:"varint,4,opt,name=inherit,oneof"`
+}
+
+func (*EnvironmentVariable_Value) isEnvironmentVariable_State() {}
+
+func (*EnvironmentVariable_Unset) isEnvironmentVariable_State() {}
+
+func (*EnvironmentVariable_Inherit) isEnvironmentVariable_State() {}
+
 // Copy describes a from-to pair of files to copy.  The paths may be relative, the root that they
 // are relative to is specific to the context the Copy is used in and will be different for
 // from and to.
@@ -209,7 +339,7 @@
 func (x *Copy) Reset() {
 	*x = Copy{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_sbox_proto_msgTypes[2]
+		mi := &file_sbox_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -222,7 +352,7 @@
 func (*Copy) ProtoMessage() {}
 
 func (x *Copy) ProtoReflect() protoreflect.Message {
-	mi := &file_sbox_proto_msgTypes[2]
+	mi := &file_sbox_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -235,7 +365,7 @@
 
 // Deprecated: Use Copy.ProtoReflect.Descriptor instead.
 func (*Copy) Descriptor() ([]byte, []int) {
-	return file_sbox_proto_rawDescGZIP(), []int{2}
+	return file_sbox_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *Copy) GetFrom() string {
@@ -274,7 +404,7 @@
 func (x *RspFile) Reset() {
 	*x = RspFile{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_sbox_proto_msgTypes[3]
+		mi := &file_sbox_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -287,7 +417,7 @@
 func (*RspFile) ProtoMessage() {}
 
 func (x *RspFile) ProtoReflect() protoreflect.Message {
-	mi := &file_sbox_proto_msgTypes[3]
+	mi := &file_sbox_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -300,7 +430,7 @@
 
 // Deprecated: Use RspFile.ProtoReflect.Descriptor instead.
 func (*RspFile) Descriptor() ([]byte, []int) {
-	return file_sbox_proto_rawDescGZIP(), []int{3}
+	return file_sbox_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *RspFile) GetFile() string {
@@ -330,7 +460,7 @@
 func (x *PathMapping) Reset() {
 	*x = PathMapping{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_sbox_proto_msgTypes[4]
+		mi := &file_sbox_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -343,7 +473,7 @@
 func (*PathMapping) ProtoMessage() {}
 
 func (x *PathMapping) ProtoReflect() protoreflect.Message {
-	mi := &file_sbox_proto_msgTypes[4]
+	mi := &file_sbox_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -356,7 +486,7 @@
 
 // Deprecated: Use PathMapping.ProtoReflect.Descriptor instead.
 func (*PathMapping) Descriptor() ([]byte, []int) {
-	return file_sbox_proto_rawDescGZIP(), []int{4}
+	return file_sbox_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *PathMapping) GetFrom() string {
@@ -383,7 +513,7 @@
 	0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x75, 0x74,
 	0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x70, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
 	0x09, 0x52, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x44, 0x65, 0x70, 0x66, 0x69, 0x6c, 0x65,
-	0x22, 0xdc, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x2b, 0x0a, 0x0b,
+	0x22, 0xb3, 0x02, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x2b, 0x0a, 0x0b,
 	0x63, 0x6f, 0x70, 0x79, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28,
 	0x0b, 0x32, 0x0a, 0x2e, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x0a, 0x63,
 	0x6f, 0x70, 0x79, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x64,
@@ -396,23 +526,37 @@
 	0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x48,
 	0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x09, 0x72, 0x73, 0x70, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73,
 	0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x52, 0x73,
-	0x70, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x72, 0x73, 0x70, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22,
-	0x4a, 0x0a, 0x04, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18,
-	0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74,
-	0x6f, 0x18, 0x02, 0x20, 0x02, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x65,
-	0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
-	0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x55, 0x0a, 0x07, 0x52,
-	0x73, 0x70, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01,
-	0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x36, 0x0a, 0x0d, 0x70, 0x61,
-	0x74, 0x68, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
-	0x0b, 0x32, 0x11, 0x2e, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x4d, 0x61, 0x70,
-	0x70, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x70, 0x61, 0x74, 0x68, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
-	0x67, 0x73, 0x22, 0x31, 0x0a, 0x0b, 0x50, 0x61, 0x74, 0x68, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
-	0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52,
-	0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x02, 0x28,
-	0x09, 0x52, 0x02, 0x74, 0x6f, 0x42, 0x23, 0x5a, 0x21, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-	0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x73, 0x62, 0x6f, 0x78, 0x2f,
-	0x73, 0x62, 0x6f, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x70, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x72, 0x73, 0x70, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12,
+	0x2b, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73,
+	0x62, 0x6f, 0x78, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56,
+	0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x28, 0x0a, 0x10,
+	0x64, 0x6f, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x5f, 0x65, 0x6e, 0x76,
+	0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x6f, 0x6e, 0x74, 0x49, 0x6e, 0x68, 0x65,
+	0x72, 0x69, 0x74, 0x45, 0x6e, 0x76, 0x22, 0x7e, 0x0a, 0x13, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f,
+	0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a,
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
+	0x65, 0x12, 0x16, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x48, 0x00, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x05, 0x75, 0x6e, 0x73,
+	0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x05, 0x75, 0x6e, 0x73, 0x65,
+	0x74, 0x12, 0x1a, 0x0a, 0x07, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01,
+	0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x42, 0x07, 0x0a,
+	0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x4a, 0x0a, 0x04, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x12,
+	0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72,
+	0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x02, 0x28, 0x09, 0x52, 0x02,
+	0x74, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62,
+	0x6c, 0x65, 0x22, 0x55, 0x0a, 0x07, 0x52, 0x73, 0x70, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a,
+	0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c,
+	0x65, 0x12, 0x36, 0x0a, 0x0d, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e,
+	0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x73, 0x62, 0x6f, 0x78, 0x2e,
+	0x50, 0x61, 0x74, 0x68, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x70, 0x61, 0x74,
+	0x68, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x31, 0x0a, 0x0b, 0x50, 0x61, 0x74,
+	0x68, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d,
+	0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02,
+	0x74, 0x6f, 0x18, 0x02, 0x20, 0x02, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x42, 0x23, 0x5a, 0x21,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6d,
+	0x64, 0x2f, 0x73, 0x62, 0x6f, 0x78, 0x2f, 0x73, 0x62, 0x6f, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f,
 }
 
 var (
@@ -427,25 +571,27 @@
 	return file_sbox_proto_rawDescData
 }
 
-var file_sbox_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_sbox_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
 var file_sbox_proto_goTypes = []interface{}{
-	(*Manifest)(nil),    // 0: sbox.Manifest
-	(*Command)(nil),     // 1: sbox.Command
-	(*Copy)(nil),        // 2: sbox.Copy
-	(*RspFile)(nil),     // 3: sbox.RspFile
-	(*PathMapping)(nil), // 4: sbox.PathMapping
+	(*Manifest)(nil),            // 0: sbox.Manifest
+	(*Command)(nil),             // 1: sbox.Command
+	(*EnvironmentVariable)(nil), // 2: sbox.EnvironmentVariable
+	(*Copy)(nil),                // 3: sbox.Copy
+	(*RspFile)(nil),             // 4: sbox.RspFile
+	(*PathMapping)(nil),         // 5: sbox.PathMapping
 }
 var file_sbox_proto_depIdxs = []int32{
 	1, // 0: sbox.Manifest.commands:type_name -> sbox.Command
-	2, // 1: sbox.Command.copy_before:type_name -> sbox.Copy
-	2, // 2: sbox.Command.copy_after:type_name -> sbox.Copy
-	3, // 3: sbox.Command.rsp_files:type_name -> sbox.RspFile
-	4, // 4: sbox.RspFile.path_mappings:type_name -> sbox.PathMapping
-	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
+	3, // 1: sbox.Command.copy_before:type_name -> sbox.Copy
+	3, // 2: sbox.Command.copy_after:type_name -> sbox.Copy
+	4, // 3: sbox.Command.rsp_files:type_name -> sbox.RspFile
+	2, // 4: sbox.Command.env:type_name -> sbox.EnvironmentVariable
+	5, // 5: sbox.RspFile.path_mappings:type_name -> sbox.PathMapping
+	6, // [6:6] is the sub-list for method output_type
+	6, // [6:6] is the sub-list for method input_type
+	6, // [6:6] is the sub-list for extension type_name
+	6, // [6:6] is the sub-list for extension extendee
+	0, // [0:6] is the sub-list for field type_name
 }
 
 func init() { file_sbox_proto_init() }
@@ -479,7 +625,7 @@
 			}
 		}
 		file_sbox_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Copy); i {
+			switch v := v.(*EnvironmentVariable); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -491,7 +637,7 @@
 			}
 		}
 		file_sbox_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*RspFile); i {
+			switch v := v.(*Copy); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -503,6 +649,18 @@
 			}
 		}
 		file_sbox_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*RspFile); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_sbox_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*PathMapping); i {
 			case 0:
 				return &v.state
@@ -515,13 +673,18 @@
 			}
 		}
 	}
+	file_sbox_proto_msgTypes[2].OneofWrappers = []interface{}{
+		(*EnvironmentVariable_Value)(nil),
+		(*EnvironmentVariable_Unset)(nil),
+		(*EnvironmentVariable_Inherit)(nil),
+	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_sbox_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   5,
+			NumMessages:   6,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/cmd/sbox/sbox_proto/sbox.proto b/cmd/sbox/sbox_proto/sbox.proto
index 2f0dcf0..1158554 100644
--- a/cmd/sbox/sbox_proto/sbox.proto
+++ b/cmd/sbox/sbox_proto/sbox.proto
@@ -51,6 +51,30 @@
   // A list of files that will be copied before the sandboxed command, and whose contents should be
   // copied as if they were listed in copy_before.
   repeated RspFile rsp_files = 6;
+
+  // The environment variables that will be set or unset while running the command.
+  // Also see dont_inherit_env.
+  repeated EnvironmentVariable env = 7;
+
+  // By default, all environment variables are inherited from the calling process, but may be
+  // replaced or unset by env. If dont_inherit_env is set, no environment variables will be
+  // inherited, and instead only the variables in env will be defined.
+  optional bool dont_inherit_env = 8;
+}
+
+message EnvironmentVariable {
+  // The name of the environment variable
+  required string name = 1;
+  oneof state {
+    // The value to set the environment variable to.
+    string value = 2;
+    // This environment variable should be unset in the command.
+    bool unset = 3;
+    // This environment variable should be inherited from the parent process.
+    // Can be combined with dont_inherit_env to only inherit certain environment
+    // variables.
+    bool inherit = 4;
+  }
 }
 
 // Copy describes a from-to pair of files to copy.  The paths may be relative, the root that they
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 3dac8bd..577c6cc 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -15,7 +15,7 @@
 package main
 
 import (
-	"bytes"
+	"encoding/json"
 	"errors"
 	"flag"
 	"fmt"
@@ -33,6 +33,8 @@
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/deptools"
 	"github.com/google/blueprint/metrics"
+	"github.com/google/blueprint/pathtools"
+	"github.com/google/blueprint/proptools"
 	androidProtobuf "google.golang.org/protobuf/android"
 )
 
@@ -41,22 +43,26 @@
 	availableEnvFile string
 	usedEnvFile      string
 
-	globFile    string
-	globListDir string
 	delveListen string
 	delvePath   string
 
 	cmdlineArgs android.CmdArgs
 )
 
+const configCacheFile = "config.cache"
+
+type ConfigCache struct {
+	EnvDepsHash                  uint64
+	ProductVariableFileTimestamp int64
+	SoongBuildFileTimestamp      int64
+}
+
 func init() {
 	// Flags that make sense in every mode
 	flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree")
 	flag.StringVar(&cmdlineArgs.SoongOutDir, "soong_out", "", "Soong output directory (usually $TOP/out/soong)")
 	flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables")
 	flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
-	flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
-	flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
 	flag.StringVar(&cmdlineArgs.OutDir, "out", "", "the ninja builddir directory")
 	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
 
@@ -82,6 +88,7 @@
 	// Flags that probably shouldn't be flags of soong_build, but we haven't found
 	// the time to remove them yet
 	flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
+	flag.BoolVar(&cmdlineArgs.IncrementalBuildActions, "incremental-build-actions", false, "generate build actions incrementally")
 
 	// Disable deterministic randomization in the protobuf package, so incremental
 	// builds with unrelated Soong changes don't trigger large rebuilds (since we
@@ -196,20 +203,6 @@
 	ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile)
 }
 
-func writeBuildGlobsNinjaFile(ctx *android.Context) {
-	ctx.EventHandler.Begin("globs_ninja_file")
-	defer ctx.EventHandler.End("globs_ninja_file")
-
-	globDir := bootstrap.GlobDirectory(ctx.Config().SoongOutDir(), globListDir)
-	err := bootstrap.WriteBuildGlobsNinjaFile(&bootstrap.GlobSingleton{
-		GlobLister: ctx.Globs,
-		GlobFile:   globFile,
-		GlobDir:    globDir,
-		SrcDir:     ctx.SrcDir(),
-	}, ctx.Config())
-	maybeQuit(err, "")
-}
-
 func writeDepFile(outputFile string, eventHandler *metrics.EventHandler, ninjaDeps []string) {
 	eventHandler.Begin("ninja_deps")
 	defer eventHandler.End("ninja_deps")
@@ -218,8 +211,71 @@
 	maybeQuit(err, "error writing depfile '%s'", depFile)
 }
 
+// Check if there are changes to the environment file, product variable file and
+// soong_build binary, in which case no incremental will be performed. For env
+// variables we check the used env file, which will be removed in soong ui if
+// there is any changes to the env variables used last time, in which case the
+// check below will fail and a full build will be attempted. If any new env
+// variables are added in the new run, soong ui won't be able to detect it, the
+// used env file check below will pass. But unless there is a soong build code
+// change, in which case the soong build binary check will fail, otherwise the
+// new env variables shouldn't have any affect.
+func incrementalValid(config android.Config, configCacheFile string) (*ConfigCache, bool) {
+	var newConfigCache ConfigCache
+	data, err := os.ReadFile(shared.JoinPath(topDir, usedEnvFile))
+	if err != nil {
+		// Clean build
+		if os.IsNotExist(err) {
+			data = []byte{}
+		} else {
+			maybeQuit(err, "")
+		}
+	}
+
+	newConfigCache.EnvDepsHash, err = proptools.CalculateHash(data)
+	newConfigCache.ProductVariableFileTimestamp = getFileTimestamp(filepath.Join(topDir, cmdlineArgs.SoongVariables))
+	newConfigCache.SoongBuildFileTimestamp = getFileTimestamp(filepath.Join(topDir, config.HostToolDir(), "soong_build"))
+	//TODO(b/344917959): out/soong/dexpreopt.config might need to be checked as well.
+
+	file, err := os.Open(configCacheFile)
+	if err != nil && os.IsNotExist(err) {
+		return &newConfigCache, false
+	}
+	maybeQuit(err, "")
+	defer file.Close()
+
+	var configCache ConfigCache
+	decoder := json.NewDecoder(file)
+	err = decoder.Decode(&configCache)
+	maybeQuit(err, "")
+
+	return &newConfigCache, newConfigCache == configCache
+}
+
+func getFileTimestamp(file string) int64 {
+	stat, err := os.Stat(file)
+	if err == nil {
+		return stat.ModTime().UnixMilli()
+	} else if !os.IsNotExist(err) {
+		maybeQuit(err, "")
+	}
+	return 0
+}
+
+func writeConfigCache(configCache *ConfigCache, configCacheFile string) {
+	file, err := os.Create(configCacheFile)
+	maybeQuit(err, "")
+	defer file.Close()
+
+	encoder := json.NewEncoder(file)
+	err = encoder.Encode(*configCache)
+	maybeQuit(err, "")
+}
+
 // runSoongOnlyBuild runs the standard Soong build in a number of different modes.
-func runSoongOnlyBuild(ctx *android.Context, extraNinjaDeps []string) string {
+// It returns the path to the output file (usually the ninja file) and the deps that need
+// to trigger a soong rerun.
+func runSoongOnlyBuild(ctx *android.Context) (string, []string) {
 	ctx.EventHandler.Begin("soong_build")
 	defer ctx.EventHandler.End("soong_build")
 
@@ -235,37 +291,30 @@
 
 	ninjaDeps, err := bootstrap.RunBlueprint(cmdlineArgs.Args, stopBefore, ctx.Context, ctx.Config())
 	maybeQuit(err, "")
-	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
-
-	writeBuildGlobsNinjaFile(ctx)
 
 	// 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)
-		writeDepFile(queryviewMarkerFile, ctx.EventHandler, ninjaDeps)
-		return queryviewMarkerFile
+		return queryviewMarkerFile, ninjaDeps
 	case android.GenerateModuleGraph:
 		writeJsonModuleGraphAndActions(ctx, cmdlineArgs)
-		writeDepFile(cmdlineArgs.ModuleGraphFile, ctx.EventHandler, ninjaDeps)
-		return cmdlineArgs.ModuleGraphFile
+		return cmdlineArgs.ModuleGraphFile, ninjaDeps
 	case android.GenerateDocFile:
 		// TODO: we could make writeDocs() return the list of documentation files
 		// written and add them to the .d file. Then soong_docs would be re-run
 		// whenever one is deleted.
 		err := writeDocs(ctx, shared.JoinPath(topDir, cmdlineArgs.DocFile))
 		maybeQuit(err, "error building Soong documentation")
-		writeDepFile(cmdlineArgs.DocFile, ctx.EventHandler, ninjaDeps)
-		return cmdlineArgs.DocFile
+		return cmdlineArgs.DocFile, ninjaDeps
 	default:
 		// The actual output (build.ninja) was written in the RunBlueprint() call
 		// above
-		writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
 		if needToWriteNinjaHint(ctx) {
 			writeNinjaHint(ctx)
 		}
-		return cmdlineArgs.OutFile
+		return cmdlineArgs.OutFile, ninjaDeps
 	}
 }
 
@@ -295,6 +344,8 @@
 func main() {
 	flag.Parse()
 
+	soongStartTime := time.Now()
+
 	shared.ReexecWithDelveMaybe(delveListen, delvePath)
 	android.InitSandbox(topDir)
 
@@ -305,13 +356,6 @@
 		configuration.SetAllowMissingDependencies()
 	}
 
-	extraNinjaDeps := []string{configuration.ProductVariablesFileName, usedEnvFile}
-	if shared.IsDebugging() {
-		// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
-		// enabled even if it completed successfully.
-		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve"))
-	}
-
 	// Bypass configuration.Getenv, as LOG_DIR does not need to be dependency tracked. By definition, it will
 	// change between every CI build, so tracking it would require re-running Soong for every build.
 	metricsDir := availableEnv["LOG_DIR"]
@@ -319,12 +363,42 @@
 	ctx := newContext(configuration)
 	android.StartBackgroundMetrics(configuration)
 
+	var configCache *ConfigCache
+	configFile := filepath.Join(topDir, ctx.Config().OutDir(), configCacheFile)
+	incremental := false
+	ctx.SetIncrementalEnabled(cmdlineArgs.IncrementalBuildActions)
+	if cmdlineArgs.IncrementalBuildActions {
+		configCache, incremental = incrementalValid(ctx.Config(), configFile)
+	}
+	ctx.SetIncrementalAnalysis(incremental)
+
 	ctx.Register()
-	finalOutputFile := runSoongOnlyBuild(ctx, extraNinjaDeps)
+	finalOutputFile, ninjaDeps := runSoongOnlyBuild(ctx)
+
+	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
+		// enabled even if it completed successfully.
+		ninjaDeps = append(ninjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve"))
+	}
+
+	writeDepFile(finalOutputFile, ctx.EventHandler, ninjaDeps)
+
+	if ctx.GetIncrementalEnabled() {
+		data, err := shared.EnvFileContents(configuration.EnvDeps())
+		maybeQuit(err, "")
+		configCache.EnvDepsHash, err = proptools.CalculateHash(data)
+		maybeQuit(err, "")
+		writeConfigCache(configCache, configFile)
+	}
+
 	writeMetrics(configuration, ctx.EventHandler, metricsDir)
 
 	writeUsedEnvironmentFile(configuration)
 
+	err = writeGlobFile(ctx.EventHandler, finalOutputFile, ctx.Globs(), soongStartTime)
+	maybeQuit(err, "")
+
 	// Touch the output file so that it's the newest file created by soong_build.
 	// This is necessary because, if soong_build generated any files which
 	// are ninja inputs to the main output file, then ninja would superfluously
@@ -341,18 +415,33 @@
 	data, err := shared.EnvFileContents(configuration.EnvDeps())
 	maybeQuit(err, "error writing used environment file '%s'\n", usedEnvFile)
 
-	if preexistingData, err := os.ReadFile(path); err != nil {
-		if !os.IsNotExist(err) {
-			maybeQuit(err, "error reading used environment file '%s'", usedEnvFile)
-		}
-	} else if bytes.Equal(preexistingData, data) {
-		// used environment file is unchanged
-		return
-	}
-	err = os.WriteFile(path, data, 0666)
+	err = pathtools.WriteFileIfChanged(path, data, 0666)
 	maybeQuit(err, "error writing used environment file '%s'", usedEnvFile)
 }
 
+func writeGlobFile(eventHandler *metrics.EventHandler, finalOutFile string, globs pathtools.MultipleGlobResults, soongStartTime time.Time) error {
+	eventHandler.Begin("writeGlobFile")
+	defer eventHandler.End("writeGlobFile")
+
+	globsFile, err := os.Create(shared.JoinPath(topDir, finalOutFile+".globs"))
+	if err != nil {
+		return err
+	}
+	defer globsFile.Close()
+	globsFileEncoder := json.NewEncoder(globsFile)
+	for _, glob := range globs {
+		if err := globsFileEncoder.Encode(glob); err != nil {
+			return err
+		}
+	}
+
+	return os.WriteFile(
+		shared.JoinPath(topDir, finalOutFile+".globs_time"),
+		[]byte(fmt.Sprintf("%d\n", soongStartTime.UnixMicro())),
+		0666,
+	)
+}
+
 func touch(path string) {
 	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
 	maybeQuit(err, "Error touching '%s'", path)
diff --git a/cmd/symbols_map/Android.bp b/cmd/symbols_map/Android.bp
index e3ae6ed..272e806 100644
--- a/cmd/symbols_map/Android.bp
+++ b/cmd/symbols_map/Android.bp
@@ -30,4 +30,5 @@
     srcs: [
         "symbols_map_proto/symbols_map.pb.go",
     ],
+    visibility: ["//visibility:public"],
 }
diff --git a/cmd/zip2zip/Android.bp b/cmd/zip2zip/Android.bp
index 3ef7668..7f9b165 100644
--- a/cmd/zip2zip/Android.bp
+++ b/cmd/zip2zip/Android.bp
@@ -27,4 +27,6 @@
         "zip2zip.go",
     ],
     testSrcs: ["zip2zip_test.go"],
+    // Used by genrules
+    visibility: ["//visibility:public"],
 }
diff --git a/compliance/Android.bp b/compliance/Android.bp
new file mode 100644
index 0000000..08736b4
--- /dev/null
+++ b/compliance/Android.bp
@@ -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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-compliance",
+    pkgPath: "android/soong/compliance",
+    deps: [
+        "soong-android",
+    ],
+    srcs: [
+        "notice.go",
+    ],
+    testSrcs: [
+    ],
+    pluginFor: ["soong_build"],
+}
+
+notice_xml {
+    name: "notice_xml_system",
+    partition_name: "system",
+    visibility: [
+        "//device/google/cuttlefish/system_image",
+    ],
+}
diff --git a/compliance/license_metadata_proto/Android.bp b/compliance/license_metadata_proto/Android.bp
index 3c041e4..4761285 100644
--- a/compliance/license_metadata_proto/Android.bp
+++ b/compliance/license_metadata_proto/Android.bp
@@ -24,4 +24,8 @@
         "golang-protobuf-reflect-protoreflect",
         "golang-protobuf-runtime-protoimpl",
     ],
+    visibility: [
+        "//build/make/tools/compliance:__subpackages__",
+        "//build/soong:__subpackages__",
+    ],
 }
diff --git a/compliance/notice.go b/compliance/notice.go
new file mode 100644
index 0000000..4fc83ab
--- /dev/null
+++ b/compliance/notice.go
@@ -0,0 +1,100 @@
+// 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 compliance
+
+import (
+	"path/filepath"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+func init() {
+	RegisterNoticeXmlBuildComponents(android.InitRegistrationContext)
+}
+
+var PrepareForTestWithNoticeXmlBuildComponents = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(RegisterNoticeXmlBuildComponents),
+)
+
+var PrepareForTestWithNoticeXml = android.GroupFixturePreparers(
+	PrepareForTestWithNoticeXmlBuildComponents,
+)
+
+func RegisterNoticeXmlBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("notice_xml", NoticeXmlFactory)
+}
+
+var (
+	pctx = android.NewPackageContext("android/soong/compliance")
+
+	genNoticeXml = pctx.HostBinToolVariable("genNoticeXml", "gen_notice_xml")
+
+	// Command to generate NOTICE.xml.gz for a partition
+	genNoticeXmlRule = pctx.AndroidStaticRule("genNoticeXmlRule", blueprint.RuleParams{
+		Command: "rm -rf $out && " +
+			"${genNoticeXml} --output_file ${out} --metadata ${in} --partition ${partition} --product_out ${productOut} --soong_out ${soongOut}",
+		CommandDeps: []string{"${genNoticeXml}"},
+	}, "partition", "productOut", "soongOut")
+)
+
+func NoticeXmlFactory() android.Module {
+	m := &NoticeXmlModule{}
+	m.AddProperties(&m.props)
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibFirst)
+	return m
+}
+
+type NoticeXmlModule struct {
+	android.ModuleBase
+
+	props noticeXmlProperties
+
+	outputFile  android.OutputPath
+	installPath android.InstallPath
+}
+
+type noticeXmlProperties struct {
+	Partition_name string
+}
+
+func (nx *NoticeXmlModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	output := android.PathForModuleOut(ctx, "NOTICE.xml.gz")
+	metadataDb := android.PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "compliance-metadata.db")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   genNoticeXmlRule,
+		Input:  metadataDb,
+		Output: output,
+		Args: map[string]string{
+			"productOut": filepath.Join(ctx.Config().OutDir(), "target", "product", ctx.Config().DeviceName()),
+			"soongOut":   ctx.Config().SoongOutDir(),
+			"partition":  nx.props.Partition_name,
+		},
+	})
+
+	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)
+	}
+}
+
+func (nx *NoticeXmlModule) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(nx.outputFile),
+	}}
+}
diff --git a/compliance/notice_test.go b/compliance/notice_test.go
new file mode 100644
index 0000000..6187e53
--- /dev/null
+++ b/compliance/notice_test.go
@@ -0,0 +1,38 @@
+// 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 compliance
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+var prepareForNoticeXmlTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithArchMutator,
+	PrepareForTestWithNoticeXml,
+)
+
+func TestPrebuiltEtcOutputFile(t *testing.T) {
+	result := prepareForNoticeXmlTest.RunTestWithBp(t, `
+		notice_xml {
+			name: "notice_xml_system",
+			partition_name: "system",
+		}
+	`)
+
+	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/compliance/project_metadata_proto/Android.bp b/compliance/project_metadata_proto/Android.bp
index 56e76e7..0c807b2 100644
--- a/compliance/project_metadata_proto/Android.bp
+++ b/compliance/project_metadata_proto/Android.bp
@@ -24,4 +24,5 @@
         "golang-protobuf-reflect-protoreflect",
         "golang-protobuf-runtime-protoimpl",
     ],
+    visibility: ["//build/make/tools/compliance:__subpackages__"],
 }
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 93351f1..5616483 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -244,6 +244,7 @@
 	}
 
 	odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
+	odexSymbolsPath := odexPath.ReplaceExtension(ctx, "symbols.odex")
 	odexInstallPath := ToOdexPath(module.DexLocation, arch)
 	if odexOnSystemOther(module, global) {
 		odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath)
@@ -258,7 +259,8 @@
 	systemServerClasspathJars := global.AllSystemServerClasspathJars(ctx)
 
 	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
-	rule.Command().FlagWithOutput("rm -f ", odexPath)
+	rule.Command().FlagWithOutput("rm -f ", odexPath).
+		FlagWithArg("rm -f ", odexSymbolsPath.String())
 
 	if jarIndex := systemServerJars.IndexOfJar(module.Name); jarIndex >= 0 {
 		// System server jars should be dexpreopted together: class loader context of each jar
@@ -386,7 +388,9 @@
 		FlagWithArg("--instruction-set=", arch.String()).
 		FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]).
 		FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]).
-		Flag("--no-generate-debug-info").
+		FlagWithOutput("--oat-symbols=", odexSymbolsPath).
+		Flag("--generate-debug-info").
+		Flag("--strip").
 		Flag("--generate-build-id").
 		Flag("--abort-on-hard-verifier-error").
 		Flag("--force-determinism").
@@ -527,12 +531,12 @@
 		return false
 	}
 
-	if contains(global.SpeedApps, name) || contains(global.SystemServerApps, name) {
+	if contains(global.SystemServerApps, name) {
 		return false
 	}
 
 	for _, f := range global.PatternsOnSystemOther {
-		if makefileMatch(filepath.Join(SystemPartition, f), dexLocation) {
+		if makefileMatch("/"+f, dexLocation) || makefileMatch(filepath.Join(SystemPartition, f), dexLocation) {
 			return true
 		}
 	}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index eff2416..6f7d3bb 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -153,7 +153,7 @@
 			moduleTests: []moduleTest{
 				{module: systemModule, expectedPartition: "system_other/system"},
 				{module: systemProductModule, expectedPartition: "system_other/system/product"},
-				{module: productModule, expectedPartition: "product"},
+				{module: productModule, expectedPartition: "system_other/product"},
 			},
 		},
 	}
diff --git a/docs/OWNERS b/docs/OWNERS
new file mode 100644
index 0000000..776beca
--- /dev/null
+++ b/docs/OWNERS
@@ -0,0 +1 @@
+per-file map_files.md = danalbert@google.com
diff --git a/docs/java.dot b/docs/java.dot
new file mode 100644
index 0000000..ad7628d
--- /dev/null
+++ b/docs/java.dot
@@ -0,0 +1,127 @@
+digraph java {
+	//rankdir="LR";
+	//splines="false";
+	//cluster=true;
+	//node [ ordering="in" ];
+	node [ shape="rect" style="rounded" color="blue" ];
+
+	{
+		rank="same";
+		lib_java_sources [ label="library\njava sources" group="lib" ];
+		lib2_java_sources [ label="library\njava sources" group="lib2" ];
+		app_java_sources [ label="app\njava sources" group="app" ];
+	}
+
+	node [ group="lib"];
+	{
+		rank="same";
+		lib_java_classes [ label="library java\n.class files" ];
+		lib_java_headers [ label="library java\nheader .class files" ];
+	}
+
+	node [ group="lib2"];
+	{
+		rank="same";
+		lib_spacer [ style=invis width=4 ];
+		lib2_java_classes [ label="library java\n.class files" ];
+		lib2_java_headers [ label="library java\nheader .class files" ];
+	}
+	{
+		rank="same";
+		lib2_combined_classes [ label="combined library\n.class files" ];
+		lib2_combined_headers [ label="combined library\nheader .class files" ];
+	}
+
+	node [ group="app"];
+	{
+		rank="same";
+		lib2_spacer [ style=invis width=4 ];
+		app_java_classes [ label="app java\n.class files" ];
+	}
+	{
+		rank="same";
+		app_combined_classes [ label="combined app and library\n.class files" ];
+	}
+	{
+		rank="same";
+		app_dex [ label="app classes.dex files" ];
+	}
+
+
+	node [ shape="rect" style="" color="black" ];
+	node [ group="lib"];
+	{
+		rank="same";
+		lib_turbine_action [ label="turbine" ];
+		lib_javac_action [ label="javac" ];
+	}
+
+	node [ group="lib2"];
+	{
+		rank="same";
+		lib2_turbine_action [ label="turbine" ];
+		lib2_javac_action [ label="javac" ];
+	}
+	{
+		rank="same";
+		lib2_combine_action [ label="merge_zips" ];
+		lib2_combine_headers_action [ label="merge_zips" ];
+	}
+
+	node [ group="app"];
+	{
+		rank="same";
+		app_javac_action [ label="javac" ];
+	}
+	{
+		rank="same";
+		app_combine_action [ label="merge_zips" ];
+	}
+	{
+		rank="same";
+		app_r8_action [ label="r8" ];
+	}
+
+	// library
+
+	lib_java_sources -> lib_turbine_action [ weight=100 ];
+	lib_turbine_action -> lib_java_headers [ weight=100 ];
+
+	lib_java_sources -> lib_javac_action [ weight=1000 ];
+	lib_javac_action -> lib_java_classes [ weight=100 ];
+
+	lib_java_headers -> lib_spacer [ style=invis ];
+
+	// library 2
+
+	lib_java_headers -> lib2_turbine_action [ weight=0 ];
+	lib2_java_sources -> lib2_turbine_action [ weight=100 ];
+	lib2_turbine_action -> lib2_java_headers [ weight=100 ];
+
+	lib_java_headers -> lib2_javac_action [ weight=0 ];
+	lib2_java_sources -> lib2_javac_action [ weight=1000 ];
+	lib2_javac_action ->lib2_java_classes [ weight=100 ];
+
+	lib_java_classes -> lib2_combine_action [ weight=0 ];
+	lib2_java_classes -> lib2_combine_action [ weight=100 ];
+	lib2_combine_action -> lib2_combined_classes [ weight=100 ];
+
+	lib_java_headers -> lib2_combine_headers_action [ weight=0 ];
+	lib2_java_headers -> lib2_combine_headers_action [ weight=100 ];
+	lib2_combine_headers_action -> lib2_combined_headers [ weight=100 ];
+
+	lib2_combined_headers -> lib2_spacer [ style=invis ];
+
+	// app
+
+	lib2_combined_headers -> app_javac_action [ weight=0 ];
+	app_java_sources -> app_javac_action [ weight=1000 ];
+	app_javac_action -> app_java_classes [ weight=100 ];
+
+	lib2_combined_classes -> app_combine_action [ weight=0 ];
+	app_java_classes -> app_combine_action [ weight=100 ];
+	app_combine_action -> app_combined_classes [ weight=100 ];
+
+	app_combined_classes -> app_r8_action;
+	app_r8_action -> app_dex [ weight=100 ];
+}
diff --git a/docs/kotlin.dot b/docs/kotlin.dot
new file mode 100644
index 0000000..7a23c16
--- /dev/null
+++ b/docs/kotlin.dot
@@ -0,0 +1,196 @@
+digraph java {
+	//rankdir="LR";
+	//splines="false";
+	//cluster=true;
+	ranksep="0.75 equally"
+	//node [ ordering="in" ];
+	node [ shape="rect" style="rounded" color="blue" ];
+	{
+		rank="same";
+		lib_java_sources [ label="library\njava sources" group="lib" ];
+		lib_kotlin_sources [ label="library\nkotlin sources" group="lib" ];
+		lib2_java_sources [ label="library\njava sources" group="lib2" ];
+		lib2_kotlin_sources [ label="library\nkotlin sources" group="lib2" ];
+		app_java_sources [ label="app\njava sources" group="app" ];
+		app_kotlin_sources [ label="app\nkotlin sources" group="app" ];
+	}
+
+	node [ group="lib"];
+	{
+		rank="same";
+		lib_kotlin_classes [ label="library kotlin\n.class files" ];
+		lib_kotlin_headers [ label="library kotlin\nheader .class files" ];
+	}
+	{
+		rank="same";
+		lib_java_classes [ label="library java\n.class files" ];
+		lib_java_headers [ label="library java\nheader .class files" ];
+	}
+	{
+		rank="same";
+		lib_combined_classes [ label="combined library\n.class files" ];
+		lib_combined_headers [ label="combined library\nheader .class files" ];
+	}
+
+	node [ group="lib2"];
+	{
+		rank="same";
+		lib_spacer [ style=invis width=4 ];
+		lib2_kotlin_classes [ label="library kotlin\n.class files" ];
+		lib2_kotlin_headers [ label="library kotlin\nheader .class files" ];
+	}
+	{
+		rank="same";
+		lib2_java_classes [ label="library java\n.class files" ];
+		lib2_java_headers [ label="library java\nheader .class files" ];
+	}
+	{
+		rank="same";
+		lib2_combined_classes [ label="combined library\n.class files" ];
+		lib2_combined_headers [ label="combined library\nheader .class files" ];
+	}
+
+	node [ group="app"];
+	{
+		rank="same";
+		lib2_spacer [ style=invis width=4 ];
+		app_kotlin_classes [ label="app kotlin\n.class files" ];
+		app_kotlin_headers [ label="app kotlin\nheader .class files" ]	}
+	{
+		rank="same";
+		app_java_classes [ label="app java\n.class files" ];
+	}
+	{
+		rank="same";
+		app_combined_classes [ label="combined app and library\n.class files" ];
+	}
+	{
+		rank="same";
+		app_dex [ label="app classes.dex files" ];
+	}
+
+
+	node [ shape="rect" style="" color="black" ];
+	node [ group="lib"];
+	{
+		rank="same";
+		lib_kotlinc_action [ label="kotlinc" ];
+	}
+	{
+		rank="same";
+		lib_turbine_action [ label="turbine" ];
+		lib_javac_action [ label="javac" ];
+	}
+	{
+		rank="same";
+		lib_combine_action [ label="merge_zips" ];
+		lib_combine_headers_action [ label="merge_zips" ];
+	}
+
+	node [ group="lib2"];
+	{
+		rank="same";
+		lib2_kotlinc_action [ label="kotlinc" ];
+	}
+	{
+		rank="same";
+		lib2_turbine_action [ label="turbine" ];
+		lib2_javac_action [ label="javac" ];
+	}
+	{
+		rank="same";
+		lib2_combine_action [ label="merge_zips" ];
+		lib2_combine_headers_action [ label="merge_zips" ];
+	}
+
+	node [ group="app"];
+	{
+		rank="same";
+		app_kotlinc_action [ label="kotlinc" ];
+	}
+	{
+		rank="same";
+		app_javac_action [ label="javac" ];
+	}
+	{
+		rank="same";
+		app_combine_action [ label="merge_zips" ];
+	}
+	{
+		rank="same";
+		app_r8_action [ label="r8" ];
+	}
+
+	// library
+
+	lib_kotlin_sources -> lib_kotlinc_action [ weight=100 ];
+	lib_java_sources -> lib_kotlinc_action;
+	lib_kotlinc_action -> lib_kotlin_classes, lib_kotlin_headers [ weight=100 ];
+
+	lib_kotlin_headers -> lib_turbine_action [ weight=0 ];
+	lib_java_sources -> lib_turbine_action [ weight=100 ];
+	lib_turbine_action -> lib_java_headers [ weight=100 ];
+
+	lib_kotlin_headers -> lib_javac_action [ weight=0 ];
+	lib_java_sources -> lib_javac_action [ weight=1000 ];
+	lib_javac_action -> lib_java_classes [ weight=100 ];
+
+	lib_kotlin_classes -> lib_combine_action [ weight = 0 ];
+	lib_java_classes -> lib_combine_action [ weight = 100 ];
+	lib_combine_action -> lib_combined_classes [ weight=100 ];
+
+	lib_kotlin_headers -> lib_combine_headers_action [ weight = 0 ];
+	lib_java_headers -> lib_combine_headers_action [ weight = 100 ];
+	lib_combine_headers_action -> lib_combined_headers [ weight=100 ];
+
+	lib_combined_headers -> lib_spacer [ style=invis ];
+
+	// library 2
+
+	lib_combined_headers -> lib2_kotlinc_action [ weight=0 ];
+	lib2_kotlin_sources -> lib2_kotlinc_action [ weight=100 ];
+	lib2_java_sources  -> lib2_kotlinc_action;
+	lib2_kotlinc_action -> lib2_kotlin_classes, lib2_kotlin_headers [ weight=100 ];
+
+	lib_combined_headers -> lib2_turbine_action [ weight=0 ];
+	lib2_kotlin_headers -> lib2_turbine_action [ weight=0 ];
+	lib2_java_sources -> lib2_turbine_action [ weight=100 ];
+	lib2_turbine_action -> lib2_java_headers [ weight=100 ];
+
+	lib_combined_headers -> lib2_javac_action [ weight=0 ];
+	lib2_kotlin_headers -> lib2_javac_action [ weight=0 ];
+	lib2_java_sources -> lib2_javac_action [ weight=1000 ];
+	lib2_javac_action ->lib2_java_classes [ weight=100 ];
+
+	lib_combined_classes -> lib2_combine_action [ weight=0 ];
+	lib2_kotlin_classes -> lib2_combine_action [ weight=0 ];
+	lib2_java_classes -> lib2_combine_action [ weight=100 ];
+	lib2_combine_action -> lib2_combined_classes [ weight=100 ];
+
+	lib_combined_headers -> lib2_combine_headers_action [ weight=0 ];
+	lib2_kotlin_headers -> lib2_combine_headers_action [ weight=0 ];
+	lib2_java_headers -> lib2_combine_headers_action [ weight=100 ];
+	lib2_combine_headers_action -> lib2_combined_headers [ weight=100 ];
+
+	lib2_combined_headers -> lib2_spacer [ style=invis ];
+
+	// app
+
+	lib2_combined_headers -> app_kotlinc_action [ weight=0 ];
+	app_kotlin_sources -> app_kotlinc_action [ weight=100 ];
+	app_java_sources -> app_kotlinc_action;
+	app_kotlinc_action -> app_kotlin_headers, app_kotlin_classes [ weight=100 ];
+
+	lib2_combined_headers -> app_javac_action [ weight=0 ];
+	app_kotlin_headers -> app_javac_action [ weight=0 ];
+	app_java_sources -> app_javac_action [ weight=1000 ];
+	app_javac_action -> app_java_classes [ weight=100 ];
+
+	lib2_combined_classes -> app_combine_action [ weight=0 ];
+	app_kotlin_classes -> app_combine_action [ weight=0 ];
+	app_java_classes -> app_combine_action [ weight=100 ];
+	app_combine_action -> app_combined_classes [ weight=100 ];
+
+	app_combined_classes -> app_r8_action;
+	app_r8_action -> app_dex [ weight=100 ];
+}
diff --git a/docs/kotlin_with_annotation_processors.dot b/docs/kotlin_with_annotation_processors.dot
new file mode 100644
index 0000000..70c9bf3
--- /dev/null
+++ b/docs/kotlin_with_annotation_processors.dot
@@ -0,0 +1,277 @@
+digraph java {
+	//rankdir="LR";
+	//splines="false";
+	//cluster=true;
+	ranksep="0.75 equally"
+	//node [ ordering="in" ];
+	node [ shape="rect" style="rounded" color="blue" ];
+	{
+		rank="same";
+		lib_java_sources [ label="library\njava sources" group="lib" ];
+		lib_kotlin_sources [ label="library\nkotlin sources" group="lib" ];
+		lib2_java_sources [ label="library\njava sources" group="lib2" ];
+		lib2_kotlin_sources [ label="library\nkotlin sources" group="lib2" ];
+		app_java_sources [ label="app\njava sources" group="app" ];
+		app_kotlin_sources [ label="app\nkotlin sources" group="app" ];
+	}
+
+	node [ group="lib"];
+	{
+		rank="same";
+		lib_kotlin_stubs [ label="library\nkotlin stubs" ];
+	}
+	{
+		rank="same";
+		lib_apt_src_jar [ label="library annotation\nprocessor sources" ];
+	}
+	{
+		rank="same";
+		lib_kotlin_classes [ label="library kotlin\n.class files" ];
+		lib_kotlin_headers [ label="library kotlin\nheader .class files" ];
+	}
+	{
+		rank="same";
+		lib_java_classes [ label="library java\n.class files" ];
+		lib_java_headers [ label="library java\nheader .class files" ];
+	}
+	{
+		rank="same";
+		lib_combined_classes [ label="combined library\n.class files" ];
+		lib_combined_headers [ label="combined library\nheader .class files" ];
+	}
+
+	node [ group="lib2"];
+	{
+		rank="same";
+		lib_spacer [ style=invis width=4 ];
+		lib2_kotlin_stubs [ label="library\nkotlin stubs" ];
+	}
+	{
+		rank="same";
+		lib2_apt_src_jar [ label="library annotation\nprocessor sources" ];
+	}
+	{
+		rank="same";
+		lib2_kotlin_classes [ label="library kotlin\n.class files" ];
+		lib2_kotlin_headers [ label="library kotlin\nheader .class files" ];
+	}
+	{
+		rank="same";
+		lib2_java_classes [ label="library java\n.class files" ];
+		lib2_java_headers [ label="library java\nheader .class files" ];
+	}
+	{
+		rank="same";
+		lib2_combined_classes [ label="combined library\n.class files" ];
+		lib2_combined_headers [ label="combined library\nheader .class files" ];
+	}
+
+	node [ group="app"];
+	{
+		rank="same";
+		lib2_spacer [ style=invis width=4 ];
+		app_kotlin_stubs [ label="app\nkotlin stubs" ];
+	}
+	{
+		rank="same";
+		app_apt_src_jar [ label="app annotation\nprocessor sources" ];
+	}
+	{
+		rank="same";
+		app_kotlin_classes [ label="app kotlin\n.class files" ];
+		app_kotlin_headers [ label="app kotlin\nheader .class files" ]	}
+	{
+		rank="same";
+		app_java_classes [ label="app java\n.class files" ];
+	}
+	{
+		rank="same";
+		app_combined_classes [ label="combined app and library\n.class files" ];
+	}
+	{
+		rank="same";
+		app_dex [ label="app classes.dex files" ];
+	}
+
+
+	node [ shape="rect" style="" color="black" ];
+	node [ group="lib"];
+	{
+		rank="same";
+		lib_kapt_action [ label="kapt" ];
+	}
+	{
+		rank="same";
+		lib_turbine_apt_action [ label="turbine apt" ];
+	}
+	{
+		rank="same";
+		lib_kotlinc_action [ label="kotlinc" ];
+	}
+	{
+		rank="same";
+		lib_turbine_action [ label="turbine" ];
+		lib_javac_action [ label="javac" ];
+	}
+	{
+		rank="same";
+		lib_combine_action [ label="merge_zips" ];
+		lib_combine_headers_action [ label="merge_zips" ];
+	}
+
+	node [ group="lib2"];
+	{
+		rank="same";
+		lib2_kapt_action [ label="kapt" ];
+	}
+	{
+		rank="same";
+		lib2_turbine_apt_action [ label="turbine apt" ];
+	}
+	{
+		rank="same";
+		lib2_kotlinc_action [ label="kotlinc" ];
+	}
+	{
+		rank="same";
+		lib2_turbine_action [ label="turbine" ];
+		lib2_javac_action [ label="javac" ];
+	}
+	{
+		rank="same";
+		lib2_combine_action [ label="merge_zips" ];
+		lib2_combine_headers_action [ label="merge_zips" ];
+	}
+
+	node [ group="app"];
+	{
+		rank="same";
+		app_kapt_action [ label="kapt" ];
+	}
+	{
+		rank="same";
+		app_turbine_apt_action [ label="turbine apt" ];
+	}
+	{
+		rank="same";
+		app_kotlinc_action [ label="kotlinc" ];
+	}
+	{
+		rank="same";
+		app_javac_action [ label="javac" ];
+	}
+	{
+		rank="same";
+		app_combine_action [ label="merge_zips" ];
+	}
+	{
+		rank="same";
+		app_r8_action [ label="r8" ];
+	}
+
+	// library
+
+	lib_kotlin_sources -> lib_kapt_action [ weight=0 ];
+	lib_java_sources -> lib_kapt_action;
+	lib_kapt_action -> lib_kotlin_stubs [ weight=100 ];
+
+	lib_kotlin_stubs -> lib_turbine_apt_action [ weight=100 ];
+	lib_turbine_apt_action -> lib_apt_src_jar [ weight=100 ];
+
+	lib_apt_src_jar -> lib_kotlinc_action [ weight=0 ];
+	lib_kotlin_sources -> lib_kotlinc_action [ weight=100 ];
+	lib_java_sources -> lib_kotlinc_action;
+	lib_kotlinc_action -> lib_kotlin_classes, lib_kotlin_headers [ weight=100 ];
+
+	lib_apt_src_jar -> lib_turbine_action [ weight=0 ];
+	lib_kotlin_headers -> lib_turbine_action [ weight=0 ];
+	lib_java_sources -> lib_turbine_action [ weight=100 ];
+	lib_turbine_action -> lib_java_headers [ weight=100 ];
+
+	lib_apt_src_jar -> lib_javac_action [ weight=0 ];
+	lib_kotlin_headers -> lib_javac_action [ weight=0 ];
+	lib_java_sources -> lib_javac_action [ weight=1000 ];
+	lib_javac_action -> lib_java_classes [ weight=100 ];
+
+	lib_kotlin_classes -> lib_combine_action [ weight = 0 ];
+	lib_java_classes -> lib_combine_action [ weight = 100 ];
+	lib_combine_action -> lib_combined_classes [ weight=100 ];
+
+	lib_kotlin_headers -> lib_combine_headers_action [ weight = 0 ];
+	lib_java_headers -> lib_combine_headers_action [ weight = 100 ];
+	lib_combine_headers_action -> lib_combined_headers [ weight=100 ];
+
+	lib_combined_headers -> lib_spacer [ style=invis ];
+
+	// library 2
+
+	lib_combined_headers -> lib2_kapt_action [ weight=0 ];
+	lib2_kotlin_sources -> lib2_kapt_action [ weight=0 ];
+	lib2_java_sources -> lib2_kapt_action;
+	lib2_kapt_action -> lib2_kotlin_stubs [ weight=100 ];
+
+	lib_combined_headers -> lib2_turbine_apt_action [ weight=0 ];
+	lib2_kotlin_stubs -> lib2_turbine_apt_action [ weight=100 ];
+	lib2_turbine_apt_action -> lib2_apt_src_jar [ weight=100 ];
+
+	lib_combined_headers -> lib2_kotlinc_action [ weight=0 ];
+	lib2_apt_src_jar -> lib2_kotlinc_action [ weight=0 ];
+	lib2_kotlin_sources -> lib2_kotlinc_action [ weight=100 ];
+	lib2_java_sources  -> lib2_kotlinc_action;
+	lib2_kotlinc_action -> lib2_kotlin_classes, lib2_kotlin_headers [ weight=100 ];
+
+	lib_combined_headers -> lib2_turbine_action [ weight=0 ];
+	lib2_apt_src_jar -> lib2_turbine_action [ weight=0 ];
+	lib2_kotlin_headers -> lib2_turbine_action [ weight=0 ];
+	lib2_java_sources -> lib2_turbine_action [ weight=100 ];
+	lib2_turbine_action -> lib2_java_headers [ weight=100 ];
+
+	lib_combined_headers -> lib2_javac_action [ weight=0 ];
+	lib2_apt_src_jar -> lib2_javac_action [ weight=0 ];
+	lib2_kotlin_headers -> lib2_javac_action [ weight=0 ];
+	lib2_java_sources -> lib2_javac_action [ weight=1000 ];
+	lib2_javac_action ->lib2_java_classes [ weight=100 ];
+
+	lib_combined_classes -> lib2_combine_action [ weight=0 ];
+	lib2_kotlin_classes -> lib2_combine_action [ weight=0 ];
+	lib2_java_classes -> lib2_combine_action [ weight=100 ];
+	lib2_combine_action -> lib2_combined_classes [ weight=100 ];
+
+	lib_combined_headers -> lib2_combine_headers_action [ weight=0 ];
+	lib2_kotlin_headers -> lib2_combine_headers_action [ weight=0 ];
+	lib2_java_headers -> lib2_combine_headers_action [ weight=100 ];
+	lib2_combine_headers_action -> lib2_combined_headers [ weight=100 ];
+
+	lib2_combined_headers -> lib2_spacer [ style=invis ];
+
+	// app
+
+	lib2_combined_headers -> app_kapt_action [ weight=0 ];
+	app_kotlin_sources -> app_kapt_action [ weight=0 ];
+	app_java_sources -> app_kapt_action;
+	app_kapt_action -> app_kotlin_stubs [ weight=100 ];
+
+	lib2_combined_headers -> app_turbine_apt_action [ weight=0 ];
+	app_kotlin_stubs -> app_turbine_apt_action [ weight=100 ];
+	app_turbine_apt_action -> app_apt_src_jar [ weight=100 ];
+
+	lib2_combined_headers -> app_kotlinc_action [ weight=0 ];
+	app_apt_src_jar -> app_kotlinc_action [ weight=0 ];
+	app_kotlin_sources -> app_kotlinc_action [ weight=100 ];
+	app_java_sources -> app_kotlinc_action;
+	app_kotlinc_action -> app_kotlin_headers, app_kotlin_classes [ weight=100 ];
+
+	lib2_combined_headers -> app_javac_action [ weight=0 ];
+	app_apt_src_jar -> app_javac_action [ weight=0 ];
+	app_kotlin_headers -> app_javac_action [ weight=0 ];
+	app_java_sources -> app_javac_action [ weight=1000 ];
+	app_javac_action -> app_java_classes [ weight=100 ];
+
+	lib2_combined_classes -> app_combine_action [ weight=0 ];
+	app_kotlin_classes -> app_combine_action [ weight=0 ];
+	app_java_classes -> app_combine_action [ weight=100 ];
+	app_combine_action -> app_combined_classes [ weight=100 ];
+
+	app_combined_classes -> app_r8_action;
+	app_r8_action -> app_dex [ weight=100 ];
+}
diff --git a/docs/map_files.md b/docs/map_files.md
index e1ddefc..8d6af87 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -88,12 +88,17 @@
 
 ### introduced
 
-Indicates the version in which an API was first introduced. For example,
-`introduced=21` specifies that the API was first added (or first made public) in
-API level 21. This tag can be applied to either a version definition or an
-individual symbol. If applied to a version, all symbols contained in the version
-will have the tag applied. An `introduced` tag on a symbol overrides the value
-set for the version, if both are defined.
+Indicates the version in which an API was first introduced in the NDK. For
+example, `introduced=21` specifies that the API was first added (or first made
+public) in API level 21. This tag can be applied to either a version definition
+or an individual symbol. If applied to a version, all symbols contained in the
+version will have the tag applied. An `introduced` tag on a symbol overrides the
+value set for the version, if both are defined.
+
+The `introduced` tag should only be used with NDK APIs. Other API surface tags
+(such as `apex`) will override `introduced`. APIs that are in the NDK should
+never use tags like `apex`, and APIs that are not in the NDK should never use
+`introduced`.
 
 Note: The map file alone does not contain all the information needed to
 determine which API level an API was added in. The `first_version` property of
diff --git a/docs/resources.md b/docs/resources.md
new file mode 100644
index 0000000..c7cb0cf
--- /dev/null
+++ b/docs/resources.md
@@ -0,0 +1,89 @@
+## Soong Android Resource Compilation
+
+The Android build process involves several steps to compile resources into a format that the Android app can use
+efficiently in android_library, android_app and android_test modules.  See the
+[resources documentation](https://developer.android.com/guide/topics/resources/providing-resources) for general
+information on resources (with a focus on building with Gradle).
+
+For all modules, AAPT2 compiles resources provided by directories listed in the resource_dirs directory (which is
+implicitly set to `["res"]` if unset, but can be overridden by setting the `resource_dirs` property).
+
+## android_library with resource processor
+For an android_library with resource processor enabled (currently by setting `use_resource_processor: true`, but will be
+enabled by default in the future):
+- AAPT2 generates the `package-res.apk` file with a resource table that contains all resources from the current
+android_library module.  `package-res.apk` files from transitive dependencies are passed to AAPT2 with the `-I` flag to
+resolve references to resources from dependencies.
+- AAPT2 generates an R.txt file that lists all the resources provided by the current android_library module.
+- ResourceProcessorBusyBox reads the `R.txt` file for the current android_library and produces an `R.jar` with an
+`R.class` in the package listed in the android_library's `AndroidManifest.xml` file that contains java fields for each
+resource ID.  The resource IDs are non-final, as the final IDs will not be known until the resource table of the final
+android_app or android_test module is built.
+- The android_library's java and/or kotlin code is compiled with the generated `R.jar` in the classpath, along with the
+`R.jar` files from all transitive android_library dependencies.
+
+## android_app or android_test with resource processor
+For an android_app or android_test with resource processor enabled (currently by setting `use_resource_processor: true`,
+but will be enabled by default in the future):
+- AAPT2 generates the `package-res.apk` file with a resource table that contains all resources from the current
+android_app or android_test, as well as all transitive android_library modules referenced via `static_libs`.  The
+current module is overlaid on dependencies so that resources from the current module replace resources from dependencies
+in the case of conflicts.
+- AAPT2 generates an R.txt file that lists all the resources provided by the current android_app or android_test, as
+well as all transitive android_library modules referenced via `static_libs`.  The R.txt file contains the final resource
+ID for each resource.
+- ResourceProcessorBusyBox reads the `R.txt` file for the current android_app or android_test, as well as all transitive
+android_library modules referenced via `static_libs`, and produces an `R.jar` with an `R.class` in the package listed in
+the android_app or android_test's `AndroidManifest.xml` file that contains java fields for all local or transitive
+resource IDs.  In addition, it creates an `R.class` in the package listed in each android_library dependency's
+`AndroidManifest.xml` file that contains final resource IDs for the resources that were found in that library.
+- The android_app or android_test's java and/or kotlin code is compiled with the current module's `R.jar` in the
+classpath, but not the `R.jar` files from transitive android_library dependencies.  The `R.jar` file is also merged into
+the program  classes that are dexed and placed in the final APK.
+
+## android_app, android_test or android_library without resource processor
+For an android_app, android_test or android_library without resource processor enabled (current the default, or
+explicitly set with `use_resource_processor: false`):
+- AAPT2 generates the `package-res.apk` file with a resource table that contains all resources from the current
+android_app, android_test or android_library module, as well as all transitive android_library modules referenced via
+`static_libs`.  The current module is overlaid on dependencies so that resources from the current module replace
+resources from dependencies in the case of conflicts.
+- AAPT2 generates an `R.java` file in the package listed in each the current module's `AndroidManifest.xml` file that
+contains resource IDs for all resources from the current module as well as all transitive android_library modules
+referenced via `static_libs`.  The same `R.java` containing all local and transitive resources is also duplicated into
+every package listed in an `AndroidManifest.xml` file in any static `android_library` dependency.
+- The module's java and/or kotlin code is compiled along with all the generated `R.java` files.
+
+
+## Downsides of legacy resource compilation without resource processor
+
+Compiling resources without using the resource processor results in a generated R.java source file for every transitive
+package that contains every transitive resource.  For modules with large transitive dependency trees this can be tens of
+thousands of resource IDs duplicated in tens to a hundred java sources.  These java sources all have to be compiled in
+every successive module in the dependency tree, and then the final R8 step has to drop hundreds of thousands of
+unreferenced fields.  This results in significant build time and disk usage increases over building with resource
+processor.
+
+## Converting to compilation with resource processor
+
+### Reference resources using the package name of the module that includes them.
+Converting an android_library module to build with resource processor requires fixing any references to resources
+provided by android_library dependencies to reference the R classes using the package name found in the
+`AndroidManifest.xml` file of the dependency.  For example, when referencing an androidx resource:
+```java
+View.inflate(mContext, R.layout.preference, null));
+```
+must be replaced with:
+```java
+View.inflate(mContext, androidx.preference.R.layout.preference, null));
+```
+
+### Use unique package names for each module in `AndroidManifest.xml`
+
+Each module will produce an `R.jar` containing an `R.class` in the package specified in it's `AndroidManifest.xml`.
+If multiple modules use the same package name they will produce conflicting `R.class` files, which can cause some
+resource IDs to appear to be missing.
+
+If existing code has multiple modules that contribute resources to the same package, one option is to move all the
+resources into a single resources-only `android_library` module with no code, and then depend on that from all the other
+modules.
\ No newline at end of file
diff --git a/docs/tidy.md b/docs/tidy.md
index ae0ca93..2e4c957 100644
--- a/docs/tidy.md
+++ b/docs/tidy.md
@@ -38,7 +38,7 @@
 clang-tidy is enabled explicitly and with a different check list:
 ```
 cc_defaults {
-    name: "bpf_defaults",
+    name: "bpf_cc_defaults",
     // snipped
     tidy: true,
     tidy_checks: [
@@ -52,7 +52,7 @@
 }
 ```
 That means in normal builds, even without `WITH_TIDY=1`,
-the modules that use `bpf_defaults` _should_ run clang-tidy
+the modules that use `bpf_cc_defaults` _should_ run clang-tidy
 over C/C++ source files with the given `tidy_checks`.
 
 However since clang-tidy warnings and its runtime cost might
diff --git a/elf/Android.bp b/elf/Android.bp
index 6450be1..6d3f4f0 100644
--- a/elf/Android.bp
+++ b/elf/Android.bp
@@ -20,6 +20,7 @@
     name: "soong-elf",
     pkgPath: "android/soong/elf",
     srcs: [
+        "build_id_dir.go",
         "elf.go",
     ],
     testSrcs: [
diff --git a/elf/build_id_dir.go b/elf/build_id_dir.go
new file mode 100644
index 0000000..5fb7dda
--- /dev/null
+++ b/elf/build_id_dir.go
@@ -0,0 +1,172 @@
+// 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 elf
+
+import (
+	"io/fs"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+	"time"
+)
+
+func UpdateBuildIdDir(path string) error {
+	path = filepath.Clean(path)
+	buildIdPath := path + "/.build-id"
+
+	// Collect the list of files and build-id symlinks. If the symlinks are
+	// up to date (newer than the symbol files), there is nothing to do.
+	var buildIdFiles, symbolFiles []string
+	var buildIdMtime, symbolsMtime time.Time
+	filepath.WalkDir(path, func(path string, entry fs.DirEntry, err error) error {
+		if entry == nil || entry.IsDir() {
+			return nil
+		}
+		info, err := entry.Info()
+		if err != nil {
+			return err
+		}
+		mtime := info.ModTime()
+		if strings.HasPrefix(path, buildIdPath) {
+			if buildIdMtime.Compare(mtime) < 0 {
+				buildIdMtime = mtime
+			}
+			buildIdFiles = append(buildIdFiles, path)
+		} else {
+			if symbolsMtime.Compare(mtime) < 0 {
+				symbolsMtime = mtime
+			}
+			symbolFiles = append(symbolFiles, path)
+		}
+		return nil
+	})
+	if symbolsMtime.Compare(buildIdMtime) < 0 {
+		return nil
+	}
+
+	// Collect build-id -> file mapping from ELF files in the symbols directory.
+	concurrency := 8
+	done := make(chan error)
+	buildIdToFile := make(map[string]string)
+	var mu sync.Mutex
+	for i := 0; i != concurrency; i++ {
+		go func(paths []string) {
+			for _, path := range paths {
+				id, err := Identifier(path, true)
+				if err != nil {
+					done <- err
+					return
+				}
+				if id == "" {
+					continue
+				}
+				mu.Lock()
+				oldPath := buildIdToFile[id]
+				if oldPath == "" || oldPath > path {
+					buildIdToFile[id] = path
+				}
+				mu.Unlock()
+			}
+			done <- nil
+		}(symbolFiles[len(symbolFiles)*i/concurrency : len(symbolFiles)*(i+1)/concurrency])
+	}
+
+	// Collect previously generated build-id -> file mapping from the .build-id directory.
+	// We will use this for incremental updates. If we see anything in the .build-id
+	// directory that we did not expect, we'll delete it and start over.
+	prevBuildIdToFile := make(map[string]string)
+out:
+	for _, buildIdFile := range buildIdFiles {
+		if !strings.HasSuffix(buildIdFile, ".debug") {
+			prevBuildIdToFile = nil
+			break
+		}
+		buildId := buildIdFile[len(buildIdPath)+1 : len(buildIdFile)-6]
+		for i, ch := range buildId {
+			if i == 2 {
+				if ch != '/' {
+					prevBuildIdToFile = nil
+					break out
+				}
+			} else {
+				if (ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') {
+					prevBuildIdToFile = nil
+					break out
+				}
+			}
+		}
+		target, err := os.Readlink(buildIdFile)
+		if err != nil || !strings.HasPrefix(target, "../../") {
+			prevBuildIdToFile = nil
+			break
+		}
+		prevBuildIdToFile[buildId[0:2]+buildId[3:]] = path + target[5:]
+	}
+	if prevBuildIdToFile == nil {
+		err := os.RemoveAll(buildIdPath)
+		if err != nil {
+			return err
+		}
+		prevBuildIdToFile = make(map[string]string)
+	}
+
+	// Wait for build-id collection from ELF files to finish.
+	for i := 0; i != concurrency; i++ {
+		err := <-done
+		if err != nil {
+			return err
+		}
+	}
+
+	// Delete old symlinks.
+	for id, _ := range prevBuildIdToFile {
+		if buildIdToFile[id] == "" {
+			symlinkDir := buildIdPath + "/" + id[:2]
+			symlinkPath := symlinkDir + "/" + id[2:] + ".debug"
+			if err := os.Remove(symlinkPath); err != nil {
+				return err
+			}
+		}
+	}
+
+	// Add new symlinks and update changed symlinks.
+	for id, path := range buildIdToFile {
+		prevPath := prevBuildIdToFile[id]
+		if prevPath == path {
+			continue
+		}
+		symlinkDir := buildIdPath + "/" + id[:2]
+		symlinkPath := symlinkDir + "/" + id[2:] + ".debug"
+		if prevPath == "" {
+			if err := os.MkdirAll(symlinkDir, 0755); err != nil {
+				return err
+			}
+		} else {
+			if err := os.Remove(symlinkPath); err != nil {
+				return err
+			}
+		}
+
+		target, err := filepath.Rel(symlinkDir, path)
+		if err != nil {
+			return err
+		}
+		if err := os.Symlink(target, symlinkPath); err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/etc/Android.bp b/etc/Android.bp
index 97788e4..580c54f 100644
--- a/etc/Android.bp
+++ b/etc/Android.bp
@@ -11,12 +11,15 @@
         "soong-android",
     ],
     srcs: [
-        "prebuilt_etc.go",
         "install_symlink.go",
+        "otacerts_zip.go",
+        "prebuilt_etc.go",
     ],
     testSrcs: [
         "prebuilt_etc_test.go",
         "install_symlink_test.go",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/etc/otacerts_zip.go b/etc/otacerts_zip.go
new file mode 100644
index 0000000..b6f175a
--- /dev/null
+++ b/etc/otacerts_zip.go
@@ -0,0 +1,146 @@
+// 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/proptools"
+)
+
+func init() {
+	RegisterOtacertsZipBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterOtacertsZipBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("otacerts_zip", otacertsZipFactory)
+}
+
+type otacertsZipProperties struct {
+	// Make this module available when building for recovery.
+	// Only the recovery partition is available.
+	Recovery_available *bool
+
+	// Optional subdirectory under which the zip file is installed into.
+	Relative_install_path *string
+
+	// Optional name for the installed file. If unspecified, otacerts.zip is used.
+	Filename *string
+}
+
+type otacertsZipModule struct {
+	android.ModuleBase
+
+	properties otacertsZipProperties
+	outputPath android.OutputPath
+}
+
+// otacerts_zip collects key files defined in PRODUCT_DEFAULT_DEV_CERTIFICATE
+// and PRODUCT_EXTRA_OTA_KEYS for system or PRODUCT_EXTRA_RECOVERY_KEYS for
+// recovery image. The output file (otacerts.zip by default) is installed into
+// the relative_install_path directory under the etc directory of the target
+// partition.
+func otacertsZipFactory() android.Module {
+	module := &otacertsZipModule{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+var _ android.ImageInterface = (*otacertsZipModule)(nil)
+
+func (m *otacertsZipModule) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (m *otacertsZipModule) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (m *otacertsZipModule) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (m *otacertsZipModule) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return !m.ModuleBase.InstallInRecovery()
+}
+
+func (m *otacertsZipModule) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (m *otacertsZipModule) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (m *otacertsZipModule) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (m *otacertsZipModule) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return proptools.Bool(m.properties.Recovery_available) || m.ModuleBase.InstallInRecovery()
+}
+
+func (m *otacertsZipModule) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	return nil
+}
+
+func (m *otacertsZipModule) SetImageVariation(ctx android.BaseModuleContext, variation string) {
+}
+
+func (m *otacertsZipModule) InRecovery() bool {
+	return m.ModuleBase.InRecovery() || m.ModuleBase.InstallInRecovery()
+}
+
+func (m *otacertsZipModule) InstallInRecovery() bool {
+	return m.InRecovery()
+}
+
+func (m *otacertsZipModule) outputFileName() string {
+	// Use otacerts.zip if not specified.
+	return proptools.StringDefault(m.properties.Filename, "otacerts.zip")
+}
+
+func (m *otacertsZipModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Read .x509.pem file defined in PRODUCT_DEFAULT_DEV_CERTIFICATE or the default test key.
+	pem, _ := ctx.Config().DefaultAppCertificate(ctx)
+	// 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
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+	cmd := rule.Command().BuiltTool("soong_zip").
+		FlagWithOutput("-o ", m.outputPath).
+		Flag("-j ").
+		Flag("-symlinks=false ")
+	for _, src := range srcPaths {
+		cmd.FlagWithInput("-f ", src)
+	}
+	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)
+}
+
+func (m *otacertsZipModule) AndroidMkEntries() []android.AndroidMkEntries {
+	nameSuffix := ""
+	if m.InRecovery() {
+		nameSuffix = ".recovery"
+	}
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		SubName:    nameSuffix,
+		OutputFile: android.OptionalPathForPath(m.outputPath),
+	}}
+}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 2075488..fc6d1f7 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -50,6 +50,7 @@
 	ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
 	ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
 	ctx.RegisterModuleType("prebuilt_etc_cacerts", PrebuiltEtcCaCertsFactory)
+	ctx.RegisterModuleType("prebuilt_avb", PrebuiltAvbFactory)
 	ctx.RegisterModuleType("prebuilt_root", PrebuiltRootFactory)
 	ctx.RegisterModuleType("prebuilt_root_host", PrebuiltRootHostFactory)
 	ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
@@ -125,6 +126,15 @@
 	Relative_install_path *string `android:"arch_variant"`
 }
 
+type prebuiltRootProperties struct {
+	// Install this module to the root directory, without partition subdirs.  When this module is
+	// added to PRODUCT_PACKAGES, this module will be installed to $PRODUCT_OUT/root, which will
+	// then be copied to the root of system.img. When this module is packaged by other modules like
+	// android_filesystem, this module will be installed to the root ("/"), unlike normal
+	// prebuilt_root modules which are installed to the partition subdir (e.g. "/system/").
+	Install_in_root *bool
+}
+
 type PrebuiltEtcModule interface {
 	android.Module
 
@@ -139,7 +149,12 @@
 	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.
+	rootProperties prebuiltRootProperties
+
 	subdirProperties prebuiltSubdirProperties
 
 	sourceFilePaths android.Paths
@@ -216,6 +231,14 @@
 
 func (p *PrebuiltEtc) ImageMutatorBegin(ctx android.BaseModuleContext) {}
 
+func (p *PrebuiltEtc) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (p *PrebuiltEtc) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (p *PrebuiltEtc) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return !p.ModuleBase.InstallInRecovery() && !p.ModuleBase.InstallInRamdisk() &&
 		!p.ModuleBase.InstallInVendorRamdisk() && !p.ModuleBase.InstallInDebugRamdisk()
@@ -233,6 +256,10 @@
 	return proptools.Bool(p.properties.Debug_ramdisk_available) || p.ModuleBase.InstallInDebugRamdisk()
 }
 
+func (p *PrebuiltEtc) InstallInRoot() bool {
+	return proptools.Bool(p.rootProperties.Install_in_root)
+}
+
 func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return proptools.Bool(p.properties.Recovery_available) || p.ModuleBase.InstallInRecovery()
 }
@@ -493,6 +520,13 @@
 func InitPrebuiltRootModule(p *PrebuiltEtc) {
 	p.installDirBase = "."
 	p.AddProperties(&p.properties)
+	p.AddProperties(&p.rootProperties)
+}
+
+func InitPrebuiltAvbModule(p *PrebuiltEtc) {
+	p.installDirBase = "avb"
+	p.AddProperties(&p.properties)
+	p.rootProperties.Install_in_root = proptools.BoolPtr(true)
 }
 
 // prebuilt_etc is for a prebuilt artifact that is installed in
@@ -545,6 +579,20 @@
 	return module
 }
 
+// Generally, a <partition> directory will contain a `system` subdirectory, but the <partition> of
+// `prebuilt_avb` will not have a `system` subdirectory.
+// Ultimately, prebuilt_avb will install the prebuilt artifact to the `avb` subdirectory under the
+// root directory of the partition: <partition_root>/avb.
+// prebuilt_avb does not allow adding any other subdirectories.
+func PrebuiltAvbFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltAvbModule(module)
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+	return module
+}
+
 // prebuilt_root is for a prebuilt artifact that is installed in
 // <partition>/ directory. Can't have any sub directories.
 func PrebuiltRootFactory() android.Module {
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index c44574a..e739afe 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -244,6 +244,31 @@
 	`)
 }
 
+func TestPrebuiltAvbInstallDirPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_avb {
+			name: "foo.conf",
+			src: "foo.conf",
+			filename: "foo.conf",
+			//recovery: true,
+		}
+	`)
+
+	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)
+}
+
+func TestPrebuiltAvdInstallDirPathValidate(t *testing.T) {
+	prepareForPrebuiltEtcTest.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("filename cannot contain separator")).RunTestWithBp(t, `
+		prebuilt_avb {
+			name: "foo.conf",
+			src: "foo.conf",
+			filename: "foo/bar.conf",
+		}
+	`)
+}
+
 func TestPrebuiltUserShareInstallDirPath(t *testing.T) {
 	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_usr_share {
diff --git a/filesystem/aconfig_files.go b/filesystem/aconfig_files.go
index 8daee85..5c047bc 100644
--- a/filesystem/aconfig_files.go
+++ b/filesystem/aconfig_files.go
@@ -16,7 +16,6 @@
 
 import (
 	"android/soong/android"
-	"path/filepath"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -56,6 +55,7 @@
 	sb.WriteString(" \\\n")
 	sb.WriteString(sbCaches.String())
 	cmd.ImplicitOutput(installAconfigFlagsPath)
+	f.appendToEntry(ctx, installAconfigFlagsPath)
 
 	installAconfigStorageDir := dir.Join(ctx, "etc", "aconfig")
 	sb.WriteString("mkdir -p ")
@@ -63,16 +63,18 @@
 	sb.WriteRune('\n')
 
 	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(filepath.Join(installAconfigStorageDir.String(), fileName))
+		sb.WriteString(outputPath.String())
 		sb.WriteString(" \\\n")
 		sb.WriteString(sbCaches.String())
-		cmd.ImplicitOutput(installAconfigStorageDir.Join(ctx, fileName))
+		cmd.ImplicitOutput(outputPath)
+		f.appendToEntry(ctx, outputPath)
 	}
 	generatePartitionAconfigStorageFile("package_map", "package.map")
 	generatePartitionAconfigStorageFile("flag_map", "flag.map")
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index c889dd6..0353992 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -60,7 +60,9 @@
 	output     android.OutputPath
 	installDir android.InstallPath
 
-	// For testing. Keeps the result of CopySpecsToDir()
+	fileListFile android.OutputPath
+
+	// Keeps the entries installed from this filesystem
 	entries []string
 }
 
@@ -145,14 +147,14 @@
 func filesystemFactory() android.Module {
 	module := &filesystem{}
 	module.filterPackagingSpec = module.filterInstallablePackagingSpec
-	initFilesystemModule(module)
+	initFilesystemModule(module, module)
 	return module
 }
 
-func initFilesystemModule(module *filesystem) {
-	module.AddProperties(&module.properties)
-	android.InitPackageModule(module)
-	module.PackagingBase.DepsCollectFirstTargetOnly = true
+func initFilesystemModule(module android.DefaultableModule, filesystemModule *filesystem) {
+	module.AddProperties(&filesystemModule.properties)
+	android.InitPackageModule(filesystemModule)
+	filesystemModule.PackagingBase.DepsCollectFirstTargetOnly = true
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
 }
@@ -221,8 +223,26 @@
 
 	f.installDir = android.PathForModuleInstall(ctx, "etc")
 	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())
+}
+
+func (f *filesystem) appendToEntry(ctx android.ModuleContext, installedFile android.OutputPath) {
+	partitionBaseDir := android.PathForModuleOut(ctx, "root", f.partitionName()).String() + "/"
+
+	relPath, inTargetPartition := strings.CutPrefix(installedFile.String(), partitionBaseDir)
+	if inTargetPartition {
+		f.entries = append(f.entries, relPath)
+	}
+}
+
+func (f *filesystem) installedFilesList() string {
+	installedFilePaths := android.FirstUniqueStrings(f.entries)
+	slices.Sort(installedFilePaths)
+
+	return strings.Join(installedFilePaths, "\n")
 }
 
 func validatePartitionType(ctx android.ModuleContext, p partition) {
@@ -269,17 +289,19 @@
 		builder.Command().Textf("(! [ -e %s -o -L %s ] || (echo \"%s already exists from an earlier stage of the build\" && exit 1))", dst, dst, dst)
 		builder.Command().Text("mkdir -p").Text(filepath.Dir(dst.String()))
 		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 _, f := range extraFiles {
-			rel, err := filepath.Rel(rootForExtraFiles.String(), f.String())
+		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", f, rootForExtraFiles)
+				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").
@@ -290,6 +312,33 @@
 	}
 }
 
+func (f *filesystem) copyPackagingSpecs(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir, rebasedDir android.WritablePath) []string {
+	rootDirSpecs := make(map[string]android.PackagingSpec)
+	rebasedDirSpecs := make(map[string]android.PackagingSpec)
+
+	for rel, spec := range specs {
+		if spec.Partition() == "root" {
+			rootDirSpecs[rel] = spec
+		} else {
+			rebasedDirSpecs[rel] = spec
+		}
+	}
+
+	dirsToSpecs := make(map[android.WritablePath]map[string]android.PackagingSpec)
+	dirsToSpecs[rootDir] = rootDirSpecs
+	dirsToSpecs[rebasedDir] = rebasedDirSpecs
+
+	return f.CopySpecsToDirs(ctx, builder, dirsToSpecs)
+}
+
+func (f *filesystem) copyFilesToProductOut(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
+	if f.Name() != ctx.Config().SoongDefinedSystemImage() {
+		return
+	}
+	installPath := android.PathForModuleInPartitionInstall(ctx, f.partitionName())
+	builder.Command().Textf("cp -prf %s/* %s", rebasedDir, installPath)
+}
+
 func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath {
 	rootDir := android.PathForModuleOut(ctx, "root").OutputPath
 	rebasedDir := rootDir
@@ -300,13 +349,14 @@
 	// Wipe the root dir to get rid of leftover files from prior builds
 	builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
 	specs := f.gatherFilteredPackagingSpecs(ctx)
-	f.entries = f.CopySpecsToDir(ctx, builder, specs, rebasedDir)
+	f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir)
 
 	f.buildNonDepsFiles(ctx, builder, rootDir)
 	f.addMakeBuiltFiles(ctx, builder, rootDir)
 	f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
 	f.buildEventLogtagsFile(ctx, builder, rebasedDir)
 	f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir)
+	f.copyFilesToProductOut(ctx, builder, rebasedDir)
 
 	// run host_init_verifier
 	// Ideally we should have a concept of pluggable linters that verify the generated image.
@@ -443,12 +493,13 @@
 	// Wipe the root dir to get rid of leftover files from prior builds
 	builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
 	specs := f.gatherFilteredPackagingSpecs(ctx)
-	f.entries = f.CopySpecsToDir(ctx, builder, specs, rebasedDir)
+	f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir)
 
 	f.buildNonDepsFiles(ctx, builder, rootDir)
 	f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
 	f.buildEventLogtagsFile(ctx, builder, rebasedDir)
 	f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir)
+	f.copyFilesToProductOut(ctx, builder, rebasedDir)
 
 	output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
 	cmd := builder.Command().
@@ -535,6 +586,8 @@
 	for _, path := range android.SortedKeys(logtagsFilePaths) {
 		cmd.Text(path)
 	}
+
+	f.appendToEntry(ctx, eventLogtagsPath)
 }
 
 type partition interface {
@@ -558,6 +611,7 @@
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_PATH", f.installDir.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
+				entries.SetString("LOCAL_FILESYSTEM_FILELIST", f.fileListFile.String())
 			},
 		},
 	}}
@@ -607,7 +661,7 @@
 
 var _ cc.UseCoverage = (*filesystem)(nil)
 
-func (*filesystem) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (*filesystem) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 2dc8c21..8c0d111 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"os"
-	"path/filepath"
 	"testing"
 
 	"android/soong/android"
@@ -147,8 +146,8 @@
 
 	output := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img")
 
-	stampFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/staging_dir.stamp")
-	fileListFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/file_list.txt")
+	stampFile := "out/target/product/test_device/obj/PACKAGING/system_intermediates/staging_dir.stamp"
+	fileListFile := "out/target/product/test_device/obj/PACKAGING/system_intermediates/file_list.txt"
 	android.AssertStringListContains(t, "deps of filesystem must include the staging dir stamp file", output.Implicits.Strings(), stampFile)
 	android.AssertStringListContains(t, "deps of filesystem must include the staging dir file list", output.Implicits.Strings(), fileListFile)
 }
diff --git a/filesystem/fsverity_metadata.go b/filesystem/fsverity_metadata.go
index 3e50ff7..d7bb654 100644
--- a/filesystem/fsverity_metadata.go
+++ b/filesystem/fsverity_metadata.go
@@ -87,6 +87,7 @@
 		sb.WriteRune(' ')
 		sb.WriteString(srcPath.String())
 		sb.WriteRune('\n')
+		f.appendToEntry(ctx, destPath)
 	}
 
 	// STEP 2: generate signed BuildManifest.apk
@@ -108,6 +109,7 @@
 	sb.WriteString(" --output ")
 	sb.WriteString(manifestPbPath.String())
 	sb.WriteRune(' ')
+	f.appendToEntry(ctx, manifestPbPath)
 
 	manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity_manifest.list")
 	f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath.OutputPath, matchedSpecs, rebasedDir)
@@ -115,15 +117,18 @@
 	sb.WriteString(manifestGeneratorListPath.String())
 	sb.WriteRune('\n')
 	cmd.Implicit(manifestGeneratorListPath)
+	f.appendToEntry(ctx, manifestGeneratorListPath.OutputPath)
 
 	// 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")
 	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 ")
@@ -150,12 +155,15 @@
 	sb.WriteString(f.partitionName())
 	sb.WriteRune('\n')
 
+	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())
@@ -165,5 +173,7 @@
 	sb.WriteString(keyPath.String())
 	sb.WriteRune('\n')
 
+	f.appendToEntry(ctx, idsigPath)
+
 	android.WriteExecutableFileRuleVerbatim(ctx, fsverityBuilderPath, sb.String())
 }
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index e483fe4..988a57b 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -146,9 +146,16 @@
 				partitionNames[pName] = true
 			}
 			// Get size of the partition by reading the -size.txt file
-			pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
+			var pSize string
+			if size, hasSize := sparseImageSizes[pName]; hasSize {
+				pSize = fmt.Sprintf("$(cat %s)", size)
+			} else {
+				pSize = "0"
+			}
 			cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
-			cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
+			if image, hasImage := sparseImages[pName]; hasImage {
+				cmd.FlagWithInput("--image="+pName+"=", image)
+			}
 		}
 	}
 
@@ -192,6 +199,9 @@
 // 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))
 	name := proptools.String(p.Name)
 	sparseImg = android.PathForModuleOut(ctx, name+".img").OutputPath
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 15cacfb..63cb627 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -27,7 +27,7 @@
 
 type systemImageProperties struct {
 	// Path to the input linker config json file.
-	Linker_config_src *string
+	Linker_config_src *string `android:"path"`
 }
 
 // android_system_image is a specialization of android_filesystem for the 'system' partition.
@@ -38,7 +38,7 @@
 	module.AddProperties(&module.properties)
 	module.filesystem.buildExtraFiles = module.buildExtraFiles
 	module.filesystem.filterPackagingSpec = module.filterPackagingSpec
-	initFilesystemModule(&module.filesystem)
+	initFilesystemModule(module, &module.filesystem)
 	return module
 }
 
@@ -61,7 +61,8 @@
 
 	deps := s.gatherFilteredPackagingSpecs(ctx)
 	ctx.WalkDeps(func(child, parent android.Module) bool {
-		for _, ps := range child.PackagingSpecs() {
+		for _, ps := range android.OtherModuleProviderOrDefault(
+			ctx, child, android.InstallFilesProvider).PackagingSpecs {
 			if _, ok := deps[ps.RelPathInPackage()]; ok {
 				modulesInPackageByModule[child] = true
 				modulesInPackageByName[child.Name()] = true
@@ -94,9 +95,10 @@
 	return output
 }
 
-// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" partition.
-// Note that "apex" module installs its contents to "apex"(fake partition) as well
+// 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"
+	return s.filesystem.filterInstallablePackagingSpec(ps) &&
+		(ps.Partition() == "system" || ps.Partition() == "root")
 }
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 0c6e7f4..1d64796 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -59,7 +59,7 @@
 
 	// List of filesystem modules that this vbmeta has descriptors for. The filesystem modules
 	// have to be signed (use_avb: true).
-	Partitions []string
+	Partitions proptools.Configurable[[]string]
 
 	// List of chained partitions that this vbmeta deletages the verification.
 	Chained_partitions []chainedPartitionProperties
@@ -110,7 +110,7 @@
 var vbmetaPartitionDep = vbmetaDep{kind: "partition"}
 
 func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions...)
+	ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions.GetOrDefault(ctx, nil)...)
 }
 
 func (v *vbmeta) installFileName() string {
@@ -213,6 +213,7 @@
 	ctx.InstallFile(v.installDir, v.installFileName(), v.output)
 
 	ctx.SetOutputFiles([]android.Path{v.output}, "")
+	android.SetProvider(ctx, android.AndroidMkInfoProvider, v.prepareAndroidMKProviderInfo())
 }
 
 // Returns the embedded shell command that prints the rollback index
@@ -265,20 +266,17 @@
 	return result
 }
 
-var _ android.AndroidMkEntriesProvider = (*vbmeta)(nil)
-
-// Implements android.AndroidMkEntriesProvider
-func (v *vbmeta) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(v.output),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", v.installDir.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName())
-			},
+func (v *vbmeta) prepareAndroidMKProviderInfo() *android.AndroidMkProviderInfo {
+	providerData := android.AndroidMkProviderInfo{
+		PrimaryInfo: android.AndroidMkInfo{
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(v.output),
+			EntryMap:   make(map[string][]string),
 		},
-	}}
+	}
+	providerData.PrimaryInfo.SetString("LOCAL_MODULE_PATH", v.installDir.String())
+	providerData.PrimaryInfo.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName())
+	return &providerData
 }
 
 var _ Filesystem = (*vbmeta)(nil)
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 306d65e..a059837 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -449,7 +449,7 @@
 	}
 }
 
-func IsValid(ctx android.ConfigAndErrorContext, fuzzModule FuzzModule) bool {
+func IsValid(ctx android.ConfigurableEvaluatorContext, fuzzModule FuzzModule) bool {
 	// Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
 	// fuzz targets we're going to package anyway.
 	if !fuzzModule.Enabled(ctx) || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() {
diff --git a/genrule/Android.bp b/genrule/Android.bp
index 7331741..f4197e6 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -22,4 +22,6 @@
         "genrule_test.go",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
index 7c71b77..45a7f72 100644
--- a/genrule/allowlists.go
+++ b/genrule/allowlists.go
@@ -17,8 +17,6 @@
 var (
 	SandboxingDenyModuleList = []string{
 		// go/keep-sorted start
-		"aidl_camera_build_version",
-		"com.google.pixel.camera.hal.manifest",
 		// go/keep-sorted end
 	}
 )
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 06a7e18..a48038b 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -25,7 +25,6 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -139,13 +138,29 @@
 	Export_include_dirs []string
 
 	// list of input files
-	Srcs []string `android:"path,arch_variant"`
+	Srcs         proptools.Configurable[[]string] `android:"path,arch_variant"`
+	ResolvedSrcs []string                         `blueprint:"mutated"`
 
 	// input files to exclude
 	Exclude_srcs []string `android:"path,arch_variant"`
 
 	// Enable restat to update the output only if the output is changed
 	Write_if_changed *bool
+
+	// When set to true, an additional $(build_number_file) label will be available
+	// to use in the cmd. This will be the location of a text file containing the
+	// build number. The dependency on this file will be "order-only", meaning that
+	// the genrule will not rerun when only this file changes, to avoid rerunning
+	// the genrule every build, because the build number changes every build.
+	// This also means that you should not attempt to consume the build number from
+	// the result of this genrule in another build rule. If you do, the build number
+	// in the second build rule will be stale when the second build rule rebuilds
+	// but this genrule does not. Only certain allowlisted modules are allowed to
+	// use this property, usages of the build number should be kept to the absolute
+	// minimum. Particularly no modules on the system image may include the build
+	// number. Prefer using libbuildversion via the use_version_lib property on
+	// cc modules.
+	Uses_order_only_build_number_file *bool
 }
 
 type Module struct {
@@ -213,21 +228,7 @@
 	return g.outputDeps
 }
 
-func (g *Module) OutputFiles(tag string) (android.Paths, error) {
-	if tag == "" {
-		return append(android.Paths{}, g.outputFiles...), nil
-	}
-	// otherwise, tag should match one of outputs
-	for _, outputFile := range g.outputFiles {
-		if outputFile.Rel() == tag {
-			return android.Paths{outputFile}, nil
-		}
-	}
-	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
-
 var _ android.SourceFileProducer = (*Module)(nil)
-var _ android.OutputFileProducer = (*Module)(nil)
 
 func toolDepsMutator(ctx android.BottomUpMutatorContext) {
 	if g, ok := ctx.Module().(*Module); ok {
@@ -241,6 +242,37 @@
 	}
 }
 
+var buildNumberAllowlistKey = android.NewOnceKey("genruleBuildNumberAllowlistKey")
+
+// This allowlist should be kept to the bare minimum, it's
+// intended for things that existed before the build number
+// was tightly controlled. Prefer using libbuildversion
+// via the use_version_lib property of cc modules.
+// This is a function instead of a global map so that
+// soong plugins cannot add entries to the allowlist
+func isModuleInBuildNumberAllowlist(ctx android.ModuleContext) bool {
+	allowlist := ctx.Config().Once(buildNumberAllowlistKey, func() interface{} {
+		// Define the allowlist as a list and then copy it into a map so that
+		// gofmt doesn't change unnecessary lines trying to align the values of the map.
+		allowlist := []string{
+			// go/keep-sorted start
+			"build/soong/tests:gen",
+			"hardware/google/camera/common/hal/aidl_service:aidl_camera_build_version",
+			"tools/tradefederation/core:tradefed_zip",
+			"vendor/google/services/LyricCameraHAL/src/apex:com.google.pixel.camera.hal.manifest",
+			// go/keep-sorted end
+		}
+		allowlistMap := make(map[string]bool, len(allowlist))
+		for _, a := range allowlist {
+			allowlistMap[a] = true
+		}
+		return allowlistMap
+	}).(map[string]bool)
+
+	_, ok := allowlist[ctx.ModuleDir()+":"+ctx.ModuleName()]
+	return ok
+}
+
 // generateCommonBuildActions contains build action generation logic
 // common to both the mixed build case and the legacy case of genrule processing.
 // To fully support genrule in mixed builds, the contents of this function should
@@ -309,7 +341,8 @@
 						ctx.ModuleErrorf("host tool %q missing output file", tool)
 						return
 					}
-					if specs := t.TransitivePackagingSpecs(); specs != nil {
+					if specs := android.OtherModuleProviderOrDefault(
+						ctx, t, 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
@@ -331,11 +364,6 @@
 						tools = append(tools, path.Path())
 						addLocationLabel(tag.label, toolLocation{android.Paths{path.Path()}})
 					}
-				case bootstrap.GoBinaryTool:
-					// A GoBinaryTool provides the install path to a tool, which will be copied.
-					p := android.PathForGoBinary(ctx, t)
-					tools = append(tools, p)
-					addLocationLabel(tag.label, toolLocation{android.Paths{p}})
 				default:
 					ctx.ModuleErrorf("%q is not a host tool provider", tool)
 					return
@@ -396,7 +424,8 @@
 		}
 		return srcFiles
 	}
-	srcFiles := addLabelsForInputs("srcs", g.properties.Srcs, g.properties.Exclude_srcs)
+	g.properties.ResolvedSrcs = g.properties.Srcs.GetOrDefault(ctx, nil)
+	srcFiles := addLabelsForInputs("srcs", g.properties.ResolvedSrcs, g.properties.Exclude_srcs)
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcFiles.Strings()})
 
 	var copyFrom android.Paths
@@ -482,6 +511,11 @@
 				return strings.Join(proptools.ShellEscapeList(sandboxOuts), " "), nil
 			case "genDir":
 				return proptools.ShellEscape(cmd.PathForOutput(task.genDir)), nil
+			case "build_number_file":
+				if !proptools.Bool(g.properties.Uses_order_only_build_number_file) {
+					return reportError("to use the $(build_number_file) label, you must set uses_order_only_build_number_file: true")
+				}
+				return proptools.ShellEscape(cmd.PathForInput(ctx.Config().BuildNumberFile(ctx))), nil
 			default:
 				if strings.HasPrefix(name, "location ") {
 					label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@@ -528,6 +562,12 @@
 		cmd.Implicits(task.in)
 		cmd.ImplicitTools(tools)
 		cmd.ImplicitPackagedTools(packagedTools)
+		if proptools.Bool(g.properties.Uses_order_only_build_number_file) {
+			if !isModuleInBuildNumberAllowlist(ctx) {
+				ctx.ModuleErrorf("Only allowlisted modules may use uses_order_only_build_number_file: true")
+			}
+			cmd.OrderOnly(ctx.Config().BuildNumberFile(ctx))
+		}
 
 		// Create the rule to run the genrule command inside sbox.
 		rule.Build(name, desc)
@@ -585,12 +625,25 @@
 		})
 		g.outputDeps = android.Paths{phonyFile}
 	}
+
+	g.setOutputFiles(ctx)
+}
+
+func (g *Module) setOutputFiles(ctx android.ModuleContext) {
+	if len(g.outputFiles) == 0 {
+		return
+	}
+	ctx.SetOutputFiles(g.outputFiles, "")
+	// non-empty-string-tag should match one of the outputs
+	for _, files := range g.outputFiles {
+		ctx.SetOutputFiles(android.Paths{files}, files.Rel())
+	}
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
-func (g *Module) IDEInfo(dpInfo *android.IdeInfo) {
+func (g *Module) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
 	dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...)
-	for _, src := range g.properties.Srcs {
+	for _, src := range g.properties.ResolvedSrcs {
 		if strings.HasPrefix(src, ":") {
 			src = strings.Trim(src, ":")
 			dpInfo.Deps = append(dpInfo.Deps, src)
@@ -644,6 +697,8 @@
 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 }
@@ -753,6 +808,7 @@
 func GenSrcsFactory() android.Module {
 	m := NewGenSrcs()
 	android.InitAndroidModule(m)
+	android.InitDefaultableModule(m)
 	return m
 }
 
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 1df887b..9278f15 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -19,6 +19,7 @@
 	"os"
 	"regexp"
 	"strconv"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -694,7 +695,7 @@
 	android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
 
 	expectedSrcs := []string{"in1"}
-	android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
+	android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.ResolvedSrcs)
 }
 
 func TestGenruleAllowMissingDependencies(t *testing.T) {
@@ -1192,6 +1193,68 @@
 	}
 }
 
+func TestGenruleUsesOrderOnlyBuildNumberFile(t *testing.T) {
+	testCases := []struct {
+		name            string
+		bp              string
+		fs              android.MockFS
+		expectedError   string
+		expectedCommand string
+	}{
+		{
+			name: "not allowed when not in allowlist",
+			fs: android.MockFS{
+				"foo/Android.bp": []byte(`
+genrule {
+	name: "gen",
+	uses_order_only_build_number_file: true,
+	cmd: "cp $(build_number_file) $(out)",
+	out: ["out.txt"],
+}
+`),
+			},
+			expectedError: `Only allowlisted modules may use uses_order_only_build_number_file: true`,
+		},
+		{
+			name: "normal",
+			fs: android.MockFS{
+				"build/soong/tests/Android.bp": []byte(`
+genrule {
+	name: "gen",
+	uses_order_only_build_number_file: true,
+	cmd: "cp $(build_number_file) $(out)",
+	out: ["out.txt"],
+}
+`),
+			},
+			expectedCommand: `cp BUILD_NUMBER_FILE __SBOX_SANDBOX_DIR__/out/out.txt`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			fixtures := android.GroupFixturePreparers(
+				prepareForGenRuleTest,
+				android.PrepareForTestWithVisibility,
+				android.FixtureMergeMockFs(tc.fs),
+				android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) {
+					config.TestProductVariables.BuildNumberFile = proptools.StringPtr("build_number.txt")
+				}),
+			)
+			if tc.expectedError != "" {
+				fixtures = fixtures.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(tc.expectedError))
+			}
+			result := fixtures.RunTest(t)
+
+			if tc.expectedError == "" {
+				tc.expectedCommand = strings.ReplaceAll(tc.expectedCommand, "BUILD_NUMBER_FILE", result.Config.SoongOutDir()+"/build_number.txt")
+				gen := result.Module("gen", "").(*Module)
+				android.AssertStringEquals(t, "raw commands", tc.expectedCommand, gen.rawCommands[0])
+			}
+		})
+	}
+}
+
 type testTool struct {
 	android.ModuleBase
 	outputFile android.Path
@@ -1254,12 +1317,6 @@
 	t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
 }
 
-func (t *testOutputProducer) OutputFiles(tag string) (android.Paths, error) {
-	return android.Paths{t.outputFile}, nil
-}
-
-var _ android.OutputFileProducer = (*testOutputProducer)(nil)
-
 type useSource struct {
 	android.ModuleBase
 	props struct {
diff --git a/go.mod b/go.mod
index 13834fc..aa43066 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,5 @@
 require (
 	github.com/google/blueprint v0.0.0
 	google.golang.org/protobuf v0.0.0
-	prebuilts/bazel/common/proto/analysis_v2 v0.0.0
 	go.starlark.net v0.0.0
 )
diff --git a/go.work b/go.work
index 9a7e6db..46a135b 100644
--- a/go.work
+++ b/go.work
@@ -5,8 +5,6 @@
 	../../external/go-cmp
 	../../external/golang-protobuf
 	../../external/starlark-go
-	../../prebuilts/bazel/common/proto/analysis_v2
-	../../prebuilts/bazel/common/proto/build
 	../blueprint
 )
 
@@ -15,7 +13,5 @@
 	github.com/google/blueprint v0.0.0 => ../blueprint
 	github.com/google/go-cmp v0.0.0 => ../../external/go-cmp
 	google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
-	prebuilts/bazel/common/proto/analysis_v2 v0.0.0 => ../../prebuilts/bazel/common/proto/analysis_v2
-	prebuilts/bazel/common/proto/build v0.0.0 => ../../prebuilts/bazel/common/proto/build
 	go.starlark.net v0.0.0 => ../../external/starlark-go
 )
diff --git a/golang/Android.bp b/golang/Android.bp
new file mode 100644
index 0000000..3eae94f
--- /dev/null
+++ b/golang/Android.bp
@@ -0,0 +1,22 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-golang",
+    pkgPath: "android/soong/golang",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "blueprint-bootstrap",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "golang.go",
+    ],
+    testSrcs: [
+        "golang_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/golang/golang.go b/golang/golang.go
new file mode 100644
index 0000000..618a085
--- /dev/null
+++ b/golang/golang.go
@@ -0,0 +1,136 @@
+// 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 golang wraps the blueprint blueprint_go_binary and bootstrap_go_binary module types in versions
+// that implement android.Module that are used when building in Soong.  This simplifies the code in Soong
+// so it can always assume modules are an android.Module.
+// The original blueprint blueprint_go_binary and bootstrap_go_binary module types are still used during
+// bootstrapping, so the Android.bp entries for these module types must be compatible with both the
+// original blueprint module types and these wrapped module types.
+package golang
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/bootstrap"
+)
+
+func init() {
+	// Wrap the blueprint Go module types with Soong ones that interoperate with the rest of the Soong modules.
+	bootstrap.GoModuleTypesAreWrapped()
+	RegisterGoModuleTypes(android.InitRegistrationContext)
+}
+
+func RegisterGoModuleTypes(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("bootstrap_go_package", goPackageModuleFactory)
+	ctx.RegisterModuleType("blueprint_go_binary", goBinaryModuleFactory)
+}
+
+// A GoPackage is a module for building Go packages.
+type GoPackage struct {
+	android.ModuleBase
+	bootstrap.GoPackage
+}
+
+func goPackageModuleFactory() android.Module {
+	module := &GoPackage{}
+	module.AddProperties(module.Properties()...)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
+func (g *GoPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	// The embedded ModuleBase and bootstrap.GoPackage each implement GenerateBuildActions,
+	// the delegation has to be implemented manually to disambiguate.  Call ModuleBase's
+	// GenerateBuildActions, which will call GenerateAndroidBuildActions, which will call
+	// bootstrap.GoPackage.GenerateBuildActions.
+	g.ModuleBase.GenerateBuildActions(ctx)
+}
+
+func (g *GoPackage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	g.GoPackage.GenerateBuildActions(ctx.BlueprintModuleContext())
+}
+
+// A GoBinary is a module for building executable binaries from Go sources.
+type GoBinary struct {
+	android.ModuleBase
+	bootstrap.GoBinary
+
+	outputFile android.Path
+}
+
+func goBinaryModuleFactory() android.Module {
+	module := &GoBinary{}
+	module.AddProperties(module.Properties()...)
+	android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibFirst)
+	return module
+}
+
+func (g *GoBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
+	// The embedded ModuleBase and bootstrap.GoBinary each implement GenerateBuildActions,
+	// the delegation has to be implemented manually to disambiguate.  Call ModuleBase's
+	// GenerateBuildActions, which will call GenerateAndroidBuildActions, which will call
+	// bootstrap.GoBinary.GenerateBuildActions.
+	g.ModuleBase.GenerateBuildActions(ctx)
+}
+
+func (g *GoBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Install the file in Soong instead of blueprint so that Soong knows about the install rules.
+	g.GoBinary.SetSkipInstall()
+
+	// Run the build actions from the wrapped blueprint bootstrap module.
+	g.GoBinary.GenerateBuildActions(ctx.BlueprintModuleContext())
+
+	// Translate the bootstrap module's string path into a Path
+	outputFile := android.PathForArbitraryOutput(ctx, android.Rel(ctx, ctx.Config().OutDir(), g.IntermediateFile())).WithoutRel()
+	g.outputFile = outputFile
+
+	// Don't create install rules for modules used by bootstrap, the install command line will differ from
+	// what was used during bootstrap, which will cause ninja to rebuild the module on the next run,
+	// triggering reanalysis.
+	if !usedByBootstrap(ctx.ModuleName()) {
+		installPath := ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), outputFile)
+
+		// Modules in an unexported namespace have no install rule, only add modules in the exported namespaces
+		// to the blueprint_tools phony rules.
+		if !ctx.Config().KatiEnabled() || g.ExportedToMake() {
+			ctx.Phony("blueprint_tools", installPath)
+		}
+	}
+
+	ctx.SetOutputFiles(android.Paths{outputFile}, "")
+}
+
+func usedByBootstrap(name string) bool {
+	switch name {
+	case "loadplugins", "soong_build":
+		return true
+	default:
+		return false
+	}
+}
+
+func (g *GoBinary) HostToolPath() android.OptionalPath {
+	return android.OptionalPathForPath(g.outputFile)
+}
+
+func (g *GoBinary) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{
+		{
+			Class:      "EXECUTABLES",
+			OutputFile: android.OptionalPathForPath(g.outputFile),
+			Include:    "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
+		},
+	}
+}
diff --git a/golang/golang_test.go b/golang/golang_test.go
new file mode 100644
index 0000000..b512144
--- /dev/null
+++ b/golang/golang_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 golang
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint/bootstrap"
+	"path/filepath"
+	"testing"
+)
+
+func TestGolang(t *testing.T) {
+	bp := `
+		bootstrap_go_package {
+			name: "gopkg",
+			pkgPath: "test/pkg",
+		}
+
+		blueprint_go_binary {
+			name: "gobin",
+			deps: ["gopkg"],
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		android.PrepareForTestWithArchMutator,
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			RegisterGoModuleTypes(ctx)
+			ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+				ctx.BottomUpBlueprint("bootstrap_deps", bootstrap.BootstrapDeps)
+			})
+		}),
+	).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, ""))
+}
diff --git a/java/Android.bp b/java/Android.bp
index 54b36ab..926a294 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -87,6 +87,7 @@
         "app_set_test.go",
         "app_test.go",
         "code_metadata_test.go",
+        "container_test.go",
         "bootclasspath_fragment_test.go",
         "device_host_converter_test.go",
         "dex_test.go",
@@ -100,6 +101,7 @@
         "hiddenapi_singleton_test.go",
         "jacoco_test.go",
         "java_test.go",
+        "jarjar_test.go",
         "jdeps_test.go",
         "kotlin_test.go",
         "lint_test.go",
@@ -118,4 +120,5 @@
         "test_spec_test.go",
     ],
     pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
 }
diff --git a/java/aapt2.go b/java/aapt2.go
index f704fc6..61cf373 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -69,7 +69,7 @@
 
 // aapt2Compile compiles resources and puts the results in the requested directory.
 func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths,
-	flags []string, productToFilter string) android.WritablePaths {
+	flags []string, productToFilter string, featureFlagsPaths android.Paths) android.WritablePaths {
 	if productToFilter != "" && productToFilter != "default" {
 		// --filter-product leaves only product-specific resources. Product-specific resources only exist
 		// in value resources (values/*.xml), so filter value resource files only. Ignore other types of
@@ -85,6 +85,10 @@
 		flags = append([]string{"--filter-product " + productToFilter}, flags...)
 	}
 
+	for _, featureFlagsPath := range android.SortedUniquePaths(featureFlagsPaths) {
+		flags = append(flags, "--feature-flags", "@"+featureFlagsPath.String())
+	}
+
 	// Shard the input paths so that they can be processed in parallel. If we shard them into too
 	// small chunks, the additional cost of spinning up aapt2 outweighs the performance gain. The
 	// current shard size, 100, seems to be a good balance between the added cost and the gain.
@@ -112,6 +116,7 @@
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        aapt2CompileRule,
 			Description: "aapt2 compile " + dir.String() + shardDesc,
+			Implicits:   featureFlagsPaths,
 			Inputs:      shard,
 			Outputs:     outPaths,
 			Args: map[string]string{
diff --git a/java/aar.go b/java/aar.go
index 4691d70..368ad33 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -15,10 +15,10 @@
 package java
 
 import (
+	"crypto/sha256"
 	"fmt"
 	"path/filepath"
 	"slices"
-	"strconv"
 	"strings"
 
 	"android/soong/android"
@@ -76,7 +76,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
+	Resource_dirs []string `android:"path"`
 
 	// list of zip files containing Android resources.
 	Resource_zips []string `android:"path"`
@@ -166,7 +166,11 @@
 func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool {
 	return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault()) &&
 		// TODO(b/331641946): remove this when ResourceProcessorBusyBox supports generating shared libraries.
-		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib")
+		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib") &&
+		// Use the legacy resource processor in kythe builds.
+		// The legacy resource processor creates an R.srcjar, which kythe can use for generating crossrefs.
+		// TODO(b/354854007): Re-enable BusyBox in kythe builds
+		!ctx.Config().EmitXrefRules()
 }
 
 func (a *aapt) filterProduct() string {
@@ -232,18 +236,20 @@
 		rroDirs = append(rroDirs, resRRODirs...)
 	}
 
+	assetDirsHasher := sha256.New()
 	var assetDeps android.Paths
-	for i, dir := range assetDirs {
+	for _, dir := range assetDirs {
 		// Add a dependency on every file in the asset directory.  This ensures the aapt2
 		// rule will be rerun if one of the files in the asset directory is modified.
-		assetDeps = append(assetDeps, androidResourceGlob(ctx, dir)...)
+		dirContents := androidResourceGlob(ctx, dir)
+		assetDeps = append(assetDeps, dirContents...)
 
-		// Add a dependency on a file that contains a list of all the files in the asset directory.
+		// Add a hash of all the files in the asset directory to the command line.
 		// This ensures the aapt2 rule will be run if a file is removed from the asset directory,
 		// or a file is added whose timestamp is older than the output of aapt2.
-		assetFileListFile := android.PathForModuleOut(ctx, "asset_dir_globs", strconv.Itoa(i)+".glob")
-		androidResourceGlobList(ctx, dir, assetFileListFile)
-		assetDeps = append(assetDeps, assetFileListFile)
+		for _, path := range dirContents.Strings() {
+			assetDirsHasher.Write([]byte(path))
+		}
 	}
 
 	assetDirStrings := assetDirs.Strings()
@@ -278,6 +284,7 @@
 	linkDeps = append(linkDeps, manifestPath)
 
 	linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
+	linkFlags = append(linkFlags, fmt.Sprintf("$$(: %x)", assetDirsHasher.Sum(nil)))
 	linkDeps = append(linkDeps, assetDeps...)
 
 	// Returns the effective version for {min|target}_sdk_version
@@ -402,6 +409,7 @@
 			packageName:        a.manifestValues.applicationId,
 		}
 		a.mergedManifestFile = manifestMerger(ctx, transitiveManifestPaths[0], manifestMergerParams)
+		ctx.CheckbuildFile(a.mergedManifestFile)
 		if !a.isLibrary {
 			// Only use the merged manifest for applications.  For libraries, the transitive closure of manifests
 			// will be propagated to the final application and merged there.  The merged manifest for libraries is
@@ -443,7 +451,8 @@
 	var compiledResDirs []android.Paths
 	for _, dir := range resDirs {
 		a.resourceFiles = append(a.resourceFiles, dir.files...)
-		compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files, compileFlags, a.filterProduct()).Paths())
+		compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files,
+			compileFlags, a.filterProduct(), opts.aconfigTextFiles).Paths())
 	}
 
 	for i, zip := range resZips {
@@ -502,7 +511,8 @@
 	}
 
 	for _, dir := range overlayDirs {
-		compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files, compileFlags, a.filterProduct()).Paths()...)
+		compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files,
+			compileFlags, a.filterProduct(), opts.aconfigTextFiles).Paths()...)
 	}
 
 	var splitPackages android.WritablePaths
@@ -534,6 +544,8 @@
 	aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt,
 		linkFlags, linkDeps, compiledRes, compiledOverlay, transitiveAssets, splitPackages,
 		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.
 	if android.PrefixInList(linkFlags, "-A ") {
@@ -801,18 +813,6 @@
 	aarFile android.WritablePath
 }
 
-var _ android.OutputFileProducer = (*AndroidLibrary)(nil)
-
-// For OutputFileProducer interface
-func (a *AndroidLibrary) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case ".aar":
-		return []android.Path{a.aarFile}, nil
-	default:
-		return a.Library.OutputFiles(tag)
-	}
-}
-
 var _ AndroidLibraryDependency = (*AndroidLibrary)(nil)
 
 func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -834,12 +834,13 @@
 	if a.usesLibrary.shouldDisableDexpreopt {
 		a.dexpreopter.disableDexpreopt()
 	}
+	aconfigTextFilePaths := getAconfigFilePaths(ctx)
 	a.aapt.buildActions(ctx,
 		aaptBuildActionOptions{
 			sdkContext:                     android.SdkContext(a),
 			classLoaderContexts:            a.classLoaderContexts,
 			enforceDefaultTargetSdkVersion: false,
-			aconfigTextFiles:               getAconfigFilePaths(ctx),
+			aconfigTextFiles:               aconfigTextFilePaths,
 			usesLibrary:                    &a.usesLibrary,
 		},
 	)
@@ -889,13 +890,12 @@
 		extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
 	}
 
-	a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars)
+	a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
 
 	a.aarFile = android.PathForModuleOut(ctx, ctx.ModuleName()+".aar")
 	var res android.Paths
 	if a.androidLibraryProperties.BuildAAR {
 		BuildAAR(ctx, a.aarFile, a.outputFile, a.manifestPath, a.rTxt, res)
-		ctx.CheckbuildFile(a.aarFile)
 	}
 
 	prebuiltJniPackages := android.Paths{}
@@ -909,14 +909,25 @@
 			JniPackages: prebuiltJniPackages,
 		})
 	}
+
+	android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{
+		AconfigTextFiles: aconfigTextFilePaths,
+	})
+
+	a.setOutputFiles(ctx)
 }
 
-func (a *AndroidLibrary) IDEInfo(dpInfo *android.IdeInfo) {
-	a.Library.IDEInfo(dpInfo)
-	a.aapt.IDEInfo(dpInfo)
+func (a *AndroidLibrary) setOutputFiles(ctx android.ModuleContext) {
+	ctx.SetOutputFiles([]android.Path{a.aarFile}, ".aar")
+	setOutputFiles(ctx, a.Library.Module)
 }
 
-func (a *aapt) IDEInfo(dpInfo *android.IdeInfo) {
+func (a *AndroidLibrary) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
+	a.Library.IDEInfo(ctx, dpInfo)
+	a.aapt.IDEInfo(ctx, dpInfo)
+}
+
+func (a *aapt) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
 	if a.rJar != nil {
 		dpInfo.Jars = append(dpInfo.Jars, a.rJar.String())
 	}
@@ -965,7 +976,7 @@
 	// Defaults to sdk_version if not set. See sdk_version for possible values.
 	Min_sdk_version *string
 	// List of java static libraries that the included ARR (android library prebuilts) has dependencies to.
-	Static_libs []string
+	Static_libs proptools.Configurable[[]string]
 	// List of java libraries that the included ARR (android library prebuilts) has dependencies to.
 	Libs []string
 	// If set to true, run Jetifier against .aar file. Defaults to false.
@@ -988,21 +999,21 @@
 	// Functionality common to Module and Import.
 	embeddableInModuleAndImport
 
-	providesTransitiveHeaderJars
+	providesTransitiveHeaderJarsForR8
 
 	properties AARImportProperties
 
-	headerJarFile                      android.WritablePath
-	implementationJarFile              android.WritablePath
-	implementationAndResourcesJarFile  android.WritablePath
-	proguardFlags                      android.WritablePath
-	exportPackage                      android.WritablePath
+	headerJarFile                      android.Path
+	implementationJarFile              android.Path
+	implementationAndResourcesJarFile  android.Path
+	proguardFlags                      android.Path
+	exportPackage                      android.Path
 	transitiveAaptResourcePackagesFile android.Path
-	extraAaptPackagesFile              android.WritablePath
+	extraAaptPackagesFile              android.Path
 	manifest                           android.Path
-	assetsPackage                      android.WritablePath
-	rTxt                               android.WritablePath
-	rJar                               android.WritablePath
+	assetsPackage                      android.Path
+	rTxt                               android.Path
+	rJar                               android.Path
 
 	resourcesNodesDepSet *android.DepSet[*resourcesNode]
 	manifestsDepSet      *android.DepSet[android.Path]
@@ -1019,20 +1030,6 @@
 	classLoaderContexts dexpreopt.ClassLoaderContextMap
 }
 
-var _ android.OutputFileProducer = (*AARImport)(nil)
-
-// For OutputFileProducer interface
-func (a *AARImport) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case ".aar":
-		return []android.Path{a.aarPath}, nil
-	case "":
-		return []android.Path{a.implementationAndResourcesJarFile}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (a *AARImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return android.SdkSpecFrom(ctx, String(a.properties.Sdk_version))
 }
@@ -1112,7 +1109,7 @@
 	}
 
 	ctx.AddVariationDependencies(nil, libTag, a.properties.Libs...)
-	ctx.AddVariationDependencies(nil, staticLibTag, a.properties.Static_libs...)
+	ctx.AddVariationDependencies(nil, staticLibTag, a.properties.Static_libs.GetOrDefault(ctx, nil)...)
 
 	a.usesLibrary.deps(ctx, false)
 }
@@ -1168,8 +1165,9 @@
 
 	if Bool(a.properties.Jetifier) {
 		inputFile := a.aarPath
-		a.aarPath = android.PathForModuleOut(ctx, "jetifier", aarName)
-		TransformJetifier(ctx, a.aarPath.(android.WritablePath), inputFile)
+		jetifierPath := android.PathForModuleOut(ctx, "jetifier", aarName)
+		TransformJetifier(ctx, jetifierPath, inputFile)
+		a.aarPath = jetifierPath
 	}
 
 	jarName := ctx.ModuleName() + ".jar"
@@ -1184,14 +1182,14 @@
 		a.manifest = extractedManifest
 	}
 
-	a.rTxt = extractedAARDir.Join(ctx, "R.txt")
-	a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip")
-	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
+	rTxt := extractedAARDir.Join(ctx, "R.txt")
+	assetsPackage := android.PathForModuleOut(ctx, "assets.zip")
+	proguardFlags := extractedAARDir.Join(ctx, "proguard.txt")
 	transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx)
 	android.SetProvider(ctx, ProguardSpecInfoProvider, ProguardSpecInfo{
 		ProguardFlagsFiles: android.NewDepSet[android.Path](
 			android.POSTORDER,
-			android.Paths{a.proguardFlags},
+			android.Paths{proguardFlags},
 			transitiveProguardFlags,
 		),
 		UnconditionallyExportedProguardFlags: android.NewDepSet[android.Path](
@@ -1204,15 +1202,19 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        unzipAAR,
 		Input:       a.aarPath,
-		Outputs:     android.WritablePaths{classpathFile, a.proguardFlags, extractedManifest, a.assetsPackage, a.rTxt},
+		Outputs:     android.WritablePaths{classpathFile, proguardFlags, extractedManifest, assetsPackage, rTxt},
 		Description: "unzip AAR",
 		Args: map[string]string{
 			"outDir":             extractedAARDir.String(),
 			"combinedClassesJar": classpathFile.String(),
-			"assetsPackage":      a.assetsPackage.String(),
+			"assetsPackage":      assetsPackage.String(),
 		},
 	})
 
+	a.proguardFlags = proguardFlags
+	a.assetsPackage = assetsPackage
+	a.rTxt = rTxt
+
 	// Always set --pseudo-localize, it will be stripped out later for release
 	// builds that don't want it.
 	compileFlags := []string{"--pseudo-localize"}
@@ -1220,10 +1222,10 @@
 	flata := compiledResDir.Join(ctx, "gen_res.flata")
 	aapt2CompileZip(ctx, flata, a.aarPath, "res", compileFlags)
 
-	a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk")
+	exportPackage := android.PathForModuleOut(ctx, "package-res.apk")
 	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
 	aaptRTxt := android.PathForModuleOut(ctx, "R.txt")
-	a.extraAaptPackagesFile = android.PathForModuleOut(ctx, "extra_packages")
+	extraAaptPackagesFile := android.PathForModuleOut(ctx, "extra_packages")
 
 	var linkDeps android.Paths
 
@@ -1259,13 +1261,18 @@
 	}
 
 	transitiveAssets := android.ReverseSliceInPlace(staticDeps.assets())
-	aapt2Link(ctx, a.exportPackage, nil, proguardOptionsFile, aaptRTxt,
+	aapt2Link(ctx, exportPackage, nil, proguardOptionsFile, aaptRTxt,
 		linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil, nil)
+	ctx.CheckbuildFile(exportPackage)
+	a.exportPackage = exportPackage
 
-	a.rJar = android.PathForModuleOut(ctx, "busybox/R.jar")
-	resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true, nil, false)
+	rJar := android.PathForModuleOut(ctx, "busybox/R.jar")
+	resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, rJar, nil, true, nil, false)
+	ctx.CheckbuildFile(rJar)
+	a.rJar = rJar
 
-	aapt2ExtractExtraPackages(ctx, a.extraAaptPackagesFile, a.rJar)
+	aapt2ExtractExtraPackages(ctx, extraAaptPackagesFile, a.rJar)
+	a.extraAaptPackagesFile = extraAaptPackagesFile
 
 	resourcesNodesDepSetBuilder := android.NewDepSetBuilder[*resourcesNode](android.TOPOLOGICAL)
 	resourcesNodesDepSetBuilder.Direct(&resourcesNode{
@@ -1292,13 +1299,17 @@
 	android.WriteFileRule(ctx, transitiveAaptResourcePackagesFile, strings.Join(transitiveAaptResourcePackages, "\n"))
 	a.transitiveAaptResourcePackagesFile = transitiveAaptResourcePackagesFile
 
-	a.collectTransitiveHeaderJars(ctx)
+	a.collectTransitiveHeaderJarsForR8(ctx)
 
 	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 
 	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]
+
 	ctx.VisitDirectDeps(func(module android.Module) {
 		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			tag := ctx.OtherModuleDependencyTag(module)
@@ -1307,61 +1318,111 @@
 				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)
+				}
 			}
 		}
 		addCLCFromDep(ctx, module, a.classLoaderContexts)
 		addMissingOptionalUsesLibsFromDep(ctx, module, &a.usesLibrary)
 	})
 
-	var implementationJarFile android.OutputPath
-	if len(staticJars) > 0 {
-		combineJars := append(android.Paths{classpathFile}, staticJars...)
-		implementationJarFile = android.PathForModuleOut(ctx, "combined", jarName).OutputPath
-		TransformJarsToJar(ctx, implementationJarFile, "combine", combineJars, android.OptionalPath{}, false, nil, nil)
+	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)
+
+	var implementationJarFile android.Path
+	var combineJars android.Paths
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		combineJars = completeStaticLibsImplementationJars.ToList()
+	} else {
+		combineJars = append(android.Paths{classpathFile}, staticJars...)
+	}
+
+	if len(combineJars) > 1 {
+		implementationJarOutputPath := android.PathForModuleOut(ctx, "combined", jarName)
+		TransformJarsToJar(ctx, implementationJarOutputPath, "combine", combineJars, android.OptionalPath{}, false, nil, nil)
+		implementationJarFile = implementationJarOutputPath
 	} else {
 		implementationJarFile = classpathFile
 	}
 
 	var resourceJarFile android.Path
-	if len(staticResourceJars) > 1 {
+	var resourceJars android.Paths
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		resourceJars = completeStaticLibsResourceJars.ToList()
+	} else {
+		resourceJars = staticResourceJars
+	}
+	if len(resourceJars) > 1 {
 		combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
-		TransformJarsToJar(ctx, combinedJar, "for resources", staticResourceJars, android.OptionalPath{},
+		TransformJarsToJar(ctx, combinedJar, "for resources", resourceJars, android.OptionalPath{},
 			false, nil, nil)
 		resourceJarFile = combinedJar
-	} else if len(staticResourceJars) == 1 {
-		resourceJarFile = staticResourceJars[0]
+	} else if len(resourceJars) == 1 {
+		resourceJarFile = resourceJars[0]
 	}
 
 	// merge implementation jar with resources if necessary
-	implementationAndResourcesJar := implementationJarFile
-	if resourceJarFile != nil {
-		jars := android.Paths{resourceJarFile, implementationAndResourcesJar}
-		combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath
-		TransformJarsToJar(ctx, combinedJar, "for resources", jars, android.OptionalPath{},
+	var implementationAndResourcesJars android.Paths
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		implementationAndResourcesJars = append(slices.Clone(resourceJars), combineJars...)
+	} else {
+		implementationAndResourcesJars = android.PathsIfNonNil(resourceJarFile, implementationJarFile)
+	}
+	var implementationAndResourcesJar android.Path
+	if len(implementationAndResourcesJars) > 1 {
+		combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
+		TransformJarsToJar(ctx, combinedJar, "for resources", implementationAndResourcesJars, android.OptionalPath{},
 			false, nil, nil)
 		implementationAndResourcesJar = combinedJar
+	} else {
+		implementationAndResourcesJar = implementationAndResourcesJars[0]
 	}
 
 	a.implementationJarFile = implementationJarFile
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
 	a.implementationAndResourcesJarFile = implementationAndResourcesJar.WithoutRel()
 
-	if len(staticHeaderJars) > 0 {
-		combineJars := append(android.Paths{classpathFile}, staticHeaderJars...)
-		a.headerJarFile = android.PathForModuleOut(ctx, "turbine-combined", jarName)
-		TransformJarsToJar(ctx, a.headerJarFile, "combine header jars", combineJars, android.OptionalPath{}, false, nil, nil)
+	var headerJars android.Paths
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		headerJars = completeStaticLibsHeaderJars.ToList()
 	} else {
-		a.headerJarFile = classpathFile
+		headerJars = append(android.Paths{classpathFile}, staticHeaderJars...)
+	}
+	if len(headerJars) > 1 {
+		headerJarFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
+		TransformJarsToJar(ctx, headerJarFile, "combine header jars", headerJars, android.OptionalPath{}, false, nil, nil)
+		a.headerJarFile = headerJarFile
+	} else {
+		a.headerJarFile = headerJars[0]
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
-		HeaderJars:                     android.PathsIfNonNil(a.headerJarFile),
-		ResourceJars:                   android.PathsIfNonNil(resourceJarFile),
-		TransitiveLibsHeaderJars:       a.transitiveLibsHeaderJars,
-		TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars,
-		ImplementationAndResourcesJars: android.PathsIfNonNil(a.implementationAndResourcesJarFile),
-		ImplementationJars:             android.PathsIfNonNil(a.implementationJarFile),
-		StubsLinkType:                  Implementation,
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		ctx.CheckbuildFile(classpathFile)
+	} else {
+		ctx.CheckbuildFile(a.headerJarFile)
+		ctx.CheckbuildFile(a.implementationJarFile)
+	}
+
+	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+		HeaderJars:                             android.PathsIfNonNil(a.headerJarFile),
+		LocalHeaderJars:                        android.PathsIfNonNil(classpathFile),
+		TransitiveStaticLibsHeaderJars:         completeStaticLibsHeaderJars,
+		TransitiveStaticLibsImplementationJars: completeStaticLibsImplementationJars,
+		TransitiveStaticLibsResourceJars:       completeStaticLibsResourceJars,
+		ResourceJars:                           android.PathsIfNonNil(resourceJarFile),
+		TransitiveLibsHeaderJarsForR8:          a.transitiveLibsHeaderJarsForR8,
+		TransitiveStaticLibsHeaderJarsForR8:    a.transitiveStaticLibsHeaderJarsForR8,
+		ImplementationAndResourcesJars:         android.PathsIfNonNil(a.implementationAndResourcesJarFile),
+		ImplementationJars:                     android.PathsIfNonNil(a.implementationJarFile),
+		StubsLinkType:                          Implementation,
 		// TransitiveAconfigFiles: // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 	})
 
@@ -1389,6 +1450,9 @@
 	android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{
 		JniPackages: a.jniPackages,
 	})
+
+	ctx.SetOutputFiles([]android.Path{a.implementationAndResourcesJarFile}, "")
+	ctx.SetOutputFiles([]android.Path{a.aarPath}, ".aar")
 }
 
 func (a *AARImport) HeaderJars() android.Paths {
@@ -1451,3 +1515,7 @@
 	InitJavaModuleMultiTargets(module, android.DeviceSupported)
 	return module
 }
+
+func (a *AARImport) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
+	dpInfo.Jars = append(dpInfo.Jars, a.headerJarFile.String(), a.rJar.String())
+}
diff --git a/java/aar_test.go b/java/aar_test.go
index 18efd20..aa4f0af 100644
--- a/java/aar_test.go
+++ b/java/aar_test.go
@@ -53,7 +53,7 @@
 			appMod := ctx.Module(tc.name, "android_common")
 			appTestMod := ctx.ModuleForTests(tc.name, "android_common")
 
-			info, ok := android.SingletonModuleProvider(ctx, appMod, JniPackageProvider)
+			info, ok := android.OtherModuleProvider(ctx, appMod, JniPackageProvider)
 			if !ok {
 				t.Errorf("expected android_library_import to have JniPackageProvider")
 			}
@@ -159,21 +159,21 @@
 	bar := result.ModuleForTests("bar", "android_common")
 	baz := result.ModuleForTests("baz", "android_common")
 
-	fooOutputPath := android.OutputFileForModule(android.PathContext(nil), foo.Module(), "")
-	barOutputPath := android.OutputFileForModule(android.PathContext(nil), bar.Module(), "")
-	bazOutputPath := android.OutputFileForModule(android.PathContext(nil), baz.Module(), "")
+	fooOutputPaths := foo.OutputFiles(result.TestContext, t, "")
+	barOutputPaths := bar.OutputFiles(result.TestContext, t, "")
+	bazOutputPaths := baz.OutputFiles(result.TestContext, t, "")
 
-	android.AssertPathRelativeToTopEquals(t, "foo output path",
-		"out/soong/.intermediates/foo/android_common/withres/foo.jar", fooOutputPath)
-	android.AssertPathRelativeToTopEquals(t, "bar output path",
-		"out/soong/.intermediates/bar/android_common/aar/bar.jar", barOutputPath)
-	android.AssertPathRelativeToTopEquals(t, "baz output path",
-		"out/soong/.intermediates/baz/android_common/withres/baz.jar", bazOutputPath)
+	android.AssertPathsRelativeToTopEquals(t, "foo output path",
+		[]string{"out/soong/.intermediates/foo/android_common/withres/foo.jar"}, fooOutputPaths)
+	android.AssertPathsRelativeToTopEquals(t, "bar output path",
+		[]string{"out/soong/.intermediates/bar/android_common/aar/bar.jar"}, barOutputPaths)
+	android.AssertPathsRelativeToTopEquals(t, "baz output path",
+		[]string{"out/soong/.intermediates/baz/android_common/withres/baz.jar"}, bazOutputPaths)
 
 	android.AssertStringEquals(t, "foo relative output path",
-		"foo.jar", fooOutputPath.Rel())
+		"foo.jar", fooOutputPaths[0].Rel())
 	android.AssertStringEquals(t, "bar relative output path",
-		"bar.jar", barOutputPath.Rel())
+		"bar.jar", barOutputPaths[0].Rel())
 	android.AssertStringEquals(t, "baz relative output path",
-		"baz.jar", bazOutputPath.Rel())
+		"baz.jar", bazOutputPaths[0].Rel())
 }
diff --git a/java/android_manifest.go b/java/android_manifest.go
index fdbe43b..44b12f1 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -71,12 +71,15 @@
 	return targetSdkVersionLevel.IsPreview() && (ctx.Config().UnbundledBuildApps() || includedInMts(ctx.Module()))
 }
 
-// Helper function that casts android.Module to java.androidTestApp
-// If this type conversion is possible, it queries whether the test app is included in an MTS suite
+// Helper function that returns true if android_test, android_test_helper_app, java_test are in an MTS suite.
 func includedInMts(module android.Module) bool {
 	if test, ok := module.(androidTestApp); ok {
 		return test.includedInTestSuite("mts")
 	}
+	// java_test
+	if test, ok := module.(*Test); ok {
+		return android.PrefixInList(test.testProperties.Test_suites, "mts")
+	}
 	return false
 }
 
diff --git a/java/android_resources.go b/java/android_resources.go
index 038a260..3bb3eb5 100644
--- a/java/android_resources.go
+++ b/java/android_resources.go
@@ -39,15 +39,6 @@
 	return ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), androidResourceIgnoreFilenames)
 }
 
-// androidResourceGlobList creates a rule to write the list of files in the given directory, using
-// the standard exclusion patterns for Android resources, to the given output file.
-func androidResourceGlobList(ctx android.ModuleContext, dir android.Path,
-	fileListFile android.WritablePath) {
-
-	android.GlobToListFileRule(ctx, filepath.Join(dir.String(), "**/*"),
-		androidResourceIgnoreFilenames, fileListFile)
-}
-
 type overlayType int
 
 const (
diff --git a/java/app.go b/java/app.go
index fe98a7f..13f1568 100644
--- a/java/app.go
+++ b/java/app.go
@@ -47,6 +47,13 @@
 		}, "packageName")
 )
 
+type FlagsPackages struct {
+	// Paths to the aconfig dump output text files that are consumed by aapt2
+	AconfigTextFiles android.Paths
+}
+
+var FlagsPackagesProvider = blueprint.NewProvider[FlagsPackages]()
+
 func RegisterAppBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_app", AndroidAppFactory)
 	ctx.RegisterModuleType("android_test", AndroidTestFactory)
@@ -338,7 +345,35 @@
 	}
 }
 
+// TODO(b/156476221): Remove this allowlist
+var (
+	missingMinSdkVersionMtsAllowlist = []string{
+		"CellBroadcastReceiverGoogleUnitTests",
+		"CellBroadcastReceiverUnitTests",
+		"CtsBatterySavingTestCases",
+		"CtsDeviceAndProfileOwnerApp23",
+		"CtsDeviceAndProfileOwnerApp30",
+		"CtsIntentSenderApp",
+		"CtsJobSchedulerTestCases",
+		"CtsMimeMapTestCases",
+		"CtsTareTestCases",
+		"LibStatsPullTests",
+		"MediaProviderClientTests",
+		"TeleServiceTests",
+		"TestExternalImsServiceApp",
+		"TestSmsRetrieverApp",
+		"TetheringPrivilegedTests",
+	}
+)
+
+func checkMinSdkVersionMts(ctx android.ModuleContext, minSdkVersion android.ApiLevel) {
+	if includedInMts(ctx.Module()) && !minSdkVersion.Specified() && !android.InList(ctx.ModuleName(), missingMinSdkVersionMtsAllowlist) {
+		ctx.PropertyErrorf("min_sdk_version", "min_sdk_version is a required property for tests included in MTS")
+	}
+}
+
 func (a *AndroidTestHelperApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	checkMinSdkVersionMts(ctx, a.MinSdkVersion(ctx))
 	applicationId := a.appTestHelperAppProperties.Manifest_values.ApplicationId
 	if applicationId != nil {
 		if a.overridableAppProperties.Package_name != nil {
@@ -478,18 +513,27 @@
 }
 
 func getAconfigFilePaths(ctx android.ModuleContext) (aconfigTextFilePaths android.Paths) {
-	ctx.VisitDirectDepsWithTag(aconfigDeclarationTag, func(dep android.Module) {
-		if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok {
-			aconfigTextFilePaths = append(aconfigTextFilePaths, provider.IntermediateDumpOutputPath)
-		} else {
-			ctx.ModuleErrorf("Only aconfig_declarations module type is allowed for "+
-				"flags_packages property, but %s is not aconfig_declarations module type",
-				dep.Name(),
-			)
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		tag := ctx.OtherModuleDependencyTag(dep)
+		switch tag {
+		case staticLibTag:
+			if flagPackages, ok := android.OtherModuleProvider(ctx, dep, FlagsPackagesProvider); ok {
+				aconfigTextFilePaths = append(aconfigTextFilePaths, flagPackages.AconfigTextFiles...)
+			}
+
+		case aconfigDeclarationTag:
+			if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok {
+				aconfigTextFilePaths = append(aconfigTextFilePaths, provider.IntermediateDumpOutputPath)
+			} else {
+				ctx.ModuleErrorf("Only aconfig_declarations module type is allowed for "+
+					"flags_packages property, but %s is not aconfig_declarations module type",
+					dep.Name(),
+				)
+			}
 		}
 	})
 
-	return aconfigTextFilePaths
+	return android.FirstUniquePaths(aconfigTextFilePaths)
 }
 
 func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) {
@@ -548,6 +592,9 @@
 
 	// Use non final ids if we are doing optimized shrinking and are using R8.
 	nonFinalIds := a.dexProperties.optimizedResourceShrinkingEnabled(ctx) && a.dexer.effectiveOptimizeEnabled()
+
+	aconfigTextFilePaths := getAconfigFilePaths(ctx)
+
 	a.aapt.buildActions(ctx,
 		aaptBuildActionOptions{
 			sdkContext:                     android.SdkContext(a),
@@ -556,13 +603,17 @@
 			enforceDefaultTargetSdkVersion: a.enforceDefaultTargetSdkVersion(),
 			forceNonFinalResourceIDs:       nonFinalIds,
 			extraLinkFlags:                 aaptLinkFlags,
-			aconfigTextFiles:               getAconfigFilePaths(ctx),
+			aconfigTextFiles:               aconfigTextFilePaths,
 			usesLibrary:                    &a.usesLibrary,
 		},
 	)
 
 	// apps manifests are handled by aapt, don't let Module see them
 	a.properties.Manifest = nil
+
+	android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{
+		AconfigTextFiles: aconfigTextFilePaths,
+	})
 }
 
 func (a *AndroidApp) proguardBuildActions(ctx android.ModuleContext) {
@@ -642,7 +693,7 @@
 			extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
 		}
 
-		a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars)
+		a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
 		if a.dexProperties.resourceShrinkingEnabled(ctx) {
 			binaryResources := android.PathForModuleOut(ctx, packageResources.Base()+".binary.out.apk")
 			aapt2Convert(ctx, binaryResources, a.dexer.resourcesOutput.Path(), "binary")
@@ -964,6 +1015,8 @@
 		ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...)
 	}
 
+	ctx.CheckbuildFile(a.outputFile)
+
 	a.buildAppDependencyInfo(ctx)
 
 	providePrebuiltInfo(ctx,
@@ -972,6 +1025,22 @@
 			isPrebuilt:     false,
 		},
 	)
+
+	a.setOutputFiles(ctx)
+}
+
+func (a *AndroidApp) setOutputFiles(ctx android.ModuleContext) {
+	ctx.SetOutputFiles([]android.Path{a.proguardOptionsFile}, ".aapt.proguardOptionsFile")
+	if a.aaptSrcJar != nil {
+		ctx.SetOutputFiles([]android.Path{a.aaptSrcJar}, ".aapt.srcjar")
+	}
+	if a.rJar != nil {
+		ctx.SetOutputFiles([]android.Path{a.rJar}, ".aapt.jar")
+	}
+	ctx.SetOutputFiles([]android.Path{a.outputFile}, ".apk")
+	ctx.SetOutputFiles([]android.Path{a.exportPackage}, ".export-package.apk")
+	ctx.SetOutputFiles([]android.Path{a.aapt.manifestPath}, ".manifest.xml")
+	setOutputFiles(ctx, a.Library.Module)
 }
 
 type appDepsInterface interface {
@@ -1048,7 +1117,7 @@
 						coverageFile:   dep.CoverageOutputFile(),
 						unstrippedFile: dep.UnstrippedOutputFile(),
 						partition:      dep.Partition(),
-						installPaths:   dep.FilesToInstall(),
+						installPaths:   android.OtherModuleProviderOrDefault(ctx, dep, android.InstallFilesProvider).InstallFiles,
 					})
 				} else if ctx.Config().AllowMissingDependencies() {
 					ctx.AddMissingDependencies([]string{otherName})
@@ -1162,36 +1231,11 @@
 	return a.Library.DepIsInSameApex(ctx, dep)
 }
 
-// For OutputFileProducer interface
-func (a *AndroidApp) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	// In some instances, it can be useful to reference the aapt-generated flags from another
-	// target, e.g., system server implements services declared in the framework-res manifest.
-	case ".aapt.proguardOptionsFile":
-		return []android.Path{a.proguardOptionsFile}, nil
-	case ".aapt.srcjar":
-		if a.aaptSrcJar != nil {
-			return []android.Path{a.aaptSrcJar}, nil
-		}
-	case ".aapt.jar":
-		if a.rJar != nil {
-			return []android.Path{a.rJar}, nil
-		}
-	case ".apk":
-		return []android.Path{a.outputFile}, nil
-	case ".export-package.apk":
-		return []android.Path{a.exportPackage}, nil
-	case ".manifest.xml":
-		return []android.Path{a.aapt.manifestPath}, nil
-	}
-	return a.Library.OutputFiles(tag)
-}
-
 func (a *AndroidApp) Privileged() bool {
 	return Bool(a.appProperties.Privileged)
 }
 
-func (a *AndroidApp) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (a *AndroidApp) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
@@ -1207,9 +1251,9 @@
 
 var _ cc.Coverage = (*AndroidApp)(nil)
 
-func (a *AndroidApp) IDEInfo(dpInfo *android.IdeInfo) {
-	a.Library.IDEInfo(dpInfo)
-	a.aapt.IDEInfo(dpInfo)
+func (a *AndroidApp) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
+	a.Library.IDEInfo(ctx, dpInfo)
+	a.aapt.IDEInfo(ctx, dpInfo)
 }
 
 func (a *AndroidApp) productCharacteristicsRROPackageName() string {
@@ -1353,6 +1397,7 @@
 }
 
 func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	checkMinSdkVersionMts(ctx, a.MinSdkVersion(ctx))
 	var configs []tradefed.Config
 	if a.appTestProperties.Instrumentation_target_package != nil {
 		a.additionalAaptFlags = append(a.additionalAaptFlags,
@@ -1434,7 +1479,23 @@
 	return testConfig
 }
 
+func (a *AndroidTestHelperApp) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if len(a.ApexProperties.Apex_available) == 0 && ctx.Config().IsEnvTrue("EMMA_API_MAPPER") {
+		// Instrument the android_test_helper target to log potential API calls at the run time.
+		// Contact android-xts-infra team before using the environment var EMMA_API_MAPPER.
+		ctx.AddVariationDependencies(nil, staticLibTag, "apimapper-helper-device-lib")
+		a.setApiMapper(true)
+	}
+	a.AndroidApp.DepsMutator(ctx)
+}
+
 func (a *AndroidTest) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if len(a.ApexProperties.Apex_available) == 0 && ctx.Config().IsEnvTrue("EMMA_API_MAPPER") {
+		// Instrument the android_test_helper target to log potential API calls at the run time.
+		// Contact android-xts-infra team before using the environment var EMMA_API_MAPPER.
+		ctx.AddVariationDependencies(nil, staticLibTag, "apimapper-helper-device-lib")
+		a.setApiMapper(true)
+	}
 	a.AndroidApp.DepsMutator(ctx)
 }
 
diff --git a/java/app_import.go b/java/app_import.go
index dc8470d..045a89a 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -17,7 +17,6 @@
 // This file contains the module implementations for android_app_import and android_test_import.
 
 import (
-	"fmt"
 	"reflect"
 	"strings"
 
@@ -422,6 +421,8 @@
 		},
 	)
 
+	ctx.SetOutputFiles([]android.Path{a.outputFile}, "")
+
 	// TODO: androidmk converter jni libs
 }
 
@@ -430,6 +431,9 @@
 	var extraArgs []string
 	if a.Privileged() {
 		extraArgs = append(extraArgs, "--privileged")
+		if ctx.Config().UncompressPrivAppDex() {
+			extraArgs = append(extraArgs, "--uncompress-priv-app-dex")
+		}
 	}
 	if proptools.Bool(a.properties.Skip_preprocessed_apk_checks) {
 		extraArgs = append(extraArgs, "--skip-preprocessed-apk-checks")
@@ -461,15 +465,6 @@
 	return a.outputFile
 }
 
-func (a *AndroidAppImport) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return []android.Path{a.outputFile}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (a *AndroidAppImport) JacocoReportClassesFile() android.Path {
 	return nil
 }
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 496fc13..54a5e75 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -777,30 +777,79 @@
 }
 
 func TestAndroidAppImport_Preprocessed(t *testing.T) {
-	ctx, _ := testJava(t, `
-		android_app_import {
-			name: "foo",
-			apk: "prebuilts/apk/app.apk",
-			presigned: true,
-			preprocessed: true,
-		}
-		`)
+	for _, dontUncompressPrivAppDexs := range []bool{false, true} {
+		name := fmt.Sprintf("dontUncompressPrivAppDexs:%t", dontUncompressPrivAppDexs)
+		t.Run(name, func(t *testing.T) {
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.UncompressPrivAppDex = proptools.BoolPtr(!dontUncompressPrivAppDexs)
+				}),
+			).RunTestWithBp(t, `
+				android_app_import {
+					name: "foo",
+					apk: "prebuilts/apk/app.apk",
+					presigned: true,
+					preprocessed: true,
+				}
 
-	apkName := "foo.apk"
-	variant := ctx.ModuleForTests("foo", "android_common")
-	outputBuildParams := variant.Output(apkName).BuildParams
-	if outputBuildParams.Rule.String() != android.Cp.String() {
-		t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
-	}
+				android_app_import {
+					name: "bar",
+					apk: "prebuilts/apk/app.apk",
+					presigned: true,
+					privileged: true,
+					preprocessed: true,
+				}
+			`)
 
-	// Make sure compression and aligning were validated.
-	if outputBuildParams.Validation == nil {
-		t.Errorf("Expected validation rule, but was not found")
-	}
+			// non-privileged app
+			apkName := "foo.apk"
+			variant := result.ModuleForTests("foo", "android_common")
+			outputBuildParams := variant.Output(apkName).BuildParams
+			if outputBuildParams.Rule.String() != android.Cp.String() {
+				t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
+			}
 
-	validationBuildParams := variant.Output("validated-prebuilt/check.stamp").BuildParams
-	if validationBuildParams.Rule.String() != checkPresignedApkRule.String() {
-		t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String())
+			// Make sure compression and aligning were validated.
+			if outputBuildParams.Validation == nil {
+				t.Errorf("Expected validation rule, but was not found")
+			}
+
+			validationBuildParams := variant.Output("validated-prebuilt/check.stamp").BuildParams
+			if validationBuildParams.Rule.String() != checkPresignedApkRule.String() {
+				t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String())
+			}
+
+			expectedScriptArgs := "--preprocessed"
+			actualScriptArgs := validationBuildParams.Args["extraArgs"]
+			android.AssertStringEquals(t, "check script extraArgs", expectedScriptArgs, actualScriptArgs)
+
+			// privileged app
+			apkName = "bar.apk"
+			variant = result.ModuleForTests("bar", "android_common")
+			outputBuildParams = variant.Output(apkName).BuildParams
+			if outputBuildParams.Rule.String() != android.Cp.String() {
+				t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
+			}
+
+			// Make sure compression and aligning were validated.
+			if outputBuildParams.Validation == nil {
+				t.Errorf("Expected validation rule, but was not found")
+			}
+
+			validationBuildParams = variant.Output("validated-prebuilt/check.stamp").BuildParams
+			if validationBuildParams.Rule.String() != checkPresignedApkRule.String() {
+				t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String())
+			}
+
+			expectedScriptArgs = "--privileged"
+			if !dontUncompressPrivAppDexs {
+				expectedScriptArgs += " --uncompress-priv-app-dex"
+			}
+			expectedScriptArgs += " --preprocessed"
+			actualScriptArgs = validationBuildParams.Args["extraArgs"]
+			android.AssertStringEquals(t, "check script extraArgs", expectedScriptArgs, actualScriptArgs)
+		})
 	}
 }
 
diff --git a/java/app_set.go b/java/app_set.go
index 33d3ade..7997570 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -35,7 +35,7 @@
 
 type AndroidAppSetProperties struct {
 	// APK Set path
-	Set *string
+	Set *string `android:"path"`
 
 	// Specifies that this app should be installed to the priv-app directory,
 	// where the system will grant it additional privileges not available to
diff --git a/java/app_set_test.go b/java/app_set_test.go
index 10bc5de..c02b359 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -56,7 +56,7 @@
 	mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0]
 	actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
 	expectedInstallFile := []string{
-		strings.Replace(params.ImplicitOutputs[0].String(), android.OutSoongDir, result.Config.SoongOutDir(), 1),
+		strings.Replace(params.ImplicitOutputs[0].String(), android.TestOutSoongDir, result.Config.SoongOutDir(), 1),
 	}
 	if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
 		t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',",
diff --git a/java/app_test.go b/java/app_test.go
index c15980e..5ec0ad4 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -122,10 +122,7 @@
 		foo.Output(expectedOutput)
 	}
 
-	outputFiles, err := foo.Module().(*AndroidApp).OutputFiles("")
-	if err != nil {
-		t.Fatal(err)
-	}
+	outputFiles := foo.OutputFiles(ctx, t, "")
 	android.AssertPathsRelativeToTopEquals(t, `OutputFiles("")`, expectedOutputs, outputFiles)
 }
 
@@ -3247,7 +3244,7 @@
 		java_library {
 			name: "static-runtime-helper",
 			srcs: ["a.java"],
-			libs: ["runtime-library"],
+			libs: ["runtime-library.impl"],
 			sdk_version: "current",
 		}
 
@@ -3311,7 +3308,7 @@
 			name: "app",
 			srcs: ["a.java"],
 			libs: [
-				"qux",
+				"qux.impl",
 				"quuz.stubs"
 			],
 			static_libs: [
@@ -4192,6 +4189,7 @@
 	bpTemplate := `
 	%v {
 		name: "mytest",
+		min_sdk_version: "34",
 		target_sdk_version: "%v",
 		test_suites: ["othersuite", "%v"],
 	}
@@ -4369,7 +4367,16 @@
 }
 
 func TestAppFlagsPackages(t *testing.T) {
-	ctx := testApp(t, `
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		android.FixtureMergeMockFs(
+			map[string][]byte{
+				"res/layout/layout.xml":         nil,
+				"res/values/strings.xml":        nil,
+				"res/values-en-rUS/strings.xml": nil,
+			},
+		),
+	).RunTestWithBp(t, `
 		android_app {
 			name: "foo",
 			srcs: ["a.java"],
@@ -4401,10 +4408,10 @@
 
 	// android_app module depends on aconfig_declarations listed in flags_packages
 	android.AssertBoolEquals(t, "foo expected to depend on bar", true,
-		CheckModuleHasDependency(t, ctx, "foo", "android_common", "bar"))
+		CheckModuleHasDependency(t, ctx.TestContext, "foo", "android_common", "bar"))
 
 	android.AssertBoolEquals(t, "foo expected to depend on baz", true,
-		CheckModuleHasDependency(t, ctx, "foo", "android_common", "baz"))
+		CheckModuleHasDependency(t, ctx.TestContext, "foo", "android_common", "baz"))
 
 	aapt2LinkRule := foo.Rule("android/soong/java.aapt2Link")
 	linkInFlags := aapt2LinkRule.Args["inFlags"]
@@ -4413,6 +4420,90 @@
 		linkInFlags,
 		"--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt",
 	)
+
+	aapt2CompileRule := foo.Rule("android/soong/java.aapt2Compile")
+	compileFlags := aapt2CompileRule.Args["cFlags"]
+	android.AssertStringDoesContain(t,
+		"aapt2 compile command expected to pass feature flags arguments",
+		compileFlags,
+		"--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt",
+	)
+}
+
+func TestAppFlagsPackagesPropagation(t *testing.T) {
+	ctx := testApp(t, `
+		aconfig_declarations {
+			name: "foo",
+			package: "com.example.package.foo",
+			container: "com.android.foo",
+			srcs: [
+				"foo.aconfig",
+			],
+		}
+		aconfig_declarations {
+			name: "bar",
+			package: "com.example.package.bar",
+			container: "com.android.bar",
+			srcs: [
+				"bar.aconfig",
+			],
+		}
+		aconfig_declarations {
+			name: "baz",
+			package: "com.example.package.baz",
+			container: "com.android.baz",
+			srcs: [
+				"baz.aconfig",
+			],
+		}
+		android_library {
+			name: "foo_lib",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			flags_packages: [
+				"foo",
+			],
+		}
+		android_library {
+			name: "bar_lib",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			flags_packages: [
+				"bar",
+			],
+		}
+		android_app {
+			name: "baz_app",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			flags_packages: [
+				"baz",
+			],
+			static_libs: [
+				"bar_lib",
+			],
+			libs: [
+				"foo_lib",
+			],
+		}
+	`)
+
+	bazApp := ctx.ModuleForTests("baz_app", "android_common")
+
+	// android_app module depends on aconfig_declarations listed in flags_packages
+	// and that of static libs, but not libs
+	aapt2LinkRule := bazApp.Rule("android/soong/java.aapt2Link")
+	linkInFlags := aapt2LinkRule.Args["inFlags"]
+	android.AssertStringDoesContain(t,
+		"aapt2 link command expected to pass feature flags arguments of flags_packages and that of its static libs",
+		linkInFlags,
+		"--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt",
+	)
+	android.AssertStringDoesNotContain(t,
+		"aapt2 link command expected to not pass feature flags arguments of flags_packages of its libs",
+		linkInFlags,
+		"--feature-flags @out/soong/.intermediates/foo/intermediate.txt",
+	)
 }
 
 // Test that dexpreopt is disabled if an optional_uses_libs exists, but does not provide an implementation.
diff --git a/java/base.go b/java/base.go
index 49214d8..b64eb5b 100644
--- a/java/base.go
+++ b/java/base.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"encoding/gob"
 	"fmt"
 	"path/filepath"
 	"reflect"
@@ -80,7 +81,7 @@
 	Libs []string `android:"arch_variant"`
 
 	// list of java libraries that will be compiled into the resulting jar
-	Static_libs []string `android:"arch_variant"`
+	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"`
@@ -91,6 +92,10 @@
 	// if not blank, run jarjar using the specified rules file
 	Jarjar_rules *string `android:"path,arch_variant"`
 
+	// java class names to rename with jarjar when a reverse dependency has a jarjar_prefix
+	// property.
+	Jarjar_rename []string
+
 	// if not blank, used as prefix to generate repackage rule
 	Jarjar_prefix *string
 
@@ -215,6 +220,21 @@
 	// the stubs via libs, but should be set to true when the module depends on
 	// 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 {
+		Enabled *bool
+	}
+
+	// Contributing api surface of the stub module. Is not visible to bp modules, and should
+	// only be set for stub submodules generated by the java_sdk_library
+	Stub_contributing_api *string `blueprint:"mutated"`
+
+	// If true, enable the "ApiMapper" tool on the output jar. "ApiMapper" is a tool to inject
+	// bytecode to log API calls.
+	ApiMapper bool `blueprint:"mutated"`
 }
 
 // Properties that are specific to device modules. Host module factories should not add these when
@@ -441,15 +461,10 @@
 	// inserting into the bootclasspath/classpath of another compile
 	headerJarFile android.Path
 
-	repackagedHeaderJarFile android.Path
-
 	// jar file containing implementation classes including static library dependencies but no
 	// resources
 	implementationJarFile android.Path
 
-	// jar file containing only resources including from static library dependencies
-	resourceJar android.Path
-
 	// args and dependencies to package source files into a srcjar
 	srcJarArgs []string
 	srcJarDeps android.Paths
@@ -520,7 +535,8 @@
 	linter
 
 	// list of the xref extraction files
-	kytheFiles android.Paths
+	kytheFiles       android.Paths
+	kytheKotlinFiles android.Paths
 
 	hideApexVariantFromMake bool
 
@@ -547,6 +563,39 @@
 	// java_aconfig_library or java_library modules that are statically linked
 	// to this module. Does not contain cache files from all transitive dependencies.
 	aconfigCacheFiles android.Paths
+
+	// List of soong module dependencies required to compile the current module.
+	// This information is printed out to `Dependencies` field in module_bp_java_deps.json
+	compileDepNames []string
+
+	ravenizer struct {
+		enabled bool
+	}
+}
+
+var _ android.InstallableModule = (*Module)(nil)
+
+// To satisfy the InstallableModule interface
+func (j *Module) StaticDependencyTags() []blueprint.DependencyTag {
+	return []blueprint.DependencyTag{staticLibTag}
+}
+
+// To satisfy the InstallableModule interface
+func (j *Module) DynamicDependencyTags() []blueprint.DependencyTag {
+	return []blueprint.DependencyTag{libTag, sdkLibTag, bootClasspathTag, systemModulesTag,
+		instrumentationForTag, java9LibTag}
+}
+
+// Overrides android.ModuleBase.InstallInProduct()
+func (j *Module) InstallInProduct() bool {
+	return j.ProductSpecific()
+}
+
+var _ android.StubsAvailableModule = (*Module)(nil)
+
+// To safisfy the StubsAvailableModule interface
+func (j *Module) IsStubsModule() bool {
+	return proptools.Bool(j.properties.Is_stubs_module)
 }
 
 func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -649,35 +698,21 @@
 	android.SetProvider(ctx, hiddenAPIPropertyInfoProvider, hiddenAPIInfo)
 }
 
-func (j *Module) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil
-	case android.DefaultDistTag:
-		return android.Paths{j.outputFile}, nil
-	case ".jar":
-		return android.Paths{j.implementationAndResourcesJar}, nil
-	case ".hjar":
-		return android.Paths{j.headerJarFile}, nil
-	case ".proguard_map":
-		if j.dexer.proguardDictionary.Valid() {
-			return android.Paths{j.dexer.proguardDictionary.Path()}, nil
-		}
-		return nil, fmt.Errorf("%q was requested, but no output file was found.", tag)
-	case ".generated_srcjars":
-		return j.properties.Generated_srcjars, nil
-	case ".lint":
-		if j.linter.outputs.xml != nil {
-			return android.Paths{j.linter.outputs.xml}, nil
-		}
-		return nil, fmt.Errorf("%q was requested, but no output file was found.", tag)
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+// 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")
+	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")
 	}
 }
 
-var _ android.OutputFileProducer = (*Module)(nil)
-
 func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
 	initJavaModule(module, hod, false)
 }
@@ -702,6 +737,10 @@
 		ctx.DeviceConfig().JavaCoverageEnabledForPath(ctx.ModuleDir())
 }
 
+func (j *Module) shouldApiMapper() bool {
+	return j.properties.ApiMapper
+}
+
 func (j *Module) shouldInstrumentStatic(ctx android.BaseModuleContext) bool {
 	return j.properties.Supports_static_instrumentation &&
 		j.shouldInstrument(ctx) &&
@@ -730,6 +769,10 @@
 	j.properties.Instrument = value
 }
 
+func (j *Module) setApiMapper(value bool) {
+	j.properties.ApiMapper = value
+}
+
 func (j *Module) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return android.SdkSpecFrom(ctx, String(j.deviceProperties.Sdk_version))
 }
@@ -786,6 +829,10 @@
 	return j.ApexModuleBase.AvailableFor(what)
 }
 
+func (j *Module) staticLibs(ctx android.BaseModuleContext) []string {
+	return android.RemoveListFromList(j.properties.Static_libs.GetOrDefault(ctx, nil), j.properties.Exclude_static_libs)
+}
+
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
 	if ctx.Device() {
 		j.linter.deps(ctx)
@@ -802,8 +849,7 @@
 
 	libDeps := ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
 
-	j.properties.Static_libs = android.RemoveListFromList(j.properties.Static_libs, j.properties.Exclude_static_libs)
-	ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...)
+	ctx.AddVariationDependencies(nil, staticLibTag, j.staticLibs(ctx)...)
 
 	// Add dependency on libraries that provide additional hidden api annotations.
 	ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...)
@@ -866,9 +912,12 @@
 	if j.hasSrcExt(".kt") {
 		// TODO(ccross): move this to a mutator pass that can tell if generated sources contain
 		// Kotlin files
-		ctx.AddVariationDependencies(nil, kotlinStdlibTag,
-			"kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8")
-		ctx.AddVariationDependencies(nil, kotlinAnnotationsTag, "kotlin-annotations")
+		tag := staticLibTag
+		if !BoolDefault(j.properties.Static_kotlin_stdlib, true) {
+			tag = libTag
+		}
+		ctx.AddVariationDependencies(nil, tag,
+			"kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-annotations")
 	}
 
 	// Framework libraries need special handling in static coverage builds: they should not have
@@ -882,7 +931,7 @@
 		ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
 	}
 
-	if j.useCompose() {
+	if j.useCompose(ctx) {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag,
 			"androidx.compose.compiler_compiler-hosted")
 	}
@@ -1103,8 +1152,7 @@
 	j.properties.Generated_srcjars = append(j.properties.Generated_srcjars, path)
 }
 
-func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars android.Paths) {
-
+func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars, extraDepCombinedJars android.Paths) {
 	// Auto-propagating jarjar rules
 	jarjarProviderData := j.collectJarJarRules(ctx)
 	if jarjarProviderData != nil {
@@ -1119,6 +1167,10 @@
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
+	if re := proptools.Bool(j.properties.Ravenizer.Enabled); re {
+		j.ravenizer.enabled = re
+	}
+
 	deps := j.collectDeps(ctx)
 	flags := j.collectBuilderFlags(ctx, deps)
 
@@ -1198,7 +1250,6 @@
 	// Collect .java and .kt files for AIDEGen
 	j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, uniqueSrcFiles.Strings()...)
 
-	var kotlinJars android.Paths
 	var kotlinHeaderJars android.Paths
 
 	// Prepend extraClasspathJars to classpath so that the resource processor R.jar comes before
@@ -1208,6 +1259,8 @@
 
 	j.aconfigCacheFiles = append(deps.aconfigProtoFiles, j.properties.Aconfig_Cache_files...)
 
+	var localImplementationJars android.Paths
+
 	// If compiling headers then compile them and skip the rest
 	if proptools.Bool(j.properties.Headers_only) {
 		if srcFiles.HasExt(".kt") {
@@ -1217,17 +1270,43 @@
 			ctx.ModuleErrorf("headers_only is enabled but Turbine is disabled.")
 		}
 
-		_, j.headerJarFile, _ =
-			j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName,
-				extraCombinedJars)
+		transitiveStaticLibsHeaderJars := deps.transitiveStaticLibsHeaderJars
+
+		localHeaderJars, combinedHeaderJarFile := j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName,
+			extraCombinedJars)
+
+		combinedHeaderJarFile, jarjared := j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine")
+		if jarjared {
+			localHeaderJars = android.Paths{combinedHeaderJarFile}
+			transitiveStaticLibsHeaderJars = nil
+		}
+		combinedHeaderJarFile, repackaged := j.repackageFlagsIfNecessary(ctx, combinedHeaderJarFile, jarName, "repackage-turbine")
+		if repackaged {
+			localHeaderJars = android.Paths{combinedHeaderJarFile}
+			transitiveStaticLibsHeaderJars = nil
+		}
 		if ctx.Failed() {
 			return
 		}
+		j.headerJarFile = combinedHeaderJarFile
 
-		android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
+		if ctx.Config().UseTransitiveJarsInClasspath() {
+			if len(localHeaderJars) > 0 {
+				ctx.CheckbuildFile(localHeaderJars...)
+			} else {
+				// There are no local sources or resources in this module, so there is nothing to checkbuild.
+				ctx.UncheckedModule()
+			}
+		} else {
+			ctx.CheckbuildFile(j.headerJarFile)
+		}
+
+		android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
 			HeaderJars:                          android.PathsIfNonNil(j.headerJarFile),
-			TransitiveLibsHeaderJars:            j.transitiveLibsHeaderJars,
-			TransitiveStaticLibsHeaderJars:      j.transitiveStaticLibsHeaderJars,
+			LocalHeaderJars:                     localHeaderJars,
+			TransitiveStaticLibsHeaderJars:      android.NewDepSet(android.PREORDER, localHeaderJars, transitiveStaticLibsHeaderJars),
+			TransitiveLibsHeaderJarsForR8:       j.transitiveLibsHeaderJarsForR8,
+			TransitiveStaticLibsHeaderJarsForR8: j.transitiveStaticLibsHeaderJarsForR8,
 			AidlIncludeDirs:                     j.exportAidlIncludeDirs,
 			ExportedPlugins:                     j.exportedPluginJars,
 			ExportedPluginClasses:               j.exportedPluginClasses,
@@ -1275,9 +1354,6 @@
 		// Collect common .kt files for AIDEGen
 		j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, kotlinCommonSrcFiles.Strings()...)
 
-		flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
-		flags.classpath = append(flags.classpath, deps.kotlinAnnotations...)
-
 		flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...)
 		flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...)
 
@@ -1287,7 +1363,7 @@
 			kaptResJar := android.PathForModuleOut(ctx, "kapt", "kapt-res.jar")
 			kotlinKapt(ctx, kaptSrcJar, kaptResJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
 			srcJars = append(srcJars, kaptSrcJar)
-			kotlinJars = append(kotlinJars, kaptResJar)
+			localImplementationJars = append(localImplementationJars, kaptResJar)
 			// Disable annotation processing in javac, it's already been handled by kapt
 			flags.processorPath = nil
 			flags.processors = nil
@@ -1295,37 +1371,29 @@
 
 		kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName)
 		kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName)
-		kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
+		j.kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
 		if ctx.Failed() {
 			return
 		}
 
-		kotlinJarPath := j.repackageFlagsIfNecessary(ctx, kotlinJar.OutputPath, jarName, "kotlinc")
+		kotlinJarPath, _ := j.repackageFlagsIfNecessary(ctx, kotlinJar, jarName, "kotlinc")
 
 		// Make javac rule depend on the kotlinc rule
 		flags.classpath = append(classpath{kotlinHeaderJar}, flags.classpath...)
 
-		kotlinJars = append(kotlinJars, kotlinJarPath)
+		localImplementationJars = append(localImplementationJars, kotlinJarPath)
+
 		kotlinHeaderJars = append(kotlinHeaderJars, kotlinHeaderJar)
-
-		// Jar kotlin classes into the final jar after javac
-		if BoolDefault(j.properties.Static_kotlin_stdlib, true) {
-			kotlinJars = append(kotlinJars, deps.kotlinStdlib...)
-			kotlinJars = append(kotlinJars, deps.kotlinAnnotations...)
-			kotlinHeaderJars = append(kotlinHeaderJars, deps.kotlinStdlib...)
-			kotlinHeaderJars = append(kotlinHeaderJars, deps.kotlinAnnotations...)
-		} else {
-			flags.dexClasspath = append(flags.dexClasspath, deps.kotlinStdlib...)
-			flags.dexClasspath = append(flags.dexClasspath, deps.kotlinAnnotations...)
-		}
 	}
 
-	jars := slices.Clone(kotlinJars)
-
 	j.compiledSrcJars = srcJars
 
+	transitiveStaticLibsHeaderJars := deps.transitiveStaticLibsHeaderJars
+
 	enableSharding := false
-	var headerJarFileWithoutDepsOrJarjar android.Path
+	var localHeaderJars android.Paths
+	var shardingHeaderJars android.Paths
+	var repackagedHeaderJarFile android.Path
 	if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !disableTurbine {
 		if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 {
 			enableSharding = true
@@ -1335,11 +1403,28 @@
 			// allow for the use of annotation processors that do function correctly
 			// with sharding enabled. See: b/77284273.
 		}
-		extraJars := append(slices.Clone(kotlinHeaderJars), extraCombinedJars...)
-		headerJarFileWithoutDepsOrJarjar, j.headerJarFile, j.repackagedHeaderJarFile =
-			j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, extraJars)
-		if ctx.Failed() {
-			return
+		extraJars := slices.Clone(kotlinHeaderJars)
+		extraJars = append(extraJars, extraCombinedJars...)
+		var combinedHeaderJarFile android.Path
+		localHeaderJars, combinedHeaderJarFile = j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, extraJars)
+		shardingHeaderJars = localHeaderJars
+
+		var jarjared bool
+		j.headerJarFile, jarjared = j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine")
+		if jarjared {
+			// jarjar modifies transitive static dependencies, use the combined header jar and drop the transitive
+			// static libs header jars.
+			localHeaderJars = android.Paths{j.headerJarFile}
+			transitiveStaticLibsHeaderJars = nil
+		}
+		var repackaged bool
+		repackagedHeaderJarFile, repackaged = j.repackageFlagsIfNecessary(ctx, j.headerJarFile, jarName, "turbine")
+		if repackaged {
+			// repackage modifies transitive static dependencies, use the combined header jar and drop the transitive
+			// static libs header jars.
+			// TODO(b/356688296): this shouldn't export both the unmodified and repackaged header jars
+			localHeaderJars = android.Paths{j.headerJarFile, repackagedHeaderJarFile}
+			transitiveStaticLibsHeaderJars = nil
 		}
 	}
 	if len(uniqueJavaFiles) > 0 || len(srcJars) > 0 {
@@ -1375,8 +1460,8 @@
 		}
 
 		if enableSharding {
-			if headerJarFileWithoutDepsOrJarjar != nil {
-				flags.classpath = append(classpath{headerJarFileWithoutDepsOrJarjar}, flags.classpath...)
+			if len(shardingHeaderJars) > 0 {
+				flags.classpath = append(classpath(slices.Clone(shardingHeaderJars)), flags.classpath...)
 			}
 			shardSize := int(*(j.properties.Javac_shard_size))
 			var shardSrcs []android.Paths
@@ -1385,8 +1470,8 @@
 				for idx, shardSrc := range shardSrcs {
 					classes := j.compileJavaClasses(ctx, jarName, idx, shardSrc,
 						nil, flags, extraJarDeps)
-					classes = j.repackageFlagsIfNecessary(ctx, classes, jarName, "javac-"+strconv.Itoa(idx))
-					jars = append(jars, classes)
+					classes, _ = j.repackageFlagsIfNecessary(ctx, classes, jarName, "javac-"+strconv.Itoa(idx))
+					localImplementationJars = append(localImplementationJars, classes)
 				}
 			}
 			// Assume approximately 5 sources per srcjar.
@@ -1398,21 +1483,21 @@
 				for idx, shardSrcJars := range shardSrcJarsList {
 					classes := j.compileJavaClasses(ctx, jarName, startIdx+idx,
 						nil, shardSrcJars, flags, extraJarDeps)
-					classes = j.repackageFlagsIfNecessary(ctx, classes, jarName, "javac-"+strconv.Itoa(startIdx+idx))
-					jars = append(jars, classes)
+					classes, _ = j.repackageFlagsIfNecessary(ctx, classes, jarName, "javac-"+strconv.Itoa(startIdx+idx))
+					localImplementationJars = append(localImplementationJars, classes)
 				}
 			}
 		} else {
 			classes := j.compileJavaClasses(ctx, jarName, -1, uniqueJavaFiles, srcJars, flags, extraJarDeps)
-			classes = j.repackageFlagsIfNecessary(ctx, classes, jarName, "javac")
-			jars = append(jars, classes)
+			classes, _ = j.repackageFlagsIfNecessary(ctx, classes, jarName, "javac")
+			localImplementationJars = append(localImplementationJars, classes)
 		}
 		if ctx.Failed() {
 			return
 		}
 	}
 
-	jars = append(jars, extraCombinedJars...)
+	localImplementationJars = append(localImplementationJars, extraCombinedJars...)
 
 	j.srcJarArgs, j.srcJarDeps = resourcePathsToJarArgs(srcFiles), srcFiles
 
@@ -1439,40 +1524,18 @@
 	resArgs = append(resArgs, extraArgs...)
 	resDeps = append(resDeps, extraDeps...)
 
+	var localResourceJars android.Paths
 	if len(resArgs) > 0 {
 		resourceJar := android.PathForModuleOut(ctx, "res", jarName)
 		TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps)
-		j.resourceJar = resourceJar
 		if ctx.Failed() {
 			return
 		}
+		localResourceJars = append(localResourceJars, resourceJar)
 	}
 
-	var resourceJars android.Paths
-	if j.resourceJar != nil {
-		resourceJars = append(resourceJars, j.resourceJar)
-	}
 	if Bool(j.properties.Include_srcs) {
-		resourceJars = append(resourceJars, includeSrcJar)
-	}
-	resourceJars = append(resourceJars, deps.staticResourceJars...)
-
-	if len(resourceJars) > 1 {
-		combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
-		TransformJarsToJar(ctx, combinedJar, "for resources", resourceJars, android.OptionalPath{},
-			false, nil, nil)
-		j.resourceJar = combinedJar
-	} else if len(resourceJars) == 1 {
-		j.resourceJar = resourceJars[0]
-	}
-
-	if len(deps.staticJars) > 0 {
-		jars = append(jars, deps.staticJars...)
-	}
-
-	manifest := j.overrideManifest
-	if !manifest.Valid() && j.properties.Manifest != nil {
-		manifest = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *j.properties.Manifest))
+		localResourceJars = append(localResourceJars, includeSrcJar)
 	}
 
 	services := android.PathsForModuleSrc(ctx, j.properties.Services)
@@ -1497,70 +1560,126 @@
 			Implicits: services,
 			Args:      args,
 		})
-		jars = append(jars, servicesJar)
+		localResourceJars = append(localResourceJars, servicesJar)
+	}
+
+	completeStaticLibsResourceJars := android.NewDepSet(android.PREORDER, localResourceJars, deps.transitiveStaticLibsResourceJars)
+
+	var combinedResourceJar android.Path
+	var resourceJars android.Paths
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		resourceJars = completeStaticLibsResourceJars.ToList()
+	} else {
+		resourceJars = append(slices.Clone(localResourceJars), deps.staticResourceJars...)
+	}
+	if len(resourceJars) == 1 {
+		combinedResourceJar = resourceJars[0]
+	} else if len(resourceJars) > 0 {
+		combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
+		TransformJarsToJar(ctx, combinedJar, "for resources", resourceJars, android.OptionalPath{},
+			false, nil, nil)
+		combinedResourceJar = combinedJar
+	}
+
+	manifest := j.overrideManifest
+	if !manifest.Valid() && j.properties.Manifest != nil {
+		manifest = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *j.properties.Manifest))
 	}
 
 	// Combine the classes built from sources, any manifests, and any static libraries into
 	// classes.jar. If there is only one input jar this step will be skipped.
-	var outputFile android.OutputPath
+	var outputFile android.Path
+
+	completeStaticLibsImplementationJars := android.NewDepSet(android.PREORDER, localImplementationJars, deps.transitiveStaticLibsImplementationJars)
+
+	var jars android.Paths
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		jars = completeStaticLibsImplementationJars.ToList()
+	} else {
+		jars = append(slices.Clone(localImplementationJars), deps.staticJars...)
+	}
+
+	jars = append(jars, extraDepCombinedJars...)
 
 	if len(jars) == 1 && !manifest.Valid() {
 		// Optimization: skip the combine step as there is nothing to do
 		// TODO(ccross): this leaves any module-info.class files, but those should only come from
 		// prebuilt dependencies until we support modules in the platform build, so there shouldn't be
-		// any if len(jars) == 1.
+		// any if len(extraJars) == 0.
 
 		// moduleStubLinkType determines if the module is the TopLevelStubLibrary generated
 		// from sdk_library. The TopLevelStubLibrary contains only one static lib,
 		// either with .from-source or .from-text suffix.
 		// outputFile should be agnostic to the build configuration,
-		// thus "combine" the single static lib in order to prevent the static lib from being exposed
+		// thus copy the single input static lib in order to prevent the static lib from being exposed
 		// to the copy rules.
-		stub, _ := moduleStubLinkType(ctx.ModuleName())
-
-		// Transform the single path to the jar into an OutputPath as that is required by the following
-		// code.
-		if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok && !stub {
-			// The path contains an embedded OutputPath so reuse that.
-			outputFile = moduleOutPath.OutputPath
-		} else if outputPath, ok := jars[0].(android.OutputPath); ok && !stub {
-			// The path is an OutputPath so reuse it directly.
-			outputFile = outputPath
-		} else {
-			// The file is not in the out directory so create an OutputPath into which it can be copied
-			// and which the following code can use to refer to it.
-			combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
+		if stub, _ := moduleStubLinkType(j); stub {
+			copiedJar := android.PathForModuleOut(ctx, "combined", jarName)
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   android.Cp,
 				Input:  jars[0],
-				Output: combinedJar,
+				Output: copiedJar,
 			})
-			outputFile = combinedJar.OutputPath
+			completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, android.Paths{copiedJar}, nil)
+			outputFile = copiedJar
+		} else {
+			outputFile = jars[0]
 		}
 	} else {
 		combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
 		TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest,
 			false, nil, nil)
-		outputFile = combinedJar.OutputPath
+		outputFile = combinedJar
 	}
 
 	// jarjar implementation jar if necessary
-	if j.expandJarjarRules != nil {
-		// Transform classes.jar into classes-jarjar.jar
-		jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName).OutputPath
-		TransformJarJar(ctx, jarjarFile, outputFile, j.expandJarjarRules)
-		outputFile = jarjarFile
+	jarjarFile, jarjarred := j.jarjarIfNecessary(ctx, outputFile, jarName, "")
+	if jarjarred {
+		localImplementationJars = android.Paths{jarjarFile}
+		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, localImplementationJars, nil)
+	}
+	outputFile = jarjarFile
 
-		// jarjar resource jar if necessary
-		if j.resourceJar != nil {
-			resourceJarJarFile := android.PathForModuleOut(ctx, "res-jarjar", jarName)
-			TransformJarJar(ctx, resourceJarJarFile, j.resourceJar, j.expandJarjarRules)
-			j.resourceJar = resourceJarJarFile
+	// jarjar resource jar if necessary
+	if combinedResourceJar != nil {
+		resourceJarJarFile, jarjarred := j.jarjarIfNecessary(ctx, combinedResourceJar, jarName, "resource")
+		combinedResourceJar = resourceJarJarFile
+		if jarjarred {
+			localResourceJars = android.Paths{resourceJarJarFile}
+			completeStaticLibsResourceJars = android.NewDepSet(android.PREORDER, localResourceJars, nil)
 		}
+	}
 
-		if ctx.Failed() {
-			return
-		}
+	if ctx.Failed() {
+		return
+	}
+
+	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,
+		})
+		outputFile = ravenizerOutput
+		localImplementationJars = android.Paths{ravenizerOutput}
+		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, localImplementationJars, nil)
+	}
+
+	if j.shouldApiMapper() {
+		inputFile := outputFile
+		apiMapperFile := android.PathForModuleOut(ctx, "apimapper", jarName)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        apimapper,
+			Description: "apimapper",
+			Input:       inputFile,
+			Output:      apiMapperFile,
+		})
+		outputFile = apiMapperFile
+		localImplementationJars = android.Paths{apiMapperFile}
+		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, localImplementationJars, nil)
 	}
 
 	// Check package restrictions if necessary.
@@ -1572,15 +1691,18 @@
 		// will check that the jar only contains the permitted packages. The new location will become
 		// the output file of this module.
 		inputFile := outputFile
-		outputFile = android.PathForModuleOut(ctx, "package-check", jarName).OutputPath
+		packageCheckOutputFile := android.PathForModuleOut(ctx, "package-check", jarName)
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.Cp,
 			Input:  inputFile,
-			Output: outputFile,
+			Output: packageCheckOutputFile,
 			// Make sure that any dependency on the output file will cause ninja to run the package check
 			// rule.
 			Validation: pkgckFile,
 		})
+		outputFile = packageCheckOutputFile
+		localImplementationJars = android.Paths{packageCheckOutputFile}
+		completeStaticLibsImplementationJars = android.NewDepSet(android.PREORDER, localImplementationJars, nil)
 
 		// Check packages and create a timestamp file when complete.
 		CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages)
@@ -1599,6 +1721,13 @@
 		headerJarFile := android.PathForModuleOut(ctx, "javac-header", jarName)
 		convertImplementationJarToHeaderJar(ctx, j.implementationJarFile, headerJarFile)
 		j.headerJarFile = headerJarFile
+		if len(localImplementationJars) == 1 && ctx.Config().UseTransitiveJarsInClasspath() {
+			localHeaderJarFile := android.PathForModuleOut(ctx, "local-javac-header", jarName)
+			convertImplementationJarToHeaderJar(ctx, localImplementationJars[0], localHeaderJarFile)
+			localHeaderJars = append(localHeaderJars, localHeaderJarFile)
+		} else {
+			localHeaderJars = append(localHeaderJars, headerJarFile)
+		}
 	}
 
 	// enforce syntax check to jacoco filters for any build (http://b/183622051)
@@ -1612,16 +1741,27 @@
 	}
 
 	// merge implementation jar with resources if necessary
-	implementationAndResourcesJar := outputFile
-	if j.resourceJar != nil {
-		jars := android.Paths{j.resourceJar, implementationAndResourcesJar}
-		combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath
-		TransformJarsToJar(ctx, combinedJar, "for resources", jars, manifest,
-			false, nil, nil)
-		implementationAndResourcesJar = combinedJar
+	var implementationAndResourcesJarsToCombine android.Paths
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		resourceJars := completeStaticLibsResourceJars.ToList()
+		if len(resourceJars) > 0 {
+			implementationAndResourcesJarsToCombine = append(resourceJars, completeStaticLibsImplementationJars.ToList()...)
+			implementationAndResourcesJarsToCombine = append(implementationAndResourcesJarsToCombine, extraDepCombinedJars...)
+		}
+	} else {
+		if combinedResourceJar != nil {
+			implementationAndResourcesJarsToCombine = android.Paths{combinedResourceJar, outputFile}
+		}
 	}
 
-	j.implementationAndResourcesJar = implementationAndResourcesJar
+	if len(implementationAndResourcesJarsToCombine) > 0 {
+		combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
+		TransformJarsToJar(ctx, combinedJar, "for resources", implementationAndResourcesJarsToCombine, manifest,
+			false, nil, nil)
+		outputFile = combinedJar
+	}
+
+	j.implementationAndResourcesJar = outputFile
 
 	// Enable dex compilation for the APEX variants, unless it is disabled explicitly
 	compileDex := j.dexProperties.Compile_dex
@@ -1642,22 +1782,22 @@
 					android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
 			}
 			// Dex compilation
-			var dexOutputFile android.OutputPath
+			var dexOutputFile android.Path
 			params := &compileDexParams{
 				flags:         flags,
 				sdkVersion:    j.SdkVersion(ctx),
 				minSdkVersion: j.MinSdkVersion(ctx),
-				classesJar:    implementationAndResourcesJar,
+				classesJar:    outputFile,
 				jarName:       jarName,
 			}
-			if j.GetProfileGuided() && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting() {
+			if j.GetProfileGuided(ctx) && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting(ctx) {
 				ctx.PropertyErrorf("enable_profile_rewriting",
 					"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on. The attached profile should be sourced from an unoptimized/unobfuscated APK.",
 				)
 			}
-			if j.EnableProfileRewriting() {
-				profile := j.GetProfile()
-				if profile == "" || !j.GetProfileGuided() {
+			if j.EnableProfileRewriting(ctx) {
+				profile := j.GetProfile(ctx)
+				if profile == "" || !j.GetProfileGuided(ctx) {
 					ctx.PropertyErrorf("enable_profile_rewriting", "Profile and Profile_guided must be set when enable_profile_rewriting is true")
 				}
 				params.artProfileInput = &profile
@@ -1669,17 +1809,27 @@
 
 			// If r8/d8 provides a profile that matches the optimized dex, use that for dexpreopt.
 			if dexArtProfileOutput != nil {
-				j.dexpreopter.SetRewrittenProfile(*dexArtProfileOutput)
+				j.dexpreopter.SetRewrittenProfile(dexArtProfileOutput)
 			}
 
 			// merge dex jar with resources if necessary
-			if j.resourceJar != nil {
-				jars := android.Paths{dexOutputFile, j.resourceJar}
-				combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath
-				TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
+			var dexAndResourceJarsToCombine android.Paths
+			if ctx.Config().UseTransitiveJarsInClasspath() {
+				resourceJars := completeStaticLibsResourceJars.ToList()
+				if len(resourceJars) > 0 {
+					dexAndResourceJarsToCombine = append(android.Paths{dexOutputFile}, resourceJars...)
+				}
+			} else {
+				if combinedResourceJar != nil {
+					dexAndResourceJarsToCombine = android.Paths{dexOutputFile, combinedResourceJar}
+				}
+			}
+			if len(dexAndResourceJarsToCombine) > 0 {
+				combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
+				TransformJarsToJar(ctx, combinedJar, "for dex resources", dexAndResourceJarsToCombine, android.OptionalPath{},
 					false, nil, nil)
 				if *j.dexProperties.Uncompress_dex {
-					combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath
+					combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName)
 					TransformZipAlign(ctx, combinedAlignedJar, combinedJar, nil)
 					dexOutputFile = combinedAlignedJar
 				} else {
@@ -1704,18 +1854,17 @@
 			j.dexpreopt(ctx, libName, dexOutputFile)
 
 			outputFile = dexOutputFile
+
+			ctx.CheckbuildFile(dexOutputFile)
 		} else {
 			// There is no code to compile into a dex jar, make sure the resources are propagated
 			// to the APK if this is an app.
-			outputFile = implementationAndResourcesJar
-			j.dexJarFile = makeDexJarPathFromPath(j.resourceJar)
+			j.dexJarFile = makeDexJarPathFromPath(combinedResourceJar)
 		}
 
 		if ctx.Failed() {
 			return
 		}
-	} else {
-		outputFile = implementationAndResourcesJar
 	}
 
 	if ctx.Device() {
@@ -1747,16 +1896,34 @@
 
 	j.collectTransitiveSrcFiles(ctx, srcFiles)
 
-	ctx.CheckbuildFile(outputFile)
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		if len(localImplementationJars) > 0 || len(localResourceJars) > 0 || len(localHeaderJars) > 0 {
+			ctx.CheckbuildFile(localImplementationJars...)
+			ctx.CheckbuildFile(localResourceJars...)
+			ctx.CheckbuildFile(localHeaderJars...)
+		} else {
+			// There are no local sources or resources in this module, so there is nothing to checkbuild.
+			ctx.UncheckedModule()
+		}
+	} else {
+		ctx.CheckbuildFile(j.implementationJarFile)
+		ctx.CheckbuildFile(j.headerJarFile)
+	}
 
-	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
-		HeaderJars:                          android.PathsIfNonNil(j.headerJarFile),
-		RepackagedHeaderJars:                android.PathsIfNonNil(j.repackagedHeaderJarFile),
-		TransitiveLibsHeaderJars:            j.transitiveLibsHeaderJars,
-		TransitiveStaticLibsHeaderJars:      j.transitiveStaticLibsHeaderJars,
+	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+		HeaderJars:           android.PathsIfNonNil(j.headerJarFile),
+		RepackagedHeaderJars: android.PathsIfNonNil(repackagedHeaderJarFile),
+
+		LocalHeaderJars:                        localHeaderJars,
+		TransitiveStaticLibsHeaderJars:         android.NewDepSet(android.PREORDER, localHeaderJars, transitiveStaticLibsHeaderJars),
+		TransitiveStaticLibsImplementationJars: completeStaticLibsImplementationJars,
+		TransitiveStaticLibsResourceJars:       completeStaticLibsResourceJars,
+
+		TransitiveLibsHeaderJarsForR8:       j.transitiveLibsHeaderJarsForR8,
+		TransitiveStaticLibsHeaderJarsForR8: j.transitiveStaticLibsHeaderJarsForR8,
 		ImplementationAndResourcesJars:      android.PathsIfNonNil(j.implementationAndResourcesJar),
 		ImplementationJars:                  android.PathsIfNonNil(j.implementationJarFile),
-		ResourceJars:                        android.PathsIfNonNil(j.resourceJar),
+		ResourceJars:                        android.PathsIfNonNil(combinedResourceJar),
 		AidlIncludeDirs:                     j.exportAidlIncludeDirs,
 		SrcJarArgs:                          j.srcJarArgs,
 		SrcJarDeps:                          j.srcJarDeps,
@@ -1773,8 +1940,8 @@
 	j.outputFile = outputFile.WithoutRel()
 }
 
-func (j *Module) useCompose() bool {
-	return android.InList("androidx.compose.runtime_runtime", j.properties.Static_libs)
+func (j *Module) useCompose(ctx android.BaseModuleContext) bool {
+	return android.InList("androidx.compose.runtime_runtime", j.staticLibs(ctx))
 }
 
 func collectDepProguardSpecInfo(ctx android.ModuleContext) (transitiveProguardFlags, transitiveUnconditionalExportedFlags []*android.DepSet[android.Path]) {
@@ -1839,7 +2006,7 @@
 }
 
 func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int,
-	srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath {
+	srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.Path {
 
 	kzipName := pathtools.ReplaceExtension(jarName, "kzip")
 	annoSrcJar := android.PathForModuleOut(ctx, "javac", "anno.srcjar")
@@ -1849,7 +2016,7 @@
 		jarName += strconv.Itoa(idx)
 	}
 
-	classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath
+	classes := android.PathForModuleOut(ctx, "javac", jarName)
 	TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, annoSrcJar, flags, extraJarDeps)
 
 	if ctx.Config().EmitXrefRules() && ctx.Module() == ctx.PrimaryModule() {
@@ -1892,62 +2059,41 @@
 
 func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths,
 	deps deps, flags javaBuilderFlags, jarName string,
-	extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar android.Path) {
+	extraJars android.Paths) (localHeaderJars android.Paths, combinedHeaderJar android.Path) {
 
-	var jars android.Paths
 	if len(srcFiles) > 0 || len(srcJars) > 0 {
 		// Compile java sources into turbine.jar.
 		turbineJar := android.PathForModuleOut(ctx, "turbine", jarName)
 		TransformJavaToHeaderClasses(ctx, turbineJar, srcFiles, srcJars, flags)
-		if ctx.Failed() {
-			return nil, nil, nil
-		}
-		jars = append(jars, turbineJar)
-		headerJar = turbineJar
+		localHeaderJars = append(localHeaderJars, turbineJar)
 	}
 
-	jars = append(jars, extraJars...)
+	localHeaderJars = append(localHeaderJars, extraJars...)
 
 	// Combine any static header libraries into classes-header.jar. If there is only
 	// one input jar this step will be skipped.
-	jars = append(jars, deps.staticHeaderJars...)
+	var jars android.Paths
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		depSet := android.NewDepSet(android.PREORDER, localHeaderJars, deps.transitiveStaticLibsHeaderJars)
+		jars = depSet.ToList()
+	} else {
+		jars = append(slices.Clone(localHeaderJars), deps.staticHeaderJars...)
+	}
 
 	// we cannot skip the combine step for now if there is only one jar
 	// since we have to strip META-INF/TRANSITIVE dir from turbine.jar
-	combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName)
-	TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{},
+	combinedHeaderJarOutputPath := android.PathForModuleOut(ctx, "turbine-combined", jarName)
+	TransformJarsToJar(ctx, combinedHeaderJarOutputPath, "for turbine", jars, android.OptionalPath{},
 		false, nil, []string{"META-INF/TRANSITIVE"})
-	jarjarAndDepsHeaderJar = combinedJar
 
-	if j.expandJarjarRules != nil {
-		// Transform classes.jar into classes-jarjar.jar
-		jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName)
-		TransformJarJar(ctx, jarjarFile, jarjarAndDepsHeaderJar, j.expandJarjarRules)
-		jarjarAndDepsHeaderJar = jarjarFile
-		if ctx.Failed() {
-			return nil, nil, nil
-		}
-	}
-
-	if j.repackageJarjarRules != nil {
-		repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-turbine-jarjar", jarName)
-		TransformJarJar(ctx, repackagedJarjarFile, jarjarAndDepsHeaderJar, j.repackageJarjarRules)
-		jarjarAndDepsRepackagedHeaderJar = repackagedJarjarFile
-		if ctx.Failed() {
-			return nil, nil, nil
-		}
-	} else {
-		jarjarAndDepsRepackagedHeaderJar = jarjarAndDepsHeaderJar
-	}
-
-	return headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar
+	return localHeaderJars, combinedHeaderJarOutputPath
 }
 
 func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
-	classesJar android.Path, jarName string, specs string) android.OutputPath {
+	classesJar android.Path, jarName string, specs string) android.Path {
 
 	jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco-report-classes", jarName)
-	instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName).OutputPath
+	instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName)
 
 	jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs)
 
@@ -1956,22 +2102,18 @@
 	return instrumentedJar
 }
 
-type providesTransitiveHeaderJars struct {
+type providesTransitiveHeaderJarsForR8 struct {
 	// set of header jars for all transitive libs deps
-	transitiveLibsHeaderJars *android.DepSet[android.Path]
+	transitiveLibsHeaderJarsForR8 *android.DepSet[android.Path]
 	// set of header jars for all transitive static libs deps
-	transitiveStaticLibsHeaderJars *android.DepSet[android.Path]
+	transitiveStaticLibsHeaderJarsForR8 *android.DepSet[android.Path]
 }
 
-func (j *providesTransitiveHeaderJars) TransitiveLibsHeaderJars() *android.DepSet[android.Path] {
-	return j.transitiveLibsHeaderJars
-}
-
-func (j *providesTransitiveHeaderJars) TransitiveStaticLibsHeaderJars() *android.DepSet[android.Path] {
-	return j.transitiveStaticLibsHeaderJars
-}
-
-func (j *providesTransitiveHeaderJars) collectTransitiveHeaderJars(ctx android.ModuleContext) {
+// collectTransitiveHeaderJarsForR8 visits direct dependencies and collects all transitive libs and static_libs
+// header jars.  The semantics of the collected jars are odd (it collects combined jars that contain the static
+// libs, but also the static libs, and it collects transitive libs dependencies of static_libs), so these
+// are only used to expand the --lib arguments to R8.
+func (j *providesTransitiveHeaderJarsForR8) collectTransitiveHeaderJarsForR8(ctx android.ModuleContext) {
 	directLibs := android.Paths{}
 	directStaticLibs := android.Paths{}
 	transitiveLibs := []*android.DepSet[android.Path]{}
@@ -1982,27 +2124,29 @@
 			return
 		}
 
-		dep, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
-		tag := ctx.OtherModuleDependencyTag(module)
-		_, isUsesLibDep := tag.(usesLibraryDependencyTag)
-		if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep {
-			directLibs = append(directLibs, dep.HeaderJars...)
-		} else if tag == staticLibTag {
-			directStaticLibs = append(directStaticLibs, dep.HeaderJars...)
-		} else {
-			// Don't propagate transitive libs for other kinds of dependencies.
-			return
-		}
+		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			tag := ctx.OtherModuleDependencyTag(module)
+			_, isUsesLibDep := tag.(usesLibraryDependencyTag)
+			if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep {
+				directLibs = append(directLibs, dep.HeaderJars...)
+			} else if tag == staticLibTag {
+				directStaticLibs = append(directStaticLibs, dep.HeaderJars...)
+			} else {
+				// Don't propagate transitive libs for other kinds of dependencies.
+				return
+			}
 
-		if dep.TransitiveLibsHeaderJars != nil {
-			transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars)
-		}
-		if dep.TransitiveStaticLibsHeaderJars != nil {
-			transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars)
+			if dep.TransitiveLibsHeaderJarsForR8 != nil {
+				transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJarsForR8)
+			}
+			if dep.TransitiveStaticLibsHeaderJarsForR8 != nil {
+				transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJarsForR8)
+			}
+
 		}
 	})
-	j.transitiveLibsHeaderJars = android.NewDepSet(android.POSTORDER, directLibs, transitiveLibs)
-	j.transitiveStaticLibsHeaderJars = android.NewDepSet(android.POSTORDER, directStaticLibs, transitiveStaticLibs)
+	j.transitiveLibsHeaderJarsForR8 = android.NewDepSet(android.POSTORDER, directLibs, transitiveLibs)
+	j.transitiveStaticLibsHeaderJarsForR8 = android.NewDepSet(android.POSTORDER, directStaticLibs, transitiveStaticLibs)
 }
 
 func (j *Module) HeaderJars() android.Paths {
@@ -2044,24 +2188,25 @@
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
-func (j *Module) IDEInfo(dpInfo *android.IdeInfo) {
-	dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...)
-	dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
-	dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...)
-	dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...)
+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())
+		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.Static_libs = append(dpInfo.Static_libs, j.properties.Static_libs...)
+	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)...)
 	dpInfo.Libs = append(dpInfo.Libs, j.properties.Libs...)
-	dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...)
 }
 
 func (j *Module) CompilerDeps() []string {
-	jdeps := []string{}
-	jdeps = append(jdeps, j.properties.Libs...)
-	jdeps = append(jdeps, j.properties.Static_libs...)
-	return jdeps
+	return j.compileDepNames
 }
 
 func (j *Module) hasCode(ctx android.ModuleContext) bool {
@@ -2107,9 +2252,10 @@
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if tag == staticLibTag {
-			depInfo, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
-			if depInfo.TransitiveSrcFiles != nil {
-				fromDeps = append(fromDeps, depInfo.TransitiveSrcFiles)
+			if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+				if depInfo.TransitiveSrcFiles != nil {
+					fromDeps = append(fromDeps, depInfo.TransitiveSrcFiles)
+				}
 			}
 		}
 	})
@@ -2166,6 +2312,25 @@
 	getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool)
 }
 
+func sdkLinkTypeFromSdkKind(k android.SdkKind) sdkLinkType {
+	switch k {
+	case android.SdkCore:
+		return javaCore
+	case android.SdkSystem:
+		return javaSystem
+	case android.SdkPublic:
+		return javaSdk
+	case android.SdkModule:
+		return javaModule
+	case android.SdkSystemServer:
+		return javaSystemServer
+	case android.SdkPrivate, android.SdkNone, android.SdkCorePlatform, android.SdkTest:
+		return javaPlatform
+	default:
+		return javaSdk
+	}
+}
+
 func (m *Module) getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) {
 	switch name {
 	case android.SdkCore.DefaultJavaLibraryName(),
@@ -2187,30 +2352,16 @@
 		return javaSystem, true
 	}
 
-	if stub, linkType := moduleStubLinkType(name); stub {
+	if stub, linkType := moduleStubLinkType(m); stub {
 		return linkType, true
 	}
 
 	ver := m.SdkVersion(ctx)
-	switch ver.Kind {
-	case android.SdkCore:
-		return javaCore, false
-	case android.SdkSystem:
-		return javaSystem, false
-	case android.SdkPublic:
-		return javaSdk, false
-	case android.SdkModule:
-		return javaModule, false
-	case android.SdkSystemServer:
-		return javaSystemServer, false
-	case android.SdkPrivate, android.SdkNone, android.SdkCorePlatform, android.SdkTest:
-		return javaPlatform, false
-	}
-
 	if !ver.Valid() {
 		panic(fmt.Errorf("sdk_version is invalid. got %q", ver.Raw))
 	}
-	return javaSdk, false
+
+	return sdkLinkTypeFromSdkKind(ver.Kind), false
 }
 
 // checkSdkLinkType make sures the given dependency doesn't have a lower SDK link type rank than
@@ -2239,24 +2390,17 @@
 func (j *Module) collectDeps(ctx android.ModuleContext) deps {
 	var deps deps
 
-	if ctx.Device() {
-		sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
-		if sdkDep.invalidVersion {
-			ctx.AddMissingDependencies(sdkDep.bootclasspath)
-			ctx.AddMissingDependencies(sdkDep.java9Classpath)
-		} else if sdkDep.useFiles {
-			// sdkDep.jar is actually equivalent to turbine header.jar.
-			deps.classpath = append(deps.classpath, sdkDep.jars...)
-			deps.dexClasspath = append(deps.dexClasspath, sdkDep.jars...)
-			deps.aidlPreprocess = sdkDep.aidl
-		} else {
-			deps.aidlPreprocess = sdkDep.aidl
-		}
-	}
-
 	sdkLinkType, _ := j.getSdkLinkType(ctx, ctx.ModuleName())
 
-	j.collectTransitiveHeaderJars(ctx)
+	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]
+
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -2270,14 +2414,13 @@
 			return
 		}
 
-		if dep, ok := module.(SdkLibraryDependency); ok {
+		if _, ok := module.(SdkLibraryDependency); ok {
 			switch tag {
-			case sdkLibTag, libTag:
-				depHeaderJars := dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))
-				deps.classpath = append(deps.classpath, depHeaderJars...)
-				deps.dexClasspath = append(deps.dexClasspath, depHeaderJars...)
-			case staticLibTag:
-				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
+			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)
 			}
 		} else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			if sdkLinkType != javaPlatform {
@@ -2291,6 +2434,9 @@
 			switch tag {
 			case bootClasspathTag:
 				deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...)
+				if dep.TransitiveStaticLibsHeaderJars != nil {
+					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)
@@ -2304,8 +2450,15 @@
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
 				addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...)
 				deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine
+
+				if dep.TransitiveStaticLibsHeaderJars != nil {
+					transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
+				}
 			case java9LibTag:
 				deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...)
+				if dep.TransitiveStaticLibsHeaderJars != nil {
+					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)
@@ -2321,6 +2474,17 @@
 				// optimization.
 				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)
+				}
 			case pluginTag:
 				if plugin, ok := module.(*Plugin); ok {
 					if plugin.pluginProperties.Processor_class != nil {
@@ -2354,10 +2518,6 @@
 				} else {
 					ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
 				}
-			case kotlinStdlibTag:
-				deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...)
-			case kotlinAnnotationsTag:
-				deps.kotlinAnnotations = dep.HeaderJars
 			case kotlinPluginTag:
 				deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...)
 			case syspropPublicStubDepTag:
@@ -2373,11 +2533,18 @@
 				checkProducesJars(ctx, dep)
 				deps.classpath = append(deps.classpath, dep.Srcs()...)
 				deps.dexClasspath = append(deps.classpath, dep.Srcs()...)
+				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars,
+					android.NewDepSet(android.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)
+				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, depHeaderJars)
+				transitiveStaticJarsHeaderLibs = append(transitiveStaticJarsHeaderLibs, depHeaderJars)
+				transitiveStaticJarsImplementationLibs = append(transitiveStaticJarsImplementationLibs, depHeaderJars)
 			}
 		} else if dep, ok := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider); ok {
 			switch tag {
@@ -2389,26 +2556,75 @@
 			case bootClasspathTag:
 				// If a system modules dependency has been added to the bootclasspath
 				// then add its libs to the bootclasspath.
-				sm := module.(SystemModulesProvider)
-				deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...)
+				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)
+					}
+				} else {
+					ctx.PropertyErrorf("boot classpath dependency %q does not provide SystemModulesProvider",
+						ctx.OtherModuleName(module))
+				}
 
 			case systemModulesTag:
 				if deps.systemModules != nil {
 					panic("Found two system module dependencies")
 				}
-				sm := module.(SystemModulesProvider)
-				outputDir, outputDeps := sm.OutputDirAndDeps()
-				deps.systemModules = &systemModules{outputDir, outputDeps}
+				if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok {
+					deps.systemModules = &systemModules{sm.OutputDir, sm.OutputDirDeps}
+				} else {
+					ctx.PropertyErrorf("system modules dependency %q does not provide SystemModulesProvider",
+						ctx.OtherModuleName(module))
+				}
 
 			case instrumentationForTag:
 				ctx.PropertyErrorf("instrumentation_for", "dependency %q of type %q does not provide JavaInfo so is unsuitable for use with this property", ctx.OtherModuleName(module), ctx.OtherModuleType(module))
 			}
 		}
 
+		if android.InList(tag, compileDependencyTags) {
+			// Add the dependency name to compileDepNames so that it can be recorded in module_bp_java_deps.json
+			j.compileDepNames = append(j.compileDepNames, otherName)
+		}
+
 		addCLCFromDep(ctx, module, j.classLoaderContexts)
 		addMissingOptionalUsesLibsFromDep(ctx, module, &j.usesLibrary)
 	})
 
+	deps.transitiveStaticLibsHeaderJars = transitiveStaticJarsHeaderLibs
+	deps.transitiveStaticLibsImplementationJars = transitiveStaticJarsImplementationLibs
+	deps.transitiveStaticLibsResourceJars = transitiveStaticJarsResourceLibs
+
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		depSet := android.NewDepSet(android.PREORDER, nil, transitiveClasspathHeaderJars)
+		deps.classpath = depSet.ToList()
+		depSet = android.NewDepSet(android.PREORDER, nil, transitiveBootClasspathHeaderJars)
+		deps.bootClasspath = depSet.ToList()
+		depSet = android.NewDepSet(android.PREORDER, nil, transitiveJava9ClasspathHeaderJars)
+		deps.java9Classpath = depSet.ToList()
+	}
+
+	if ctx.Device() {
+		sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
+		if sdkDep.invalidVersion {
+			ctx.AddMissingDependencies(sdkDep.bootclasspath)
+			ctx.AddMissingDependencies(sdkDep.java9Classpath)
+		} else if sdkDep.useFiles {
+			// sdkDep.jar is actually equivalent to turbine header.jar.
+			deps.classpath = append(slices.Clone(classpath(sdkDep.jars)), deps.classpath...)
+			deps.dexClasspath = append(slices.Clone(classpath(sdkDep.jars)), deps.dexClasspath...)
+			deps.aidlPreprocess = sdkDep.aidl
+			// Add the sdk module dependency to `compileDepNames`.
+			// This ensures that the dependency is reported in `module_bp_java_deps.json`
+			// TODO (b/358608607): Move this to decodeSdkDep
+			sdkSpec := android.SdkContext(j).SdkVersion(ctx)
+			j.compileDepNames = append(j.compileDepNames, fmt.Sprintf("sdk_%s_%s_android", sdkSpec.Kind.String(), sdkSpec.ApiLevel.String()))
+		} else {
+			deps.aidlPreprocess = sdkDep.aidl
+		}
+	}
+
 	return deps
 }
 
@@ -2459,6 +2675,8 @@
 
 func init() {
 	android.SetJarJarPrefixHandler(mergeJarJarPrefixes)
+
+	gob.Register(BaseJarJarProviderData{})
 }
 
 // BaseJarJarProviderData contains information that will propagate across dependencies regardless of
@@ -2568,8 +2786,6 @@
 					return RenameUseInclude, "tagswitch"
 				case exportedPluginTag:
 					return RenameUseInclude, "tagswitch"
-				case kotlinStdlibTag, kotlinAnnotationsTag:
-					return RenameUseExclude, "tagswitch"
 				case kotlinPluginTag:
 					return RenameUseInclude, "tagswitch"
 				default:
@@ -2646,8 +2862,7 @@
 	// Gather repackage information from deps
 	result := collectDirectDepsProviders(ctx)
 
-	// Update that with entries we've stored for ourself
-	for orig, renamed := range module.jarjarRenameRules {
+	add := func(orig string, renamed string) {
 		if result == nil {
 			result = &JarJarProviderData{
 				Rename: make(map[string]string),
@@ -2656,12 +2871,22 @@
 		if renamed != "" {
 			if preexisting, exists := (*result).Rename[orig]; exists && preexisting != renamed {
 				ctx.ModuleErrorf("Conflicting jarjar rules inherited for class: %s (%s and %s)", orig, renamed, preexisting)
-				continue
+				return
 			}
 		}
 		(*result).Rename[orig] = renamed
 	}
 
+	// Update that with entries we've stored for ourself
+	for orig, renamed := range module.jarjarRenameRules {
+		add(orig, renamed)
+	}
+
+	// Update that with entries given in the jarjar_rename property.
+	for _, orig := range module.properties.Jarjar_rename {
+		add(orig, "")
+	}
+
 	// If there are no renamings, then jarjar_prefix does nothing, so skip the extra work.
 	if result == nil {
 		return nil
@@ -2711,13 +2936,23 @@
 }
 
 // Repackage the flags if the jarjar rule txt for the flags is generated
-func (j *Module) repackageFlagsIfNecessary(ctx android.ModuleContext, infile android.WritablePath, jarName, info string) android.WritablePath {
+func (j *Module) repackageFlagsIfNecessary(ctx android.ModuleContext, infile android.Path, jarName, info string) (android.Path, bool) {
 	if j.repackageJarjarRules == nil {
-		return infile
+		return infile, false
 	}
-	repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", info+jarName)
+	repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", info, jarName)
 	TransformJarJar(ctx, repackagedJarjarFile, infile, j.repackageJarjarRules)
-	return repackagedJarjarFile
+	return repackagedJarjarFile, true
+}
+
+func (j *Module) jarjarIfNecessary(ctx android.ModuleContext, infile android.Path, jarName, info string) (android.Path, bool) {
+	if j.expandJarjarRules == nil {
+		return infile, false
+	}
+	jarjarFile := android.PathForModuleOut(ctx, "jarjar", info, jarName)
+	TransformJarJar(ctx, jarjarFile, infile, j.expandJarjarRules)
+	return jarjarFile, true
+
 }
 
 func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) {
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 6223ded..3c3bd55 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -21,7 +21,7 @@
 // isActiveModule returns true if the given module should be considered for boot
 // jars, i.e. if it's enabled and the preferred one in case of source and
 // prebuilt alternatives.
-func isActiveModule(ctx android.ConfigAndErrorContext, module android.Module) bool {
+func isActiveModule(ctx android.ConfigurableEvaluatorContext, module android.Module) bool {
 	if !module.Enabled(ctx) {
 		return false
 	}
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 77ddf5c..029f6f6 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -196,7 +196,7 @@
 type BootclasspathNestedAPIProperties struct {
 	// java_library or preferably, java_sdk_library modules providing stub classes that define the
 	// APIs provided by this bootclasspath_fragment.
-	Stub_libs []string
+	Stub_libs proptools.Configurable[[]string]
 }
 
 // BootclasspathAPIProperties defines properties for defining the API provided by parts of the
@@ -229,11 +229,11 @@
 
 // apiScopeToStubLibs calculates the stub library modules for each relevant *HiddenAPIScope from the
 // Stub_libs properties.
-func (p BootclasspathAPIProperties) apiScopeToStubLibs() map[*HiddenAPIScope][]string {
+func (p BootclasspathAPIProperties) apiScopeToStubLibs(ctx android.BaseModuleContext) map[*HiddenAPIScope][]string {
 	m := map[*HiddenAPIScope][]string{}
 	for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
-		m[apiScope] = p.Api.Stub_libs
+		m[apiScope] = p.Api.Stub_libs.GetOrDefault(ctx, nil)
 	}
-	m[CorePlatformHiddenAPIScope] = p.Core_platform_api.Stub_libs
+	m[CorePlatformHiddenAPIScope] = p.Core_platform_api.Stub_libs.GetOrDefault(ctx, nil)
 	return m
 }
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 16209b7..4fcd40b 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -414,6 +414,12 @@
 		// Cross-cutting metadata dependencies are metadata.
 		return false
 	}
+	// Dependency to the bootclasspath fragment of another apex
+	// e.g. concsrypt-bootclasspath-fragment --> art-bootclasspath-fragment
+	if tag == bootclasspathFragmentDepTag {
+		return false
+
+	}
 	panic(fmt.Errorf("boot_image module %q should not have a dependency on %q via tag %s", b, dep, android.PrettyPrintTag(tag)))
 }
 
@@ -445,7 +451,7 @@
 func (b *BootclasspathFragmentModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies onto all the modules that provide the API stubs for classes on this
 	// bootclasspath fragment.
-	hiddenAPIAddStubLibDependencies(ctx, b.properties.apiScopeToStubLibs())
+	hiddenAPIAddStubLibDependencies(ctx, b.properties.apiScopeToStubLibs(ctx))
 
 	for _, additionalStubModule := range b.properties.Additional_stubs {
 		for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
@@ -463,6 +469,12 @@
 	// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
 	// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
 	dexpreopt.RegisterToolDeps(ctx)
+
+	// Add a dependency to `all_apex_contributions` to determine if prebuilts are active.
+	// If prebuilts are active, `contents` validation on the source bootclasspath fragment should be disabled.
+	if _, isPrebuiltModule := ctx.Module().(*PrebuiltBootclasspathFragmentModule); !isPrebuiltModule {
+		ctx.AddDependency(b, android.AcDepTag, "all_apex_contributions")
+	}
 }
 
 func (b *BootclasspathFragmentModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
@@ -836,7 +848,7 @@
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
-func (b *BootclasspathFragmentModule) IDEInfo(dpInfo *android.IdeInfo) {
+func (b *BootclasspathFragmentModule) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
 	dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...)
 }
 
@@ -933,8 +945,8 @@
 	b.Filtered_flags_path = android.OptionalPathForPath(hiddenAPIInfo.FilteredFlagsPath)
 
 	// Copy stub_libs properties.
-	b.Stub_libs = module.properties.Api.Stub_libs
-	b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs
+	b.Stub_libs = module.properties.Api.Stub_libs.GetOrDefault(mctx, nil)
+	b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs.GetOrDefault(mctx, nil)
 
 	// Copy fragment properties.
 	b.Fragments = module.properties.Fragments
@@ -1093,22 +1105,10 @@
 	return &output
 }
 
+// DEPRECATED: this information is now generated in the context of the top level prebuilt apex.
 // produceBootImageProfile extracts the boot image profile from the APEX if available.
 func (module *PrebuiltBootclasspathFragmentModule) produceBootImageProfile(ctx android.ModuleContext) android.WritablePath {
-	// This module does not provide a boot image profile.
-	if module.getProfileProviderApex(ctx) == "" {
-		return nil
-	}
-
-	di, err := android.FindDeapexerProviderForModule(ctx)
-	if err != nil {
-		// An error was found, possibly due to multiple apexes in the tree that export this library
-		// Defer the error till a client tries to call getProfilePath
-		module.profilePathErr = err
-		return nil // An error has been reported by FindDeapexerProviderForModule.
-	}
-
-	return di.PrebuiltExportPath(ProfileInstallPathInApex)
+	return android.PathForModuleInstall(ctx, "intentionally_no_longer_supported")
 }
 
 func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path {
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 8bc0a7e..60f1a50 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -222,11 +222,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"),
 		FixtureConfigureApexBootJars("someapex:mysdklibrary"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		bootclasspath_fragment {
 			name: "myfragment",
@@ -277,7 +273,7 @@
 	`)
 
 	fragment := result.Module("myfragment", "android_common")
-	info, _ := android.SingletonModuleProvider(result, fragment, HiddenAPIInfoProvider)
+	info, _ := android.OtherModuleProvider(result, fragment, HiddenAPIInfoProvider)
 
 	stubsJar := "out/soong/.intermediates/mystublib/android_common/dex/mystublib.jar"
 
@@ -461,7 +457,7 @@
 
 	// Make sure that the library exports hidden API properties for use by the bootclasspath_fragment.
 	library := result.Module("mynewlibrary", "android_common")
-	info, _ := android.SingletonModuleProvider(result, library, hiddenAPIPropertyInfoProvider)
+	info, _ := android.OtherModuleProvider(result, library, hiddenAPIPropertyInfoProvider)
 	android.AssertArrayString(t, "split packages", []string{"sdklibrary", "newlibrary"}, info.SplitPackages)
 	android.AssertArrayString(t, "package prefixes", []string{"newlibrary.all.mine"}, info.PackagePrefixes)
 	android.AssertArrayString(t, "single packages", []string{"newlibrary.mine"}, info.SinglePackages)
diff --git a/java/builder.go b/java/builder.go
index 5d84d0b..81b0feb 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -156,7 +156,7 @@
 	turbine, turbineRE = pctx.RemoteStaticRules("turbine",
 		blueprint.RuleParams{
 			Command: `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} $outputFlags ` +
-				`--sources @$out.rsp  --source_jars $srcJars ` +
+				`--sources @$out.rsp ` +
 				`--javacopts ${config.CommonJdkFlags} ` +
 				`$javacFlags -source $javaVersion -target $javaVersion -- $turbineFlags && ` +
 				`(for o in $outputs; do if cmp -s $${o}.tmp $${o} ; then rm $${o}.tmp ; else mv $${o}.tmp $${o} ; fi; done )`,
@@ -170,13 +170,13 @@
 		},
 		&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
 			ExecStrategy:    "${config.RETurbineExecStrategy}",
-			Inputs:          []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
-			RSPFiles:        []string{"${out}.rsp"},
+			Inputs:          []string{"${config.TurbineJar}", "${out}.rsp", "$rbeInputs"},
+			RSPFiles:        []string{"$out.rsp", "$rspFiles"},
 			OutputFiles:     []string{"$rbeOutputs"},
 			ToolchainInputs: []string{"${config.JavaCmd}"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
-		[]string{"javacFlags", "turbineFlags", "outputFlags", "javaVersion", "outputs", "rbeOutputs", "srcJars"}, []string{"implicits"})
+		[]string{"javacFlags", "turbineFlags", "outputFlags", "javaVersion", "outputs", "rbeOutputs"}, []string{"rbeInputs", "rspFiles"})
 
 	jar, jarRE = pctx.RemoteStaticRules("jar",
 		blueprint.RuleParams{
@@ -258,6 +258,20 @@
 		},
 	)
 
+	ravenizer = pctx.AndroidStaticRule("ravenizer",
+		blueprint.RuleParams{
+			Command:     "rm -f $out && ${ravenizer} --in-jar $in --out-jar $out",
+			CommandDeps: []string{"${ravenizer}"},
+		},
+	)
+
+	apimapper = pctx.AndroidStaticRule("apimapper",
+		blueprint.RuleParams{
+			Command:     "${apimapper} --in-jar $in --out-jar $out",
+			CommandDeps: []string{"${apimapper}"},
+		},
+	)
+
 	zipalign = pctx.AndroidStaticRule("zipalign",
 		blueprint.RuleParams{
 			Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " +
@@ -307,6 +321,8 @@
 	pctx.Import("android/soong/java/config")
 
 	pctx.HostBinToolVariable("aconfig", "aconfig")
+	pctx.HostBinToolVariable("ravenizer", "ravenizer")
+	pctx.HostBinToolVariable("apimapper", "apimapper")
 	pctx.HostBinToolVariable("keep-flagged-apis", "keep-flagged-apis")
 }
 
@@ -428,53 +444,72 @@
 		})
 }
 
-func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags, dir string) (string, android.Paths) {
-	var deps android.Paths
+func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags, dir string, srcJars android.Paths) (string, android.Paths, android.Paths, android.Paths) {
+	var implicits android.Paths
+	var rbeInputs android.Paths
+	var rspFiles android.Paths
 
 	classpath := flags.classpath
 
-	var bootClasspath string
+	srcJarArgs := strings.Join(srcJars.Strings(), " ")
+	implicits = append(implicits, srcJars...)
+	const srcJarArgsLimit = 32 * 1024
+	if len(srcJarArgs) > srcJarArgsLimit {
+		srcJarRspFile := android.PathForModuleOut(ctx, "turbine", "srcjars.rsp")
+		android.WriteFileRule(ctx, srcJarRspFile, srcJarArgs)
+		srcJarArgs = "@" + srcJarRspFile.String()
+		implicits = append(implicits, srcJarRspFile)
+		rbeInputs = append(rbeInputs, srcJarRspFile)
+	} else {
+		rbeInputs = append(rbeInputs, srcJars...)
+	}
+
+	var bootClasspathFlags string
 	if flags.javaVersion.usesJavaModules() {
 		var systemModuleDeps android.Paths
-		bootClasspath, systemModuleDeps = flags.systemModules.FormTurbineSystemModulesPath(ctx.Device())
-		deps = append(deps, systemModuleDeps...)
+		bootClasspathFlags, systemModuleDeps = flags.systemModules.FormTurbineSystemModulesPath(ctx.Device())
+		implicits = append(implicits, systemModuleDeps...)
+		rbeInputs = append(rbeInputs, systemModuleDeps...)
 		classpath = append(flags.java9Classpath, classpath...)
 	} else {
-		deps = append(deps, flags.bootClasspath...)
+		implicits = append(implicits, flags.bootClasspath...)
+		rbeInputs = append(rbeInputs, flags.bootClasspath...)
 		if len(flags.bootClasspath) == 0 && ctx.Device() {
 			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
 			// ensure turbine does not fall back to the default bootclasspath.
-			bootClasspath = `--bootclasspath ""`
+			bootClasspathFlags = `--bootclasspath ""`
 		} else {
-			bootClasspath = flags.bootClasspath.FormTurbineClassPath("--bootclasspath ")
+			bootClasspathFlags = flags.bootClasspath.FormTurbineClassPath("--bootclasspath ")
 		}
 	}
 
-	deps = append(deps, classpath...)
-	turbineFlags := bootClasspath + " " + classpath.FormTurbineClassPath("--classpath ")
-
-	const flagsLimit = 32 * 1024
-	if len(turbineFlags) > flagsLimit {
-		flagsRspFile := android.PathForModuleOut(ctx, dir, "turbine-flags.rsp")
-		android.WriteFileRule(ctx, flagsRspFile, turbineFlags)
-		turbineFlags = "@" + flagsRspFile.String()
-		deps = append(deps, flagsRspFile)
+	classpathFlags := classpath.FormTurbineClassPath("")
+	implicits = append(implicits, classpath...)
+	const classpathLimit = 32 * 1024
+	if len(classpathFlags) > classpathLimit {
+		classpathRspFile := android.PathForModuleOut(ctx, dir, "classpath.rsp")
+		android.WriteFileRule(ctx, classpathRspFile, classpathFlags)
+		classpathFlags = "@" + classpathRspFile.String()
+		implicits = append(implicits, classpathRspFile)
+		rspFiles = append(rspFiles, classpathRspFile)
+		rbeInputs = append(rbeInputs, classpathRspFile)
+	} else {
+		rbeInputs = append(rbeInputs, classpath...)
 	}
 
-	return turbineFlags, deps
+	turbineFlags := "--source_jars " + srcJarArgs + " " + bootClasspathFlags + " --classpath " + classpathFlags
+
+	return turbineFlags, implicits, rbeInputs, rspFiles
 }
 
 func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
 
-	turbineFlags, deps := turbineFlags(ctx, flags, "turbine")
-
-	deps = append(deps, srcJars...)
+	turbineFlags, implicits, rbeInputs, rspFiles := turbineFlags(ctx, flags, "turbine", srcJars)
 
 	rule := turbine
 	args := map[string]string{
 		"javacFlags":   flags.javacFlags,
-		"srcJars":      strings.Join(srcJars.Strings(), " "),
 		"javaVersion":  flags.javaVersion.String(),
 		"turbineFlags": turbineFlags,
 		"outputFlags":  "--output " + outputFile.String() + ".tmp",
@@ -482,15 +517,16 @@
 	}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
 		rule = turbineRE
-		args["implicits"] = strings.Join(deps.Strings(), ",")
+		args["rbeInputs"] = strings.Join(rbeInputs.Strings(), ",")
 		args["rbeOutputs"] = outputFile.String() + ".tmp"
+		args["rspFiles"] = strings.Join(rspFiles.Strings(), ",")
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
 		Description: "turbine",
 		Output:      outputFile,
 		Inputs:      srcFiles,
-		Implicits:   deps,
+		Implicits:   implicits,
 		Args:        args,
 	})
 }
@@ -499,11 +535,10 @@
 func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.WritablePath,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
 
-	turbineFlags, deps := turbineFlags(ctx, flags, "kapt")
+	turbineFlags, implicits, rbeInputs, rspFiles := turbineFlags(ctx, flags, "turbine-apt", srcJars)
 
-	deps = append(deps, srcJars...)
-
-	deps = append(deps, flags.processorPath...)
+	implicits = append(implicits, flags.processorPath...)
+	rbeInputs = append(rbeInputs, flags.processorPath...)
 	turbineFlags += " " + flags.processorPath.FormTurbineClassPath("--processorpath ")
 	turbineFlags += " --processors " + strings.Join(flags.processors, " ")
 
@@ -514,7 +549,6 @@
 	rule := turbine
 	args := map[string]string{
 		"javacFlags":   flags.javacFlags,
-		"srcJars":      strings.Join(srcJars.Strings(), " "),
 		"javaVersion":  flags.javaVersion.String(),
 		"turbineFlags": turbineFlags,
 		"outputFlags":  outputFlags,
@@ -522,8 +556,9 @@
 	}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
 		rule = turbineRE
-		args["implicits"] = strings.Join(deps.Strings(), ",")
+		args["rbeInputs"] = strings.Join(rbeInputs.Strings(), ",")
 		args["rbeOutputs"] = outputSrcJar.String() + ".tmp," + outputResJar.String() + ".tmp"
+		args["rspFiles"] = strings.Join(rspFiles.Strings(), ",")
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:            rule,
@@ -531,7 +566,7 @@
 		Output:          outputs[0],
 		ImplicitOutputs: outputs[1:],
 		Inputs:          srcFiles,
-		Implicits:       deps,
+		Implicits:       implicits,
 		Args:            args,
 	})
 }
@@ -746,6 +781,16 @@
 	})
 }
 
+func TransformRavenizer(ctx android.ModuleContext, outputFile android.WritablePath,
+	inputFile android.Path) {
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        ravenizer,
+		Description: "ravenizer",
+		Output:      outputFile,
+		Input:       inputFile,
+	})
+}
+
 func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) {
 	android.WriteFileRule(ctx, outputFile, "Main-Class: "+mainClass+"\n")
 }
diff --git a/java/code_metadata_test.go b/java/code_metadata_test.go
index 99b1f52..9dc9a22 100644
--- a/java/code_metadata_test.go
+++ b/java/code_metadata_test.go
@@ -29,7 +29,7 @@
 	module := result.ModuleForTests("module-name", "")
 
 	// Check that the provider has the right contents
-	data, _ := android.SingletonModuleProvider(result, module.Module(), soongTesting.CodeMetadataProviderKey)
+	data, _ := android.OtherModuleProvider(result, module.Module(), soongTesting.CodeMetadataProviderKey)
 	if !strings.HasSuffix(
 		data.IntermediatePath.String(), "/intermediateCodeMetadata.pb",
 	) {
diff --git a/java/config/Android.bp b/java/config/Android.bp
index bfe83ab..6217390 100644
--- a/java/config/Android.bp
+++ b/java/config/Android.bp
@@ -17,4 +17,8 @@
         "kotlin.go",
         "makevars.go",
     ],
+    visibility: [
+        "//build/soong:__subpackages__",
+        "//external/error_prone/soong",
+    ],
 }
diff --git a/java/config/config.go b/java/config/config.go
index 0d30fbd..4c1c723 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -47,6 +47,7 @@
 		"services",
 		"android.car",
 		"android.car7",
+		"android.car.builtin.impl",
 		"conscrypt",
 		"core-icu4j",
 		"core-oj",
@@ -144,6 +145,7 @@
 	pctx.SourcePathVariable("JmodCmd", "${JavaToolchain}/jmod")
 	pctx.SourcePathVariable("JrtFsJar", "${JavaHome}/lib/jrt-fs.jar")
 	pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar")
+	pctx.SourcePathVariable("KotlinKytheExtractor", "prebuilts/build-tools/${hostPrebuiltTag}/bin/kotlinc_extractor")
 	pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime")
 
 	pctx.SourcePathVariable("ResourceProcessorBusyBox", "prebuilts/bazel/common/android_tools/android_tools/all_android_tools_deploy.jar")
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index e5e187c..302d021 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -50,4 +50,11 @@
 	}, " "))
 
 	pctx.StaticVariable("KotlincGlobalFlags", strings.Join([]string{}, " "))
+	// Use KotlincKytheGlobalFlags to prevent kotlinc version skew issues between android and
+	// g3 kythe indexers.
+	// This is necessary because there might be instances of kotlin code in android
+	// platform that are not fully compatible with the kotlinc used in g3 kythe indexers.
+	// e.g. uninitialized variables are a warning in 1.*, but an error in 2.*
+	// https://github.com/JetBrains/kotlin/blob/master/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt#L748
+	pctx.StaticVariable("KotlincKytheGlobalFlags", strings.Join([]string{"-language-version 1.9"}, " "))
 }
diff --git a/java/container_test.go b/java/container_test.go
new file mode 100644
index 0000000..25cfa4c
--- /dev/null
+++ b/java/container_test.go
@@ -0,0 +1,166 @@
+// 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"
+	"fmt"
+	"testing"
+)
+
+var checkContainerMatch = func(t *testing.T, name string, container string, expected bool, actual bool) {
+	errorMessage := fmt.Sprintf("module %s container %s value differ", name, container)
+	android.AssertBoolEquals(t, errorMessage, expected, actual)
+}
+
+func TestJavaContainersModuleProperties(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			srcs: ["A.java"],
+		}
+		java_library {
+			name: "foo_vendor",
+			srcs: ["A.java"],
+			vendor: true,
+			sdk_version: "current",
+		}
+		java_library {
+			name: "foo_soc_specific",
+			srcs: ["A.java"],
+			soc_specific: true,
+			sdk_version: "current",
+		}
+		java_library {
+			name: "foo_product_specific",
+			srcs: ["A.java"],
+			product_specific: true,
+			sdk_version: "current",
+		}
+		java_test {
+			name: "foo_cts_test",
+			srcs: ["A.java"],
+			test_suites: [
+				"cts",
+			],
+		}
+		java_test {
+			name: "foo_non_cts_test",
+			srcs: ["A.java"],
+			test_suites: [
+				"general-tests",
+			],
+		}
+		java_library {
+			name: "bar",
+			static_libs: [
+				"framework-minus-apex",
+			],
+		}
+		java_library {
+			name: "baz",
+			static_libs: [
+				"bar",
+			],
+		}
+	`)
+
+	testcases := []struct {
+		moduleName         string
+		isSystemContainer  bool
+		isVendorContainer  bool
+		isProductContainer bool
+		isCts              bool
+		isUnstable         bool
+	}{
+		{
+			moduleName:         "foo",
+			isSystemContainer:  true,
+			isVendorContainer:  false,
+			isProductContainer: false,
+			isCts:              false,
+			isUnstable:         false,
+		},
+		{
+			moduleName:         "foo_vendor",
+			isSystemContainer:  false,
+			isVendorContainer:  true,
+			isProductContainer: false,
+			isCts:              false,
+			isUnstable:         false,
+		},
+		{
+			moduleName:         "foo_soc_specific",
+			isSystemContainer:  false,
+			isVendorContainer:  true,
+			isProductContainer: false,
+			isCts:              false,
+			isUnstable:         false,
+		},
+		{
+			moduleName:         "foo_product_specific",
+			isSystemContainer:  false,
+			isVendorContainer:  false,
+			isProductContainer: true,
+			isCts:              false,
+			isUnstable:         false,
+		},
+		{
+			moduleName:         "foo_cts_test",
+			isSystemContainer:  false,
+			isVendorContainer:  false,
+			isProductContainer: false,
+			isCts:              true,
+			isUnstable:         false,
+		},
+		{
+			moduleName:         "foo_non_cts_test",
+			isSystemContainer:  false,
+			isVendorContainer:  false,
+			isProductContainer: false,
+			isCts:              false,
+			isUnstable:         false,
+		},
+		{
+			moduleName:         "bar",
+			isSystemContainer:  true,
+			isVendorContainer:  false,
+			isProductContainer: false,
+			isCts:              false,
+			isUnstable:         true,
+		},
+		{
+			moduleName:         "baz",
+			isSystemContainer:  true,
+			isVendorContainer:  false,
+			isProductContainer: false,
+			isCts:              false,
+			isUnstable:         true,
+		},
+	}
+
+	for _, c := range testcases {
+		m := result.ModuleForTests(c.moduleName, "android_common")
+		containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider)
+		belongingContainers := containers.BelongingContainers()
+		checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers))
+		checkContainerMatch(t, c.moduleName, "vendor", c.isVendorContainer, android.InList(android.VendorContainer, belongingContainers))
+		checkContainerMatch(t, c.moduleName, "product", c.isProductContainer, android.InList(android.ProductContainer, belongingContainers))
+		checkContainerMatch(t, c.moduleName, "cts", c.isCts, android.InList(android.CtsContainer, belongingContainers))
+		checkContainerMatch(t, c.moduleName, "unstable", c.isUnstable, android.InList(android.UnstableContainer, belongingContainers))
+	}
+}
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index ab72e8b..1cca7ad 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -38,10 +38,11 @@
     visibility: ["//visibility:public"],
     sdk_version: "none",
     system_modules: "none",
+    is_stubs_module: true,
 }
 
 java_library {
-    name: "core.current.stubs.from-source",
+    name: "core.current.stubs",
     defaults: [
         "core.current.stubs.defaults",
     ],
@@ -52,8 +53,12 @@
     ],
 }
 
+// Used for bootstrapping ART system modules
 java_api_library {
     name: "core.current.stubs.from-text",
+    defaults: [
+        "core.current.stubs.defaults",
+    ],
     api_surface: "core",
     api_contributions: [
         "art.module.public.api.stubs.source.api.contribution",
@@ -68,27 +73,7 @@
 }
 
 java_library {
-    name: "core.current.stubs",
-    defaults: [
-        "core.current.stubs.defaults",
-    ],
-    static_libs: [
-        "core.current.stubs.from-source",
-    ],
-    product_variables: {
-        build_from_text_stub: {
-            static_libs: [
-                "core.current.stubs.from-text",
-            ],
-            exclude_static_libs: [
-                "core.current.stubs.from-source",
-            ],
-        },
-    },
-}
-
-java_library {
-    name: "core.current.stubs.exportable.from-source",
+    name: "core.current.stubs.exportable",
     defaults: [
         "core.current.stubs.defaults",
     ],
@@ -103,16 +88,6 @@
     },
 }
 
-java_library {
-    name: "core.current.stubs.exportable",
-    defaults: [
-        "core.current.stubs.defaults",
-    ],
-    static_libs: [
-        "core.current.stubs.exportable.from-source",
-    ],
-}
-
 // Distributed with the SDK for turning into system modules to compile apps
 // against.
 //
@@ -201,26 +176,6 @@
         "core.module_lib.stubs.defaults",
     ],
     static_libs: [
-        "core.module_lib.stubs.from-source",
-    ],
-    product_variables: {
-        build_from_text_stub: {
-            static_libs: [
-                "core.module_lib.stubs.from-text",
-            ],
-            exclude_static_libs: [
-                "core.module_lib.stubs.from-source",
-            ],
-        },
-    },
-}
-
-java_library {
-    name: "core.module_lib.stubs.from-source",
-    defaults: [
-        "core.module_lib.stubs.defaults",
-    ],
-    static_libs: [
         "art.module.public.api.stubs.module_lib",
 
         // Replace the following with the module-lib correspondence when Conscrypt or i18N module
@@ -231,27 +186,6 @@
     ],
 }
 
-java_api_library {
-    name: "core.module_lib.stubs.from-text",
-    api_surface: "module-lib",
-    api_contributions: [
-        "art.module.public.api.stubs.source.api.contribution",
-        "art.module.public.api.stubs.source.system.api.contribution",
-        "art.module.public.api.stubs.source.module_lib.api.contribution",
-
-        // Add the module-lib correspondence when Conscrypt or i18N module
-        // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides
-        // @SystemApi(MODULE_LIBRARIES).
-        "conscrypt.module.public.api.stubs.source.api.contribution",
-        "i18n.module.public.api.stubs.source.api.contribution",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    visibility: ["//visibility:private"],
-    stubs_type: "everything",
-}
-
 // Produces a dist file that is used by the
 // prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk
 // directory.
@@ -311,7 +245,7 @@
 // API annotations are available to the dex tools that enable enforcement of runtime
 // accessibility. b/119068555
 java_library {
-    name: "legacy.core.platform.api.stubs.from-source",
+    name: "legacy.core.platform.api.stubs",
     visibility: core_platform_visibility,
     defaults: [
         "core.platform.api.stubs.defaults",
@@ -324,7 +258,7 @@
 }
 
 java_library {
-    name: "legacy.core.platform.api.stubs.exportable.from-source",
+    name: "legacy.core.platform.api.stubs.exportable",
     visibility: core_platform_visibility,
     defaults: [
         "core.platform.api.stubs.defaults",
@@ -348,53 +282,6 @@
     ],
 }
 
-java_api_library {
-    name: "legacy.core.platform.api.stubs.from-text",
-    api_surface: "core_platform",
-    defaults: [
-        "android_core_platform_stubs_current_contributions",
-    ],
-    api_contributions: [
-        "legacy.i18n.module.platform.api.stubs.source.api.contribution",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    stubs_type: "everything",
-}
-
-java_library {
-    name: "legacy.core.platform.api.stubs",
-    visibility: core_platform_visibility,
-    defaults: [
-        "core.platform.api.stubs.defaults",
-    ],
-    static_libs: [
-        "legacy.core.platform.api.stubs.from-source",
-    ],
-    product_variables: {
-        build_from_text_stub: {
-            static_libs: [
-                "legacy.core.platform.api.stubs.from-text",
-            ],
-            exclude_static_libs: [
-                "legacy.core.platform.api.stubs.from-source",
-            ],
-        },
-    },
-}
-
-java_library {
-    name: "legacy.core.platform.api.stubs.exportable",
-    visibility: core_platform_visibility,
-    defaults: [
-        "core.platform.api.stubs.defaults",
-    ],
-    static_libs: [
-        "legacy.core.platform.api.stubs.exportable.from-source",
-    ],
-}
-
 java_defaults {
     name: "core.platform.api.stubs.defaults",
     hostdex: true,
@@ -403,6 +290,7 @@
     sdk_version: "none",
     system_modules: "none",
     patch_module: "java.base",
+    is_stubs_module: true,
 }
 
 // Same as legacy.core.platform.api.stubs, but android annotations are
@@ -421,10 +309,11 @@
         "legacy.core.platform.api.stubs",
     ],
     patch_module: "java.base",
+    is_stubs_module: true,
 }
 
 java_library {
-    name: "stable.core.platform.api.stubs.from-source",
+    name: "stable.core.platform.api.stubs",
     visibility: core_platform_visibility,
     defaults: [
         "core.platform.api.stubs.defaults",
@@ -437,42 +326,6 @@
     ],
 }
 
-java_api_library {
-    name: "stable.core.platform.api.stubs.from-text",
-    api_surface: "core_platform",
-    defaults: [
-        "android_core_platform_stubs_current_contributions",
-    ],
-    api_contributions: [
-        "stable.i18n.module.platform.api.stubs.source.api.contribution",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    stubs_type: "everything",
-}
-
-java_library {
-    name: "stable.core.platform.api.stubs",
-    visibility: core_platform_visibility,
-    defaults: [
-        "core.platform.api.stubs.defaults",
-    ],
-    static_libs: [
-        "stable.core.platform.api.stubs.from-source",
-    ],
-    product_variables: {
-        build_from_text_stub: {
-            static_libs: [
-                "stable.core.platform.api.stubs.from-text",
-            ],
-            exclude_static_libs: [
-                "stable.core.platform.api.stubs.from-source",
-            ],
-        },
-    },
-}
-
 // Same as stable.core.platform.api.stubs, but android annotations are
 // stripped. This is used by the Java toolchain, while the annotated stub is to
 // be used by Kotlin one.
@@ -489,6 +342,7 @@
         "stable.core.platform.api.stubs",
     ],
     patch_module: "java.base",
+    is_stubs_module: true,
 }
 
 // Used when compiling higher-level code against *.core.platform.api.stubs.
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 3f8735c..3f4e3cd 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -96,6 +96,10 @@
 		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]
+
 	ctx.VisitDirectDepsWithTag(deviceHostConverterDepTag, func(m android.Module) {
 		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
 			d.headerJars = append(d.headerJars, dep.HeaderJars...)
@@ -105,6 +109,16 @@
 
 			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)
+			}
 		} else {
 			ctx.PropertyErrorf("libs", "module %q cannot be used as a dependency", ctx.OtherModuleName(m))
 		}
@@ -130,14 +144,18 @@
 		d.combinedHeaderJar = d.headerJars[0]
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
-		HeaderJars:                     d.headerJars,
-		ImplementationAndResourcesJars: d.implementationAndResourceJars,
-		ImplementationJars:             d.implementationJars,
-		ResourceJars:                   d.resourceJars,
-		SrcJarArgs:                     d.srcJarArgs,
-		SrcJarDeps:                     d.srcJarDeps,
-		StubsLinkType:                  Implementation,
+	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),
+		ImplementationAndResourcesJars:         d.implementationAndResourceJars,
+		ImplementationJars:                     d.implementationJars,
+		ResourceJars:                           d.resourceJars,
+		SrcJarArgs:                             d.srcJarArgs,
+		SrcJarDeps:                             d.srcJarDeps,
+		StubsLinkType:                          Implementation,
 		// TODO: Not sure if aconfig flags that have been moved between device and host variants
 		// make sense.
 	})
@@ -188,3 +206,11 @@
 		},
 	}
 }
+
+// implement the following interface for IDE completion.
+var _ android.IDEInfo = (*DeviceHostConverter)(nil)
+
+func (d *DeviceHostConverter) IDEInfo(ctx android.BaseModuleContext, ideInfo *android.IdeInfo) {
+	ideInfo.Deps = append(ideInfo.Deps, d.properties.Libs...)
+	ideInfo.Libs = append(ideInfo.Libs, d.properties.Libs...)
+}
diff --git a/java/dex.go b/java/dex.go
index 32546d9..e0e642c 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -91,6 +91,10 @@
 
 	// Exclude kotlinc generate files: *.kotlin_module, *.kotlin_builtins. Defaults to false.
 	Exclude_kotlinc_generated_files *bool
+
+	// Disable dex container (also known as "multi-dex").
+	// This may be necessary as a temporary workaround to mask toolchain bugs (see b/341652226).
+	No_dex_container *bool
 }
 
 type dexer struct {
@@ -104,7 +108,7 @@
 	resourcesInput          android.OptionalPath
 	resourcesOutput         android.OptionalPath
 
-	providesTransitiveHeaderJars
+	providesTransitiveHeaderJarsForR8
 }
 
 func (d *dexer) effectiveOptimizeEnabled() bool {
@@ -116,7 +120,7 @@
 }
 
 func (d *DexProperties) optimizedResourceShrinkingEnabled(ctx android.ModuleContext) bool {
-	return d.resourceShrinkingEnabled(ctx) && Bool(d.Optimize.Optimized_shrink_resources)
+	return d.resourceShrinkingEnabled(ctx) && BoolDefault(d.Optimize.Optimized_shrink_resources, ctx.Config().UseOptimizedResourceShrinkingByDefault())
 }
 
 func (d *dexer) optimizeOrObfuscateEnabled() bool {
@@ -180,7 +184,7 @@
 		"$r8Template": &remoteexec.REParams{
 			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
 			Inputs:          []string{"$implicits", "${config.R8Jar}"},
-			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}", "${resourcesOutput}"},
+			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}", "${resourcesOutput}", "${outR8ArtProfile}"},
 			ExecStrategy:    "${config.RER8ExecStrategy}",
 			ToolchainInputs: []string{"${config.JavaCmd}"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
@@ -200,7 +204,7 @@
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
 	}, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
-		"r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput"}, []string{"implicits"})
+		"r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile"}, []string{"implicits"})
 
 func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
 	dexParams *compileDexParams) (flags []string, deps android.Paths) {
@@ -241,6 +245,16 @@
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
+	if !Bool(d.dexProperties.No_dex_container) && effectiveVersion.FinalOrFutureInt() >= 36 {
+		// 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...)
+			}
+		}
+	}
 
 	// If the specified SDK level is 10000, then configure the compiler to use the
 	// current platform SDK level and to compile the build as a platform build.
@@ -285,28 +299,32 @@
 	// - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version.
 	// - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version.
 	// See b/20667396
-	var proguardRaiseDeps classpath
-	ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) {
-		dep, _ := android.OtherModuleProvider(ctx, m, JavaInfoProvider)
-		proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...)
-	})
+	// TODO(b/360905238): Remove SdkSystemServer exception after resolving missing class references.
+	if !dexParams.sdkVersion.Stable() || dexParams.sdkVersion.Kind == android.SdkSystemServer {
+		var proguardRaiseDeps classpath
+		ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) {
+			if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
+				proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...)
+			}
+		})
+		r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
+		r8Deps = append(r8Deps, proguardRaiseDeps...)
+	}
 
-	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
-	r8Deps = append(r8Deps, proguardRaiseDeps...)
 	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
 	r8Deps = append(r8Deps, flags.bootClasspath...)
 	r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
 	r8Deps = append(r8Deps, flags.dexClasspath...)
 
 	transitiveStaticLibsLookupMap := map[android.Path]bool{}
-	if d.transitiveStaticLibsHeaderJars != nil {
-		for _, jar := range d.transitiveStaticLibsHeaderJars.ToList() {
+	if d.transitiveStaticLibsHeaderJarsForR8 != nil {
+		for _, jar := range d.transitiveStaticLibsHeaderJarsForR8.ToList() {
 			transitiveStaticLibsLookupMap[jar] = true
 		}
 	}
 	transitiveHeaderJars := android.Paths{}
-	if d.transitiveLibsHeaderJars != nil {
-		for _, jar := range d.transitiveLibsHeaderJars.ToList() {
+	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
@@ -382,7 +400,7 @@
 		r8Flags = append(r8Flags, "--resource-input", d.resourcesInput.Path().String())
 		r8Deps = append(r8Deps, d.resourcesInput.Path())
 		r8Flags = append(r8Flags, "--resource-output", d.resourcesOutput.Path().String())
-		if Bool(opt.Optimized_shrink_resources) {
+		if d.dexProperties.optimizedResourceShrinkingEnabled(ctx) {
 			r8Flags = append(r8Flags, "--optimized-resource-shrinking")
 		}
 	}
@@ -424,7 +442,7 @@
 }
 
 // Return the compiled dex jar and (optional) profile _after_ r8 optimization
-func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.OutputPath, *android.OutputPath) {
+func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.Path, android.Path) {
 
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
@@ -463,13 +481,6 @@
 			proguardConfiguration,
 		}
 		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams)
-		if r8ArtProfileOutputPath != nil {
-			artProfileOutputPath = r8ArtProfileOutputPath
-			implicitOutputs = append(
-				implicitOutputs,
-				artProfileOutputPath,
-			)
-		}
 		rule := r8
 		args := map[string]string{
 			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
@@ -482,6 +493,17 @@
 			"outDir":         outDir.String(),
 			"mergeZipsFlags": mergeZipsFlags,
 		}
+		if r8ArtProfileOutputPath != nil {
+			artProfileOutputPath = r8ArtProfileOutputPath
+			implicitOutputs = append(
+				implicitOutputs,
+				artProfileOutputPath,
+			)
+			// Add the implicit r8 Art profile output to args so that r8RE knows
+			// about this implicit output
+			args["outR8ArtProfile"] = artProfileOutputPath.String()
+		}
+
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") {
 			rule = r8RE
 			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 7949244..63a8634 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -88,18 +88,11 @@
 				entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
 				entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
-				// Unset LOCAL_SOONG_INSTALLED_MODULE so that this does not default to the primary .apex file
-				// Without this, installation of the dexpreopt artifacts get skipped
-				entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", "")
 			},
 		},
 	}
 }
 
-func (install dexpreopterInstall) PackageFile(ctx android.ModuleContext) android.PackagingSpec {
-	return ctx.PackageFile(install.installDirOnDevice, install.installFileOnDevice, install.outputPathOnHost)
-}
-
 type Dexpreopter struct {
 	dexpreopter
 }
@@ -154,25 +147,25 @@
 type DexpreoptProperties struct {
 	Dex_preopt struct {
 		// If false, prevent dexpreopting.  Defaults to true.
-		Enabled *bool
+		Enabled proptools.Configurable[bool] `android:"replace_instead_of_append"`
 
 		// If true, generate an app image (.art file) for this module.
-		App_image *bool
+		App_image proptools.Configurable[bool] `android:"replace_instead_of_append"`
 
 		// If true, use a checked-in profile to guide optimization.  Defaults to false unless
 		// a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR
 		// that matches the name of this module, in which case it is defaulted to true.
-		Profile_guided *bool
+		Profile_guided proptools.Configurable[bool] `android:"replace_instead_of_append"`
 
 		// If set, provides the path to profile relative to the Android.bp file.  If not set,
 		// defaults to searching for a file that matches the name of this module in the default
 		// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
-		Profile *string `android:"path"`
+		Profile proptools.Configurable[string] `android:"path,replace_instead_of_append"`
 
 		// If set to true, r8/d8 will use `profile` as input to generate a new profile that matches
 		// the optimized dex.
 		// The new profile will be subsequently used as the profile to dexpreopt the dex file.
-		Enable_profile_rewriting *bool
+		Enable_profile_rewriting proptools.Configurable[bool] `android:"replace_instead_of_append"`
 	}
 
 	Dex_preopt_result struct {
@@ -216,16 +209,25 @@
 			psi = prebuiltSelectionInfo
 		}
 	})
+
 	// Find the apex variant for this module
-	_, apexVariantsWithoutTestApexes, _ := android.ListSetDifference(apexInfo.InApexVariants, apexInfo.TestApexes)
+	apexVariantsWithoutTestApexes := []string{}
+	if apexInfo.BaseApexName != "" {
+		// This is a transitive dependency of an override_apex
+		apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, apexInfo.BaseApexName)
+	} else {
+		_, variants, _ := android.ListSetDifference(apexInfo.InApexVariants, apexInfo.TestApexes)
+		apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, variants...)
+	}
+	if apexInfo.ApexAvailableName != "" {
+		apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, apexInfo.ApexAvailableName)
+	}
 	disableSource := false
 	// find the selected apexes
 	for _, apexVariant := range apexVariantsWithoutTestApexes {
-		for _, selected := range psi.GetSelectedModulesForApiDomain(apexVariant) {
-			// If the apex_contribution for this api domain contains a prebuilt apex, disable the source variant
-			if strings.HasPrefix(selected, "prebuilt_com.google.android") {
-				disableSource = true
-			}
+		if len(psi.GetSelectedModulesForApiDomain(apexVariant)) > 0 {
+			// If the apex_contribution for this api domain is non-empty, disable the source variant
+			disableSource = true
 		}
 	}
 	return disableSource
@@ -242,7 +244,7 @@
 		return true
 	}
 
-	if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) {
+	if !d.dexpreoptProperties.Dex_preopt.Enabled.GetOrDefault(ctx, true) {
 		return true
 	}
 
@@ -359,7 +361,7 @@
 	d.dexpreopt(ctx, libraryName, dexJarFile)
 }
 
-func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.WritablePath) {
+func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.Path) {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
 	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
@@ -431,12 +433,12 @@
 
 	if d.inputProfilePathOnHost != nil {
 		profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost)
-	} else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) {
+	} else if d.dexpreoptProperties.Dex_preopt.Profile_guided.GetOrDefault(ctx, true) && !forPrebuiltApex(ctx) {
 		// If enable_profile_rewriting is set, use the rewritten profile instead of the checked-in profile
-		if d.EnableProfileRewriting() {
+		if d.EnableProfileRewriting(ctx) {
 			profileClassListing = android.OptionalPathForPath(d.GetRewrittenProfile())
 			profileIsTextListing = true
-		} else if profile := d.GetProfile(); profile != "" {
+		} else if profile := d.GetProfile(ctx); profile != "" {
 			// If dex_preopt.profile_guided is not set, default it based on the existence of the
 			// dexprepot.profile option or the profile class listing.
 			profileClassListing = android.OptionalPathForPath(
@@ -456,6 +458,8 @@
 	// Use the dexJar to create a unique scope for each
 	dexJarStem := strings.TrimSuffix(dexJarFile.Base(), dexJarFile.Ext())
 
+	appImage := d.dexpreoptProperties.Dex_preopt.App_image.Get(ctx)
+
 	// Full dexpreopt config, used to create dexpreopt build rules.
 	dexpreoptConfig := &dexpreopt.ModuleConfig{
 		Name:            libName,
@@ -484,14 +488,15 @@
 		PreoptBootClassPathDexFiles:     dexFiles.Paths(),
 		PreoptBootClassPathDexLocations: dexLocations,
 
-		NoCreateAppImage:    !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
-		ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
+		NoCreateAppImage:    !appImage.GetOrDefault(true),
+		ForceCreateAppImage: appImage.GetOrDefault(false),
 
 		PresignedPrebuilt: d.isPresignedPrebuilt,
 	}
 
 	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "dexpreopt.config")
 	dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
+	ctx.CheckbuildFile(d.configPath)
 
 	if d.dexpreoptDisabled(ctx, libName) {
 		return
@@ -583,16 +588,20 @@
 				// Preopting of boot classpath jars in the ART APEX are handled in
 				// java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
 				// The installs will be handled by Make as sub-modules of the java library.
-				d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
+				di := dexpreopterInstall{
 					name:                arch + "-" + installBase,
 					moduleName:          libName,
 					outputPathOnHost:    install.From,
 					installDirOnDevice:  installPath,
 					installFileOnDevice: installBase,
-				})
+				}
+				ctx.InstallFile(di.installDirOnDevice, di.installFileOnDevice, di.outputPathOnHost)
+				d.builtInstalledForApex = append(d.builtInstalledForApex, di)
+
 			}
 		} else if !d.preventInstall {
-			ctx.InstallFile(installPath, installBase, install.From)
+			// Install without adding to checkbuild to match behavior of previous Make-based checkbuild rules
+			ctx.InstallFileWithoutCheckbuild(installPath, installBase, install.From)
 		}
 	}
 
@@ -616,10 +625,8 @@
 	return installPath, relDir, installBase
 }
 
-// RuleBuilder.Install() adds output-to-install copy pairs to a list for Make. To share this
-// information with PackagingSpec in soong, call PackageFile for them.
-// The install path and the target install partition of the module must be the same.
-func packageFile(ctx android.ModuleContext, install android.RuleBuilderInstall) {
+// installFile will install the file if `install` path and the target install partition are the same.
+func installFile(ctx android.ModuleContext, install android.RuleBuilderInstall) {
 	installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
 	// Empty name means the install partition is not for the target image.
 	// For the system image, files for "apex" and "system_other" are skipped here.
@@ -628,7 +635,7 @@
 	// TODO(b/320196894): Files for "system_other" are skipped because soong creates the system
 	// image only for now.
 	if name != "" {
-		ctx.PackageFile(installPath.Join(ctx, relDir), name, install.From)
+		ctx.InstallFile(installPath.Join(ctx, relDir), name, install.From)
 	}
 }
 
@@ -652,16 +659,16 @@
 	d.shouldDisableDexpreopt = true
 }
 
-func (d *dexpreopter) EnableProfileRewriting() bool {
-	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting)
+func (d *dexpreopter) EnableProfileRewriting(ctx android.BaseModuleContext) bool {
+	return d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting.GetOrDefault(ctx, false)
 }
 
-func (d *dexpreopter) GetProfile() string {
-	return proptools.String(d.dexpreoptProperties.Dex_preopt.Profile)
+func (d *dexpreopter) GetProfile(ctx android.BaseModuleContext) string {
+	return d.dexpreoptProperties.Dex_preopt.Profile.GetOrDefault(ctx, "")
 }
 
-func (d *dexpreopter) GetProfileGuided() bool {
-	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Profile_guided)
+func (d *dexpreopter) GetProfileGuided(ctx android.BaseModuleContext) bool {
+	return d.dexpreoptProperties.Dex_preopt.Profile_guided.GetOrDefault(ctx, false)
 }
 
 func (d *dexpreopter) GetRewrittenProfile() android.Path {
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index defa82c..a2e4734 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -491,6 +491,11 @@
 	// Build path to a config file that Soong writes for Make (to be used in makefiles that install
 	// the default boot image).
 	dexpreoptConfigForMake android.WritablePath
+
+	// Build path to the boot framework profile.
+	// This is used as the `OutputFile` in `AndroidMkEntries`.
+	// A non-nil value ensures that this singleton module does not get skipped in AndroidMkEntries processing.
+	bootFrameworkProfile android.WritablePath
 }
 
 func (dbj *dexpreoptBootJars) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -553,6 +558,10 @@
 			apexVariationOfSelected := append(ctx.Target().Variations(), blueprint.Variation{Mutator: "apex", Variation: apex})
 			if ctx.OtherModuleDependencyVariantExists(apexVariationOfSelected, selected) {
 				ctx.AddFarVariationDependencies(apexVariationOfSelected, dexpreoptBootJarDepTag, selected)
+			} else if ctx.OtherModuleDependencyVariantExists(apexVariationOfSelected, android.RemoveOptionalPrebuiltPrefix(selected)) {
+				// The prebuilt might have been renamed by prebuilt_rename mutator if the source module does not exist.
+				// Remove the prebuilt_ prefix.
+				ctx.AddFarVariationDependencies(apexVariationOfSelected, dexpreoptBootJarDepTag, android.RemoveOptionalPrebuiltPrefix(selected))
 			}
 		}
 	}
@@ -603,7 +612,8 @@
 		installs := generateBootImage(ctx, config)
 		profileInstalls = append(profileInstalls, installs...)
 		if config == d.defaultBootImage {
-			_, installs := bootFrameworkProfileRule(ctx, config)
+			bootProfile, installs := bootFrameworkProfileRule(ctx, config)
+			d.bootFrameworkProfile = bootProfile
 			profileInstalls = append(profileInstalls, installs...)
 		}
 	}
@@ -613,7 +623,7 @@
 			profileLicenseMetadataFile: android.OptionalPathForPath(ctx.LicenseMetadataFile()),
 		})
 		for _, install := range profileInstalls {
-			packageFile(ctx, install)
+			installFile(ctx, install)
 		}
 	}
 }
@@ -939,24 +949,21 @@
 	}
 
 	for _, install := range image.installs {
-		packageFile(ctx, install)
+		installFile(ctx, install)
 	}
 
 	for _, install := range image.vdexInstalls {
-		if image.target.Arch.ArchType.Name != ctx.DeviceConfig().DeviceArch() {
-			// Note that the vdex files are identical between architectures. If the target image is
-			// not for the primary architecture create symlinks to share the vdex of the primary
-			// architecture with the other architectures.
-			//
-			// Assuming that the install path has the architecture name with it, replace the
-			// architecture name with the primary architecture name to find the source vdex file.
-			installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
-			if name != "" {
-				srcRelDir := strings.Replace(relDir, image.target.Arch.ArchType.Name, ctx.DeviceConfig().DeviceArch(), 1)
-				ctx.InstallSymlink(installPath.Join(ctx, relDir), name, installPath.Join(ctx, srcRelDir, name))
-			}
-		} else {
-			packageFile(ctx, install)
+		installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
+		if name == "" {
+			continue
+		}
+		// Note that the vdex files are identical between architectures. Copy the vdex to a no arch directory
+		// and create symlinks for both the primary and secondary arches.
+		ctx.InstallSymlink(installPath.Join(ctx, relDir), name, installPath.Join(ctx, "framework", name))
+		if image.target.Arch.ArchType.Name == ctx.DeviceConfig().DeviceArch() {
+			// Copy the vdex from the primary arch to the no-arch directory
+			// e.g. /system/framework/$bootjar.vdex
+			ctx.InstallFile(installPath.Join(ctx, "framework"), name, install.From)
 		}
 	}
 }
@@ -1234,7 +1241,7 @@
 
 	profile := bootImageProfileRuleCommon(ctx, image.name, image.dexPathsDeps.Paths(), image.getAnyAndroidVariant().dexLocationsDeps)
 
-	if image == defaultBootImageConfig(ctx) {
+	if image == defaultBootImageConfig(ctx) && profile != nil {
 		rule := android.NewRuleBuilder(pctx, ctx)
 		rule.Install(profile, "/system/etc/boot-image.prof")
 		return profile, rule.Installs()
@@ -1338,7 +1345,7 @@
 
 	image := d.defaultBootImage
 	if image != nil {
-		if profileInstallInfo, ok := android.SingletonModuleProvider(ctx, d, profileInstallInfoProvider); ok {
+		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())
@@ -1380,3 +1387,12 @@
 		ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(getImageNames(), " "))
 	}
 }
+
+// Add one of the outputs in `OutputFile`
+// This ensures that this singleton module does not get skipped when writing out/soong/Android-*.mk
+func (d *dexpreoptBootJars) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(d.bootFrameworkProfile),
+	}}
+}
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 104829f..37c54b9 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -1237,7 +1237,7 @@
 
 	if !mutated {
 		dexBootJarModule := result.ModuleForTests("dex_bootjars", "android_common")
-		profileInstallInfo, _ := android.SingletonModuleProvider(result, dexBootJarModule.Module(), profileInstallInfoProvider)
+		profileInstallInfo, _ := android.OtherModuleProvider(result, dexBootJarModule.Module(), profileInstallInfoProvider)
 		assertInstallsEqual(t, "profileInstalls", expected.profileInstalls, profileInstallInfo.profileInstalls)
 		android.AssertStringEquals(t, "profileLicenseMetadataFile", expected.profileLicenseMetadataFile, profileInstallInfo.profileLicenseMetadataFile.RelativeToTop().String())
 	}
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 73e33f4..07d0595 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -54,6 +54,7 @@
 					name: "foo",
 					installable: true,
 					srcs: ["a.java"],
+					sdk_version: "current",
 				}`,
 			enabled: true,
 		},
@@ -98,6 +99,7 @@
 				java_library {
 					name: "foo",
 					installable: true,
+					sdk_version: "current",
 				}`,
 			enabled: false,
 		},
@@ -107,6 +109,7 @@
 				java_library {
 					name: "foo",
 					srcs: ["a.java"],
+					sdk_version: "current",
 				}`,
 			enabled: false,
 		},
@@ -144,6 +147,7 @@
 					name: "foo",
 					srcs: ["a.java"],
 					compile_dex: true,
+					sdk_version: "current",
 				}`,
 			enabled: false,
 		},
@@ -164,6 +168,7 @@
 					installable: true,
 					srcs: ["a.java"],
 					apex_available: ["com.android.apex1"],
+					sdk_version: "current",
 				}`,
 			apexVariant: true,
 			enabled:     false,
@@ -176,6 +181,7 @@
 					installable: true,
 					srcs: ["a.java"],
 					apex_available: ["com.android.apex1"],
+					sdk_version: "current",
 				}`,
 			moduleName:  "service-foo",
 			apexVariant: true,
@@ -189,6 +195,7 @@
 					installable: true,
 					srcs: ["a.java"],
 					apex_available: ["com.android.apex1"],
+					sdk_version: "current",
 				}`,
 			moduleName:  "prebuilt_service-foo",
 			apexVariant: true,
@@ -202,6 +209,7 @@
 					installable: true,
 					srcs: ["a.java"],
 					apex_available: ["com.android.apex1"],
+					sdk_version: "current",
 				}`,
 			moduleName:  "service-foo",
 			apexVariant: false,
@@ -311,6 +319,7 @@
 			installable: true,
 			srcs: ["a.java"],
 			apex_available: ["com.android.apex1"],
+			sdk_version: "current",
 		}`)
 	ctx := result.TestContext
 	module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
@@ -342,6 +351,7 @@
 			name: "foo",
 			installable: true,
 			srcs: ["a.java"],
+			sdk_version: "current",
 		}`)
 	ctx = result.TestContext
 	module = ctx.ModuleForTests("foo", "android_common")
@@ -398,6 +408,7 @@
 			installable: true,
 			srcs: ["a.java"],
 			apex_available: ["com.android.apex1"],
+			sdk_version: "current",
 		}`)
 	ctx := result.TestContext
 	module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
@@ -429,6 +440,7 @@
 			name: "foo",
 			installable: true,
 			srcs: ["a.java"],
+			sdk_version: "current",
 		}`)
 	ctx = result.TestContext
 	module = ctx.ModuleForTests("foo", "android_common")
@@ -454,6 +466,7 @@
 				profile: "art-profile",
 			},
 			srcs: ["a.java"],
+			sdk_version: "current",
 		}`)
 
 	ctx := result.TestContext
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 176779e..a7e92d9 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -54,7 +54,7 @@
 	Filter_packages []string
 
 	// list of java libraries that will be in the classpath.
-	Libs []string `android:"arch_variant"`
+	Libs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// If set to false, don't allow this module(-docs.zip) to be exported. Defaults to true.
 	Installable *bool
@@ -223,17 +223,6 @@
 	exportableStubsSrcJar android.WritablePath
 }
 
-func (j *Javadoc) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{j.stubsSrcJar}, nil
-	case ".docs.zip":
-		return android.Paths{j.docZip}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 // javadoc converts .java source files to documentation using javadoc.
 func JavadocFactory() android.Module {
 	module := &Javadoc{}
@@ -254,8 +243,6 @@
 	return module
 }
 
-var _ android.OutputFileProducer = (*Javadoc)(nil)
-
 func (j *Javadoc) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return android.SdkSpecFrom(ctx, String(j.properties.Sdk_version))
 }
@@ -287,7 +274,7 @@
 		}
 	}
 
-	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
+	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs.GetOrDefault(ctx, nil)...)
 }
 
 func (j *Javadoc) collectAidlFlags(ctx android.ModuleContext, deps deps) droiddocBuilderFlags {
@@ -378,16 +365,19 @@
 		case bootClasspathTag:
 			if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 				deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars...)
-			} else if sm, ok := module.(SystemModulesProvider); ok {
+			} else if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok {
 				// A system modules dependency has been added to the bootclasspath
 				// so add its libs to the bootclasspath.
-				deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...)
+				deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars...)
 			} else {
 				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 			}
 		case libTag, sdkLibTag:
-			if dep, ok := module.(SdkLibraryDependency); ok {
-				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
+			if _, ok := module.(SdkLibraryDependency); ok {
+				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)
 			} else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
@@ -409,9 +399,12 @@
 			if deps.systemModules != nil {
 				panic("Found two system module dependencies")
 			}
-			sm := module.(SystemModulesProvider)
-			outputDir, outputDeps := sm.OutputDirAndDeps()
-			deps.systemModules = &systemModules{outputDir, outputDeps}
+			if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok {
+				deps.systemModules = &systemModules{sm.OutputDir, sm.OutputDirDeps}
+			} else {
+				ctx.PropertyErrorf("boot classpath dependency %q does not provide SystemModulesProvider",
+					ctx.OtherModuleName(module))
+			}
 		case aconfigDeclarationTag:
 			if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey); ok {
 				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPath)
@@ -585,6 +578,9 @@
 	zipSyncCleanupCmd(rule, srcJarDir)
 
 	rule.Build("javadoc", "javadoc")
+
+	ctx.SetOutputFiles(android.Paths{j.stubsSrcJar}, "")
+	ctx.SetOutputFiles(android.Paths{j.docZip}, ".docs.zip")
 }
 
 // Droiddoc
@@ -616,15 +612,6 @@
 	return module
 }
 
-func (d *Droiddoc) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "", ".docs.zip":
-		return android.Paths{d.Javadoc.docZip}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (d *Droiddoc) DepsMutator(ctx android.BottomUpMutatorContext) {
 	d.Javadoc.addDeps(ctx)
 
@@ -876,6 +863,9 @@
 	zipSyncCleanupCmd(rule, srcJarDir)
 
 	rule.Build("javadoc", desc)
+
+	ctx.SetOutputFiles(android.Paths{d.Javadoc.docZip}, "")
+	ctx.SetOutputFiles(android.Paths{d.Javadoc.docZip}, ".docs.zip")
 }
 
 // Exported Droiddoc Directory
diff --git a/java/droiddoc_test.go b/java/droiddoc_test.go
index 8d1f591..9e1ebbe 100644
--- a/java/droiddoc_test.go
+++ b/java/droiddoc_test.go
@@ -69,11 +69,7 @@
 			"bar-doc/a.java": nil,
 			"bar-doc/b.java": nil,
 		})
-	barStubs := ctx.ModuleForTests("bar-stubs", "android_common")
-	barStubsOutputs, err := barStubs.Module().(*Droidstubs).OutputFiles("")
-	if err != nil {
-		t.Errorf("Unexpected error %q retrieving \"bar-stubs\" output file", err)
-	}
+	barStubsOutputs := ctx.ModuleForTests("bar-stubs", "android_common").OutputFiles(ctx, t, "")
 	if len(barStubsOutputs) != 1 {
 		t.Errorf("Expected one output from \"bar-stubs\" got %s", barStubsOutputs)
 	}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index b32b754..6bcdf85 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -197,6 +197,10 @@
 	// a list of aconfig_declarations module names that the stubs generated in this module
 	// depend on.
 	Aconfig_declarations []string
+
+	// List of hard coded filegroups containing Metalava config files that are passed to every
+	// Metalava invocation that this module performs. See addMetalavaConfigFilesToCmd.
+	ConfigFiles []string `android:"path" blueprint:"mutated"`
 }
 
 // Used by xsd_config
@@ -259,6 +263,7 @@
 
 	module.AddProperties(&module.properties,
 		&module.Javadoc.properties)
+	module.properties.ConfigFiles = getMetalavaConfigFilegroupReference()
 	module.initModuleAndImport(module)
 
 	InitDroiddocModule(module, android.HostAndDeviceSupported)
@@ -279,70 +284,11 @@
 	module.AddProperties(&module.properties,
 		&module.Javadoc.properties)
 
+	module.properties.ConfigFiles = getMetalavaConfigFilegroupReference()
 	InitDroiddocModule(module, android.HostSupported)
 	return module
 }
 
-func getStubsTypeAndTag(tag string) (StubsType, string, error) {
-	if len(tag) == 0 {
-		return Everything, "", nil
-	}
-	if tag[0] != '.' {
-		return Unavailable, "", fmt.Errorf("tag must begin with \".\"")
-	}
-
-	stubsType := Everything
-	// Check if the tag has a stubs type prefix (e.g. ".exportable")
-	for st := Everything; st <= Exportable; st++ {
-		if strings.HasPrefix(tag, "."+st.String()) {
-			stubsType = st
-		}
-	}
-
-	return stubsType, strings.TrimPrefix(tag, "."+stubsType.String()), nil
-}
-
-// Droidstubs' tag supports specifying with the stubs type.
-// While supporting the pre-existing tags, it also supports tags with
-// the stubs type prefix. Some examples are shown below:
-// {.annotations.zip} - pre-existing behavior. Returns the path to the
-// annotation zip.
-// {.exportable} - Returns the path to the exportable stubs src jar.
-// {.exportable.annotations.zip} - Returns the path to the exportable
-// annotations zip file.
-// {.runtime.api_versions.xml} - Runtime stubs does not generate api versions
-// xml file. For unsupported combinations, the default everything output file
-// is returned.
-func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
-	stubsType, prefixRemovedTag, err := getStubsTypeAndTag(tag)
-	if err != nil {
-		return nil, err
-	}
-	switch prefixRemovedTag {
-	case "":
-		stubsSrcJar, err := d.StubsSrcJar(stubsType)
-		return android.Paths{stubsSrcJar}, err
-	case ".docs.zip":
-		docZip, err := d.DocZip(stubsType)
-		return android.Paths{docZip}, err
-	case ".api.txt", android.DefaultDistTag:
-		// This is the default dist path for dist properties that have no tag property.
-		apiFilePath, err := d.ApiFilePath(stubsType)
-		return android.Paths{apiFilePath}, err
-	case ".removed-api.txt":
-		removedApiFilePath, err := d.RemovedApiFilePath(stubsType)
-		return android.Paths{removedApiFilePath}, err
-	case ".annotations.zip":
-		annotationsZip, err := d.AnnotationsZip(stubsType)
-		return android.Paths{annotationsZip}, err
-	case ".api_versions.xml":
-		apiVersionsXmlFilePath, err := d.ApiVersionsXmlFilePath(stubsType)
-		return android.Paths{apiVersionsXmlFilePath}, err
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (d *Droidstubs) AnnotationsZip(stubsType StubsType) (ret android.Path, err error) {
 	switch stubsType {
 	case Everything:
@@ -754,7 +700,7 @@
 }
 
 func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
-	srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams) *android.RuleBuilderCommand {
+	srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams, configFiles android.Paths) *android.RuleBuilderCommand {
 	rule.Command().Text("rm -rf").Flag(homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(homeDir.String())
 
@@ -798,9 +744,26 @@
 
 	cmd.Flag(config.MetalavaFlags)
 
+	addMetalavaConfigFilesToCmd(cmd, configFiles)
+
 	return cmd
 }
 
+// MetalavaConfigFilegroup is the name of the filegroup in build/soong/java/metalava that lists
+// the configuration files to pass to Metalava.
+const MetalavaConfigFilegroup = "metalava-config-files"
+
+// Get a reference to the MetalavaConfigFilegroup suitable for use in a property.
+func getMetalavaConfigFilegroupReference() []string {
+	return []string{":" + MetalavaConfigFilegroup}
+}
+
+// addMetalavaConfigFilesToCmd adds --config-file options to use the config files list in the
+// MetalavaConfigFilegroup filegroup.
+func addMetalavaConfigFilesToCmd(cmd *android.RuleBuilderCommand, configFiles android.Paths) {
+	cmd.FlagForEachInput("--config-file ", configFiles)
+}
+
 // Pass flagged apis related flags to metalava. When aconfig_declarations property is not
 // defined for a module, simply revert all flagged apis annotations. If aconfig_declarations
 // property is defined, apply transformations and only revert the flagged apis that are not
@@ -872,7 +835,10 @@
 	srcJarList := zipSyncCmd(ctx, rule, params.srcJarDir, d.Javadoc.srcJars)
 
 	homeDir := android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "home")
-	cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig)
+
+	configFiles := android.PathsForModuleSrc(ctx, d.properties.ConfigFiles)
+
+	cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig, configFiles)
 	cmd.Implicits(d.Javadoc.implicits)
 
 	d.stubsFlags(ctx, cmd, params.stubsDir, params.stubConfig.stubsType, params.stubConfig.checkApi)
@@ -1004,6 +970,15 @@
 		d.apiLintReport = android.PathForModuleOut(ctx, Everything.String(), "api_lint_report.txt")
 		cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO:  Change to ":api-lint"
 
+		// If UnflaggedApi issues have not already been configured then make sure that existing
+		// UnflaggedApi issues are reported as warnings but issues in new/changed code are treated as
+		// errors by the Build Warnings Aye Aye Analyzer in Gerrit.
+		// Once existing issues have been fixed this will be changed to error.
+		// TODO(b/362771529): Switch to --error
+		if !strings.Contains(cmd.String(), " UnflaggedApi ") {
+			cmd.Flag("--error-when-new UnflaggedApi")
+		}
+
 		// TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
 		if d.Name() != "android.car-system-stubs-docs" &&
 			d.Name() != "android.car-stubs-docs" {
@@ -1363,6 +1338,46 @@
 
 		rule.Build("nullabilityWarningsCheck", "nullability warnings check")
 	}
+
+	d.setOutputFiles(ctx)
+}
+
+// This method sets the outputFiles property, which is used to set the
+// OutputFilesProvider later.
+// Droidstubs' tag supports specifying with the stubs type.
+// While supporting the pre-existing tags, it also supports tags with
+// the stubs type prefix. Some examples are shown below:
+// {.annotations.zip} - pre-existing behavior. Returns the path to the
+// annotation zip.
+// {.exportable} - Returns the path to the exportable stubs src jar.
+// {.exportable.annotations.zip} - Returns the path to the exportable
+// annotations zip file.
+// {.runtime.api_versions.xml} - Runtime stubs does not generate api versions
+// xml file. For unsupported combinations, the default everything output file
+// is returned.
+func (d *Droidstubs) setOutputFiles(ctx android.ModuleContext) {
+	tagToOutputFileFunc := map[string]func(StubsType) (android.Path, error){
+		"":                     d.StubsSrcJar,
+		".docs.zip":            d.DocZip,
+		".api.txt":             d.ApiFilePath,
+		android.DefaultDistTag: d.ApiFilePath,
+		".removed-api.txt":     d.RemovedApiFilePath,
+		".annotations.zip":     d.AnnotationsZip,
+		".api_versions.xml":    d.ApiVersionsXmlFilePath,
+	}
+	stubsTypeToPrefix := map[StubsType]string{
+		Everything: "",
+		Exportable: ".exportable",
+	}
+	for _, tag := range android.SortedKeys(tagToOutputFileFunc) {
+		for _, stubType := range android.SortedKeys(stubsTypeToPrefix) {
+			tagWithPrefix := stubsTypeToPrefix[stubType] + tag
+			outputFile, err := tagToOutputFileFunc[tag](stubType)
+			if err == nil {
+				ctx.SetOutputFiles(android.Paths{outputFile}, tagWithPrefix)
+			}
+		}
+	}
 }
 
 func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) {
@@ -1453,17 +1468,6 @@
 	stubsSrcJar android.Path
 }
 
-func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	// prebuilt droidstubs does not output "exportable" stubs.
-	// Output the "everything" stubs srcjar file if the tag is ".exportable".
-	case "", ".exportable":
-		return android.Paths{p.stubsSrcJar}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (d *PrebuiltStubsSources) StubsSrcJar(_ StubsType) (android.Path, error) {
 	return d.stubsSrcJar, nil
 }
@@ -1502,6 +1506,11 @@
 		rule.Build("zip src", "Create srcjar from prebuilt source")
 		p.stubsSrcJar = outPath
 	}
+
+	ctx.SetOutputFiles(android.Paths{p.stubsSrcJar}, "")
+	// prebuilt droidstubs does not output "exportable" stubs.
+	// Output the "everything" stubs srcjar file if the tag is ".exportable".
+	ctx.SetOutputFiles(android.Paths{p.stubsSrcJar}, ".exportable")
 }
 
 func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 6a14f36..1e8362c 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -421,11 +421,9 @@
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
 			variables.ExportRuntimeApis = proptools.BoolPtr(true)
 		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 		android.FixtureMergeMockFs(map[string][]byte{
 			"a/A.java":      nil,
 			"a/current.txt": nil,
diff --git a/java/generated_java_library.go b/java/generated_java_library.go
index d5e6d8f..79f1b6f 100644
--- a/java/generated_java_library.go
+++ b/java/generated_java_library.go
@@ -70,14 +70,6 @@
 	module.Library.properties.Libs = append(module.Library.properties.Libs, name)
 }
 
-// Add a java shared library as a dependency, as if they had said `libs: [ "name" ]`
-func (module *GeneratedJavaLibraryModule) AddStaticLibrary(name string) {
-	if module.depsMutatorDone {
-		panic("GeneratedJavaLibraryModule.AddStaticLibrary called after DepsMutator")
-	}
-	module.Library.properties.Static_libs = append(module.Library.properties.Static_libs, name)
-}
-
 func (module *GeneratedJavaLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	module.callbacks.DepsMutator(module, ctx)
 	module.depsMutatorDone = true
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 5441a3b..b1a9deb 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -98,8 +98,9 @@
 	// processing.
 	classesJars := android.Paths{classesJar}
 	ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) {
-		javaInfo, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
-		classesJars = append(classesJars, javaInfo.ImplementationJars...)
+		if javaInfo, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+			classesJars = append(classesJars, javaInfo.ImplementationJars...)
+		}
 	})
 	h.classesJarPaths = classesJars
 
@@ -151,7 +152,7 @@
 //
 // Otherwise, it creates a copy of the supplied dex file into which it has encoded the hiddenapi
 // flags and returns this instead of the supplied dex jar.
-func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android.OutputPath) android.OutputPath {
+func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android.Path) android.Path {
 
 	if !h.active {
 		return dexJar
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index cab5402..4144de8 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -1255,8 +1255,9 @@
 	rule := android.NewRuleBuilder(pctx, ctx)
 	rule.Command().
 		BuiltTool("metalava").
+		Text("signature-to-dex").
 		Inputs(removedTxtFiles).
-		FlagWithOutput("--dex-api ", output)
+		FlagWithOutput("--out ", output)
 	rule.Build("modular-hiddenapi-removed-dex-signatures"+suffix, "modular hiddenapi removed dex signatures"+suffix)
 	return android.OptionalPathForPath(output)
 }
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 6229797..afe8b4c 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -203,10 +203,8 @@
 				FixtureConfigureBootJars("platform:foo"),
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
-					variables.BuildFlags = map[string]string{
-						"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-					}
 				}),
+				android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 			).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
diff --git a/java/jarjar_test.go b/java/jarjar_test.go
new file mode 100644
index 0000000..82bfa2b
--- /dev/null
+++ b/java/jarjar_test.go
@@ -0,0 +1,85 @@
+// 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 java
+
+import (
+	"fmt"
+	"testing"
+
+	"android/soong/android"
+)
+
+func AssertJarJarRename(t *testing.T, result *android.TestResult, libName, original, expectedRename string) {
+	module := result.ModuleForTests(libName, "android_common")
+
+	provider, found := android.OtherModuleProvider(result.OtherModuleProviderAdaptor(), module.Module(), JarJarProvider)
+	android.AssertBoolEquals(t, fmt.Sprintf("found provider (%s)", libName), true, found)
+
+	renamed, found := provider.Rename[original]
+	android.AssertBoolEquals(t, fmt.Sprintf("found rename (%s)", libName), true, found)
+	android.AssertStringEquals(t, fmt.Sprintf("renamed (%s)", libName), expectedRename, renamed)
+}
+
+func TestJarJarRenameDifferentModules(t *testing.T) {
+	t.Parallel()
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+		java_library {
+			name: "their_lib",
+			jarjar_rename: ["com.example.a"],
+		}
+
+		java_library {
+			name: "boundary_lib",
+			jarjar_prefix: "RENAME",
+			static_libs: ["their_lib"],
+		}
+
+		java_library {
+			name: "my_lib",
+			static_libs: ["boundary_lib"],
+		}
+	`)
+
+	original := "com.example.a"
+	renamed := "RENAME.com.example.a"
+	AssertJarJarRename(t, result, "their_lib", original, "")
+	AssertJarJarRename(t, result, "boundary_lib", original, renamed)
+	AssertJarJarRename(t, result, "my_lib", original, renamed)
+}
+
+func TestJarJarRenameSameModule(t *testing.T) {
+	t.Parallel()
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+		java_library {
+			name: "their_lib",
+			jarjar_rename: ["com.example.a"],
+			jarjar_prefix: "RENAME",
+		}
+
+		java_library {
+			name: "my_lib",
+			static_libs: ["their_lib"],
+		}
+	`)
+
+	original := "com.example.a"
+	renamed := "RENAME.com.example.a"
+	AssertJarJarRename(t, result, "their_lib", original, renamed)
+	AssertJarJarRename(t, result, "my_lib", original, renamed)
+}
diff --git a/java/java.go b/java/java.go
index 8899ba4..c1187dc 100644
--- a/java/java.go
+++ b/java/java.go
@@ -254,27 +254,40 @@
 type JavaInfo struct {
 	// HeaderJars is a list of jars that can be passed as the javac classpath in order to link
 	// against this module.  If empty, ImplementationJars should be used instead.
+	// Unlike LocalHeaderJars, HeaderJars includes classes from static dependencies.
 	HeaderJars android.Paths
 
 	RepackagedHeaderJars android.Paths
 
 	// set of header jars for all transitive libs deps
-	TransitiveLibsHeaderJars *android.DepSet[android.Path]
+	TransitiveLibsHeaderJarsForR8 *android.DepSet[android.Path]
 
 	// set of header jars for all transitive static libs deps
+	TransitiveStaticLibsHeaderJarsForR8 *android.DepSet[android.Path]
+
+	// depset of header jars for this module and all transitive static dependencies
 	TransitiveStaticLibsHeaderJars *android.DepSet[android.Path]
 
+	// depset of implementation jars for this module and all transitive static dependencies
+	TransitiveStaticLibsImplementationJars *android.DepSet[android.Path]
+
+	// depset of resource jars for this module and all transitive static dependencies
+	TransitiveStaticLibsResourceJars *android.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.
 	ImplementationAndResourcesJars android.Paths
 
 	// ImplementationJars is a list of jars that contain the implementations of classes in the
-	//module.
+	// module.
 	ImplementationJars android.Paths
 
 	// ResourceJars is a list of jars that contain the resources included in the module.
 	ResourceJars android.Paths
 
+	// LocalHeaderJars is a list of jars that contain classes from this module, but not from any static dependencies.
+	LocalHeaderJars android.Paths
+
 	// AidlIncludeDirs is a list of directories that should be passed to the aidl tool when
 	// depending on this module.
 	AidlIncludeDirs android.Paths
@@ -315,14 +328,14 @@
 	AconfigIntermediateCacheOutputPaths android.Paths
 }
 
-var JavaInfoProvider = blueprint.NewProvider[JavaInfo]()
+var JavaInfoProvider = blueprint.NewProvider[*JavaInfo]()
 
 // SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to
 // the sysprop implementation library.
 type SyspropPublicStubInfo struct {
 	// JavaInfo is the JavaInfoProvider of the sysprop public stub library that corresponds to
 	// the sysprop implementation library.
-	JavaInfo JavaInfo
+	JavaInfo *JavaInfo
 }
 
 var SyspropPublicStubInfoProvider = blueprint.NewProvider[SyspropPublicStubInfo]()
@@ -343,12 +356,17 @@
 // TODO(jungjw): Move this to kythe.go once it's created.
 type xref interface {
 	XrefJavaFiles() android.Paths
+	XrefKotlinFiles() android.Paths
 }
 
 func (j *Module) XrefJavaFiles() android.Paths {
 	return j.kytheFiles
 }
 
+func (j *Module) XrefKotlinFiles() android.Paths {
+	return j.kytheKotlinFiles
+}
+
 func (d dependencyTag) PropagateAconfigValidation() bool {
 	return d.static
 }
@@ -422,8 +440,6 @@
 	systemModulesTag        = dependencyTag{name: "system modules", runtimeLinked: true}
 	frameworkResTag         = dependencyTag{name: "framework-res"}
 	omniromResTag           = dependencyTag{name: "omnirom-res"}
-	kotlinStdlibTag         = dependencyTag{name: "kotlin-stdlib", runtimeLinked: true}
-	kotlinAnnotationsTag    = dependencyTag{name: "kotlin-annotations", runtimeLinked: true}
 	kotlinPluginTag         = dependencyTag{name: "kotlin-plugin", toolchain: true}
 	proguardRaiseTag        = dependencyTag{name: "proguard-raise"}
 	certificateTag          = dependencyTag{name: "certificate"}
@@ -433,7 +449,6 @@
 	r8LibraryJarTag         = dependencyTag{name: "r8-libraryjar", runtimeLinked: true}
 	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
 	javaApiContributionTag  = dependencyTag{name: "java-api-contribution"}
-	depApiSrcsTag           = dependencyTag{name: "dep-api-srcs"}
 	aconfigDeclarationTag   = dependencyTag{name: "aconfig-declaration"}
 	jniInstallTag           = dependencyTag{name: "jni install", runtimeLinked: true, installable: true}
 	binaryInstallTag        = dependencyTag{name: "binary install", runtimeLinked: true, installable: true}
@@ -444,6 +459,28 @@
 	usesLibCompat30OptTag   = makeUsesLibraryDependencyTag(30, true)
 )
 
+// A list of tags for deps used for compiling a module.
+// Any dependency tags that modifies the following properties of `deps` in `Module.collectDeps` should be
+// added to this list:
+// - bootClasspath
+// - classpath
+// - java9Classpath
+// - systemModules
+// - kotlin deps...
+var (
+	compileDependencyTags = []blueprint.DependencyTag{
+		sdkLibTag,
+		libTag,
+		staticLibTag,
+		bootClasspathTag,
+		systemModulesTag,
+		java9LibTag,
+		kotlinPluginTag,
+		syspropPublicStubDepTag,
+		instrumentationForTag,
+	}
+)
+
 func IsLibDepTag(depTag blueprint.DependencyTag) bool {
 	return depTag == libTag || depTag == sdkLibTag
 }
@@ -540,7 +577,7 @@
 	// are provided by systemModules.
 	java9Classpath classpath
 
-	processorPath           classpath
+	processorPath           classpath ``
 	errorProneProcessorPath classpath
 	processorClasses        []string
 	staticJars              android.Paths
@@ -551,12 +588,14 @@
 	srcJars                 android.Paths
 	systemModules           *systemModules
 	aidlPreprocess          android.OptionalPath
-	kotlinStdlib            android.Paths
-	kotlinAnnotations       android.Paths
 	kotlinPlugins           android.Paths
 	aconfigProtoFiles       android.Paths
 
 	disableTurbine bool
+
+	transitiveStaticLibsHeaderJars         []*android.DepSet[android.Path]
+	transitiveStaticLibsImplementationJars []*android.DepSet[android.Path]
+	transitiveStaticLibsResourceJars       []*android.DepSet[android.Path]
 }
 
 func checkProducesJars(ctx android.ModuleContext, dep android.SourceFileProducer) {
@@ -969,7 +1008,7 @@
 			j.dexpreopter.disableDexpreopt()
 		}
 	}
-	j.compile(ctx, nil, nil, nil)
+	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.
@@ -983,6 +1022,8 @@
 		TestOnly:       Bool(j.sourceProperties.Test_only),
 		TopLevelTarget: j.sourceProperties.Top_level_test_target,
 	})
+
+	setOutputFiles(ctx, j.Module)
 }
 
 func (j *Library) setInstallRules(ctx android.ModuleContext, installModuleName string) {
@@ -995,7 +1036,7 @@
 		}
 		hostDexNeeded := Bool(j.deviceProperties.Hostdex) && !ctx.Host()
 		if hostDexNeeded {
-			j.hostdexInstallFile = ctx.InstallFile(
+			j.hostdexInstallFile = ctx.InstallFileWithoutCheckbuild(
 				android.PathForHostDexInstall(ctx, "framework"),
 				j.Stem()+"-hostdex.jar", j.outputFile)
 		}
@@ -1009,7 +1050,7 @@
 		} else {
 			installDir = android.PathForModuleInstall(ctx, "framework")
 		}
-		j.installFile = ctx.InstallFile(installDir, j.Stem()+".jar", j.outputFile, extraInstallDeps...)
+		j.installFile = ctx.InstallFileWithoutCheckbuild(installDir, j.Stem()+".jar", j.outputFile, extraInstallDeps...)
 	}
 }
 
@@ -1362,7 +1403,7 @@
 	return true
 }
 
-func (j *TestHost) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (j *TestHost) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
@@ -1507,7 +1548,7 @@
 		InstalledFiles:      j.data,
 		OutputFile:          j.outputFile,
 		TestConfig:          j.testConfig,
-		RequiredModuleNames: j.RequiredModuleNames(),
+		RequiredModuleNames: j.RequiredModuleNames(ctx),
 		TestSuites:          j.testProperties.Test_suites,
 		IsHost:              true,
 		LocalSdkVersion:     j.sdkVersion.String(),
@@ -1516,6 +1557,7 @@
 }
 
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	checkMinSdkVersionMts(ctx, j.MinSdkVersion(ctx))
 	j.generateAndroidBuildActionsWithConfig(ctx, nil)
 	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
@@ -1841,6 +1883,8 @@
 		// libraries.  This is verified by TestBinary.
 		j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
 			ctx.ModuleName()+ext, j.wrapperFile)
+
+		setOutputFiles(ctx, j.Library.Module)
 	}
 }
 
@@ -1848,10 +1892,12 @@
 	if ctx.Arch().ArchType == android.Common {
 		j.deps(ctx)
 	}
-	if ctx.Arch().ArchType != android.Common {
-		// These dependencies ensure the host installation rules will install the jar file and
-		// the jni libraries when the wrapper is installed.
+	// 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())
@@ -1976,17 +2022,11 @@
 
 	// List of shared java libs that this module has dependencies to and
 	// should be passed as classpath in javac invocation
-	Libs []string
+	Libs proptools.Configurable[[]string]
 
 	// List of java libs that this module has static dependencies to and will be
 	// merge zipped after metalava invocation
-	Static_libs []string
-
-	// Java Api library to provide the full API surface stub jar file.
-	// If this property is set, the stub jar of this module is created by
-	// extracting the compiled class files provided by the
-	// full_api_surface_stub module.
-	Full_api_surface_stub *string
+	Static_libs proptools.Configurable[[]string]
 
 	// Version of previously released API file for compatibility check.
 	Previous_api *string `android:"path"`
@@ -2016,12 +2056,26 @@
 	// List of aconfig_declarations module names that the stubs generated in this module
 	// depend on.
 	Aconfig_declarations []string
+
+	// List of hard coded filegroups containing Metalava config files that are passed to every
+	// Metalava invocation that this module performs. See addMetalavaConfigFilesToCmd.
+	ConfigFiles []string `android:"path" blueprint:"mutated"`
+
+	// If not blank, set to the version of the sdk to compile against.
+	// Defaults to an empty string, which compiles the module against the private platform APIs.
+	// Values are of one of the following forms:
+	// 1) numerical API level, "current", "none", or "core_platform"
+	// 2) An SDK kind with an API level: "<sdk kind>_<API level>"
+	// See build/soong/android/sdk_version.go for the complete and up to date list of SDK kinds.
+	// If the SDK kind is empty, it will be set to public.
+	Sdk_version *string
 }
 
 func ApiLibraryFactory() android.Module {
 	module := &ApiLibrary{}
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	module.AddProperties(&module.properties)
+	module.properties.ConfigFiles = getMetalavaConfigFilegroupReference()
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	module.initModuleAndImport(module)
 	android.InitDefaultableModule(module)
 	return module
@@ -2037,7 +2091,7 @@
 
 func metalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
 	srcs android.Paths, homeDir android.WritablePath,
-	classpath android.Paths) *android.RuleBuilderCommand {
+	classpath android.Paths, configFiles android.Paths) *android.RuleBuilderCommand {
 	rule.Command().Text("rm -rf").Flag(homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(homeDir.String())
 
@@ -2076,6 +2130,8 @@
 		FlagWithArg("--hide ", "InvalidNullabilityOverride").
 		FlagWithArg("--hide ", "ChangedDefault")
 
+	addMetalavaConfigFilesToCmd(cmd, configFiles)
+
 	if len(classpath) == 0 {
 		// The main purpose of the `--api-class-resolution api` option is to force metalava to ignore
 		// classes on the classpath when an API file contains missing classes. However, as this command
@@ -2111,40 +2167,6 @@
 	}
 }
 
-// This method extracts the stub class files from the stub jar file provided
-// from full_api_surface_stub module instead of compiling the srcjar generated from invoking metalava.
-// This method is used because metalava can generate compilable from-text stubs only when
-// the codebase encompasses all classes listed in the input API text file, and a class can extend
-// a class that is not within the same API domain.
-func (al *ApiLibrary) extractApiSrcs(ctx android.ModuleContext, rule *android.RuleBuilder, stubsDir android.OptionalPath, fullApiSurfaceStubJar android.Path) {
-	classFilesList := android.PathForModuleOut(ctx, "metalava", "classes.txt")
-	unzippedSrcJarDir := android.PathForModuleOut(ctx, "metalava", "unzipDir")
-
-	rule.Command().
-		BuiltTool("list_files").
-		Text(stubsDir.String()).
-		FlagWithOutput("--out ", classFilesList).
-		FlagWithArg("--extensions ", ".java").
-		FlagWithArg("--root ", unzippedSrcJarDir.String()).
-		Flag("--classes")
-
-	rule.Command().
-		Text("unzip").
-		Flag("-q").
-		Input(fullApiSurfaceStubJar).
-		FlagWithArg("-d ", unzippedSrcJarDir.String())
-
-	rule.Command().
-		BuiltTool("soong_zip").
-		Flag("-jar").
-		Flag("-write_if_changed").
-		Flag("-ignore_missing_files").
-		Flag("-quiet").
-		FlagWithArg("-C ", unzippedSrcJarDir.String()).
-		FlagWithInput("-l ", classFilesList).
-		FlagWithOutput("-o ", al.stubsJarWithoutStaticLibs)
-}
-
 func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
 	apiContributions := al.properties.Api_contributions
 	addValidations := !ctx.Config().IsEnvTrue("DISABLE_STUB_VALIDATION") &&
@@ -2171,14 +2193,18 @@
 			}
 		}
 	}
-	ctx.AddVariationDependencies(nil, libTag, al.properties.Libs...)
-	ctx.AddVariationDependencies(nil, staticLibTag, al.properties.Static_libs...)
-	if al.properties.Full_api_surface_stub != nil {
-		ctx.AddVariationDependencies(nil, depApiSrcsTag, String(al.properties.Full_api_surface_stub))
+	if ctx.Device() {
+		sdkDep := decodeSdkDep(ctx, android.SdkContext(al))
+		if sdkDep.useModule {
+			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+			ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
+			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
+
+		}
 	}
-	if al.properties.System_modules != nil {
-		ctx.AddVariationDependencies(nil, systemModulesTag, String(al.properties.System_modules))
-	}
+	ctx.AddVariationDependencies(nil, libTag, al.properties.Libs.GetOrDefault(ctx, nil)...)
+	ctx.AddVariationDependencies(nil, staticLibTag, al.properties.Static_libs.GetOrDefault(ctx, nil)...)
+
 	for _, aconfigDeclarationsName := range al.properties.Aconfig_declarations {
 		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationsName)
 	}
@@ -2234,8 +2260,8 @@
 
 	var srcFilesInfo []JavaApiImportInfo
 	var classPaths android.Paths
+	var bootclassPaths android.Paths
 	var staticLibs android.Paths
-	var depApiSrcsStubsJar android.Path
 	var systemModulesPaths android.Paths
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		tag := ctx.OtherModuleDependencyTag(dep)
@@ -2247,17 +2273,21 @@
 			}
 			srcFilesInfo = append(srcFilesInfo, provider)
 		case libTag:
-			provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
-			classPaths = append(classPaths, provider.HeaderJars...)
+			if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+				classPaths = append(classPaths, provider.HeaderJars...)
+			}
+		case bootClasspathTag:
+			if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+				bootclassPaths = append(bootclassPaths, provider.HeaderJars...)
+			}
 		case staticLibTag:
-			provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
-			staticLibs = append(staticLibs, provider.HeaderJars...)
-		case depApiSrcsTag:
-			provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
-			depApiSrcsStubsJar = provider.HeaderJars[0]
+			if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+				staticLibs = append(staticLibs, provider.HeaderJars...)
+			}
 		case systemModulesTag:
-			module := dep.(SystemModulesProvider)
-			systemModulesPaths = append(systemModulesPaths, module.HeaderJars()...)
+			if sm, ok := android.OtherModuleProvider(ctx, dep, SystemModulesProvider); ok {
+				systemModulesPaths = append(systemModulesPaths, sm.HeaderJars...)
+			}
 		case metalavaCurrentApiTimestampTag:
 			if currentApiTimestampProvider, ok := dep.(currentApiTimestampProvider); ok {
 				al.validationPaths = append(al.validationPaths, currentApiTimestampProvider.CurrentApiTimestamp())
@@ -2287,14 +2317,19 @@
 		ctx.ModuleErrorf("Error: %s has an empty api file.", ctx.ModuleName())
 	}
 
-	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, systemModulesPaths)
+	configFiles := android.PathsForModuleSrc(ctx, al.properties.ConfigFiles)
+
+	combinedPaths := append(([]android.Path)(nil), systemModulesPaths...)
+	combinedPaths = append(combinedPaths, classPaths...)
+	combinedPaths = append(combinedPaths, bootclassPaths...)
+	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, combinedPaths, configFiles)
 
 	al.stubsFlags(ctx, cmd, stubsDir)
 
-	migratingNullability := String(al.properties.Previous_api) != ""
-	if migratingNullability {
-		previousApi := android.PathForModuleSrc(ctx, String(al.properties.Previous_api))
-		cmd.FlagWithInput("--migrate-nullness ", previousApi)
+	previousApi := String(al.properties.Previous_api)
+	if previousApi != "" {
+		previousApiFiles := android.PathsForModuleSrc(ctx, []string{previousApi})
+		cmd.FlagForEachInput("--migrate-nullness ", previousApiFiles)
 	}
 
 	al.addValidation(ctx, cmd, al.validationPaths)
@@ -2305,9 +2340,6 @@
 	al.stubsJarWithoutStaticLibs = android.PathForModuleOut(ctx, "metalava", "stubs.jar")
 	al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName()))
 
-	if depApiSrcsStubsJar != nil {
-		al.extractApiSrcs(ctx, rule, stubsDir, depApiSrcsStubsJar)
-	}
 	rule.Command().
 		BuiltTool("soong_zip").
 		Flag("-write_if_changed").
@@ -2318,19 +2350,18 @@
 
 	rule.Build("metalava", "metalava merged text")
 
-	if depApiSrcsStubsJar == nil {
-		var flags javaBuilderFlags
-		flags.javaVersion = getStubsJavaVersion()
-		flags.javacFlags = strings.Join(al.properties.Javacflags, " ")
-		flags.classpath = classpath(classPaths)
-		flags.bootClasspath = classpath(systemModulesPaths)
-
-		annoSrcJar := android.PathForModuleOut(ctx, ctx.ModuleName(), "anno.srcjar")
-
-		TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{},
-			android.Paths{al.stubsSrcJar}, annoSrcJar, flags, android.Paths{})
+	javacFlags := javaBuilderFlags{
+		javaVersion:   getStubsJavaVersion(),
+		javacFlags:    strings.Join(al.properties.Javacflags, " "),
+		classpath:     classpath(classPaths),
+		bootClasspath: classpath(append(systemModulesPaths, bootclassPaths...)),
 	}
 
+	annoSrcJar := android.PathForModuleOut(ctx, ctx.ModuleName(), "anno.srcjar")
+
+	TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{},
+		android.Paths{al.stubsSrcJar}, annoSrcJar, javacFlags, android.Paths{})
+
 	builder := android.NewRuleBuilder(pctx, ctx)
 	builder.Command().
 		BuiltTool("merge_zips").
@@ -2341,7 +2372,7 @@
 
 	// compile stubs to .dex for hiddenapi processing
 	dexParams := &compileDexParams{
-		flags:         javaBuilderFlags{},
+		flags:         javacFlags,
 		sdkVersion:    al.SdkVersion(ctx),
 		minSdkVersion: al.MinSdkVersion(ctx),
 		classesJar:    al.stubsJar,
@@ -2355,12 +2386,15 @@
 
 	ctx.Phony(ctx.ModuleName(), al.stubsJar)
 
-	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
-		HeaderJars:                     android.PathsIfNonNil(al.stubsJar),
-		ImplementationAndResourcesJars: android.PathsIfNonNil(al.stubsJar),
-		ImplementationJars:             android.PathsIfNonNil(al.stubsJar),
-		AidlIncludeDirs:                android.Paths{},
-		StubsLinkType:                  Stubs,
+	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),
+		ImplementationAndResourcesJars:         android.PathsIfNonNil(al.stubsJar),
+		ImplementationJars:                     android.PathsIfNonNil(al.stubsJar),
+		AidlIncludeDirs:                        android.Paths{},
+		StubsLinkType:                          Stubs,
 		// No aconfig libraries on api libraries
 	})
 }
@@ -2377,19 +2411,56 @@
 	return nil
 }
 
-// java_api_library constitutes the sdk, and does not build against one
+// Most java_api_library constitues the sdk, but there are some java_api_library that
+// does not contribute to the api surface. Such modules are allowed to set sdk_version
+// other than "none"
 func (al *ApiLibrary) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
-	return android.SdkSpecNone
+	return android.SdkSpecFrom(ctx, proptools.String(al.properties.Sdk_version))
 }
 
 // java_api_library is always at "current". Return FutureApiLevel
 func (al *ApiLibrary) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
-	return android.FutureApiLevel
+	return al.SdkVersion(ctx).ApiLevel
+}
+
+func (al *ApiLibrary) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
+	return al.SdkVersion(ctx).ApiLevel
+}
+
+func (al *ApiLibrary) SystemModules() string {
+	return proptools.String(al.properties.System_modules)
+}
+
+func (al *ApiLibrary) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return al.SdkVersion(ctx).ApiLevel
+}
+
+func (al *ApiLibrary) IDEInfo(ctx android.BaseModuleContext, i *android.IdeInfo) {
+	i.Deps = append(i.Deps, al.ideDeps(ctx)...)
+	i.Libs = append(i.Libs, al.properties.Libs.GetOrDefault(ctx, nil)...)
+	i.Static_libs = append(i.Static_libs, al.properties.Static_libs.GetOrDefault(ctx, nil)...)
+	i.SrcJars = append(i.SrcJars, al.stubsSrcJar.String())
+}
+
+// deps of java_api_library for module_bp_java_deps.json
+func (al *ApiLibrary) ideDeps(ctx android.BaseModuleContext) []string {
+	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 {
+		ret = append(ret, proptools.String(al.properties.System_modules))
+	}
+	// Other non java_library dependencies like java_api_contribution are ignored for now.
+	return ret
 }
 
 // implement the following interfaces for hiddenapi processing
 var _ hiddenAPIModule = (*ApiLibrary)(nil)
 var _ UsesLibraryDependency = (*ApiLibrary)(nil)
+var _ android.SdkContext = (*ApiLibrary)(nil)
+
+// implement the following interface for IDE completion.
+var _ android.IDEInfo = (*ApiLibrary)(nil)
 
 //
 // Java prebuilts
@@ -2419,7 +2490,7 @@
 	Libs []string
 
 	// List of static java libs that this module has dependencies to
-	Static_libs []string
+	Static_libs proptools.Configurable[[]string]
 
 	// List of files to remove from the jar file(s)
 	Exclude_files []string
@@ -2560,7 +2631,7 @@
 
 func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
-	ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...)
+	ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs.GetOrDefault(ctx, nil)...)
 
 	if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
 		sdkDeps(ctx, android.SdkContext(j), j.dexer)
@@ -2594,8 +2665,15 @@
 
 	var flags javaBuilderFlags
 
-	j.collectTransitiveHeaderJars(ctx)
+	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]
+
+	j.collectTransitiveHeaderJarsForR8(ctx)
 	var staticJars android.Paths
+	var staticResourceJars android.Paths
 	var staticHeaderJars android.Paths
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -2604,107 +2682,156 @@
 			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)
+				}
 			case staticLibTag:
 				flags.classpath = append(flags.classpath, dep.HeaderJars...)
-				staticJars = append(staticJars, dep.ImplementationAndResourcesJars...)
+				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)
+				}
 			case bootClasspathTag:
 				flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars...)
+				if dep.TransitiveStaticLibsHeaderJars != nil {
+					transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
+				}
 			}
-		} else if dep, ok := module.(SdkLibraryDependency); ok {
+		} else if _, ok := module.(SdkLibraryDependency); ok {
 			switch tag {
 			case libTag, sdkLibTag:
-				flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
+				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)
 			}
 		}
 
 		addCLCFromDep(ctx, module, j.classLoaderContexts)
 	})
 
-	jars := android.PathsForModuleSrc(ctx, j.properties.Jars)
+	localJars := android.PathsForModuleSrc(ctx, j.properties.Jars)
 	jarName := j.Stem() + ".jar"
 
+	// Combine only the local jars together for use in transitive classpaths.
+	// Always pass input jar through TransformJarsToJar to strip module-info.class from prebuilts.
+	localCombinedHeaderJar := android.PathForModuleOut(ctx, "local-combined", jarName)
+	TransformJarsToJar(ctx, localCombinedHeaderJar, "combine local prebuilt implementation jars", localJars, android.OptionalPath{},
+		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)
+
 	// 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.
-	outputFile := android.PathForModuleOut(ctx, "combined", jarName)
-	implementationJars := append(slices.Clone(jars), staticJars...)
-	TransformJarsToJar(ctx, outputFile, "combine prebuilt implementation jars", implementationJars, android.OptionalPath{},
+	var outputFile android.Path
+	combinedImplementationJar := android.PathForModuleOut(ctx, "combined", jarName)
+	var implementationJars android.Paths
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		implementationJars = completeStaticLibsImplementationJars.ToList()
+	} else {
+		implementationJars = append(slices.Clone(localJars), staticJars...)
+	}
+	TransformJarsToJar(ctx, combinedImplementationJar, "combine prebuilt implementation jars", implementationJars, android.OptionalPath{},
 		false, j.properties.Exclude_files, j.properties.Exclude_dirs)
+	outputFile = combinedImplementationJar
 
 	// If no dependencies have separate header jars then there is no need to create a separate
 	// header jar for this module.
 	reuseImplementationJarAsHeaderJar := slices.Equal(staticJars, staticHeaderJars)
 
-	var headerOutputFile android.ModuleOutPath
+	var resourceJarFile android.Path
+	if len(staticResourceJars) > 1 {
+		combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
+		TransformJarsToJar(ctx, combinedJar, "for resources", staticResourceJars, android.OptionalPath{},
+			false, nil, nil)
+		resourceJarFile = combinedJar
+	} else if len(staticResourceJars) == 1 {
+		resourceJarFile = staticResourceJars[0]
+	}
+
+	var headerJar android.Path
 	if reuseImplementationJarAsHeaderJar {
-		headerOutputFile = outputFile
+		headerJar = outputFile
 	} else {
-		headerJars := append(slices.Clone(jars), staticHeaderJars...)
-		headerOutputFile = android.PathForModuleOut(ctx, "turbine-combined", jarName)
+		var headerJars android.Paths
+		if ctx.Config().UseTransitiveJarsInClasspath() {
+			headerJars = completeStaticLibsHeaderJars.ToList()
+		} else {
+			headerJars = append(slices.Clone(localJars), staticHeaderJars...)
+		}
+		headerOutputFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
 		TransformJarsToJar(ctx, headerOutputFile, "combine prebuilt header jars", headerJars, android.OptionalPath{},
 			false, j.properties.Exclude_files, j.properties.Exclude_dirs)
+		headerJar = headerOutputFile
 	}
 
 	if Bool(j.properties.Jetifier) {
-		inputFile := outputFile
-		outputFile = android.PathForModuleOut(ctx, "jetifier", jarName)
-		TransformJetifier(ctx, outputFile, inputFile)
+		jetifierOutputFile := android.PathForModuleOut(ctx, "jetifier", jarName)
+		TransformJetifier(ctx, jetifierOutputFile, outputFile)
+		outputFile = jetifierOutputFile
 
 		if !reuseImplementationJarAsHeaderJar {
-			headerInputFile := headerOutputFile
-			headerOutputFile = android.PathForModuleOut(ctx, "jetifier-headers", jarName)
-			TransformJetifier(ctx, headerOutputFile, headerInputFile)
+			jetifierHeaderJar := android.PathForModuleOut(ctx, "jetifier-headers", jarName)
+			TransformJetifier(ctx, jetifierHeaderJar, headerJar)
+			headerJar = jetifierHeaderJar
 		} else {
-			headerOutputFile = outputFile
+			headerJar = outputFile
 		}
+
+		// 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)
+	}
+
+	implementationJarFile := outputFile
+
+	// merge implementation jar with resources if necessary
+	if resourceJarFile != nil {
+		jars := android.Paths{resourceJarFile, outputFile}
+		combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
+		TransformJarsToJar(ctx, combinedJar, "for resources", jars, android.OptionalPath{},
+			false, nil, nil)
+		outputFile = combinedJar
 	}
 
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource.
 	// Also strip the relative path from the header output file so that the reuseImplementationJarAsHeaderJar check
 	// in a module that depends on this module considers them equal.
-	j.combinedHeaderFile = headerOutputFile.WithoutRel()
+	j.combinedHeaderFile = headerJar.WithoutRel()
 	j.combinedImplementationFile = outputFile.WithoutRel()
 
 	j.maybeInstall(ctx, jarName, outputFile)
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 
+	if ctx.Config().UseTransitiveJarsInClasspath() {
+		ctx.CheckbuildFile(localJars...)
+	} else {
+		ctx.CheckbuildFile(outputFile)
+	}
+
 	if ctx.Device() {
-		// If this is a variant created for a prebuilt_apex then use the dex implementation jar
-		// obtained from the associated deapexer module.
+		// Shared libraries deapexed from prebuilt apexes are no longer supported.
+		// Set the dexJarBuildPath to a fake path.
+		// This allows soong analysis pass, but will be an error during ninja execution if there are
+		// any rdeps.
 		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 		if ai.ForPrebuiltApex {
-			// Get the path of the dex implementation jar from the `deapexer` module.
-			di, err := android.FindDeapexerProviderForModule(ctx)
-			if err != nil {
-				// An error was found, possibly due to multiple apexes in the tree that export this library
-				// Defer the error till a client tries to call DexJarBuildPath
-				j.dexJarFileErr = err
-				j.initHiddenAPIError(err)
-				return
-			}
-			dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(j.BaseModuleName())
-			if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
-				dexJarFile := makeDexJarPathFromPath(dexOutputPath)
-				j.dexJarFile = dexJarFile
-				installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, ApexRootRelativePathToJavaLib(j.BaseModuleName()))
-				j.dexJarInstallFile = installPath
-
-				j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), installPath)
-				setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
-				j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
-
-				if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil {
-					j.dexpreopter.inputProfilePathOnHost = profilePath
-				}
-
-				// Initialize the hiddenapi structure.
-				j.initHiddenAPI(ctx, dexJarFile, outputFile, j.dexProperties.Uncompress_dex)
-			} else {
-				// This should never happen as a variant for a prebuilt_apex is only created if the
-				// prebuilt_apex has been configured to export the java library dex file.
-				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName())
-			}
+			j.dexJarFile = makeDexJarPathFromPath(android.PathForModuleInstall(ctx, "intentionally_no_longer_supported"))
+			j.initHiddenAPI(ctx, j.dexJarFile, outputFile, j.dexProperties.Uncompress_dex)
 		} else if Bool(j.dexProperties.Compile_dex) {
 			sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
 			if sdkDep.invalidVersion {
@@ -2722,7 +2849,7 @@
 			setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
 			j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 
-			var dexOutputFile android.OutputPath
+			var dexOutputFile android.Path
 			dexParams := &compileDexParams{
 				flags:         flags,
 				sdkVersion:    j.SdkVersion(ctx),
@@ -2735,6 +2862,7 @@
 			if ctx.Failed() {
 				return
 			}
+			ctx.CheckbuildFile(dexOutputFile)
 
 			// Initialize the hiddenapi structure.
 			j.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), outputFile, j.dexProperties.Uncompress_dex)
@@ -2747,16 +2875,24 @@
 		}
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
-		HeaderJars:                     android.PathsIfNonNil(j.combinedHeaderFile),
-		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
-		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
-		ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedImplementationFile),
-		ImplementationJars:             android.PathsIfNonNil(j.combinedImplementationFile),
-		AidlIncludeDirs:                j.exportAidlIncludeDirs,
-		StubsLinkType:                  j.stubsLinkType,
+	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+		HeaderJars:                             android.PathsIfNonNil(j.combinedHeaderFile),
+		LocalHeaderJars:                        android.PathsIfNonNil(j.combinedHeaderFile),
+		TransitiveLibsHeaderJarsForR8:          j.transitiveLibsHeaderJarsForR8,
+		TransitiveStaticLibsHeaderJarsForR8:    j.transitiveStaticLibsHeaderJarsForR8,
+		TransitiveStaticLibsHeaderJars:         completeStaticLibsHeaderJars,
+		TransitiveStaticLibsImplementationJars: completeStaticLibsImplementationJars,
+		TransitiveStaticLibsResourceJars:       completeStaticLibsResourceJars,
+		ImplementationAndResourcesJars:         android.PathsIfNonNil(j.combinedImplementationFile),
+		ImplementationJars:                     android.PathsIfNonNil(implementationJarFile.WithoutRel()),
+		ResourceJars:                           android.PathsIfNonNil(resourceJarFile),
+		AidlIncludeDirs:                        j.exportAidlIncludeDirs,
+		StubsLinkType:                          j.stubsLinkType,
 		// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 	})
+
+	ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, "")
+	ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, ".jar")
 }
 
 func (j *Import) maybeInstall(ctx android.ModuleContext, jarName string, outputFile android.Path) {
@@ -2777,17 +2913,6 @@
 	ctx.InstallFile(installDir, jarName, outputFile)
 }
 
-func (j *Import) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "", ".jar":
-		return android.Paths{j.combinedImplementationFile}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
-var _ android.OutputFileProducer = (*Import)(nil)
-
 func (j *Import) HeaderJars() android.Paths {
 	return android.PathsIfNonNil(j.combinedHeaderFile)
 }
@@ -2874,8 +2999,8 @@
 
 // Collect information for opening IDE project files in java/jdeps.go.
 
-func (j *Import) IDEInfo(dpInfo *android.IdeInfo) {
-	dpInfo.Jars = append(dpInfo.Jars, j.PrebuiltSrcs()...)
+func (j *Import) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
+	dpInfo.Jars = append(dpInfo.Jars, j.combinedHeaderFile.String())
 }
 
 func (j *Import) IDECustomizedModuleName() string {
@@ -3163,15 +3288,20 @@
 
 func (ks *kytheExtractJavaSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var xrefTargets android.Paths
+	var xrefKotlinTargets android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
 		if javaModule, ok := module.(xref); ok {
 			xrefTargets = append(xrefTargets, javaModule.XrefJavaFiles()...)
+			xrefKotlinTargets = append(xrefKotlinTargets, javaModule.XrefKotlinFiles()...)
 		}
 	})
 	// TODO(asmundak): perhaps emit a rule to output a warning if there were no xrefTargets
 	if len(xrefTargets) > 0 {
 		ctx.Phony("xref_java", xrefTargets...)
 	}
+	if len(xrefKotlinTargets) > 0 {
+		ctx.Phony("xref_kotlin", xrefKotlinTargets...)
+	}
 }
 
 var Bool = proptools.Bool
@@ -3194,6 +3324,10 @@
 	if lib, ok := depModule.(SdkLibraryDependency); 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 {
+		if depModule.Name() == proptools.String(lib.OptionalSdkLibraryImplementation())+".impl" {
+			sdkLib = lib.OptionalSdkLibraryImplementation()
+		}
 	} else if ulib, ok := depModule.(ProvidesUsesLib); ok {
 		// A non-SDK library disguised as an SDK library by the means of `provides_uses_lib`
 		// property. This should be handled in the same way as a shared SDK library.
diff --git a/java/java_test.go b/java/java_test.go
index 2f27932..e0fd0f2 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -20,6 +20,7 @@
 	"path/filepath"
 	"reflect"
 	"runtime"
+	"slices"
 	"strconv"
 	"strings"
 	"testing"
@@ -211,7 +212,7 @@
 }
 
 func TestSimple(t *testing.T) {
-	ctx, _ := testJava(t, `
+	bp := `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
@@ -222,31 +223,157 @@
 		java_library {
 			name: "bar",
 			srcs: ["b.java"],
+			static_libs: ["quz"],
 		}
 
 		java_library {
 			name: "baz",
 			srcs: ["c.java"],
+			static_libs: ["quz"],
 		}
-	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
+		java_library {
+			name: "quz",
+			srcs: ["d.java"],
+		}`
 
-	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
-		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
+	frameworkTurbineCombinedJars := []string{
+		"out/soong/.intermediates/default/java/ext/android_common/turbine-combined/ext.jar",
+		"out/soong/.intermediates/default/java/framework/android_common/turbine-combined/framework.jar",
 	}
 
-	baz := ctx.ModuleForTests("baz", "android_common").Rule("javac").Output.String()
-	barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
-	bazTurbine := filepath.Join("out", "soong", ".intermediates", "baz", "android_common", "turbine-combined", "baz.jar")
+	frameworkTurbineJars := []string{
+		"out/soong/.intermediates/default/java/ext/android_common/turbine/ext.jar",
+		"out/soong/.intermediates/default/java/framework/android_common/turbine/framework.jar",
+	}
 
-	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barTurbine)
+	testCases := []struct {
+		name string
 
-	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazTurbine)
+		preparer android.FixturePreparer
 
-	if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
-		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
+		fooJavacInputs          []string
+		fooJavacClasspath       []string
+		fooCombinedInputs       []string
+		fooHeaderCombinedInputs []string
+
+		barJavacInputs          []string
+		barJavacClasspath       []string
+		barCombinedInputs       []string
+		barHeaderCombinedInputs []string
+	}{
+		{
+			name:           "normal",
+			preparer:       android.NullFixturePreparer,
+			fooJavacInputs: []string{"a.java"},
+			fooJavacClasspath: slices.Concat(
+				frameworkTurbineCombinedJars,
+				[]string{
+					"out/soong/.intermediates/bar/android_common/turbine-combined/bar.jar",
+					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
+				},
+			),
+			fooCombinedInputs: []string{
+				"out/soong/.intermediates/foo/android_common/javac/foo.jar",
+				"out/soong/.intermediates/baz/android_common/combined/baz.jar",
+			},
+
+			fooHeaderCombinedInputs: []string{
+				"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
+				"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
+			},
+
+			barJavacInputs: []string{"b.java"},
+			barJavacClasspath: slices.Concat(
+				frameworkTurbineCombinedJars,
+				[]string{
+					"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
+				},
+			),
+			barCombinedInputs: []string{
+				"out/soong/.intermediates/bar/android_common/javac/bar.jar",
+				"out/soong/.intermediates/quz/android_common/javac/quz.jar",
+			},
+			barHeaderCombinedInputs: []string{
+				"out/soong/.intermediates/bar/android_common/turbine/bar.jar",
+				"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
+			},
+		},
+		{
+			name:           "transitive classpath",
+			preparer:       PrepareForTestWithTransitiveClasspathEnabled,
+			fooJavacInputs: []string{"a.java"},
+			fooJavacClasspath: slices.Concat(
+				frameworkTurbineJars,
+				[]string{
+					"out/soong/.intermediates/bar/android_common/turbine/bar.jar",
+					"out/soong/.intermediates/quz/android_common/turbine/quz.jar",
+					"out/soong/.intermediates/baz/android_common/turbine/baz.jar",
+				},
+			),
+			fooCombinedInputs: []string{
+				"out/soong/.intermediates/foo/android_common/javac/foo.jar",
+				"out/soong/.intermediates/baz/android_common/javac/baz.jar",
+				"out/soong/.intermediates/quz/android_common/javac/quz.jar",
+			},
+
+			fooHeaderCombinedInputs: []string{
+				"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
+				"out/soong/.intermediates/baz/android_common/turbine/baz.jar",
+				"out/soong/.intermediates/quz/android_common/turbine/quz.jar",
+			},
+
+			barJavacInputs: []string{"b.java"},
+			barJavacClasspath: slices.Concat(
+				frameworkTurbineJars,
+				[]string{"out/soong/.intermediates/quz/android_common/turbine/quz.jar"},
+			),
+			barCombinedInputs: []string{
+				"out/soong/.intermediates/bar/android_common/javac/bar.jar",
+				"out/soong/.intermediates/quz/android_common/javac/quz.jar",
+			},
+			barHeaderCombinedInputs: []string{
+				"out/soong/.intermediates/bar/android_common/turbine/bar.jar",
+				"out/soong/.intermediates/quz/android_common/turbine/quz.jar",
+			},
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				tt.preparer,
+			).RunTestWithBp(t, bp)
+			foo := result.ModuleForTests("foo", "android_common")
+
+			fooJavac := foo.Rule("javac")
+			android.AssertPathsRelativeToTopEquals(t, "foo javac inputs", tt.fooJavacInputs, fooJavac.Inputs)
+
+			fooJavacClasspath := fooJavac.Args["classpath"]
+			android.AssertStringPathsRelativeToTopEquals(t, "foo javac classpath", result.Config, tt.fooJavacClasspath,
+				strings.Split(strings.TrimPrefix(fooJavacClasspath, "-classpath "), ":"))
+
+			fooCombinedJar := foo.Output("combined/foo.jar")
+			android.AssertPathsRelativeToTopEquals(t, "foo combined inputs", tt.fooCombinedInputs, fooCombinedJar.Inputs)
+
+			fooCombinedHeaderJar := foo.Output("turbine-combined/foo.jar")
+			android.AssertPathsRelativeToTopEquals(t, "foo header combined inputs", tt.fooHeaderCombinedInputs, fooCombinedHeaderJar.Inputs)
+
+			bar := result.ModuleForTests("bar", "android_common")
+			barJavac := bar.Rule("javac")
+			android.AssertPathsRelativeToTopEquals(t, "bar javac inputs", tt.barJavacInputs, barJavac.Inputs)
+
+			barJavacClasspath := barJavac.Args["classpath"]
+			android.AssertStringPathsRelativeToTopEquals(t, "bar javac classpath", result.Config, tt.barJavacClasspath,
+				strings.Split(strings.TrimPrefix(barJavacClasspath, "-classpath "), ":"))
+
+			barCombinedJar := bar.Output("combined/bar.jar")
+			android.AssertPathsRelativeToTopEquals(t, "bar combined inputs", tt.barCombinedInputs, barCombinedJar.Inputs)
+
+			barCombinedHeaderJar := bar.Output("turbine-combined/bar.jar")
+			android.AssertPathsRelativeToTopEquals(t, "bar header combined inputs", tt.barHeaderCombinedInputs, barCombinedHeaderJar.Inputs)
+		})
 	}
 }
 
@@ -543,7 +670,7 @@
 		java_library {
 			name: "foo",
 			srcs: ["a.java", ":stubs-source"],
-			libs: ["bar", "sdklib"],
+			libs: ["bar", "sdklib.stubs"],
 			static_libs: ["baz"],
 		}
 
@@ -590,7 +717,7 @@
 	barModule := ctx.ModuleForTests("bar", "android_common")
 	barJar := barModule.Output("combined/bar.jar").Output
 	bazModule := ctx.ModuleForTests("baz", "android_common")
-	bazJar := bazModule.Rule("combineJar").Output
+	bazJar := bazModule.Output("combined/baz.jar").Output
 	sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").
 		Output("combined/sdklib.stubs.jar").Output
 
@@ -1100,7 +1227,7 @@
 	source := ctx.ModuleForTests("source_library", "android_common")
 	sourceJar := source.Output("javac/source_library.jar")
 	sourceHeaderJar := source.Output("turbine-combined/source_library.jar")
-	sourceJavaInfo, _ := android.SingletonModuleProvider(ctx, source.Module(), JavaInfoProvider)
+	sourceJavaInfo, _ := android.OtherModuleProvider(ctx, source.Module(), JavaInfoProvider)
 
 	// The source library produces separate implementation and header jars
 	android.AssertPathsRelativeToTopEquals(t, "source library implementation jar",
@@ -1110,7 +1237,7 @@
 
 	importWithNoDeps := ctx.ModuleForTests("import_with_no_deps", "android_common")
 	importWithNoDepsJar := importWithNoDeps.Output("combined/import_with_no_deps.jar")
-	importWithNoDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithNoDeps.Module(), JavaInfoProvider)
+	importWithNoDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithNoDeps.Module(), JavaInfoProvider)
 
 	// An import with no deps produces a single jar used as both the header and implementation jar.
 	android.AssertPathsRelativeToTopEquals(t, "import with no deps implementation jar",
@@ -1123,7 +1250,7 @@
 	importWithSourceDeps := ctx.ModuleForTests("import_with_source_deps", "android_common")
 	importWithSourceDepsJar := importWithSourceDeps.Output("combined/import_with_source_deps.jar")
 	importWithSourceDepsHeaderJar := importWithSourceDeps.Output("turbine-combined/import_with_source_deps.jar")
-	importWithSourceDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithSourceDeps.Module(), JavaInfoProvider)
+	importWithSourceDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithSourceDeps.Module(), JavaInfoProvider)
 
 	// An import with source deps produces separate header and implementation jars.
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps implementation jar",
@@ -1137,7 +1264,7 @@
 
 	importWithImportDeps := ctx.ModuleForTests("import_with_import_deps", "android_common")
 	importWithImportDepsJar := importWithImportDeps.Output("combined/import_with_import_deps.jar")
-	importWithImportDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithImportDeps.Module(), JavaInfoProvider)
+	importWithImportDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithImportDeps.Module(), JavaInfoProvider)
 
 	// An import with only import deps produces a single jar used as both the header and implementation jar.
 	android.AssertPathsRelativeToTopEquals(t, "import with import deps implementation jar",
@@ -1342,12 +1469,12 @@
 		}
 		`)
 
-	checkBootClasspathForSystemModule(t, ctx, "lib-with-source-system-modules", "/source-jar.jar")
+	checkBootClasspathForLibWithSystemModule(t, ctx, "lib-with-source-system-modules", "/source-jar.jar")
 
-	checkBootClasspathForSystemModule(t, ctx, "lib-with-prebuilt-system-modules", "/prebuilt-jar.jar")
+	checkBootClasspathForLibWithSystemModule(t, ctx, "lib-with-prebuilt-system-modules", "/prebuilt-jar.jar")
 }
 
-func checkBootClasspathForSystemModule(t *testing.T, ctx *android.TestContext, moduleName string, expectedSuffix string) {
+func checkBootClasspathForLibWithSystemModule(t *testing.T, ctx *android.TestContext, moduleName string, expectedSuffix string) {
 	javacRule := ctx.ModuleForTests(moduleName, "android_common").Rule("javac")
 	bootClasspath := javacRule.Args["bootClasspath"]
 	if strings.HasPrefix(bootClasspath, "--system ") && strings.HasSuffix(bootClasspath, expectedSuffix) {
@@ -2256,61 +2383,6 @@
 	}
 }
 
-func TestJavaApiLibraryFullApiSurfaceStub(t *testing.T) {
-	provider_bp_a := `
-	java_api_contribution {
-		name: "foo1",
-		api_file: "current.txt",
-		api_surface: "public",
-	}
-	`
-	provider_bp_b := `
-	java_api_contribution {
-		name: "foo2",
-		api_file: "current.txt",
-		api_surface: "public",
-	}
-	`
-	lib_bp_a := `
-	java_api_library {
-		name: "lib1",
-		api_surface: "public",
-		api_contributions: ["foo1", "foo2"],
-		stubs_type: "everything",
-	}
-	`
-
-	ctx := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		android.FixtureMergeMockFs(
-			map[string][]byte{
-				"a/Android.bp": []byte(provider_bp_a),
-				"b/Android.bp": []byte(provider_bp_b),
-				"c/Android.bp": []byte(lib_bp_a),
-			},
-		),
-		android.FixtureMergeEnv(
-			map[string]string{
-				"DISABLE_STUB_VALIDATION": "true",
-			},
-		),
-	).RunTestWithBp(t, `
-		java_api_library {
-			name: "bar1",
-			api_surface: "public",
-			api_contributions: ["foo1"],
-			full_api_surface_stub: "lib1",
-			stubs_type: "everything",
-		}
-	`)
-
-	m := ctx.ModuleForTests("bar1", "android_common")
-	manifest := m.Output("metalava.sbox.textproto")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
-	manifestCommand := sboxProto.Commands[0].GetCommand()
-	android.AssertStringDoesContain(t, "Command expected to contain full_api_surface_stub output jar", manifestCommand, "lib1.jar")
-}
-
 func TestTransitiveSrcFiles(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_library {
@@ -2329,7 +2401,7 @@
 		}
 	`)
 	c := ctx.ModuleForTests("c", "android_common").Module()
-	javaInfo, _ := android.SingletonModuleProvider(ctx, c, JavaInfoProvider)
+	javaInfo, _ := android.OtherModuleProvider(ctx, c, JavaInfoProvider)
 	transitiveSrcFiles := android.Paths(javaInfo.TransitiveSrcFiles.ToList())
 	android.AssertArrayString(t, "unexpected jar deps", []string{"b.java", "c.java"}, transitiveSrcFiles.Strings())
 }
@@ -2511,9 +2583,6 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 		android.FixtureMergeMockFs(
 			map[string][]byte{
 				"A.java": nil,
@@ -2534,12 +2603,8 @@
 			system_modules: "baz",
 		}
 	`)
-	m := result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common")
-	manifest := m.Output("metalava.sbox.textproto")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest)
-	manifestCommand := sboxProto.Commands[0].GetCommand()
-	classPathFlag := "--classpath __SBOX_SANDBOX_DIR__/out/soong/.intermediates/bar/android_common/turbine-combined/bar.jar"
-	android.AssertStringDoesContain(t, "command expected to contain classpath flag", manifestCommand, classPathFlag)
+
+	checkBootClasspathForLibWithSystemModule(t, result.TestContext, apiScopePublic.apiLibraryModuleName("foo"), "/bar.jar")
 }
 
 func TestApiLibraryDroidstubsDependency(t *testing.T) {
@@ -2547,9 +2612,6 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 		android.FixtureMergeMockFs(
 			map[string][]byte{
 				"A.java": nil,
@@ -2598,7 +2660,6 @@
 		PrepareForTestWithJacocoInstrumentation,
 		FixtureWithLastReleaseApis("foo"),
 		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
 			config.SetBuildFromTextStub(true)
 		}),
 		android.FixtureModifyEnv(func(env map[string]string) {
@@ -2613,7 +2674,7 @@
 	android.AssertBoolEquals(t, "stub module expected to depend on from-source stub",
 		true, CheckModuleHasDependency(t, result.TestContext,
 			apiScopePublic.stubsLibraryModuleName("foo"), "android_common",
-			apiScopePublic.sourceStubLibraryModuleName("foo")))
+			apiScopePublic.sourceStubsLibraryModuleName("foo")))
 
 	android.AssertBoolEquals(t, "stub module expected to not depend on from-text stub",
 		false, CheckModuleHasDependency(t, result.TestContext,
@@ -2700,11 +2761,7 @@
 	for _, tc := range testCases {
 		ctx := android.GroupFixturePreparers(
 			prepareForJavaTest,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
 		// check that rdep gets the correct variation of dep
@@ -2774,11 +2831,7 @@
 		ctx := android.GroupFixturePreparers(
 			prepareForJavaTest,
 			PrepareForTestWithPlatformCompatConfig,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
 		mergedGlobalConfig := ctx.SingletonForTests("platform_compat_config_singleton").Output("compat_config/merged_compat_config.xml")
@@ -2993,23 +3046,23 @@
 	bar := result.ModuleForTests("bar", "android_common")
 	baz := result.ModuleForTests("baz", "android_common")
 
-	fooOutputPath := android.OutputFileForModule(android.PathContext(nil), foo.Module(), "")
-	barOutputPath := android.OutputFileForModule(android.PathContext(nil), bar.Module(), "")
-	bazOutputPath := android.OutputFileForModule(android.PathContext(nil), baz.Module(), "")
+	fooOutputPaths := foo.OutputFiles(result.TestContext, t, "")
+	barOutputPaths := bar.OutputFiles(result.TestContext, t, "")
+	bazOutputPaths := baz.OutputFiles(result.TestContext, t, "")
 
-	android.AssertPathRelativeToTopEquals(t, "foo output path",
-		"out/soong/.intermediates/foo/android_common/javac/foo.jar", fooOutputPath)
-	android.AssertPathRelativeToTopEquals(t, "bar output path",
-		"out/soong/.intermediates/bar/android_common/combined/bar.jar", barOutputPath)
-	android.AssertPathRelativeToTopEquals(t, "baz output path",
-		"out/soong/.intermediates/baz/android_common/combined/baz.jar", bazOutputPath)
+	android.AssertPathsRelativeToTopEquals(t, "foo output path",
+		[]string{"out/soong/.intermediates/foo/android_common/javac/foo.jar"}, fooOutputPaths)
+	android.AssertPathsRelativeToTopEquals(t, "bar output path",
+		[]string{"out/soong/.intermediates/bar/android_common/combined/bar.jar"}, barOutputPaths)
+	android.AssertPathsRelativeToTopEquals(t, "baz output path",
+		[]string{"out/soong/.intermediates/baz/android_common/combined/baz.jar"}, bazOutputPaths)
 
 	android.AssertStringEquals(t, "foo relative output path",
-		"foo.jar", fooOutputPath.Rel())
+		"foo.jar", fooOutputPaths[0].Rel())
 	android.AssertStringEquals(t, "bar relative output path",
-		"bar.jar", barOutputPath.Rel())
+		"bar.jar", barOutputPaths[0].Rel())
 	android.AssertStringEquals(t, "baz relative output path",
-		"baz.jar", bazOutputPath.Rel())
+		"baz.jar", bazOutputPaths[0].Rel())
 }
 
 func assertTestOnlyAndTopLevel(t *testing.T, ctx *android.TestResult, expectedTestOnly []string, expectedTopLevel []string) {
diff --git a/java/jdeps.go b/java/jdeps.go
index 3400263..c2ce503 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -57,27 +57,19 @@
 			return
 		}
 
-		ideInfoProvider, ok := module.(android.IDEInfo)
+		ideInfoProvider, ok := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 		if !ok {
 			return
 		}
-		name := ideInfoProvider.BaseModuleName()
+		name := ideInfoProvider.BaseModuleName
 		ideModuleNameProvider, ok := module.(android.IDECustomizedModuleName)
 		if ok {
 			name = ideModuleNameProvider.IDECustomizedModuleName()
 		}
 
 		dpInfo := moduleInfos[name]
-		ideInfoProvider.IDEInfo(&dpInfo)
-		dpInfo.Deps = android.FirstUniqueStrings(dpInfo.Deps)
-		dpInfo.Srcs = android.FirstUniqueStrings(dpInfo.Srcs)
-		dpInfo.Aidl_include_dirs = android.FirstUniqueStrings(dpInfo.Aidl_include_dirs)
-		dpInfo.Jarjar_rules = android.FirstUniqueStrings(dpInfo.Jarjar_rules)
-		dpInfo.Jars = android.FirstUniqueStrings(dpInfo.Jars)
-		dpInfo.SrcJars = android.FirstUniqueStrings(dpInfo.SrcJars)
+		dpInfo = dpInfo.Merge(ideInfoProvider)
 		dpInfo.Paths = []string{ctx.ModuleDir(module)}
-		dpInfo.Static_libs = android.FirstUniqueStrings(dpInfo.Static_libs)
-		dpInfo.Libs = android.FirstUniqueStrings(dpInfo.Libs)
 		moduleInfos[name] = dpInfo
 
 		mkProvider, ok := module.(android.AndroidMkDataProvider)
@@ -89,7 +81,7 @@
 			dpInfo.Classes = append(dpInfo.Classes, data.Class)
 		}
 
-		if dep, ok := android.SingletonModuleProvider(ctx, module, JavaInfoProvider); ok {
+		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			dpInfo.Installed_paths = append(dpInfo.Installed_paths, dep.ImplementationJars.Strings()...)
 		}
 		dpInfo.Classes = android.FirstUniqueStrings(dpInfo.Classes)
diff --git a/java/jdeps_test.go b/java/jdeps_test.go
index 874d1d7..d282f19 100644
--- a/java/jdeps_test.go
+++ b/java/jdeps_test.go
@@ -22,66 +22,115 @@
 )
 
 func TestCollectJavaLibraryPropertiesAddLibsDeps(t *testing.T) {
-	expected := []string{"Foo", "Bar"}
-	module := LibraryFactory().(*Library)
-	module.properties.Libs = append(module.properties.Libs, expected...)
-	dpInfo := &android.IdeInfo{}
+	ctx, _ := testJava(t,
+		`
+		java_library {name: "Foo"}
+		java_library {name: "Bar"}
+		java_library {
+			name: "javalib",
+			libs: ["Foo", "Bar"],
+		}
+	`)
+	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
-	module.IDEInfo(dpInfo)
-
-	if !reflect.DeepEqual(dpInfo.Deps, expected) {
-		t.Errorf("Library.IDEInfo() Deps = %v, want %v", dpInfo.Deps, expected)
+	for _, expected := range []string{"Foo", "Bar"} {
+		if !android.InList(expected, dpInfo.Deps) {
+			t.Errorf("Library.IDEInfo() Deps = %v, %v not found", dpInfo.Deps, expected)
+		}
 	}
 }
 
 func TestCollectJavaLibraryPropertiesAddStaticLibsDeps(t *testing.T) {
-	expected := []string{"Foo", "Bar"}
-	module := LibraryFactory().(*Library)
-	module.properties.Static_libs = append(module.properties.Static_libs, expected...)
-	dpInfo := &android.IdeInfo{}
+	ctx, _ := testJava(t,
+		`
+		java_library {name: "Foo"}
+		java_library {name: "Bar"}
+		java_library {
+			name: "javalib",
+			static_libs: ["Foo", "Bar"],
+		}
+	`)
+	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
-	module.IDEInfo(dpInfo)
-
-	if !reflect.DeepEqual(dpInfo.Deps, expected) {
-		t.Errorf("Library.IDEInfo() Deps = %v, want %v", dpInfo.Deps, expected)
+	for _, expected := range []string{"Foo", "Bar"} {
+		if !android.InList(expected, dpInfo.Deps) {
+			t.Errorf("Library.IDEInfo() Deps = %v, %v not found", dpInfo.Deps, expected)
+		}
 	}
 }
 
 func TestCollectJavaLibraryPropertiesAddScrs(t *testing.T) {
-	expected := []string{"Foo", "Bar"}
-	module := LibraryFactory().(*Library)
-	module.expandIDEInfoCompiledSrcs = append(module.expandIDEInfoCompiledSrcs, expected...)
-	dpInfo := &android.IdeInfo{}
+	ctx, _ := testJava(t,
+		`
+		java_library {
+			name: "javalib",
+			srcs: ["Foo.java", "Bar.java"],
+		}
+	`)
+	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
-	module.IDEInfo(dpInfo)
-
+	expected := []string{"Foo.java", "Bar.java"}
 	if !reflect.DeepEqual(dpInfo.Srcs, expected) {
 		t.Errorf("Library.IDEInfo() Srcs = %v, want %v", dpInfo.Srcs, expected)
 	}
 }
 
 func TestCollectJavaLibraryPropertiesAddAidlIncludeDirs(t *testing.T) {
+	ctx, _ := testJava(t,
+		`
+		java_library {
+			name: "javalib",
+			aidl: {
+				include_dirs: ["Foo", "Bar"],
+			},
+		}
+	`)
+	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
+
 	expected := []string{"Foo", "Bar"}
-	module := LibraryFactory().(*Library)
-	module.deviceProperties.Aidl.Include_dirs = append(module.deviceProperties.Aidl.Include_dirs, expected...)
-	dpInfo := &android.IdeInfo{}
-
-	module.IDEInfo(dpInfo)
-
 	if !reflect.DeepEqual(dpInfo.Aidl_include_dirs, expected) {
 		t.Errorf("Library.IDEInfo() Aidl_include_dirs = %v, want %v", dpInfo.Aidl_include_dirs, expected)
 	}
 }
 
-func TestCollectJavaLibraryPropertiesAddJarjarRules(t *testing.T) {
-	expected := "Jarjar_rules.txt"
-	module := LibraryFactory().(*Library)
-	module.expandJarjarRules = android.PathForTesting(expected)
-	dpInfo := &android.IdeInfo{}
+func TestCollectJavaLibraryWithJarJarRules(t *testing.T) {
+	ctx, _ := testJava(t,
+		`
+		java_library {
+			name: "javalib",
+			srcs: ["foo.java"],
+			jarjar_rules: "jarjar_rules.txt",
+		}
+	`)
+	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
-	module.IDEInfo(dpInfo)
-
-	if dpInfo.Jarjar_rules[0] != expected {
-		t.Errorf("Library.IDEInfo() Jarjar_rules = %v, want %v", dpInfo.Jarjar_rules[0], expected)
+	android.AssertBoolEquals(t, "IdeInfo.Srcs of repackaged library should be empty", true, len(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)
 	}
 }
+
+func TestCollectJavaLibraryLinkingAgainstVersionedSdk(t *testing.T) {
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		FixtureWithPrebuiltApis(map[string][]string{
+			"29": {},
+		})).RunTestWithBp(t,
+		`
+		java_library {
+			name: "javalib",
+			srcs: ["foo.java"],
+			sdk_version: "29",
+		}
+	`)
+	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
+
+	android.AssertStringListContains(t, "IdeInfo.Deps should contain versioned sdk module", dpInfo.Deps, "sdk_public_29_android")
+}
diff --git a/java/kotlin.go b/java/kotlin.go
index aa2db0e..f42d163 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -64,6 +64,28 @@
 	"kotlincFlags", "classpath", "srcJars", "commonSrcFilesArg", "srcJarDir", "classesDir",
 	"headerClassesDir", "headerJar", "kotlinJvmTarget", "kotlinBuildFile", "emptyDir", "name")
 
+var kotlinKytheExtract = pctx.AndroidStaticRule("kotlinKythe",
+	blueprint.RuleParams{
+		Command: `rm -rf "$srcJarDir" && mkdir -p "$srcJarDir" && ` +
+			`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" -f "*.kt" $srcJars && ` +
+			`${config.KotlinKytheExtractor} -corpus ${kytheCorpus} --srcs @$out.rsp --srcs @"$srcJarDir/list" $commonSrcFilesList --cp @$classpath -o $out --kotlin_out $outJar ` +
+			// wrap the additional kotlin args.
+			// Skip Xbuild file, pass the cp explicitly.
+			// Skip header jars, those should not have an effect on kythe results.
+			` --args '${config.KotlincGlobalFlags} ` +
+			` ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} ` +
+			` $kotlincFlags -jvm-target $kotlinJvmTarget ` +
+			`${config.KotlincKytheGlobalFlags}'`,
+		CommandDeps: []string{
+			"${config.KotlinKytheExtractor}",
+			"${config.ZipSyncCmd}",
+		},
+		Rspfile:        "$out.rsp",
+		RspfileContent: "$in",
+	},
+	"classpath", "kotlincFlags", "commonSrcFilesList", "kotlinJvmTarget", "outJar", "srcJars", "srcJarDir",
+)
+
 func kotlinCommonSrcsList(ctx android.ModuleContext, commonSrcFiles android.Paths) android.OptionalPath {
 	if len(commonSrcFiles) > 0 {
 		// The list of common_srcs may be too long to put on the command line, but
@@ -81,7 +103,7 @@
 }
 
 // kotlinCompile takes .java and .kt sources and srcJars, and compiles the .kt sources into a classes jar in outputFile.
-func kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile android.WritablePath,
+func (j *Module) kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile android.WritablePath,
 	srcFiles, commonSrcFiles, srcJars android.Paths,
 	flags javaBuilderFlags) {
 
@@ -101,6 +123,10 @@
 		commonSrcFilesArg = "--common_srcs " + commonSrcsList.String()
 	}
 
+	classpathRspFile := android.PathForModuleOut(ctx, "kotlinc", "classpath.rsp")
+	android.WriteFileRule(ctx, classpathRspFile, strings.Join(flags.kotlincClasspath.Strings(), " "))
+	deps = append(deps, classpathRspFile)
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:           kotlinc,
 		Description:    "kotlinc",
@@ -109,7 +135,7 @@
 		Inputs:         srcFiles,
 		Implicits:      deps,
 		Args: map[string]string{
-			"classpath":         flags.kotlincClasspath.FormJavaClassPath(""),
+			"classpath":         classpathRspFile.String(),
 			"kotlincFlags":      flags.kotlincFlags,
 			"commonSrcFilesArg": commonSrcFilesArg,
 			"srcJars":           strings.Join(srcJars.Strings(), " "),
@@ -123,6 +149,31 @@
 			"name":              kotlinName,
 		},
 	})
+
+	// Emit kythe xref rule
+	if (ctx.Config().EmitXrefRules()) && ctx.Module() == ctx.PrimaryModule() {
+		extractionFile := outputFile.ReplaceExtension(ctx, "kzip")
+		args := map[string]string{
+			"classpath":       classpathRspFile.String(),
+			"kotlincFlags":    flags.kotlincFlags,
+			"kotlinJvmTarget": flags.javaVersion.StringForKotlinc(),
+			"outJar":          outputFile.String(),
+			"srcJars":         strings.Join(srcJars.Strings(), " "),
+			"srcJarDir":       android.PathForModuleOut(ctx, "kotlinc", "srcJars.xref").String(),
+		}
+		if commonSrcsList.Valid() {
+			args["commonSrcFilesList"] = "--common_srcs @" + commonSrcsList.String()
+		}
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        kotlinKytheExtract,
+			Description: "kotlinKythe",
+			Output:      extractionFile,
+			Inputs:      srcFiles,
+			Implicits:   deps,
+			Args:        args,
+		})
+		j.kytheKotlinFiles = append(j.kytheKotlinFiles, extractionFile)
+	}
 }
 
 var kaptStubs = pctx.AndroidRemoteStaticRule("kaptStubs", android.RemoteRuleSupports{Goma: true},
@@ -205,6 +256,10 @@
 	kotlinName := filepath.Join(ctx.ModuleDir(), ctx.ModuleSubDir(), ctx.ModuleName())
 	kotlinName = strings.ReplaceAll(kotlinName, "/", "__")
 
+	classpathRspFile := android.PathForModuleOut(ctx, "kapt", "classpath.rsp")
+	android.WriteFileRule(ctx, classpathRspFile, strings.Join(flags.kotlincClasspath.Strings(), "\n"))
+	deps = append(deps, classpathRspFile)
+
 	// First run kapt to generate .java stubs from .kt files
 	kaptStubsJar := android.PathForModuleOut(ctx, "kapt", "stubs.jar")
 	ctx.Build(pctx, android.BuildParams{
@@ -214,7 +269,7 @@
 		Inputs:      srcFiles,
 		Implicits:   deps,
 		Args: map[string]string{
-			"classpath":         flags.kotlincClasspath.FormJavaClassPath(""),
+			"classpath":         classpathRspFile.String(),
 			"kotlincFlags":      flags.kotlincFlags,
 			"commonSrcFilesArg": commonSrcFilesArg,
 			"srcJars":           strings.Join(srcJars.Strings(), " "),
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 933fc51..f6e7fca 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"slices"
 	"strconv"
 	"strings"
 	"testing"
@@ -23,10 +24,11 @@
 )
 
 func TestKotlin(t *testing.T) {
-	ctx, _ := testJava(t, `
+	bp := `
 		java_library {
 			name: "foo",
 			srcs: ["a.java", "b.kt"],
+			static_libs: ["quz"],
 		}
 
 		java_library {
@@ -39,85 +41,236 @@
 		java_library {
 			name: "baz",
 			srcs: ["c.java"],
+			static_libs: ["quz"],
 		}
-		`)
 
-	kotlinStdlib := ctx.ModuleForTests("kotlin-stdlib", "android_common").
-		Output("turbine-combined/kotlin-stdlib.jar").Output
-	kotlinStdlibJdk7 := ctx.ModuleForTests("kotlin-stdlib-jdk7", "android_common").
-		Output("turbine-combined/kotlin-stdlib-jdk7.jar").Output
-	kotlinStdlibJdk8 := ctx.ModuleForTests("kotlin-stdlib-jdk8", "android_common").
-		Output("turbine-combined/kotlin-stdlib-jdk8.jar").Output
-	kotlinAnnotations := ctx.ModuleForTests("kotlin-annotations", "android_common").
-		Output("turbine-combined/kotlin-annotations.jar").Output
+		java_library {
+			name: "quz",
+			srcs: ["d.kt"],
+		}`
 
-	fooKotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
-	fooJavac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	fooJar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
-	fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar")
-
-	fooKotlincClasses := fooKotlinc.Output
-	fooKotlincHeaderClasses := fooKotlinc.ImplicitOutput
-
-	if len(fooKotlinc.Inputs) != 2 || fooKotlinc.Inputs[0].String() != "a.java" ||
-		fooKotlinc.Inputs[1].String() != "b.kt" {
-		t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, fooKotlinc.Inputs)
+	kotlinStdlibTurbineCombinedJars := []string{
+		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/turbine-combined/kotlin-stdlib.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/turbine-combined/kotlin-stdlib-jdk7.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk8/android_common/turbine-combined/kotlin-stdlib-jdk8.jar",
+		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/turbine-combined/kotlin-annotations.jar",
 	}
 
-	if len(fooJavac.Inputs) != 1 || fooJavac.Inputs[0].String() != "a.java" {
-		t.Errorf(`foo inputs %v != ["a.java"]`, fooJavac.Inputs)
+	kotlinStdlibTurbineJars := []string{
+		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/turbine/kotlin-stdlib.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/turbine/kotlin-stdlib-jdk7.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk8/android_common/turbine/kotlin-stdlib-jdk8.jar",
+		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/turbine/kotlin-annotations.jar",
 	}
 
-	if !strings.Contains(fooJavac.Args["classpath"], fooKotlincHeaderClasses.String()) {
-		t.Errorf("foo classpath %v does not contain %q",
-			fooJavac.Args["classpath"], fooKotlincHeaderClasses.String())
+	kotlinStdlibJavacJars := []string{
+		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/javac/kotlin-stdlib.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/javac/kotlin-stdlib-jdk7.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk8/android_common/javac/kotlin-stdlib-jdk8.jar",
+		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/javac/kotlin-annotations.jar",
 	}
 
-	if !inList(fooKotlincClasses.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %q",
-			fooJar.Inputs.Strings(), fooKotlincClasses.String())
+	bootclasspathTurbineCombinedJars := []string{
+		"out/soong/.intermediates/default/java/stable.core.platform.api.stubs/android_common/turbine-combined/stable.core.platform.api.stubs.jar",
+		"out/soong/.intermediates/default/java/core-lambda-stubs/android_common/turbine-combined/core-lambda-stubs.jar",
 	}
 
-	if !inList(kotlinStdlib.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinStdlib.String())
+	bootclasspathTurbineJars := []string{
+		"out/soong/.intermediates/default/java/stable.core.platform.api.stubs/android_common/turbine/stable.core.platform.api.stubs.jar",
+		"out/soong/.intermediates/default/java/core-lambda-stubs/android_common/turbine/core-lambda-stubs.jar",
 	}
 
-	if !inList(kotlinStdlibJdk7.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinStdlibJdk7.String())
+	frameworkTurbineCombinedJars := []string{
+		"out/soong/.intermediates/default/java/ext/android_common/turbine-combined/ext.jar",
+		"out/soong/.intermediates/default/java/framework/android_common/turbine-combined/framework.jar",
 	}
 
-	if !inList(kotlinStdlibJdk8.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinStdlibJdk8.String())
+	frameworkTurbineJars := []string{
+		"out/soong/.intermediates/default/java/ext/android_common/turbine/ext.jar",
+		"out/soong/.intermediates/default/java/framework/android_common/turbine/framework.jar",
 	}
 
-	if !inList(kotlinAnnotations.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinAnnotations.String())
+	testCases := []struct {
+		name string
+
+		preparer android.FixturePreparer
+
+		fooKotlincInputs        []string
+		fooJavacInputs          []string
+		fooKotlincClasspath     []string
+		fooJavacClasspath       []string
+		fooCombinedInputs       []string
+		fooHeaderCombinedInputs []string
+
+		barKotlincInputs        []string
+		barKotlincClasspath     []string
+		barCombinedInputs       []string
+		barHeaderCombinedInputs []string
+	}{
+		{
+			name:             "normal",
+			preparer:         android.NullFixturePreparer,
+			fooKotlincInputs: []string{"a.java", "b.kt"},
+			fooJavacInputs:   []string{"a.java"},
+			fooKotlincClasspath: slices.Concat(
+				bootclasspathTurbineCombinedJars,
+				frameworkTurbineCombinedJars,
+				[]string{"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar"},
+				kotlinStdlibTurbineCombinedJars,
+			),
+			fooJavacClasspath: slices.Concat(
+				[]string{"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar"},
+				frameworkTurbineCombinedJars,
+				[]string{"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar"},
+				kotlinStdlibTurbineCombinedJars,
+			),
+			fooCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/foo/android_common/kotlin/foo.jar",
+					"out/soong/.intermediates/foo/android_common/javac/foo.jar",
+					"out/soong/.intermediates/quz/android_common/combined/quz.jar",
+				},
+				kotlinStdlibJavacJars,
+			),
+			fooHeaderCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
+					"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar",
+					"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
+				},
+				kotlinStdlibTurbineCombinedJars,
+			),
+
+			barKotlincInputs: []string{"b.kt"},
+			barKotlincClasspath: slices.Concat(
+				bootclasspathTurbineCombinedJars,
+				frameworkTurbineCombinedJars,
+				[]string{
+					"out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar",
+					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
+				},
+				kotlinStdlibTurbineCombinedJars,
+			),
+			barCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/bar/android_common/kotlin/bar.jar",
+					"out/soong/.intermediates/baz/android_common/combined/baz.jar",
+				},
+				kotlinStdlibJavacJars,
+				[]string{},
+			),
+			barHeaderCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/bar/android_common/kotlin_headers/bar.jar",
+					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
+				},
+				kotlinStdlibTurbineCombinedJars,
+			),
+		},
+		{
+			name:             "transitive classpath",
+			preparer:         PrepareForTestWithTransitiveClasspathEnabled,
+			fooKotlincInputs: []string{"a.java", "b.kt"},
+			fooJavacInputs:   []string{"a.java"},
+			fooKotlincClasspath: slices.Concat(
+				bootclasspathTurbineJars,
+				frameworkTurbineJars,
+				[]string{"out/soong/.intermediates/quz/android_common/kotlin_headers/quz.jar"},
+				kotlinStdlibTurbineJars,
+			),
+			fooJavacClasspath: slices.Concat(
+				[]string{"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar"},
+				frameworkTurbineJars,
+				[]string{"out/soong/.intermediates/quz/android_common/kotlin_headers/quz.jar"},
+				kotlinStdlibTurbineJars,
+			),
+			fooCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/foo/android_common/kotlin/foo.jar",
+					"out/soong/.intermediates/foo/android_common/javac/foo.jar",
+					"out/soong/.intermediates/quz/android_common/kotlin/quz.jar",
+				},
+				kotlinStdlibJavacJars,
+			),
+			fooHeaderCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
+					"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar",
+					"out/soong/.intermediates/quz/android_common/kotlin_headers/quz.jar",
+				},
+				kotlinStdlibTurbineJars,
+			),
+
+			barKotlincInputs: []string{"b.kt"},
+			barKotlincClasspath: slices.Concat(
+				bootclasspathTurbineJars,
+				frameworkTurbineJars,
+				[]string{
+					"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
+					"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar",
+					"out/soong/.intermediates/quz/android_common/kotlin_headers/quz.jar",
+				},
+				kotlinStdlibTurbineJars,
+				[]string{"out/soong/.intermediates/baz/android_common/turbine/baz.jar"},
+			),
+			barCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/bar/android_common/kotlin/bar.jar",
+					"out/soong/.intermediates/baz/android_common/javac/baz.jar",
+					"out/soong/.intermediates/quz/android_common/kotlin/quz.jar",
+				},
+				kotlinStdlibJavacJars,
+			),
+			barHeaderCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/bar/android_common/kotlin_headers/bar.jar",
+					"out/soong/.intermediates/baz/android_common/turbine/baz.jar",
+					"out/soong/.intermediates/quz/android_common/kotlin_headers/quz.jar",
+				},
+				kotlinStdlibTurbineJars,
+			),
+		},
 	}
 
-	if !inList(fooKotlincHeaderClasses.String(), fooHeaderJar.Inputs.Strings()) {
-		t.Errorf("foo header jar inputs %v does not contain %q",
-			fooHeaderJar.Inputs.Strings(), fooKotlincHeaderClasses.String())
-	}
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				tt.preparer,
+			).RunTestWithBp(t, bp)
+			foo := result.ModuleForTests("foo", "android_common")
+			fooKotlinc := foo.Rule("kotlinc")
+			android.AssertPathsRelativeToTopEquals(t, "foo kotlinc inputs", tt.fooKotlincInputs, fooKotlinc.Inputs)
 
-	bazHeaderJar := ctx.ModuleForTests("baz", "android_common").Output("turbine-combined/baz.jar")
-	barKotlinc := ctx.ModuleForTests("bar", "android_common").Rule("kotlinc")
+			fooKotlincClasspath := android.ContentFromFileRuleForTests(t, result.TestContext, foo.Output("kotlinc/classpath.rsp"))
+			android.AssertStringPathsRelativeToTopEquals(t, "foo kotlinc classpath", result.Config, tt.fooKotlincClasspath, strings.Fields(fooKotlincClasspath))
 
-	if len(barKotlinc.Inputs) != 1 || barKotlinc.Inputs[0].String() != "b.kt" {
-		t.Errorf(`bar kotlinc inputs %v != ["b.kt"]`, barKotlinc.Inputs)
-	}
+			fooJavac := foo.Rule("javac")
+			android.AssertPathsRelativeToTopEquals(t, "foo javac inputs", tt.fooJavacInputs, fooJavac.Inputs)
 
-	if !inList(fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
-		t.Errorf(`expected %q in bar implicits %v`,
-			fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
-	}
+			fooJavacClasspath := fooJavac.Args["classpath"]
+			android.AssertStringPathsRelativeToTopEquals(t, "foo javac classpath", result.Config, tt.fooJavacClasspath,
+				strings.Split(strings.TrimPrefix(fooJavacClasspath, "-classpath "), ":"))
 
-	if !inList(bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
-		t.Errorf(`expected %q in bar implicits %v`,
-			bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
+			fooCombinedJar := foo.Output("combined/foo.jar")
+			android.AssertPathsRelativeToTopEquals(t, "foo combined inputs", tt.fooCombinedInputs, fooCombinedJar.Inputs)
+
+			fooCombinedHeaderJar := foo.Output("turbine-combined/foo.jar")
+			android.AssertPathsRelativeToTopEquals(t, "foo header combined inputs", tt.fooHeaderCombinedInputs, fooCombinedHeaderJar.Inputs)
+
+			bar := result.ModuleForTests("bar", "android_common")
+			barKotlinc := bar.Rule("kotlinc")
+			android.AssertPathsRelativeToTopEquals(t, "bar kotlinc inputs", tt.barKotlincInputs, barKotlinc.Inputs)
+
+			barKotlincClasspath := android.ContentFromFileRuleForTests(t, result.TestContext, bar.Output("kotlinc/classpath.rsp"))
+			android.AssertStringPathsRelativeToTopEquals(t, "bar kotlinc classpath", result.Config, tt.barKotlincClasspath, strings.Fields(barKotlincClasspath))
+
+			barCombinedJar := bar.Output("combined/bar.jar")
+			android.AssertPathsRelativeToTopEquals(t, "bar combined inputs", tt.barCombinedInputs, barCombinedJar.Inputs)
+
+			barCombinedHeaderJar := bar.Output("turbine-combined/bar.jar")
+			android.AssertPathsRelativeToTopEquals(t, "bar header combined inputs", tt.barHeaderCombinedInputs, barCombinedHeaderJar.Inputs)
+		})
 	}
 }
 
diff --git a/java/lint.go b/java/lint.go
index 2eea07d..6782adc 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -650,7 +650,7 @@
 		}
 
 		if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
-			apexInfo, _ := android.SingletonModuleProvider(ctx, m, android.ApexInfoProvider)
+			apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider)
 			if apexInfo.IsForPlatform() {
 				// There are stray platform variants of modules in apexes that are not available for
 				// the platform, and they sometimes can't be built.  Don't depend on them.
diff --git a/java/metalava/Android.bp b/java/metalava/Android.bp
new file mode 100644
index 0000000..6bf1832
--- /dev/null
+++ b/java/metalava/Android.bp
@@ -0,0 +1,19 @@
+// 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.
+
+filegroup {
+    name: "metalava-config-files",
+    srcs: ["*-config.xml"],
+    visibility: ["//visibility:public"],
+}
diff --git a/java/metalava/OWNERS b/java/metalava/OWNERS
new file mode 100644
index 0000000..e8c438e
--- /dev/null
+++ b/java/metalava/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 463936
+
+file:platform/tools/metalava:/OWNERS
diff --git a/java/metalava/main-config.xml b/java/metalava/main-config.xml
new file mode 100644
index 0000000..c61196f
--- /dev/null
+++ b/java/metalava/main-config.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<config xmlns="http://www.google.com/tools/metalava/config"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.google.com/tools/metalava/config ../../../../tools/metalava/metalava/src/main/resources/schemas/config.xsd"/>
diff --git a/java/metalava/source-model-selection-config.xml b/java/metalava/source-model-selection-config.xml
new file mode 100644
index 0000000..c61196f
--- /dev/null
+++ b/java/metalava/source-model-selection-config.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
+<config xmlns="http://www.google.com/tools/metalava/config"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.google.com/tools/metalava/config ../../../../tools/metalava/metalava/src/main/resources/schemas/config.xsd"/>
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 8d4cf68..d794e51 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -15,8 +15,6 @@
 package java
 
 import (
-	"fmt"
-
 	"android/soong/android"
 	"android/soong/dexpreopt"
 )
@@ -57,9 +55,6 @@
 
 	// Path to the monolithic hiddenapi-unsupported.csv file.
 	hiddenAPIMetadataCSV android.OutputPath
-
-	// Path to a srcjar containing all the transitive sources of the bootclasspath.
-	srcjar android.OutputPath
 }
 
 type platformBootclasspathProperties struct {
@@ -76,8 +71,6 @@
 	return m
 }
 
-var _ android.OutputFileProducer = (*platformBootclasspathModule)(nil)
-
 func (b *platformBootclasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) {
 	entries = append(entries, android.AndroidMkEntries{
 		Class: "FAKE",
@@ -89,22 +82,6 @@
 	return
 }
 
-// Make the hidden API files available from the platform-bootclasspath module.
-func (b *platformBootclasspathModule) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "hiddenapi-flags.csv":
-		return android.Paths{b.hiddenAPIFlagsCSV}, nil
-	case "hiddenapi-index.csv":
-		return android.Paths{b.hiddenAPIIndexCSV}, nil
-	case "hiddenapi-metadata.csv":
-		return android.Paths{b.hiddenAPIMetadataCSV}, nil
-	case ".srcjar":
-		return android.Paths{b.srcjar}, nil
-	}
-
-	return nil, fmt.Errorf("unknown tag %s", tag)
-}
-
 func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Create a dependency on all_apex_contributions to determine the selected mainline module
 	ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions")
@@ -191,15 +168,16 @@
 
 	var transitiveSrcFiles android.Paths
 	for _, module := range allModules {
-		depInfo, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
-		if depInfo.TransitiveSrcFiles != nil {
-			transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...)
+		if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			if depInfo.TransitiveSrcFiles != nil {
+				transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...)
+			}
 		}
 	}
 	jarArgs := resourcePathsToJarArgs(transitiveSrcFiles)
 	jarArgs = append(jarArgs, "-srcjar") // Move srcfiles to the right package
-	b.srcjar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-transitive.srcjar").OutputPath
-	TransformResourcesToJar(ctx, b.srcjar, jarArgs, transitiveSrcFiles)
+	srcjar := android.PathForModuleOut(ctx, ctx.ModuleName()+"-transitive.srcjar").OutputPath
+	TransformResourcesToJar(ctx, srcjar, jarArgs, transitiveSrcFiles)
 
 	// Gather all the fragments dependencies.
 	b.fragments = gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag)
@@ -213,6 +191,11 @@
 
 	bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
 	buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
+
+	ctx.SetOutputFiles(android.Paths{b.hiddenAPIFlagsCSV}, "hiddenapi-flags.csv")
+	ctx.SetOutputFiles(android.Paths{b.hiddenAPIIndexCSV}, "hiddenapi-index.csv")
+	ctx.SetOutputFiles(android.Paths{b.hiddenAPIMetadataCSV}, "hiddenapi-metadata.csv")
+	ctx.SetOutputFiles(android.Paths{srcjar}, ".srcjar")
 }
 
 // Generate classpaths.proto config
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 99fa092..5b145c6 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -15,7 +15,6 @@
 package java
 
 import (
-	"fmt"
 	"path/filepath"
 
 	"android/soong/android"
@@ -61,6 +60,8 @@
 	installDirPath android.InstallPath
 	configFile     android.OutputPath
 	metadataFile   android.OutputPath
+
+	installConfigFile android.InstallPath
 }
 
 func (p *platformCompatConfig) compatConfigMetadata() android.Path {
@@ -106,21 +107,16 @@
 		FlagWithOutput("--merged-config ", p.metadataFile)
 
 	p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig")
+	p.installConfigFile = android.PathForModuleInstall(ctx, "etc", "compatconfig", p.configFile.Base())
 	rule.Build(configFileName, "Extract compat/compat_config.xml and install it")
-
+	ctx.InstallFile(p.installDirPath, p.configFile.Base(), p.configFile)
+	ctx.SetOutputFiles(android.Paths{p.configFile}, "")
 }
 
 func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries {
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(p.configFile),
-		Include:    "$(BUILD_PREBUILT)",
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
-			},
-		},
 	}}
 }
 
@@ -266,7 +262,7 @@
 
 func (p *platformCompatConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
 	if p.metadata != nil {
-		ctx.Strict("INTERNAL_PLATFORM_MERGED_COMPAT_CONFIG", p.metadata.String())
+		ctx.DistForGoal("droidcore", p.metadata)
 	}
 }
 
@@ -284,32 +280,23 @@
 	android.ModuleBase
 
 	properties globalCompatConfigProperties
-
-	outputFilePath android.OutputPath
 }
 
 func (c *globalCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	filename := String(c.properties.Filename)
 
 	inputPath := platformCompatConfigPath(ctx)
-	c.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
+	outputFilePath := android.PathForModuleOut(ctx, filename).OutputPath
 
 	// This ensures that outputFilePath has the correct name for others to
 	// use, as the source file may have a different name.
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   android.Cp,
-		Output: c.outputFilePath,
+		Output: outputFilePath,
 		Input:  inputPath,
 	})
-}
 
-func (h *globalCompatConfig) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{h.outputFilePath}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
+	ctx.SetOutputFiles(android.Paths{outputFilePath}, "")
 }
 
 // global_compat_config provides access to the merged compat config xml file generated by the build.
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 00613ee..527e479 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -124,8 +124,8 @@
 	return
 }
 
-func prebuiltApiModuleName(mctx android.LoadHookContext, module, scope, version string) string {
-	return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module)
+func prebuiltApiModuleName(moduleName, module, scope, version string) string {
+	return fmt.Sprintf("%s_%s_%s_%s", moduleName, scope, version, module)
 }
 func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) {
 	props := struct {
@@ -135,7 +135,7 @@
 		Installable *bool
 		Compile_dex *bool
 	}{
-		Name:        proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version)),
+		Name:        proptools.StringPtr(prebuiltApiModuleName(mctx.ModuleName(), module, scope, version)),
 		Jars:        []string{path},
 		Sdk_version: proptools.StringPtr(sdkVersion),
 		Installable: proptools.BoolPtr(false),
@@ -257,8 +257,8 @@
 		Name *string
 		Libs []string
 	}{}
-	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, version))
-	props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, version))
+	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx.ModuleName(), "system_modules", scope, version))
+	props.Libs = append(props.Libs, prebuiltApiModuleName(mctx.ModuleName(), "core-for-system-modules", scope, version))
 
 	mctx.CreateModule(systemModulesImportFactory, &props)
 }
diff --git a/java/ravenwood.go b/java/ravenwood.go
index 908619d..9239bbd 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -33,6 +33,8 @@
 var ravenwoodLibContentTag = dependencyTag{name: "ravenwoodlibcontent"}
 var ravenwoodUtilsTag = dependencyTag{name: "ravenwoodutils"}
 var ravenwoodRuntimeTag = dependencyTag{name: "ravenwoodruntime"}
+var ravenwoodTestResourceApkTag = dependencyTag{name: "ravenwoodtestresapk"}
+var ravenwoodTestInstResourceApkTag = dependencyTag{name: "ravenwoodtest-inst-res-apk"}
 
 const ravenwoodUtilsName = "ravenwood-utils"
 const ravenwoodRuntimeName = "ravenwood-runtime"
@@ -53,6 +55,19 @@
 
 type ravenwoodTestProperties struct {
 	Jni_libs []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
+	// target app.
+	// TODO: For now, we simply refer to another android_app module and copy it to the
+	// test directory. Eventually, android_ravenwood_test should support all the resource
+	// related properties and build resources from the `res/` directory.
+	Resource_apk *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
+	// instrumentation app itself.
+	Inst_resource_apk *string
 }
 
 type ravenwoodTest struct {
@@ -114,6 +129,15 @@
 	for _, lib := range r.ravenwoodTestProperties.Jni_libs {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
 	}
+
+	// Resources APK
+	if resourceApk := proptools.String(r.ravenwoodTestProperties.Resource_apk); resourceApk != "" {
+		ctx.AddVariationDependencies(nil, ravenwoodTestResourceApkTag, resourceApk)
+	}
+
+	if resourceApk := proptools.String(r.ravenwoodTestProperties.Inst_resource_apk); resourceApk != "" {
+		ctx.AddVariationDependencies(nil, ravenwoodTestInstResourceApkTag, resourceApk)
+	}
 }
 
 func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -129,6 +153,9 @@
 		HostTemplate:           "${RavenwoodTestConfigTemplate}",
 	})
 
+	// Always enable Ravenizer for ravenwood tests.
+	r.Library.ravenizer.enabled = true
+
 	r.Library.GenerateAndroidBuildActions(ctx)
 
 	// Start by depending on all files installed by dependencies
@@ -138,7 +165,8 @@
 	var runtimeJniModuleNames map[string]bool
 
 	if utils := ctx.GetDirectDepsWithTag(ravenwoodUtilsTag)[0]; utils != nil {
-		for _, installFile := range utils.FilesToInstall() {
+		for _, installFile := range android.OtherModuleProviderOrDefault(
+			ctx, utils, android.InstallFilesProvider).InstallFiles {
 			installDeps = append(installDeps, installFile)
 		}
 		jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
@@ -148,7 +176,8 @@
 	}
 
 	if runtime := ctx.GetDirectDepsWithTag(ravenwoodRuntimeTag)[0]; runtime != nil {
-		for _, installFile := range runtime.FilesToInstall() {
+		for _, installFile := range android.OtherModuleProviderOrDefault(
+			ctx, runtime, android.InstallFilesProvider).InstallFiles {
 			installDeps = append(installDeps, installFile)
 		}
 		jniDeps, ok := android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
@@ -175,6 +204,18 @@
 		installDeps = append(installDeps, installJni)
 	}
 
+	resApkInstallPath := installPath.Join(ctx, "ravenwood-res-apks")
+
+	copyResApk := func(tag blueprint.DependencyTag, toFileName string) {
+		if resApk := ctx.GetDirectDepsWithTag(tag); len(resApk) > 0 {
+			installFile := android.OutputFileForModule(ctx, resApk[0], "")
+			installResApk := ctx.InstallFile(resApkInstallPath, toFileName, installFile)
+			installDeps = append(installDeps, installResApk)
+		}
+	}
+	copyResApk(ravenwoodTestResourceApkTag, "ravenwood-res.apk")
+	copyResApk(ravenwoodTestInstResourceApkTag, "ravenwood-inst-res.apk")
+
 	// Install our JAR with all dependencies
 	ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
 }
@@ -198,6 +239,12 @@
 	Libs []string
 
 	Jni_libs []string
+
+	// We use this to copy framework-res.apk to the ravenwood runtime directory.
+	Data []string `android:"path,arch_variant"`
+
+	// We use this to copy font files to the ravenwood runtime directory.
+	Fonts []string `android:"path,arch_variant"`
 }
 
 type ravenwoodLibgroup struct {
@@ -257,6 +304,14 @@
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
 		libModule := ctx.GetDirectDepWithTag(lib, ravenwoodLibContentTag)
+		if libModule == nil {
+			if ctx.Config().AllowMissingDependencies() {
+				ctx.AddMissingDependencies([]string{lib})
+			} else {
+				ctx.PropertyErrorf("lib", "missing dependency %q", lib)
+			}
+			continue
+		}
 		libJar := android.OutputFileForModule(ctx, libModule, "")
 		ctx.InstallFile(installPath, lib+".jar", libJar)
 	}
@@ -266,6 +321,18 @@
 		ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
 	}
 
+	dataInstallPath := installPath.Join(ctx, "ravenwood-data")
+	data := android.PathsForModuleSrc(ctx, r.ravenwoodLibgroupProperties.Data)
+	for _, file := range data {
+		ctx.InstallFile(dataInstallPath, file.Base(), file)
+	}
+
+	fontsInstallPath := installPath.Join(ctx, "fonts")
+	fonts := android.PathsForModuleSrc(ctx, r.ravenwoodLibgroupProperties.Fonts)
+	for _, file := range fonts {
+		ctx.InstallFile(fontsInstallPath, file.Base(), file)
+	}
+
 	// Normal build should perform install steps
 	ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install"))
 }
diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go
index 5961264..753a118 100644
--- a/java/ravenwood_test.go
+++ b/java/ravenwood_test.go
@@ -19,6 +19,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/etc"
 )
 
 var prepareRavenwoodRuntime = android.GroupFixturePreparers(
@@ -57,6 +58,22 @@
 			name: "framework-rules.ravenwood",
 			srcs: ["Rules.java"],
 		}
+		android_app {
+			name: "app1",
+			sdk_version: "current",
+		}
+		android_app {
+			name: "app2",
+			sdk_version: "current",
+		}
+		android_app {
+			name: "app3",
+			sdk_version: "current",
+		}
+		prebuilt_font {
+			name: "Font.ttf",
+			src: "Font.ttf",
+		}
 		android_ravenwood_libgroup {
 			name: "ravenwood-runtime",
 			libs: [
@@ -67,6 +84,12 @@
 				"ravenwood-runtime-jni1",
 				"ravenwood-runtime-jni2",
 			],
+			data: [
+				":app1",
+			],
+			fonts: [
+				":Font.ttf"
+			],
 		}
 		android_ravenwood_libgroup {
 			name: "ravenwood-utils",
@@ -86,6 +109,7 @@
 
 	ctx := android.GroupFixturePreparers(
 		PrepareForIntegrationTestWithJava,
+		etc.PrepareForTestWithPrebuiltEtc,
 		prepareRavenwoodRuntime,
 	).RunTest(t)
 
@@ -102,6 +126,8 @@
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni1.so")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/libred.so")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so")
+	runtime.Output(installPathPrefix + "/ravenwood-runtime/ravenwood-data/app1.apk")
+	runtime.Output(installPathPrefix + "/ravenwood-runtime/fonts/Font.ttf")
 	utils := ctx.ModuleForTests("ravenwood-utils", "android_common")
 	utils.Output(installPathPrefix + "/ravenwood-utils/framework-rules.ravenwood.jar")
 }
@@ -113,29 +139,30 @@
 
 	ctx := android.GroupFixturePreparers(
 		PrepareForIntegrationTestWithJava,
+		etc.PrepareForTestWithPrebuiltEtc,
 		prepareRavenwoodRuntime,
 	).RunTestWithBp(t, `
-	cc_library_shared {
-		name: "jni-lib1",
-		host_supported: true,
-		srcs: ["jni.cpp"],
-	}
-	cc_library_shared {
-		name: "jni-lib2",
-		host_supported: true,
-		srcs: ["jni.cpp"],
-		stem: "libblue",
-		shared_libs: [
-			"jni-lib3",
-		],
-	}
-	cc_library_shared {
-		name: "jni-lib3",
-		host_supported: true,
-		srcs: ["jni.cpp"],
-		stem: "libpink",
-	}
-	android_ravenwood_test {
+		cc_library_shared {
+			name: "jni-lib1",
+			host_supported: true,
+			srcs: ["jni.cpp"],
+		}
+		cc_library_shared {
+			name: "jni-lib2",
+			host_supported: true,
+			srcs: ["jni.cpp"],
+			stem: "libblue",
+			shared_libs: [
+				"jni-lib3",
+			],
+		}
+		cc_library_shared {
+			name: "jni-lib3",
+			host_supported: true,
+			srcs: ["jni.cpp"],
+			stem: "libpink",
+		}
+		android_ravenwood_test {
 			name: "ravenwood-test",
 			srcs: ["Test.java"],
 			jni_libs: [
@@ -143,6 +170,8 @@
 				"jni-lib2",
 				"ravenwood-runtime-jni2",
 			],
+			resource_apk: "app2",
+			inst_resource_apk: "app3",
 			sdk_version: "test_current",
 		}
 	`)
@@ -169,6 +198,8 @@
 	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")
 
 	// ravenwood-runtime*.so are included in the runtime, so it shouldn't be emitted.
 	for _, o := range module.AllOutputs() {
diff --git a/java/robolectric.go b/java/robolectric.go
index 18386c9..374fc5f 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -16,9 +16,6 @@
 
 import (
 	"fmt"
-	"io"
-	"strconv"
-	"strings"
 
 	"android/soong/android"
 	"android/soong/java/config"
@@ -29,8 +26,12 @@
 )
 
 func init() {
-	android.RegisterModuleType("android_robolectric_test", RobolectricTestFactory)
-	android.RegisterModuleType("android_robolectric_runtimes", robolectricRuntimesFactory)
+	RegisterRobolectricBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterRobolectricBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("android_robolectric_test", RobolectricTestFactory)
+	ctx.RegisterModuleType("android_robolectric_runtimes", robolectricRuntimesFactory)
 }
 
 var robolectricDefaultLibs = []string{
@@ -74,6 +75,8 @@
 
 	// Use strict mode to limit access of Robolectric API directly. See go/roboStrictMode
 	Strict_mode *bool
+
+	Jni_libs []string
 }
 
 type robolectricTest struct {
@@ -82,16 +85,6 @@
 	robolectricProperties robolectricProperties
 	testProperties        testProperties
 
-	libs  []string
-	tests []string
-
-	manifest    android.Path
-	resourceApk android.Path
-
-	combinedJar android.WritablePath
-
-	roboSrcJar android.Path
-
 	testConfig android.Path
 	data       android.Paths
 
@@ -115,25 +108,32 @@
 	}
 
 	if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
-		ctx.AddVariationDependencies(nil, libTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v))
-	} else if !proptools.Bool(r.robolectricProperties.Strict_mode) {
+		ctx.AddVariationDependencies(nil, staticLibTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v))
+	} else if !proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
 		if proptools.Bool(r.robolectricProperties.Upstream) {
-			ctx.AddVariationDependencies(nil, libTag, robolectricCurrentLib+"_upstream")
+			ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib+"_upstream")
 		} else {
-			ctx.AddVariationDependencies(nil, libTag, robolectricCurrentLib)
+			ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib)
 		}
 	}
 
-	if proptools.Bool(r.robolectricProperties.Strict_mode) {
+	if proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
 		ctx.AddVariationDependencies(nil, roboRuntimeOnlyTag, robolectricCurrentLib+"_upstream")
+	} else {
+		// opting out from strict mode, robolectric_non_strict_mode_permission lib should be added
+		ctx.AddVariationDependencies(nil, staticLibTag, "robolectric_non_strict_mode_permission")
 	}
 
-	ctx.AddVariationDependencies(nil, libTag, robolectricDefaultLibs...)
+	ctx.AddVariationDependencies(nil, staticLibTag, robolectricDefaultLibs...)
 
 	ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...)
 
 	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
 		roboRuntimesTag, "robolectric-android-all-prebuilts")
+
+	for _, lib := range r.robolectricProperties.Jni_libs {
+		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
+	}
 }
 
 func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -150,9 +150,6 @@
 	})
 	r.data = android.PathsForModuleSrc(ctx, r.testProperties.Data)
 
-	roboTestConfig := android.PathForModuleGen(ctx, "robolectric").
-		Join(ctx, "com/android/tools/test_config.properties")
-
 	var ok bool
 	var instrumentedApp *AndroidApp
 
@@ -168,89 +165,58 @@
 		panic(fmt.Errorf("expected exactly 1 instrumented dependency, got %d", len(instrumented)))
 	}
 
+	var resourceApk android.Path
+	var manifest android.Path
 	if instrumentedApp != nil {
-		r.manifest = instrumentedApp.mergedManifestFile
-		r.resourceApk = instrumentedApp.outputFile
-
-		generateRoboTestConfig(ctx, roboTestConfig, instrumentedApp)
-		r.extraResources = android.Paths{roboTestConfig}
-	}
-
-	r.Library.GenerateAndroidBuildActions(ctx)
-
-	roboSrcJar := android.PathForModuleGen(ctx, "robolectric", ctx.ModuleName()+".srcjar")
-
-	if instrumentedApp != nil {
-		r.generateRoboSrcJar(ctx, roboSrcJar, instrumentedApp)
-		r.roboSrcJar = roboSrcJar
+		manifest = instrumentedApp.mergedManifestFile
+		resourceApk = instrumentedApp.outputFile
 	}
 
 	roboTestConfigJar := android.PathForModuleOut(ctx, "robolectric_samedir", "samedir_config.jar")
 	generateSameDirRoboTestConfigJar(ctx, roboTestConfigJar)
 
-	combinedJarJars := android.Paths{
-		// roboTestConfigJar comes first so that its com/android/tools/test_config.properties
-		// overrides the one from r.extraResources.  The r.extraResources one can be removed
-		// once the Make test runner is removed.
-		roboTestConfigJar,
-		r.outputFile,
-	}
+	extraCombinedJars := android.Paths{roboTestConfigJar}
 
-	if instrumentedApp != nil {
-		combinedJarJars = append(combinedJarJars, instrumentedApp.implementationAndResourcesJar)
-	}
-
-	handleLibDeps := func(dep android.Module, runtimeOnly bool) {
-		m, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
-		if !runtimeOnly {
-			r.libs = append(r.libs, ctx.OtherModuleName(dep))
-		}
+	handleLibDeps := func(dep android.Module) {
 		if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) {
-			combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars...)
+			if m, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+				extraCombinedJars = append(extraCombinedJars, m.ImplementationAndResourcesJars...)
+			}
 		}
 	}
 
 	for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
-		handleLibDeps(dep, false)
+		handleLibDeps(dep)
 	}
 	for _, dep := range ctx.GetDirectDepsWithTag(sdkLibTag) {
-		handleLibDeps(dep, false)
+		handleLibDeps(dep)
 	}
 	// handle the runtimeOnly tag for strict_mode
 	for _, dep := range ctx.GetDirectDepsWithTag(roboRuntimeOnlyTag) {
-		handleLibDeps(dep, true)
+		handleLibDeps(dep)
 	}
 
-	r.combinedJar = android.PathForModuleOut(ctx, "robolectric_combined", r.outputFile.Base())
-	TransformJarsToJar(ctx, r.combinedJar, "combine jars", combinedJarJars, android.OptionalPath{},
-		false, nil, nil)
-
-	// TODO: this could all be removed if tradefed was used as the test runner, it will find everything
-	// annotated as a test and run it.
-	for _, src := range r.uniqueSrcFiles {
-		s := src.Rel()
-		if !strings.HasSuffix(s, "Test.java") && !strings.HasSuffix(s, "Test.kt") {
-			continue
-		} else if strings.HasSuffix(s, "/BaseRobolectricTest.java") {
-			continue
-		} else {
-			s = strings.TrimPrefix(s, "src/")
-		}
-		r.tests = append(r.tests, s)
+	if instrumentedApp != nil {
+		extraCombinedJars = append(extraCombinedJars, instrumentedApp.implementationAndResourcesJar)
 	}
 
+	r.stem = proptools.StringDefault(r.overridableProperties.Stem, ctx.ModuleName())
+	r.classLoaderContexts = r.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+	r.dexpreopter.disableDexpreopt()
+	r.compile(ctx, nil, nil, nil, extraCombinedJars)
+
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	var installDeps android.InstallPaths
 
-	if r.manifest != nil {
-		r.data = append(r.data, r.manifest)
-		installedManifest := ctx.InstallFile(installPath, ctx.ModuleName()+"-AndroidManifest.xml", r.manifest)
+	if manifest != nil {
+		r.data = append(r.data, manifest)
+		installedManifest := ctx.InstallFile(installPath, ctx.ModuleName()+"-AndroidManifest.xml", manifest)
 		installDeps = append(installDeps, installedManifest)
 	}
 
-	if r.resourceApk != nil {
-		r.data = append(r.data, r.resourceApk)
-		installedResourceApk := ctx.InstallFile(installPath, ctx.ModuleName()+".apk", r.resourceApk)
+	if resourceApk != nil {
+		r.data = append(r.data, resourceApk)
+		installedResourceApk := ctx.InstallFile(installPath, ctx.ModuleName()+".apk", resourceApk)
 		installDeps = append(installDeps, installedResourceApk)
 	}
 
@@ -267,29 +233,16 @@
 		installDeps = append(installDeps, installedData)
 	}
 
-	r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...)
+	soInstallPath := installPath.Join(ctx, getLibPath(r.forceArchType))
+	for _, jniLib := range collectTransitiveJniDeps(ctx) {
+		installJni := ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
+		installDeps = append(installDeps, installJni)
+	}
+
+	r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
 	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
 
-func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath,
-	instrumentedApp *AndroidApp) {
-	rule := android.NewRuleBuilder(pctx, ctx)
-
-	manifest := instrumentedApp.mergedManifestFile
-	resourceApk := instrumentedApp.outputFile
-
-	rule.Command().Text("rm -f").Output(outputFile)
-	rule.Command().
-		Textf(`echo "android_merged_manifest=%s" >>`, manifest.String()).Output(outputFile).Text("&&").
-		Textf(`echo "android_resource_apk=%s" >>`, resourceApk.String()).Output(outputFile).
-		// Make it depend on the files to which it points so the test file's timestamp is updated whenever the
-		// contents change
-		Implicit(manifest).
-		Implicit(resourceApk)
-
-	rule.Build("generate_test_config", "generate test_config.properties")
-}
-
 func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile android.ModuleOutPath) {
 	rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -312,22 +265,6 @@
 	rule.Build("generate_test_config_samedir", "generate test_config.properties")
 }
 
-func (r *robolectricTest) generateRoboSrcJar(ctx android.ModuleContext, outputFile android.WritablePath,
-	instrumentedApp *AndroidApp) {
-
-	srcJarArgs := android.CopyOf(instrumentedApp.srcJarArgs)
-	srcJarDeps := append(android.Paths(nil), instrumentedApp.srcJarDeps...)
-
-	for _, m := range ctx.GetDirectDepsWithTag(roboCoverageLibsTag) {
-		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
-			srcJarArgs = append(srcJarArgs, dep.SrcJarArgs...)
-			srcJarDeps = append(srcJarDeps, dep.SrcJarDeps...)
-		}
-	}
-
-	TransformResourcesToJar(ctx, outputFile, srcJarArgs, srcJarDeps)
-}
-
 func (r *robolectricTest) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := r.Library.AndroidMkEntries()
 	entries := &entriesList[0]
@@ -339,61 +276,11 @@
 				entries.SetPath("LOCAL_FULL_TEST_CONFIG", r.testConfig)
 			}
 		})
-
-	entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{
-		func(w io.Writer, name, prefix, moduleDir string) {
-			if s := r.robolectricProperties.Test_options.Shards; s != nil && *s > 1 {
-				numShards := int(*s)
-				shardSize := (len(r.tests) + numShards - 1) / numShards
-				shards := android.ShardStrings(r.tests, shardSize)
-				for i, shard := range shards {
-					r.writeTestRunner(w, name, "Run"+name+strconv.Itoa(i), shard)
-				}
-
-				// TODO: add rules to dist the outputs of the individual tests, or combine them together?
-				fmt.Fprintln(w, "")
-				fmt.Fprintln(w, ".PHONY:", "Run"+name)
-				fmt.Fprintln(w, "Run"+name, ": \\")
-				for i := range shards {
-					fmt.Fprintln(w, "   ", "Run"+name+strconv.Itoa(i), "\\")
-				}
-				fmt.Fprintln(w, "")
-			} else {
-				r.writeTestRunner(w, name, "Run"+name, r.tests)
-			}
-		},
-	}
-
 	return entriesList
 }
 
-func (r *robolectricTest) writeTestRunner(w io.Writer, module, name string, tests []string) {
-	fmt.Fprintln(w, "")
-	fmt.Fprintln(w, "include $(CLEAR_VARS)", " # java.robolectricTest")
-	fmt.Fprintln(w, "LOCAL_MODULE :=", name)
-	android.AndroidMkEmitAssignList(w, "LOCAL_JAVA_LIBRARIES", []string{module}, r.libs)
-	fmt.Fprintln(w, "LOCAL_TEST_PACKAGE :=", String(r.robolectricProperties.Instrumentation_for))
-	if r.roboSrcJar != nil {
-		fmt.Fprintln(w, "LOCAL_INSTRUMENT_SRCJARS :=", r.roboSrcJar.String())
-	}
-	android.AndroidMkEmitAssignList(w, "LOCAL_ROBOTEST_FILES", tests)
-	if t := r.robolectricProperties.Test_options.Timeout; t != nil {
-		fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
-	}
-	if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
-		fmt.Fprintf(w, "-include prebuilts/misc/common/robolectric/%s/run_robotests.mk\n", v)
-	} else {
-		fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk")
-	}
-}
-
 // An android_robolectric_test module compiles tests against the Robolectric framework that can run on the local host
-// instead of on a device.  It also generates a rule with the name of the module prefixed with "Run" that can be
-// used to run the tests.  Running the tests with build rule will eventually be deprecated and replaced with atest.
-//
-// The test runner considers any file listed in srcs whose name ends with Test.java to be a test class, unless
-// it is named BaseRobolectricTest.java.  The path to the each source file must exactly match the package
-// name, or match the package name when the prefix "src/" is removed.
+// instead of on a device.
 func RobolectricTestFactory() android.Module {
 	module := &robolectricTest{}
 
diff --git a/java/robolectric_test.go b/java/robolectric_test.go
new file mode 100644
index 0000000..4775bac
--- /dev/null
+++ b/java/robolectric_test.go
@@ -0,0 +1,98 @@
+// 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 (
+	"runtime"
+	"testing"
+
+	"android/soong/android"
+)
+
+var prepareRobolectricRuntime = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		RegisterRobolectricBuildComponents(ctx)
+	}),
+	android.FixtureAddTextFile("robolectric/Android.bp", `
+	java_library {
+		name: "Robolectric_all-target_upstream",
+		srcs: ["Robo.java"]
+	}
+
+	java_library {
+		name: "mockito-robolectric-prebuilt",
+		srcs: ["Mockito.java"]
+	}
+
+	java_library {
+		name: "truth",
+		srcs: ["Truth.java"]
+	}
+
+	java_library {
+		name: "junitxml",
+		srcs: ["JUnitXml.java"]
+	}
+
+	java_library_host {
+		name: "robolectric-host-android_all",
+		srcs: ["Runtime.java"]
+	}
+
+	android_robolectric_runtimes {
+		name: "robolectric-android-all-prebuilts",
+		jars: ["android-all/Runtime.jar"],
+		lib: "robolectric-host-android_all",
+	}
+	`),
+)
+
+func TestRobolectricJniTest(t *testing.T) {
+	if runtime.GOOS != "linux" {
+		t.Skip("requires linux")
+	}
+
+	ctx := android.GroupFixturePreparers(
+		PrepareForIntegrationTestWithJava,
+		prepareRobolectricRuntime,
+	).RunTestWithBp(t, `
+	android_app {
+		name: "inst-target",
+		srcs: ["App.java"],
+		platform_apis: true,
+	}
+
+	cc_library_shared {
+		name: "jni-lib1",
+		host_supported: true,
+		srcs: ["jni.cpp"],
+	}
+
+	android_robolectric_test {
+		name: "robo-test",
+		instrumentation_for: "inst-target",
+		srcs: ["FooTest.java"],
+		jni_libs: [
+			"jni-lib1"
+		],
+	}
+	`)
+
+	CheckModuleHasDependency(t, ctx.TestContext, "robo-test", "android_common", "jni-lib1")
+
+	// Check that the .so files make it into the output.
+	module := ctx.ModuleForTests("robo-test", "android_common")
+	module.Output(installPathPrefix + "/robo-test/lib64/jni-lib1.so")
+}
diff --git a/java/rro.go b/java/rro.go
index 72170fc..8bb9be2 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -17,7 +17,11 @@
 // This file contains the module implementations for runtime_resource_overlay and
 // override_runtime_resource_overlay.
 
-import "android/soong/android"
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
+)
 
 func init() {
 	RegisterRuntimeResourceOverlayBuildComponents(android.InitRegistrationContext)
@@ -71,7 +75,7 @@
 	Min_sdk_version *string
 
 	// list of android_library modules whose resources are extracted and linked against statically
-	Static_libs []string
+	Static_libs proptools.Configurable[[]string]
 
 	// list of android_app modules whose resources are extracted and linked against
 	Resource_libs []string
@@ -120,7 +124,7 @@
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
 
-	ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...)
+	ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs.GetOrDefault(ctx, nil)...)
 	ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...)
 
 	for _, aconfig_declaration := range r.aaptProperties.Flags_packages {
@@ -150,12 +154,13 @@
 		aaptLinkFlags = append(aaptLinkFlags,
 			"--rename-overlay-category "+*r.overridableProperties.Category)
 	}
+	aconfigTextFilePaths := getAconfigFilePaths(ctx)
 	r.aapt.buildActions(ctx,
 		aaptBuildActionOptions{
 			sdkContext:                     r,
 			enforceDefaultTargetSdkVersion: false,
 			extraLinkFlags:                 aaptLinkFlags,
-			aconfigTextFiles:               getAconfigFilePaths(ctx),
+			aconfigTextFiles:               aconfigTextFilePaths,
 		},
 	)
 
@@ -176,6 +181,10 @@
 	partition := rroPartition(ctx)
 	r.installDir = android.PathForModuleInPartitionInstall(ctx, partition, "overlay", String(r.properties.Theme))
 	ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
+
+	android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{
+		AconfigTextFiles: aconfigTextFilePaths,
+	})
 }
 
 func (r *RuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
diff --git a/java/sdk.go b/java/sdk.go
index 72184cf..1beb7d5 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -280,7 +280,7 @@
 
 	ctx.VisitAllModules(func(module android.Module) {
 		// Collect dex jar paths for the modules listed above.
-		if j, ok := android.SingletonModuleProvider(ctx, module, JavaInfoProvider); ok {
+		if j, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			name := ctx.ModuleName(module)
 			if i := android.IndexList(name, stubsModules); i != -1 {
 				stubsJars[i] = j.HeaderJars
@@ -310,10 +310,12 @@
 
 			rule.Command().
 				Text("rm -f").Output(aidl)
+
 			rule.Command().
 				BuiltTool("sdkparcelables").
 				Input(jar).
-				Output(aidl)
+				Output(aidl).
+				Flag("--guarantee_stable")
 
 			aidls = append(aidls, aidl)
 		}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index e9fa83a..bb0d7d0 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -20,7 +20,6 @@
 	"path"
 	"path/filepath"
 	"reflect"
-	"regexp"
 	"sort"
 	"strings"
 	"sync"
@@ -118,9 +117,6 @@
 	// The tag to use to depend on the stubs source module (if separate from the API module).
 	stubsSourceTag scopeDependencyTag
 
-	// The tag to use to depend on the API file generating module (if separate from the stubs source module).
-	apiFileTag scopeDependencyTag
-
 	// The tag to use to depend on the stubs source and API module.
 	stubsSourceAndApiTag scopeDependencyTag
 
@@ -195,11 +191,6 @@
 		apiScope:         scope,
 		depInfoExtractor: (*scopePaths).extractStubsSourceInfoFromDep,
 	}
-	scope.apiFileTag = scopeDependencyTag{
-		name:             name + "-api",
-		apiScope:         scope,
-		depInfoExtractor: (*scopePaths).extractApiInfoFromDep,
-	}
 	scope.stubsSourceAndApiTag = scopeDependencyTag{
 		name:             name + "-stubs-source-and-api",
 		apiScope:         scope,
@@ -257,7 +248,7 @@
 	return scope.stubsLibraryModuleName(baseName) + ".from-text"
 }
 
-func (scope *apiScope) sourceStubLibraryModuleName(baseName string) string {
+func (scope *apiScope) sourceStubsLibraryModuleName(baseName string) string {
 	return scope.stubsLibraryModuleName(baseName) + ".from-source"
 }
 
@@ -277,10 +268,6 @@
 	return baseName + ".stubs.source" + scope.moduleSuffix
 }
 
-func (scope *apiScope) apiModuleName(baseName string) string {
-	return baseName + ".api" + scope.moduleSuffix
-}
-
 func (scope *apiScope) String() string {
 	return scope.name
 }
@@ -435,22 +422,10 @@
 		apiScopeModuleLib,
 		apiScopeSystemServer,
 	}
-	apiLibraryAdditionalProperties = map[string]struct {
-		FullApiSurfaceStubLib     string
-		AdditionalApiContribution string
-	}{
-		"legacy.i18n.module.platform.api": {
-			FullApiSurfaceStubLib:     "legacy.core.platform.api.stubs",
-			AdditionalApiContribution: "i18n.module.public.api.stubs.source.api.contribution",
-		},
-		"stable.i18n.module.platform.api": {
-			FullApiSurfaceStubLib:     "stable.core.platform.api.stubs",
-			AdditionalApiContribution: "i18n.module.public.api.stubs.source.api.contribution",
-		},
-		"conscrypt.module.platform.api": {
-			FullApiSurfaceStubLib:     "stable.core.platform.api.stubs",
-			AdditionalApiContribution: "conscrypt.module.public.api.stubs.source.api.contribution",
-		},
+	apiLibraryAdditionalProperties = map[string]string{
+		"legacy.i18n.module.platform.api": "i18n.module.public.api.stubs.source.api.contribution",
+		"stable.i18n.module.platform.api": "i18n.module.public.api.stubs.source.api.contribution",
+		"conscrypt.module.platform.api":   "conscrypt.module.public.api.stubs.source.api.contribution",
 	}
 )
 
@@ -546,9 +521,6 @@
 	// of the API.
 	Api_packages []string
 
-	// list of package names that must be hidden from the API
-	Hidden_api_packages []string
-
 	// the relative path to the directory containing the api specification files.
 	// Defaults to "api".
 	Api_dir *string
@@ -661,17 +633,14 @@
 		Legacy_errors_allowed *bool
 	}
 
-	// Determines if the module contributes to any api surfaces.
-	// This property should be set to true only if the module is listed under
-	// frameworks-base-api.bootclasspath in frameworks/base/api/Android.bp.
-	// Otherwise, this property should be set to false.
-	// Defaults to false.
-	Contribute_to_android_api *bool
-
 	// a list of aconfig_declarations module names that the stubs generated in this module
 	// depend on.
 	Aconfig_declarations []string
 
+	// Determines if the module generates the stubs from the api signature files
+	// instead of the source Java files. Defaults to true.
+	Build_from_text_stub *bool
+
 	// TODO: determines whether to create HTML doc or not
 	// Html_doc *bool
 }
@@ -804,12 +773,6 @@
 	return combinedError
 }
 
-func (paths *scopePaths) extractApiInfoFromDep(ctx android.ModuleContext, dep android.Module) error {
-	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) error {
-		return paths.extractApiInfoFromApiStubsProvider(provider, Everything)
-	})
-}
-
 func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider, stubsType StubsType) error {
 	stubsSrcJar, err := provider.StubsSrcJar(stubsType)
 	if err == nil {
@@ -819,22 +782,23 @@
 }
 
 func (paths *scopePaths) extractStubsSourceInfoFromDep(ctx android.ModuleContext, dep android.Module) error {
+	stubsType := Everything
+	if ctx.Config().ReleaseHiddenApiExportableStubs() {
+		stubsType = Exportable
+	}
 	return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) error {
-		return paths.extractStubsSourceInfoFromApiStubsProviders(provider, Everything)
+		return paths.extractStubsSourceInfoFromApiStubsProviders(provider, stubsType)
 	})
 }
 
 func (paths *scopePaths) extractStubsSourceAndApiInfoFromApiStubsProvider(ctx android.ModuleContext, dep android.Module) error {
+	stubsType := Everything
 	if ctx.Config().ReleaseHiddenApiExportableStubs() {
-		return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) error {
-			extractApiInfoErr := paths.extractApiInfoFromApiStubsProvider(provider, Exportable)
-			extractStubsSourceInfoErr := paths.extractStubsSourceInfoFromApiStubsProviders(provider, Exportable)
-			return errors.Join(extractApiInfoErr, extractStubsSourceInfoErr)
-		})
+		stubsType = Exportable
 	}
 	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) error {
-		extractApiInfoErr := paths.extractApiInfoFromApiStubsProvider(provider, Everything)
-		extractStubsSourceInfoErr := paths.extractStubsSourceInfoFromApiStubsProviders(provider, Everything)
+		extractApiInfoErr := paths.extractApiInfoFromApiStubsProvider(provider, stubsType)
+		extractStubsSourceInfoErr := paths.extractStubsSourceInfoFromApiStubsProviders(provider, stubsType)
 		return errors.Join(extractApiInfoErr, extractStubsSourceInfoErr)
 	})
 }
@@ -862,16 +826,6 @@
 }
 
 type commonToSdkLibraryAndImportProperties struct {
-	// The naming scheme to use for the components that this module creates.
-	//
-	// If not specified then it defaults to "default".
-	//
-	// This is a temporary mechanism to simplify conversion from separate modules for each
-	// component that follow a different naming pattern to the default one.
-	//
-	// TODO(b/155480189) - Remove once naming inconsistencies have been resolved.
-	Naming_scheme *string
-
 	// Specifies whether this module can be used as an Android shared library; defaults
 	// to true.
 	//
@@ -947,8 +901,6 @@
 
 	scopePaths map[*apiScope]*scopePaths
 
-	namingScheme sdkLibraryComponentNamingScheme
-
 	commonSdkLibraryProperties commonToSdkLibraryAndImportProperties
 
 	// Paths to commonSdkLibraryProperties.Doctag_files
@@ -976,15 +928,6 @@
 }
 
 func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool {
-	schemeProperty := proptools.StringDefault(c.commonSdkLibraryProperties.Naming_scheme, "default")
-	switch schemeProperty {
-	case "default":
-		c.namingScheme = &defaultNamingScheme{}
-	default:
-		ctx.PropertyErrorf("naming_scheme", "expected 'default' but was %q", schemeProperty)
-		return false
-	}
-
 	namePtr := proptools.StringPtr(c.module.RootLibraryName())
 	c.sdkLibraryComponentProperties.SdkLibraryName = namePtr
 
@@ -1027,41 +970,41 @@
 // Name of the java_library module that compiles the stubs source.
 func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
 	baseName := c.module.RootLibraryName()
-	return c.namingScheme.stubsLibraryModuleName(apiScope, baseName)
+	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 c.namingScheme.exportableStubsLibraryModuleName(apiScope, baseName)
+	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 c.namingScheme.stubsSourceModuleName(apiScope, baseName)
+	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 c.namingScheme.apiLibraryModuleName(apiScope, baseName)
+	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 c.namingScheme.sourceStubsLibraryModuleName(apiScope, baseName)
+	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 c.namingScheme.exportableSourceStubsLibraryModuleName(apiScope, baseName)
+	return apiScope.exportableSourceStubsLibraryModuleName(baseName)
 }
 
 // The component names for different outputs of the java_sdk_library.
@@ -1077,79 +1020,26 @@
 	annotationsComponentName = "annotations.zip"
 )
 
-// A regular expression to match tags that reference a specific stubs component.
-//
-// It will only match if given a valid scope and a valid component. It is verfy strict
-// to ensure it does not accidentally match a similar looking tag that should be processed
-// by the embedded Library.
-var tagSplitter = func() *regexp.Regexp {
-	// Given a list of literal string items returns a regular expression that will
-	// match any one of the items.
-	choice := func(items ...string) string {
-		return `\Q` + strings.Join(items, `\E|\Q`) + `\E`
+func (module *commonToSdkLibraryAndImport) setOutputFiles(ctx android.ModuleContext) {
+	if module.doctagPaths != nil {
+		ctx.SetOutputFiles(module.doctagPaths, ".doctags")
 	}
-
-	// Regular expression to match one of the scopes.
-	scopesRegexp := choice(allScopeNames...)
-
-	// Regular expression to match one of the components.
-	componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName, annotationsComponentName)
-
-	// Regular expression to match any combination of one scope and one component.
-	return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp))
-}()
-
-// For OutputFileProducer interface
-//
-// .<scope>.<component name>, for all ComponentNames (for example: .public.removed-api.txt)
-func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Paths, error) {
-	if groups := tagSplitter.FindStringSubmatch(tag); groups != nil {
-		scopeName := groups[1]
-		component := groups[2]
-
-		if scope, ok := scopeByName[scopeName]; ok {
-			paths := c.findScopePaths(scope)
-			if paths == nil {
-				return nil, fmt.Errorf("%q does not provide api scope %s", c.module.RootLibraryName(), scopeName)
-			}
-
-			switch component {
-			case stubsSourceComponentName:
-				if paths.stubsSrcJar.Valid() {
-					return android.Paths{paths.stubsSrcJar.Path()}, nil
-				}
-
-			case apiTxtComponentName:
-				if paths.currentApiFilePath.Valid() {
-					return android.Paths{paths.currentApiFilePath.Path()}, nil
-				}
-
-			case removedApiTxtComponentName:
-				if paths.removedApiFilePath.Valid() {
-					return android.Paths{paths.removedApiFilePath.Path()}, nil
-				}
-
-			case annotationsComponentName:
-				if paths.annotationsZip.Valid() {
-					return android.Paths{paths.annotationsZip.Path()}, nil
-				}
-			}
-
-			return nil, fmt.Errorf("%s not available for api scope %s", component, scopeName)
-		} else {
-			return nil, fmt.Errorf("unknown scope %s in %s", scope, tag)
+	for _, scopeName := range android.SortedKeys(scopeByName) {
+		paths := module.findScopePaths(scopeByName[scopeName])
+		if paths == nil {
+			continue
 		}
-
-	} else {
-		switch tag {
-		case ".doctags":
-			if c.doctagPaths != nil {
-				return c.doctagPaths, nil
-			} else {
-				return nil, fmt.Errorf("no doctag_files specified on %s", c.module.RootLibraryName())
+		componentToOutput := map[string]android.OptionalPath{
+			stubsSourceComponentName:   paths.stubsSrcJar,
+			apiTxtComponentName:        paths.currentApiFilePath,
+			removedApiTxtComponentName: paths.removedApiFilePath,
+			annotationsComponentName:   paths.annotationsZip,
+		}
+		for _, component := range android.SortedKeys(componentToOutput) {
+			if componentToOutput[component].Valid() {
+				ctx.SetOutputFiles(android.Paths{componentToOutput[component].Path()}, "."+scopeName+"."+component)
 			}
 		}
-		return nil, nil
 	}
 }
 
@@ -1188,21 +1078,6 @@
 	return nil
 }
 
-func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-
-	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
-	if !sdkVersion.ApiLevel.IsPreview() {
-		return PrebuiltJars(ctx, c.module.RootLibraryName(), sdkVersion)
-	}
-
-	paths := c.selectScopePaths(ctx, sdkVersion.Kind)
-	if paths == nil {
-		return nil
-	}
-
-	return paths.stubsHeaderPath
-}
-
 // 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
@@ -1369,12 +1244,6 @@
 type SdkLibraryDependency interface {
 	SdkLibraryComponentDependency
 
-	// Get the header jars appropriate for the supplied sdk_version.
-	//
-	// These are turbine generated jars so they only change if the externals of the
-	// class changes but it does not contain and implementation or JavaDoc.
-	SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths
-
 	// 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.
@@ -1391,9 +1260,36 @@
 	// 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
+}
+
+var SdkLibraryInfoProvider = blueprint.NewProvider[SdkLibraryInfo]()
+
+func getGeneratingLibs(ctx android.ModuleContext, sdkVersion android.SdkSpec, sdkLibraryModuleName string, sdkInfo SdkLibraryInfo) []string {
+	apiLevel := sdkVersion.ApiLevel
+	if apiLevel.IsPreview() {
+		return sdkInfo.GeneratingLibs
+	}
+
+	generatingPrebuilts := []string{}
+	for _, apiScope := range AllApiScopes {
+		scopePrebuiltModuleName := prebuiltApiModuleName("sdk", sdkLibraryModuleName, apiScope.name, apiLevel.String())
+		if ctx.OtherModuleExists(scopePrebuiltModuleName) {
+			generatingPrebuilts = append(generatingPrebuilts, scopePrebuiltModuleName)
+		}
+	}
+	return generatingPrebuilts
+}
+
 type SdkLibrary struct {
 	Library
 
@@ -1563,6 +1459,13 @@
 
 // Add other dependencies as normal.
 func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// If the module does not create an implementation library or defaults to stubs,
+	// mark the top level sdk library as stubs module as the module will provide stubs via
+	// "magic" when listed as a dependency in the Android.bp files.
+	notCreateImplLib := proptools.Bool(module.sdkLibraryProperties.Api_only)
+	preferStubs := proptools.Bool(module.sdkLibraryProperties.Default_to_stubs)
+	module.properties.Is_stubs_module = proptools.BoolPtr(notCreateImplLib || preferStubs)
+
 	var missingApiModules []string
 	for _, apiScope := range module.getGeneratedApiScopes(ctx) {
 		if apiScope.unstable {
@@ -1592,20 +1495,6 @@
 	}
 }
 
-func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
-	paths, err := module.commonOutputFiles(tag)
-	if paths != nil || err != nil {
-		return paths, err
-	}
-	if module.requiresRuntimeImplementationLibrary() {
-		return module.implLibraryModule.OutputFiles(tag)
-	}
-	if tag == "" {
-		return nil, nil
-	}
-	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
-
 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if disableSourceApexVariant(ctx) {
 		// Prebuilts are active, do not create the installation rules for the source javalib.
@@ -1640,6 +1529,8 @@
 			scopeTag.extractDepInfo(ctx, to, scopePaths)
 
 			exportedComponents[ctx.OtherModuleName(to)] = struct{}{}
+
+			ctx.Phony(ctx.ModuleName(), scopePaths.stubsHeaderPath...)
 		}
 
 		if tag == implLibraryTag {
@@ -1679,11 +1570,17 @@
 		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 !module.Host() {
 			module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile
 		}
 
+		if installFilesInfo, ok := android.OtherModuleProvider(ctx, module.implLibraryModule, android.InstallFilesProvider); ok {
+			if installFilesInfo.CheckbuildTarget != nil {
+				ctx.CheckbuildFile(installFilesInfo.CheckbuildTarget)
+			}
+		}
 		android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: module.implLibraryModule.uniqueSrcFiles.Strings()})
 	}
 
@@ -1714,6 +1611,23 @@
 		}
 	}
 	android.SetProvider(ctx, android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo})
+	module.setOutputFiles(ctx)
+
+	var generatingLibs []string
+	for _, apiScope := range AllApiScopes {
+		if _, ok := module.scopePaths[apiScope]; ok {
+			generatingLibs = append(generatingLibs, module.stubsLibraryModuleName(apiScope))
+		}
+	}
+
+	if module.requiresRuntimeImplementationLibrary() && module.implLibraryModule != nil {
+		generatingLibs = append(generatingLibs, module.implLibraryModuleName())
+		setOutputFiles(ctx, module.implLibraryModule.Module)
+	}
+
+	android.SetProvider(ctx, SdkLibraryInfoProvider, SdkLibraryInfo{
+		GeneratingLibs: generatingLibs,
+	})
 }
 
 func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall {
@@ -1796,30 +1710,13 @@
 	return latestPrebuiltApiModuleName(module.distStem()+"-incompatibilities", apiScope)
 }
 
-func (module *SdkLibrary) contributesToApiSurface(c android.Config) bool {
-	_, exists := c.GetApiLibraries()[module.Name()]
-	return exists
-}
-
-// The listed modules are the special java_sdk_libraries where apiScope.kind do not match the
-// api surface that the module contribute to. For example, the public droidstubs and java_library
-// do not contribute to the public api surface, but contributes to the core platform api surface.
-// This method returns the full api surface stub lib that
-// the generated java_api_library should depend on.
-func (module *SdkLibrary) alternativeFullApiSurfaceStubLib() string {
-	if val, ok := apiLibraryAdditionalProperties[module.Name()]; ok {
-		return val.FullApiSurfaceStubLib
-	}
-	return ""
-}
-
 // The listed modules' stubs contents do not match the corresponding txt files,
 // but require additional api contributions to generate the full stubs.
 // This method returns the name of the additional api contribution module
 // for corresponding sdk_library modules.
 func (module *SdkLibrary) apiLibraryAdditionalApiContribution() string {
 	if val, ok := apiLibraryAdditionalProperties[module.Name()]; ok {
-		return val.AdditionalApiContribution
+		return val
 	}
 	return ""
 }
@@ -1841,20 +1738,24 @@
 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    []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: append(module.properties.Static_libs, module.sdkLibraryProperties.Impl_only_static_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(),
@@ -1878,6 +1779,7 @@
 
 type libraryProperties struct {
 	Name           *string
+	Enabled        proptools.Configurable[bool]
 	Visibility     []string
 	Srcs           []string
 	Installable    *bool
@@ -1898,11 +1800,13 @@
 		Dir     *string
 		Tag     *string
 	}
-	Is_stubs_module *bool
+	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)
@@ -1924,6 +1828,7 @@
 	// 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
 }
@@ -1952,13 +1857,14 @@
 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                             []string
+		Libs                             proptools.Configurable[[]string]
 		Output_javadoc_comments          *bool
 		Arg_files                        []string
 		Args                             *string
@@ -1993,6 +1899,7 @@
 	// * 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...)
@@ -2002,10 +1909,11 @@
 	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 = module.properties.Libs
-	props.Libs = append(props.Libs, module.properties.Static_libs...)
-	props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...)
-	props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...)
+	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
@@ -2019,10 +1927,6 @@
 	if len(module.sdkLibraryProperties.Api_packages) != 0 {
 		droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
 	}
-	if len(module.sdkLibraryProperties.Hidden_api_packages) != 0 {
-		droidstubsArgs = append(droidstubsArgs,
-			android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package "))
-	}
 	droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
 	disabledWarnings := []string{"HiddenSuperclass"}
 	if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) {
@@ -2118,20 +2022,23 @@
 	mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx)
 }
 
-func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope, alternativeFullApiSurfaceStub string) {
+func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
 	props := struct {
-		Name                  *string
-		Visibility            []string
-		Api_contributions     []string
-		Libs                  []string
-		Static_libs           []string
-		Full_api_surface_stub *string
-		System_modules        *string
-		Enable_validation     *bool
-		Stubs_type            *string
+		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{}
@@ -2152,40 +2059,37 @@
 	}
 
 	props.Api_contributions = apiContributions
-	props.Libs = module.properties.Libs
-	props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...)
-	props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...)
-	props.Libs = append(props.Libs, "stub-annotations")
+
+	// 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.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName())
-	if alternativeFullApiSurfaceStub != "" {
-		props.Full_api_surface_stub = proptools.StringPtr(alternativeFullApiSurfaceStub)
-	}
-
-	// android_module_lib_stubs_current.from-text only comprises api contributions from art, conscrypt and i18n.
-	// Thus, replace with android_module_lib_stubs_current_full.from-text, which comprises every api domains.
-	if apiScope.kind == android.SdkModule {
-		props.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + "_full.from-text")
-	}
-
-	// java_sdk_library modules that set sdk_version as none does not depend on other api
-	// domains. Therefore, java_api_library created from such modules should not depend on
-	// full_api_surface_stubs but create and compile stubs by the java_api_library module
-	// itself.
-	if module.SdkVersion(mctx).Kind == android.SdkNone {
-		props.Full_api_surface_stub = nil
-	}
 
 	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)
@@ -2199,18 +2103,21 @@
 	}
 	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, contributesToApiSurface bool) {
+	mctx android.DefaultableHookContext, apiScope *apiScope) {
 
 	// Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false
 	doDist := !mctx.Config().ReleaseHiddenApiExportableStubs()
@@ -2219,7 +2126,7 @@
 
 	// Add the stub compiling java_library/java_api_library as static lib based on build config
 	staticLib := module.sourceStubsLibraryModuleName(apiScope)
-	if mctx.Config().BuildFromTextStub() && contributesToApiSurface {
+	if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
 		staticLib = module.apiLibraryModuleName(apiScope)
 	}
 	props.Static_libs = append(props.Static_libs, staticLib)
@@ -2262,8 +2169,8 @@
 	return module.uniqueApexVariations()
 }
 
-func (module *SdkLibrary) ContributeToApi() bool {
-	return proptools.BoolDefault(module.sdkLibraryProperties.Contribute_to_android_api, false)
+func (module *SdkLibrary) ModuleBuildFromTextStubs() bool {
+	return proptools.BoolDefault(module.sdkLibraryProperties.Build_from_text_stub, true)
 }
 
 // Creates the xml file that publicizes the runtime library
@@ -2275,6 +2182,7 @@
 	}
 	props := struct {
 		Name                      *string
+		Enabled                   proptools.Configurable[bool]
 		Lib_name                  *string
 		Apex_available            []string
 		On_bootclasspath_since    *string
@@ -2285,6 +2193,7 @@
 		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,
@@ -2298,72 +2207,6 @@
 	mctx.CreateModule(sdkLibraryXmlFactory, &props)
 }
 
-func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s android.SdkSpec) android.Paths {
-	var ver android.ApiLevel
-	var kind android.SdkKind
-	if s.UsePrebuilt(ctx) {
-		ver = s.ApiLevel
-		kind = s.Kind
-	} else {
-		// We don't have prebuilt SDK for the specific sdkVersion.
-		// Instead of breaking the build, fallback to use "system_current"
-		ver = android.FutureApiLevel
-		kind = android.SdkSystem
-	}
-
-	dir := filepath.Join("prebuilts", "sdk", ver.String(), kind.String())
-	jar := filepath.Join(dir, baseName+".jar")
-	jarPath := android.ExistentPathForSource(ctx, jar)
-	if !jarPath.Valid() {
-		if ctx.Config().AllowMissingDependencies() {
-			return android.Paths{android.PathForSource(ctx, jar)}
-		} else {
-			ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", s.Raw, jar)
-		}
-		return nil
-	}
-	return android.Paths{jarPath.Path()}
-}
-
-// Check to see if the other module is within the same set of named APEXes as this module.
-//
-// If either this or the other module are on the platform then this will return
-// false.
-func withinSameApexesAs(ctx android.BaseModuleContext, other android.Module) bool {
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
-	otherApexInfo, _ := android.OtherModuleProvider(ctx, other, android.ApexInfoProvider)
-	return len(otherApexInfo.InApexVariants) > 0 && reflect.DeepEqual(apexInfo.InApexVariants, otherApexInfo.InApexVariants)
-}
-
-func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-	// If the client doesn't set sdk_version, but if this library prefers stubs over
-	// the impl library, let's provide the widest API surface possible. To do so,
-	// force override sdk_version to module_current so that the closest possible API
-	// surface could be found in selectHeaderJarsForSdkVersion
-	if module.defaultsToStubs() && !sdkVersion.Specified() {
-		sdkVersion = android.SdkSpecFrom(ctx, "module_current")
-	}
-
-	// Only provide access to the implementation library if it is actually built.
-	if module.requiresRuntimeImplementationLibrary() {
-		// Check any special cases for java_sdk_library.
-		//
-		// Only allow access to the implementation library in the following condition:
-		// * No sdk_version specified on the referencing module.
-		// * The referencing module is in the same apex as this.
-		if sdkVersion.Kind == android.SdkPrivate || withinSameApexesAs(ctx, module) {
-			return module.implLibraryHeaderJars
-		}
-	}
-
-	return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion)
-}
-
-// to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-	return module.sdkJars(ctx, sdkVersion)
-}
-
 var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries")
 
 func javaSdkLibraries(config android.Config) *[]string {
@@ -2380,11 +2223,6 @@
 // runtime libs and xml file. If requested, the stubs and docs are created twice
 // once for public API level and once for system API level
 func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookContext) {
-	// If the module has been disabled then don't create any child modules.
-	if !module.Enabled(mctx) {
-		return
-	}
-
 	if len(module.properties.Srcs) == 0 {
 		mctx.PropertyErrorf("srcs", "java_sdk_library must specify srcs")
 		return
@@ -2439,16 +2277,10 @@
 		module.createStubsLibrary(mctx, scope)
 		module.createExportableStubsLibrary(mctx, scope)
 
-		alternativeFullApiSurfaceStubLib := ""
-		if scope == apiScopePublic {
-			alternativeFullApiSurfaceStubLib = module.alternativeFullApiSurfaceStubLib()
+		if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
+			module.createApiLibrary(mctx, scope)
 		}
-		contributesToApiSurface := module.contributesToApiSurface(mctx.Config()) || alternativeFullApiSurfaceStubLib != ""
-		if contributesToApiSurface {
-			module.createApiLibrary(mctx, scope, alternativeFullApiSurfaceStubLib)
-		}
-
-		module.createTopLevelStubsLibrary(mctx, scope, contributesToApiSurface)
+		module.createTopLevelStubsLibrary(mctx, scope)
 		module.createTopLevelExportableStubsLibrary(mctx, scope)
 	}
 
@@ -2476,7 +2308,7 @@
 
 	// Add the impl_only_libs and impl_only_static_libs *after* we're done using them in submodules.
 	module.properties.Libs = append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...)
-	module.properties.Static_libs = append(module.properties.Static_libs, module.sdkLibraryProperties.Impl_only_static_libs...)
+	module.properties.Static_libs.AppendSimpleValue(module.sdkLibraryProperties.Impl_only_static_libs)
 }
 
 func (module *SdkLibrary) InitSdkLibraryProperties() {
@@ -2493,84 +2325,25 @@
 	return !proptools.Bool(module.sdkLibraryProperties.Api_only)
 }
 
-func (module *SdkLibrary) defaultsToStubs() bool {
-	return proptools.Bool(module.sdkLibraryProperties.Default_to_stubs)
-}
-
-// Defines how to name the individual component modules the sdk library creates.
-type sdkLibraryComponentNamingScheme interface {
-	stubsLibraryModuleName(scope *apiScope, baseName string) string
-
-	stubsSourceModuleName(scope *apiScope, baseName string) string
-
-	apiLibraryModuleName(scope *apiScope, baseName string) string
-
-	sourceStubsLibraryModuleName(scope *apiScope, baseName string) string
-
-	exportableStubsLibraryModuleName(scope *apiScope, baseName string) string
-
-	exportableSourceStubsLibraryModuleName(scope *apiScope, baseName string) string
-}
-
-type defaultNamingScheme struct {
-}
-
-func (s *defaultNamingScheme) stubsLibraryModuleName(scope *apiScope, baseName string) string {
-	return scope.stubsLibraryModuleName(baseName)
-}
-
-func (s *defaultNamingScheme) stubsSourceModuleName(scope *apiScope, baseName string) string {
-	return scope.stubsSourceModuleName(baseName)
-}
-
-func (s *defaultNamingScheme) apiLibraryModuleName(scope *apiScope, baseName string) string {
-	return scope.apiLibraryModuleName(baseName)
-}
-
-func (s *defaultNamingScheme) sourceStubsLibraryModuleName(scope *apiScope, baseName string) string {
-	return scope.sourceStubLibraryModuleName(baseName)
-}
-
-func (s *defaultNamingScheme) exportableStubsLibraryModuleName(scope *apiScope, baseName string) string {
-	return scope.exportableStubsLibraryModuleName(baseName)
-}
-
-func (s *defaultNamingScheme) exportableSourceStubsLibraryModuleName(scope *apiScope, baseName string) string {
-	return scope.exportableSourceStubsLibraryModuleName(baseName)
-}
-
-var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
-
-func hasStubsLibrarySuffix(name string, apiScope *apiScope) bool {
-	return strings.HasSuffix(name, apiScope.stubsLibraryModuleNameSuffix()) ||
-		strings.HasSuffix(name, apiScope.exportableStubsLibraryModuleNameSuffix())
-}
-
-func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) {
-	name = strings.TrimSuffix(name, ".from-source")
-
-	// This suffix-based approach is fragile and could potentially mis-trigger.
-	// TODO(b/155164730): Clean this up when modules no longer reference sdk_lib stubs directly.
-	if hasStubsLibrarySuffix(name, apiScopePublic) {
-		if name == "hwbinder.stubs" || name == "libcore_private.stubs" {
-			// Due to a previous bug, these modules were not considered stubs, so we retain that.
-			return false, javaPlatform
-		}
+func moduleStubLinkType(j *Module) (stub bool, ret sdkLinkType) {
+	kind := android.ToSdkKind(proptools.String(j.properties.Stub_contributing_api))
+	switch kind {
+	case android.SdkPublic:
 		return true, javaSdk
-	}
-	if hasStubsLibrarySuffix(name, apiScopeSystem) {
+	case android.SdkSystem:
 		return true, javaSystem
-	}
-	if hasStubsLibrarySuffix(name, apiScopeModuleLib) {
+	case android.SdkModule:
 		return true, javaModule
-	}
-	if hasStubsLibrarySuffix(name, apiScopeTest) {
+	case android.SdkTest:
 		return true, javaSystem
-	}
-	if hasStubsLibrarySuffix(name, apiScopeSystemServer) {
+	case android.SdkSystemServer:
 		return true, javaSystemServer
+	// Default value for all modules other than java_sdk_library-generated stub submodules
+	case android.SdkInvalid:
+		return false, javaPlatform
+	default:
+		panic(fmt.Sprintf("stub_contributing_api set as an unsupported sdk kind %s", kind.String()))
 	}
-	return false, javaPlatform
 }
 
 // java_sdk_library is a special Java library that provides optional platform APIs to apps.
@@ -2949,18 +2722,6 @@
 
 var _ hiddenAPIModule = (*SdkLibraryImport)(nil)
 
-func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) {
-	paths, err := module.commonOutputFiles(tag)
-	if paths != nil || err != nil {
-		return paths, err
-	}
-	if module.implLibraryModule != nil {
-		return module.implLibraryModule.OutputFiles(tag)
-	} else {
-		return nil, nil
-	}
-}
-
 func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	module.generateCommonBuildActions(ctx)
 
@@ -3007,64 +2768,36 @@
 	}
 
 	if ctx.Device() {
-		// If this is a variant created for a prebuilt_apex then use the dex implementation jar
-		// obtained from the associated deapexer module.
+		// Shared libraries deapexed from prebuilt apexes are no longer supported.
+		// Set the dexJarBuildPath to a fake path.
+		// This allows soong analysis pass, but will be an error during ninja execution if there are
+		// any rdeps.
 		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 		if ai.ForPrebuiltApex {
-			// Get the path of the dex implementation jar from the `deapexer` module.
-			di, err := android.FindDeapexerProviderForModule(ctx)
-			if err != nil {
-				// An error was found, possibly due to multiple apexes in the tree that export this library
-				// Defer the error till a client tries to call DexJarBuildPath
-				module.dexJarFileErr = err
-				module.initHiddenAPIError(err)
-				return
-			}
-			dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(module.BaseModuleName())
-			if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil {
-				dexJarFile := makeDexJarPathFromPath(dexOutputPath)
-				module.dexJarFile = dexJarFile
-				installPath := android.PathForModuleInPartitionInstall(
-					ctx, "apex", ai.ApexVariationName, dexJarFileApexRootRelative)
-				module.installFile = installPath
-				module.initHiddenAPI(ctx, dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
-
-				module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), installPath)
-				module.dexpreopter.isSDKLibrary = true
-				module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &module.dexpreopter)
-
-				if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil {
-					module.dexpreopter.inputProfilePathOnHost = profilePath
-				}
-			} else {
-				// This should never happen as a variant for a prebuilt_apex is only created if the
-				// prebuilt_apex has been configured to export the java library dex file.
-				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName())
-			}
-		}
-	}
-}
-
-func (module *SdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths {
-
-	// For consistency with SdkLibrary make the implementation jar available to libraries that
-	// are within the same APEX.
-	implLibraryModule := module.implLibraryModule
-	if implLibraryModule != nil && withinSameApexesAs(ctx, module) {
-		if headerJars {
-			return implLibraryModule.HeaderJars()
-		} else {
-			return implLibraryModule.ImplementationJars()
+			module.dexJarFile = makeDexJarPathFromPath(android.PathForModuleInstall(ctx, "intentionally_no_longer_supported"))
+			module.initHiddenAPI(ctx, module.dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
 		}
 	}
 
-	return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion)
-}
+	var generatingLibs []string
+	for _, apiScope := range AllApiScopes {
+		if scopeProperties, ok := module.scopeProperties[apiScope]; ok {
+			if len(scopeProperties.Jars) == 0 {
+				continue
+			}
+			generatingLibs = append(generatingLibs, module.stubsLibraryModuleName(apiScope))
+		}
+	}
 
-// to satisfy SdkLibraryDependency interface
-func (module *SdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-	// This module is just a wrapper for the prebuilt stubs.
-	return module.sdkJars(ctx, sdkVersion, true)
+	module.setOutputFiles(ctx)
+	if module.implLibraryModule != nil {
+		generatingLibs = append(generatingLibs, module.implLibraryModuleName())
+		setOutputFiles(ctx, module.implLibraryModule.Module)
+	}
+
+	android.SetProvider(ctx, SdkLibraryInfoProvider, SdkLibraryInfo{
+		GeneratingLibs: generatingLibs,
+	})
 }
 
 // to satisfy UsesLibraryDependency interface
@@ -3279,7 +3012,7 @@
 		// 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.ApexVariationName, implName)
+		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName)
 	}
 	partition := "system"
 	if module.SocSpecific() {
@@ -3630,7 +3363,6 @@
 		}
 	}
 
-	s.Naming_scheme = sdk.commonSdkLibraryProperties.Naming_scheme
 	s.Shared_library = proptools.BoolPtr(sdk.sharedLibrary())
 	s.Compile_dex = sdk.dexProperties.Compile_dex
 	s.Doctag_paths = sdk.doctagPaths
@@ -3727,3 +3459,19 @@
 		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_test.go b/java/sdk_library_test.go
index 5f0c174..6031d72 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -22,8 +22,6 @@
 	"testing"
 
 	"android/soong/android"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func TestJavaSdkLibrary(t *testing.T) {
@@ -35,14 +33,7 @@
 			"29": {"foo"},
 			"30": {"bar", "barney", "baz", "betty", "foo", "fred", "quuz", "wilma"},
 		}),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		droiddoc_exported_dir {
 			name: "droiddoc-templates-sdk",
@@ -62,7 +53,7 @@
 		java_library {
 			name: "baz",
 			srcs: ["c.java"],
-			libs: ["foo", "bar.stubs"],
+			libs: ["foo.stubs.system", "bar.stubs"],
 			sdk_version: "system_current",
 		}
 		java_sdk_library {
@@ -99,25 +90,25 @@
 		java_library {
 		    name: "qux",
 		    srcs: ["c.java"],
-		    libs: ["baz", "fred", "quuz.stubs", "wilma", "barney", "betty"],
+		    libs: ["baz", "fred.stubs", "quuz.stubs", "wilma.stubs", "barney.stubs.system", "betty.stubs.system"],
 		    sdk_version: "system_current",
 		}
 		java_library {
 			name: "baz-test",
 			srcs: ["c.java"],
-			libs: ["foo"],
+			libs: ["foo.stubs.test"],
 			sdk_version: "test_current",
 		}
 		java_library {
 			name: "baz-29",
 			srcs: ["c.java"],
-			libs: ["foo"],
+			libs: ["sdk_system_29_foo"],
 			sdk_version: "system_29",
 		}
 		java_library {
 			name: "baz-module-30",
 			srcs: ["c.java"],
-			libs: ["foo"],
+			libs: ["sdk_module-lib_30_foo"],
 			sdk_version: "module_30",
 		}
 	`)
@@ -137,7 +128,7 @@
 	result.ModuleForTests("foo.api.system.28", "")
 	result.ModuleForTests("foo.api.test.28", "")
 
-	exportedComponentsInfo, _ := android.SingletonModuleProvider(result, foo.Module(), android.ExportedComponentsInfoProvider)
+	exportedComponentsInfo, _ := android.OtherModuleProvider(result, foo.Module(), android.ExportedComponentsInfoProvider)
 	expectedFooExportedComponents := []string{
 		"foo-removed.api.combined.public.latest",
 		"foo-removed.api.combined.system.latest",
@@ -169,11 +160,11 @@
 
 	baz29Javac := result.ModuleForTests("baz-29", "android_common").Rule("javac")
 	// tests if baz-29 is actually linked to the system 29 stubs lib
-	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/29/system/foo.jar")
+	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/sdk_system_29_foo/android_common/combined/sdk_system_29_foo.jar")
 
 	bazModule30Javac := result.ModuleForTests("baz-module-30", "android_common").Rule("javac")
 	// tests if "baz-module-30" is actually linked to the module 30 stubs lib
-	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/30/module-lib/foo.jar")
+	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/sdk_module-lib_30_foo/android_common/combined/sdk_module-lib_30_foo.jar")
 
 	// test if baz has exported SDK lib names foo and bar to qux
 	qux := result.ModuleForTests("qux", "android_common")
@@ -429,7 +420,7 @@
 	for _, expectation := range expectations {
 		verify("sdklib.impl", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined)
 
-		stubName := apiScopePublic.sourceStubLibraryModuleName("sdklib")
+		stubName := apiScopePublic.sourceStubsLibraryModuleName("sdklib")
 		verify(stubName, expectation.lib, expectation.on_stub_classpath, expectation.in_stub_combined)
 	}
 }
@@ -452,7 +443,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["b.java"],
-			libs: ["foo"],
+			libs: ["foo.stubs"],
 		}
 		`)
 
@@ -492,7 +483,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": failed to get output file from module "foo" at tag ".public.annotations.zip": annotations.zip not available for api scope public`)).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported module reference tag ".public.annotations.zip"`)).
 		RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
@@ -517,7 +508,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"foo" does not provide api scope system`)).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported module reference tag ".system.stubs.source"`)).
 		RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
@@ -540,11 +531,7 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
@@ -606,7 +593,7 @@
 
 	t.Run("stubs.source", func(t *testing.T) {
 		prepareForJavaTest.
-			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`stubs.source not available for api scope public`)).
+			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.stubs.source"`)).
 			RunTestWithBp(t, bp+`
 				java_library {
 					name: "bar",
@@ -621,7 +608,7 @@
 
 	t.Run("api.txt", func(t *testing.T) {
 		prepareForJavaTest.
-			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`api.txt not available for api scope public`)).
+			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.api.txt"`)).
 			RunTestWithBp(t, bp+`
 				java_library {
 					name: "bar",
@@ -635,7 +622,7 @@
 
 	t.Run("removed-api.txt", func(t *testing.T) {
 		prepareForJavaTest.
-			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`removed-api.txt not available for api scope public`)).
+			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.removed-api.txt"`)).
 			RunTestWithBp(t, bp+`
 				java_library {
 					name: "bar",
@@ -774,7 +761,7 @@
 		java_library {
 			name: "bar",
 			srcs: ["a.java"],
-			libs: ["foo-public", "foo-system", "foo-module-lib", "foo-system-server"],
+			libs: ["foo-public.stubs", "foo-system.stubs.system", "foo-module-lib.stubs.module_lib", "foo-system-server.stubs.system_server"],
 			sdk_version: "system_server_current",
 		}
 		`)
@@ -800,102 +787,26 @@
 	}
 }
 
-func TestJavaSdkLibrary_MissingScope(t *testing.T) {
-	prepareForJavaTest.
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)).
-		RunTestWithBp(t, `
-			java_sdk_library {
-				name: "foo",
-				srcs: ["a.java"],
-				public: {
-					enabled: false,
-				},
-			}
-
-			java_library {
-				name: "baz",
-				srcs: ["a.java"],
-				libs: ["foo"],
-				sdk_version: "module_current",
-			}
-		`)
-}
-
-func TestJavaSdkLibrary_FallbackScope(t *testing.T) {
-	android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java"],
-			system: {
-				enabled: true,
-			},
-		}
-
-		java_library {
-			name: "baz",
-			srcs: ["a.java"],
-			libs: ["foo"],
-			// foo does not have module-lib scope so it should fallback to system
-			sdk_version: "module_current",
-		}
-		`)
-}
-
-func TestJavaSdkLibrary_DefaultToStubs(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java"],
-			system: {
-				enabled: true,
-			},
-			default_to_stubs: true,
-		}
-
-		java_library {
-			name: "baz",
-			srcs: ["a.java"],
-			libs: ["foo"],
-			// does not have sdk_version set, should fallback to module,
-			// which will then fallback to system because the module scope
-			// is not enabled.
-		}
-		`)
-	// The baz library should depend on the system stubs jar.
-	bazLibrary := result.ModuleForTests("baz", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
-		t.Errorf("expected %q, found %#q", expected, actual)
-	}
-}
-
 func TestJavaSdkLibraryImport(t *testing.T) {
 	result := prepareForJavaTest.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
-			libs: ["sdklib"],
+			libs: ["sdklib.stubs"],
 			sdk_version: "current",
 		}
 
 		java_library {
 			name: "foo.system",
 			srcs: ["a.java"],
-			libs: ["sdklib"],
+			libs: ["sdklib.stubs.system"],
 			sdk_version: "system_current",
 		}
 
 		java_library {
 			name: "foo.test",
 			srcs: ["a.java"],
-			libs: ["sdklib"],
+			libs: ["sdklib.stubs.test"],
 			sdk_version: "test_current",
 		}
 
@@ -918,7 +829,7 @@
 		fooModule := result.ModuleForTests("foo"+scope, "android_common")
 		javac := fooModule.Rule("javac")
 
-		sdklibStubsJar := result.ModuleForTests("sdklib.stubs"+scope, "android_common").Rule("combineJar").Output
+		sdklibStubsJar := result.ModuleForTests("sdklib.stubs"+scope, "android_common").Output("combined/sdklib.stubs" + scope + ".jar").Output
 		android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], sdklibStubsJar.String())
 	}
 
@@ -937,11 +848,7 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
@@ -990,11 +897,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
 		preparer,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
@@ -1036,7 +939,7 @@
 		java_library {
 			name: "public",
 			srcs: ["a.java"],
-			libs: ["sdklib"],
+			libs: ["sdklib.stubs"],
 			sdk_version: "current",
 		}
 		`)
@@ -1186,11 +1089,7 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib.source_preferred_using_legacy_flags", "sdklib.prebuilt_preferred_using_legacy_flags"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_mainline_module_contributions"),
 	).RunTestWithBp(t, bp)
 
 	// Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
@@ -1213,155 +1112,6 @@
 	}
 }
 
-func TestJavaSdkLibraryEnforce(t *testing.T) {
-	partitionToBpOption := func(partition string) string {
-		switch partition {
-		case "system":
-			return ""
-		case "vendor":
-			return "soc_specific: true,"
-		case "product":
-			return "product_specific: true,"
-		default:
-			panic("Invalid partition group name: " + partition)
-		}
-	}
-
-	type testConfigInfo struct {
-		libraryType                string
-		fromPartition              string
-		toPartition                string
-		enforceProductInterface    bool
-		enforceJavaSdkLibraryCheck bool
-		allowList                  []string
-	}
-
-	createPreparer := func(info testConfigInfo) android.FixturePreparer {
-		bpFileTemplate := `
-			java_library {
-				name: "foo",
-				srcs: ["foo.java"],
-				libs: ["bar"],
-				sdk_version: "current",
-				%s
-			}
-
-			%s {
-				name: "bar",
-				srcs: ["bar.java"],
-				sdk_version: "current",
-				%s
-			}
-		`
-
-		bpFile := fmt.Sprintf(bpFileTemplate,
-			partitionToBpOption(info.fromPartition),
-			info.libraryType,
-			partitionToBpOption(info.toPartition))
-
-		return android.GroupFixturePreparers(
-			PrepareForTestWithJavaSdkLibraryFiles,
-			FixtureWithLastReleaseApis("bar"),
-			android.FixtureWithRootAndroidBp(bpFile),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface)
-				variables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck)
-				variables.InterPartitionJavaLibraryAllowList = info.allowList
-			}),
-		)
-	}
-
-	runTest := func(t *testing.T, info testConfigInfo, expectedErrorPattern string) {
-		t.Run(fmt.Sprintf("%v", info), func(t *testing.T) {
-			errorHandler := android.FixtureExpectsNoErrors
-			if expectedErrorPattern != "" {
-				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorPattern)
-			}
-			android.GroupFixturePreparers(
-				prepareForJavaTest,
-				createPreparer(info),
-			).
-				ExtendWithErrorHandler(errorHandler).
-				RunTest(t)
-		})
-	}
-
-	errorMessage := "is not allowed across the partitions"
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: false,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceProductInterface:    false,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, errorMessage)
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "vendor",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, errorMessage)
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "vendor",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-		allowList:                  []string{"bar"},
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "vendor",
-		toPartition:                "product",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, errorMessage)
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_sdk_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_sdk_library",
-		fromPartition:              "vendor",
-		toPartition:                "system",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_sdk_library",
-		fromPartition:              "vendor",
-		toPartition:                "product",
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-}
-
 func TestJavaSdkLibraryDist(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaBuildComponents,
@@ -1372,11 +1122,7 @@
 			"sdklib_group_foo",
 			"sdklib_owner_foo",
 			"foo"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib_no_group",
@@ -1555,7 +1301,8 @@
 
 	// The foo.stubs.source should depend on bar-lib
 	fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs)
-	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs, "bar-lib")
+	eval := fooStubsSources.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs.GetOrDefault(eval, nil), "bar-lib")
 }
 
 func TestJavaSdkLibrary_Scope_Libs_PassedToDroidstubs(t *testing.T) {
@@ -1581,7 +1328,8 @@
 
 	// The foo.stubs.source should depend on bar-lib
 	fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs)
-	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs, "bar-lib")
+	eval := fooStubsSources.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs.GetOrDefault(eval, nil), "bar-lib")
 }
 
 func TestJavaSdkLibrary_ApiLibrary(t *testing.T) {
@@ -1589,9 +1337,6 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
@@ -1610,36 +1355,30 @@
 	`)
 
 	testCases := []struct {
-		scope              *apiScope
-		apiContributions   []string
-		fullApiSurfaceStub string
+		scope            *apiScope
+		apiContributions []string
 	}{
 		{
-			scope:              apiScopePublic,
-			apiContributions:   []string{"foo.stubs.source.api.contribution"},
-			fullApiSurfaceStub: "android_stubs_current",
+			scope:            apiScopePublic,
+			apiContributions: []string{"foo.stubs.source.api.contribution"},
 		},
 		{
-			scope:              apiScopeSystem,
-			apiContributions:   []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
-			fullApiSurfaceStub: "android_system_stubs_current",
+			scope:            apiScopeSystem,
+			apiContributions: []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
 		},
 		{
-			scope:              apiScopeTest,
-			apiContributions:   []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
-			fullApiSurfaceStub: "android_test_stubs_current",
+			scope:            apiScopeTest,
+			apiContributions: []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
 		},
 		{
-			scope:              apiScopeModuleLib,
-			apiContributions:   []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
-			fullApiSurfaceStub: "android_module_lib_stubs_current_full.from-text",
+			scope:            apiScopeModuleLib,
+			apiContributions: []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
 		},
 	}
 
 	for _, c := range testCases {
 		m := result.ModuleForTests(c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary)
 		android.AssertArrayString(t, "Module expected to contain api contributions", c.apiContributions, m.properties.Api_contributions)
-		android.AssertStringEquals(t, "Module expected to contain full api surface api library", c.fullApiSurfaceStub, *m.properties.Full_api_surface_stub)
 	}
 }
 
@@ -1691,7 +1430,7 @@
 			name: "bar",
 			srcs: ["c.java", "b.java"],
 			libs: [
-				"foo",
+				"foo.stubs",
 			],
 			uses_libs: [
 				"foo",
@@ -1709,9 +1448,6 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 	).RunTestWithBp(t, `
 		aconfig_declarations {
 			name: "bar",
@@ -1744,18 +1480,15 @@
 	exportableSourceStubsLibraryModuleName := apiScopePublic.exportableSourceStubsLibraryModuleName("foo")
 
 	// Check modules generation
-	topLevelModule := result.ModuleForTests(exportableStubsLibraryModuleName, "android_common")
+	result.ModuleForTests(exportableStubsLibraryModuleName, "android_common")
 	result.ModuleForTests(exportableSourceStubsLibraryModuleName, "android_common")
 
 	// Check static lib dependency
 	android.AssertBoolEquals(t, "exportable top level stubs library module depends on the"+
 		"exportable source stubs library module", true,
-		CheckModuleHasDependency(t, result.TestContext, exportableStubsLibraryModuleName,
-			"android_common", exportableSourceStubsLibraryModuleName),
+		CheckModuleHasDependencyWithTag(t, result.TestContext, exportableStubsLibraryModuleName,
+			"android_common", staticLibTag, exportableSourceStubsLibraryModuleName),
 	)
-	android.AssertArrayString(t, "exportable source stub library is a static lib of the"+
-		"top level exportable stubs library", []string{exportableSourceStubsLibraryModuleName},
-		topLevelModule.Module().(*Library).properties.Static_libs)
 }
 
 // For java libraries depending on java_sdk_library(_import) via libs, assert that
@@ -1792,7 +1525,7 @@
 			name: "mymodule",
 			srcs: ["a.java"],
 			sdk_version: "current",
-			libs: ["sdklib",], // this should be dynamically resolved to sdklib.stubs (source) or prebuilt_sdklib.stubs (prebuilt)
+			libs: ["sdklib.stubs",], // this should be dynamically resolved to sdklib.stubs (source) or prebuilt_sdklib.stubs (prebuilt)
 		}
 	`
 
@@ -1800,12 +1533,8 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				// We can use any of the apex contribution build flags from build/soong/android/config.go#mainlineApexContributionBuildFlags here
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
-			}
-		}),
+		// We can use any of the apex contribution build flags from build/soong/android/config.go#mainlineApexContributionBuildFlags here
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_mainline_module_contributions"),
 	)
 
 	result := fixture.RunTestWithBp(t, bp)
@@ -1888,11 +1617,7 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib", "sdklib.v1", "sdklib.v2"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_mainline_module_contributions"),
 	)
 
 	for _, tc := range testCases {
@@ -1905,3 +1630,147 @@
 		android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, tc.expectedStubPath)
 	}
 }
+
+func TestStubLinkType(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		`module "baz" variant "android_common": compiles against system API, but dependency `+
+			`"bar.stubs.system" is compiling against module API. In order to fix this, `+
+			`consider adjusting sdk_version: OR platform_apis: property of the source or `+
+			`target module so that target module is built with the same or smaller API set `+
+			`when compared to the source.`),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+		}
+		java_library {
+			name: "bar.stubs.system",
+			srcs: ["a.java"],
+			sdk_version: "module_current",
+			is_stubs_module: false,
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["b.java"],
+			libs: [
+				"foo.stubs.system",
+				"bar.stubs.system",
+			],
+			sdk_version: "system_current",
+		}
+		`)
+}
+
+func TestSdkLibDirectDependency(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo", "bar"),
+	).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{
+		`module "baz" variant "android_common": cannot depend directly on java_sdk_library ` +
+			`"foo"; try depending on "foo.stubs", or "foo.impl" instead`,
+		`module "baz" variant "android_common": cannot depend directly on java_sdk_library ` +
+			`"prebuilt_bar"; try depending on "bar.stubs", or "bar.impl" instead`,
+	}),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library_import {
+			name: "foo",
+			public: {
+				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "current.txt",
+				removed_api: "removed.txt",
+				annotations: "annotations.zip",
+			},
+		}
+
+		java_sdk_library {
+			name: "bar",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library_import {
+			name: "bar",
+			prefer: true,
+			public: {
+				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "current.txt",
+				removed_api: "removed.txt",
+				annotations: "annotations.zip",
+			},
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["b.java"],
+			libs: [
+				"foo",
+				"bar",
+			],
+		}
+	`)
+}
+
+func TestSdkLibDirectDependencyWithPrebuiltSdk(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_sdk_version = intPtr(34)
+			variables.Platform_sdk_codename = stringPtr("VanillaIceCream")
+			variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
+			variables.Platform_systemsdk_versions = []string{"33", "34", "VanillaIceCream"}
+			variables.DeviceSystemSdkVersions = []string{"VanillaIceCream"}
+		}),
+		FixtureWithPrebuiltApis(map[string][]string{
+			"33": {"foo"},
+			"34": {"foo"},
+			"35": {"foo"},
+		}),
+	).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		`module "baz" variant "android_common": cannot depend directly on java_sdk_library "foo"; `+
+			`try depending on "sdk_public_33_foo", "sdk_system_33_foo", "sdk_test_33_foo", `+
+			`"sdk_module-lib_33_foo", or "sdk_system-server_33_foo" instead`),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			public: {
+				enabled: true,
+			},
+			system: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["b.java"],
+			libs: [
+				"foo",
+			],
+			sdk_version: "system_33",
+		}
+	`)
+}
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 9e8ba6e..9bfe6a2 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -388,22 +388,29 @@
 		},
 	}
 
+	t.Parallel()
 	t.Run("basic", func(t *testing.T) {
-		testClasspathTestCases(t, classpathTestcases, false)
+		t.Parallel()
+		testClasspathTestCases(t, classpathTestcases, false, false)
 	})
 
 	t.Run("Always_use_prebuilt_sdks=true", func(t *testing.T) {
-		testClasspathTestCases(t, classpathTestcases, true)
+		testClasspathTestCases(t, classpathTestcases, true, false)
+	})
+
+	t.Run("UseTransitiveJarsInClasspath", func(t *testing.T) {
+		testClasspathTestCases(t, classpathTestcases, false, true)
 	})
 }
 
-func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks bool) {
+func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks, useTransitiveJarsInClasspath bool) {
 	for _, testcase := range classpathTestcases {
 		if testcase.forAlwaysUsePrebuiltSdks != nil && *testcase.forAlwaysUsePrebuiltSdks != alwaysUsePrebuiltSdks {
 			continue
 		}
 
 		t.Run(testcase.name, func(t *testing.T) {
+			t.Parallel()
 			moduleType := "java_library"
 			if testcase.moduleType != "" {
 				moduleType = testcase.moduleType
@@ -434,7 +441,14 @@
 			convertModulesToPaths := func(cp []string) []string {
 				ret := make([]string, len(cp))
 				for i, e := range cp {
-					ret[i] = defaultModuleToPath(e)
+					switch {
+					case e == `""`, strings.HasSuffix(e, ".jar"):
+						ret[i] = e
+					case useTransitiveJarsInClasspath:
+						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine", e+".jar")
+					default:
+						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine-combined", e+".jar")
+					}
 				}
 				return ret
 			}
@@ -528,6 +542,9 @@
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				})
 			}
+			if useTransitiveJarsInClasspath {
+				preparer = PrepareForTestWithTransitiveClasspathEnabled
+			}
 
 			fixtureFactory := android.GroupFixturePreparers(
 				prepareForJavaTest,
diff --git a/java/system_modules.go b/java/system_modules.go
index 8e2d5d8..d9430b2 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -120,14 +120,19 @@
 	return module
 }
 
-type SystemModulesProvider interface {
-	HeaderJars() android.Paths
-	OutputDirAndDeps() (android.Path, android.Paths)
+type SystemModulesProviderInfo struct {
+	// The aggregated header jars from all jars specified in the libs property.
+	// Used when system module is added as a dependency to bootclasspath.
+	HeaderJars android.Paths
+
+	OutputDir     android.Path
+	OutputDirDeps android.Paths
+
+	// depset of header jars for this module and all transitive static dependencies
+	TransitiveStaticLibsHeaderJars *android.DepSet[android.Path]
 }
 
-var _ SystemModulesProvider = (*SystemModules)(nil)
-
-var _ SystemModulesProvider = (*systemModulesImport)(nil)
+var SystemModulesProvider = blueprint.NewProvider[*SystemModulesProviderInfo]()
 
 type SystemModules struct {
 	android.ModuleBase
@@ -135,9 +140,6 @@
 
 	properties SystemModulesProperties
 
-	// The aggregated header jars from all jars specified in the libs property.
-	// Used when system module is added as a dependency to bootclasspath.
-	headerJars android.Paths
 	outputDir  android.Path
 	outputDeps android.Paths
 }
@@ -147,28 +149,27 @@
 	Libs []string
 }
 
-func (system *SystemModules) HeaderJars() android.Paths {
-	return system.headerJars
-}
-
-func (system *SystemModules) OutputDirAndDeps() (android.Path, android.Paths) {
-	if system.outputDir == nil || len(system.outputDeps) == 0 {
-		panic("Missing directory for system module dependency")
-	}
-	return system.outputDir, system.outputDeps
-}
-
 func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	var jars android.Paths
 
+	var transitiveStaticLibsHeaderJars []*android.DepSet[android.Path]
 	ctx.VisitDirectDepsWithTag(systemModulesLibsTag, func(module android.Module) {
-		dep, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
-		jars = append(jars, dep.HeaderJars...)
+		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			jars = append(jars, dep.HeaderJars...)
+			if dep.TransitiveStaticLibsHeaderJars != nil {
+				transitiveStaticLibsHeaderJars = append(transitiveStaticLibsHeaderJars, dep.TransitiveStaticLibsHeaderJars)
+			}
+		}
 	})
 
-	system.headerJars = jars
-
 	system.outputDir, system.outputDeps = TransformJarsToSystemModules(ctx, jars)
+
+	android.SetProvider(ctx, SystemModulesProvider, &SystemModulesProviderInfo{
+		HeaderJars:                     jars,
+		OutputDir:                      system.outputDir,
+		OutputDirDeps:                  system.outputDeps,
+		TransitiveStaticLibsHeaderJars: android.NewDepSet(android.PREORDER, nil, transitiveStaticLibsHeaderJars),
+	})
 }
 
 // ComponentDepsMutator is called before prebuilt modules without a corresponding source module are
@@ -310,3 +311,11 @@
 		propertySet.AddPropertyWithTag("libs", p.Libs, ctx.SnapshotBuilder().SdkMemberReferencePropertyTag(true))
 	}
 }
+
+// implement the following interface for IDE completion.
+var _ android.IDEInfo = (*SystemModules)(nil)
+
+func (s *SystemModules) IDEInfo(ctx android.BaseModuleContext, ideInfo *android.IdeInfo) {
+	ideInfo.Deps = append(ideInfo.Deps, s.properties.Libs...)
+	ideInfo.Libs = append(ideInfo.Libs, s.properties.Libs...)
+}
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index 336dd21..b05b0e4 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -25,7 +25,7 @@
 	paths := []string{}
 	for _, moduleName := range moduleNames {
 		module := result.Module(moduleName, "android_common")
-		info, _ := android.SingletonModuleProvider(result, module, JavaInfoProvider)
+		info, _ := android.OtherModuleProvider(result, module, JavaInfoProvider)
 		paths = append(paths, info.HeaderJars.RelativeToTop().Strings()...)
 	}
 	return paths
@@ -182,11 +182,7 @@
 	for _, tc := range testCases {
 		res := android.GroupFixturePreparers(
 			prepareForJavaTest,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
 		// check that rdep gets the correct variation of system_modules
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index bad2cf1..924abd4 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -216,6 +216,11 @@
 	return tag == systemServerClasspathFragmentContentDepTag
 }
 
+// The dexpreopt artifacts of apex system server jars are installed onto system image.
+func (s systemServerClasspathFragmentContentDependencyTag) InstallDepNeeded() bool {
+	return true
+}
+
 func (s *SystemServerClasspathModule) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
 	module := ctx.Module()
 	_, isSourceModule := module.(*SystemServerClasspathModule)
@@ -234,7 +239,7 @@
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
-func (s *SystemServerClasspathModule) IDEInfo(dpInfo *android.IdeInfo) {
+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...)
 }
diff --git a/java/test_spec_test.go b/java/test_spec_test.go
index 4144dad..f0a5fdb 100644
--- a/java/test_spec_test.go
+++ b/java/test_spec_test.go
@@ -32,7 +32,7 @@
 	module := result.ModuleForTests("module-name", "")
 
 	// Check that the provider has the right contents
-	data, _ := android.SingletonModuleProvider(result, module.Module(), soongTesting.TestSpecProviderKey)
+	data, _ := android.OtherModuleProvider(result, module.Module(), soongTesting.TestSpecProviderKey)
 	if !strings.HasSuffix(
 		data.IntermediatePath.String(), "/intermediateTestSpecMetadata.pb",
 	) {
diff --git a/java/testing.go b/java/testing.go
index 20152fd..700314f 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -52,6 +52,8 @@
 	android.MockFS{
 		// Needed for linter used by java_library.
 		"build/soong/java/lint_defaults.txt": nil,
+		// Needed for java components that invoke Metalava.
+		"build/soong/java/metalava/Android.bp": []byte(`filegroup {name: "metalava-config-files"}`),
 		// Needed for apps that do not provide their own.
 		"build/make/target/product/security": nil,
 		// Required to generate Java used-by API coverage
@@ -182,6 +184,10 @@
 			host_supported: true,
 			srcs: ["Test.java"],
 			sdk_version: "current",
+			apex_available: [
+				"//apex_available:anyapex",
+				"//apex_available:platform",
+			],
 		}
 	`)),
 )
@@ -406,7 +412,6 @@
 		"core.current.stubs",
 		"legacy.core.platform.api.stubs",
 		"stable.core.platform.api.stubs",
-
 		"android_stubs_current_exportable",
 		"android_system_stubs_current_exportable",
 		"android_test_stubs_current_exportable",
@@ -414,14 +419,13 @@
 		"android_system_server_stubs_current_exportable",
 		"core.current.stubs.exportable",
 		"legacy.core.platform.api.stubs.exportable",
-
 		"kotlin-stdlib",
 		"kotlin-stdlib-jdk7",
 		"kotlin-stdlib-jdk8",
 		"kotlin-annotations",
 		"stub-annotations",
-
 		"aconfig-annotations-lib",
+		"aconfig_storage_reader_java",
 		"unsupportedappusage",
 	}
 
@@ -433,6 +437,7 @@
 				sdk_version: "none",
 				system_modules: "stable-core-platform-api-stubs-system-modules",
 				compile_dex: true,
+				is_stubs_module: true,
 			}
 		`, extra)
 	}
@@ -484,21 +489,17 @@
 	}
 
 	extraApiLibraryModules := map[string]droidstubsStruct{
-		"android_stubs_current.from-text":                  publicDroidstubs,
-		"android_system_stubs_current.from-text":           systemDroidstubs,
-		"android_test_stubs_current.from-text":             testDroidstubs,
-		"android_module_lib_stubs_current.from-text":       moduleLibDroidstubs,
-		"android_module_lib_stubs_current_full.from-text":  moduleLibDroidstubs,
-		"android_system_server_stubs_current.from-text":    systemServerDroidstubs,
-		"core.current.stubs.from-text":                     publicDroidstubs,
-		"legacy.core.platform.api.stubs.from-text":         publicDroidstubs,
-		"stable.core.platform.api.stubs.from-text":         publicDroidstubs,
-		"core-lambda-stubs.from-text":                      publicDroidstubs,
-		"android-non-updatable.stubs.from-text":            publicDroidstubs,
-		"android-non-updatable.stubs.system.from-text":     systemDroidstubs,
-		"android-non-updatable.stubs.test.from-text":       testDroidstubs,
-		"android-non-updatable.stubs.module_lib.from-text": moduleLibDroidstubs,
-		"android-non-updatable.stubs.test_module_lib":      moduleLibDroidstubs,
+		"android_stubs_current.from-text":                 publicDroidstubs,
+		"android_system_stubs_current.from-text":          systemDroidstubs,
+		"android_test_stubs_current.from-text":            testDroidstubs,
+		"android_module_lib_stubs_current.from-text":      moduleLibDroidstubs,
+		"android_module_lib_stubs_current_full.from-text": moduleLibDroidstubs,
+		"android_system_server_stubs_current.from-text":   systemServerDroidstubs,
+		"core.current.stubs.from-text":                    publicDroidstubs,
+		"legacy.core.platform.api.stubs.from-text":        publicDroidstubs,
+		"stable.core.platform.api.stubs.from-text":        publicDroidstubs,
+		"core-lambda-stubs.from-text":                     publicDroidstubs,
+		"android-non-updatable.stubs.test_module_lib":     moduleLibDroidstubs,
 	}
 
 	for _, droidstubs := range droidstubsStructs {
@@ -527,6 +528,8 @@
 			name: "%s",
 			api_contributions: ["%s"],
 			stubs_type: "everything",
+			sdk_version: "none",
+			system_modules: "none",
 		}
         `, libName, droidstubs.name+".api.contribution")
 	}
@@ -542,6 +545,16 @@
 			},
 			compile_dex: true,
 		}
+		java_library {
+			name: "framework-minus-apex",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "stable-core-platform-api-stubs-system-modules",
+			aidl: {
+				export_include_dirs: ["framework/aidl"],
+			},
+			compile_dex: true,
+		}
 
 		android_app {
 			name: "framework-res",
@@ -574,6 +587,7 @@
 				name: "%[1]s-lib",
 				sdk_version: "none",
 				system_modules: "none",
+				srcs: ["a.java"],
 			}
 		`, extra)
 	}
@@ -625,6 +639,18 @@
 	return false
 }
 
+// CheckModuleHasDependency returns true if the module depends on the expected dependency.
+func CheckModuleHasDependencyWithTag(t *testing.T, ctx *android.TestContext, name, variant string, desiredTag blueprint.DependencyTag, expected string) bool {
+	module := ctx.ModuleForTests(name, variant).Module()
+	found := false
+	ctx.VisitDirectDepsWithTags(module, func(m blueprint.Module, tag blueprint.DependencyTag) {
+		if tag == desiredTag && m.Name() == expected {
+			found = true
+		}
+	})
+	return found
+}
+
 // CheckPlatformBootclasspathModules returns the apex:module pair for the modules depended upon by
 // the platform-bootclasspath module.
 func CheckPlatformBootclasspathModules(t *testing.T, result *android.TestResult, name string, expected []string) {
@@ -637,7 +663,7 @@
 func CheckClasspathFragmentProtoContentInfoProvider(t *testing.T, result *android.TestResult, generated bool, contents, outputFilename, installDir string) {
 	t.Helper()
 	p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
-	info, _ := android.SingletonModuleProvider(result, p, ClasspathFragmentProtoContentInfoProvider)
+	info, _ := android.OtherModuleProvider(result, p, ClasspathFragmentProtoContentInfoProvider)
 
 	android.AssertBoolEquals(t, "classpath proto generated", generated, info.ClasspathFragmentProtoGenerated)
 	android.AssertStringEquals(t, "classpath proto contents", contents, info.ClasspathFragmentProtoContents.String())
@@ -657,7 +683,7 @@
 func apexNamePairFromModule(ctx *android.TestContext, module android.Module) string {
 	name := module.Name()
 	var apex string
-	apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
+	apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
 	if apexInfo.IsForPlatform() {
 		apex = "platform"
 	} else {
@@ -773,3 +799,5 @@
 		config.installDir = installDir
 	})
 }
+
+var PrepareForTestWithTransitiveClasspathEnabled = android.PrepareForTestWithBuildFlag("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH", "true")
diff --git a/kernel/prebuilt_kernel_modules_test.go b/kernel/prebuilt_kernel_modules_test.go
index 90b9886..7b81869 100644
--- a/kernel/prebuilt_kernel_modules_test.go
+++ b/kernel/prebuilt_kernel_modules_test.go
@@ -49,7 +49,8 @@
 	}
 
 	var actual []string
-	for _, ps := range ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().PackagingSpecs() {
+	for _, ps := range android.OtherModuleProviderOrDefault(
+		ctx, ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module(), android.InstallFilesProvider).PackagingSpecs {
 		actual = append(actual, ps.RelPathInPackage())
 	}
 	actual = android.SortedUniqueStrings(actual)
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 3a8d3cf..05b99fd 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -98,14 +98,16 @@
 	builder.Command().
 		BuiltTool("conv_linker_config").
 		Flag("proto").
+		Flag("--force").
 		FlagWithInput("-s ", input).
 		FlagWithOutput("-o ", interimOutput)
 
 	// Secondly, if there's provideLibs gathered from provideModules, append them
 	var provideLibs []string
 	for _, m := range provideModules {
-		if c, ok := m.(*cc.Module); ok && cc.IsStubTarget(c) {
-			for _, ps := range c.PackagingSpecs() {
+		if c, ok := m.(*cc.Module); ok && (cc.IsStubTarget(c) || c.HasLlndkStubs()) {
+			for _, ps := range android.OtherModuleProviderOrDefault(
+				ctx, c, android.InstallFilesProvider).PackagingSpecs {
 				provideLibs = append(provideLibs, ps.FileName())
 			}
 		}
diff --git a/linkerconfig/proto/Android.bp b/linkerconfig/proto/Android.bp
index 754e7bf..a930502 100644
--- a/linkerconfig/proto/Android.bp
+++ b/linkerconfig/proto/Android.bp
@@ -15,6 +15,7 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    visibility: ["//system/linkerconfig"],
 }
 
 python_library_host {
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 78ab771..2e408b7 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -72,6 +72,7 @@
 	"add_soong_config_namespace":           &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
 	"add_soong_config_var_value":           &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
 	soongConfigAssign:                      &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+	"soong_config_set_bool":                &simpleCallParser{name: baseName + ".soong_config_set_bool", returnType: starlarkTypeVoid, addGlobals: true},
 	soongConfigAppend:                      &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
 	"soong_config_get":                     &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
 	"add-to-product-copy-files-if-exists":  &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList},
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 0c4d213..c295c40 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -853,6 +853,7 @@
 $(call add_soong_config_namespace,snsconfig)
 $(call add_soong_config_var_value,snsconfig,imagetype,odm_image)
 $(call soong_config_set, snsconfig, foo, foo_value)
+$(call soong_config_set_bool, snsconfig, bar, true)
 $(call soong_config_append, snsconfig, bar, bar_value)
 PRODUCT_COPY_FILES := $(call copy-files,$(wildcard foo*.mk),etc)
 PRODUCT_COPY_FILES := $(call product-copy-files-by-pattern,from/%,to/%,a b c)
@@ -880,6 +881,7 @@
   rblf.soong_config_namespace(g, "snsconfig")
   rblf.soong_config_set(g, "snsconfig", "imagetype", "odm_image")
   rblf.soong_config_set(g, "snsconfig", "foo", "foo_value")
+  rblf.soong_config_set_bool(g, "snsconfig", "bar", "true")
   rblf.soong_config_append(g, "snsconfig", "bar", "bar_value")
   cfg["PRODUCT_COPY_FILES"] = rblf.copy_files(rblf.expand_wildcard("foo*.mk"), "etc")
   cfg["PRODUCT_COPY_FILES"] = rblf.product_copy_files_by_pattern("from/%", "to/%", "a b c")
diff --git a/multitree/Android.bp b/multitree/Android.bp
deleted file mode 100644
index 78c4962..0000000
--- a/multitree/Android.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
-    name: "soong-multitree",
-    pkgPath: "android/soong/multitree",
-    deps: [
-        "blueprint",
-        "soong-android",
-    ],
-    srcs: [
-        "api_imports.go",
-        "api_surface.go",
-        "export.go",
-        "metadata.go",
-        "import.go",
-    ],
-    pluginFor: ["soong_build"],
-}
diff --git a/multitree/api_imports.go b/multitree/api_imports.go
deleted file mode 100644
index 51b9e07..0000000
--- a/multitree/api_imports.go
+++ /dev/null
@@ -1,102 +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 multitree
-
-import (
-	"android/soong/android"
-	"strings"
-
-	"github.com/google/blueprint"
-)
-
-var (
-	apiImportNameSuffix = ".apiimport"
-)
-
-func init() {
-	RegisterApiImportsModule(android.InitRegistrationContext)
-	android.RegisterMakeVarsProvider(pctx, makeVarsProvider)
-}
-
-func RegisterApiImportsModule(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("api_imports", apiImportsFactory)
-}
-
-type ApiImports struct {
-	android.ModuleBase
-	properties apiImportsProperties
-}
-
-type apiImportsProperties struct {
-	Shared_libs      []string // List of C shared libraries from API surfaces
-	Header_libs      []string // List of C header libraries from API surfaces
-	Apex_shared_libs []string // List of C shared libraries with APEX stubs
-}
-
-// 'api_imports' is a module which describes modules available from API surfaces.
-// This module is required to get the list of all imported API modules, because
-// it is discouraged to loop and fetch all modules from its type information. The
-// only module with name 'api_imports' will be used from the build.
-func apiImportsFactory() android.Module {
-	module := &ApiImports{}
-	module.AddProperties(&module.properties)
-	android.InitAndroidModule(module)
-	return module
-}
-
-func (imports *ApiImports) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// ApiImport module does not generate any build actions
-}
-
-type ApiImportInfo struct {
-	SharedLibs, HeaderLibs, ApexSharedLibs map[string]string
-}
-
-var ApiImportsProvider = blueprint.NewMutatorProvider[ApiImportInfo]("deps")
-
-// Store module lists into ApiImportInfo and share it over mutator provider.
-func (imports *ApiImports) DepsMutator(ctx android.BottomUpMutatorContext) {
-	generateNameMapWithSuffix := func(names []string) map[string]string {
-		moduleNameMap := make(map[string]string)
-		for _, name := range names {
-			moduleNameMap[name] = name + apiImportNameSuffix
-		}
-
-		return moduleNameMap
-	}
-
-	sharedLibs := generateNameMapWithSuffix(imports.properties.Shared_libs)
-	headerLibs := generateNameMapWithSuffix(imports.properties.Header_libs)
-	apexSharedLibs := generateNameMapWithSuffix(imports.properties.Apex_shared_libs)
-
-	android.SetProvider(ctx, ApiImportsProvider, ApiImportInfo{
-		SharedLibs:     sharedLibs,
-		HeaderLibs:     headerLibs,
-		ApexSharedLibs: apexSharedLibs,
-	})
-}
-
-func GetApiImportSuffix() string {
-	return apiImportNameSuffix
-}
-
-func makeVarsProvider(ctx android.MakeVarsContext) {
-	ctx.VisitAllModules(func(m android.Module) {
-		if i, ok := m.(*ApiImports); ok {
-			ctx.Strict("API_IMPORTED_SHARED_LIBRARIES", strings.Join(i.properties.Shared_libs, " "))
-			ctx.Strict("API_IMPORTED_HEADER_LIBRARIES", strings.Join(i.properties.Header_libs, " "))
-		}
-	})
-}
diff --git a/multitree/api_surface.go b/multitree/api_surface.go
deleted file mode 100644
index f739a24..0000000
--- a/multitree/api_surface.go
+++ /dev/null
@@ -1,119 +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 multitree
-
-import (
-	"android/soong/android"
-	"fmt"
-
-	"github.com/google/blueprint"
-)
-
-var (
-	pctx = android.NewPackageContext("android/soong/multitree")
-)
-
-func init() {
-	RegisterApiSurfaceBuildComponents(android.InitRegistrationContext)
-}
-
-var PrepareForTestWithApiSurface = android.FixtureRegisterWithContext(RegisterApiSurfaceBuildComponents)
-
-func RegisterApiSurfaceBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("api_surface", ApiSurfaceFactory)
-}
-
-type ApiSurface struct {
-	android.ModuleBase
-	ExportableModuleBase
-	properties apiSurfaceProperties
-
-	allOutputs    android.Paths
-	taggedOutputs map[string]android.Paths
-}
-
-type apiSurfaceProperties struct {
-	Contributions []string
-}
-
-func ApiSurfaceFactory() android.Module {
-	module := &ApiSurface{}
-	module.AddProperties(&module.properties)
-	android.InitAndroidModule(module)
-	InitExportableModule(module)
-	return module
-}
-
-func (surface *ApiSurface) DepsMutator(ctx android.BottomUpMutatorContext) {
-	if surface.properties.Contributions != nil {
-		ctx.AddVariationDependencies(nil, nil, surface.properties.Contributions...)
-	}
-
-}
-func (surface *ApiSurface) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	contributionFiles := make(map[string]android.Paths)
-	var allOutputs android.Paths
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		if contribution, ok := child.(ApiContribution); ok {
-			copied := contribution.CopyFilesWithTag(ctx)
-			for tag, files := range copied {
-				contributionFiles[child.Name()+"#"+tag] = files
-			}
-			for _, paths := range copied {
-				allOutputs = append(allOutputs, paths...)
-			}
-			return false // no transitive dependencies
-		}
-		return false
-	})
-
-	// phony target
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   blueprint.Phony,
-		Output: android.PathForPhony(ctx, ctx.ModuleName()),
-		Inputs: allOutputs,
-	})
-
-	surface.allOutputs = allOutputs
-	surface.taggedOutputs = contributionFiles
-}
-
-func (surface *ApiSurface) OutputFiles(tag string) (android.Paths, error) {
-	if tag != "" {
-		return nil, fmt.Errorf("unknown tag: %q", tag)
-	}
-	return surface.allOutputs, nil
-}
-
-func (surface *ApiSurface) TaggedOutputs() map[string]android.Paths {
-	return surface.taggedOutputs
-}
-
-func (surface *ApiSurface) Exportable() bool {
-	return true
-}
-
-var _ android.OutputFileProducer = (*ApiSurface)(nil)
-var _ Exportable = (*ApiSurface)(nil)
-
-type ApiContribution interface {
-	// copy files necessaryt to construct an API surface
-	// For C, it will be map.txt and .h files
-	// For Java, it will be api.txt
-	CopyFilesWithTag(ctx android.ModuleContext) map[string]android.Paths // output paths
-
-	// Generate Android.bp in out/ to use the exported .txt files
-	// GenerateBuildFiles(ctx ModuleContext) Paths //output paths
-}
diff --git a/multitree/export.go b/multitree/export.go
deleted file mode 100644
index aecade5..0000000
--- a/multitree/export.go
+++ /dev/null
@@ -1,67 +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 multitree
-
-import (
-	"android/soong/android"
-
-	"github.com/google/blueprint/proptools"
-)
-
-type moduleExportProperty struct {
-	// True if the module is exported to the other components in a multi-tree.
-	// Any components in the multi-tree can import this module to use.
-	Export *bool
-}
-
-type ExportableModuleBase struct {
-	properties moduleExportProperty
-}
-
-type Exportable interface {
-	// Properties for the exporable module.
-	exportableModuleProps() *moduleExportProperty
-
-	// Check if this module can be exported.
-	// If this returns false, the module will not be exported regardless of the 'export' value.
-	Exportable() bool
-
-	// Returns 'true' if this module has 'export: true'
-	// This module will not be exported if it returns 'false' to 'Exportable()' interface even if
-	// it has 'export: true'.
-	IsExported() bool
-
-	// Map from tags to outputs.
-	// Each module can tag their outputs for convenience.
-	TaggedOutputs() map[string]android.Paths
-}
-
-type ExportableModule interface {
-	android.Module
-	android.OutputFileProducer
-	Exportable
-}
-
-func InitExportableModule(module ExportableModule) {
-	module.AddProperties(module.exportableModuleProps())
-}
-
-func (m *ExportableModuleBase) exportableModuleProps() *moduleExportProperty {
-	return &m.properties
-}
-
-func (m *ExportableModuleBase) IsExported() bool {
-	return proptools.Bool(m.properties.Export)
-}
diff --git a/multitree/import.go b/multitree/import.go
deleted file mode 100644
index 1e5c421..0000000
--- a/multitree/import.go
+++ /dev/null
@@ -1,96 +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 multitree
-
-import (
-	"android/soong/android"
-)
-
-var (
-	nameSuffix = ".imported"
-)
-
-type MultitreeImportedModuleInterface interface {
-	GetMultitreeImportedModuleName() string
-}
-
-func init() {
-	android.RegisterModuleType("imported_filegroup", importedFileGroupFactory)
-
-	android.PreArchMutators(RegisterMultitreePreArchMutators)
-}
-
-type importedFileGroupProperties struct {
-	// Imported modules from the other components in a multi-tree
-	Imported []string
-}
-
-type importedFileGroup struct {
-	android.ModuleBase
-
-	properties importedFileGroupProperties
-	srcs       android.Paths
-}
-
-func (ifg *importedFileGroup) Name() string {
-	return ifg.BaseModuleName() + nameSuffix
-}
-
-func importedFileGroupFactory() android.Module {
-	module := &importedFileGroup{}
-	module.AddProperties(&module.properties)
-
-	android.InitAndroidModule(module)
-	return module
-}
-
-var _ MultitreeImportedModuleInterface = (*importedFileGroup)(nil)
-
-func (ifg *importedFileGroup) GetMultitreeImportedModuleName() string {
-	// The base module name of the imported filegroup is used as the imported module name
-	return ifg.BaseModuleName()
-}
-
-var _ android.SourceFileProducer = (*importedFileGroup)(nil)
-
-func (ifg *importedFileGroup) Srcs() android.Paths {
-	return ifg.srcs
-}
-
-func (ifg *importedFileGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// srcs from this module must not be used. Adding a dot path to avoid the empty
-	// source failure. Still soong returns error when a module wants to build against
-	// this source, which is intended.
-	ifg.srcs = android.PathsForModuleSrc(ctx, []string{"."})
-}
-
-func RegisterMultitreePreArchMutators(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("multitree_imported_rename", MultitreeImportedRenameMutator).Parallel()
-}
-
-func MultitreeImportedRenameMutator(ctx android.BottomUpMutatorContext) {
-	if m, ok := ctx.Module().(MultitreeImportedModuleInterface); ok {
-		name := m.GetMultitreeImportedModuleName()
-		if !ctx.OtherModuleExists(name) {
-			// Provide an empty filegroup not to break the build while updating the metadata.
-			// In other cases, soong will report an error to guide users to run 'm update-meta'
-			// first.
-			if !ctx.Config().TargetMultitreeUpdateMeta() {
-				ctx.ModuleErrorf("\"%s\" filegroup must be imported.\nRun 'm update-meta' first to import the filegroup.", name)
-			}
-			ctx.Rename(name)
-		}
-	}
-}
diff --git a/multitree/metadata.go b/multitree/metadata.go
deleted file mode 100644
index 0eb0efc..0000000
--- a/multitree/metadata.go
+++ /dev/null
@@ -1,74 +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 multitree
-
-import (
-	"android/soong/android"
-	"encoding/json"
-)
-
-func init() {
-	android.RegisterParallelSingletonType("update-meta", UpdateMetaSingleton)
-}
-
-func UpdateMetaSingleton() android.Singleton {
-	return &updateMetaSingleton{}
-}
-
-type jsonImported struct {
-	FileGroups map[string][]string `json:",omitempty"`
-}
-
-type metadataJsonFlags struct {
-	Imported jsonImported        `json:",omitempty"`
-	Exported map[string][]string `json:",omitempty"`
-}
-
-type updateMetaSingleton struct {
-	importedModules       []string
-	generatedMetadataFile android.OutputPath
-}
-
-func (s *updateMetaSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	metadata := metadataJsonFlags{
-		Imported: jsonImported{
-			FileGroups: make(map[string][]string),
-		},
-		Exported: make(map[string][]string),
-	}
-	ctx.VisitAllModules(func(module android.Module) {
-		if ifg, ok := module.(*importedFileGroup); ok {
-			metadata.Imported.FileGroups[ifg.BaseModuleName()] = ifg.properties.Imported
-		}
-		if e, ok := module.(ExportableModule); ok {
-			if e.IsExported() && e.Exportable() {
-				for tag, files := range e.TaggedOutputs() {
-					// TODO(b/219846705): refactor this to a dictionary
-					metadata.Exported[e.Name()+":"+tag] = append(metadata.Exported[e.Name()+":"+tag], files.Strings()...)
-				}
-			}
-		}
-	})
-	jsonStr, err := json.Marshal(metadata)
-	if err != nil {
-		ctx.Errorf(err.Error())
-	}
-	s.generatedMetadataFile = android.PathForOutput(ctx, "multitree", "metadata.json")
-	android.WriteFileRule(ctx, s.generatedMetadataFile, string(jsonStr))
-}
-
-func (s *updateMetaSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.Strict("MULTITREE_METADATA", s.generatedMetadataFile.String())
-}
diff --git a/phony/Android.bp b/phony/Android.bp
index db5efc9..2e250c6 100644
--- a/phony/Android.bp
+++ b/phony/Android.bp
@@ -13,4 +13,5 @@
         "phony.go",
     ],
     pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
 }
diff --git a/phony/phony.go b/phony/phony.go
index 5469238..807b95b 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -20,6 +20,8 @@
 	"strings"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -49,7 +51,7 @@
 }
 
 func (p *phony) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	p.requiredModuleNames = ctx.RequiredModuleNames()
+	p.requiredModuleNames = ctx.RequiredModuleNames(ctx)
 	p.hostRequiredModuleNames = ctx.HostRequiredModuleNames()
 	p.targetRequiredModuleNames = ctx.TargetRequiredModuleNames()
 }
@@ -88,14 +90,15 @@
 	android.ModuleBase
 	android.DefaultableModuleBase
 
-	properties PhonyProperties
+	phonyDepsModuleNames []string
+	properties           PhonyProperties
 }
 
 type PhonyProperties struct {
 	// The Phony_deps is the set of all dependencies for this target,
 	// and it can function similarly to .PHONY in a makefile.
 	// Additionally, dependencies within it can even include genrule.
-	Phony_deps []string
+	Phony_deps proptools.Configurable[[]string]
 }
 
 // The phony_rule provides functionality similar to the .PHONY in a makefile.
@@ -109,13 +112,14 @@
 }
 
 func (p *PhonyRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.phonyDepsModuleNames = p.properties.Phony_deps.GetOrDefault(ctx, nil)
 }
 
 func (p *PhonyRule) AndroidMk() android.AndroidMkData {
 	return android.AndroidMkData{
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			if len(p.properties.Phony_deps) > 0 {
-				depModulesStr := strings.Join(p.properties.Phony_deps, " ")
+			if len(p.phonyDepsModuleNames) > 0 {
+				depModulesStr := strings.Join(p.phonyDepsModuleNames, " ")
 				fmt.Fprintln(w, ".PHONY:", name)
 				fmt.Fprintln(w, name, ":", depModulesStr)
 			}
diff --git a/python/binary.go b/python/binary.go
index b935aba..5f60761 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -103,6 +103,7 @@
 	p.buildBinary(ctx)
 	p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
 		p.installSource.Base(), p.installSource)
+	ctx.SetOutputFiles(android.Paths{p.installSource}, "")
 }
 
 func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
@@ -187,16 +188,6 @@
 	return android.OptionalPathForPath(p.installedDest)
 }
 
-// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
-func (p *PythonBinaryModule) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{p.installSource}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (p *PythonBinaryModule) isEmbeddedLauncherEnabled() bool {
 	return BoolDefault(p.properties.Embedded_launcher, true)
 }
diff --git a/python/python.go b/python/python.go
index 1ee533f..01ac86c 100644
--- a/python/python.go
+++ b/python/python.go
@@ -38,7 +38,7 @@
 
 // Exported to support other packages using Python modules in tests.
 func RegisterPythonPreDepsMutators(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("python_version", versionSplitMutator()).Parallel()
+	ctx.Transition("python_version", &versionSplitTransitionMutator{})
 }
 
 // the version-specific properties that apply to python modules.
@@ -245,7 +245,6 @@
 	protoExt                 = ".proto"
 	pyVersion2               = "PY2"
 	pyVersion3               = "PY3"
-	pyVersion2And3           = "PY2ANDPY3"
 	internalPath             = "internal"
 )
 
@@ -253,46 +252,67 @@
 	getBaseProperties() *BaseProperties
 }
 
-// versionSplitMutator creates version variants for modules and appends the version-specific
-// properties for a given variant to the properties in the variant module
-func versionSplitMutator() func(android.BottomUpMutatorContext) {
-	return func(mctx android.BottomUpMutatorContext) {
-		if base, ok := mctx.Module().(basePropertiesProvider); ok {
-			props := base.getBaseProperties()
-			var versionNames []string
-			// collect version specific properties, so that we can merge version-specific properties
-			// into the module's overall properties
-			var versionProps []VersionProperties
-			// PY3 is first so that we alias the PY3 variant rather than PY2 if both
-			// are available
-			if proptools.BoolDefault(props.Version.Py3.Enabled, true) {
-				versionNames = append(versionNames, pyVersion3)
-				versionProps = append(versionProps, props.Version.Py3)
+type versionSplitTransitionMutator struct{}
+
+func (versionSplitTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	if base, ok := ctx.Module().(basePropertiesProvider); ok {
+		props := base.getBaseProperties()
+		var variants []string
+		// PY3 is first so that we alias the PY3 variant rather than PY2 if both
+		// are available
+		if proptools.BoolDefault(props.Version.Py3.Enabled, true) {
+			variants = append(variants, pyVersion3)
+		}
+		if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
+			if ctx.ModuleName() != "py2-cmd" &&
+				ctx.ModuleName() != "py2-stdlib" {
+				ctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3.")
 			}
-			if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
-				if !mctx.DeviceConfig().BuildBrokenUsesSoongPython2Modules() &&
-					mctx.ModuleName() != "py2-cmd" &&
-					mctx.ModuleName() != "py2-stdlib" {
-					mctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3. This error can be temporarily overridden by setting BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES := true in the product configuration")
-				}
-				versionNames = append(versionNames, pyVersion2)
-				versionProps = append(versionProps, props.Version.Py2)
-			}
-			modules := mctx.CreateLocalVariations(versionNames...)
-			// Alias module to the first variant
-			if len(versionNames) > 0 {
-				mctx.AliasVariation(versionNames[0])
-			}
-			for i, v := range versionNames {
-				// set the actual version for Python module.
-				newProps := modules[i].(basePropertiesProvider).getBaseProperties()
-				newProps.Actual_version = v
-				// append versioned properties for the Python module to the overall properties
-				err := proptools.AppendMatchingProperties([]interface{}{newProps}, &versionProps[i], nil)
-				if err != nil {
-					panic(err)
-				}
-			}
+			variants = append(variants, pyVersion2)
+		}
+		return variants
+	}
+	return []string{""}
+}
+
+func (versionSplitTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return ""
+}
+
+func (versionSplitTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if incomingVariation != "" {
+		return incomingVariation
+	}
+	if base, ok := ctx.Module().(basePropertiesProvider); ok {
+		props := base.getBaseProperties()
+		if proptools.BoolDefault(props.Version.Py3.Enabled, true) {
+			return pyVersion3
+		} else {
+			return pyVersion2
+		}
+	}
+
+	return ""
+}
+
+func (versionSplitTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if variation == "" {
+		return
+	}
+	if base, ok := ctx.Module().(basePropertiesProvider); ok {
+		props := base.getBaseProperties()
+		props.Actual_version = variation
+
+		var versionProps *VersionProperties
+		if variation == pyVersion3 {
+			versionProps = &props.Version.Py3
+		} else if variation == pyVersion2 {
+			versionProps = &props.Version.Py2
+		}
+
+		err := proptools.AppendMatchingProperties([]interface{}{props}, versionProps, nil)
+		if err != nil {
+			panic(err)
 		}
 	}
 }
diff --git a/response/Android.bp b/response/Android.bp
index e19981f..2f319fe 100644
--- a/response/Android.bp
+++ b/response/Android.bp
@@ -13,4 +13,8 @@
     testSrcs: [
         "response_test.go",
     ],
+    visibility: [
+        "//build/make/tools/compliance",
+        "//build/soong:__subpackages__",
+    ],
 }
diff --git a/rust/Android.bp b/rust/Android.bp
index 53c9462..781f325 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -61,4 +61,5 @@
         "test_test.go",
     ],
     pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
 }
diff --git a/rust/benchmark.go b/rust/benchmark.go
index c0f1e24..8c3e515 100644
--- a/rust/benchmark.go
+++ b/rust/benchmark.go
@@ -22,7 +22,7 @@
 type BenchmarkProperties struct {
 	// Disables the creation of a test-specific directory when used with
 	// relative_install_path. Useful if several tests need to be in the same
-	// directory, but test_per_src doesn't work.
+	// directory.
 	No_named_install_directory *bool
 
 	// the name of the test configuration (for example "AndroidBenchmark.xml") that should be
diff --git a/rust/binary.go b/rust/binary.go
index 9969513..cba29a0 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -134,6 +134,9 @@
 	ret := buildOutput{outputFile: outputFile}
 	crateRootPath := crateRootPath(ctx, binary)
 
+	// Ensure link dirs are not duplicated
+	deps.linkDirs = android.FirstUniqueStrings(deps.linkDirs)
+
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 83a6e95..31aa137 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r510928"
+	bindgenClangVersion = "clang-r530567"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -285,7 +285,7 @@
 	if isCpp {
 		cflags = append(cflags, "-x c++")
 		// Add any C++ only flags.
-		cflags = append(cflags, esc(b.ClangProperties.Cppflags)...)
+		cflags = append(cflags, esc(b.ClangProperties.Cppflags.GetOrDefault(ctx, nil))...)
 	} else {
 		cflags = append(cflags, "-x c")
 	}
@@ -364,7 +364,7 @@
 		ClangProperties:    cc.RustBindgenClangProperties{},
 	}
 
-	module := NewSourceProviderModule(hod, bindgen, false, true)
+	module := NewSourceProviderModule(hod, bindgen, false, false)
 
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
 		type stub_props struct {
@@ -393,8 +393,8 @@
 		deps.StaticLibs = append(deps.StaticLibs, String(b.Properties.Static_inline_library))
 	}
 
-	deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...)
-	deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs...)
-	deps.HeaderLibs = append(deps.HeaderLibs, b.ClangProperties.Header_libs...)
+	deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs.GetOrDefault(ctx, nil)...)
+	deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs.GetOrDefault(ctx, nil)...)
+	deps.HeaderLibs = append(deps.HeaderLibs, b.ClangProperties.Header_libs.GetOrDefault(ctx, nil)...)
 	return deps
 }
diff --git a/rust/builder.go b/rust/builder.go
index 1ce92f4..f469f56 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -520,6 +520,9 @@
 	// this flag.
 	rustdocFlags = append(rustdocFlags, "-Z", "unstable-options", "--enable-index-page")
 
+	// Ensure we use any special-case code-paths for Soong.
+	rustdocFlags = append(rustdocFlags, "--cfg", "soong")
+
 	targetTriple := ctx.toolchain().RustTriple()
 
 	// Collect rustc flags
diff --git a/rust/builder_test.go b/rust/builder_test.go
index c093ac4..ae5ccde 100644
--- a/rust/builder_test.go
+++ b/rust/builder_test.go
@@ -65,6 +65,11 @@
 			crate_name: "rust_ffi",
 			srcs: ["lib.rs"],
 		}
+		rust_ffi_static {
+			name: "librust_ffi_static",
+			crate_name: "rust_ffi",
+			srcs: ["lib.rs"],
+		}
 	`)
 	testcases := []struct {
 		testName      string
@@ -118,14 +123,14 @@
 			},
 		},
 		{
-			testName:   "rust_ffi static",
-			moduleName: "librust_ffi",
-			variant:    "android_arm64_armv8-a_static",
+			testName:   "rust_ffi_static rlib",
+			moduleName: "librust_ffi_static",
+			variant:    "android_arm64_armv8-a_rlib_rlib-std",
 			expectedFiles: []string{
-				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_static/librust_ffi.a",
-				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_static/librust_ffi.a.clippy",
-				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_static/meta_lic",
-				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_static/rustdoc.timestamp",
+				"out/soong/.intermediates/librust_ffi_static/android_arm64_armv8-a_rlib_rlib-std/librust_ffi_static.rlib",
+				"out/soong/.intermediates/librust_ffi_static/android_arm64_armv8-a_rlib_rlib-std/librust_ffi_static.rlib.clippy",
+				"out/soong/.intermediates/librust_ffi_static/android_arm64_armv8-a_rlib_rlib-std/meta_lic",
+				"out/soong/.intermediates/librust_ffi_static/android_arm64_armv8-a_rlib_rlib-std/rustdoc.timestamp",
 			},
 		},
 		{
@@ -148,6 +153,7 @@
 				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_shared/unstripped/librust_ffi.so",
 				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_shared/unstripped/librust_ffi.so.toc",
 				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_shared/meta_lic",
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_shared/rustdoc.timestamp",
 				"out/soong/target/product/test_device/system/lib64/librust_ffi.so",
 			},
 		},
diff --git a/rust/clippy.go b/rust/clippy.go
index 6f0ed7f..426fd73 100644
--- a/rust/clippy.go
+++ b/rust/clippy.go
@@ -38,11 +38,14 @@
 }
 
 func (c *clippy) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
-	enabled, lints, err := config.ClippyLintsForDir(ctx.ModuleDir(), c.Properties.Clippy_lints)
+	dirEnabled, lints, err := config.ClippyLintsForDir(ctx.ModuleDir(), c.Properties.Clippy_lints)
 	if err != nil {
 		ctx.PropertyErrorf("clippy_lints", err.Error())
 	}
-	flags.Clippy = enabled
+
+	envDisable := ctx.Config().IsEnvTrue("SOONG_DISABLE_CLIPPY")
+
+	flags.Clippy = dirEnabled && !envDisable
 	flags.ClippyFlags = append(flags.ClippyFlags, lints)
 	return flags, deps
 }
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index 79ea7a1..25f7580 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -24,4 +24,8 @@
         "x86_64_device.go",
         "arm64_linux_host.go",
     ],
+    visibility: [
+        "//build/soong:__subpackages__",
+        "//prebuilts/rust/soong",
+    ],
 }
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 9850570..94a4457 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -35,8 +35,13 @@
 		},
 		"armv8-2a":         []string{},
 		"armv8-2a-dotprod": []string{},
+
+		// branch-protection=bti,pac-ret is equivalent to Clang's mbranch-protection=standard
 		"armv9-a": []string{
-			// branch-protection=bti,pac-ret is equivalent to Clang's mbranch-protection=standard
+			"-Z branch-protection=bti,pac-ret",
+			"-Z stack-protector=none",
+		},
+		"armv9-2a": []string{
 			"-Z branch-protection=bti,pac-ret",
 			"-Z stack-protector=none",
 		},
diff --git a/rust/config/darwin_host.go b/rust/config/darwin_host.go
index 03bea82..df8c6ac 100644
--- a/rust/config/darwin_host.go
+++ b/rust/config/darwin_host.go
@@ -15,13 +15,14 @@
 package config
 
 import (
+	"runtime"
 	"strings"
 
 	"android/soong/android"
 )
 
 var (
-	DarwinRustFlags     = []string{}
+	DarwinRustFlags     = []string{"-C split-debuginfo=off"}
 	DarwinRustLinkFlags = []string{
 		"-B${cc_config.MacToolPath}",
 	}
@@ -35,13 +36,15 @@
 	registerToolchainFactory(android.Darwin, android.Arm64, darwinArm64ToolchainFactory)
 	registerToolchainFactory(android.Darwin, android.X86_64, darwinX8664ToolchainFactory)
 
-	pctx.StaticVariable("DarwinToolchainRustFlags", strings.Join(DarwinRustFlags, " "))
-	pctx.StaticVariable("DarwinToolchainLinkFlags", strings.Join(DarwinRustLinkFlags, " "))
+	if runtime.GOOS == "darwin" {
+		pctx.StaticVariable("DarwinToolchainRustFlags", strings.Join(DarwinRustFlags, " "))
+		pctx.StaticVariable("DarwinToolchainLinkFlags", strings.Join(DarwinRustLinkFlags, " "))
 
-	pctx.StaticVariable("DarwinToolchainArm64RustFlags", strings.Join(darwinArm64Rustflags, " "))
-	pctx.StaticVariable("DarwinToolchainArm64LinkFlags", strings.Join(darwinArm64Linkflags, " "))
-	pctx.StaticVariable("DarwinToolchainX8664RustFlags", strings.Join(darwinX8664Rustflags, " "))
-	pctx.StaticVariable("DarwinToolchainX8664LinkFlags", strings.Join(darwinX8664Linkflags, " "))
+		pctx.StaticVariable("DarwinToolchainArm64RustFlags", strings.Join(darwinArm64Rustflags, " "))
+		pctx.StaticVariable("DarwinToolchainArm64LinkFlags", strings.Join(darwinArm64Linkflags, " "))
+		pctx.StaticVariable("DarwinToolchainX8664RustFlags", strings.Join(darwinX8664Rustflags, " "))
+		pctx.StaticVariable("DarwinToolchainX8664LinkFlags", strings.Join(darwinX8664Linkflags, " "))
+	}
 
 }
 
diff --git a/rust/config/global.go b/rust/config/global.go
index 6943467..68a74c2 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var (
 	pctx = android.NewPackageContext("android/soong/rust/config")
 
-	RustDefaultVersion = "1.78.0"
+	RustDefaultVersion = "1.81.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
diff --git a/rust/coverage.go b/rust/coverage.go
index e0e919c..381fcf1 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -47,7 +47,7 @@
 
 		// no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
 		if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
-			ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}, rlibDepTag, ProfilerBuiltins)
+			ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}, rlibDepTag, ProfilerBuiltins)
 		}
 	}
 
@@ -87,10 +87,6 @@
 }
 
 func (cov *coverage) begin(ctx BaseModuleContext) {
-	if ctx.Host() {
-		// Host coverage not yet supported.
-	} else {
-		// Update useSdk and sdkVersion args if Rust modules become SDK aware.
-		cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.RustModule().nativeCoverage(), false, "")
-	}
+	// Update useSdk and sdkVersion args if Rust modules become SDK aware.
+	cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.RustModule().nativeCoverage(), false, "")
 }
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index 0d4622a..6cb8b93 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -114,17 +114,23 @@
 				srcs: ["foo.rs"],
 				shared_libs: ["libcc_transitive_dep"],
 			}
+			rust_ffi_static {
+				name: "libtest_fuzzing_static",
+				crate_name: "test_fuzzing",
+				srcs: ["foo.rs"],
+				shared_libs: ["libcc_transitive_dep"],
+			}
 			cc_fuzz {
 				name: "fuzz_shared_libtest",
 				shared_libs: ["libtest_fuzzing"],
 			}
 			cc_fuzz {
 				name: "fuzz_static_libtest",
-				static_rlibs: ["libtest_fuzzing"],
+				static_libs: ["libtest_fuzzing"],
 			}
 			cc_fuzz {
 				name: "fuzz_staticffi_libtest",
-				static_libs: ["libtest_fuzzing"],
+				static_libs: ["libtest_fuzzing_static"],
 			}
 	`)
 
diff --git a/rust/image.go b/rust/image.go
index fec6d92..26929b1 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -77,6 +77,14 @@
 	mod.Properties.CoreVariantNeeded = b
 }
 
+func (mod *Module) SetProductVariantNeeded(b bool) {
+	mod.Properties.ProductVariantNeeded = b
+}
+
+func (mod *Module) SetVendorVariantNeeded(b bool) {
+	mod.Properties.VendorVariantNeeded = b
+}
+
 func (mod *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
 	if snapshot, ok := mod.compiler.(cc.SnapshotInterface); ok {
 		return snapshot.Version()
@@ -86,6 +94,14 @@
 	}
 }
 
+func (mod *Module) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+	return mod.Properties.VendorVariantNeeded
+}
+
+func (mod *Module) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+	return mod.Properties.ProductVariantNeeded
+}
+
 func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
 	return mod.Properties.VendorRamdiskVariantNeeded
 }
@@ -184,12 +200,12 @@
 }
 
 func (mod *Module) InProduct() bool {
-	return mod.Properties.ImageVariation == cc.ProductVariation
+	return mod.Properties.ImageVariation == android.ProductVariation
 }
 
 // Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
 func (mod *Module) InVendor() bool {
-	return mod.Properties.ImageVariation == cc.VendorVariation
+	return mod.Properties.ImageVariation == android.VendorVariation
 }
 
 // Returns true if the module is "vendor" or "product" variant.
@@ -202,13 +218,13 @@
 		mod.MakeAsPlatform()
 	} else if variant == android.RecoveryVariation {
 		mod.MakeAsPlatform()
-	} else if strings.HasPrefix(variant, cc.VendorVariation) {
-		mod.Properties.ImageVariation = cc.VendorVariation
+	} else if strings.HasPrefix(variant, android.VendorVariation) {
+		mod.Properties.ImageVariation = android.VendorVariation
 		if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
 			mod.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
 		}
-	} else if strings.HasPrefix(variant, cc.ProductVariation) {
-		mod.Properties.ImageVariation = cc.ProductVariation
+	} else if strings.HasPrefix(variant, android.ProductVariation) {
+		mod.Properties.ImageVariation = android.ProductVariation
 		if strings.HasPrefix(variant, cc.ProductVariationPrefix) {
 			mod.Properties.VndkVersion = strings.TrimPrefix(variant, cc.ProductVariationPrefix)
 		}
diff --git a/rust/image_test.go b/rust/image_test.go
index 71e271c..d84eb10 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -22,18 +22,20 @@
 	"android/soong/cc"
 )
 
-// Test that cc modules can link against vendor_available rust_ffi_rlib/rust_ffi_static libraries.
+// Test that cc modules can depend on vendor_available rust_ffi_rlib/rust_ffi_static libraries.
 func TestVendorLinkage(t *testing.T) {
 	ctx := testRust(t, `
 			cc_binary {
 				name: "fizz_vendor_available",
-				static_libs: ["libfoo_vendor_static"],
-				static_rlibs: ["libfoo_vendor"],
+				static_libs: [
+					"libfoo_vendor",
+					"libfoo_vendor_static"
+				],
 				vendor_available: true,
 			}
 			cc_binary {
 				name: "fizz_soc_specific",
-				static_rlibs: ["libfoo_vendor"],
+				static_libs: ["libfoo_vendor"],
 				soc_specific: true,
 			}
 			rust_ffi_rlib {
@@ -52,8 +54,8 @@
 
 	vendorBinary := ctx.ModuleForTests("fizz_vendor_available", "android_vendor_arm64_armv8-a").Module().(*cc.Module)
 
-	if !android.InList("libfoo_vendor_static.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("vendorBinary should have a dependency on libfoo_vendor_static.vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
+	if android.InList("libfoo_vendor_static.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("vendorBinary should not have a staticlib dependency on libfoo_vendor_static.vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
 	}
 }
 
@@ -110,8 +112,10 @@
 	ctx := testRust(t, `
 			cc_library_shared {
 				name: "libcc_vendor_ramdisk",
-				static_rlibs: ["libfoo_vendor_ramdisk"],
-				static_libs: ["libfoo_static_vendor_ramdisk"],
+				static_libs: [
+					"libfoo_vendor_ramdisk",
+					"libfoo_static_vendor_ramdisk"
+				],
 				system_shared_libs: [],
 				vendor_ramdisk_available: true,
 			}
@@ -131,8 +135,8 @@
 
 	vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_shared").Module().(*cc.Module)
 
-	if !android.InList("libfoo_static_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_static_vendor_ramdisk")
+	if android.InList("libfoo_static_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("libcc_vendor_ramdisk should not have a dependency on the libfoo_static_vendor_ramdisk static library")
 	}
 }
 
diff --git a/rust/library.go b/rust/library.go
index 2a21263..7db8f36 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -20,6 +20,8 @@
 	"regexp"
 	"strings"
 
+	"github.com/google/blueprint"
+
 	"android/soong/android"
 	"android/soong/cc"
 )
@@ -43,9 +45,9 @@
 	android.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory)
 
 	// TODO: Remove when all instances of rust_ffi_static have been switched to rust_ffi_rlib
-	// Alias rust_ffi_static to the combined rust_ffi_rlib factory
-	android.RegisterModuleType("rust_ffi_static", RustFFIStaticRlibFactory)
-	android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticRlibHostFactory)
+	// Alias rust_ffi_static to the rust_ffi_rlib factory
+	android.RegisterModuleType("rust_ffi_static", RustFFIRlibFactory)
+	android.RegisterModuleType("rust_ffi_host_static", RustFFIRlibHostFactory)
 }
 
 type VariantLibraryProperties struct {
@@ -68,6 +70,10 @@
 
 	// Whether this library is part of the Rust toolchain sysroot.
 	Sysroot *bool
+
+	// Exclude this rust_ffi target from being included in APEXes.
+	// TODO(b/362509506): remove this once stubs are properly supported by rust_ffi targets.
+	Apex_exclude *bool
 }
 
 type LibraryMutatedProperties struct {
@@ -86,8 +92,6 @@
 	VariantIsRlib bool `blueprint:"mutated"`
 	// This variant is a shared library
 	VariantIsShared bool `blueprint:"mutated"`
-	// This variant is a static library
-	VariantIsStatic bool `blueprint:"mutated"`
 	// This variant is a source provider
 	VariantIsSource bool `blueprint:"mutated"`
 
@@ -122,6 +126,7 @@
 	shared() bool
 	sysroot() bool
 	source() bool
+	apexExclude() bool
 
 	// Returns true if the build options for the module have selected a particular build type
 	buildRlib() bool
@@ -179,13 +184,17 @@
 }
 
 func (library *libraryDecorator) static() bool {
-	return library.MutatedProperties.VariantIsStatic
+	return false
 }
 
 func (library *libraryDecorator) source() bool {
 	return library.MutatedProperties.VariantIsSource
 }
 
+func (library *libraryDecorator) apexExclude() bool {
+	return Bool(library.Properties.Apex_exclude)
+}
+
 func (library *libraryDecorator) buildRlib() bool {
 	return library.MutatedProperties.BuildRlib && BoolDefault(library.Properties.Rlib.Enabled, true)
 }
@@ -205,14 +214,12 @@
 func (library *libraryDecorator) setRlib() {
 	library.MutatedProperties.VariantIsRlib = true
 	library.MutatedProperties.VariantIsDylib = false
-	library.MutatedProperties.VariantIsStatic = false
 	library.MutatedProperties.VariantIsShared = false
 }
 
 func (library *libraryDecorator) setDylib() {
 	library.MutatedProperties.VariantIsRlib = false
 	library.MutatedProperties.VariantIsDylib = true
-	library.MutatedProperties.VariantIsStatic = false
 	library.MutatedProperties.VariantIsShared = false
 }
 
@@ -229,17 +236,13 @@
 }
 
 func (library *libraryDecorator) setShared() {
-	library.MutatedProperties.VariantIsStatic = false
 	library.MutatedProperties.VariantIsShared = true
 	library.MutatedProperties.VariantIsRlib = false
 	library.MutatedProperties.VariantIsDylib = false
 }
 
 func (library *libraryDecorator) setStatic() {
-	library.MutatedProperties.VariantIsStatic = true
-	library.MutatedProperties.VariantIsShared = false
-	library.MutatedProperties.VariantIsRlib = false
-	library.MutatedProperties.VariantIsDylib = false
+	panic(fmt.Errorf("static variant is not supported for rust modules, use the rlib variant instead"))
 }
 
 func (library *libraryDecorator) setSource() {
@@ -353,7 +356,7 @@
 // type "rlib").
 func RustFFIRlibHostFactory() android.Module {
 	module, library := NewRustLibrary(android.HostSupported)
-	library.BuildOnlyRlibStatic()
+	library.BuildOnlyRlib()
 
 	library.isFFI = true
 	return module.Init()
@@ -368,30 +371,12 @@
 	return module.Init()
 }
 
-// rust_ffi_static produces a staticlib and an rlib variant
-func RustFFIStaticRlibFactory() android.Module {
-	module, library := NewRustLibrary(android.HostAndDeviceSupported)
-	library.BuildOnlyRlibStatic()
-
-	library.isFFI = true
-	return module.Init()
-}
-
-// rust_ffi_static_host produces a staticlib and an rlib variant for the host
-func RustFFIStaticRlibHostFactory() android.Module {
-	module, library := NewRustLibrary(android.HostSupported)
-	library.BuildOnlyRlibStatic()
-
-	library.isFFI = true
-	return module.Init()
-}
-
 func (library *libraryDecorator) BuildOnlyFFI() {
 	library.MutatedProperties.BuildDylib = false
 	// we build rlibs for later static ffi linkage.
 	library.MutatedProperties.BuildRlib = true
 	library.MutatedProperties.BuildShared = true
-	library.MutatedProperties.BuildStatic = true
+	library.MutatedProperties.BuildStatic = false
 
 	library.isFFI = true
 }
@@ -417,14 +402,6 @@
 	library.MutatedProperties.BuildStatic = false
 }
 
-func (library *libraryDecorator) BuildOnlyRlibStatic() {
-	library.MutatedProperties.BuildDylib = false
-	library.MutatedProperties.BuildRlib = true
-	library.MutatedProperties.BuildShared = false
-	library.MutatedProperties.BuildStatic = true
-	library.isFFI = true
-}
-
 func (library *libraryDecorator) BuildOnlyStatic() {
 	library.MutatedProperties.BuildRlib = false
 	library.MutatedProperties.BuildDylib = false
@@ -726,31 +703,28 @@
 	}
 }
 
-// LibraryMutator mutates the libraries into variants according to the
-// build{Rlib,Dylib} attributes.
-func LibraryMutator(mctx android.BottomUpMutatorContext) {
-	// Only mutate on Rust libraries.
-	m, ok := mctx.Module().(*Module)
+type libraryTransitionMutator struct{}
+
+func (libraryTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	m, ok := ctx.Module().(*Module)
 	if !ok || m.compiler == nil {
-		return
+		return []string{""}
 	}
 	library, ok := m.compiler.(libraryInterface)
 	if !ok {
-		return
+		return []string{""}
 	}
 
 	// Don't produce rlib/dylib/source variants for shared or static variants
 	if library.shared() || library.static() {
-		return
+		return []string{""}
 	}
 
 	var variants []string
 	// The source variant is used for SourceProvider modules. The other variants (i.e. rlib and dylib)
 	// depend on this variant. It must be the first variant to be declared.
-	sourceVariant := false
 	if m.sourceProvider != nil {
-		variants = append(variants, "source")
-		sourceVariant = true
+		variants = append(variants, sourceVariation)
 	}
 	if library.buildRlib() {
 		variants = append(variants, rlibVariation)
@@ -760,80 +734,134 @@
 	}
 
 	if len(variants) == 0 {
+		return []string{""}
+	}
+
+	return variants
+}
+
+func (libraryTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return ""
+}
+
+func (libraryTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	m, ok := ctx.Module().(*Module)
+	if !ok || m.compiler == nil {
+		return ""
+	}
+	library, ok := m.compiler.(libraryInterface)
+	if !ok {
+		return ""
+	}
+
+	if incomingVariation == "" {
+		if m.sourceProvider != nil {
+			return sourceVariation
+		}
+		if library.shared() {
+			return ""
+		}
+		if library.buildRlib() {
+			return rlibVariation
+		}
+		if library.buildDylib() {
+			return dylibVariation
+		}
+	}
+	return incomingVariation
+}
+
+func (libraryTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	m, ok := ctx.Module().(*Module)
+	if !ok || m.compiler == nil {
 		return
 	}
-	modules := mctx.CreateLocalVariations(variants...)
+	library, ok := m.compiler.(libraryInterface)
+	if !ok {
+		return
+	}
 
-	// The order of the variations (modules) matches the variant names provided. Iterate
-	// through the new variation modules and set their mutated properties.
-	for i, v := range modules {
-		switch variants[i] {
-		case rlibVariation:
-			v.(*Module).compiler.(libraryInterface).setRlib()
-		case dylibVariation:
-			v.(*Module).compiler.(libraryInterface).setDylib()
-			if v.(*Module).ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
-				// TODO(b/165791368)
-				// Disable dylib Vendor Ramdisk variations until we support these.
-				v.(*Module).Disable()
-			}
-
-		case "source":
-			v.(*Module).compiler.(libraryInterface).setSource()
-			// The source variant does not produce any library.
-			// Disable the compilation steps.
-			v.(*Module).compiler.SetDisabled()
-		case "":
-			// if there's an empty variant, alias it so it is the default variant
-			mctx.AliasVariation("")
+	switch variation {
+	case rlibVariation:
+		library.setRlib()
+	case dylibVariation:
+		library.setDylib()
+		if m.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
+			// TODO(b/165791368)
+			// Disable dylib Vendor Ramdisk variations until we support these.
+			m.Disable()
 		}
+
+	case sourceVariation:
+		library.setSource()
+		// The source variant does not produce any library.
+		// Disable the compilation steps.
+		m.compiler.SetDisabled()
 	}
 
 	// If a source variant is created, add an inter-variant dependency
 	// between the other variants and the source variant.
-	if sourceVariant {
-		sv := modules[0]
-		for _, v := range modules[1:] {
-			if !v.Enabled(mctx) {
-				continue
-			}
-			mctx.AddInterVariantDependency(sourceDepTag, v, sv)
-		}
-		// Alias the source variation so it can be named directly in "srcs" properties.
-		mctx.AliasVariation("source")
+	if m.sourceProvider != nil && variation != sourceVariation {
+		ctx.AddVariationDependencies(
+			[]blueprint.Variation{
+				{"rust_libraries", sourceVariation},
+			},
+			sourceDepTag, ctx.ModuleName())
 	}
 }
 
-func LibstdMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok && m.compiler != nil && !m.compiler.Disabled() {
-		switch library := m.compiler.(type) {
-		case libraryInterface:
-			// Only create a variant if a library is actually being built.
-			if library.rlib() && !library.sysroot() {
-				// If this is a rust_ffi variant it only needs rlib-std
-				if library.isFFILibrary() {
-					variants := []string{"rlib-std"}
-					modules := mctx.CreateLocalVariations(variants...)
-					rlib := modules[0].(*Module)
-					rlib.compiler.(libraryInterface).setRlibStd()
-					rlib.Properties.RustSubName += RlibStdlibSuffix
-				} else {
-					variants := []string{"rlib-std", "dylib-std"}
-					modules := mctx.CreateLocalVariations(variants...)
+type libstdTransitionMutator struct{}
 
-					rlib := modules[0].(*Module)
-					dylib := modules[1].(*Module)
-					rlib.compiler.(libraryInterface).setRlibStd()
-					dylib.compiler.(libraryInterface).setDylibStd()
-					if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
-						// TODO(b/165791368)
-						// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
-						// variants are properly supported.
-						dylib.Disable()
-					}
-					rlib.Properties.RustSubName += RlibStdlibSuffix
+func (libstdTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+	if m, ok := ctx.Module().(*Module); ok && m.compiler != nil && !m.compiler.Disabled() {
+		// Only create a variant if a library is actually being built.
+		if library, ok := m.compiler.(libraryInterface); ok {
+			if library.rlib() && !library.sysroot() {
+				if library.isFFILibrary() {
+					return []string{"rlib-std"}
+				} else {
+					return []string{"rlib-std", "dylib-std"}
 				}
 			}
 		}
 	}
+	return []string{""}
+}
+
+func (libstdTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return ""
+}
+
+func (libstdTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if m, ok := ctx.Module().(*Module); ok && m.compiler != nil && !m.compiler.Disabled() {
+		if library, ok := m.compiler.(libraryInterface); ok {
+			if library.shared() {
+				return ""
+			}
+			if library.rlib() && !library.sysroot() {
+				if incomingVariation != "" {
+					return incomingVariation
+				}
+				return "rlib-std"
+			}
+		}
+	}
+	return ""
+}
+
+func (libstdTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if variation == "rlib-std" {
+		rlib := ctx.Module().(*Module)
+		rlib.compiler.(libraryInterface).setRlibStd()
+		rlib.Properties.RustSubName += RlibStdlibSuffix
+	} else if variation == "dylib-std" {
+		dylib := ctx.Module().(*Module)
+		dylib.compiler.(libraryInterface).setDylibStd()
+		if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
+			// TODO(b/165791368)
+			// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
+			// variants are properly supported.
+			dylib.Disable()
+		}
+	}
 }
diff --git a/rust/library_test.go b/rust/library_test.go
index 1133c28..35a420c 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -34,10 +34,14 @@
 			name: "libfoo.ffi",
 			srcs: ["foo.rs"],
 			crate_name: "foo"
+		}
+		rust_ffi_host_static {
+			name: "libfoo.ffi_static",
+			srcs: ["foo.rs"],
+			crate_name: "foo"
 		}`)
 
 	// Test all variants are being built.
-	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc")
 	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
 	libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
@@ -46,7 +50,6 @@
 	rlibCrateType := "rlib"
 	dylibCrateType := "dylib"
 	sharedCrateType := "cdylib"
-	staticCrateType := "staticlib"
 
 	// Test crate type for rlib is correct.
 	if !strings.Contains(libfooRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) {
@@ -58,11 +61,6 @@
 		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", dylibCrateType, libfooDylib.Args["rustcFlags"])
 	}
 
-	// Test crate type for C static libraries is correct.
-	if !strings.Contains(libfooStatic.Args["rustcFlags"], "crate-type="+staticCrateType) {
-		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", staticCrateType, libfooStatic.Args["rustcFlags"])
-	}
-
 	// Test crate type for FFI rlibs is correct
 	if !strings.Contains(libfooFFIRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) {
 		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooFFIRlib.Args["rustcFlags"])
@@ -188,23 +186,18 @@
 
 func TestStaticLibraryLinkage(t *testing.T) {
 	ctx := testRust(t, `
-		rust_ffi {
+		rust_ffi_static {
 			name: "libfoo",
 			srcs: ["foo.rs"],
 			crate_name: "foo",
 		}`)
 
 	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std")
-	libfooStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
 
 	if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) {
 		t.Errorf("Static libstd rlib expected to be a dependency of Rust rlib libraries. Rlib deps are: %#v",
 			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
 	}
-	if !android.InList("libstd", libfooStatic.Module().(*Module).Properties.AndroidMkRlibs) {
-		t.Errorf("Static libstd rlib expected to be a dependency of Rust static libraries. Rlib deps are: %#v",
-			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
-	}
 }
 
 func TestNativeDependencyOfRlib(t *testing.T) {
@@ -215,39 +208,31 @@
 			rlibs: ["librust_rlib"],
 			srcs: ["foo.rs"],
 		}
-		rust_ffi_static {
-			name: "libffi_static",
-			crate_name: "ffi_static",
-			rlibs: ["librust_rlib"],
-			srcs: ["foo.rs"],
-		}
 		rust_library_rlib {
 			name: "librust_rlib",
 			crate_name: "rust_rlib",
 			srcs: ["foo.rs"],
-			shared_libs: ["shared_cc_dep"],
-			static_libs: ["static_cc_dep"],
+			shared_libs: ["libshared_cc_dep"],
+			static_libs: ["libstatic_cc_dep"],
 		}
 		cc_library_shared {
-			name: "shared_cc_dep",
+			name: "libshared_cc_dep",
 			srcs: ["foo.cpp"],
 		}
 		cc_library_static {
-			name: "static_cc_dep",
+			name: "libstatic_cc_dep",
 			srcs: ["foo.cpp"],
 		}
 		`)
 
 	rustRlibRlibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_rlib-std")
 	rustRlibDylibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_dylib-std")
-	ffiStatic := ctx.ModuleForTests("libffi_static", "android_arm64_armv8-a_static")
 	ffiRlib := ctx.ModuleForTests("libffi_rlib", "android_arm64_armv8-a_rlib_rlib-std")
 
 	modules := []android.TestingModule{
 		rustRlibRlibStd,
 		rustRlibDylibStd,
 		ffiRlib,
-		ffiStatic,
 	}
 
 	// librust_rlib specifies -L flag to cc deps output directory on rustc command
@@ -258,17 +243,17 @@
 	// TODO: We could consider removing these flags
 	for _, module := range modules {
 		if !strings.Contains(module.Rule("rustc").Args["libFlags"],
-			"-L out/soong/.intermediates/shared_cc_dep/android_arm64_armv8-a_shared/") {
+			"-L out/soong/.intermediates/libshared_cc_dep/android_arm64_armv8-a_shared/") {
 			t.Errorf(
-				"missing -L flag for shared_cc_dep, rustcFlags: %#v",
-				rustRlibRlibStd.Rule("rustc").Args["libFlags"],
+				"missing -L flag for libshared_cc_dep of %s, rustcFlags: %#v",
+				module.Module().Name(), rustRlibRlibStd.Rule("rustc").Args["libFlags"],
 			)
 		}
 		if !strings.Contains(module.Rule("rustc").Args["libFlags"],
-			"-L out/soong/.intermediates/static_cc_dep/android_arm64_armv8-a_static/") {
+			"-L out/soong/.intermediates/libstatic_cc_dep/android_arm64_armv8-a_static/") {
 			t.Errorf(
-				"missing -L flag for static_cc_dep, rustcFlags: %#v",
-				rustRlibRlibStd.Rule("rustc").Args["libFlags"],
+				"missing -L flag for libstatic_cc_dep of %s, rustcFlags: %#v",
+				module.Module().Name(), rustRlibRlibStd.Rule("rustc").Args["libFlags"],
 			)
 		}
 	}
@@ -305,15 +290,23 @@
 				"libbar",
 				"librlib_only",
 			],
+		}
+		rust_ffi_host_static {
+			name: "libfoo.ffi.static",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			rustlibs: [
+				"libbar",
+				"librlib_only",
+			],
 		}`)
 
 	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib")
 	libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std")
-	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared")
 
-	for _, static := range []android.TestingModule{libfooRlib, libfooStatic, libfooFFIRlib} {
+	for _, static := range []android.TestingModule{libfooRlib, libfooFFIRlib} {
 		if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) {
 			t.Errorf("libbar not present as rlib dependency in static lib: %s", static.Module().Name())
 		}
@@ -381,6 +374,12 @@
 			crate_name: "bar",
 			rustlibs: ["libfoo"],
 		}
+		rust_ffi_static {
+			name: "libbar_static",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+			rustlibs: ["libfoo"],
+		}
 		rust_ffi {
 			name: "libbar.prefer_rlib",
 			srcs: ["foo.rs"],
@@ -394,7 +393,6 @@
 	libfooRlibDynamic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module)
 
 	libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module)
-	libbarStatic := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module().(*Module)
 	libbarFFIRlib := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module)
 
 	// prefer_rlib works the same for both rust_library and rust_ffi, so a single check is sufficient here.
@@ -413,12 +411,6 @@
 	if !android.InList("libstd", libbarShared.Properties.AndroidMkDylibs) {
 		t.Errorf("Device rust_ffi_shared does not link libstd as an dylib")
 	}
-	if !android.InList("libstd", libbarStatic.Properties.AndroidMkRlibs) {
-		t.Errorf("Device rust_ffi_static does not link libstd as an rlib")
-	}
-	if !android.InList("libfoo.rlib-std", libbarStatic.Properties.AndroidMkRlibs) {
-		t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant")
-	}
 	if !android.InList("libstd", libbarFFIRlib.Properties.AndroidMkRlibs) {
 		t.Errorf("Device rust_ffi_rlib does not link libstd as an rlib")
 	}
diff --git a/rust/rust.go b/rust/rust.go
index 7cd9df4..50f822b 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"strconv"
 	"strings"
 
 	"android/soong/bloaty"
@@ -28,7 +29,6 @@
 	"android/soong/cc"
 	cc_config "android/soong/cc/config"
 	"android/soong/fuzz"
-	"android/soong/multitree"
 	"android/soong/rust/config"
 )
 
@@ -36,20 +36,24 @@
 
 func init() {
 	android.RegisterModuleType("rust_defaults", defaultsFactory)
-	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
-		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
-		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
-	})
-	android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
-	})
+	android.PreDepsMutators(registerPreDepsMutators)
+	android.PostDepsMutators(registerPostDepsMutators)
 	pctx.Import("android/soong/android")
 	pctx.Import("android/soong/rust/config")
 	pctx.ImportAs("cc_config", "android/soong/cc/config")
 	android.InitRegistrationContext.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory)
 }
 
+func registerPreDepsMutators(ctx android.RegisterMutatorsContext) {
+	ctx.Transition("rust_libraries", &libraryTransitionMutator{})
+	ctx.Transition("rust_stdlinkage", &libstdTransitionMutator{})
+	ctx.BottomUp("rust_begin", BeginMutator).Parallel()
+}
+
+func registerPostDepsMutators(ctx android.RegisterMutatorsContext) {
+	ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
+}
+
 type Flags struct {
 	GlobalRustFlags []string // Flags that apply globally to rust
 	GlobalLinkFlags []string // Flags that apply globally to linker
@@ -80,6 +84,8 @@
 	RustSubName string `blueprint:"mutated"`
 
 	// Set by imageMutator
+	ProductVariantNeeded       bool     `blueprint:"mutated"`
+	VendorVariantNeeded        bool     `blueprint:"mutated"`
 	CoreVariantNeeded          bool     `blueprint:"mutated"`
 	VendorRamdiskVariantNeeded bool     `blueprint:"mutated"`
 	RamdiskVariantNeeded       bool     `blueprint:"mutated"`
@@ -206,27 +212,6 @@
 	return false
 }
 
-func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		if mod.sourceProvider != nil && (mod.compiler == nil || mod.compiler.Disabled()) {
-			return mod.sourceProvider.Srcs(), nil
-		} else {
-			if mod.OutputFile().Valid() {
-				return android.Paths{mod.OutputFile().Path()}, nil
-			}
-			return android.Paths{}, nil
-		}
-	case "unstripped":
-		if mod.compiler != nil {
-			return android.PathsIfNonNil(mod.compiler.unstrippedOutputFilePath()), nil
-		}
-		return nil, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (mod *Module) SelectedStl() string {
 	return ""
 }
@@ -308,6 +293,15 @@
 	return mod.StaticallyLinked()
 }
 
+func (mod *Module) ApexExclude() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.apexExclude()
+		}
+	}
+	return false
+}
+
 func (mod *Module) Object() bool {
 	// Rust has no modules which produce only object files.
 	return false
@@ -438,7 +432,7 @@
 	depFlags     []string
 	depLinkFlags []string
 
-	// linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker.
+	// linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker
 	// Both of these are exported and propagate to dependencies.
 	linkDirs    []string
 	linkObjects []string
@@ -460,9 +454,6 @@
 	// Paths to generated source files
 	SrcDeps          android.Paths
 	srcProviderFiles android.Paths
-
-	// Used by Generated Libraries
-	depExportedRlibs []cc.RustRlibDep
 }
 
 type RustLibraries []RustLibrary
@@ -483,6 +474,7 @@
 
 type flagExporter struct {
 	linkDirs    []string
+	ccLinkDirs  []string
 	linkObjects []string
 }
 
@@ -521,7 +513,7 @@
 
 var _ cc.Coverage = (*Module)(nil)
 
-func (mod *Module) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (mod *Module) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return mod.coverage != nil && mod.coverage.Properties.NeedCoverageVariant
 }
 
@@ -676,6 +668,24 @@
 	panic(fmt.Errorf("BuildStaticVariant called on non-library module: %q", mod.BaseModuleName()))
 }
 
+func (mod *Module) BuildRlibVariant() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.buildRlib()
+		}
+	}
+	panic(fmt.Errorf("BuildRlibVariant called on non-library module: %q", mod.BaseModuleName()))
+}
+
+func (mod *Module) IsRustFFI() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.isFFILibrary()
+		}
+	}
+	return false
+}
+
 func (mod *Module) BuildSharedVariant() bool {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
@@ -936,6 +946,7 @@
 			sourceLib := sourceMod.(*Module).compiler.(*libraryDecorator)
 			mod.sourceProvider.setOutputFiles(sourceLib.sourceProvider.Srcs())
 		}
+		ctx.CheckbuildFile(mod.sourceProvider.Srcs()...)
 		android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: mod.sourceProvider.Srcs().Strings()})
 	}
 
@@ -946,15 +957,13 @@
 			return
 		}
 		mod.outputFile = android.OptionalPathForPath(buildOutput.outputFile)
+		ctx.CheckbuildFile(buildOutput.outputFile)
 		if buildOutput.kytheFile != nil {
 			mod.kytheFiles = append(mod.kytheFiles, buildOutput.kytheFile)
 		}
 		bloaty.MeasureSizeForPaths(ctx, mod.compiler.strippedOutputFilePath(), android.OptionalPathForPath(mod.compiler.unstrippedOutputFilePath()))
 
 		mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps)
-		if mod.docTimestampFile.Valid() {
-			ctx.CheckbuildFile(mod.docTimestampFile.Path())
-		}
 
 		apexInfo, _ := android.ModuleProvider(actx, android.ApexInfoProvider)
 		if !proptools.BoolDefault(mod.Installable(), mod.EverInstallable()) && !mod.ProcMacro() {
@@ -986,6 +995,61 @@
 	if mod.testModule {
 		android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 	}
+
+	mod.setOutputFiles(ctx)
+
+	buildComplianceMetadataInfo(ctx, mod, deps)
+}
+
+func (mod *Module) setOutputFiles(ctx ModuleContext) {
+	if mod.sourceProvider != nil && (mod.compiler == nil || mod.compiler.Disabled()) {
+		ctx.SetOutputFiles(mod.sourceProvider.Srcs(), "")
+	} else if mod.OutputFile().Valid() {
+		ctx.SetOutputFiles(android.Paths{mod.OutputFile().Path()}, "")
+	} else {
+		ctx.SetOutputFiles(android.Paths{}, "")
+	}
+	if mod.compiler != nil {
+		ctx.SetOutputFiles(android.PathsIfNonNil(mod.compiler.unstrippedOutputFilePath()), "unstripped")
+	}
+}
+
+func buildComplianceMetadataInfo(ctx *moduleContext, mod *Module, deps PathDeps) {
+	// Dump metadata that can not be done in android/compliance-metadata.go
+	metadataInfo := ctx.ComplianceMetadataInfo()
+	metadataInfo.SetStringValue(android.ComplianceMetadataProp.IS_STATIC_LIB, strconv.FormatBool(mod.Static()))
+	metadataInfo.SetStringValue(android.ComplianceMetadataProp.BUILT_FILES, mod.outputFile.String())
+
+	// Static libs
+	staticDeps := ctx.GetDirectDepsWithTag(rlibDepTag)
+	staticDepNames := make([]string, 0, len(staticDeps))
+	for _, dep := range staticDeps {
+		staticDepNames = append(staticDepNames, dep.Name())
+	}
+	ccStaticDeps := ctx.GetDirectDepsWithTag(cc.StaticDepTag(false))
+	for _, dep := range ccStaticDeps {
+		staticDepNames = append(staticDepNames, dep.Name())
+	}
+
+	staticDepPaths := make([]string, 0, len(deps.StaticLibs)+len(deps.RLibs))
+	// C static libraries
+	for _, dep := range deps.StaticLibs {
+		staticDepPaths = append(staticDepPaths, dep.String())
+	}
+	// Rust static libraries
+	for _, dep := range deps.RLibs {
+		staticDepPaths = append(staticDepPaths, dep.Path.String())
+	}
+	metadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEPS, android.FirstUniqueStrings(staticDepNames))
+	metadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEP_FILES, android.FirstUniqueStrings(staticDepPaths))
+
+	// C Whole static libs
+	ccWholeStaticDeps := ctx.GetDirectDepsWithTag(cc.StaticDepTag(true))
+	wholeStaticDepNames := make([]string, 0, len(ccWholeStaticDeps))
+	for _, dep := range ccStaticDeps {
+		wholeStaticDepNames = append(wholeStaticDepNames, dep.Name())
+	}
+	metadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEPS, android.FirstUniqueStrings(staticDepNames))
 }
 
 func (mod *Module) deps(ctx DepsContext) Deps {
@@ -1054,7 +1118,6 @@
 	rlibDepTag          = dependencyTag{name: "rlibTag", library: true}
 	dylibDepTag         = dependencyTag{name: "dylib", library: true, dynamic: true}
 	procMacroDepTag     = dependencyTag{name: "procMacro", procMacro: true}
-	testPerSrcDepTag    = dependencyTag{name: "rust_unit_tests"}
 	sourceDepTag        = dependencyTag{name: "source"}
 	dataLibDepTag       = dependencyTag{name: "data lib"}
 	dataBinDepTag       = dependencyTag{name: "data bin"}
@@ -1076,10 +1139,11 @@
 }
 
 var (
-	rlibVariation  = "rlib"
-	dylibVariation = "dylib"
-	rlibAutoDep    = autoDep{variation: rlibVariation, depTag: rlibDepTag}
-	dylibAutoDep   = autoDep{variation: dylibVariation, depTag: dylibDepTag}
+	sourceVariation = "source"
+	rlibVariation   = "rlib"
+	dylibVariation  = "dylib"
+	rlibAutoDep     = autoDep{variation: rlibVariation, depTag: rlibDepTag}
+	dylibAutoDep    = autoDep{variation: dylibVariation, depTag: dylibDepTag}
 )
 
 type autoDeppable interface {
@@ -1153,54 +1217,12 @@
 
 	skipModuleList := map[string]bool{}
 
-	var apiImportInfo multitree.ApiImportInfo
-	hasApiImportInfo := false
-
-	ctx.VisitDirectDeps(func(dep android.Module) {
-		if dep.Name() == "api_imports" {
-			apiImportInfo, _ = android.OtherModuleProvider(ctx, dep, multitree.ApiImportsProvider)
-			hasApiImportInfo = true
-		}
-	})
-
-	if hasApiImportInfo {
-		targetStubModuleList := map[string]string{}
-		targetOrigModuleList := map[string]string{}
-
-		// Search for dependency which both original module and API imported library with APEX stub exists
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok {
-				targetStubModuleList[apiLibrary] = depName
-			}
-		})
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if origLibrary, ok := targetStubModuleList[depName]; ok {
-				targetOrigModuleList[origLibrary] = depName
-			}
-		})
-
-		// Decide which library should be used between original and API imported library
-		ctx.VisitDirectDeps(func(dep android.Module) {
-			depName := ctx.OtherModuleName(dep)
-			if apiLibrary, ok := targetOrigModuleList[depName]; ok {
-				if cc.ShouldUseStubForApex(ctx, dep) {
-					skipModuleList[depName] = true
-				} else {
-					skipModuleList[apiLibrary] = true
-				}
-			}
-		})
-	}
-
 	var transitiveAndroidMkSharedLibs []*android.DepSet[string]
 	var directAndroidMkSharedLibs []string
 
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
-
 		if _, exists := skipModuleList[depName]; exists {
 			return
 		}
@@ -1213,8 +1235,8 @@
 			//Handle Rust Modules
 			makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
 
-			switch depTag {
-			case dylibDepTag:
+			switch {
+			case depTag == dylibDepTag:
 				dylib, ok := rustDep.compiler.(libraryInterface)
 				if !ok || !dylib.dylib() {
 					ctx.ModuleErrorf("mod %q not an dylib library", depName)
@@ -1224,8 +1246,7 @@
 				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName)
 				mod.Properties.SnapshotDylibs = append(mod.Properties.SnapshotDylibs, cc.BaseLibName(depName))
 
-			case rlibDepTag:
-
+			case depTag == rlibDepTag:
 				rlib, ok := rustDep.compiler.(libraryInterface)
 				if !ok || !rlib.rlib() {
 					ctx.ModuleErrorf("mod %q not an rlib library", makeLibName)
@@ -1240,16 +1261,25 @@
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
 
-			case procMacroDepTag:
+			case depTag == procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
 				// proc_macro link dirs need to be exported, so collect those here.
 				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
 
-			case sourceDepTag:
+			case depTag == sourceDepTag:
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok {
 					collectIncludedProtos(mod, rustDep)
 				}
+			case cc.IsStaticDepTag(depTag):
+				// Rust FFI rlibs should not be declared in a Rust modules
+				// "static_libs" list as we can't handle them properly at the
+				// moment (for example, they only produce an rlib-std variant).
+				// Instead, a normal rust_library variant should be used.
+				ctx.PropertyErrorf("static_libs",
+					"found '%s' in static_libs; use a rust_library module in rustlibs instead of a rust_ffi module in static_libs",
+					depName)
+
 			}
 
 			transitiveAndroidMkSharedLibs = append(transitiveAndroidMkSharedLibs, rustDep.transitiveAndroidMkSharedLibs)
@@ -1469,7 +1499,7 @@
 
 	var srcProviderDepFiles android.Paths
 	for _, dep := range directSrcProvidersDeps {
-		srcs, _ := dep.OutputFiles("")
+		srcs := android.OutputFilesForModule(ctx, dep, "")
 		srcProviderDepFiles = append(srcProviderDepFiles, srcs...)
 	}
 	for _, dep := range directSrcDeps {
@@ -1537,13 +1567,6 @@
 	deps := mod.deps(ctx)
 	var commonDepVariations []blueprint.Variation
 
-	apiImportInfo := cc.GetApiImports(mod, actx)
-	if mod.usePublicApi() || mod.useVendorApi() {
-		for idx, lib := range deps.SharedLibs {
-			deps.SharedLibs[idx] = cc.GetReplaceModuleName(lib, apiImportInfo.SharedLibs)
-		}
-	}
-
 	if ctx.Os() == android.Android {
 		deps.SharedLibs, _ = cc.FilterNdkLibs(mod, ctx.Config(), deps.SharedLibs)
 	}
@@ -1554,7 +1577,6 @@
 	}
 
 	rlibDepVariations := commonDepVariations
-	rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 
 	if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() {
 		rlibDepVariations = append(rlibDepVariations,
@@ -1570,7 +1592,6 @@
 
 	// dylibs
 	dylibDepVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: dylibVariation})
-	dylibDepVariations = append(dylibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 
 	for _, lib := range deps.Dylibs {
 		actx.AddVariationDependencies(dylibDepVariations, dylibDepTag, lib)
@@ -1591,7 +1612,6 @@
 					// otherwise select the rlib variant.
 					autoDepVariations := append(commonDepVariations,
 						blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation})
-					autoDepVariations = append(autoDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 					if actx.OtherModuleDependencyVariantExists(autoDepVariations, lib) {
 						actx.AddVariationDependencies(autoDepVariations, autoDep.depTag, lib)
 
@@ -1605,8 +1625,7 @@
 		} else if _, ok := mod.sourceProvider.(*protobufDecorator); ok {
 			for _, lib := range deps.Rustlibs {
 				srcProviderVariations := append(commonDepVariations,
-					blueprint.Variation{Mutator: "rust_libraries", Variation: "source"})
-				srcProviderVariations = append(srcProviderVariations, blueprint.Variation{Mutator: "link", Variation: ""})
+					blueprint.Variation{Mutator: "rust_libraries", Variation: sourceVariation})
 
 				// Only add rustlib dependencies if they're source providers themselves.
 				// This is used to track which crate names need to be added to the source generated
@@ -1622,7 +1641,7 @@
 	if deps.Stdlibs != nil {
 		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
 			for _, lib := range deps.Stdlibs {
-				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}...),
+				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
 					rlibDepTag, lib)
 			}
 		} else {
@@ -1640,15 +1659,7 @@
 		variations := []blueprint.Variation{
 			{Mutator: "link", Variation: "shared"},
 		}
-		// For core variant, add a dep on the implementation (if it exists) and its .apiimport (if it exists)
-		// GenerateAndroidBuildActions will pick the correct impl/stub based on the api_domain boundary
-		if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) {
-			cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
-		}
-
-		if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok {
-			cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, apiLibraryName, version, false)
-		}
+		cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
 	}
 
 	for _, lib := range deps.WholeStaticLibs {
@@ -1803,6 +1814,10 @@
 		return false
 	}
 
+	if rustDep, ok := dep.(*Module); ok && rustDep.ApexExclude() {
+		return false
+	}
+
 	return true
 }
 
@@ -1856,5 +1871,3 @@
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
 var StringPtr = proptools.StringPtr
-
-var _ android.OutputFileProducer = (*Module)(nil)
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 8b96df8..eeedf3f 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -60,6 +60,7 @@
 // testRust returns a TestContext in which a basic environment has been setup.
 // This environment contains a few mocked files. See rustMockedFiles for the list of these files.
 func testRust(t *testing.T, bp string) *android.TestContext {
+	t.Helper()
 	skipTestIfOsNotSupported(t)
 	result := android.GroupFixturePreparers(
 		prepareForRustTest,
@@ -447,23 +448,30 @@
 			export_include_dirs: ["foo_includes"]
 		}
 
+		rust_ffi_rlib {
+			name: "libbuzz",
+			crate_name: "buzz",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["buzz_includes"]
+		}
+
 		cc_library_shared {
 			name: "libcc_shared",
 			srcs:["foo.c"],
-			static_rlibs: ["libbar"],
+			static_libs: ["libbar"],
 		}
 
 		cc_library_static {
 			name: "libcc_static",
 			srcs:["foo.c"],
-			static_rlibs: ["libfoo"],
+			static_libs: ["libbuzz"],
+			whole_static_libs: ["libfoo"],
 		}
 
 		cc_binary {
 			name: "ccBin",
 			srcs:["foo.c"],
-			static_rlibs: ["libbar"],
-			static_libs: ["libcc_static"],
+			static_libs: ["libcc_static", "libbar"],
 		}
 		`)
 
@@ -514,10 +522,13 @@
 			"-Ibar_includes", ccbin_cc.Args)
 	}
 
-	// Make sure that direct dependencies and indirect dependencies are
+	// Make sure that direct dependencies and indirect whole static dependencies are
 	// propagating correctly to the generated rlib.
 	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern foo=") {
-		t.Errorf("Missing indirect dependency libfoo when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
+		t.Errorf("Missing indirect whole_static_lib dependency libfoo when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
+	}
+	if strings.Contains(ccbin_rustc.Args["libFlags"], "--extern buzz=") {
+		t.Errorf("Indirect static_lib dependency libbuzz found when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
 	}
 	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern bar=") {
 		t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
diff --git a/rust/test.go b/rust/test.go
index 2583893..b7ddd06 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -27,7 +27,7 @@
 type TestProperties struct {
 	// Disables the creation of a test-specific directory when used with
 	// relative_install_path. Useful if several tests need to be in the same
-	// directory, but test_per_src doesn't work.
+	// directory.
 	No_named_install_directory *bool
 
 	// the name of the test configuration (for example "AndroidTest.xml") that should be
@@ -116,7 +116,8 @@
 }
 
 func (test *testDecorator) install(ctx ModuleContext) {
-	testInstallBase := "/data/local/tests/unrestricted"
+	// TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base.
+	testInstallBase := "/data/local/tmp"
 	if ctx.RustModule().InVendorOrProduct() {
 		testInstallBase = "/data/local/tests/vendor"
 	}
diff --git a/rust/test_test.go b/rust/test_test.go
index 6d0ebcf..1097da2 100644
--- a/rust/test_test.go
+++ b/rust/test_test.go
@@ -106,12 +106,9 @@
 
 	ctx := testRust(t, bp)
 
-	module := ctx.ModuleForTests("main_test", "android_arm64_armv8-a").Module()
-	testBinary := module.(*Module).compiler.(*testDecorator)
-	outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
-	if err != nil {
-		t.Fatalf("Expected rust_test to produce output files, error: %s", err)
-	}
+	testingModule := ctx.ModuleForTests("main_test", "android_arm64_armv8-a")
+	testBinary := testingModule.Module().(*Module).compiler.(*testDecorator)
+	outputFiles := testingModule.OutputFiles(ctx, t, "")
 	if len(outputFiles) != 1 {
 		t.Fatalf("expected exactly one output file. output files: [%s]", outputFiles)
 	}
@@ -168,12 +165,10 @@
  `
 
 	ctx := testRust(t, bp)
-	module := ctx.ModuleForTests("main_test", "android_arm64_armv8-a").Module()
+	testingModule := ctx.ModuleForTests("main_test", "android_arm64_armv8-a")
+	module := testingModule.Module()
 	testBinary := module.(*Module).compiler.(*testDecorator)
-	outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
-	if err != nil {
-		t.Fatalf("Expected rust_test to produce output files, error: %s", err)
-	}
+	outputFiles := testingModule.OutputFiles(ctx, t, "")
 	if len(outputFiles) != 1 {
 		t.Fatalf("expected exactly one output file. output files: [%s]", outputFiles)
 	}
diff --git a/rust/testing.go b/rust/testing.go
index f31c591..32cc823 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -189,26 +189,19 @@
 	ctx.RegisterModuleType("rust_ffi", RustFFIFactory)
 	ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
 	ctx.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory)
-	ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticRlibFactory)
+	ctx.RegisterModuleType("rust_ffi_static", RustFFIRlibFactory)
 	ctx.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
 	ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
 	ctx.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory)
-	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticRlibHostFactory)
+	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIRlibHostFactory)
 	ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
 	ctx.RegisterModuleType("rust_protobuf", RustProtobufFactory)
 	ctx.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
 	ctx.RegisterModuleType("rust_prebuilt_library", PrebuiltLibraryFactory)
 	ctx.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory)
 	ctx.RegisterModuleType("rust_prebuilt_rlib", PrebuiltRlibFactory)
-	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		// rust mutators
-		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
-		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
-		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
-	})
+	ctx.PreDepsMutators(registerPreDepsMutators)
 	ctx.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
 	ctx.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory)
-	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
-	})
+	ctx.PostDepsMutators(registerPostDepsMutators)
 }
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 80cd935..3d81b83 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -292,9 +292,17 @@
 }
 
 python_binary_host {
-    name: "buildinfo",
-    main: "buildinfo.py",
-    srcs: ["buildinfo.py"],
+    name: "merge_json",
+    main: "merge_json.py",
+    srcs: [
+        "merge_json.py",
+    ],
+}
+
+python_binary_host {
+    name: "gen_build_prop",
+    main: "gen_build_prop.py",
+    srcs: ["gen_build_prop.py"],
 }
 
 python_binary_host {
diff --git a/scripts/buildinfo.py b/scripts/buildinfo.py
deleted file mode 100755
index 45c9bc3..0000000
--- a/scripts/buildinfo.py
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2024 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-"""A tool for generating buildinfo.prop"""
-
-import argparse
-import contextlib
-import subprocess
-
-def parse_args():
-  """Parse commandline arguments."""
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--use-vbmeta-digest-in-fingerprint', action='store_true')
-  parser.add_argument('--build-flavor', required=True)
-  parser.add_argument('--build-hostname-file', required=True, type=argparse.FileType('r')),
-  parser.add_argument('--build-id', required=True)
-  parser.add_argument('--build-keys', required=True)
-  parser.add_argument('--build-number-file', required=True, type=argparse.FileType('r'))
-  parser.add_argument('--build-thumbprint-file', type=argparse.FileType('r'))
-  parser.add_argument('--build-type', required=True)
-  parser.add_argument('--build-username', required=True)
-  parser.add_argument('--build-variant', required=True)
-  parser.add_argument('--cpu-abis', action='append', required=True)
-  parser.add_argument('--date-file', required=True, type=argparse.FileType('r'))
-  parser.add_argument('--default-locale')
-  parser.add_argument('--default-wifi-channels', action='append', default=[])
-  parser.add_argument('--device', required=True)
-  parser.add_argument("--display-build-number", action='store_true')
-  parser.add_argument('--platform-base-os', required=True)
-  parser.add_argument('--platform-display-version', required=True)
-  parser.add_argument('--platform-min-supported-target-sdk-version', required=True)
-  parser.add_argument('--platform-preview-sdk-fingerprint-file',
-                      required=True,
-                      type=argparse.FileType('r'))
-  parser.add_argument('--platform-preview-sdk-version', required=True)
-  parser.add_argument('--platform-sdk-version', required=True)
-  parser.add_argument('--platform-security-patch', required=True)
-  parser.add_argument('--platform-version', required=True)
-  parser.add_argument('--platform-version-codename',required=True)
-  parser.add_argument('--platform-version-all-codenames', action='append', required=True)
-  parser.add_argument('--platform-version-known-codenames', required=True)
-  parser.add_argument('--platform-version-last-stable', required=True)
-  parser.add_argument('--product', required=True)
-
-  parser.add_argument('--out', required=True, type=argparse.FileType('w'))
-
-  return parser.parse_args()
-
-def main():
-  option = parse_args()
-
-  build_hostname = option.build_hostname_file.read().strip()
-  build_number = option.build_number_file.read().strip()
-  build_version_tags = option.build_keys
-  if option.build_type == "debug":
-    build_version_tags = "debug," + build_version_tags
-
-  raw_date = option.date_file.read().strip()
-  date = subprocess.check_output(["date", "-d", f"@{raw_date}"], text=True).strip()
-  date_utc = subprocess.check_output(["date", "-d", f"@{raw_date}", "+%s"], text=True).strip()
-
-  # build_desc is human readable strings that describe this build. This has the same info as the
-  # build fingerprint.
-  # e.g. "aosp_cf_x86_64_phone-userdebug VanillaIceCream MAIN eng.20240319.143939 test-keys"
-  build_desc = f"{option.product}-{option.build_variant} {option.platform_version} " \
-               f"{option.build_id} {build_number} {build_version_tags}"
-
-  platform_preview_sdk_fingerprint = option.platform_preview_sdk_fingerprint_file.read().strip()
-
-  with contextlib.redirect_stdout(option.out):
-    print("# begin build properties")
-    print("# autogenerated by buildinfo.py")
-
-    # The ro.build.id will be set dynamically by init, by appending the unique vbmeta digest.
-    if option.use_vbmeta_digest_in_fingerprint:
-      print(f"ro.build.legacy.id={option.build_id}")
-    else:
-      print(f"ro.build.id?={option.build_id}")
-
-    # ro.build.display.id is shown under Settings -> About Phone
-    if option.build_variant == "user":
-      # User builds should show:
-      # release build number or branch.buld_number non-release builds
-
-      # Dev. branches should have DISPLAY_BUILD_NUMBER set
-      if option.display_build_number:
-        print(f"ro.build.display.id?={option.build_id}.{build_number} {option.build_keys}")
-      else:
-        print(f"ro.build.display.id?={option.build_id} {option.build_keys}")
-    else:
-      # Non-user builds should show detailed build information (See build desc above)
-      print(f"ro.build.display.id?={build_desc}")
-    print(f"ro.build.version.incremental={build_number}")
-    print(f"ro.build.version.sdk={option.platform_sdk_version}")
-    print(f"ro.build.version.preview_sdk={option.platform_preview_sdk_version}")
-    print(f"ro.build.version.preview_sdk_fingerprint={platform_preview_sdk_fingerprint}")
-    print(f"ro.build.version.codename={option.platform_version_codename}")
-    print(f"ro.build.version.all_codenames={','.join(option.platform_version_all_codenames)}")
-    print(f"ro.build.version.known_codenames={option.platform_version_known_codenames}")
-    print(f"ro.build.version.release={option.platform_version_last_stable}")
-    print(f"ro.build.version.release_or_codename={option.platform_version}")
-    print(f"ro.build.version.release_or_preview_display={option.platform_display_version}")
-    print(f"ro.build.version.security_patch={option.platform_security_patch}")
-    print(f"ro.build.version.base_os={option.platform_base_os}")
-    print(f"ro.build.version.min_supported_target_sdk={option.platform_min_supported_target_sdk_version}")
-    print(f"ro.build.date={date}")
-    print(f"ro.build.date.utc={date_utc}")
-    print(f"ro.build.type={option.build_variant}")
-    print(f"ro.build.user={option.build_username}")
-    print(f"ro.build.host={build_hostname}")
-    # TODO: Remove any tag-related optional property declarations once the goals
-    # from go/arc-android-sigprop-changes have been achieved.
-    print(f"ro.build.tags?={build_version_tags}")
-    # ro.build.flavor are used only by the test harness to distinguish builds.
-    # Only add _asan for a sanitized build if it isn't already a part of the
-    # flavor (via a dedicated lunch config for example).
-    print(f"ro.build.flavor={option.build_flavor}")
-
-    # These values are deprecated, use "ro.product.cpu.abilist"
-    # instead (see below).
-    print(f"# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,")
-    print(f"# use ro.product.cpu.abilist instead.")
-    print(f"ro.product.cpu.abi={option.cpu_abis[0]}")
-    if len(option.cpu_abis) > 1:
-      print(f"ro.product.cpu.abi2={option.cpu_abis[1]}")
-
-    if option.default_locale:
-      print(f"ro.product.locale={option.default_locale}")
-    print(f"ro.wifi.channels={' '.join(option.default_wifi_channels)}")
-
-    print(f"# ro.build.product is obsolete; use ro.product.device")
-    print(f"ro.build.product={option.device}")
-
-    print(f"# Do not try to parse description or thumbprint")
-    print(f"ro.build.description?={build_desc}")
-    if option.build_thumbprint_file:
-      build_thumbprint = option.build_thumbprint_file.read().strip()
-      print(f"ro.build.thumbprint={build_thumbprint}")
-
-    print(f"# end build properties")
-
-if __name__ == "__main__":
-  main()
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index 6dae3ff..2f0907c 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -74,6 +74,7 @@
 javax\.xml\.xpath
 jdk\.internal
 jdk\.internal\.access
+jdk\.internal\.event
 jdk\.internal\.math
 jdk\.internal\.misc
 jdk\.internal\.ref
@@ -81,6 +82,7 @@
 jdk\.internal\.util
 jdk\.internal\.util\.jar
 jdk\.internal\.util\.random
+jdk\.internal\.vm
 jdk\.internal\.vm\.annotation
 jdk\.net
 jdk\.random
diff --git a/scripts/check_prebuilt_presigned_apk.py b/scripts/check_prebuilt_presigned_apk.py
index abedfb7..db64f90 100755
--- a/scripts/check_prebuilt_presigned_apk.py
+++ b/scripts/check_prebuilt_presigned_apk.py
@@ -36,8 +36,8 @@
                 if fail:
                     sys.exit(args.apk + ': Contains compressed JNI libraries')
                 return True
-            # It's ok for non-privileged apps to have compressed dex files, see go/gms-uncompressed-jni-slides
-            if args.privileged:
+            # It's ok for non-privileged apps to have compressed dex files
+            if args.privileged and args.uncompress_priv_app_dex:
                 if info.filename.endswith('.dex') and info.compress_type != zipfile.ZIP_STORED:
                     if fail:
                         sys.exit(args.apk + ': Contains compressed dex files and is privileged')
@@ -46,12 +46,17 @@
 
 
 def main():
+    # This script enforces requirements for presigned apps as documented in:
+    # go/gms-uncompressed-jni-slides
+    # https://docs.partner.android.com/gms/building/integrating/jni-libs
+    # https://docs.partner.android.com/gms/policies/domains/mba#jni-lib
     parser = argparse.ArgumentParser()
     parser.add_argument('--aapt2', help = "the path to the aapt2 executable")
     parser.add_argument('--zipalign', help = "the path to the zipalign executable")
     parser.add_argument('--skip-preprocessed-apk-checks', action = 'store_true', help = "the value of the soong property with the same name")
     parser.add_argument('--preprocessed', action = 'store_true', help = "the value of the soong property with the same name")
     parser.add_argument('--privileged', action = 'store_true', help = "the value of the soong property with the same name")
+    parser.add_argument('--uncompress-priv-app-dex', action = 'store_true', help = "the value of the product variable with the same name")
     parser.add_argument('apk', help = "the apk to check")
     parser.add_argument('stampfile', help = "a file to touch if successful")
     args = parser.parse_args()
diff --git a/scripts/gen-kotlin-build-file.py b/scripts/gen-kotlin-build-file.py
index 99afdca..8b7876f 100644
--- a/scripts/gen-kotlin-build-file.py
+++ b/scripts/gen-kotlin-build-file.py
@@ -37,7 +37,7 @@
   parser.add_argument('--out', dest='out',
                       help='file to which the module.xml contents will be written.')
   parser.add_argument('--classpath', dest='classpath', action='append', default=[],
-                      help='classpath to pass to kotlinc.')
+                      help='file containing classpath to pass to kotlinc.')
   parser.add_argument('--name', dest='name',
                       help='name of the module.')
   parser.add_argument('--out_dir', dest='out_dir',
@@ -65,8 +65,8 @@
     f.write('  <module name="%s" type="java-production" outputDir="%s">\n' % (args.name, args.out_dir or ''))
 
     # Print classpath entries
-    for c in args.classpath:
-      for entry in c.split(':'):
+    for classpath_rsp_file in args.classpath:
+      for entry in NinjaRspFileReader(classpath_rsp_file):
         path = os.path.abspath(entry)
         f.write('    <classpath path="%s"/>\n' % path)
 
diff --git a/scripts/gen_build_prop.py b/scripts/gen_build_prop.py
new file mode 100644
index 0000000..b82c8f9
--- /dev/null
+++ b/scripts/gen_build_prop.py
@@ -0,0 +1,647 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""A tool for generating {partition}/build.prop"""
+
+import argparse
+import contextlib
+import json
+import os
+import subprocess
+import sys
+
+TEST_KEY_DIR = "build/make/target/product/security"
+
+def get_build_variant(product_config):
+  if product_config["Eng"]:
+    return "eng"
+  elif product_config["Debuggable"]:
+    return "userdebug"
+  else:
+    return "user"
+
+def get_build_flavor(product_config):
+  build_flavor = product_config["DeviceProduct"] + "-" + get_build_variant(product_config)
+  if "address" in product_config.get("SanitizeDevice", []) and "_asan" not in build_flavor:
+    build_flavor += "_asan"
+  return build_flavor
+
+def get_build_keys(product_config):
+  default_cert = product_config.get("DefaultAppCertificate", "")
+  if "ROM_BUILDTYPE" in product_config:
+    return "release-keys"
+  elif default_cert == os.path.join(TEST_KEY_DIR, "testKey"):
+    return "test-keys"
+  else:
+    return "dev-keys"
+
+def override_config(config):
+  if "PRODUCT_BUILD_PROP_OVERRIDES" in config:
+    current_key = None
+    props_overrides = {}
+
+    for var in config["PRODUCT_BUILD_PROP_OVERRIDES"]:
+      if "=" in var:
+        current_key, value = var.split("=")
+        props_overrides[current_key] = value
+      else:
+        props_overrides[current_key] += f" {var}"
+
+    for key, value in props_overrides.items():
+      if key not in config:
+        print(f"Key \"{key}\" isn't a valid prop override", file=sys.stderr)
+        sys.exit(1)
+      config[key] = value
+
+def parse_args():
+  """Parse commandline arguments."""
+  parser = argparse.ArgumentParser()
+  parser.add_argument("--build-fingerprint-file", required=True, type=argparse.FileType("r"))
+  parser.add_argument("--build-hostname-file", required=True, type=argparse.FileType("r"))
+  parser.add_argument("--build-number-file", required=True, type=argparse.FileType("r"))
+  parser.add_argument("--build-thumbprint-file", type=argparse.FileType("r"))
+  parser.add_argument("--build-username", required=True)
+  parser.add_argument("--date-file", required=True, type=argparse.FileType("r"))
+  parser.add_argument("--platform-preview-sdk-fingerprint-file", required=True, type=argparse.FileType("r"))
+  parser.add_argument("--prop-files", action="append", type=argparse.FileType("r"), default=[])
+  parser.add_argument("--product-config", required=True, type=argparse.FileType("r"))
+  parser.add_argument("--partition", required=True)
+  parser.add_argument("--build-broken-dup-sysprop", action="store_true", default=False)
+
+  parser.add_argument("--out", required=True, type=argparse.FileType("w"))
+
+  args = parser.parse_args()
+
+  # post process parse_args requiring manual handling
+  args.config = json.load(args.product_config)
+  config = args.config
+
+  config["BuildFlavor"] = get_build_flavor(config)
+  config["BuildKeys"] = get_build_keys(config)
+  config["BuildVariant"] = get_build_variant(config)
+
+  config["BuildFingerprint"] = args.build_fingerprint_file.read().strip()
+  config["BuildHostname"] = args.build_hostname_file.read().strip()
+  config["BuildNumber"] = args.build_number_file.read().strip()
+  config["BuildUsername"] = args.build_username
+
+  build_version_tags_list = config["BuildVersionTags"]
+  if config["BuildType"] == "debug":
+    build_version_tags_list.append("debug")
+  build_version_tags_list.append(config["BuildKeys"])
+  build_version_tags = ",".join(sorted(set(build_version_tags_list)))
+  config["BuildVersionTags"] = build_version_tags
+
+  raw_date = args.date_file.read().strip()
+  config["Date"] = subprocess.check_output(["date", "-d", f"@{raw_date}"], text=True).strip()
+  config["DateUtc"] = subprocess.check_output(["date", "-d", f"@{raw_date}", "+%s"], text=True).strip()
+
+  # build_desc is human readable strings that describe this build. This has the same info as the
+  # build fingerprint.
+  # e.g. "aosp_cf_x86_64_phone-userdebug VanillaIceCream MAIN eng.20240319.143939 test-keys"
+  config["BuildDesc"] = f"{config['DeviceProduct']}-{config['BuildVariant']} " \
+                        f"{config['Platform_version_name']} {config['BuildId']} " \
+                        f"{config['BuildNumber']} {config['BuildVersionTags']}"
+
+  config["PlatformPreviewSdkFingerprint"] = args.platform_preview_sdk_fingerprint_file.read().strip()
+
+  if args.build_thumbprint_file:
+    config["BuildThumbprint"] = args.build_thumbprint_file.read().strip()
+
+  config["OmniRomDesc"] = config["BuildDesc"]
+  config["OmniRomDevice"] = config["DeviceName"]
+
+  override_config(config)
+
+  append_additional_system_props(args)
+  append_additional_vendor_props(args)
+  append_additional_product_props(args)
+
+  return args
+
+def generate_common_build_props(args):
+  print("####################################")
+  print("# from generate_common_build_props")
+  print("# These properties identify this partition image.")
+  print("####################################")
+
+  config = args.config
+  partition = args.partition
+
+  if partition == "system":
+    print(f"ro.product.{partition}.brand={config['SystemBrand']}")
+    print(f"ro.product.{partition}.device={config['SystemDevice']}")
+    print(f"ro.product.{partition}.manufacturer={config['SystemManufacturer']}")
+    print(f"ro.product.{partition}.model={config['SystemModel']}")
+    print(f"ro.product.{partition}.name={config['SystemName']}")
+  else:
+    print(f"ro.product.{partition}.brand={config['ProductBrand']}")
+    print(f"ro.product.{partition}.device={config['DeviceName']}")
+    print(f"ro.product.{partition}.manufacturer={config['ProductManufacturer']}")
+    print(f"ro.product.{partition}.model={config['ProductModel']}")
+    print(f"ro.product.{partition}.name={config['DeviceProduct']}")
+
+  if partition != "system":
+    if config["ProductModelForAttestation"]:
+        print(f"ro.product.model_for_attestation={config['ProductModelForAttestation']}")
+    if config["ProductBrandForAttestation"]:
+        print(f"ro.product.brand_for_attestation={config['ProductBrandForAttestation']}")
+    if config["ProductNameForAttestation"]:
+        print(f"ro.product.name_for_attestation={config['ProductNameForAttestation']}")
+    if config["ProductDeviceForAttestation"]:
+        print(f"ro.product.device_for_attestation={config['ProductDeviceForAttestation']}")
+    if config["ProductManufacturerForAttestation"]:
+        print(f"ro.product.manufacturer_for_attestation={config['ProductManufacturerForAttestation']}")
+
+  if config["ZygoteForce64"]:
+    if partition == "vendor":
+      print(f"ro.{partition}.product.cpu.abilist={config['DeviceAbiList64']}")
+      print(f"ro.{partition}.product.cpu.abilist32=")
+      print(f"ro.{partition}.product.cpu.abilist64={config['DeviceAbiList64']}")
+  else:
+    if partition == "system" or partition == "vendor" or partition == "odm":
+      print(f"ro.{partition}.product.cpu.abilist={config['DeviceAbiList']}")
+      print(f"ro.{partition}.product.cpu.abilist32={config['DeviceAbiList32']}")
+      print(f"ro.{partition}.product.cpu.abilist64={config['DeviceAbiList64']}")
+
+  print(f"ro.{partition}.build.date={config['Date']}")
+  print(f"ro.{partition}.build.date.utc={config['DateUtc']}")
+  # Allow optional assignments for ARC forward-declarations (b/249168657)
+  # TODO: Remove any tag-related inconsistencies once the goals from
+  # go/arc-android-sigprop-changes have been achieved.
+  print(f"ro.{partition}.build.fingerprint?={config['BuildFingerprint']}")
+  print(f"ro.{partition}.build.id?={config['BuildId']}")
+  print(f"ro.{partition}.build.tags?={config['BuildVersionTags']}")
+  print(f"ro.{partition}.build.type={config['BuildVariant']}")
+  print(f"ro.{partition}.build.version.incremental={config['BuildNumber']}")
+  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']}")
+
+def generate_build_info(args):
+  print()
+  print("####################################")
+  print("# from gen_build_prop.py:generate_build_info")
+  print("####################################")
+  print("# begin build properties")
+
+  config = args.config
+  build_flags = config["BuildFlags"]
+
+  print(f"ro.build.fingerprint?={config['BuildFingerprint']}")
+
+  # The ro.build.id will be set dynamically by init, by appending the unique vbmeta digest.
+  if config["BoardUseVbmetaDigestInFingerprint"]:
+    print(f"ro.build.legacy.id={config['BuildId']}")
+  else:
+    print(f"ro.build.id?={config['BuildId']}")
+
+  # ro.build.display.id is shown under Settings -> About Phone
+  if config["BuildVariant"] == "user":
+    # User builds should show:
+    # release build number or branch.buld_number non-release builds
+
+    # Dev. branches should have DISPLAY_BUILD_NUMBER set
+    if config["DisplayBuildNumber"]:
+      print(f"ro.build.display.id?={config['BuildId']}.{config['BuildNumber']} {config['BuildKeys']}")
+    else:
+      print(f"ro.build.display.id?={config['BuildId']} {config['BuildKeys']}")
+  else:
+    # Non-user builds should show detailed build information (See build desc above)
+    print(f"ro.build.display.id?={config['OmniRomDesc']}")
+    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.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']}")
+  print(f"ro.build.version.all_codenames={','.join(config['Platform_version_active_codenames'])}")
+  print(f"ro.build.version.known_codenames={config['Platform_version_known_codenames']}")
+  print(f"ro.build.version.release={config['Platform_version_last_stable']}")
+  print(f"ro.build.version.release_or_codename={config['Platform_version_name']}")
+  print(f"ro.build.version.release_or_preview_display={config['Platform_display_version_name']}")
+  print(f"ro.build.version.security_patch={config['Platform_security_patch']}")
+  print(f"ro.build.version.base_os={config['Platform_base_os']}")
+  print(f"ro.build.version.min_supported_target_sdk={build_flags['RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION']}")
+  print(f"ro.build.date={config['Date']}")
+  print(f"ro.build.date.utc={config['DateUtc']}")
+  print(f"ro.build.type={config['BuildVariant']}")
+  print(f"ro.build.user={config['BuildUsername']}")
+  print(f"ro.build.host={config['BuildHostname']}")
+  # TODO: Remove any tag-related optional property declarations once the goals
+  # from go/arc-android-sigprop-changes have been achieved.
+  print(f"ro.build.tags?={config['BuildVersionTags']}")
+  # ro.build.flavor are used only by the test harness to distinguish builds.
+  # Only add _asan for a sanitized build if it isn't already a part of the
+  # flavor (via a dedicated lunch config for example).
+  print(f"ro.build.flavor={config['BuildFlavor']}")
+
+  print(f"ro.omni.device={config['OmniRomDevice']}")
+
+  # These values are deprecated, use "ro.product.cpu.abilist"
+  # instead (see below).
+  print(f"# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,")
+  print(f"# use ro.product.cpu.abilist instead.")
+  print(f"ro.product.cpu.abi={config['DeviceAbi'][0]}")
+  if len(config["DeviceAbi"]) > 1:
+    print(f"ro.product.cpu.abi2={config['DeviceAbi'][1]}")
+
+  if config["ProductLocales"]:
+    print(f"ro.product.locale={config['ProductLocales'][0]}")
+  print(f"ro.wifi.channels={' '.join(config['ProductDefaultWifiChannels'])}")
+
+  print(f"# ro.build.product is obsolete; use ro.product.device")
+  print(f"ro.build.product={config['DeviceName']}")
+
+  print(f"# Do not try to parse description or thumbprint")
+  print(f"ro.build.description?={config['BuildDesc']}")
+  if "BuildThumbprint" in config:
+    print(f"ro.build.thumbprint={config['BuildThumbprint']}")
+
+  print(f"# end build properties")
+
+def write_properties_from_file(file):
+  print()
+  print("####################################")
+  print(f"# from {file.name}")
+  print("####################################")
+  print(file.read(), end="")
+
+def write_properties_from_variable(name, props, build_broken_dup_sysprop):
+  print()
+  print("####################################")
+  print(f"# from variable {name}")
+  print("####################################")
+
+  # Implement the legacy behavior when BUILD_BROKEN_DUP_SYSPROP is on.
+  # Optional assignments are all converted to normal assignments and
+  # when their duplicates the first one wins.
+  if build_broken_dup_sysprop:
+    processed_props = []
+    seen_props = set()
+    for line in props:
+      line = line.replace("?=", "=")
+      key, value = line.split("=", 1)
+      if key in seen_props:
+        continue
+      seen_props.add(key)
+      processed_props.append(line)
+    props = processed_props
+
+  for line in props:
+    print(line)
+
+def append_additional_system_props(args):
+  props = []
+
+  config = args.config
+
+  # Add the product-defined properties to the build properties.
+  if not config["PropertySplitEnabled"] or not config["VendorImageFileSystemType"]:
+    if "PRODUCT_PROPERTY_OVERRIDES" in config:
+      props += config["PRODUCT_PROPERTY_OVERRIDES"]
+
+  props.append(f"ro.treble.enabled={'true' if config['FullTreble'] else 'false'}")
+  # Set ro.llndk.api_level to show the maximum vendor API level that the LLNDK
+  # in the system partition supports.
+  if config["VendorApiLevel"]:
+    props.append(f"ro.llndk.api_level={config['VendorApiLevel']}")
+
+  # Sets ro.actionable_compatible_property.enabled to know on runtime whether
+  # the allowed list of actionable compatible properties is enabled or not.
+  props.append("ro.actionable_compatible_property.enabled=true")
+
+  # Enable core platform API violation warnings on userdebug and eng builds.
+  if config["BuildVariant"] != "user":
+    props.append("persist.debug.dalvik.vm.core_platform_api_policy=just-warn")
+
+  # Define ro.sanitize.<name> properties for all global sanitizers.
+  for sanitize_target in config["SanitizeDevice"]:
+    props.append(f"ro.sanitize.{sanitize_target}=true")
+
+  # Sets the default value of ro.postinstall.fstab.prefix to /system.
+  # Device board config should override the value to /product when needed by:
+  #
+  #     PRODUCT_PRODUCT_PROPERTIES += ro.postinstall.fstab.prefix=/product
+  #
+  # It then uses ${ro.postinstall.fstab.prefix}/etc/fstab.postinstall to
+  # mount system_other partition.
+  props.append("ro.postinstall.fstab.prefix=/system")
+
+  enable_target_debugging = True
+  enable_dalvik_lock_contention_logging = True
+  if config["BuildVariant"] == "user" or config["BuildVariant"] == "userdebug":
+    # Target is secure in user builds.
+    props.append("ro.secure=1")
+    props.append("security.perf_harden=1")
+
+    if config["BuildVariant"] == "user":
+      # Disable debugging in plain user builds.
+      props.append("ro.adb.secure=1")
+      enable_target_debugging = False
+      enable_dalvik_lock_contention_logging = False
+    else:
+      # Disable debugging in userdebug builds if PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG
+      # is set.
+      if config["ProductNotDebuggableInUserdebug"]:
+        enable_target_debugging = False
+
+    # Disallow mock locations by default for user builds
+    props.append("ro.allow.mock.location=0")
+  else:
+    # Turn on checkjni for non-user builds.
+    props.append("ro.kernel.android.checkjni=1")
+    # Set device insecure for non-user builds.
+    props.append("ro.secure=0")
+    # Allow mock locations by default for non user builds
+    props.append("ro.allow.mock.location=1")
+
+  if enable_dalvik_lock_contention_logging:
+    # Enable Dalvik lock contention logging.
+    props.append("dalvik.vm.lockprof.threshold=500")
+
+  if enable_target_debugging:
+    # Target is more debuggable and adbd is on by default
+    props.append("ro.debuggable=1")
+  else:
+    # Target is less debuggable and adbd is off by default
+    props.append("ro.debuggable=0")
+
+  if config["BuildVariant"] == "eng":
+    if "ro.setupwizard.mode=ENABLED" in props:
+      # Don't require the setup wizard on eng builds
+      props = list(filter(lambda x: not x.startswith("ro.setupwizard.mode="), props))
+      props.append("ro.setupwizard.mode=OPTIONAL")
+
+    if not config["SdkBuild"]:
+      # To speedup startup of non-preopted builds, don't verify or compile the boot image.
+      props.append("dalvik.vm.image-dex2oat-filter=extract")
+    # b/323566535
+    props.append("init.svc_debug.no_fatal.zygote=true")
+
+  if config["SdkBuild"]:
+    props.append("xmpp.auto-presence=true")
+    props.append("ro.config.nocheckin=yes")
+
+  props.append("net.bt.name=Android")
+
+  # This property is set by flashing debug boot image, so default to false.
+  props.append("ro.force.debuggable=0")
+
+  config["ADDITIONAL_SYSTEM_PROPERTIES"] = props
+
+def append_additional_vendor_props(args):
+  props = []
+
+  config = args.config
+  build_flags = config["BuildFlags"]
+
+  # Add cpu properties for bionic and ART.
+  props.append(f"ro.bionic.arch={config['DeviceArch']}")
+  props.append(f"ro.bionic.cpu_variant={config['DeviceCpuVariantRuntime']}")
+  props.append(f"ro.bionic.2nd_arch={config['DeviceSecondaryArch']}")
+  props.append(f"ro.bionic.2nd_cpu_variant={config['DeviceSecondaryCpuVariantRuntime']}")
+
+  props.append(f"persist.sys.dalvik.vm.lib.2=libart.so")
+  props.append(f"dalvik.vm.isa.{config['DeviceArch']}.variant={config['Dex2oatTargetCpuVariantRuntime']}")
+  if config["Dex2oatTargetInstructionSetFeatures"]:
+    props.append(f"dalvik.vm.isa.{config['DeviceArch']}.features={config['Dex2oatTargetInstructionSetFeatures']}")
+
+  if config["DeviceSecondaryArch"]:
+    props.append(f"dalvik.vm.isa.{config['DeviceSecondaryArch']}.variant={config['SecondaryDex2oatCpuVariantRuntime']}")
+    if config["SecondaryDex2oatInstructionSetFeatures"]:
+      props.append(f"dalvik.vm.isa.{config['DeviceSecondaryArch']}.features={config['SecondaryDex2oatInstructionSetFeatures']}")
+
+  # Although these variables are prefixed with TARGET_RECOVERY_, they are also needed under charger
+  # mode (via libminui).
+  if config["RecoveryDefaultRotation"]:
+    props.append(f"ro.minui.default_rotation={config['RecoveryDefaultRotation']}")
+
+  if config["RecoveryOverscanPercent"]:
+    props.append(f"ro.minui.overscan_percent={config['RecoveryOverscanPercent']}")
+
+  if config["RecoveryPixelFormat"]:
+    props.append(f"ro.minui.pixel_format={config['RecoveryPixelFormat']}")
+
+  if "UseDynamicPartitions" in config:
+    props.append(f"ro.boot.dynamic_partitions={'true' if config['UseDynamicPartitions'] else 'false'}")
+
+  if "RetrofitDynamicPartitions" in config:
+    props.append(f"ro.boot.dynamic_partitions_retrofit={'true' if config['RetrofitDynamicPartitions'] else 'false'}")
+
+  if config["ShippingApiLevel"]:
+    props.append(f"ro.product.first_api_level={config['ShippingApiLevel']}")
+
+  if config["ShippingVendorApiLevel"]:
+    props.append(f"ro.vendor.api_level={config['ShippingVendorApiLevel']}")
+
+  if config["BuildVariant"] != "user" and config["BuildDebugfsRestrictionsEnabled"]:
+    props.append(f"ro.product.debugfs_restrictions.enabled=true")
+
+  # Vendors with GRF must define BOARD_SHIPPING_API_LEVEL for the vendor API level.
+  # This must not be defined for the non-GRF devices.
+  # The values of the GRF properties will be verified by post_process_props.py
+  if config["BoardShippingApiLevel"]:
+    props.append(f"ro.board.first_api_level={config['BoardShippingApiLevel']}")
+
+  # 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']}")
+
+  # RELEASE_BOARD_API_LEVEL_FROZEN is true when the vendor API surface is frozen.
+  if build_flags["RELEASE_BOARD_API_LEVEL_FROZEN"]:
+    props.append(f"ro.board.api_frozen=true")
+
+  # Set build prop. This prop is read by ota_from_target_files when generating OTA,
+  # to decide if VABC should be disabled.
+  if config["DontUseVabcOta"]:
+    props.append(f"ro.vendor.build.dont_use_vabc=true")
+
+  # Set the flag in vendor. So VTS would know if the new fingerprint format is in use when
+  # the system images are replaced by GSI.
+  if config["BoardUseVbmetaDigestInFingerprint"]:
+    props.append(f"ro.vendor.build.fingerprint_has_digest=1")
+
+  props.append(f"ro.vendor.build.security_patch={config['VendorSecurityPatch']}")
+  props.append(f"ro.product.board={config['BootloaderBoardName']}")
+  props.append(f"ro.board.platform={config['BoardPlatform']}")
+  props.append(f"ro.hwui.use_vulkan={'true' if config['UsesVulkan'] else 'false'}")
+
+  if config["ScreenDensity"]:
+    props.append(f"ro.sf.lcd_density={config['ScreenDensity']}")
+
+  if "AbOtaUpdater" in config:
+    props.append(f"ro.build.ab_update={'true' if config['AbOtaUpdater'] else 'false'}")
+    if config["AbOtaUpdater"]:
+      props.append(f"ro.vendor.build.ab_ota_partitions={config['AbOtaPartitions']}")
+
+  config["ADDITIONAL_VENDOR_PROPERTIES"] = props
+
+def append_additional_product_props(args):
+  props = []
+
+  config = args.config
+
+  # Add the system server compiler filter if they are specified for the product.
+  if config["SystemServerCompilerFilter"]:
+    props.append(f"dalvik.vm.systemservercompilerfilter={config['SystemServerCompilerFilter']}")
+
+  # Add the 16K developer args if it is defined for the product.
+  props.append(f"ro.product.build.16k_page.enabled={'true' if config['Product16KDeveloperOption'] else 'false'}")
+
+  props.append(f"ro.product.page_size={16384 if config['TargetBoots16K'] else 4096}")
+
+  props.append(f"ro.build.characteristics={config['AAPTCharacteristics']}")
+
+  if "AbOtaUpdater" in config and config["AbOtaUpdater"]:
+    props.append(f"ro.product.ab_ota_partitions={config['AbOtaPartitions']}")
+
+  # Set this property for VTS to skip large page size tests on unsupported devices.
+  props.append(f"ro.product.cpu.pagesize.max={config['DeviceMaxPageSizeSupported']}")
+
+  if config["NoBionicPageSizeMacro"]:
+    props.append(f"ro.product.build.no_bionic_page_size_macro=true")
+
+  # This is a temporary system property that controls the ART module. The plan is
+  # to remove it by Aug 2025, at which time Mainline updates of the ART module
+  # will ignore it as well.
+  # If the value is "default", it will be mangled by post_process_props.py.
+  props.append(f"ro.dalvik.vm.enable_uffd_gc={config['EnableUffdGc']}")
+
+  config["ADDITIONAL_PRODUCT_PROPERTIES"] = props
+
+def build_system_prop(args):
+  config = args.config
+
+  # Order matters here. When there are duplicates, the last one wins.
+  # TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter
+  variables = [
+    "ADDITIONAL_SYSTEM_PROPERTIES",
+    "PRODUCT_SYSTEM_PROPERTIES",
+    # TODO(b/117892318): deprecate this
+    "PRODUCT_SYSTEM_DEFAULT_PROPERTIES",
+  ]
+
+  if not config["PropertySplitEnabled"]:
+    variables += [
+      "ADDITIONAL_VENDOR_PROPERTIES",
+      "PRODUCT_VENDOR_PROPERTIES",
+    ]
+
+  build_prop(args, gen_build_info=True, gen_common_build_props=True, variables=variables)
+
+def build_system_ext_prop(args):
+  config = args.config
+
+  # Order matters here. When there are duplicates, the last one wins.
+  # TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter
+  variables = ["PRODUCT_SYSTEM_EXT_PROPERTIES"]
+
+  build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
+
+'''
+def build_vendor_prop(args):
+  config = args.config
+
+  # Order matters here. When there are duplicates, the last one wins.
+  # TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter
+  variables = []
+  if config["PropertySplitEnabled"]:
+    variables += [
+      "ADDITIONAL_VENDOR_PROPERTIES",
+      "PRODUCT_VENDOR_PROPERTIES",
+      # TODO(b/117892318): deprecate this
+      "PRODUCT_DEFAULT_PROPERTY_OVERRIDES",
+      "PRODUCT_PROPERTY_OVERRIDES",
+    ]
+
+  build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
+'''
+
+def build_product_prop(args):
+  config = args.config
+
+  # Order matters here. When there are duplicates, the last one wins.
+  # TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter
+  variables = [
+    "ADDITIONAL_PRODUCT_PROPERTIES",
+    "OMNI_PRODUCT_PROPERTIES",
+    "PRODUCT_PRODUCT_PROPERTIES",
+  ]
+
+  gen_common_build_props = True
+
+  # Skip common /product properties generation if device released before R and
+  # has no product partition. This is the first part of the check.
+  if config["Shipping_api_level"] and int(config["Shipping_api_level"]) < 30:
+    gen_common_build_props = False
+
+  # The second part of the check - always generate common properties for the
+  # devices with product partition regardless of shipping level.
+  if config["UsesProductImage"]:
+    gen_common_build_props = True
+
+  build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
+
+  if config["OemProperties"]:
+    print("####################################")
+    print("# PRODUCT_OEM_PROPERTIES")
+    print("####################################")
+
+    for prop in config["OemProperties"]:
+      print(f"import /oem/oem.prop {prop}")
+
+def build_odm_prop(args):
+  variables = ["ADDITIONAL_ODM_PROPERTIES", "PRODUCT_ODM_PROPERTIES"]
+  build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
+
+def build_prop(args, gen_build_info, gen_common_build_props, variables):
+  config = args.config
+
+  if gen_common_build_props:
+    generate_common_build_props(args)
+
+  if gen_build_info:
+    generate_build_info(args)
+
+  for prop_file in args.prop_files:
+    write_properties_from_file(prop_file)
+
+  for variable in variables:
+    if variable in config:
+      write_properties_from_variable(variable, config[variable], args.build_broken_dup_sysprop)
+
+def main():
+  args = parse_args()
+
+  with contextlib.redirect_stdout(args.out):
+    match args.partition:
+      case "system":
+        build_system_prop(args)
+      case "system_ext":
+        build_system_ext_prop(args)
+      case "odm":
+        build_odm_prop(args)
+      case "product":
+        build_product_prop(args)
+      # case "vendor":  # NOT IMPLEMENTED
+      #  build_vendor_prop(args)
+      case _:
+        sys.exit(f"not supported partition {args.partition}")
+
+if __name__ == "__main__":
+  main()
diff --git a/scripts/manifest.py b/scripts/manifest.py
index 81f9c61..32603e8 100755
--- a/scripts/manifest.py
+++ b/scripts/manifest.py
@@ -23,9 +23,40 @@
 android_ns = 'http://schemas.android.com/apk/res/android'
 
 
+def get_or_create_applications(doc, manifest):
+  """Get all <application> tags from the manifest, or create one if none exist.
+  Multiple <application> tags may exist when manifest feature flagging is used.
+  """
+  applications = get_children_with_tag(manifest, 'application')
+  if len(applications) == 0:
+    application = doc.createElement('application')
+    indent = get_indent(manifest.firstChild, 1)
+    first = manifest.firstChild
+    manifest.insertBefore(doc.createTextNode(indent), first)
+    manifest.insertBefore(application, first)
+    applications.append(application)
+  return applications
+
+
+def get_or_create_uses_sdks(doc, manifest):
+  """Get all <uses-sdk> tags from the manifest, or create one if none exist.
+  Multiple <uses-sdk> tags may exist when manifest feature flagging is used.
+  """
+  uses_sdks = get_children_with_tag(manifest, 'uses-sdk')
+  if len(uses_sdks) == 0:
+    uses_sdk = doc.createElement('uses-sdk')
+    indent = get_indent(manifest.firstChild, 1)
+    manifest.insertBefore(uses_sdk, manifest.firstChild)
+
+    # Insert an indent before uses-sdk to line it up with the indentation of the
+    # other children of the <manifest> tag.
+    manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild)
+    uses_sdks.append(uses_sdk)
+  return uses_sdks
+
 def get_children_with_tag(parent, tag_name):
   children = []
-  for child in  parent.childNodes:
+  for child in parent.childNodes:
     if child.nodeType == minidom.Node.ELEMENT_NODE and \
        child.tagName == tag_name:
       children.append(child)
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index b101259..1e32d1d 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -25,10 +25,7 @@
 import sys
 from xml.dom import minidom
 
-from manifest import android_ns
-from manifest import get_children_with_tag
-from manifest import parse_manifest
-from manifest import write_xml
+from manifest import *
 
 
 class ManifestMismatchError(Exception):
@@ -122,7 +119,7 @@
     # handles module names specified in Android.bp properties. However not all
     # <uses-library> entries in the manifest correspond to real modules: some of
     # the optional libraries may be missing at build time. Therefor this script
-    # accepts raw module names as spelled in Android.bp/Amdroid.mk and trims the
+    # accepts raw module names as spelled in Android.bp/Android.mk and trims the
     # optional namespace part manually.
     required = trim_namespace_parts(required)
     optional = trim_namespace_parts(optional)
@@ -205,15 +202,9 @@
     """Extract <uses-library> tags from the manifest."""
 
     manifest = parse_manifest(xml)
-    elems = get_children_with_tag(manifest, 'application')
-    if len(elems) > 1:
-        raise RuntimeError('found multiple <application> tags')
-    if not elems:
-        return [], [], []
-
-    application = elems[0]
-
-    libs = get_children_with_tag(application, 'uses-library')
+    libs = [child
+            for application in get_or_create_applications(xml, manifest)
+            for child in get_children_with_tag(application, 'uses-library')]
 
     required = [uses_library_name(x) for x in libs if uses_library_required(x)]
     optional = [
@@ -266,7 +257,7 @@
     manifest: manifest (either parsed XML or aapt dump of APK)
     is_apk:   if the manifest comes from an APK or an XML file
     """
-    if is_apk: #pylint: disable=no-else-return
+    if is_apk:  #pylint: disable=no-else-return
         return extract_target_sdk_version_apk(manifest)
     else:
         return extract_target_sdk_version_xml(manifest)
@@ -376,7 +367,7 @@
 
             # Create a status file that is empty on success, or contains an
             # error message on failure. When exceptions are suppressed,
-            # dexpreopt command command will check file size to determine if
+            # dexpreopt command will check file size to determine if
             # the check has failed.
             if args.enforce_uses_libraries_status:
                 with open(args.enforce_uses_libraries_status, 'w') as f:
@@ -386,7 +377,7 @@
         if args.extract_target_sdk_version:
             try:
                 print(extract_target_sdk_version(manifest, is_apk))
-            except: #pylint: disable=bare-except
+            except:  #pylint: disable=bare-except
                 # Failed; don't crash, return "any" SDK version. This will
                 # result in dexpreopt not adding any compatibility libraries.
                 print(10000)
diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py
index 8003b3e..abe0d8b 100755
--- a/scripts/manifest_check_test.py
+++ b/scripts/manifest_check_test.py
@@ -44,8 +44,8 @@
 class EnforceUsesLibrariesTest(unittest.TestCase):
     """Unit tests for add_extract_native_libs function."""
 
-    def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[],
-                 missing_optional_uses_libraries=[]): #pylint: disable=dangerous-default-value
+    def run_test(self, xml, apk, uses_libraries=(), optional_uses_libraries=(),
+                 missing_optional_uses_libraries=()):  #pylint: disable=dangerous-default-value
         doc = minidom.parseString(xml)
         try:
             relax = False
@@ -114,14 +114,14 @@
         self.assertFalse(matches)
 
     def test_missing_uses_library(self):
-        xml = self.xml_tmpl % ('')
-        apk = self.apk_tmpl % ('')
+        xml = self.xml_tmpl % ''
+        apk = self.apk_tmpl % ''
         matches = self.run_test(xml, apk, uses_libraries=['foo'])
         self.assertFalse(matches)
 
     def test_missing_optional_uses_library(self):
-        xml = self.xml_tmpl % ('')
-        apk = self.apk_tmpl % ('')
+        xml = self.xml_tmpl % ''
+        apk = self.apk_tmpl % ''
         matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
         self.assertFalse(matches)
 
@@ -234,6 +234,32 @@
             optional_uses_libraries=['//x/y/z:bar'])
         self.assertTrue(matches)
 
+    def test_multiple_applications(self):
+        xml = """<?xml version="1.0" encoding="utf-8"?>
+            <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+                <application android:featureFlag="foo">
+                    <uses-library android:name="foo" />
+                    <uses-library android:name="bar" android:required="false" />
+                </application>
+                <application android:featureFlag="!foo">
+                    <uses-library android:name="foo" />
+                    <uses-library android:name="qux" android:required="false" />
+                </application>
+            </manifest>
+        """
+        apk = self.apk_tmpl % ('\n'.join([
+            uses_library_apk('foo'),
+            uses_library_apk('bar', required_apk(False)),
+            uses_library_apk('foo'),
+            uses_library_apk('qux', required_apk(False))
+        ]))
+        matches = self.run_test(
+            xml,
+            apk,
+            uses_libraries=['//x/y/z:foo'],
+            optional_uses_libraries=['//x/y/z:bar', '//x/y/z:qux'])
+        self.assertTrue(matches)
+
 
 class ExtractTargetSdkVersionTest(unittest.TestCase):
 
@@ -256,12 +282,12 @@
         "targetSdkVersion:'%s'\n"
         "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n")
 
-    def test_targert_sdk_version_28(self):
+    def test_target_sdk_version_28(self):
         xml = self.xml_tmpl % '28'
         apk = self.apk_tmpl % '28'
         self.run_test(xml, apk, '28')
 
-    def test_targert_sdk_version_29(self):
+    def test_target_sdk_version_29(self):
         xml = self.xml_tmpl % '29'
         apk = self.apk_tmpl % '29'
         self.run_test(xml, apk, '29')
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index 58079aa..9847ad5 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -23,15 +23,7 @@
 from xml.dom import minidom
 
 
-from manifest import android_ns
-from manifest import compare_version_gt
-from manifest import ensure_manifest_android_ns
-from manifest import find_child_with_attribute
-from manifest import get_children_with_tag
-from manifest import get_indent
-from manifest import parse_manifest
-from manifest import write_xml
-
+from manifest import *
 
 def parse_args():
   """Parse commandline arguments."""
@@ -48,9 +40,9 @@
   parser.add_argument('--library', dest='library', action='store_true',
                       help='manifest is for a static library')
   parser.add_argument('--uses-library', dest='uses_libraries', action='append',
-                      help='specify additional <uses-library> tag to add. android:requred is set to true')
+                      help='specify additional <uses-library> tag to add. android:required is set to true')
   parser.add_argument('--optional-uses-library', dest='optional_uses_libraries', action='append',
-                      help='specify additional <uses-library> tag to add. android:requred is set to false')
+                      help='specify additional <uses-library> tag to add. android:required is set to false')
   parser.add_argument('--uses-non-sdk-api', dest='uses_non_sdk_api', action='store_true',
                       help='manifest is for a package built against the platform')
   parser.add_argument('--logging-parent', dest='logging_parent', default='',
@@ -91,47 +83,33 @@
 
   manifest = parse_manifest(doc)
 
-  # Get or insert the uses-sdk element
-  uses_sdk = get_children_with_tag(manifest, 'uses-sdk')
-  if len(uses_sdk) > 1:
-    raise RuntimeError('found multiple uses-sdk elements')
-  elif len(uses_sdk) == 1:
-    element = uses_sdk[0]
-  else:
-    element = doc.createElement('uses-sdk')
-    indent = get_indent(manifest.firstChild, 1)
-    manifest.insertBefore(element, manifest.firstChild)
-
-    # Insert an indent before uses-sdk to line it up with the indentation of the
-    # other children of the <manifest> tag.
-    manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild)
-
-  # Get or insert the minSdkVersion attribute.  If it is already present, make
-  # sure it as least the requested value.
-  min_attr = element.getAttributeNodeNS(android_ns, 'minSdkVersion')
-  if min_attr is None:
-    min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion')
-    min_attr.value = min_sdk_version
-    element.setAttributeNode(min_attr)
-  else:
-    if compare_version_gt(min_sdk_version, min_attr.value):
+  for uses_sdk in get_or_create_uses_sdks(doc, manifest):
+    # Get or insert the minSdkVersion attribute.  If it is already present, make
+    # sure it as least the requested value.
+    min_attr = uses_sdk.getAttributeNodeNS(android_ns, 'minSdkVersion')
+    if min_attr is None:
+      min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion')
       min_attr.value = min_sdk_version
-
-  # Insert the targetSdkVersion attribute if it is missing.  If it is already
-  # present leave it as is.
-  target_attr = element.getAttributeNodeNS(android_ns, 'targetSdkVersion')
-  if target_attr is None:
-    target_attr = doc.createAttributeNS(android_ns, 'android:targetSdkVersion')
-    if library:
-      # TODO(b/117122200): libraries shouldn't set targetSdkVersion at all, but
-      # ManifestMerger treats minSdkVersion="Q" as targetSdkVersion="Q" if it
-      # is empty.  Set it to something low so that it will be overriden by the
-      # main manifest, but high enough that it doesn't cause implicit
-      # permissions grants.
-      target_attr.value = '16'
+      uses_sdk.setAttributeNode(min_attr)
     else:
-      target_attr.value = target_sdk_version
-    element.setAttributeNode(target_attr)
+      if compare_version_gt(min_sdk_version, min_attr.value):
+        min_attr.value = min_sdk_version
+
+    # Insert the targetSdkVersion attribute if it is missing.  If it is already
+    # present leave it as is.
+    target_attr = uses_sdk.getAttributeNodeNS(android_ns, 'targetSdkVersion')
+    if target_attr is None:
+      target_attr = doc.createAttributeNS(android_ns, 'android:targetSdkVersion')
+      if library:
+        # TODO(b/117122200): libraries shouldn't set targetSdkVersion at all, but
+        # ManifestMerger treats minSdkVersion="Q" as targetSdkVersion="Q" if it
+        # is empty.  Set it to something low so that it will be overridden by the
+        # main manifest, but high enough that it doesn't cause implicit
+        # permissions grants.
+        target_attr.value = '16'
+      else:
+        target_attr.value = target_sdk_version
+      uses_sdk.setAttributeNode(target_attr)
 
 
 def add_logging_parent(doc, logging_parent_value):
@@ -147,37 +125,27 @@
   manifest = parse_manifest(doc)
 
   logging_parent_key = 'android.content.pm.LOGGING_PARENT'
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
+  for application in get_or_create_applications(doc, manifest):
+    indent = get_indent(application.firstChild, 2)
 
-  indent = get_indent(application.firstChild, 2)
-
-  last = application.lastChild
-  if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
-    last = None
-
-  if not find_child_with_attribute(application, 'meta-data', android_ns,
-                                   'name', logging_parent_key):
-    ul = doc.createElement('meta-data')
-    ul.setAttributeNS(android_ns, 'android:name', logging_parent_key)
-    ul.setAttributeNS(android_ns, 'android:value', logging_parent_value)
-    application.insertBefore(doc.createTextNode(indent), last)
-    application.insertBefore(ul, last)
     last = application.lastChild
+    if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
+      last = None
 
-  # align the closing tag with the opening tag if it's not
-  # indented
-  if last and last.nodeType != minidom.Node.TEXT_NODE:
-    indent = get_indent(application.previousSibling, 1)
-    application.appendChild(doc.createTextNode(indent))
+    if not find_child_with_attribute(application, 'meta-data', android_ns,
+                                     'name', logging_parent_key):
+      ul = doc.createElement('meta-data')
+      ul.setAttributeNS(android_ns, 'android:name', logging_parent_key)
+      ul.setAttributeNS(android_ns, 'android:value', logging_parent_value)
+      application.insertBefore(doc.createTextNode(indent), last)
+      application.insertBefore(ul, last)
+      last = application.lastChild
+
+    # align the closing tag with the opening tag if it's not
+    # indented
+    if last and last.nodeType != minidom.Node.TEXT_NODE:
+      indent = get_indent(application.previousSibling, 1)
+      application.appendChild(doc.createTextNode(indent))
 
 
 def add_uses_libraries(doc, new_uses_libraries, required):
@@ -192,42 +160,32 @@
   """
 
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
+  for application in get_or_create_applications(doc, manifest):
+    indent = get_indent(application.firstChild, 2)
 
-  indent = get_indent(application.firstChild, 2)
+    last = application.lastChild
+    if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
+      last = None
 
-  last = application.lastChild
-  if last is not None and last.nodeType != minidom.Node.TEXT_NODE:
-    last = None
+    for name in new_uses_libraries:
+      if find_child_with_attribute(application, 'uses-library', android_ns,
+                                   'name', name) is not None:
+        # If the uses-library tag of the same 'name' attribute value exists,
+        # respect it.
+        continue
 
-  for name in new_uses_libraries:
-    if find_child_with_attribute(application, 'uses-library', android_ns,
-                                 'name', name) is not None:
-      # If the uses-library tag of the same 'name' attribute value exists,
-      # respect it.
-      continue
+      ul = doc.createElement('uses-library')
+      ul.setAttributeNS(android_ns, 'android:name', name)
+      ul.setAttributeNS(android_ns, 'android:required', str(required).lower())
 
-    ul = doc.createElement('uses-library')
-    ul.setAttributeNS(android_ns, 'android:name', name)
-    ul.setAttributeNS(android_ns, 'android:required', str(required).lower())
+      application.insertBefore(doc.createTextNode(indent), last)
+      application.insertBefore(ul, last)
 
-    application.insertBefore(doc.createTextNode(indent), last)
-    application.insertBefore(ul, last)
-
-  # align the closing tag with the opening tag if it's not
-  # indented
-  if application.lastChild.nodeType != minidom.Node.TEXT_NODE:
-    indent = get_indent(application.previousSibling, 1)
-    application.appendChild(doc.createTextNode(indent))
+    # align the closing tag with the opening tag if it's not
+    # indented
+    if application.lastChild.nodeType != minidom.Node.TEXT_NODE:
+      indent = get_indent(application.previousSibling, 1)
+      application.appendChild(doc.createTextNode(indent))
 
 
 def add_uses_non_sdk_api(doc):
@@ -240,111 +198,63 @@
   """
 
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
-
-  attr = application.getAttributeNodeNS(android_ns, 'usesNonSdkApi')
-  if attr is None:
-    attr = doc.createAttributeNS(android_ns, 'android:usesNonSdkApi')
-    attr.value = 'true'
-    application.setAttributeNode(attr)
+  for application in get_or_create_applications(doc, manifest):
+    attr = application.getAttributeNodeNS(android_ns, 'usesNonSdkApi')
+    if attr is None:
+      attr = doc.createAttributeNS(android_ns, 'android:usesNonSdkApi')
+      attr.value = 'true'
+      application.setAttributeNode(attr)
 
 
 def add_use_embedded_dex(doc):
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
-
-  attr = application.getAttributeNodeNS(android_ns, 'useEmbeddedDex')
-  if attr is None:
-    attr = doc.createAttributeNS(android_ns, 'android:useEmbeddedDex')
-    attr.value = 'true'
-    application.setAttributeNode(attr)
-  elif attr.value != 'true':
-    raise RuntimeError('existing attribute mismatches the option of --use-embedded-dex')
+  for application in get_or_create_applications(doc, manifest):
+    attr = application.getAttributeNodeNS(android_ns, 'useEmbeddedDex')
+    if attr is None:
+      attr = doc.createAttributeNS(android_ns, 'android:useEmbeddedDex')
+      attr.value = 'true'
+      application.setAttributeNode(attr)
+    elif attr.value != 'true':
+      raise RuntimeError('existing attribute mismatches the option of --use-embedded-dex')
 
 
 def add_extract_native_libs(doc, extract_native_libs):
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
-
-  value = str(extract_native_libs).lower()
-  attr = application.getAttributeNodeNS(android_ns, 'extractNativeLibs')
-  if attr is None:
-    attr = doc.createAttributeNS(android_ns, 'android:extractNativeLibs')
-    attr.value = value
-    application.setAttributeNode(attr)
-  elif attr.value != value:
-    raise RuntimeError('existing attribute extractNativeLibs="%s" conflicts with --extract-native-libs="%s"' %
-                       (attr.value, value))
+  for application in get_or_create_applications(doc, manifest):
+    value = str(extract_native_libs).lower()
+    attr = application.getAttributeNodeNS(android_ns, 'extractNativeLibs')
+    if attr is None:
+      attr = doc.createAttributeNS(android_ns, 'android:extractNativeLibs')
+      attr.value = value
+      application.setAttributeNode(attr)
+    elif attr.value != value:
+      raise RuntimeError('existing attribute extractNativeLibs="%s" conflicts with --extract-native-libs="%s"' %
+                         (attr.value, value))
 
 
 def set_has_code_to_false(doc):
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
+  for application in get_or_create_applications(doc, manifest):
+    attr = application.getAttributeNodeNS(android_ns, 'hasCode')
+    if attr is not None:
+      # Do nothing if the application already has a hasCode attribute.
+      continue
+    attr = doc.createAttributeNS(android_ns, 'android:hasCode')
+    attr.value = 'false'
+    application.setAttributeNode(attr)
 
-  attr = application.getAttributeNodeNS(android_ns, 'hasCode')
-  if attr is not None:
-    # Do nothing if the application already has a hasCode attribute.
-    return
-  attr = doc.createAttributeNS(android_ns, 'android:hasCode')
-  attr.value = 'false'
-  application.setAttributeNode(attr)
 
 def set_test_only_flag_to_true(doc):
   manifest = parse_manifest(doc)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    application = doc.createElement('application')
-    indent = get_indent(manifest.firstChild, 1)
-    first = manifest.firstChild
-    manifest.insertBefore(doc.createTextNode(indent), first)
-    manifest.insertBefore(application, first)
+  for application in get_or_create_applications(doc, manifest):
+    attr = application.getAttributeNodeNS(android_ns, 'testOnly')
+    if attr is not None:
+      # Do nothing If the application already has a testOnly attribute.
+      continue
+    attr = doc.createAttributeNS(android_ns, 'android:testOnly')
+    attr.value = 'true'
+    application.setAttributeNode(attr)
 
-  attr = application.getAttributeNodeNS(android_ns, 'testOnly')
-  if attr is not None:
-    # Do nothing If the application already has a testOnly attribute.
-    return
-  attr = doc.createAttributeNS(android_ns, 'android:testOnly')
-  attr.value = 'true'
-  application.setAttributeNode(attr)
 
 def set_max_sdk_version(doc, max_sdk_version):
   """Replace the maxSdkVersion attribute value for permission and
@@ -364,6 +274,7 @@
       if max_attr and max_attr.value == 'current':
         max_attr.value = max_sdk_version
 
+
 def override_placeholder_version(doc, new_version):
   """Replace the versionCode attribute value if it\'s currently
   set to the placeholder version of 0.
@@ -374,9 +285,10 @@
   """
   manifest = parse_manifest(doc)
   version = manifest.getAttribute("android:versionCode")
-  if (version == '0'):
+  if version == '0':
     manifest.setAttribute("android:versionCode", new_version)
 
+
 def main():
   """Program entry point."""
   try:
@@ -427,5 +339,6 @@
     print('error: ' + str(err), file=sys.stderr)
     sys.exit(-1)
 
+
 if __name__ == '__main__':
   main()
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index 0a62b10..e4d8dc3 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -20,12 +20,13 @@
 import sys
 import unittest
 from xml.dom import minidom
-import xml.etree.ElementTree as ET
+import xml.etree.ElementTree as ElementTree
 
 import manifest_fixer
 
 sys.dont_write_bytecode = True
 
+
 class CompareVersionGtTest(unittest.TestCase):
   """Unit tests for compare_version_gt function."""
 
@@ -69,25 +70,24 @@
       '%s'
       '</manifest>\n')
 
-  # pylint: disable=redefined-builtin
-  def uses_sdk(self, min=None, target=None, extra=''):
+  def uses_sdk(self, min_sdk=None, target_sdk=None, extra=''):
     attrs = ''
-    if min:
-      attrs += ' android:minSdkVersion="%s"' % (min)
-    if target:
-      attrs += ' android:targetSdkVersion="%s"' % (target)
+    if min_sdk:
+      attrs += ' android:minSdkVersion="%s"' % min_sdk
+    if target_sdk:
+      attrs += ' android:targetSdkVersion="%s"' % target_sdk
     if extra:
       attrs += ' ' + extra
-    return '    <uses-sdk%s/>\n' % (attrs)
+    return '    <uses-sdk%s/>\n' % attrs
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def test_no_uses_sdk(self):
     """Tests inserting a uses-sdk element into a manifest."""
 
     manifest_input = self.manifest_tmpl % ''
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
     self.assert_xml_equal(output, expected)
 
@@ -95,7 +95,7 @@
     """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
 
     manifest_input = self.manifest_tmpl % '    <uses-sdk extra="foo"/>\n'
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28',
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28',
                                                   extra='extra="foo"')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
     self.assert_xml_equal(output, expected)
@@ -103,64 +103,64 @@
   def test_raise_min(self):
     """Tests inserting a minSdkVersion attribute into a uses-sdk element."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
     self.assert_xml_equal(output, expected)
 
   def test_raise(self):
     """Tests raising a minSdkVersion attribute."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
     self.assert_xml_equal(output, expected)
 
   def test_no_raise_min(self):
     """Tests a minSdkVersion that doesn't need raising."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='28')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27')
     output = self.raise_min_sdk_version_test(manifest_input, '27', '27', False)
     self.assert_xml_equal(output, expected)
 
   def test_raise_codename(self):
     """Tests raising a minSdkVersion attribute to a codename."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='28')
-    expected = self.manifest_tmpl % self.uses_sdk(min='P', target='P')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='28')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='P', target_sdk='P')
     output = self.raise_min_sdk_version_test(manifest_input, 'P', 'P', False)
     self.assert_xml_equal(output, expected)
 
   def test_no_raise_codename(self):
     """Tests a minSdkVersion codename that doesn't need raising."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='P')
-    expected = self.manifest_tmpl % self.uses_sdk(min='P', target='28')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='P')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='P', target_sdk='28')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
     self.assert_xml_equal(output, expected)
 
   def test_target(self):
     """Tests an existing targetSdkVersion is preserved."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='26', target='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='26', target_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
     self.assert_xml_equal(output, expected)
 
   def test_no_target(self):
     """Tests inserting targetSdkVersion when minSdkVersion exists."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='29')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
     self.assert_xml_equal(output, expected)
 
   def test_target_no_min(self):
     """"Tests inserting targetSdkVersion when minSdkVersion exists."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(target_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
     self.assert_xml_equal(output, expected)
 
@@ -168,23 +168,23 @@
     """Tests inserting targetSdkVersion when minSdkVersion does not exist."""
 
     manifest_input = self.manifest_tmpl % ''
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='29')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False)
     self.assert_xml_equal(output, expected)
 
   def test_library_no_target(self):
     """Tests inserting targetSdkVersion when minSdkVersion exists."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='16')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
     self.assert_xml_equal(output, expected)
 
   def test_library_target_no_min(self):
     """Tests inserting targetSdkVersion when minSdkVersion exists."""
 
-    manifest_input = self.manifest_tmpl % self.uses_sdk(target='27')
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27')
+    manifest_input = self.manifest_tmpl % self.uses_sdk(target_sdk='27')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
     self.assert_xml_equal(output, expected)
 
@@ -192,7 +192,7 @@
     """Tests inserting targetSdkVersion when minSdkVersion does not exist."""
 
     manifest_input = self.manifest_tmpl % ''
-    expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
+    expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='16')
     output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
     self.assert_xml_equal(output, expected)
 
@@ -228,12 +228,24 @@
 
     self.assert_xml_equal(output, expected)
 
+  def test_multiple_uses_sdks(self):
+    """Tests a manifest that contains multiple uses_sdks elements."""
+
+    manifest_input = self.manifest_tmpl % (
+        '    <uses-sdk android:featureFlag="foo" android:minSdkVersion="21" />\n'
+        '    <uses-sdk android:featureFlag="!foo" android:minSdkVersion="22" />\n')
+    expected = self.manifest_tmpl % (
+      '    <uses-sdk android:featureFlag="foo" android:minSdkVersion="28" android:targetSdkVersion="28" />\n'
+      '    <uses-sdk android:featureFlag="!foo" android:minSdkVersion="28" android:targetSdkVersion="28" />\n')
+
+    output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False)
+    self.assert_xml_equal(output, expected)
 
 class AddLoggingParentTest(unittest.TestCase):
   """Unit tests for add_logging_parent function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def add_logging_parent_test(self, input_manifest, logging_parent=None):
     doc = minidom.parseString(input_manifest)
@@ -253,8 +265,8 @@
     attrs = ''
     if logging_parent:
       meta_text = ('<meta-data android:name="android.content.pm.LOGGING_PARENT" '
-                   'android:value="%s"/>\n') % (logging_parent)
-      attrs += '    <application>\n        %s    </application>\n' % (meta_text)
+                   'android:value="%s"/>\n') % logging_parent
+      attrs += '    <application>\n        %s    </application>\n' % meta_text
 
     return attrs
 
@@ -277,7 +289,7 @@
   """Unit tests for add_uses_libraries function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest, new_uses_libraries):
     doc = minidom.parseString(input_manifest)
@@ -289,18 +301,16 @@
   manifest_tmpl = (
       '<?xml version="1.0" encoding="utf-8"?>\n'
       '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
-      '    <application>\n'
       '%s'
-      '    </application>\n'
       '</manifest>\n')
 
   def uses_libraries(self, name_required_pairs):
-    ret = ''
+    ret = '    <application>\n'
     for name, required in name_required_pairs:
       ret += (
           '        <uses-library android:name="%s" android:required="%s"/>\n'
       ) % (name, required)
-
+    ret += '    </application>\n'
     return ret
 
   def test_empty(self):
@@ -361,12 +371,23 @@
     output = self.run_test(manifest_input, ['foo', 'bar'])
     self.assert_xml_equal(output, expected)
 
+  def test_multiple_application(self):
+    """When there are multiple applications, the libs are added to each."""
+    manifest_input = self.manifest_tmpl % (
+            self.uses_libraries([('foo', 'false')]) +
+            self.uses_libraries([('bar', 'false')]))
+    expected = self.manifest_tmpl % (
+            self.uses_libraries([('foo', 'false'), ('bar', 'true')]) +
+            self.uses_libraries([('bar', 'false'), ('foo', 'true')]))
+    output = self.run_test(manifest_input, ['foo', 'bar'])
+    self.assert_xml_equal(output, expected)
+
 
 class AddUsesNonSdkApiTest(unittest.TestCase):
   """Unit tests for add_uses_libraries function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest):
     doc = minidom.parseString(input_manifest)
@@ -378,11 +399,11 @@
   manifest_tmpl = (
       '<?xml version="1.0" encoding="utf-8"?>\n'
       '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
-      '    <application%s/>\n'
+      '    %s\n'
       '</manifest>\n')
 
   def uses_non_sdk_api(self, value):
-    return ' android:usesNonSdkApi="true"' if value else ''
+    return '<application %s/>' % ('android:usesNonSdkApi="true"' if value else '')
 
   def test_set_true(self):
     """Empty new_uses_libraries must not touch the manifest."""
@@ -398,12 +419,19 @@
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, expected)
 
+  def test_multiple_applications(self):
+    """new_uses_libraries must be added to all applications."""
+    manifest_input = self.manifest_tmpl % (self.uses_non_sdk_api(True) +  self.uses_non_sdk_api(False))
+    expected = self.manifest_tmpl % (self.uses_non_sdk_api(True) +  self.uses_non_sdk_api(True))
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
 
 class UseEmbeddedDexTest(unittest.TestCase):
   """Unit tests for add_use_embedded_dex function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest):
     doc = minidom.parseString(input_manifest)
@@ -415,14 +443,14 @@
   manifest_tmpl = (
       '<?xml version="1.0" encoding="utf-8"?>\n'
       '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
-      '    <application%s/>\n'
+      '    %s\n'
       '</manifest>\n')
 
   def use_embedded_dex(self, value):
-    return ' android:useEmbeddedDex="%s"' % value
+    return '<application android:useEmbeddedDex="%s" />' % value
 
   def test_manifest_with_undeclared_preference(self):
-    manifest_input = self.manifest_tmpl % ''
+    manifest_input = self.manifest_tmpl % '<application/>'
     expected = self.manifest_tmpl % self.use_embedded_dex('true')
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, expected)
@@ -437,12 +465,24 @@
     manifest_input = self.manifest_tmpl % self.use_embedded_dex('false')
     self.assertRaises(RuntimeError, self.run_test, manifest_input)
 
+  def test_multiple_applications(self):
+    manifest_input = self.manifest_tmpl % (
+        self.use_embedded_dex('true') +
+        '<application/>'
+    )
+    expected = self.manifest_tmpl % (
+        self.use_embedded_dex('true') +
+        self.use_embedded_dex('true')
+    )
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
 
 class AddExtractNativeLibsTest(unittest.TestCase):
   """Unit tests for add_extract_native_libs function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest, value):
     doc = minidom.parseString(input_manifest)
@@ -454,20 +494,20 @@
   manifest_tmpl = (
       '<?xml version="1.0" encoding="utf-8"?>\n'
       '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
-      '    <application%s/>\n'
+      '    %s\n'
       '</manifest>\n')
 
   def extract_native_libs(self, value):
-    return ' android:extractNativeLibs="%s"' % value
+    return '<application android:extractNativeLibs="%s" />' % value
 
   def test_set_true(self):
-    manifest_input = self.manifest_tmpl % ''
+    manifest_input = self.manifest_tmpl % '<application/>'
     expected = self.manifest_tmpl % self.extract_native_libs('true')
     output = self.run_test(manifest_input, True)
     self.assert_xml_equal(output, expected)
 
   def test_set_false(self):
-    manifest_input = self.manifest_tmpl % ''
+    manifest_input = self.manifest_tmpl % '<application/>'
     expected = self.manifest_tmpl % self.extract_native_libs('false')
     output = self.run_test(manifest_input, False)
     self.assert_xml_equal(output, expected)
@@ -482,12 +522,18 @@
     manifest_input = self.manifest_tmpl % self.extract_native_libs('true')
     self.assertRaises(RuntimeError, self.run_test, manifest_input, False)
 
+  def test_multiple_applications(self):
+    manifest_input = self.manifest_tmpl % (self.extract_native_libs('true') + '<application/>')
+    expected = self.manifest_tmpl % (self.extract_native_libs('true') + self.extract_native_libs('true'))
+    output = self.run_test(manifest_input, True)
+    self.assert_xml_equal(output, expected)
+
 
 class AddNoCodeApplicationTest(unittest.TestCase):
   """Unit tests for set_has_code_to_false function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest):
     doc = minidom.parseString(input_manifest)
@@ -515,7 +561,7 @@
     self.assert_xml_equal(output, expected)
 
   def test_has_application_has_code_false(self):
-    """ Do nothing if there's already an application elemeent. """
+    """ Do nothing if there's already an application element. """
     manifest_input = self.manifest_tmpl % '    <application android:hasCode="false"/>\n'
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, manifest_input)
@@ -527,12 +573,25 @@
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, manifest_input)
 
+  def test_multiple_applications(self):
+    """ Apply to all applications  """
+    manifest_input = self.manifest_tmpl % (
+        '    <application android:hasCode="true" />\n' +
+        '    <application android:hasCode="false" />\n' +
+        '    <application/>\n')
+    expected = self.manifest_tmpl % (
+        '    <application android:hasCode="true" />\n' +
+        '    <application android:hasCode="false" />\n' +
+        '    <application android:hasCode="false" />\n')
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
 
 class AddTestOnlyApplicationTest(unittest.TestCase):
   """Unit tests for set_test_only_flag_to_true function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest):
     doc = minidom.parseString(input_manifest)
@@ -571,12 +630,26 @@
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, manifest_input)
 
+  def test_multiple_applications(self):
+    manifest_input = self.manifest_tmpl % (
+        '    <application android:testOnly="true" />\n' +
+        '    <application android:testOnly="false" />\n' +
+        '    <application/>\n'
+    )
+    expected = self.manifest_tmpl % (
+        '    <application android:testOnly="true" />\n' +
+        '    <application android:testOnly="false" />\n' +
+        '    <application android:testOnly="true" />\n'
+    )
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
 
 class SetMaxSdkVersionTest(unittest.TestCase):
   """Unit tests for set_max_sdk_version function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest, max_sdk_version):
     doc = minidom.parseString(input_manifest)
@@ -591,15 +664,15 @@
       '%s'
       '</manifest>\n')
 
-  def permission(self, max=None):
-    if max is None:
+  def permission(self, max_sdk=None):
+    if max_sdk is None:
       return '   <permission/>'
-    return '    <permission android:maxSdkVersion="%s"/>\n' % max
+    return '    <permission android:maxSdkVersion="%s"/>\n' % max_sdk
 
-  def uses_permission(self, max=None):
-    if max is None:
+  def uses_permission(self, max_sdk=None):
+    if max_sdk is None:
       return '   <uses-permission/>'
-    return '    <uses-permission android:maxSdkVersion="%s"/>\n' % max
+    return '    <uses-permission android:maxSdkVersion="%s"/>\n' % max_sdk
 
   def test_permission_no_max_sdk_version(self):
     """Tests if permission has no maxSdkVersion attribute"""
@@ -643,11 +716,12 @@
     output = self.run_test(manifest_input, '9000')
     self.assert_xml_equal(output, expected)
 
+
 class OverrideDefaultVersionTest(unittest.TestCase):
   """Unit tests for override_default_version function."""
 
   def assert_xml_equal(self, output, expected):
-    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+    self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected))
 
   def run_test(self, input_manifest, version):
     doc = minidom.parseString(input_manifest)
diff --git a/scripts/merge_json.py b/scripts/merge_json.py
new file mode 100644
index 0000000..7e2f6eb
--- /dev/null
+++ b/scripts/merge_json.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+"""A tool for merging two or more JSON files."""
+
+import argparse
+import logging
+import json
+import sys
+
+def parse_args():
+  """Parse commandline arguments."""
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument("output", help="output JSON file", type=argparse.FileType("w"))
+  parser.add_argument("input", help="input JSON files", nargs="+", type=argparse.FileType("r"))
+  return parser.parse_args()
+
+def main():
+  """Program entry point."""
+  args = parse_args()
+  merged_dict = {}
+  has_error = False
+  logger = logging.getLogger(__name__)
+
+  for json_file in args.input:
+    try:
+      data = json.load(json_file)
+    except json.JSONDecodeError as e:
+      logger.error(f"Error parsing JSON in file: {json_file.name}. Reason: {e}")
+      has_error = True
+      continue
+
+    for key, value in data.items():
+      if key not in merged_dict:
+        merged_dict[key] = value
+      elif merged_dict[key] == value:
+        logger.warning(f"Duplicate key '{key}' with identical values found.")
+      else:
+        logger.error(f"Conflicting values for key '{key}': {merged_dict[key]} != {value}")
+        has_error = True
+
+  if has_error:
+    sys.exit(1)
+
+  json.dump(merged_dict, args.output)
+
+if __name__ == "__main__":
+  main()
diff --git a/scripts/run-ckati.sh b/scripts/run-ckati.sh
index b670c8a..70f5a7a 100755
--- a/scripts/run-ckati.sh
+++ b/scripts/run-ckati.sh
@@ -73,12 +73,12 @@
   --writable out/ \
   -f build/make/core/main.mk \
   "${tracing[@]}" \
-  ANDROID_JAVA_HOME=prebuilts/jdk/jdk17/linux-x86 \
+  ANDROID_JAVA_HOME=prebuilts/jdk/jdk21/linux-x86 \
   ASAN_SYMBOLIZER_PATH=$PWD/prebuilts/clang/host/linux-x86/llvm-binutils-stable/llvm-symbolizer \
   BUILD_DATETIME_FILE="$timestamp_file" \
   BUILD_HOSTNAME=$(hostname) \
   BUILD_USERNAME="$USER" \
-  JAVA_HOME=$PWD/prebuilts/jdk/jdk17/linux-x86 \
+  JAVA_HOME=$PWD/prebuilts/jdk/jdk21/linux-x86 \
   KATI_PACKAGE_MK_DIR="{$out}/target/product/${target_device}/CONFIG/kati_packaging" \
   OUT_DIR="$out" \
   PATH="$PWD/prebuilts/build-tools/path/linux-x86:$PWD/${out}/.path" \
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 7048a15..39c8123 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -204,7 +204,19 @@
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core1.jar
 .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core2.jar
 		`),
-		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+		snapshotTestPreparer(checkSnapshotWithoutSource, android.GroupFixturePreparers(
+			preparerForSnapshot,
+			// Flag ART prebuilts
+			android.FixtureMergeMockFs(android.MockFS{
+				"apex_contributions/Android.bp": []byte(`
+				apex_contributions {
+					name: "prebuilt_art_contributions",
+					contents: ["prebuilt_com.android.art"],
+					api_domain: "com.android.art",
+				}
+			`)}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
+		)),
 
 		// Check the behavior of the snapshot without the source.
 		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
@@ -212,8 +224,8 @@
 			checkBootJarsPackageCheckRule(t, result,
 				append(
 					[]string{
-						"out/soong/.intermediates/prebuilts/apex/prebuilt_com.android.art.deapexer/android_common/deapexer/javalib/core1.jar",
-						"out/soong/.intermediates/prebuilts/apex/prebuilt_com.android.art.deapexer/android_common/deapexer/javalib/core2.jar",
+						"out/soong/.intermediates/prebuilts/apex/com.android.art/android_common_com.android.art/deapexer/javalib/core1.jar",
+						"out/soong/.intermediates/prebuilts/apex/com.android.art/android_common_com.android.art/deapexer/javalib/core2.jar",
 						"out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar",
 					},
 					java.ApexBootJarDexJarPaths...,
@@ -276,11 +288,7 @@
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
 
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 		// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
 		// file creation.
 		java.FixtureConfigureBootJars("platform:foo"),
@@ -342,6 +350,7 @@
 				shared_library: false,
 				public: {enabled: true},
 				min_sdk_version: "2",
+				sdk_version: "current",
 			}
 
 			java_sdk_library {
@@ -352,6 +361,7 @@
 				public: {enabled: true},
 				min_sdk_version: "2",
 				permitted_packages: ["myothersdklibrary"],
+				sdk_version: "current",
 			}
 
 			java_sdk_library {
@@ -361,6 +371,7 @@
 				compile_dex: true,
 				public: {enabled: true},
 				min_sdk_version: "2",
+				sdk_version: "current",
 			}
 		`),
 	).RunTest(t)
@@ -628,6 +639,7 @@
 				min_sdk_version: "2",
 				permitted_packages: ["myothersdklibrary"],
 				compile_dex: true,
+				sdk_version: "current",
 			}
 		`),
 
@@ -659,6 +671,7 @@
 				shared_library: false,
 				public: {enabled: true},
 				min_sdk_version: "2",
+				sdk_version: "current",
 			}
 		`),
 	).RunTest(t)
@@ -799,11 +812,7 @@
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
 
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 
 		android.MockFS{
 			"my-blocked.txt":                   nil,
@@ -885,6 +894,7 @@
 				public: {enabled: true},
 				permitted_packages: ["mysdklibrary"],
 				min_sdk_version: "current",
+				sdk_version: "current",
 			}
 
 			java_sdk_library {
@@ -903,6 +913,7 @@
 					package_prefixes: ["newlibrary.all.mine"],
 					single_packages: ["newlibrary.mine"],
 				},
+				sdk_version: "current",
 			}
 		`),
 	).RunTest(t)
@@ -1053,11 +1064,7 @@
 			variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
 		}),
 
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 
 		android.FixtureWithRootAndroidBp(`
 			sdk {
@@ -1092,6 +1099,7 @@
 				shared_library: false,
 				public: {enabled: true},
 				min_sdk_version: "S",
+				sdk_version: "current",
 			}
 
 			java_sdk_library {
@@ -1102,6 +1110,7 @@
 				public: {enabled: true},
 				min_sdk_version: "Tiramisu",
 				permitted_packages: ["mynewsdklibrary"],
+				sdk_version: "current",
 			}
 		`),
 	).RunTest(t)
@@ -1299,6 +1308,7 @@
 				shared_library: false,
 				public: {enabled: true},
 				min_sdk_version: "Tiramisu",
+				sdk_version: "current",
 			}
 			java_sdk_library {
 				name: "mynewsdklibrary",
@@ -1308,6 +1318,7 @@
 				public: {enabled: true},
 				min_sdk_version: "Tiramisu",
 				permitted_packages: ["mynewsdklibrary"],
+				sdk_version: "current",
 			}
 		`),
 	).RunTest(t)
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 9490d12..25839b8 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -148,6 +148,9 @@
             srcs: ["linux_glibc/x86_64/lib/sdkmember.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -368,6 +371,9 @@
             srcs: ["arm/lib/mynativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -455,6 +461,9 @@
             },
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -697,6 +706,9 @@
             srcs: ["x86_64/lib/mynativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -824,6 +836,9 @@
             export_include_dirs: ["arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -932,6 +947,9 @@
             srcs: ["arm/lib/mynativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 
 cc_prebuilt_library_shared {
@@ -950,6 +968,9 @@
             srcs: ["arm/lib/myothernativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 
 cc_prebuilt_library_shared {
@@ -967,6 +988,9 @@
             srcs: ["arm/lib/mysystemnativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -1041,6 +1065,9 @@
             export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -1127,6 +1154,9 @@
             srcs: ["windows/x86_64/lib/mynativelib.dll"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -2021,6 +2051,9 @@
             srcs: ["arm/lib/sslnil.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 
 cc_prebuilt_library_shared {
@@ -2038,6 +2071,9 @@
             srcs: ["arm/lib/sslempty.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 
 cc_prebuilt_library_shared {
@@ -2055,6 +2091,9 @@
             srcs: ["arm/lib/sslnonempty.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `))
 
@@ -2114,6 +2153,9 @@
             srcs: ["linux_glibc/x86/lib/sslvariants.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 	)
@@ -2154,6 +2196,7 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
+    stl: "none",
     compile_multilib: "both",
     stubs: {
         versions: [
@@ -2162,6 +2205,7 @@
             "3",
             "current",
         ],
+        symbol_file: "stubslib.map.txt",
     },
     arch: {
         arm64: {
@@ -2171,6 +2215,9 @@
             srcs: ["arm/lib/stubslib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `))
 }
@@ -2222,6 +2269,7 @@
             "3",
             "current",
         ],
+        symbol_file: "stubslib.map.txt",
     },
     target: {
         host: {
@@ -2242,6 +2290,9 @@
             srcs: ["linux_glibc/x86/lib/stubslib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 	)
@@ -2298,6 +2349,9 @@
             srcs: ["linux_glibc/x86/lib/mylib-host.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -2355,6 +2409,9 @@
             srcs: ["arm/lib/mynativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 0a5483b..09a7c9e 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -45,11 +45,7 @@
 	java.PrepareForTestWithJavaDefaultModules,
 	java.PrepareForTestWithJavaSdkLibraryFiles,
 	java.FixtureWithLastReleaseApis("myjavalib"),
-	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.BuildFlags = map[string]string{
-			"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-		}
-	}),
+	android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 )
 
 // Contains tests for SDK members provided by the java package.
@@ -666,11 +662,7 @@
 			"1": {"myjavalib"},
 			"2": {"myjavalib"},
 		}),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1313,11 +1305,7 @@
 func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJavaSdkLibrary,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1715,61 +1703,6 @@
 	)
 }
 
-func TestSnapshotWithJavaSdkLibrary_NamingScheme(t *testing.T) {
-	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
-		sdk {
-			name: "mysdk",
-			java_sdk_libs: ["myjavalib"],
-		}
-
-		java_sdk_library {
-			name: "myjavalib",
-			apex_available: ["//apex_available:anyapex"],
-			srcs: ["Test.java"],
-			sdk_version: "current",
-			naming_scheme: "default",
-			public: {
-				enabled: true,
-			},
-		}
-	`)
-
-	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-apex_contributions_defaults {
-    name: "mysdk.contributions",
-    contents: ["prebuilt_myjavalib"],
-}
-
-java_sdk_library_import {
-    name: "myjavalib",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:anyapex"],
-    naming_scheme: "default",
-    shared_library: true,
-    public: {
-        jars: ["sdk_library/public/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
-        current_api: "sdk_library/public/myjavalib.txt",
-        removed_api: "sdk_library/public/myjavalib-removed.txt",
-        sdk_version: "current",
-    },
-}
-`),
-		checkAllCopyRules(`
-.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
-`),
-		checkMergeZips(
-			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
-		),
-	)
-}
-
 func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJavaSdkLibrary,
diff --git a/sdk/sdk.go b/sdk/sdk.go
index fd16ab6..aa82abb 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -84,20 +84,6 @@
 
 	// True if this is a module_exports (or module_exports_snapshot) module type.
 	Module_exports bool `blueprint:"mutated"`
-
-	// The additional visibility to add to the prebuilt modules to allow them to
-	// reference each other.
-	//
-	// This can only be used to widen the visibility of the members:
-	//
-	// * Specifying //visibility:public here will make all members visible and
-	//   essentially ignore their own visibility.
-	// * Specifying //visibility:private here is an error.
-	// * Specifying any other rule here will add it to the members visibility and
-	//   be output to the member prebuilt in the snapshot. Duplicates will be
-	//   dropped. Adding a rule to members that have //visibility:private will
-	//   cause the //visibility:private to be discarded.
-	Prebuilt_visibility []string
 }
 
 // sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.)
@@ -130,8 +116,6 @@
 
 	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties, &traitsWrapper)
 
-	// Make sure that the prebuilt visibility property is verified for errors.
-	android.AddVisibilityProperty(s, "prebuilt_visibility", &s.properties.Prebuilt_visibility)
 	android.InitCommonOSAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(s)
 	android.AddLoadHook(s, func(ctx android.LoadHookContext) {
@@ -193,6 +177,10 @@
 		// Generate the snapshot from the member info.
 		s.buildSnapshot(ctx, sdkVariants)
 	}
+
+	if s.snapshotFile.Valid() {
+		ctx.SetOutputFiles([]android.Path{s.snapshotFile.Path()}, "")
+	}
 }
 
 func (s *sdk) AndroidMkEntries() []android.AndroidMkEntries {
@@ -207,6 +195,11 @@
 		OutputFile: s.snapshotFile,
 		DistFiles:  android.MakeDefaultDistFiles(s.snapshotFile.Path(), s.infoFile.Path()),
 		Include:    "$(BUILD_PHONY_PACKAGE)",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetBool("LOCAL_DONT_CHECK_MODULE", true)
+			},
+		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
 			func(w io.Writer, name, prefix, moduleDir string) {
 				// Allow the sdk to be built by simply passing its name on the command line.
@@ -222,18 +215,6 @@
 	}}
 }
 
-func (s *sdk) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		if s.snapshotFile.Valid() {
-			return []android.Path{s.snapshotFile.Path()}, nil
-		}
-		return nil, fmt.Errorf("snapshot file not defined. This is most likely because this isn't the common_os variant of this module")
-	default:
-		return nil, fmt.Errorf("unknown tag %q", tag)
-	}
-}
-
 // gatherTraits gathers the traits from the dynamically generated trait specific properties.
 //
 // Returns a map from member name to the set of required traits.
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 4894210..2532a25 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -53,9 +53,6 @@
 				// generated sdk_snapshot.
 				":__subpackages__",
 			],
-			prebuilt_visibility: [
-				"//prebuilts/mysdk",
-			],
 			java_header_libs: [
 				"myjavalib",
 				"mypublicjavalib",
@@ -131,11 +128,7 @@
 java_import {
     name: "myjavalib",
     prefer: false,
-    visibility: [
-        "//other/foo",
-        "//package",
-        "//prebuilts/mysdk",
-    ],
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/myjavalib.jar"],
 }
@@ -151,11 +144,7 @@
 java_import {
     name: "mydefaultedjavalib",
     prefer: false,
-    visibility: [
-        "//other/bar",
-        "//package",
-        "//prebuilts/mysdk",
-    ],
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/mydefaultedjavalib.jar"],
 }
@@ -163,50 +152,13 @@
 java_import {
     name: "myprivatejavalib",
     prefer: false,
-    visibility: [
-        "//package",
-        "//prebuilts/mysdk",
-    ],
+    visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
     jars: ["java/myprivatejavalib.jar"],
 }
 `))
 }
 
-func TestPrebuiltVisibilityProperty_IsValidated(t *testing.T) {
-	testSdkError(t, `prebuilt_visibility: cannot mix "//visibility:private" with any other visibility rules`, `
-		sdk {
-			name: "mysdk",
-			prebuilt_visibility: [
-				"//foo",
-				"//visibility:private",
-			],
-		}
-`)
-}
-
-func TestPrebuiltVisibilityProperty_AddPrivate(t *testing.T) {
-	testSdkError(t, `prebuilt_visibility: "//visibility:private" does not widen the visibility`, `
-		sdk {
-			name: "mysdk",
-			prebuilt_visibility: [
-				"//visibility:private",
-			],
-			java_header_libs: [
-				"myjavalib",
-			],
-		}
-
-		java_library {
-			name: "myjavalib",
-			// Uses package default visibility
-			srcs: ["Test.java"],
-			system_modules: "none",
-			sdk_version: "none",
-		}
-`)
-}
-
 func TestSdkInstall(t *testing.T) {
 	sdk := `
 		sdk {
@@ -457,11 +409,7 @@
 			android.FixtureMergeEnv(map[string]string{
 				"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
 			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 		).RunTest(t)
 
 		CheckSnapshot(t, result, "mysdk", "",
@@ -573,11 +521,9 @@
 				"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
 			}),
 			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-				}
 				variables.Platform_version_active_codenames = []string{"UpsideDownCake", "Tiramisu", "S-V2"}
 			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 		).RunTest(t)
 
 		CheckSnapshot(t, result, "mysdk", "",
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index c1c4ed6..fd6c4e7 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -80,6 +80,7 @@
 				dex_preopt: {
 					profile: "art-profile",
 				},
+				sdk_version: "current",
 			}
 		`),
 	).RunTest(t)
@@ -110,12 +111,14 @@
 			apex_available: ["myapex"],
 			srcs: ["Test.java"],
 			min_sdk_version: "33", // Tiramisu
+			sdk_version: "current",
 		}
 		java_sdk_library {
 			name: "mysdklibrary-future",
 			apex_available: ["myapex"],
 			srcs: ["Test.java"],
 			min_sdk_version: "34", // UpsideDownCake
+			sdk_version: "current",
 		}
 		sdk {
 			name: "mysdk",
@@ -199,6 +202,7 @@
 			apex_available: ["myapex"],
 			srcs: ["Test.java"],
 			min_sdk_version: "34", // UpsideDownCake
+			sdk_version: "current",
 		}
 		sdk {
 			name: "mysdk",
diff --git a/sdk/update.go b/sdk/update.go
index 0a97fd9..7f4f80a 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -22,7 +22,6 @@
 	"sort"
 	"strings"
 
-	"android/soong/apex"
 	"android/soong/cc"
 	"android/soong/java"
 
@@ -434,6 +433,14 @@
 		prebuiltModule := memberType.AddPrebuiltModule(memberCtx, member)
 		s.createMemberSnapshot(memberCtx, member, prebuiltModule.(*bpModule))
 
+		// Set stripper to none to skip stripping for generated snapshots.
+		// Mainline prebuilts (cc_prebuilt_library_shared) are not strippable in older platforms.
+		// Thus, stripping should be skipped when being used as prebuilts.
+		if memberType.DisablesStrip() {
+			stripPropertySet := prebuiltModule.(*bpModule).AddPropertySet("strip")
+			stripPropertySet.AddProperty("none", true)
+		}
+
 		if member.memberType != android.LicenseModuleSdkMemberType && !builder.isInternalMember(member.name) {
 			// More exceptions
 			// 1. Skip BCP and SCCP fragments
@@ -555,11 +562,11 @@
 	}
 	builder.infoContents = string(output)
 	android.WriteFileRuleVerbatim(ctx, info, builder.infoContents)
-	installedInfo := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), info.Base(), info)
+	installedInfo := ctx.InstallFileWithoutCheckbuild(android.PathForMainlineSdksInstall(ctx), info.Base(), info)
 	s.infoFile = android.OptionalPathForPath(installedInfo)
 
 	// Install the zip, making sure that the info file has been installed as well.
-	installedZip := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), outputZipFile.Base(), outputZipFile, installedInfo)
+	installedZip := ctx.InstallFileWithoutCheckbuild(android.PathForMainlineSdksInstall(ctx), outputZipFile.Base(), outputZipFile, installedInfo)
 	s.snapshotFile = android.OptionalPathForPath(installedZip)
 }
 
@@ -1101,20 +1108,24 @@
 		// same package so can be marked as private.
 		m.AddProperty("visibility", []string{"//visibility:private"})
 	} else {
-		// Extract visibility information from a member variant. All variants have the same
-		// visibility so it doesn't matter which one is used.
-		visibilityRules := android.EffectiveVisibilityRules(s.ctx, variant)
-
-		// Add any additional visibility rules needed for the prebuilts to reference each other.
-		err := visibilityRules.Widen(s.sdk.properties.Prebuilt_visibility)
-		if err != nil {
-			s.ctx.PropertyErrorf("prebuilt_visibility", "%s", err)
-		}
-
-		visibility := visibilityRules.Strings()
-		if len(visibility) != 0 {
-			m.AddProperty("visibility", visibility)
-		}
+		// Change the visibility of the module SDK prebuilts to public.
+		// This includes
+		// 1. Stub libraries of `sdk` modules
+		// 2. Binaries and libraries of `module_exports` modules
+		//
+		// This is a workaround to improve maintainlibility of the module SDK.
+		// Since module sdks are generated from release branches and dropped to development
+		// branches, there might be a visibility skew between the sources and prebuilts
+		// of a specific module.
+		// To reconcile this potential skew, change the visibility to public.
+		//
+		// This means dependencies can bypass visibility restrictions when prebuilts are used, so we rely
+		// on source builds in CI to check them.
+		//
+		// TODO (b/361303067): This special case for category (2) can be removed if existing usages
+		// of host/test prebuilts of modules like conscrypt,tzdata,i18n are switched to source builds.
+		// It will also require ART switching to full manifests.
+		m.AddProperty("visibility", []string{"//visibility:public"})
 	}
 
 	// Where available copy apex_available properties from the member.
@@ -1125,9 +1136,6 @@
 			apexAvailable = []string{android.AvailableToPlatform}
 		}
 
-		// Add in any baseline apex available settings.
-		apexAvailable = append(apexAvailable, apex.BaselineApexAvailable(member.Name())...)
-
 		// Remove duplicates and sort.
 		apexAvailable = android.FirstUniqueStrings(apexAvailable)
 		sort.Strings(apexAvailable)
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 48a442d..2e48d83 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -212,6 +212,14 @@
 
 func (s *ShBinary) ImageMutatorBegin(ctx android.BaseModuleContext) {}
 
+func (s *ShBinary) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+	return s.InstallInVendor()
+}
+
+func (s *ShBinary) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+	return s.InstallInProduct()
+}
+
 func (s *ShBinary) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return !s.InstallInRecovery() && !s.InstallInRamdisk() && !s.InstallInVendorRamdisk() && !s.ModuleBase.InstallInVendor()
 }
@@ -233,14 +241,7 @@
 }
 
 func (s *ShBinary) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	extraVariations := []string{}
-	if s.InstallInProduct() {
-		extraVariations = append(extraVariations, cc.ProductVariation)
-	}
-	if s.InstallInVendor() {
-		extraVariations = append(extraVariations, cc.VendorVariation)
-	}
-	return extraVariations
+	return nil
 }
 
 func (s *ShBinary) SetImageVariation(ctx android.BaseModuleContext, variation string) {
@@ -306,7 +307,7 @@
 func (s *ShBinary) GetSubname(ctx android.ModuleContext) string {
 	ret := ""
 	if s.properties.ImageVariation != "" {
-		if s.properties.ImageVariation != cc.VendorVariation {
+		if s.properties.ImageVariation != android.VendorVariation {
 			ret = "." + s.properties.ImageVariation
 		}
 	}
diff --git a/shared/Android.bp b/shared/Android.bp
index 3c84f55..2092869 100644
--- a/shared/Android.bp
+++ b/shared/Android.bp
@@ -15,7 +15,8 @@
         "paths_test.go",
     ],
     deps: [
-        "soong-bazel",
         "golang-protobuf-proto",
     ],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/shared/paths.go b/shared/paths.go
index fca8b4c..1ee66d5 100644
--- a/shared/paths.go
+++ b/shared/paths.go
@@ -18,8 +18,6 @@
 
 import (
 	"path/filepath"
-
-	"android/soong/bazel"
 )
 
 // A SharedPaths represents a list of paths that are shared between
@@ -49,11 +47,3 @@
 func TempDirForOutDir(outDir string) (tempPath string) {
 	return filepath.Join(outDir, ".temp")
 }
-
-// BazelMetricsFilename returns the bazel profile filename based
-// on the action name. This is to help to store a set of bazel
-// profiles since bazel may execute multiple times during a single
-// build.
-func BazelMetricsFilename(s SharedPaths, actionName bazel.RunName) string {
-	return filepath.Join(s.BazelMetricsDir(), actionName.String()+"_bazel_profile.gz")
-}
diff --git a/snapshot/Android.bp b/snapshot/Android.bp
index 6cb318e..ae1869a 100644
--- a/snapshot/Android.bp
+++ b/snapshot/Android.bp
@@ -14,13 +14,8 @@
     // Source file name convention is to include _snapshot as a
     // file suffix for files that are generating snapshots.
     srcs: [
-        "host_fake_snapshot.go",
-        "host_snapshot.go",
         "snapshot_base.go",
         "util.go",
     ],
-    testSrcs: [
-        "host_test.go",
-    ],
     pluginFor: ["soong_build"],
 }
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
deleted file mode 100644
index b416ebd..0000000
--- a/snapshot/host_fake_snapshot.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2021 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 snapshot
-
-import (
-	"encoding/json"
-	"path/filepath"
-
-	"android/soong/android"
-)
-
-// The host_snapshot module creates a snapshot of host tools to be used
-// in a minimal source tree.   In order to create the host_snapshot the
-// user must explicitly list the modules to be included.  The
-// host-fake-snapshot, defined in this file, is a utility to help determine
-// which host modules are being used in the minimal source tree.
-//
-// The host-fake-snapshot is designed to run in a full source tree and
-// will result in a snapshot that contains an empty file for each host
-// tool found in the tree.  The fake snapshot is only used to determine
-// the host modules that the minimal source tree depends on, hence the
-// snapshot uses an empty file for each module and saves on having to
-// actually build any tool to generate the snapshot.  The fake snapshot
-// is compatible with an actual host_snapshot and is installed into a
-// minimal source tree via the development/vendor_snapshot/update.py
-// script.
-//
-// After generating the fake snapshot and installing into the minimal
-// source tree, the dependent modules are determined via the
-// development/vendor_snapshot/update.py script (see script for more
-// information).  These modules are then used to define the actual
-// host_snapshot to be used.  This is a similar process to the other
-// snapshots (vendor, recovery,...)
-//
-// Example
-//
-// Full source tree:
-//   1/ Generate fake host snapshot
-//
-// Minimal source tree:
-//   2/ Install the fake host snapshot
-//   3/ List the host modules used from the snapshot
-//   4/ Remove fake host snapshot
-//
-// Full source tree:
-//   4/ Create host_snapshot with modules identified in step 3
-//
-// Minimal source tree:
-//   5/ Install host snapshot
-//   6/ Build
-//
-// The host-fake-snapshot is a singleton module, that will be built
-// if HOST_FAKE_SNAPSHOT_ENABLE=true.
-
-func init() {
-	registerHostSnapshotComponents(android.InitRegistrationContext)
-}
-
-// Add prebuilt information to snapshot data
-type hostSnapshotFakeJsonFlags struct {
-	SnapshotJsonFlags
-	Prebuilt bool `json:",omitempty"`
-}
-
-func registerHostSnapshotComponents(ctx android.RegistrationContext) {
-	ctx.RegisterParallelSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
-}
-
-type hostFakeSingleton struct {
-	snapshotDir string
-	zipFile     android.OptionalPath
-}
-
-func (c *hostFakeSingleton) init() {
-	c.snapshotDir = "host-fake-snapshot"
-
-}
-func HostToolsFakeAndroidSingleton() android.Singleton {
-	singleton := &hostFakeSingleton{}
-	singleton.init()
-	return singleton
-}
-
-func (c *hostFakeSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	if !ctx.DeviceConfig().HostFakeSnapshotEnabled() {
-		return
-	}
-	// Find all host binary modules add 'fake' versions to snapshot
-	var outputs android.Paths
-	seen := make(map[string]bool)
-	var jsonData []hostSnapshotFakeJsonFlags
-	prebuilts := make(map[string]bool)
-
-	ctx.VisitAllModules(func(module android.Module) {
-		if module.Target().Os != ctx.Config().BuildOSTarget.Os {
-			return
-		}
-		if module.Target().Arch.ArchType != ctx.Config().BuildOSTarget.Arch.ArchType {
-			return
-		}
-
-		if android.IsModulePrebuilt(module) {
-			// Add non-prebuilt module name to map of prebuilts
-			prebuilts[android.RemoveOptionalPrebuiltPrefix(module.Name())] = true
-			return
-		}
-		if !module.Enabled(ctx) || module.IsHideFromMake() {
-			return
-		}
-		apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
-		if !apexInfo.IsForPlatform() {
-			return
-		}
-		path := hostToolPath(module)
-		if path.Valid() && path.String() != "" {
-			outFile := filepath.Join(c.snapshotDir, path.String())
-			if !seen[outFile] {
-				seen[outFile] = true
-				outputs = append(outputs, WriteStringToFileRule(ctx, "", outFile))
-				jsonData = append(jsonData, hostSnapshotFakeJsonFlags{*hostJsonDesc(module), false})
-			}
-		}
-	})
-	// Update any module prebuilt information
-	for idx, _ := range jsonData {
-		if _, ok := prebuilts[jsonData[idx].ModuleName]; ok {
-			// Prebuilt exists for this module
-			jsonData[idx].Prebuilt = true
-		}
-	}
-	marsh, err := json.Marshal(jsonData)
-	if err != nil {
-		ctx.Errorf("host fake snapshot json marshal failure: %#v", err)
-		return
-	}
-	outputs = append(outputs, WriteStringToFileRule(ctx, string(marsh), filepath.Join(c.snapshotDir, "host_snapshot.json")))
-	c.zipFile = zipSnapshot(ctx, c.snapshotDir, c.snapshotDir, outputs)
-
-}
-func (c *hostFakeSingleton) MakeVars(ctx android.MakeVarsContext) {
-	if !c.zipFile.Valid() {
-		return
-	}
-	ctx.Phony(
-		"host-fake-snapshot",
-		c.zipFile.Path())
-
-	ctx.DistForGoal(
-		"host-fake-snapshot",
-		c.zipFile.Path())
-
-}
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
deleted file mode 100644
index edcc163..0000000
--- a/snapshot/host_snapshot.go
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2021 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 snapshot
-
-import (
-	"encoding/json"
-	"fmt"
-	"path/filepath"
-	"sort"
-	"strings"
-
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
-
-	"android/soong/android"
-)
-
-//
-// The host_snapshot module creates a snapshot of the modules defined in
-// the deps property.  The modules within the deps property (host tools)
-// are ones that return a valid path via HostToolPath() of the
-// HostToolProvider.  The created snapshot contains the binaries and any
-// transitive PackagingSpecs of the included host tools, along with a JSON
-// meta file.
-//
-// The snapshot is installed into a source tree via
-// development/vendor_snapshot/update.py, the included modules are
-// provided as preferred prebuilts.
-//
-// To determine which tools to include in the host snapshot see
-// host_fake_snapshot.go.
-
-func init() {
-	registerHostBuildComponents(android.InitRegistrationContext)
-}
-
-func registerHostBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("host_snapshot", hostSnapshotFactory)
-}
-
-// Relative installation path
-type RelativeInstallPath interface {
-	RelativeInstallPath() string
-}
-
-type hostSnapshot struct {
-	android.ModuleBase
-	android.PackagingBase
-
-	outputFile android.OutputPath
-	installDir android.InstallPath
-}
-
-type ProcMacro interface {
-	ProcMacro() bool
-	CrateName() string
-}
-
-func hostSnapshotFactory() android.Module {
-	module := &hostSnapshot{}
-	initHostToolsModule(module)
-	return module
-}
-func initHostToolsModule(module *hostSnapshot) {
-	android.InitPackageModule(module)
-	android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon)
-}
-
-var dependencyTag = struct {
-	blueprint.BaseDependencyTag
-	android.InstallAlwaysNeededDependencyTag
-	android.PackagingItemAlwaysDepTag
-}{}
-
-func (f *hostSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
-	f.AddDeps(ctx, dependencyTag)
-}
-func (f *hostSnapshot) installFileName() string {
-	return f.Name() + ".zip"
-}
-
-// Create zipfile with JSON description, notice files... for dependent modules
-func (f *hostSnapshot) CreateMetaData(ctx android.ModuleContext, fileName string) android.OutputPath {
-	var jsonData []SnapshotJsonFlags
-	var metaPaths android.Paths
-
-	installedNotices := make(map[string]bool)
-	metaZipFile := android.PathForModuleOut(ctx, fileName).OutputPath
-
-	// Create JSON file based on the direct dependencies
-	ctx.VisitDirectDeps(func(dep android.Module) {
-		desc := hostJsonDesc(dep)
-		if desc != nil {
-			jsonData = append(jsonData, *desc)
-		}
-		for _, notice := range dep.EffectiveLicenseFiles() {
-			if _, ok := installedNotices[notice.String()]; !ok {
-				installedNotices[notice.String()] = true
-				noticeOut := android.PathForModuleOut(ctx, "NOTICE_FILES", notice.String()).OutputPath
-				CopyFileToOutputPathRule(pctx, ctx, notice, noticeOut)
-				metaPaths = append(metaPaths, noticeOut)
-			}
-		}
-	})
-	// Sort notice paths and json data for repeatble build
-	sort.Slice(jsonData, func(i, j int) bool {
-		return (jsonData[i].ModuleName < jsonData[j].ModuleName)
-	})
-	sort.Slice(metaPaths, func(i, j int) bool {
-		return (metaPaths[i].String() < metaPaths[j].String())
-	})
-
-	marsh, err := json.Marshal(jsonData)
-	if err != nil {
-		ctx.ModuleErrorf("host snapshot json marshal failure: %#v", err)
-		return android.OutputPath{}
-	}
-
-	jsonZipFile := android.PathForModuleOut(ctx, "host_snapshot.json").OutputPath
-	metaPaths = append(metaPaths, jsonZipFile)
-	rspFile := android.PathForModuleOut(ctx, "host_snapshot.rsp").OutputPath
-	android.WriteFileRule(ctx, jsonZipFile, string(marsh))
-
-	builder := android.NewRuleBuilder(pctx, ctx)
-
-	builder.Command().
-		BuiltTool("soong_zip").
-		FlagWithArg("-C ", android.PathForModuleOut(ctx).OutputPath.String()).
-		FlagWithOutput("-o ", metaZipFile).
-		FlagWithRspFileInputList("-r ", rspFile, metaPaths)
-	builder.Build("zip_meta", fmt.Sprintf("zipping meta data for %s", ctx.ModuleName()))
-
-	return metaZipFile
-}
-
-// Create the host tool zip file
-func (f *hostSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Create a zip file for the binaries, and a zip of the meta data, then merge zips
-	depsZipFile := android.PathForModuleOut(ctx, f.Name()+"_deps.zip").OutputPath
-	modsZipFile := android.PathForModuleOut(ctx, f.Name()+"_mods.zip").OutputPath
-	f.outputFile = android.PathForModuleOut(ctx, f.installFileName()).OutputPath
-
-	f.installDir = android.PathForModuleInstall(ctx)
-
-	f.CopyDepsToZip(ctx, f.GatherPackagingSpecs(ctx), depsZipFile)
-
-	builder := android.NewRuleBuilder(pctx, ctx)
-	builder.Command().
-		BuiltTool("zip2zip").
-		FlagWithInput("-i ", depsZipFile).
-		FlagWithOutput("-o ", modsZipFile).
-		Text("**/*:" + proptools.ShellEscape(f.installDir.String()))
-
-	metaZipFile := f.CreateMetaData(ctx, f.Name()+"_meta.zip")
-
-	builder.Command().
-		BuiltTool("merge_zips").
-		Output(f.outputFile).
-		Input(metaZipFile).
-		Input(modsZipFile)
-
-	builder.Build("manifest", fmt.Sprintf("Adding manifest %s", f.installFileName()))
-	ctx.InstallFile(f.installDir, f.installFileName(), f.outputFile)
-
-}
-
-// Implements android.AndroidMkEntriesProvider
-func (f *hostSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(f.outputFile),
-		DistFiles:  android.MakeDefaultDistFiles(f.outputFile),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", f.installDir.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
-			},
-		},
-	}}
-}
-
-// Get host tools path and relative install string helpers
-func hostToolPath(m android.Module) android.OptionalPath {
-	if provider, ok := m.(android.HostToolProvider); ok {
-		return provider.HostToolPath()
-	}
-	return android.OptionalPath{}
-
-}
-func hostRelativePathString(m android.Module) string {
-	var outString string
-	if rel, ok := m.(RelativeInstallPath); ok {
-		outString = rel.RelativeInstallPath()
-	}
-	return outString
-}
-
-// Create JSON description for given module, only create descriptions for binary modules
-// and rust_proc_macro modules which provide a valid HostToolPath
-func hostJsonDesc(m android.Module) *SnapshotJsonFlags {
-	path := hostToolPath(m)
-	relPath := hostRelativePathString(m)
-	procMacro := false
-	moduleStem := filepath.Base(path.String())
-	crateName := ""
-
-	if pm, ok := m.(ProcMacro); ok && pm.ProcMacro() {
-		procMacro = pm.ProcMacro()
-		moduleStem = strings.TrimSuffix(moduleStem, filepath.Ext(moduleStem))
-		crateName = pm.CrateName()
-	}
-
-	if path.Valid() && path.String() != "" {
-		props := &SnapshotJsonFlags{
-			ModuleStemName:      moduleStem,
-			Filename:            path.String(),
-			Required:            append(m.HostRequiredModuleNames(), m.RequiredModuleNames()...),
-			RelativeInstallPath: relPath,
-			RustProcMacro:       procMacro,
-			CrateName:           crateName,
-		}
-		props.InitBaseSnapshotProps(m)
-		return props
-	}
-	return nil
-}
diff --git a/snapshot/host_test.go b/snapshot/host_test.go
deleted file mode 100644
index ab9fedd..0000000
--- a/snapshot/host_test.go
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2021 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 snapshot
-
-import (
-	"path/filepath"
-	"testing"
-
-	"android/soong/android"
-)
-
-// host_snapshot and host-fake-snapshot test functions
-
-type hostTestModule struct {
-	android.ModuleBase
-	props struct {
-		Deps []string
-	}
-}
-
-func hostTestBinOut(bin string) string {
-	return filepath.Join("out", "bin", bin)
-}
-
-func (c *hostTestModule) HostToolPath() android.OptionalPath {
-	return (android.OptionalPathForPath(android.PathForTesting(hostTestBinOut(c.Name()))))
-}
-
-func hostTestModuleFactory() android.Module {
-	m := &hostTestModule{}
-	m.AddProperties(&m.props)
-	android.InitAndroidArchModule(m, android.HostSupported, android.MultilibFirst)
-	return m
-}
-func (m *hostTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	builtFile := android.PathForModuleOut(ctx, m.Name())
-	dir := ctx.Target().Arch.ArchType.Multilib
-	installDir := android.PathForModuleInstall(ctx, dir)
-	ctx.InstallFile(installDir, m.Name(), builtFile)
-}
-
-// Common blueprint used for testing
-var hostTestBp = `
-		license_kind {
-			name: "test_notice",
-			conditions: ["notice"],
-		}
-		license {
-			name: "host_test_license",
-			visibility: ["//visibility:public"],
-			license_kinds: [
-				"test_notice"
-			],
-			license_text: [
-				"NOTICE",
-			],
-		}
-		component {
-			name: "foo",
-			deps: ["bar"],
-		}
-		component {
-			name: "bar",
-			licenses: ["host_test_license"],
-		}
-		`
-
-var hostTestModBp = `
-		host_snapshot {
-			name: "test-host-snapshot",
-			deps: [
-				"foo",
-			],
-		}
-		`
-
-var prepareForHostTest = android.GroupFixturePreparers(
-	android.PrepareForTestWithAndroidBuildComponents,
-	android.PrepareForTestWithLicenses,
-	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		ctx.RegisterModuleType("component", hostTestModuleFactory)
-	}),
-)
-
-// Prepare for host_snapshot test
-var prepareForHostModTest = android.GroupFixturePreparers(
-	prepareForHostTest,
-	android.FixtureWithRootAndroidBp(hostTestBp+hostTestModBp),
-	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		registerHostBuildComponents(ctx)
-	}),
-)
-
-// Prepare for fake host snapshot test disabled
-var prepareForFakeHostTest = android.GroupFixturePreparers(
-	prepareForHostTest,
-	android.FixtureWithRootAndroidBp(hostTestBp),
-	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		registerHostSnapshotComponents(ctx)
-	}),
-)
-
-// Prepare for fake host snapshot test enabled
-var prepareForFakeHostTestEnabled = android.GroupFixturePreparers(
-	prepareForFakeHostTest,
-	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.HostFakeSnapshotEnabled = true
-	}),
-)
-
-// Validate that a hostSnapshot object is created containing zip files and JSON file
-// content of zip file is not validated as this is done by PackagingSpecs
-func TestHostSnapshot(t *testing.T) {
-	result := prepareForHostModTest.RunTest(t)
-	t.Helper()
-	ctx := result.TestContext.ModuleForTests("test-host-snapshot", result.Config.BuildOS.String()+"_common")
-	mod := ctx.Module().(*hostSnapshot)
-	if ctx.MaybeOutput("host_snapshot.json").Rule == nil {
-		t.Error("Manifest file not found")
-	}
-	zips := []string{"_deps.zip", "_mods.zip", ".zip"}
-
-	for _, zip := range zips {
-		zFile := mod.Name() + zip
-		if ctx.MaybeOutput(zFile).Rule == nil {
-			t.Error("Zip file ", zFile, "not found")
-		}
-
-	}
-}
-
-// Validate fake host snapshot contains binary modules as well as the JSON meta file
-func TestFakeHostSnapshotEnable(t *testing.T) {
-	result := prepareForFakeHostTestEnabled.RunTest(t)
-	t.Helper()
-	bins := []string{"foo", "bar"}
-	ctx := result.TestContext.SingletonForTests("host-fake-snapshot")
-	if ctx.MaybeOutput(filepath.Join("host-fake-snapshot", "host_snapshot.json")).Rule == nil {
-		t.Error("Manifest file not found")
-	}
-	for _, bin := range bins {
-		if ctx.MaybeOutput(filepath.Join("host-fake-snapshot", hostTestBinOut(bin))).Rule == nil {
-			t.Error("Binary file ", bin, "not found")
-		}
-
-	}
-}
-
-// Validate not fake host snapshot if HostFakeSnapshotEnabled has not been set to true
-func TestFakeHostSnapshotDisable(t *testing.T) {
-	result := prepareForFakeHostTest.RunTest(t)
-	t.Helper()
-	ctx := result.TestContext.SingletonForTests("host-fake-snapshot")
-	if len(ctx.AllOutputs()) != 0 {
-		t.Error("Fake host snapshot not empty when disabled")
-	}
-
-}
diff --git a/sysprop/Android.bp b/sysprop/Android.bp
index a00a5e4..22cba3b 100644
--- a/sysprop/Android.bp
+++ b/sysprop/Android.bp
@@ -21,4 +21,6 @@
         "sysprop_test.go",
     ],
     pluginFor: ["soong_build"],
+    // Used by plugins
+    visibility: ["//visibility:public"],
 }
diff --git a/testing/all_code_metadata.go b/testing/all_code_metadata.go
index 12aa7b5..e89b281 100644
--- a/testing/all_code_metadata.go
+++ b/testing/all_code_metadata.go
@@ -21,7 +21,7 @@
 
 	ctx.VisitAllModules(
 		func(module android.Module) {
-			if metadata, ok := android.SingletonModuleProvider(ctx, module, CodeMetadataProviderKey); ok {
+			if metadata, ok := android.OtherModuleProvider(ctx, module, CodeMetadataProviderKey); ok {
 				intermediateMetadataPaths = append(intermediateMetadataPaths, metadata.IntermediatePath)
 			}
 		},
diff --git a/testing/all_test_specs.go b/testing/all_test_specs.go
index b035435..68f24d1 100644
--- a/testing/all_test_specs.go
+++ b/testing/all_test_specs.go
@@ -21,7 +21,7 @@
 	var intermediateMetadataPaths android.Paths
 
 	ctx.VisitAllModules(func(module android.Module) {
-		if metadata, ok := android.SingletonModuleProvider(ctx, module, TestSpecProviderKey); ok {
+		if metadata, ok := android.OtherModuleProvider(ctx, module, TestSpecProviderKey); ok {
 			intermediateMetadataPaths = append(intermediateMetadataPaths, metadata.IntermediatePath)
 		}
 	})
diff --git a/testing/code_metadata_internal_proto/Android.bp b/testing/code_metadata_internal_proto/Android.bp
index a534cc2..396e44f 100644
--- a/testing/code_metadata_internal_proto/Android.bp
+++ b/testing/code_metadata_internal_proto/Android.bp
@@ -20,10 +20,14 @@
     name: "soong-testing-code_metadata_internal_proto",
     pkgPath: "android/soong/testing/code_metadata_internal_proto",
     deps: [
-            "golang-protobuf-reflect-protoreflect",
-            "golang-protobuf-runtime-protoimpl",
-        ],
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "code_metadata_internal.pb.go",
     ],
+    visibility: [
+        "//build/make/tools/metadata",
+        "//build/soong:__subpackages__",
+    ],
 }
diff --git a/testing/code_metadata_proto/Android.bp b/testing/code_metadata_proto/Android.bp
index f07efff..ae41d4a 100644
--- a/testing/code_metadata_proto/Android.bp
+++ b/testing/code_metadata_proto/Android.bp
@@ -26,6 +26,7 @@
     srcs: [
         "code_metadata.pb.go",
     ],
+    visibility: ["//build/make/tools/metadata"],
 }
 
 python_library_host {
@@ -40,4 +41,5 @@
     proto: {
         canonical_path_from_root: false,
     },
+    visibility: ["//tools/asuite/team_build_scripts"],
 }
diff --git a/testing/test_spec_proto/Android.bp b/testing/test_spec_proto/Android.bp
index d5ad70b..1070d1a 100644
--- a/testing/test_spec_proto/Android.bp
+++ b/testing/test_spec_proto/Android.bp
@@ -26,6 +26,11 @@
     srcs: [
         "test_spec.pb.go",
     ],
+    visibility: [
+        "//build/make/tools/metadata",
+        "//build/soong:__subpackages__",
+        "//vendor:__subpackages__",
+    ],
 }
 
 python_library_host {
@@ -40,4 +45,5 @@
     proto: {
         canonical_path_from_root: false,
     },
+    visibility: ["//tools/asuite/team_build_scripts"],
 }
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 2e40950..715f976 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -145,36 +145,19 @@
   run_soong
   local -r ninja_mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
-  local glob_deps_file=out/soong/globs/"${target_product}"/0.d
-
   run_soong
   local -r ninja_mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
-  # There is an ineffiencency in glob that requires bpglob to rerun once for each glob to update
-  # the entry in the .ninja_log.  It doesn't update the output file, but we can detect the rerun
-  # by checking if the deps file was created.
-  if [ ! -e "$glob_deps_file" ]; then
-    fail "Glob deps file missing after second build"
-  fi
-
-  local -r glob_deps_mtime2=$(stat -c "%y" "$glob_deps_file")
-
   if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
     fail "Ninja file rewritten on null incremental build"
   fi
 
   run_soong
   local -r ninja_mtime3=$(stat -c "%y" out/soong/build."${target_product}".ninja)
-  local -r glob_deps_mtime3=$(stat -c "%y" "$glob_deps_file")
 
   if [[ "$ninja_mtime2" != "$ninja_mtime3" ]]; then
     fail "Ninja file rewritten on null incremental build"
   fi
-
-  # The bpglob commands should not rerun after the first incremental build.
-  if [[ "$glob_deps_mtime2" != "$glob_deps_mtime3" ]]; then
-    fail "Glob deps file rewritten on second null incremental build"
-  fi
 }
 
 function test_add_file_to_glob() {
diff --git a/tests/build_action_caching_test.sh b/tests/build_action_caching_test.sh
new file mode 100755
index 0000000..8ecd037
--- /dev/null
+++ b/tests/build_action_caching_test.sh
@@ -0,0 +1,190 @@
+#!/bin/bash -u
+
+set -o pipefail
+
+# Test that the mk and ninja files generated by Soong don't change if some
+# incremental modules are restored from cache.
+
+OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
+
+echo ${OUTPUT_DIR}
+
+function cleanup {
+  rm -rf "${OUTPUT_DIR}"
+}
+trap cleanup EXIT
+
+function run_soong_build {
+  USE_RBE=false TARGET_PRODUCT=aosp_arm TARGET_RELEASE=trunk_staging TARGET_BUILD_VARIANT=userdebug build/soong/soong_ui.bash --make-mode "$@" nothing
+}
+
+function run_soong_clean {
+  build/soong/soong_ui.bash --make-mode clean
+}
+
+function assert_files_equal {
+  if [ $# -ne 2 ]; then
+    echo "Usage: assert_files_equal file1 file2"
+    exit 1
+  fi
+
+  if ! cmp -s "$1" "$2"; then
+    echo "Files are different: $1 $2"
+    exit 1
+  fi
+}
+
+function compare_mtimes() {
+  if [ $# -ne 2 ]; then
+    echo "Usage: compare_mtimes file1 file2"
+    exit 1
+  fi
+
+  file1_mtime=$(stat -c '%Y' $1)
+  file2_mtime=$(stat -c '%Y' $2)
+
+  if [ "$file1_mtime" -eq "$file2_mtime" ]; then
+      return 1
+  else
+      return 0
+  fi
+}
+
+function test_build_action_restoring() {
+  local test_dir="${OUTPUT_DIR}/test_build_action_restoring"
+  mkdir -p ${test_dir}
+  run_soong_clean
+  cat > ${test_dir}/Android.bp <<'EOF'
+python_binary_host {
+  name: "my_little_binary_host",
+  srcs: ["my_little_binary_host.py"],
+}
+EOF
+  touch ${test_dir}/my_little_binary_host.py
+  run_soong_build --incremental-build-actions
+  local dir_before="${test_dir}/before"
+  mkdir -p ${dir_before}
+  cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/before
+  # add a comment to the bp file, this should force a new analysis but no module
+  # should be really impacted, so all the incremental modules should be skipped.
+  cat >> ${test_dir}/Android.bp <<'EOF'
+// new comments
+EOF
+  run_soong_build --incremental-build-actions
+  local dir_after="${test_dir}/after"
+  mkdir -p ${dir_after}
+  cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/after
+
+  compare_incremental_files $dir_before $dir_after
+  rm -rf "$test_dir"
+  echo "test_build_action_restoring test passed"
+}
+
+function test_incremental_build_parity() {
+  local test_dir="${OUTPUT_DIR}/test_incremental_build_parity"
+  run_soong_clean
+  run_soong_build
+  local dir_before="${test_dir}/before"
+  mkdir -p ${dir_before}
+  cp -pr out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/before
+
+  # Now run clean build with incremental enabled
+  run_soong_clean
+  run_soong_build --incremental-build-actions
+  local dir_after="${test_dir}/after"
+  mkdir -p ${dir_after}
+  cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/after
+
+  compare_files_parity $dir_before $dir_after
+  rm -rf "$test_dir"
+  echo "test_incremental_build_parity test passed"
+}
+
+function compare_files_parity() {
+  local dir_before=$1; shift
+  local dir_after=$1; shift
+  count=0
+  for file_before in ${dir_before}/*.mk; do
+    file_after="${dir_after}/$(basename "$file_before")"
+    assert_files_equal $file_before $file_after
+    ((count++))
+  done
+  echo "Compared $count mk files"
+
+  combined_before_file="${dir_before}/combined_files.ninja"
+  count=0
+  for file in ${dir_before}/build.aosp_arm.*.ninja; do
+    cat $file >> $combined_before_file
+    ((count++))
+  done
+  echo "Combined $count ninja files from normal build"
+
+  combined_after_file="${dir_after}/combined_files.ninja"
+  count=0
+  for file in ${dir_after}/build.aosp_arm.*.ninja; do
+    cat $file >> $combined_after_file
+    ((count++))
+  done
+  echo "Combined $count ninja files from incremental build"
+
+  combined_incremental_ninjas="${dir_after}/combined_incremental_files.ninja"
+  count=0
+  for file in ${dir_after}/build_aosp_arm_ninja_incremental/*.ninja; do
+    cat $file >> $combined_incremental_ninjas
+    ((count++))
+  done
+  echo "Combined $count incremental ninja files"
+
+  cat $combined_incremental_ninjas >> $combined_after_file
+  sort $combined_after_file -o $combined_after_file
+  sort $combined_before_file -o $combined_before_file
+  assert_files_equal $combined_before_file $combined_after_file
+}
+
+function compare_incremental_files() {
+  local dir_before=$1; shift
+  local dir_after=$1; shift
+  count=0
+  for file_before in ${dir_before}/*.ninja; do
+    file_after="${dir_after}/$(basename "$file_before")"
+    assert_files_equal $file_before $file_after
+    compare_mtimes $file_before $file_after
+    if [ $? -ne 0 ]; then
+      echo "Files have identical mtime: $file_before $file_after"
+      exit 1
+    fi
+    ((count++))
+  done
+  echo "Compared $count ninja files"
+
+  count=0
+  for file_before in ${dir_before}/*.mk; do
+    file_after="${dir_after}/$(basename "$file_before")"
+    assert_files_equal $file_before $file_after
+    compare_mtimes $file_before $file_after
+    # mk files shouldn't be regenerated
+    if [ $? -ne 1 ]; then
+      echo "Files have different mtimes: $file_before $file_after"
+      exit 1
+    fi
+    ((count++))
+  done
+  echo "Compared $count mk files"
+
+  count=0
+  for file_before in ${dir_before}/build_aosp_arm_ninja_incremental/*.ninja; do
+    file_after="${dir_after}/build_aosp_arm_ninja_incremental/$(basename "$file_before")"
+    assert_files_equal $file_before $file_after
+    compare_mtimes $file_before $file_after
+    # ninja files of skipped modules shouldn't be regenerated
+    if [ $? -ne 1 ]; then
+      echo "Files have different mtimes: $file_before $file_after"
+      exit 1
+    fi
+    ((count++))
+  done
+  echo "Compared $count incremental ninja files"
+}
+
+test_incremental_build_parity
+test_build_action_restoring
diff --git a/tests/run_tool_with_logging_test.py b/tests/run_tool_with_logging_test.py
index 57a6d62..1a946a1 100644
--- a/tests/run_tool_with_logging_test.py
+++ b/tests/run_tool_with_logging_test.py
@@ -193,7 +193,7 @@
     logger_path = self._import_executable("tool_event_logger")
 
     self._run_script_and_wait(f"""
-      TMPDIR="{self.working_dir.name}"
+      export TMPDIR="{self.working_dir.name}"
       export ANDROID_TOOL_LOGGER="{logger_path}"
       export ANDROID_TOOL_LOGGER_EXTRA_ARGS="--dry_run"
       {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
@@ -206,7 +206,7 @@
     logger_path = self._import_executable("tool_event_logger")
 
     self._run_script_and_wait(f"""
-      TMPDIR="{self.working_dir.name}"
+      export TMPDIR="{self.working_dir.name}"
       export ANDROID_TOOL_LOGGER="{logger_path}"
       export ANDROID_TOOL_LOGGER_EXTRA_ARGS="--dry_run"
       {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} --tool-arg1
diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh
index 8dc1630..0471853 100755
--- a/tests/sbom_test.sh
+++ b/tests/sbom_test.sh
@@ -70,13 +70,14 @@
   # m droid, build sbom later in case additional dependencies might be built and included in partition images.
   run_soong "${out_dir}" "droid dump.erofs lz4"
 
+  soong_sbom_out=$out_dir/soong/sbom/$target_product
   product_out=$out_dir/target/product/vsoc_x86_64
   sbom_test=$product_out/sbom_test
   mkdir -p $sbom_test
   cp $product_out/*.img $sbom_test
 
   # m sbom
-  run_soong "${out_dir}" sbom
+  run_soong "${out_dir}" "sbom"
 
   # Generate installed file list from .img files in PRODUCT_OUT
   dump_erofs=$out_dir/host/linux-x86/bin/dump.erofs
@@ -117,7 +118,7 @@
   for f in $EROFS_IMAGES; do
     partition_name=$(basename $f | cut -d. -f1)
     file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
-    files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
+    files_in_soong_spdx_file="${sbom_test}/soong-sbom-${partition_name}-files-in-spdx.txt"
     rm "$file_list_file" > /dev/null 2>&1 || true
     all_dirs="/"
     while [ ! -z "$all_dirs" ]; do
@@ -145,22 +146,23 @@
     done
     sort -n -o "$file_list_file" "$file_list_file"
 
-    grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' > "$files_in_spdx_file"
+    # Diff the file list from image and file list in SBOM created by Soong
+    grep "FileName: /${partition_name}/" $soong_sbom_out/sbom.spdx | sed 's/^FileName: //' > "$files_in_soong_spdx_file"
     if [ "$partition_name" = "system" ]; then
       # system partition is mounted to /, so include FileName starts with /root/ too.
-      grep "FileName: /root/" $product_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_spdx_file"
+      grep "FileName: /root/" $soong_sbom_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_soong_spdx_file"
     fi
-    sort -n -o "$files_in_spdx_file" "$files_in_spdx_file"
+    sort -n -o "$files_in_soong_spdx_file" "$files_in_soong_spdx_file"
 
-    echo ============ Diffing files in $f and SBOM
-    diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name" ""
+    echo ============ Diffing files in $f and SBOM created by Soong
+    diff_files "$file_list_file" "$files_in_soong_spdx_file" "$partition_name" ""
   done
 
   RAMDISK_IMAGES="$product_out/ramdisk.img"
   for f in $RAMDISK_IMAGES; do
     partition_name=$(basename $f | cut -d. -f1)
     file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
-    files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
+    files_in_soong_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-soong-spdx.txt"
     # lz4 decompress $f to stdout
     # cpio list all entries like ls -l
     # grep filter normal files and symlinks
@@ -168,13 +170,15 @@
     # sed remove partition name from entry names
     $lz4 -c -d $f | cpio -tv 2>/dev/null | grep '^[-l]' | awk -F ' ' '{print $9}' | sed "s:^:/$partition_name/:" | sort -n > "$file_list_file"
 
-    grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_spdx_file"
+    grep "FileName: /${partition_name}/" $soong_sbom_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_soong_spdx_file"
 
-    echo ============ Diffing files in $f and SBOM
-    diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name" ""
+    echo ============ Diffing files in $f and SBOM created by Soong
+    diff_files "$file_list_file" "$files_in_soong_spdx_file" "$partition_name" ""
   done
 
-  verify_package_verification_code "$product_out/sbom.spdx"
+  verify_package_verification_code "$soong_sbom_out/sbom.spdx"
+
+  verify_packages_licenses "$soong_sbom_out/sbom.spdx"
 
   # Teardown
   cleanup "${out_dir}"
@@ -213,6 +217,41 @@
   fi
 }
 
+function verify_packages_licenses {
+  local sbom_file="$1"; shift
+
+  num_of_packages=$(grep 'PackageName:' $sbom_file | wc -l)
+  num_of_declared_licenses=$(grep 'PackageLicenseDeclared:' $sbom_file | wc -l)
+  if [ "$num_of_packages" = "$num_of_declared_licenses" ]
+  then
+    echo "Number of packages with declared license is correct."
+  else
+    echo "Number of packages with declared license is WRONG."
+    exit 1
+  fi
+
+  # PRODUCT and 7 prebuilt packages have "PackageLicenseDeclared: NOASSERTION"
+  # All other packages have declared licenses
+  num_of_packages_with_noassertion_license=$(grep 'PackageLicenseDeclared: NOASSERTION' $sbom_file | wc -l)
+  if [ $num_of_packages_with_noassertion_license = 15 ]
+  then
+    echo "Number of packages with NOASSERTION license is correct."
+  else
+    echo "Number of packages with NOASSERTION license is WRONG."
+    exit 1
+  fi
+
+  num_of_files=$(grep 'FileName:' $sbom_file | wc -l)
+  num_of_concluded_licenses=$(grep 'LicenseConcluded:' $sbom_file | wc -l)
+  if [ "$num_of_files" = "$num_of_concluded_licenses" ]
+  then
+    echo "Number of files with concluded license is correct."
+  else
+    echo "Number of files with concluded license is WRONG."
+    exit 1
+  fi
+}
+
 function test_sbom_unbundled_apex {
   # Setup
   out_dir="$(setup)"
@@ -274,7 +313,7 @@
 
 target_product=aosp_cf_x86_64_phone
 target_release=trunk_staging
-target_build_variant=userdebug
+target_build_variant=eng
 for i in "$@"; do
   case $i in
     TARGET_PRODUCT=*)
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index f9622d3..7a04c19 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -6,6 +6,7 @@
 	"encoding/json"
 	"fmt"
 	"io"
+	"slices"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -174,6 +175,20 @@
 		return false
 	}
 
+	var extra_derived_suites []string
+	// Ensure all suites listed are also in base.
+	for _, s := range m.tradefedProperties.Test_suites {
+		if !slices.Contains(m.provider.TestSuites, s) {
+			extra_derived_suites = append(extra_derived_suites, s)
+		}
+	}
+	if len(extra_derived_suites) != 0 {
+		ctx.ModuleErrorf("Suites: [%s] listed but do not exist in base module: %s",
+			strings.Join(extra_derived_suites, ", "),
+			*m.tradefedProperties.Base)
+		return false
+	}
+
 	return true
 }
 
@@ -227,6 +242,7 @@
 
 				entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", m.provider.IsUnitTest)
 				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.
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index 97179f5..f76a152 100644
--- a/tradefed_modules/test_module_config_test.go
+++ b/tradefed_modules/test_module_config_test.go
@@ -40,6 +40,8 @@
 			name: "base",
 			sdk_version: "current",
                         data: [":HelperApp", "data/testfile"],
+                        host_required: ["other-module"],
+                        test_suites: ["general-tests"],
 		}
 
                 test_module_config {
@@ -79,6 +81,7 @@
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{})
 
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_REQUIRED_MODULES"], []string{"base"})
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_HOST_REQUIRED_MODULES"], []string{"other-module"})
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_CERTIFICATE"], []string{"build/make/target/product/security/testkey.x509.pem"})
 	android.AssertStringEquals(t, "", entries.Class, "APPS")
 
@@ -160,7 +163,7 @@
 		java.PrepareForTestWithJavaDefaultModules,
 		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
 	).ExtendWithErrorHandler(
-		android.FixtureExpectsOneErrorPattern("'base' module used as base but it is not a 'android_test' module.")).
+		android.FixtureExpectsAtLeastOneErrorMatchingPattern("'base' module used as base but it is not a 'android_test' module.")).
 		RunTestWithBp(t, badBp)
 }
 
@@ -193,6 +196,7 @@
 			name: "base",
 			sdk_version: "current",
                         srcs: ["a.java"],
+                        test_suites: ["general-tests"],
 		}
 
                 test_module_config {
@@ -218,6 +222,7 @@
 			sdk_version: "current",
                         srcs: ["a.java"],
                         data: [":HelperApp", "data/testfile"],
+                        test_suites: ["general-tests"],
 		}
 
                 android_test_helper_app {
@@ -362,6 +367,31 @@
 		RunTestWithBp(t, badBp)
 }
 
+func TestModuleConfigNonMatchingTestSuitesGiveErrors(t *testing.T) {
+	badBp := `
+		java_test_host {
+			name: "base",
+                        srcs: ["a.java"],
+                        test_suites: ["general-tests", "some-compat"],
+		}
+
+                test_module_config_host {
+                        name: "derived_test",
+                        base: "base",
+                        exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+                        include_annotations: ["android.platform.test.annotations.LargeTest"],
+                        test_suites: ["device-tests", "random-suite"],
+                }`
+
+	android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+	).ExtendWithErrorHandler(
+		// Use \\ to escape bracket so it isn't used as [] set for regex.
+		android.FixtureExpectsAtLeastOneErrorMatchingPattern("Suites: \\[device-tests, random-suite] listed but do not exist in base module")).
+		RunTestWithBp(t, badBp)
+}
+
 func TestTestOnlyProvider(t *testing.T) {
 	t.Parallel()
 	ctx := android.GroupFixturePreparers(
@@ -389,6 +419,7 @@
 			name: "base",
 			sdk_version: "current",
                         data: ["data/testfile"],
+                        test_suites: ["general-tests"],
 		}
 
 		java_test_host {
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index ee286f6..fcf29c5 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -36,6 +36,7 @@
         "blueprint-bootstrap",
         "blueprint-microfactory",
         "soong-android",
+        "soong-elf",
         "soong-finder",
         "soong-remoteexec",
         "soong-shared",
diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go
index bbac2db..2ec8972 100644
--- a/ui/build/androidmk_denylist.go
+++ b/ui/build/androidmk_denylist.go
@@ -25,6 +25,8 @@
 	"dalvik/",
 	"developers/",
 	"development/",
+	"device/common/",
+	"device/google_car/",
 	"device/sample/",
 	"frameworks/",
 	// Do not block other directories in kernel/, see b/319658303.
@@ -33,6 +35,7 @@
 	"kernel/tests/",
 	"libcore/",
 	"libnativehelper/",
+	"packages/",
 	"pdk/",
 	"prebuilts/",
 	"sdk/",
@@ -40,6 +43,15 @@
 	"trusty/",
 	// Add back toolchain/ once defensive Android.mk files are removed
 	//"toolchain/",
+	"vendor/google_contexthub/",
+	"vendor/google_data/",
+	"vendor/google_elmyra/",
+	"vendor/google_mhl/",
+	"vendor/google_pdk/",
+	"vendor/google_testing/",
+	"vendor/partner_testing/",
+	"vendor/partner_tools/",
+	"vendor/pdk/",
 }
 
 func blockAndroidMks(ctx Context, androidMks []string) {
diff --git a/ui/build/build.go b/ui/build/build.go
index 03d8392..28c3284 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -22,6 +22,7 @@
 	"sync"
 	"text/template"
 
+	"android/soong/elf"
 	"android/soong/ui/metrics"
 )
 
@@ -79,7 +80,7 @@
 		if username, ok = config.environ.Get("BUILD_USERNAME"); !ok {
 			ctx.Fatalln("Missing BUILD_USERNAME")
 		}
-		buildNumber = fmt.Sprintf("eng.%.6s.00000000.000000", username)
+		buildNumber = fmt.Sprintf("eng.%.6s", username)
 		writeValueIfChanged(ctx, config, config.OutDir(), "file_name_tag.txt", username)
 	}
 	// Write the build number to a file so it can be read back in
@@ -210,9 +211,38 @@
 	}
 }
 
+func abfsBuildStarted(ctx Context, config Config) {
+	abfsBox := config.PrebuiltBuildTool("abfsbox")
+	cmdArgs := []string{"build-started", "--"}
+	cmdArgs = append(cmdArgs, config.Arguments()...)
+	cmd := Command(ctx, config, "abfsbox", abfsBox, cmdArgs...)
+	cmd.Sandbox = noSandbox
+	cmd.RunAndPrintOrFatal()
+}
+
+func abfsBuildFinished(ctx Context, config Config, finished bool) {
+	var errMsg string
+	if !finished {
+		errMsg = "build was interrupted"
+	}
+	abfsBox := config.PrebuiltBuildTool("abfsbox")
+	cmdArgs := []string{"build-finished", "-e", errMsg, "--"}
+	cmdArgs = append(cmdArgs, config.Arguments()...)
+	cmd := Command(ctx, config, "abfsbox", abfsBox, cmdArgs...)
+	cmd.RunAndPrintOrFatal()
+}
+
 // Build the tree. Various flags in `config` govern which components of
 // the build to run.
 func Build(ctx Context, config Config) {
+	done := false
+	if config.UseABFS() {
+		abfsBuildStarted(ctx, config)
+		defer func() {
+			abfsBuildFinished(ctx, config, done)
+		}()
+	}
+
 	ctx.Verboseln("Starting build with args:", config.Arguments())
 	ctx.Verboseln("Environment:", config.Environment().Environ())
 
@@ -344,11 +374,23 @@
 			installCleanIfNecessary(ctx, config)
 		}
 		runNinjaForBuild(ctx, config)
+		updateBuildIdDir(ctx, config)
 	}
 
 	if what&RunDistActions != 0 {
 		runDistActions(ctx, config)
 	}
+	done = true
+}
+
+func updateBuildIdDir(ctx Context, config Config) {
+	ctx.BeginTrace(metrics.RunShutdownTool, "update_build_id_dir")
+	defer ctx.EndTrace()
+
+	symbolsDir := filepath.Join(config.ProductOut(), "symbols")
+	if err := elf.UpdateBuildIdDir(symbolsDir); err != nil {
+		ctx.Printf("failed to update %s/.build-id: %v", symbolsDir, err)
+	}
 }
 
 func evaluateWhatToRun(config Config, verboseln func(v ...interface{})) int {
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 251c955..46e38c9 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -125,7 +125,7 @@
 		hostCommonOut("obj/PACKAGING"),
 		productOut("*.img"),
 		productOut("*.zip"),
-		productOut("*.zip.sha256sum"),
+		productOut("*.zip.md5sum"),
 		productOut("android-info.txt"),
 		productOut("misc_info.txt"),
 		productOut("apex"),
diff --git a/ui/build/config.go b/ui/build/config.go
index feded1c..851a22a 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -41,6 +41,7 @@
 const (
 	envConfigDir = "vendor/google/tools/soong_config"
 	jsonSuffix   = "json"
+	abfsSrcDir   = "/src"
 )
 
 var (
@@ -53,6 +54,16 @@
 	rbeRandPrefix = rand.Intn(1000)
 }
 
+// Which builder are we using?
+type ninjaCommandType = int
+
+const (
+	_ = iota
+	NINJA_NINJA
+	NINJA_N2
+	NINJA_SISO
+)
+
 type Config struct{ *configImpl }
 
 type configImpl struct {
@@ -85,6 +96,7 @@
 	skipMetricsUpload        bool
 	buildStartedTime         int64 // For metrics-upload-only - manually specify a build-started time
 	buildFromSourceStub      bool
+	incrementalBuildActions  bool
 	ensureAllowlistIntegrity bool // For CI builds - make sure modules are mixed-built
 
 	// From the product config
@@ -98,9 +110,10 @@
 	// Autodetected
 	totalRAM uint64
 
-	brokenDupRules     bool
-	brokenUsesNetwork  bool
-	brokenNinjaEnvVars []string
+	brokenDupRules       bool
+	brokenUsesNetwork    bool
+	brokenNinjaEnvVars   []string
+	brokenMissingOutputs bool
 
 	pathReplaced bool
 
@@ -119,6 +132,9 @@
 	// There's quite a bit of overlap with module-info.json and soong module graph. We
 	// could consider merging them.
 	moduleDebugFile string
+
+	// Which builder are we using
+	ninjaCommand ninjaCommandType
 }
 
 type NinjaWeightListSource uint
@@ -208,6 +224,10 @@
 		sandboxConfig:         &SandboxConfig{},
 		ninjaWeightListSource: DEFAULT,
 	}
+	wd, err := os.Getwd()
+	if err != nil {
+		ctx.Fatalln("Failed to get working directory:", err)
+	}
 
 	// Skip soong tests by default on Linux
 	if runtime.GOOS == "linux" {
@@ -239,17 +259,13 @@
 
 	// Make sure OUT_DIR is set appropriately
 	if outDir, ok := ret.environ.Get("OUT_DIR"); ok {
-		ret.environ.Set("OUT_DIR", filepath.Clean(outDir))
+		ret.environ.Set("OUT_DIR", ret.sandboxPath(wd, filepath.Clean(outDir)))
 	} else {
 		outDir := "out"
 		if baseDir, ok := ret.environ.Get("OUT_DIR_COMMON_BASE"); ok {
-			if wd, err := os.Getwd(); err != nil {
-				ctx.Fatalln("Failed to get working directory:", err)
-			} else {
-				outDir = filepath.Join(baseDir, filepath.Base(wd))
-			}
+			outDir = filepath.Join(baseDir, filepath.Base(wd))
 		}
-		ret.environ.Set("OUT_DIR", outDir)
+		ret.environ.Set("OUT_DIR", ret.sandboxPath(wd, outDir))
 	}
 
 	// loadEnvConfig needs to know what the OUT_DIR is, so it should
@@ -281,6 +297,18 @@
 		ret.moduleDebugFile, _ = filepath.Abs(shared.JoinPath(ret.SoongOutDir(), "soong-debug-info.json"))
 	}
 
+	ret.ninjaCommand = NINJA_NINJA
+	switch os.Getenv("SOONG_NINJA") {
+	case "n2":
+		ret.ninjaCommand = NINJA_N2
+	case "siso":
+		ret.ninjaCommand = NINJA_SISO
+	default:
+		if os.Getenv("SOONG_USE_N2") == "true" {
+			ret.ninjaCommand = NINJA_N2
+		}
+	}
+
 	ret.environ.Unset(
 		// We're already using it
 		"USE_SOONG_UI",
@@ -311,6 +339,7 @@
 		"DISPLAY",
 		"GREP_OPTIONS",
 		"JAVAC",
+		"LEX",
 		"NDK_ROOT",
 		"POSIXLY_CORRECT",
 
@@ -336,6 +365,10 @@
 
 		// We read it here already, don't let others share in the fun
 		"GENERATE_SOONG_DEBUG",
+
+		// Use config.ninjaCommand instead.
+		"SOONG_NINJA",
+		"SOONG_USE_N2",
 	)
 
 	if ret.UseGoma() || ret.ForceUseGoma() {
@@ -347,12 +380,12 @@
 	ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1")
 
 	tmpDir := absPath(ctx, ret.TempDir())
-	ret.environ.Set("TMPDIR", tmpDir)
+	ret.environ.Set("TMPDIR", ret.sandboxPath(wd, tmpDir))
 
 	// Always set ASAN_SYMBOLIZER_PATH so that ASAN-based tools can symbolize any crashes
 	symbolizerPath := filepath.Join("prebuilts/clang/host", ret.HostPrebuiltTag(),
 		"llvm-binutils-stable/llvm-symbolizer")
-	ret.environ.Set("ASAN_SYMBOLIZER_PATH", absPath(ctx, symbolizerPath))
+	ret.environ.Set("ASAN_SYMBOLIZER_PATH", ret.sandboxPath(wd, absPath(ctx, symbolizerPath)))
 
 	// Precondition: the current directory is the top of the source tree
 	checkTopDir(ctx)
@@ -412,9 +445,9 @@
 	}
 
 	ret.environ.Unset("OVERRIDE_ANDROID_JAVA_HOME")
-	ret.environ.Set("JAVA_HOME", absJavaHome)
-	ret.environ.Set("ANDROID_JAVA_HOME", javaHome)
-	ret.environ.Set("ANDROID_JAVA8_HOME", java8Home)
+	ret.environ.Set("JAVA_HOME", ret.sandboxPath(wd, absJavaHome))
+	ret.environ.Set("ANDROID_JAVA_HOME", ret.sandboxPath(wd, javaHome))
+	ret.environ.Set("ANDROID_JAVA8_HOME", ret.sandboxPath(wd, java8Home))
 	ret.environ.Set("PATH", strings.Join(newPath, string(filepath.ListSeparator)))
 
 	// b/286885495, https://bugzilla.redhat.com/show_bug.cgi?id=2227130: some versions of Fedora include patches
@@ -430,7 +463,7 @@
 		ret.buildDateTime = strconv.FormatInt(time.Now().Unix(), 10)
 	}
 
-	ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile)
+	ret.environ.Set("BUILD_DATETIME_FILE", ret.sandboxPath(wd, buildDateTimeFile))
 
 	if _, ok := ret.environ.Get("BUILD_USERNAME"); !ok {
 		username := "unknown"
@@ -441,6 +474,7 @@
 		}
 		ret.environ.Set("BUILD_USERNAME", username)
 	}
+	ret.environ.Set("PWD", ret.sandboxPath(wd, wd))
 
 	if ret.UseRBE() {
 		for k, v := range getRBEVars(ctx, Config{ret}) {
@@ -811,6 +845,8 @@
 			}
 		} else if arg == "--build-from-source-stub" {
 			c.buildFromSourceStub = true
+		} else if arg == "--incremental-build-actions" {
+			c.incrementalBuildActions = true
 		} else if strings.HasPrefix(arg, "--build-command=") {
 			buildCmd := strings.TrimPrefix(arg, "--build-command=")
 			// remove quotations
@@ -1019,13 +1055,9 @@
 	}
 }
 
-func (c *configImpl) NamedGlobFile(name string) string {
-	return shared.JoinPath(c.SoongOutDir(), "globs-"+name+".ninja")
-}
-
 func (c *configImpl) UsedEnvFile(tag string) string {
 	if v, ok := c.environ.Get("TARGET_PRODUCT"); ok {
-		return shared.JoinPath(c.SoongOutDir(), usedEnvFile+"."+v+"."+tag)
+		return shared.JoinPath(c.SoongOutDir(), usedEnvFile+"."+v+c.CoverageSuffix()+"."+tag)
 	}
 	return shared.JoinPath(c.SoongOutDir(), usedEnvFile+"."+tag)
 }
@@ -1133,6 +1165,13 @@
 	return "", fmt.Errorf("TARGET_PRODUCT is not defined")
 }
 
+func (c *configImpl) CoverageSuffix() string {
+	if v := c.environ.IsEnvTrue("EMMA_INSTRUMENT"); v {
+		return ".coverage"
+	}
+	return ""
+}
+
 func (c *configImpl) TargetDevice() string {
 	return c.targetDevice
 }
@@ -1243,15 +1282,49 @@
 }
 
 func (c *configImpl) canSupportRBE() bool {
+	// Only supported on linux
+	if runtime.GOOS != "linux" {
+		return false
+	}
+
 	// Do not use RBE with prod credentials in scenarios when stubby doesn't exist, since
 	// its unlikely that we will be able to obtain necessary creds without stubby.
 	authType, _ := c.rbeAuth()
 	if !c.StubbyExists() && strings.Contains(authType, "use_google_prod_creds") {
 		return false
 	}
+	if c.UseABFS() {
+		return false
+	}
 	return true
 }
 
+func (c *configImpl) UseABFS() bool {
+	if v, ok := c.environ.Get("NO_ABFS"); ok {
+		v = strings.ToLower(strings.TrimSpace(v))
+		if v == "true" || v == "1" {
+			return false
+		}
+	}
+
+	abfsBox := c.PrebuiltBuildTool("abfsbox")
+	err := exec.Command(abfsBox, "hash", srcDirFileCheck).Run()
+	return err == nil
+}
+
+func (c *configImpl) sandboxPath(base, in string) string {
+	if !c.UseABFS() {
+		return in
+	}
+
+	rel, err := filepath.Rel(base, in)
+	if err != nil {
+		return in
+	}
+
+	return filepath.Join(abfsSrcDir, rel)
+}
+
 func (c *configImpl) UseRBE() bool {
 	// These alternate modes of running Soong do not use RBE / reclient.
 	if c.Queryview() || c.JsonModuleGraph() {
@@ -1304,8 +1377,7 @@
 }
 
 func (c *configImpl) rbeTmpDir() string {
-	buildTmpDir := shared.TempDirForOutDir(c.SoongOutDir())
-	return filepath.Join(buildTmpDir, "rbe")
+	return filepath.Join(c.SoongOutDir(), "rbe")
 }
 
 func (c *configImpl) rbeCacheDir() string {
@@ -1321,8 +1393,10 @@
 	// Perform a log directory cleanup only when the log directory
 	// is auto created by the build rather than user-specified.
 	for _, f := range []string{"RBE_proxy_log_dir", "FLAG_output_dir"} {
-		if _, ok := c.environ.Get(f); ok {
-			return false
+		if v, ok := c.environ.Get(f); ok {
+			if v != c.rbeTmpDir() {
+				return false
+			}
 		}
 	}
 	return true
@@ -1484,7 +1558,16 @@
 	if err != nil {
 		return filepath.Join(c.SoongOutDir(), "soong.variables")
 	} else {
-		return filepath.Join(c.SoongOutDir(), "soong."+targetProduct+".variables")
+		return filepath.Join(c.SoongOutDir(), "soong."+targetProduct+c.CoverageSuffix()+".variables")
+	}
+}
+
+func (c *configImpl) SoongExtraVarsFile() string {
+	targetProduct, err := c.TargetProductOrErr()
+	if err != nil {
+		return filepath.Join(c.SoongOutDir(), "soong.extra.variables")
+	} else {
+		return filepath.Join(c.SoongOutDir(), "soong."+targetProduct+c.CoverageSuffix()+".extra.variables")
 	}
 }
 
@@ -1493,7 +1576,7 @@
 	if err != nil {
 		return filepath.Join(c.SoongOutDir(), "build.ninja")
 	} else {
-		return filepath.Join(c.SoongOutDir(), "build."+targetProduct+".ninja")
+		return filepath.Join(c.SoongOutDir(), "build."+targetProduct+c.CoverageSuffix()+".ninja")
 	}
 }
 
@@ -1505,11 +1588,11 @@
 }
 
 func (c *configImpl) SoongAndroidMk() string {
-	return filepath.Join(c.SoongOutDir(), "Android-"+c.TargetProduct()+".mk")
+	return filepath.Join(c.SoongOutDir(), "Android-"+c.TargetProduct()+c.CoverageSuffix()+".mk")
 }
 
 func (c *configImpl) SoongMakeVarsMk() string {
-	return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk")
+	return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+c.CoverageSuffix()+".mk")
 }
 
 func (c *configImpl) SoongBuildMetrics() string {
@@ -1555,6 +1638,35 @@
 	}
 }
 
+func (c *configImpl) KatiBin() string {
+	binName := "ckati"
+	if c.UseABFS() {
+		binName = "ckati-wrap"
+	}
+
+	return c.PrebuiltBuildTool(binName)
+}
+
+func (c *configImpl) NinjaBin() string {
+	binName := "ninja"
+	if c.UseABFS() {
+		binName = "ninjago"
+	}
+	return c.PrebuiltBuildTool(binName)
+}
+
+func (c *configImpl) N2Bin() string {
+	path := c.PrebuiltBuildTool("n2")
+	// Use musl instead of glibc because glibc on the build server is old and has bugs
+	return strings.ReplaceAll(path, "/linux-x86/", "/linux_musl-x86/")
+}
+
+func (c *configImpl) SisoBin() string {
+	path := c.PrebuiltBuildTool("siso")
+	// Use musl instead of glibc because glibc on the build server is old and has bugs
+	return strings.ReplaceAll(path, "/linux-x86/", "/linux_musl-x86/")
+}
+
 func (c *configImpl) PrebuiltBuildTool(name string) string {
 	if v, ok := c.environ.Get("SANITIZE_HOST"); ok {
 		if sanitize := strings.Fields(v); inList("address", sanitize) {
@@ -1591,6 +1703,14 @@
 	return c.brokenNinjaEnvVars
 }
 
+func (c *configImpl) SetBuildBrokenMissingOutputs(val bool) {
+	c.brokenMissingOutputs = val
+}
+
+func (c *configImpl) BuildBrokenMissingOutputs() bool {
+	return c.brokenMissingOutputs
+}
+
 func (c *configImpl) SetTargetDeviceDir(dir string) {
 	c.targetDeviceDir = dir
 }
@@ -1639,6 +1759,11 @@
 }
 
 func (c *configImpl) SkipMetricsUpload() bool {
+	// b/362625275 - Metrics upload sometimes prevents abfs unmount
+	if c.UseABFS() {
+		return true
+	}
+
 	return c.skipMetricsUpload
 }
 
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index b1222fe..b42edb0 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -22,6 +22,7 @@
 	"os"
 	"path/filepath"
 	"reflect"
+	"runtime"
 	"strings"
 	"testing"
 
@@ -1043,12 +1044,13 @@
 			},
 		},
 		{
+			// RBE is only supported on linux.
 			name:    "use rbe",
 			environ: Environment{"USE_RBE=1"},
 			expectedBuildConfig: &smpb.BuildConfig{
 				ForceUseGoma:          proto.Bool(false),
 				UseGoma:               proto.Bool(false),
-				UseRbe:                proto.Bool(true),
+				UseRbe:                proto.Bool(runtime.GOOS == "linux"),
 				NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
 			},
 		},
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 3d43786..06b1185 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -93,7 +93,7 @@
 	defer tool.Finish()
 
 	cmd := Command(ctx, config, "dumpvars",
-		config.PrebuiltBuildTool("ckati"),
+		config.KatiBin(),
 		"-f", "build/make/core/config.mk",
 		"--color_warnings",
 		"--kati_stats",
@@ -236,6 +236,11 @@
 		"BUILD_BROKEN_SRC_DIR_IS_WRITABLE",
 		"BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST",
 
+		// Whether missing outputs should be treated as warnings
+		// instead of errors.
+		// `true` will relegate missing outputs to warnings.
+		"BUILD_BROKEN_MISSING_OUTPUTS",
+
 		// Not used, but useful to be in the soong.log
 		"TARGET_BUILD_TYPE",
 		"HOST_ARCH",
@@ -302,4 +307,5 @@
 	config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true")
 	config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(makeVars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
 	config.SetSourceRootDirs(strings.Fields(makeVars["PRODUCT_SOURCE_ROOT_DIRS"]))
+	config.SetBuildBrokenMissingOutputs(makeVars["BUILD_BROKEN_MISSING_OUTPUTS"] == "true")
 }
diff --git a/ui/build/kati.go b/ui/build/kati.go
index d599c99..5743ff7 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -41,7 +41,7 @@
 // arguments.
 func genKatiSuffix(ctx Context, config Config) {
 	// Construct the base suffix.
-	katiSuffix := "-" + config.TargetProduct()
+	katiSuffix := "-" + config.TargetProduct() + config.CoverageSuffix()
 
 	// Append kati arguments to the suffix.
 	if args := config.KatiArgs(); len(args) > 0 {
@@ -84,7 +84,7 @@
 // arguments, and a custom function closure to mutate the environment Kati runs
 // in.
 func runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) {
-	executable := config.PrebuiltBuildTool("ckati")
+	executable := config.KatiBin()
 	// cKati arguments.
 	args = append([]string{
 		// Instead of executing commands directly, generate a Ninja file.
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 551b8ab..def0783 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -49,14 +49,44 @@
 	nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
 	defer nr.Close()
 
-	executable := config.PrebuiltBuildTool("ninja")
-	args := []string{
-		"-d", "keepdepfile",
-		"-d", "keeprsp",
-		"-d", "stats",
-		"--frontend_file", fifo,
+	var executable string
+	var args []string
+	switch config.ninjaCommand {
+	case NINJA_N2:
+		executable = config.N2Bin()
+		args = []string{
+			"-d", "trace",
+			// TODO: implement these features, or remove them.
+			//"-d", "keepdepfile",
+			//"-d", "keeprsp",
+			//"-d", "stats",
+			"--frontend-file", fifo,
+		}
+	case NINJA_SISO:
+		executable = config.SisoBin()
+		args = []string{
+			"ninja",
+			"--log_dir", config.SoongOutDir(),
+			// TODO: implement these features, or remove them.
+			//"-d", "trace",
+			//"-d", "keepdepfile",
+			//"-d", "keeprsp",
+			//"-d", "stats",
+			//"--frontend-file", fifo,
+		}
+	default:
+		// NINJA_NINJA is the default.
+		executable = config.NinjaBin()
+		args = []string{
+			"-d", "keepdepfile",
+			"-d", "keeprsp",
+			"-d", "stats",
+			"--frontend_file", fifo,
+			"-o", "usesphonyoutputs=yes",
+			"-w", "dupbuild=err",
+			"-w", "missingdepfile=err",
+		}
 	}
-
 	args = append(args, config.NinjaArgs()...)
 
 	var parallel int
@@ -72,10 +102,15 @@
 
 	args = append(args, "-f", config.CombinedNinjaFile())
 
-	args = append(args,
-		"-o", "usesphonyoutputs=yes",
-		"-w", "dupbuild=err",
-		"-w", "missingdepfile=err")
+	if !config.BuildBrokenMissingOutputs() {
+		// Missing outputs will be treated as errors.
+		// BUILD_BROKEN_MISSING_OUTPUTS can be used to bypass this check.
+		if config.ninjaCommand != NINJA_N2 {
+			args = append(args,
+				"-w", "missingoutfile=err",
+			)
+		}
+	}
 
 	cmd := Command(ctx, config, "ninja", executable, args...)
 
@@ -87,18 +122,21 @@
 		cmd.Environment.AppendFromKati(config.KatiEnvFile())
 	}
 
-	switch config.NinjaWeightListSource() {
-	case NINJA_LOG:
-		cmd.Args = append(cmd.Args, "-o", "usesninjalogasweightlist=yes")
-	case EVENLY_DISTRIBUTED:
-		// pass empty weight list means ninja considers every tasks's weight as 1(default value).
-		cmd.Args = append(cmd.Args, "-o", "usesweightlist=/dev/null")
-	case EXTERNAL_FILE:
-		fallthrough
-	case HINT_FROM_SOONG:
-		// The weight list is already copied/generated.
-		ninjaWeightListPath := filepath.Join(config.OutDir(), ninjaWeightListFileName)
-		cmd.Args = append(cmd.Args, "-o", "usesweightlist="+ninjaWeightListPath)
+	// TODO(b/346806126): implement this for the other ninjaCommand values.
+	if config.ninjaCommand == NINJA_NINJA {
+		switch config.NinjaWeightListSource() {
+		case NINJA_LOG:
+			cmd.Args = append(cmd.Args, "-o", "usesninjalogasweightlist=yes")
+		case EVENLY_DISTRIBUTED:
+			// pass empty weight list means ninja considers every tasks's weight as 1(default value).
+			cmd.Args = append(cmd.Args, "-o", "usesweightlist=/dev/null")
+		case EXTERNAL_FILE:
+			fallthrough
+		case HINT_FROM_SOONG:
+			// The weight list is already copied/generated.
+			ninjaWeightListPath := filepath.Join(config.OutDir(), ninjaWeightListFileName)
+			cmd.Args = append(cmd.Args, "-o", "usesweightlist="+ninjaWeightListPath)
+		}
 	}
 
 	// Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been
@@ -198,11 +236,22 @@
 			// We don't want this build broken flag to cause reanalysis, so allow it through to the
 			// actions.
 			"BUILD_BROKEN_INCORRECT_PARTITION_IMAGES",
+			// Do not do reanalysis just because we changed ninja commands.
+			"SOONG_NINJA",
+			"SOONG_USE_N2",
+			"RUST_BACKTRACE",
+			"RUST_LOG",
 		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
 	}
 
 	cmd.Environment.Set("DIST_DIR", config.DistDir())
 	cmd.Environment.Set("SHELL", "/bin/bash")
+	switch config.ninjaCommand {
+	case NINJA_N2:
+		cmd.Environment.Set("RUST_BACKTRACE", "1")
+	default:
+		// Only set RUST_BACKTRACE for n2.
+	}
 
 	// Print the environment variables that Ninja is operating in.
 	ctx.Verboseln("Ninja environment: ")
diff --git a/ui/build/path.go b/ui/build/path.go
index 51ebff1..cc1d7e9 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -57,6 +57,22 @@
 	return ret
 }
 
+func updatePathForSandbox(config Config) {
+	wd, err := os.Getwd()
+	if err != nil {
+		return
+	}
+
+	var newPath []string
+	if path, ok := config.Environment().Get("PATH"); ok && path != "" {
+		entries := strings.Split(path, string(filepath.ListSeparator))
+		for _, ent := range entries {
+			newPath = append(newPath, config.sandboxPath(wd, ent))
+		}
+	}
+	config.Environment().Set("PATH", strings.Join(newPath, string(filepath.ListSeparator)))
+}
+
 // SetupLitePath is the "lite" version of SetupPath used for dumpvars, or other
 // places that does not need the full logging capabilities of path_interposer,
 // wants the minimal performance overhead, and still get the benefits of $PATH
@@ -109,18 +125,10 @@
 	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
 	myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
 
-	if value, _ := config.Environment().Get("BUILD_BROKEN_PYTHON_IS_PYTHON2"); value == "true" {
-		py2Path, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86/py2")
-		if info, err := os.Stat(py2Path); err == nil && info.IsDir() {
-			myPath = py2Path + string(os.PathListSeparator) + myPath
-		}
-	} else if value != "" {
-		ctx.Fatalf("BUILD_BROKEN_PYTHON_IS_PYTHON2 can only be set to 'true' or an empty string, but got %s\n", value)
-	}
-
 	// Set $PATH to be the directories containing the host tool symlinks, and
 	// the prebuilts directory for the current host OS.
 	config.Environment().Set("PATH", myPath)
+	updatePathForSandbox(config)
 	config.pathReplaced = true
 }
 
@@ -253,17 +261,9 @@
 	prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
 	myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
 
-	if value, _ := config.Environment().Get("BUILD_BROKEN_PYTHON_IS_PYTHON2"); value == "true" {
-		py2Path, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86/py2")
-		if info, err := os.Stat(py2Path); err == nil && info.IsDir() {
-			myPath = py2Path + string(os.PathListSeparator) + myPath
-		}
-	} else if value != "" {
-		ctx.Fatalf("BUILD_BROKEN_PYTHON_IS_PYTHON2 can only be set to 'true' or an empty string, but got %s\n", value)
-	}
-
 	// Replace the $PATH variable with the path_interposer symlinks, and
 	// checked-in prebuilts.
 	config.Environment().Set("PATH", myPath)
+	updatePathForSandbox(config)
 	config.pathReplaced = true
 }
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 6c25680..a532e6d 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -86,28 +86,28 @@
 // This list specifies whether a particular binary from $PATH is allowed to be
 // run during the build. For more documentation, see path_interposer.go .
 var Configuration = map[string]PathConfig{
-	"bash":           Allowed,
-	"diff":           Allowed,
-	"dlv":            Allowed,
-	"expr":           Allowed,
-	"fuser":          Allowed,
-	"gcert":          Allowed,
-	"gcertstatus":    Allowed,
-	"gcloud":         Allowed,
-	"git":            Allowed,
-	"hexdump":        Allowed,
-	"jar":            Allowed,
-	"java":           Allowed,
-	"javap":          Allowed,
-	"lsof":           Allowed,
-	"openssl":        Allowed,
-	"pstree":         Allowed,
-	"rsync":          Allowed,
-	"sh":             Allowed,
-	"stubby":         Allowed,
-	"tr":             Allowed,
-	"unzip":          Allowed,
-	"zip":            Allowed,
+	"bash":        Allowed,
+	"diff":        Allowed,
+	"dlv":         Allowed,
+	"expr":        Allowed,
+	"fuser":       Allowed,
+	"gcert":       Allowed,
+	"gcertstatus": Allowed,
+	"gcloud":      Allowed,
+	"git":         Allowed,
+	"hexdump":     Allowed,
+	"jar":         Allowed,
+	"java":        Allowed,
+	"javap":       Allowed,
+	"lsof":        Allowed,
+	"openssl":     Allowed,
+	"pstree":      Allowed,
+	"rsync":       Allowed,
+	"sh":          Allowed,
+	"stubby":      Allowed,
+	"tr":          Allowed,
+	"unzip":       Allowed,
+	"zip":         Allowed,
 	"nproc":          Allowed,
 	"perl":           Allowed,
 
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 8fa147f..0a0f956 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -65,7 +65,7 @@
 		"RBE_platform":         "container-image=" + remoteexec.DefaultImage,
 	}
 	if config.StartRBE() {
-		name, err := config.rbeSockAddr(absPath(ctx, config.TempDir()))
+		name, err := config.rbeSockAddr(absPath(ctx, config.rbeTmpDir()))
 		if err != nil {
 			ctx.Fatalf("Error retrieving socket address: %v", err)
 			return nil
diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go
index 266f76b..d1b8e26 100644
--- a/ui/build/rbe_test.go
+++ b/ui/build/rbe_test.go
@@ -19,6 +19,7 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"runtime"
 	"strings"
 	"testing"
 
@@ -26,6 +27,10 @@
 )
 
 func TestDumpRBEMetrics(t *testing.T) {
+	// RBE is only supported on linux.
+	if runtime.GOOS != "linux" {
+		t.Skip("RBE is only supported on linux")
+	}
 	ctx := testContext()
 	tests := []struct {
 		description string
@@ -82,6 +87,10 @@
 }
 
 func TestDumpRBEMetricsErrors(t *testing.T) {
+	// RBE is only supported on linux.
+	if runtime.GOOS != "linux" {
+		t.Skip("RBE is only supported on linux")
+	}
 	ctx := testContext()
 	tests := []struct {
 		description      string
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index edb3b66..95b71a7 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -48,7 +48,9 @@
 	}
 )
 
-const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail"
+const (
+	nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail"
+)
 
 var sandboxConfig struct {
 	once sync.Once
@@ -145,10 +147,81 @@
 	return sandboxConfig.working
 }
 
-func (c *Cmd) wrapSandbox() {
-	wd, _ := os.Getwd()
+// Assumes input path is absolute, clean, and if applicable, an evaluated
+// symlink. If path is not a subdirectory of src dir or relative path
+// cannot be determined, return the input untouched.
+func (c *Cmd) relFromSrcDir(path string) string {
+	if !strings.HasPrefix(path, sandboxConfig.srcDir) {
+		return path
+	}
 
-	sandboxArgs := []string{
+	rel, err := filepath.Rel(sandboxConfig.srcDir, path)
+	if err != nil {
+		return path
+	}
+
+	return rel
+}
+
+func (c *Cmd) dirArg(path string) string {
+	if !c.config.UseABFS() {
+		return path
+	}
+
+	rel := c.relFromSrcDir(path)
+
+	return path + ":" + filepath.Join(abfsSrcDir, rel)
+}
+
+func (c *Cmd) srcDirArg() string {
+	return c.dirArg(sandboxConfig.srcDir)
+}
+
+func (c *Cmd) outDirArg() string {
+	return c.dirArg(sandboxConfig.outDir)
+}
+
+func (c *Cmd) distDirArg() string {
+	return c.dirArg(sandboxConfig.distDir)
+}
+
+// When configured to use ABFS, we need to allow the creation of the /src
+// directory. Therefore, we cannot mount the root "/" directory as read-only.
+// Instead, we individually mount the children of "/" as RO.
+func (c *Cmd) readMountArgs() []string {
+	if !c.config.UseABFS() {
+		// For now, just map everything. Make most things readonly.
+		return []string{"-R", "/"}
+	}
+
+	entries, err := os.ReadDir("/")
+	if err != nil {
+		// If we can't read "/", just use the default non-ABFS behavior.
+		return []string{"-R", "/"}
+	}
+
+	args := make([]string, 0, 2*len(entries))
+	for _, ent := range entries {
+		args = append(args, "-R", "/"+ent.Name())
+	}
+
+	return args
+}
+
+func (c *Cmd) workDir() string {
+	if !c.config.UseABFS() {
+		wd, _ := os.Getwd()
+		return wd
+	}
+
+	return abfsSrcDir
+}
+
+func (c *Cmd) wrapSandbox() {
+	wd := c.workDir()
+
+	var sandboxArgs []string
+	sandboxArgs = append(sandboxArgs,
 		// The executable to run
 		"-x", c.Path,
 
@@ -180,18 +253,21 @@
 		"--rlimit_cpu", "soft",
 		"--rlimit_fsize", "soft",
 		"--rlimit_nofile", "soft",
+	)
 
-		// For now, just map everything. Make most things readonly.
-		"-R", "/",
+	sandboxArgs = append(sandboxArgs,
+		c.readMountArgs()...,
+	)
 
+	sandboxArgs = append(sandboxArgs,
 		// Mount a writable tmp dir
 		"-B", "/tmp",
 
 		// Mount source
-		c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
+		c.config.sandboxConfig.SrcDirMountFlag(), c.srcDirArg(),
 
 		//Mount out dir as read-write
-		"-B", sandboxConfig.outDir,
+		"-B", c.outDirArg(),
 
 		// Disable newcgroup for now, since it may require newer kernels
 		// TODO: try out cgroups
@@ -199,6 +275,9 @@
 
 		// Only log important warnings / errors
 		"-q",
+	)
+	if c.config.UseABFS() {
+		sandboxArgs = append(sandboxArgs, "-B", "{ABFS_DIR}")
 	}
 
 	// Mount srcDir RW allowlists as Read-Write
@@ -215,7 +294,7 @@
 
 	if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
 		//Mount dist dir as read-write if it already exists
-		sandboxArgs = append(sandboxArgs, "-B", sandboxConfig.distDir)
+		sandboxArgs = append(sandboxArgs, "-B", c.distDirArg())
 	}
 
 	if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 2f3150d..41425ac 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,19 +15,23 @@
 package build
 
 import (
+	"encoding/json"
+	"errors"
 	"fmt"
 	"io/fs"
 	"os"
 	"path/filepath"
+	"runtime"
+	"slices"
 	"strconv"
 	"strings"
 	"sync"
 	"sync/atomic"
+	"syscall"
 	"time"
 
 	"android/soong/ui/tracer"
 
-	"android/soong/bazel"
 	"android/soong/ui/metrics"
 	"android/soong/ui/metrics/metrics_proto"
 	"android/soong/ui/status"
@@ -53,7 +57,7 @@
 
 	// bootstrapEpoch is used to determine if an incremental build is incompatible with the current
 	// version of bootstrap and needs cleaning before continuing the build.  Increment this for
-	// incompatible changes, for example when moving the location of the bpglob binary that is
+	// incompatible changes, for example when moving the location of a microfactory binary that is
 	// executed during bootstrap before the primary builder has had a chance to update the path.
 	bootstrapEpoch = 1
 )
@@ -227,10 +231,6 @@
 
 	var allArgs []string
 	allArgs = append(allArgs, pb.specificArgs...)
-	globPathName := getGlobPathNameFromPrimaryBuilderFactory(config, pb)
-	allArgs = append(allArgs,
-		"--globListDir", globPathName,
-		"--globFile", pb.config.NamedGlobFile(globPathName))
 
 	allArgs = append(allArgs, commonArgs...)
 	allArgs = append(allArgs, environmentArgs(pb.config, pb.name)...)
@@ -242,11 +242,8 @@
 	}
 	allArgs = append(allArgs, "Android.bp")
 
-	globfiles := bootstrap.GlobFileListFiles(bootstrap.GlobDirectory(config.SoongOutDir(), globPathName))
-
 	return bootstrap.PrimaryBuilderInvocation{
-		Inputs:      []string{"Android.bp"},
-		Implicits:   globfiles,
+		Implicits:   []string{pb.output + ".glob_results"},
 		Outputs:     []string{pb.output},
 		Args:        allArgs,
 		Description: pb.description,
@@ -278,24 +275,15 @@
 				os.Remove(file)
 			}
 		}
-		for _, globFile := range bootstrapGlobFileList(config) {
-			os.Remove(globFile)
-		}
+		os.Remove(soongNinjaFile + ".globs")
+		os.Remove(soongNinjaFile + ".globs_time")
+		os.Remove(soongNinjaFile + ".glob_results")
 
 		// Mark the tree as up to date with the current epoch by writing the epoch marker file.
 		writeEmptyFile(ctx, epochPath)
 	}
 }
 
-func bootstrapGlobFileList(config Config) []string {
-	return []string{
-		config.NamedGlobFile(getGlobPathName(config)),
-		config.NamedGlobFile(jsonModuleGraphTag),
-		config.NamedGlobFile(queryviewTag),
-		config.NamedGlobFile(soongDocsTag),
-	}
-}
-
 func bootstrapBlueprint(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
 	defer ctx.EndTrace()
@@ -315,6 +303,9 @@
 	if config.ensureAllowlistIntegrity {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--ensure-allowlist-integrity")
 	}
+	if config.incrementalBuildActions {
+		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--incremental-build-actions")
+	}
 
 	queryviewDir := filepath.Join(config.SoongOutDir(), "queryview")
 
@@ -410,32 +401,9 @@
 		runGoTests:  !config.skipSoongTests,
 		// If we want to debug soong_build, we need to compile it for debugging
 		debugCompilation:          delvePort != "",
-		subninjas:                 bootstrapGlobFileList(config),
 		primaryBuilderInvocations: invocations,
 	}
 
-	// The glob ninja files are generated during the main build phase. However, the
-	// primary buildifer invocation depends on all of its glob files, even before
-	// it's been run. Generate a "empty" glob ninja file on the first run,
-	// so that the files can be there to satisfy the dependency.
-	for _, pb := range pbfs {
-		globPathName := getGlobPathNameFromPrimaryBuilderFactory(config, pb)
-		globNinjaFile := config.NamedGlobFile(globPathName)
-		if _, err := os.Stat(globNinjaFile); os.IsNotExist(err) {
-			err := bootstrap.WriteBuildGlobsNinjaFile(&bootstrap.GlobSingleton{
-				GlobLister: func() pathtools.MultipleGlobResults { return nil },
-				GlobFile:   globNinjaFile,
-				GlobDir:    bootstrap.GlobDirectory(config.SoongOutDir(), globPathName),
-				SrcDir:     ".",
-			}, blueprintConfig)
-			if err != nil {
-				ctx.Fatal(err)
-			}
-		} else if err != nil {
-			ctx.Fatal(err)
-		}
-	}
-
 	// 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)
@@ -600,10 +568,6 @@
 
 		checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(soongBuildTag))
 
-		// Remove bazel files in the event that bazel is disabled for the build.
-		// These files may have been left over from a previous bazel-enabled build.
-		cleanBazelFiles(config)
-
 		if config.JsonModuleGraph() {
 			checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(jsonModuleGraphTag))
 		}
@@ -617,9 +581,6 @@
 		}
 	}()
 
-	runMicrofactory(ctx, config, "bpglob", "github.com/google/blueprint/bootstrap/bpglob",
-		map[string]string{"github.com/google/blueprint": "build/blueprint"})
-
 	ninja := func(targets ...string) {
 		ctx.BeginTrace(metrics.RunSoong, "bootstrap")
 		defer ctx.EndTrace()
@@ -628,17 +589,58 @@
 		nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
 		defer nr.Close()
 
-		ninjaArgs := []string{
-			"-d", "keepdepfile",
-			"-d", "stats",
-			"-o", "usesphonyoutputs=yes",
-			"-o", "preremoveoutputs=yes",
-			"-w", "dupbuild=err",
-			"-w", "outputdir=err",
-			"-w", "missingoutfile=err",
-			"-j", strconv.Itoa(config.Parallel()),
-			"--frontend_file", fifo,
-			"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
+		var ninjaCmd string
+		var ninjaArgs []string
+		switch config.ninjaCommand {
+		case NINJA_N2:
+			ninjaCmd = config.N2Bin()
+			ninjaArgs = []string{
+				// TODO: implement these features, or remove them.
+				//"-d", "keepdepfile",
+				//"-d", "stats",
+				//"-o", "usesphonyoutputs=yes",
+				//"-o", "preremoveoutputs=yes",
+				//"-w", "dupbuild=err",
+				//"-w", "outputdir=err",
+				//"-w", "missingoutfile=err",
+				"-v",
+				"-j", strconv.Itoa(config.Parallel()),
+				"--frontend-file", fifo,
+				"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
+			}
+		case NINJA_SISO:
+			ninjaCmd = config.SisoBin()
+			ninjaArgs = []string{
+				"ninja",
+				// TODO: implement these features, or remove them.
+				//"-d", "keepdepfile",
+				//"-d", "stats",
+				//"-o", "usesphonyoutputs=yes",
+				//"-o", "preremoveoutputs=yes",
+				//"-w", "dupbuild=err",
+				//"-w", "outputdir=err",
+				//"-w", "missingoutfile=err",
+				"-v",
+				"-j", strconv.Itoa(config.Parallel()),
+				//"--frontend-file", fifo,
+				"--log_dir", config.SoongOutDir(),
+				"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
+			}
+		default:
+			// NINJA_NINJA is the default.
+			ninjaCmd = config.NinjaBin()
+			ninjaArgs = []string{
+				"-d", "keepdepfile",
+				"-d", "stats",
+				"-o", "usesphonyoutputs=yes",
+				"-o", "preremoveoutputs=yes",
+				"-w", "dupbuild=err",
+				"-w", "outputdir=err",
+				"-w", "missingoutfile=err",
+				"-j", strconv.Itoa(config.Parallel()),
+				"--frontend_file", fifo,
+				"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
+			}
 		}
 
 		if extra, ok := config.Environment().Get("SOONG_UI_NINJA_ARGS"); ok {
@@ -647,8 +649,9 @@
 		}
 
 		ninjaArgs = append(ninjaArgs, targets...)
+
 		cmd := Command(ctx, config, "soong bootstrap",
-			config.PrebuiltBuildTool("ninja"), ninjaArgs...)
+			ninjaCmd, ninjaArgs...)
 
 		var ninjaEnv Environment
 
@@ -680,6 +683,12 @@
 		targets = append(targets, config.SoongNinjaFile())
 	}
 
+	for _, target := range targets {
+		if err := checkGlobs(ctx, target); err != nil {
+			ctx.Fatalf("Error checking globs: %s", err.Error())
+		}
+	}
+
 	beforeSoongTimestamp := time.Now()
 
 	ninja(targets...)
@@ -694,6 +703,7 @@
 		}
 	}
 	distFile(ctx, config, config.SoongVarsFile(), "soong")
+	distFile(ctx, config, config.SoongExtraVarsFile(), "soong")
 
 	if !config.SkipKati() {
 		distGzipFile(ctx, config, config.SoongAndroidMk(), "soong")
@@ -705,6 +715,160 @@
 	}
 }
 
+// checkGlobs manages the globs that cause soong to rerun.
+//
+// When soong_build runs, it will run globs. It will write all the globs
+// it ran into the "{finalOutFile}.globs" file. Then every build,
+// soong_ui will check that file, rerun the globs, and if they changed
+// from the results that soong_build got, update the ".glob_results"
+// file, causing soong_build to rerun. The ".glob_results" file will
+// be empty on the first run of soong_build, because we don't know
+// what the globs are yet, but also remain empty until the globs change
+// so that we don't run soong_build a second time unnecessarily.
+// Both soong_build and soong_ui will also update a ".globs_time" file
+// with the time that they ran at every build. When soong_ui checks
+// globs, it only reruns globs whose dependencies are newer than the
+// time in the ".globs_time" file.
+func checkGlobs(ctx Context, finalOutFile string) error {
+	ctx.BeginTrace(metrics.RunSoong, "check_globs")
+	defer ctx.EndTrace()
+	st := ctx.Status.StartTool()
+	st.Status("Running globs...")
+	defer st.Finish()
+
+	globsFile, err := os.Open(finalOutFile + ".globs")
+	if errors.Is(err, fs.ErrNotExist) {
+		// if the glob file doesn't exist, make sure the glob_results file exists and is empty.
+		if err := os.MkdirAll(filepath.Dir(finalOutFile), 0777); err != nil {
+			return err
+		}
+		f, err := os.Create(finalOutFile + ".glob_results")
+		if err != nil {
+			return err
+		}
+		return f.Close()
+	} else if err != nil {
+		return err
+	}
+	defer globsFile.Close()
+	globsFileDecoder := json.NewDecoder(globsFile)
+
+	globsTimeBytes, err := os.ReadFile(finalOutFile + ".globs_time")
+	if err != nil {
+		return err
+	}
+	globsTimeMicros, err := strconv.ParseInt(strings.TrimSpace(string(globsTimeBytes)), 10, 64)
+	if err != nil {
+		return err
+	}
+	globCheckStartTime := time.Now().UnixMicro()
+
+	globsChan := make(chan pathtools.GlobResult)
+	errorsChan := make(chan error)
+	wg := sync.WaitGroup{}
+	hasChangedGlobs := false
+	for i := 0; i < runtime.NumCPU()*2; i++ {
+		wg.Add(1)
+		go func() {
+			for cachedGlob := range globsChan {
+				// If we've already determined we have changed globs, just finish consuming
+				// the channel without doing any more checks.
+				if hasChangedGlobs {
+					continue
+				}
+				// First, check if any of the deps are newer than the last time globs were checked.
+				// If not, we don't need to rerun the glob.
+				hasNewDep := false
+				for _, dep := range cachedGlob.Deps {
+					info, err := os.Stat(dep)
+					if errors.Is(err, fs.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) {
+						hasNewDep = true
+						break
+					} else if err != nil {
+						errorsChan <- err
+						continue
+					}
+					if info.ModTime().UnixMicro() > globsTimeMicros {
+						hasNewDep = true
+						break
+					}
+				}
+				if !hasNewDep {
+					continue
+				}
+
+				// Then rerun the glob and check if we got the same result as before.
+				result, err := pathtools.Glob(cachedGlob.Pattern, cachedGlob.Excludes, pathtools.FollowSymlinks)
+				if err != nil {
+					errorsChan <- err
+				} else {
+					if !slices.Equal(result.Matches, cachedGlob.Matches) {
+						hasChangedGlobs = true
+					}
+				}
+			}
+			wg.Done()
+		}()
+	}
+	go func() {
+		wg.Wait()
+		close(errorsChan)
+	}()
+
+	errorsWg := sync.WaitGroup{}
+	errorsWg.Add(1)
+	var errFromGoRoutines error
+	go func() {
+		for result := range errorsChan {
+			if errFromGoRoutines == nil {
+				errFromGoRoutines = result
+			}
+		}
+		errorsWg.Done()
+	}()
+
+	var cachedGlob pathtools.GlobResult
+	for globsFileDecoder.More() {
+		if err := globsFileDecoder.Decode(&cachedGlob); err != nil {
+			return err
+		}
+		// Need to clone the GlobResult because the json decoder will
+		// reuse the same slice allocations.
+		globsChan <- cachedGlob.Clone()
+	}
+	close(globsChan)
+	errorsWg.Wait()
+	if errFromGoRoutines != nil {
+		return errFromGoRoutines
+	}
+
+	// Update the globs_time file whether or not we found changed globs,
+	// so that we don't rerun globs in the future that we just saw didn't change.
+	err = os.WriteFile(
+		finalOutFile+".globs_time",
+		[]byte(fmt.Sprintf("%d\n", globCheckStartTime)),
+		0666,
+	)
+	if err != nil {
+		return err
+	}
+
+	if hasChangedGlobs {
+		fmt.Fprintf(os.Stdout, "Globs changed, rerunning soong...\n")
+		// 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(
+			finalOutFile+".glob_results",
+			[]byte(fmt.Sprintf("%d\n", globCheckStartTime)),
+			0666,
+		)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 // loadSoongBuildMetrics reads out/soong_build_metrics.pb if it was generated by soong_build and copies the
 // events stored in it into the soong_ui trace to provide introspection into how long the different phases of
 // soong_build are taking.
@@ -754,18 +918,6 @@
 	}
 }
 
-func cleanBazelFiles(config Config) {
-	files := []string{
-		shared.JoinPath(config.SoongOutDir(), "bp2build"),
-		shared.JoinPath(config.SoongOutDir(), "workspace"),
-		shared.JoinPath(config.SoongOutDir(), bazel.SoongInjectionDirName),
-		shared.JoinPath(config.OutDir(), "bazel")}
-
-	for _, f := range files {
-		os.RemoveAll(f)
-	}
-}
-
 func runMicrofactory(ctx Context, config Config, name string, pkg string, mapping map[string]string) {
 	ctx.BeginTrace(metrics.RunSoong, name)
 	defer ctx.EndTrace()
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 24ad082..ba53119 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -15,14 +15,16 @@
 package build
 
 import (
-	"android/soong/ui/metrics"
-	"android/soong/ui/status"
 	"bufio"
 	"fmt"
 	"path/filepath"
+	"regexp"
 	"runtime"
 	"sort"
 	"strings"
+
+	"android/soong/ui/metrics"
+	"android/soong/ui/status"
 )
 
 // Checks for files in the out directory that have a rule that depends on them but no rule to
@@ -64,7 +66,8 @@
 	outDir := config.OutDir()
 	modulePathsDir := filepath.Join(outDir, ".module_paths")
 	rawFilesDir := filepath.Join(outDir, "soong", "raw")
-	variablesFilePath := filepath.Join(outDir, "soong", "soong.variables")
+	variablesFilePath := config.SoongVarsFile()
+	extraVariablesFilePath := config.SoongExtraVarsFile()
 
 	// dexpreopt.config is an input to the soong_docs action, which runs the
 	// soong_build primary builder. However, this file is created from $(shell)
@@ -76,13 +79,14 @@
 	// out/build_date.txt is considered a "source file"
 	buildDatetimeFilePath := filepath.Join(outDir, "build_date.txt")
 
-	// bpglob is built explicitly using Microfactory
-	bpglob := filepath.Join(config.SoongOutDir(), "bpglob")
-
 	// release-config files are generated from the initial lunch or Kati phase
 	// before running soong and ninja.
 	releaseConfigDir := filepath.Join(outDir, "soong", "release-config")
 
+	// out/target/product/<xxxxx>/build_fingerprint.txt is a source file created in sysprop.mk
+	// ^out/target/product/[^/]+/build_fingerprint.txt$
+	buildFingerPrintFilePattern := regexp.MustCompile("^" + filepath.Join(outDir, "target", "product") + "/[^/]+/build_fingerprint.txt$")
+
 	danglingRules := make(map[string]bool)
 
 	scanner := bufio.NewScanner(stdout)
@@ -95,10 +99,11 @@
 		if strings.HasPrefix(line, modulePathsDir) ||
 			strings.HasPrefix(line, rawFilesDir) ||
 			line == variablesFilePath ||
+			line == extraVariablesFilePath ||
 			line == dexpreoptConfigFilePath ||
 			line == buildDatetimeFilePath ||
-			line == bpglob ||
-			strings.HasPrefix(line, releaseConfigDir) {
+			strings.HasPrefix(line, releaseConfigDir) ||
+			buildFingerPrintFilePattern.MatchString(line) {
 			// Leaf node is in one of Soong's bootstrap directories, which do not have
 			// full build rules in the primary build.ninja file.
 			continue
diff --git a/ui/terminal/format.go b/ui/terminal/format.go
index 241a1dd..01f8b0d 100644
--- a/ui/terminal/format.go
+++ b/ui/terminal/format.go
@@ -23,26 +23,28 @@
 )
 
 type formatter struct {
-	format string
-	quiet  bool
-	start  time.Time
+	colorize bool
+	format   string
+	quiet    bool
+	start    time.Time
 }
 
 // newFormatter returns a formatter for formatting output to
 // the terminal in a format similar to Ninja.
 // format takes nearly all the same options as NINJA_STATUS.
 // %c is currently unsupported.
-func newFormatter(format string, quiet bool) formatter {
+func newFormatter(colorize bool, format string, quiet bool) formatter {
 	return formatter{
-		format: format,
-		quiet:  quiet,
-		start:  time.Now(),
+		colorize: colorize,
+		format:   format,
+		quiet:    quiet,
+		start:    time.Now(),
 	}
 }
 
 func (s formatter) message(level status.MsgLevel, message string) string {
 	if level >= status.ErrorLvl {
-		return fmt.Sprintf("FAILED: %s", message)
+		return fmt.Sprintf("%s %s", s.failedString(), message)
 	} else if level > status.StatusLvl {
 		return fmt.Sprintf("%s%s", level.Prefix(), message)
 	} else if level == status.StatusLvl {
@@ -127,9 +129,9 @@
 	if result.Error != nil {
 		targets := strings.Join(result.Outputs, " ")
 		if s.quiet || result.Command == "" {
-			ret = fmt.Sprintf("FAILED: %s\n%s", targets, result.Output)
+			ret = fmt.Sprintf("%s %s\n%s", s.failedString(), targets, result.Output)
 		} else {
-			ret = fmt.Sprintf("FAILED: %s\n%s\n%s", targets, result.Command, result.Output)
+			ret = fmt.Sprintf("%s %s\n%s\n%s", s.failedString(), targets, result.Command, result.Output)
 		}
 	} else if result.Output != "" {
 		ret = result.Output
@@ -141,3 +143,11 @@
 
 	return ret
 }
+
+func (s formatter) failedString() string {
+	failed := "FAILED:"
+	if s.colorize {
+		failed = ansi.red() + ansi.bold() + failed + ansi.regular()
+	}
+	return failed
+}
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index 2ad174f..92f2994 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -27,9 +27,10 @@
 // statusFormat takes nearly all the same options as NINJA_STATUS.
 // %c is currently unsupported.
 func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput {
-	formatter := newFormatter(statusFormat, quietBuild)
+	canUseSmartFormatting := !forceSimpleOutput && isSmartTerminal(w)
+	formatter := newFormatter(canUseSmartFormatting, statusFormat, quietBuild)
 
-	if !forceSimpleOutput && isSmartTerminal(w) {
+	if canUseSmartFormatting {
 		return NewSmartStatusOutput(w, formatter)
 	} else {
 		return NewSimpleStatusOutput(w, formatter, forceKeepANSI)
diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go
index 8dd1809..991eca0 100644
--- a/ui/terminal/status_test.go
+++ b/ui/terminal/status_test.go
@@ -58,7 +58,7 @@
 		{
 			name:   "action with error",
 			calls:  actionsWithError,
-			smart:  "\r\x1b[1m[  0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
+			smart:  "\r\x1b[1m[  0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
 			simple: "[ 33% 1/3] action1\n[ 66% 2/3] action2\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n[100% 3/3] action3\n",
 		},
 		{
@@ -70,7 +70,7 @@
 		{
 			name:   "messages",
 			calls:  actionsWithMessages,
-			smart:  "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\nFAILED: error\n\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
+			smart:  "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\n\x1b[31m\x1b[1mFAILED:\x1b[0m error\n\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
 			simple: "[ 50% 1/2] action1\nstatus\nprint\nFAILED: error\n[100% 2/2] action2\n",
 		},
 		{
@@ -362,7 +362,7 @@
 
 	stat.Flush()
 
-	w := "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[  0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nThere was 1 action that completed after the action that failed. See verbose.log.gz for its output.\n"
+	w := "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[  0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nThere was 1 action that completed after the action that failed. See verbose.log.gz for its output.\n"
 
 	if g := smart.String(); g != w {
 		t.Errorf("want:\n%q\ngot:\n%q", w, g)
@@ -407,7 +407,7 @@
 
 	stat.Flush()
 
-	w := "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[  0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[  0% 0/2] action3\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\r\x1b[1m[150% 3/2] action3\x1b[0m\x1b[K\nThere were 2 actions that completed after the action that failed. See verbose.log.gz for their output.\n"
+	w := "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[  0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[  0% 0/2] action3\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\r\x1b[1m[150% 3/2] action3\x1b[0m\x1b[K\nThere were 2 actions that completed after the action that failed. See verbose.log.gz for their output.\n"
 
 	if g := smart.String(); g != w {
 		t.Errorf("want:\n%q\ngot:\n%q", w, g)
@@ -445,7 +445,7 @@
 
 	stat.Flush()
 
-	w := "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[  0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nFAILED: \nOutput2\n"
+	w := "\r\x1b[1m[  0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[  0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput2\n"
 
 	if g := smart.String(); g != w {
 		t.Errorf("want:\n%q\ngot:\n%q", w, g)
diff --git a/zip/cmd/Android.bp b/zip/cmd/Android.bp
index 43bf232..16c3f69 100644
--- a/zip/cmd/Android.bp
+++ b/zip/cmd/Android.bp
@@ -24,4 +24,6 @@
     srcs: [
         "main.go",
     ],
+    // Used by genrules
+    visibility: ["//visibility:public"],
 }