Merge "Generate .build-id directory tree after every build." into main
diff --git a/.gitignore b/.gitignore
index 5d2bc0d..89de74e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@
 *.iml
 *.ipr
 *.iws
-
+*.swp
+/.vscode
diff --git a/Android.bp b/Android.bp
index b1db8e9..682711d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,6 +104,7 @@
 // Instantiate the dex_bootjars singleton module.
 dex_bootjars {
     name: "dex_bootjars",
+    no_full_install: true,
 }
 
 // Pseudo-test that's run on checkbuilds to ensure that get_clang_version can
@@ -121,12 +122,15 @@
 }
 
 // buildinfo.prop contains common properties for system/build.prop, like ro.build.version.*
+// TODO(b/322090587): merge this to gen_build_prop.py script.
 buildinfo_prop {
     name: "buildinfo.prop",
 
     // not installable because this will be included to system/build.prop
     installable: false,
 
+    product_config: ":product_config",
+
     // Currently, only microdroid can refer to buildinfo.prop
     visibility: ["//packages/modules/Virtualization/microdroid"],
 }
@@ -135,3 +139,8 @@
 all_apex_contributions {
     name: "all_apex_contributions",
 }
+
+product_config {
+    name: "product_config",
+    visibility: ["//device/google/cuttlefish/system_image"],
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 317f5c4..e8cad7d 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,3 +4,4 @@
 
 [Hook Scripts]
 do_not_use_DO_NOT_MERGE = ${REPO_ROOT}/build/soong/scripts/check_do_not_merge.sh ${PREUPLOAD_COMMIT}
+aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/README.md b/README.md
index 93260e6..ad282a5 100644
--- a/README.md
+++ b/README.md
@@ -449,6 +449,7 @@
     config_namespace: "acme",
     variables: ["board"],
     bool_variables: ["feature"],
+    list_variables: ["impl"],
     value_variables: ["width"],
     properties: ["cflags", "srcs"],
 }
@@ -460,24 +461,40 @@
 ```
 
 This example describes a new `acme_cc_defaults` module type that extends the
-`cc_defaults` module type, with three additional conditionals based on
-variables `board`, `feature` and `width`, which can affect properties `cflags`
-and `srcs`. Additionally, each conditional will contain a `conditions_default`
-property can affect `cflags` and `srcs` in the following conditions:
+`cc_defaults` module type, with four additional conditionals based on variables
+`board`, `feature`, `impl` and `width` which can affect properties `cflags` and
+`srcs`. The four types of soong variables control properties in the following
+ways.
 
-* bool variable (e.g. `feature`): the variable is unspecified or not set to a true value
+* bool variable (e.g. `feature`): Properties are applied if set to `true`.
+* list variable (e.g. `impl`): (lists of strings properties only) Properties are
+  applied for each value in the list, using `%s` substitution. For example, if
+  the property is `["%s.cpp", "%s.h"]` and the list value is `foo bar`,
+  the result is `["foo.cpp", "foo.h", "bar.cpp", "bar.h"]`.
+* value variable (e.g. `width`): (strings or lists of strings) The value are
+  directly substituted into properties using `%s`.
+* string variable (e.g. `board`): Properties are applied only if they match the
+  variable's value.
+
+Additionally, each conditional containing a `conditions_default` property can
+affect `cflags` and `srcs` in the following conditions:
+
+* bool variable (e.g. `feature`): the variable is unspecified or not set to
+  `true`
+* list variable (e.g. `impl`): the variable is unspecified
 * value variable (e.g. `width`): the variable is unspecified
-* string variable (e.g. `board`): the variable is unspecified or the variable is set to a string unused in the
-given module. For example, with `board`, if the `board`
-conditional contains the properties `soc_a` and `conditions_default`, when
-board=soc_b, the `cflags` and `srcs` values under `conditions_default` will be
-used. To specify that no properties should be amended for `soc_b`, you can set
-`soc_b: {},`.
+* string variable (e.g. `board`): the variable is unspecified or the variable is
+  set to a string unused in the given module. For example, with `board`, if the
+  `board` conditional contains the properties `soc_a` and `conditions_default`,
+  when `board` is `soc_b`, the `cflags` and `srcs` values under
+  `conditions_default` is used. To specify that no properties should be amended
+  for `soc_b`, you can set `soc_b: {},`.
 
 The values of the variables can be set from a product's `BoardConfig.mk` file:
 ```
 $(call soong_config_set,acme,board,soc_a)
 $(call soong_config_set,acme,feature,true)
+$(call soong_config_set,acme,impl,foo.cpp bar.cpp)
 $(call soong_config_set,acme,width,200)
 ```
 
@@ -519,6 +536,12 @@
                 cflags: ["-DWIDTH=DEFAULT"],
             },
         },
+        impl: {
+            srcs: ["impl/%s"],
+            conditions_default: {
+                srcs: ["impl/default.cpp"],
+            },
+        },
     },
 }
 
@@ -530,7 +553,8 @@
 ```
 
 With the `BoardConfig.mk` snippet above, `libacme_foo` would build with
-`cflags: "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200"`.
+`cflags: "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200"` and
+`srcs: ["*.cpp", "impl/foo.cpp", "impl/bar.cpp"]`.
 
 Alternatively, with `DefaultBoardConfig.mk`:
 
@@ -539,12 +563,14 @@
 SOONG_CONFIG_acme += \
     board \
     feature \
+    impl \
     width \
 
 SOONG_CONFIG_acme_feature := false
 ```
 
-then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT"`.
+then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT"`
+and `srcs: ["*.cpp", "impl/default.cpp"]`.
 
 Alternatively, with `DefaultBoardConfig.mk`:
 
@@ -553,32 +579,28 @@
 SOONG_CONFIG_acme += \
     board \
     feature \
+    impl \
     width \
 
 SOONG_CONFIG_acme_board := soc_c
+SOONG_CONFIG_acme_impl := baz
 ```
 
 then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT
--DFEATURE_DEFAULT -DSIZE=DEFAULT"`.
+-DFEATURE_DEFAULT -DSIZE=DEFAULT"` and `srcs: ["*.cpp", "impl/baz.cpp"]`.
 
 `soong_config_module_type` modules will work best when used to wrap defaults
 modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
 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/TEST_MAPPING b/TEST_MAPPING
index 9f386ca..6eabd7c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -3,5 +3,14 @@
     {
       "path": "packages/modules/SdkExtensions"
     }
+  ],
+
+  "postsubmit": [
+    {
+      "name": "MicrodroidHostTestCases"
+    },
+    {
+      "name": "MicrodroidTestApp"
+    }
   ]
 }
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index 392e819..9e3d291 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -15,7 +15,8 @@
 package aconfig
 
 import (
-	"fmt"
+	"path/filepath"
+	"slices"
 	"strings"
 
 	"android/soong/android"
@@ -23,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 {
@@ -35,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
@@ -44,8 +53,6 @@
 		// The flags will only be repackaged if this prop is true.
 		Exportable bool
 	}
-
-	intermediatePath android.WritablePath
 }
 
 func DeclarationsFactory() android.Module {
@@ -60,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{}
@@ -73,8 +84,9 @@
 	if len(module.properties.Package) == 0 {
 		ctx.PropertyErrorf("package", "missing package property")
 	}
-	// TODO(b/311155208): Add mandatory check for container after all pre-existing
-	// ones are changed.
+	if len(module.properties.Container) == 0 {
+		ctx.PropertyErrorf("container", "missing container property")
+	}
 
 	// Add a dependency on the aconfig_value_sets defined in
 	// RELEASE_ACONFIG_VALUE_SETS, and add any aconfig_values that
@@ -83,17 +95,10 @@
 	if len(valuesFromConfig) > 0 {
 		ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig...)
 	}
-}
-
-func (module *DeclarationsModule) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		// The default output of this module is the intermediates format, which is
-		// not installable and in a private format that no other rules can handle
-		// correctly.
-		return []android.Path{module.intermediatePath}, nil
-	default:
-		return nil, fmt.Errorf("unsupported aconfig_declarations module reference tag %q", tag)
+	for rcName, valueSets := range ctx.Config().ReleaseAconfigExtraReleaseConfigsValueSets() {
+		if len(valueSets) > 0 {
+			ctx.AddDependency(ctx.Module(), implicitValuesTagType{ReleaseConfig: rcName}, valueSets...)
+		}
 	}
 }
 
@@ -115,56 +120,115 @@
 	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...)
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        aconfigRule,
-		Output:      intermediateCacheFilePath,
-		Inputs:      inputFiles,
-		Description: "aconfig_declarations",
-		Args: map[string]string{
+		// 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 ", module.properties.Values),
+			"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, "intermediate.txt")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        aconfigTextRule,
-		Output:      intermediateDumpFilePath,
-		Inputs:      android.Paths{intermediateCacheFilePath},
-		Description: "aconfig_text",
-	})
+		intermediateDumpFilePath := android.PathForModuleOut(ctx, assembleFileName(config, "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,
-	})
-
+		providerData[config] = 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)
 }
+
+func (module *DeclarationsModule) BuildActionProviderKeys() []blueprint.AnyProviderKey {
+	return []blueprint.AnyProviderKey{android.AconfigDeclarationsProviderKey}
+}
+
+func (module *DeclarationsModule) PackageContextPath() string {
+	return pkgPath
+}
+
+func (module *DeclarationsModule) CachedRules() []blueprint.Rule {
+	return []blueprint.Rule{aconfigRule, aconfigTextRule}
+}
+
+var _ blueprint.Incremental = &DeclarationsModule{}
diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go
index 1fe3c86..5483295 100644
--- a/aconfig/aconfig_declarations_test.go
+++ b/aconfig/aconfig_declarations_test.go
@@ -15,6 +15,7 @@
 package aconfig
 
 import (
+	"slices"
 	"strings"
 	"testing"
 
@@ -69,3 +70,160 @@
 	depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
 	android.AssertBoolEquals(t, "exportable", depData.Exportable, false)
 }
+
+func TestAconfigDeclarationsWithContainer(t *testing.T) {
+	bp := `
+		aconfig_declarations {
+			name: "module_name",
+			package: "com.example.package",
+			container: "com.android.foo",
+			srcs: [
+				"foo.aconfig",
+			],
+		}
+	`
+	result := runTest(t, android.FixtureExpectsNoErrors, bp)
+
+	module := result.ModuleForTests("module_name", "")
+	rule := module.Rule("aconfig")
+	android.AssertStringEquals(t, "rule must contain container", rule.Args["container"], "--container com.android.foo")
+}
+
+func TestMandatoryProperties(t *testing.T) {
+	testCases := []struct {
+		name          string
+		expectedError string
+		bp            string
+	}{
+		{
+			name: "Srcs missing from aconfig_declarations",
+			bp: `
+				aconfig_declarations {
+					name: "my_aconfig_declarations_foo",
+					package: "com.example.package",
+					container: "otherapex",
+				}`,
+			expectedError: `missing source files`,
+		},
+		{
+			name: "Package missing from aconfig_declarations",
+			bp: `
+				aconfig_declarations {
+					name: "my_aconfig_declarations_foo",
+					container: "otherapex",
+					srcs: ["foo.aconfig"],
+				}`,
+			expectedError: `missing package property`,
+		},
+		{
+			name: "Container missing from aconfig_declarations",
+			bp: `
+				aconfig_declarations {
+					name: "my_aconfig_declarations_foo",
+					package: "com.example.package",
+					srcs: ["foo.aconfig"],
+				}`,
+			expectedError: `missing container property`,
+		},
+	}
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
+			android.GroupFixturePreparers(PrepareForTestWithAconfigBuildComponents).
+				ExtendWithErrorHandler(errorHandler).
+				RunTestWithBp(t, test.bp)
+		})
+	}
+}
+
+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.SingletonModuleProvider(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/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go
index 72fe495..0437c26 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"fmt"
+	"slices"
 )
 
 // A singleton module that collects all of the aconfig flags declared in the
@@ -27,70 +28,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.SingletonModuleProvider(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{"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/Android.bp b/aconfig/build_flags/Android.bp
new file mode 100644
index 0000000..b3c7339
--- /dev/null
+++ b/aconfig/build_flags/Android.bp
@@ -0,0 +1,24 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-aconfig-build_flags",
+    pkgPath: "android/soong/aconfig/build_flags",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "sbox_proto",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "all_build_flag_declarations.go",
+        "build_flags.go",
+        "declarations.go",
+        "init.go",
+    ],
+    testSrcs: [
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/aconfig/build_flags/all_build_flag_declarations.go b/aconfig/build_flags/all_build_flag_declarations.go
new file mode 100644
index 0000000..282c9dc
--- /dev/null
+++ b/aconfig/build_flags/all_build_flag_declarations.go
@@ -0,0 +1,78 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"android/soong/android"
+)
+
+// A singleton module that collects all of the build flags declared in the
+// tree into a single combined file for export to the external flag setting
+// server (inside Google it's Gantry).
+//
+// Note that this is ALL build_declarations modules present in the tree, not just
+// ones that are relevant to the product currently being built, so that that infra
+// doesn't need to pull from multiple builds and merge them.
+func AllBuildFlagDeclarationsFactory() android.Singleton {
+	return &allBuildFlagDeclarationsSingleton{}
+}
+
+type allBuildFlagDeclarationsSingleton struct {
+	intermediateBinaryProtoPath android.OutputPath
+	intermediateTextProtoPath   android.OutputPath
+}
+
+func (this *allBuildFlagDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	// Find all of the build_flag_declarations modules
+	var intermediateFiles android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		decl, ok := android.SingletonModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
+		if !ok {
+			return
+		}
+		intermediateFiles = append(intermediateFiles, decl.IntermediateCacheOutputPath)
+	})
+
+	// Generate build action for build_flag (binary proto output)
+	this.intermediateBinaryProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allDeclarationsRule,
+		Inputs:      intermediateFiles,
+		Output:      this.intermediateBinaryProtoPath,
+		Description: "all_build_flag_declarations",
+		Args: map[string]string{
+			"intermediates": android.JoinPathsWithPrefix(intermediateFiles, "--intermediate "),
+		},
+	})
+	ctx.Phony("all_build_flag_declarations", this.intermediateBinaryProtoPath)
+
+	// Generate build action for build_flag (text proto output)
+	this.intermediateTextProtoPath = android.PathForIntermediates(ctx, "all_build_flag_declarations.textproto")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        allDeclarationsRuleTextProto,
+		Input:       this.intermediateBinaryProtoPath,
+		Output:      this.intermediateTextProtoPath,
+		Description: "all_build_flag_declarations_textproto",
+	})
+	ctx.Phony("all_build_flag_declarations_textproto", this.intermediateTextProtoPath)
+}
+
+func (this *allBuildFlagDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoal("droid", this.intermediateBinaryProtoPath)
+	for _, goal := range []string{"docs", "droid", "sdk"} {
+		ctx.DistForGoalWithFilename(goal, this.intermediateBinaryProtoPath, "build_flags/all_flags.pb")
+		ctx.DistForGoalWithFilename(goal, this.intermediateTextProtoPath, "build_flags/all_flags.textproto")
+	}
+}
diff --git a/aconfig/build_flags/build_flags.go b/aconfig/build_flags/build_flags.go
new file mode 100644
index 0000000..e878b5a
--- /dev/null
+++ b/aconfig/build_flags/build_flags.go
@@ -0,0 +1,71 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"fmt"
+
+	"android/soong/android"
+)
+
+const (
+	outJsonFileName = "build_flags.json"
+)
+
+func init() {
+	registerBuildFlagsModuleType(android.InitRegistrationContext)
+}
+
+func registerBuildFlagsModuleType(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("build_flags_json", buildFlagsFactory)
+}
+
+type buildFlags struct {
+	android.ModuleBase
+
+	outputPath android.OutputPath
+}
+
+func buildFlagsFactory() android.Module {
+	module := &buildFlags{}
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+func (m *buildFlags) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Read the build_flags_<partition>.json file generated by soong
+	// 'release-config' command.
+	srcPath := android.PathForOutput(ctx, "release-config", fmt.Sprintf("build_flags_%s.json", m.PartitionTag(ctx.DeviceConfig())))
+	m.outputPath = android.PathForModuleOut(ctx, outJsonFileName).OutputPath
+
+	// The 'release-config' command is called for every build, and generates the
+	// build_flags_<partition>.json file.
+	// Update the output file only if the source file is changed.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.CpIfChanged,
+		Input:  srcPath,
+		Output: m.outputPath,
+	})
+
+	installPath := android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(installPath, outJsonFileName, m.outputPath)
+}
+
+func (m *buildFlags) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(m.outputPath),
+	}}
+}
diff --git a/aconfig/build_flags/declarations.go b/aconfig/build_flags/declarations.go
new file mode 100644
index 0000000..e927db2
--- /dev/null
+++ b/aconfig/build_flags/declarations.go
@@ -0,0 +1,103 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"strings"
+
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+type BuildFlagDeclarationsProviderData struct {
+	IntermediateCacheOutputPath android.WritablePath
+	IntermediateDumpOutputPath  android.WritablePath
+}
+
+var BuildFlagDeclarationsProviderKey = blueprint.NewProvider[BuildFlagDeclarationsProviderData]()
+
+type DeclarationsModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	// Properties for "aconfig_declarations"
+	properties struct {
+		// aconfig files, relative to this Android.bp file
+		Srcs []string `android:"path"`
+	}
+}
+
+func DeclarationsFactory() android.Module {
+	module := &DeclarationsModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+
+	return module
+}
+
+func joinAndPrefix(prefix string, values []string) string {
+	var sb strings.Builder
+	for _, v := range values {
+		sb.WriteString(prefix)
+		sb.WriteString(v)
+	}
+	return sb.String()
+}
+
+func optionalVariable(prefix string, value string) string {
+	var sb strings.Builder
+	if value != "" {
+		sb.WriteString(prefix)
+		sb.WriteString(value)
+	}
+	return sb.String()
+}
+
+func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Intermediate format
+	declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs)
+	intermediateCacheFilePath := android.PathForModuleOut(ctx, "build_flag_intermediate.pb")
+	inputFiles := make([]android.Path, len(declarationFiles))
+	copy(inputFiles, declarationFiles)
+
+	// TODO(lamont): generate the rc_proto.FlagArtifacts message for the sources.
+	args := map[string]string{
+		"release_version": ctx.Config().ReleaseVersion(),
+		"declarations":    android.JoinPathsWithPrefix(declarationFiles, "--decl "),
+	}
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        buildFlagRule,
+		Output:      intermediateCacheFilePath,
+		Inputs:      inputFiles,
+		Description: "build_flag_declarations",
+		Args:        args,
+	})
+
+	intermediateDumpFilePath := android.PathForModuleOut(ctx, "build_flag_intermediate.textproto")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        buildFlagTextRule,
+		Output:      intermediateDumpFilePath,
+		Input:       intermediateCacheFilePath,
+		Description: "build_flag_declarations_text",
+	})
+
+	android.SetProvider(ctx, BuildFlagDeclarationsProviderKey, BuildFlagDeclarationsProviderData{
+		IntermediateCacheOutputPath: intermediateCacheFilePath,
+		IntermediateDumpOutputPath:  intermediateDumpFilePath,
+	})
+}
diff --git a/aconfig/build_flags/init.go b/aconfig/build_flags/init.go
new file mode 100644
index 0000000..dc1369c
--- /dev/null
+++ b/aconfig/build_flags/init.go
@@ -0,0 +1,79 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build_flags
+
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+var (
+	pctx = android.NewPackageContext("android/soong/aconfig/build_flags")
+
+	// For build_flag_declarations: Generate cache file
+	buildFlagRule = pctx.AndroidStaticRule("build-flag-declarations",
+		blueprint.RuleParams{
+			Command: `${buildFlagDeclarations} ` +
+				` ${declarations}` +
+				` --format pb` +
+				` --output ${out}.tmp` +
+				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+			CommandDeps: []string{
+				"${buildFlagDeclarations}",
+			},
+			Restat: true,
+		}, "release_version", "declarations")
+
+	buildFlagTextRule = pctx.AndroidStaticRule("build-flag-declarations-text",
+		blueprint.RuleParams{
+			Command: `${buildFlagDeclarations} --format=textproto` +
+				` --intermediate ${in}` +
+				` --format textproto` +
+				` --output ${out}.tmp` +
+				` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+			CommandDeps: []string{
+				"${buildFlagDeclarations}",
+			},
+			Restat: true,
+		})
+
+	allDeclarationsRule = pctx.AndroidStaticRule("all-build-flag-declarations-dump",
+		blueprint.RuleParams{
+			Command: `${buildFlagDeclarations} ${intermediates} --format pb --output ${out}`,
+			CommandDeps: []string{
+				"${buildFlagDeclarations}",
+			},
+		}, "intermediates")
+
+	allDeclarationsRuleTextProto = pctx.AndroidStaticRule("All_build_flag_declarations_dump_textproto",
+		blueprint.RuleParams{
+			Command: `${buildFlagDeclarations} --intermediate ${in} --format textproto --output ${out}`,
+			CommandDeps: []string{
+				"${buildFlagDeclarations}",
+			},
+		})
+)
+
+func init() {
+	RegisterBuildComponents(android.InitRegistrationContext)
+	pctx.Import("android/soong/android")
+	pctx.HostBinToolVariable("buildFlagDeclarations", "build-flag-declarations")
+}
+
+func RegisterBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("build_flag_declarations", DeclarationsFactory)
+	ctx.RegisterParallelSingletonType("all_build_flag_declarations", AllBuildFlagDeclarationsFactory)
+}
diff --git a/aconfig/codegen/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/aconfig_declarations_group.go b/aconfig/codegen/aconfig_declarations_group.go
index 1c91bee..13daf47 100644
--- a/aconfig/codegen/aconfig_declarations_group.go
+++ b/aconfig/codegen/aconfig_declarations_group.go
@@ -15,7 +15,6 @@
 package codegen
 
 import (
-	"fmt"
 	"maps"
 
 	"android/soong/android"
@@ -40,11 +39,6 @@
 	android.DefaultableModuleBase
 
 	properties AconfigDeclarationsGroupProperties
-
-	aconfigDeclarationNames      []string
-	intermediateCacheOutputPaths android.Paths
-	javaSrcjars                  android.Paths
-	modeInfos                    map[string]android.ModeInfo
 }
 
 type AconfigDeclarationsGroupProperties struct {
@@ -77,63 +71,45 @@
 	ctx.AddDependency(ctx.Module(), rustAconfigLibraryTag, adg.properties.Rust_aconfig_libraries...)
 }
 
-func (adg *AconfigDeclarationsGroup) VisitDeps(ctx android.ModuleContext) {
-	adg.modeInfos = make(map[string]android.ModeInfo)
+func (adg *AconfigDeclarationsGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	modeInfos := make(map[string]android.ModeInfo)
+	var aconfigDeclarationNames []string
+	var intermediateCacheOutputPaths android.Paths
+	var javaSrcjars android.Paths
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		tag := ctx.OtherModuleDependencyTag(dep)
 		if provider, ok := android.OtherModuleProvider(ctx, dep, android.CodegenInfoProvider); ok {
 
 			// aconfig declaration names and cache files are collected for all aconfig library dependencies
-			adg.aconfigDeclarationNames = append(adg.aconfigDeclarationNames, provider.AconfigDeclarations...)
-			adg.intermediateCacheOutputPaths = append(adg.intermediateCacheOutputPaths, provider.IntermediateCacheOutputPaths...)
+			aconfigDeclarationNames = append(aconfigDeclarationNames, provider.AconfigDeclarations...)
+			intermediateCacheOutputPaths = append(intermediateCacheOutputPaths, provider.IntermediateCacheOutputPaths...)
 
 			switch tag {
 			case aconfigDeclarationsGroupTag:
 				// Will retrieve outputs from another language codegen modules when support is added
-				adg.javaSrcjars = append(adg.javaSrcjars, provider.Srcjars...)
-				maps.Copy(adg.modeInfos, provider.ModeInfos)
+				javaSrcjars = append(javaSrcjars, provider.Srcjars...)
+				maps.Copy(modeInfos, provider.ModeInfos)
 			case javaAconfigLibraryTag:
-				adg.javaSrcjars = append(adg.javaSrcjars, provider.Srcjars...)
-				maps.Copy(adg.modeInfos, provider.ModeInfos)
+				javaSrcjars = append(javaSrcjars, provider.Srcjars...)
+				maps.Copy(modeInfos, provider.ModeInfos)
 			case ccAconfigLibraryTag:
-				maps.Copy(adg.modeInfos, provider.ModeInfos)
+				maps.Copy(modeInfos, provider.ModeInfos)
 			case rustAconfigLibraryTag:
-				maps.Copy(adg.modeInfos, provider.ModeInfos)
+				maps.Copy(modeInfos, provider.ModeInfos)
 			}
 		}
 	})
-}
 
-func (adg *AconfigDeclarationsGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	adg.VisitDeps(ctx)
-	adg.aconfigDeclarationNames = android.FirstUniqueStrings(adg.aconfigDeclarationNames)
-	adg.intermediateCacheOutputPaths = android.FirstUniquePaths(adg.intermediateCacheOutputPaths)
+	aconfigDeclarationNames = android.FirstUniqueStrings(aconfigDeclarationNames)
+	intermediateCacheOutputPaths = android.FirstUniquePaths(intermediateCacheOutputPaths)
 
 	android.SetProvider(ctx, android.CodegenInfoProvider, android.CodegenInfo{
-		AconfigDeclarations:          adg.aconfigDeclarationNames,
-		IntermediateCacheOutputPaths: adg.intermediateCacheOutputPaths,
-		Srcjars:                      adg.javaSrcjars,
-		ModeInfos:                    adg.modeInfos,
+		AconfigDeclarations:          aconfigDeclarationNames,
+		IntermediateCacheOutputPaths: intermediateCacheOutputPaths,
+		Srcjars:                      javaSrcjars,
+		ModeInfos:                    modeInfos,
 	})
-}
 
-var _ android.OutputFileProducer = (*AconfigDeclarationsGroup)(nil)
-
-func (adg *AconfigDeclarationsGroup) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return adg.intermediateCacheOutputPaths, nil
-	case ".srcjars":
-		return adg.javaSrcjars, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %s", tag)
-	}
-}
-
-func (adg *AconfigDeclarationsGroup) Srcjars() android.Paths {
-	return adg.javaSrcjars
-}
-
-func (adg *AconfigDeclarationsGroup) AconfigDeclarations() []string {
-	return adg.aconfigDeclarationNames
+	ctx.SetOutputFiles(intermediateCacheOutputPaths, "")
+	ctx.SetOutputFiles(javaSrcjars, ".srcjars")
 }
diff --git a/aconfig/codegen/aconfig_declarations_group_test.go b/aconfig/codegen/aconfig_declarations_group_test.go
index ec7cea3..c69d21f 100644
--- a/aconfig/codegen/aconfig_declarations_group_test.go
+++ b/aconfig/codegen/aconfig_declarations_group_test.go
@@ -15,9 +15,10 @@
 package codegen
 
 import (
+	"testing"
+
 	"android/soong/android"
 	"android/soong/java"
-	"testing"
 )
 
 func TestAconfigDeclarationsGroup(t *testing.T) {
@@ -28,6 +29,7 @@
 		aconfig_declarations {
 			name: "foo-aconfig",
 			package: "com.example.package",
+			container: "com.android.foo",
 			srcs: ["foo.aconfig"],
 		}
 
@@ -39,6 +41,7 @@
 		aconfig_declarations {
 			name: "bar-aconfig",
 			package: "com.example.package",
+			container: "com.android.foo",
 			srcs: ["foo.aconfig"],
 		}
 
diff --git a/aconfig/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go
index 80e4926..ec0a6b6 100644
--- a/aconfig/codegen/cc_aconfig_library.go
+++ b/aconfig/codegen/cc_aconfig_library.go
@@ -22,6 +22,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"fmt"
+	"strconv"
 	"strings"
 )
 
@@ -33,6 +34,10 @@
 
 const baseLibDep = "server_configurable_flags"
 
+const libBaseDep = "libbase"
+const libLogDep = "liblog"
+const libAconfigStorageReadApiCcDep = "libaconfig_storage_read_api_cc"
+
 type CcAconfigLibraryProperties struct {
 	// name of the aconfig_declarations module to generate a library for
 	Aconfig_declarations string
@@ -82,7 +87,14 @@
 	// Add a dependency for the aconfig flags base library if it is not forced read only
 	if mode != "force-read-only" {
 		deps.SharedLibs = append(deps.SharedLibs, baseLibDep)
+
 	}
+
+	// TODO: after storage migration is over, don't add these in force-read-only-mode.
+	deps.SharedLibs = append(deps.SharedLibs, libAconfigStorageReadApiCcDep)
+	deps.SharedLibs = append(deps.SharedLibs, libBaseDep)
+	deps.SharedLibs = append(deps.SharedLibs, libLogDep)
+
 	// TODO: It'd be really nice if we could reexport this library and not make everyone do it.
 
 	return deps
@@ -144,6 +156,7 @@
 		Args: map[string]string{
 			"gendir": this.generatedDir.String(),
 			"mode":   mode,
+			"debug":  strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorageCc()),
 		},
 	})
 
diff --git a/aconfig/codegen/cc_aconfig_library_test.go b/aconfig/codegen/cc_aconfig_library_test.go
index 05449bc..2f6c1a6 100644
--- a/aconfig/codegen/cc_aconfig_library_test.go
+++ b/aconfig/codegen/cc_aconfig_library_test.go
@@ -50,6 +50,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 
@@ -58,6 +59,21 @@
     		srcs: ["server_configurable_flags.cc"],
 			}
 
+			cc_library {
+    		name: "libbase",
+    		srcs: ["libbase.cc"],
+			}
+
+			cc_library {
+    		name: "liblog",
+    		srcs: ["liblog.cc"],
+			}
+
+			cc_library {
+    		name: "libaconfig_storage_read_api_cc",
+    		srcs: ["libaconfig_storage_read_api_cc.cc"],
+			}
+
 			cc_aconfig_library {
 				name: "my_cc_aconfig_library",
 				aconfig_declarations: "my_aconfig_declarations",
@@ -92,6 +108,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 
@@ -100,6 +117,21 @@
     		srcs: ["server_configurable_flags.cc"],
 			}
 
+			cc_library {
+    		name: "libbase",
+    		srcs: ["libbase.cc"],
+			}
+
+			cc_library {
+    		name: "liblog",
+    		srcs: ["liblog.cc"],
+			}
+
+			cc_library {
+    		name: "libaconfig_storage_read_api_cc",
+    		srcs: ["libaconfig_storage_read_api_cc.cc"],
+			}
+
 			cc_aconfig_library {
 				name: "my_cc_aconfig_library",
 				aconfig_declarations: "my_aconfig_declarations",
@@ -126,6 +158,7 @@
 		aconfig_declarations {
 			name: "my_aconfig_declarations_bar",
 			package: "com.example.package",
+			container: "com.android.foo",
 			srcs: ["bar.aconfig"],
 		}
 
@@ -152,6 +185,24 @@
 			srcs: ["server_configurable_flags.cc"],
 			vendor_available: true,
 		}
+
+		cc_library {
+			name: "libbase",
+			srcs: ["libbase.cc"],
+			vendor_available: true,
+		}
+
+		cc_library {
+			name: "liblog",
+			srcs: ["liblog.cc"],
+			vendor_available: true,
+		}
+
+		cc_library {
+			name: "libaconfig_storage_read_api_cc",
+			srcs: ["libaconfig_storage_read_api_cc.cc"],
+			vendor_available: true,
+		}
 	`
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithAconfigBuildComponents,
@@ -176,6 +227,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 
@@ -184,6 +236,22 @@
 				aconfig_declarations: "my_aconfig_declarations",
 				mode: "force-read-only",
 			}
+
+
+			cc_library {
+    		                name: "libbase",
+    		                srcs: ["libbase.cc"],
+			}
+
+			cc_library {
+    		                name: "liblog",
+    		                srcs: ["liblog.cc"],
+			}
+
+			cc_library {
+    		                name: "libaconfig_storage_read_api_cc",
+    		                srcs: ["libaconfig_storage_read_api_cc.cc"],
+			}
 		`))
 
 	module := result.ModuleForTests("my_cc_aconfig_library", "android_arm64_armv8-a_shared").Module()
diff --git a/aconfig/codegen/init.go b/aconfig/codegen/init.go
index 73a8951..6182e14 100644
--- a/aconfig/codegen/init.go
+++ b/aconfig/codegen/init.go
@@ -49,11 +49,12 @@
 				` && ${aconfig} create-cpp-lib` +
 				`    --mode ${mode}` +
 				`    --cache ${in}` +
-				`    --out ${gendir}`,
+				`    --out ${gendir}` +
+				`    --allow-instrumentation ${debug}`,
 			CommandDeps: []string{
 				"$aconfig",
 			},
-		}, "gendir", "mode")
+		}, "gendir", "mode", "debug")
 
 	// For rust_aconfig_library: Generate Rust library
 	rustRule = pctx.AndroidStaticRule("rust_aconfig_library",
diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
index 1378dfe..673ac2a 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -15,8 +15,6 @@
 package codegen
 
 import (
-	"fmt"
-
 	"android/soong/android"
 	"android/soong/java"
 
@@ -76,11 +74,11 @@
 	}
 }
 
-func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) GenerateSourceJarBuildActions(module *java.GeneratedJavaLibraryModule, ctx android.ModuleContext) android.Path {
+func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) GenerateSourceJarBuildActions(module *java.GeneratedJavaLibraryModule, ctx android.ModuleContext) (android.Path, android.Path) {
 	// Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag
 	declarationsModules := ctx.GetDirectDepsWithTag(declarationsTag)
 	if len(declarationsModules) != 1 {
-		panic(fmt.Errorf("Exactly one aconfig_declarations property required"))
+		panic("Exactly one aconfig_declarations property required")
 	}
 	declarations, _ := android.OtherModuleProvider(ctx, declarationsModules[0], android.AconfigDeclarationsProviderKey)
 
@@ -115,6 +113,7 @@
 		module.AddJarJarRenameRule(declarations.Package+".Flags", "")
 		module.AddJarJarRenameRule(declarations.Package+".FeatureFlags", "")
 		module.AddJarJarRenameRule(declarations.Package+".FeatureFlagsImpl", "")
+		module.AddJarJarRenameRule(declarations.Package+".CustomFeatureFlags", "")
 		module.AddJarJarRenameRule(declarations.Package+".FakeFeatureFlagsImpl", "")
 	}
 
@@ -129,7 +128,7 @@
 			}},
 	})
 
-	return srcJarPath
+	return srcJarPath, declarations.IntermediateCacheOutputPath
 }
 
 func isModeSupported(mode string) bool {
diff --git a/aconfig/codegen/java_aconfig_library_test.go b/aconfig/codegen/java_aconfig_library_test.go
index de45b5c..87b54a4 100644
--- a/aconfig/codegen/java_aconfig_library_test.go
+++ b/aconfig/codegen/java_aconfig_library_test.go
@@ -35,6 +35,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations_foo",
 				package: "com.example.package.foo",
+				container: "system",
 				srcs: ["foo.aconfig"],
 			}
 
@@ -46,6 +47,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations_bar",
 				package: "com.example.package.bar",
+				container: "system",
 				srcs: ["bar.aconfig"],
 			}
 
@@ -60,7 +62,7 @@
 	entry := android.AndroidMkEntriesForTest(t, result.TestContext, module)[0]
 
 	makeVar := entry.EntryMap["LOCAL_ACONFIG_FILES"]
-	android.EnsureListContainsSuffix(t, makeVar, "android_common/aconfig_merged.pb")
+	android.EnsureListContainsSuffix(t, makeVar, "android_common/system/aconfig_merged.pb")
 }
 
 func TestAndroidMkJavaLibrary(t *testing.T) {
@@ -175,6 +177,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 				exportable: true,
 			}
@@ -200,6 +203,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 
@@ -234,3 +238,52 @@
 func TestUnsupportedMode(t *testing.T) {
 	testCodegenModeWithError(t, "mode: `unsupported`,", "mode: \"unsupported\" is not a supported mode")
 }
+
+func TestMkEntriesMatchedContainer(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		java.PrepareForTestWithJavaDefaultModules).
+		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).
+		RunTestWithBp(t, `
+			aconfig_declarations {
+				name: "my_aconfig_declarations_foo",
+				package: "com.example.package.foo",
+				container: "system",
+				srcs: ["foo.aconfig"],
+			}
+
+			java_aconfig_library {
+				name: "my_java_aconfig_library_foo",
+				aconfig_declarations: "my_aconfig_declarations_foo",
+			}
+
+			aconfig_declarations {
+				name: "my_aconfig_declarations_bar",
+				package: "com.example.package.bar",
+				container: "system_ext",
+				srcs: ["bar.aconfig"],
+			}
+
+			java_aconfig_library {
+				name: "my_java_aconfig_library_bar",
+				aconfig_declarations: "my_aconfig_declarations_bar",
+			}
+
+			java_library {
+				name: "my_module",
+				srcs: [
+					"src/foo.java",
+				],
+				static_libs: [
+					"my_java_aconfig_library_foo",
+					"my_java_aconfig_library_bar",
+				],
+				platform_apis: true,
+			}
+		`)
+
+	module := result.ModuleForTests("my_module", "android_common").Module()
+	entry := android.AndroidMkEntriesForTest(t, result.TestContext, module)[0]
+	makeVar := entry.EntryMap["LOCAL_ACONFIG_FILES"]
+	android.EnsureListContainsSuffix(t, makeVar, "my_aconfig_declarations_foo/intermediate.pb")
+}
diff --git a/aconfig/codegen/rust_aconfig_library.go b/aconfig/codegen/rust_aconfig_library.go
index 3f7495b..ad8d632 100644
--- a/aconfig/codegen/rust_aconfig_library.go
+++ b/aconfig/codegen/rust_aconfig_library.go
@@ -99,8 +99,11 @@
 
 func (a *aconfigDecorator) SourceProviderDeps(ctx rust.DepsContext, deps rust.Deps) rust.Deps {
 	deps = a.BaseSourceProvider.SourceProviderDeps(ctx, deps)
+	deps.Rustlibs = append(deps.Rustlibs, "libaconfig_storage_read_api")
 	deps.Rustlibs = append(deps.Rustlibs, "libflags_rust")
 	deps.Rustlibs = append(deps.Rustlibs, "liblazy_static")
+	deps.Rustlibs = append(deps.Rustlibs, "liblogger")
+	deps.Rustlibs = append(deps.Rustlibs, "liblog_rust")
 	ctx.AddDependency(ctx.Module(), rustDeclarationsTag, a.Properties.Aconfig_declarations)
 	return deps
 }
diff --git a/aconfig/codegen/rust_aconfig_library_test.go b/aconfig/codegen/rust_aconfig_library_test.go
index 60bc9f7..523b464 100644
--- a/aconfig/codegen/rust_aconfig_library_test.go
+++ b/aconfig/codegen/rust_aconfig_library_test.go
@@ -11,7 +11,7 @@
 func TestRustAconfigLibrary(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithAconfigBuildComponents,
-		rust.PrepareForTestWithRustIncludeVndk,
+		rust.PrepareForIntegrationTestWithRust,
 		android.PrepareForTestWithArchMutator,
 		android.PrepareForTestWithDefaults,
 		android.PrepareForTestWithPrebuilts,
@@ -28,9 +28,25 @@
 				crate_name: "lazy_static",
 				srcs: ["src/lib.rs"],
 			}
+			rust_library {
+				name: "libaconfig_storage_read_api", // test mock
+				crate_name: "aconfig_storage_read_api",
+				srcs: ["lib.rs"],
+                        }
+			rust_library {
+				name: "liblogger", // test mock
+				crate_name: "logger",
+				srcs: ["lib.rs"],
+                        }
+			rust_library {
+				name: "liblog_rust", // test mock
+				crate_name: "log_rust",
+				srcs: ["lib.rs"],
+                        }
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 
@@ -85,7 +101,7 @@
 	t.Helper()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithAconfigBuildComponents,
-		rust.PrepareForTestWithRustIncludeVndk).
+		rust.PrepareForIntegrationTestWithRust).
 		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).
 		RunTestWithBp(t, fmt.Sprintf(`
 			rust_library {
@@ -98,9 +114,25 @@
 				crate_name: "lazy_static",
 				srcs: ["src/lib.rs"],
 			}
+			rust_library {
+				name: "libaconfig_storage_read_api", // test mock
+				crate_name: "aconfig_storage_read_api",
+				srcs: ["lib.rs"],
+                        }
+			rust_library {
+				name: "liblogger", // test mock
+				crate_name: "logger",
+				srcs: ["lib.rs"],
+                        }
+			rust_library {
+				name: "liblog_rust", // test mock
+				crate_name: "log_rust",
+				srcs: ["lib.rs"],
+                        }
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 			rust_aconfig_library {
@@ -132,7 +164,7 @@
 	t.Helper()
 	android.GroupFixturePreparers(
 		PrepareForTestWithAconfigBuildComponents,
-		rust.PrepareForTestWithRustIncludeVndk).
+		rust.PrepareForIntegrationTestWithRust).
 		ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(err)).
 		RunTestWithBp(t, fmt.Sprintf(`
 			rust_library {
@@ -145,9 +177,25 @@
 				crate_name: "lazy_static",
 				srcs: ["src/lib.rs"],
 			}
+			rust_library {
+				name: "libaconfig_storage_read_api", // test mock
+				crate_name: "aconfig_storage_read_api",
+				srcs: ["lib.rs"],
+                        }
+			rust_library {
+				name: "liblogger", // test mock
+				crate_name: "logger",
+				srcs: ["lib.rs"],
+                        }
+			rust_library {
+				name: "liblog_rust", // test mock
+				crate_name: "log_rust",
+				srcs: ["lib.rs"],
+                        }
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 			rust_aconfig_library {
diff --git a/aconfig/init.go b/aconfig/init.go
index 4625128..256b213 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -15,19 +15,23 @@
 package aconfig
 
 import (
+	"encoding/gob"
+
 	"android/soong/android"
 
 	"github.com/google/blueprint"
 )
 
 var (
-	pctx = android.NewPackageContext("android/soong/aconfig")
+	pkgPath = "android/soong/aconfig"
+	pctx    = android.NewPackageContext(pkgPath)
 
 	// For aconfig_declarations: Generate cache file
 	aconfigRule = pctx.AndroidStaticRule("aconfig",
 		blueprint.RuleParams{
 			Command: `${aconfig} create-cache` +
 				` --package ${package}` +
+				` ${container}` +
 				` ${declarations}` +
 				` ${values}` +
 				` ${default-permission}` +
@@ -38,7 +42,7 @@
 				"${aconfig}",
 			},
 			Restat: true,
-		}, "release_version", "package", "declarations", "values", "default-permission")
+		}, "release_version", "package", "container", "declarations", "values", "default-permission")
 
 	// For create-device-config-sysprops: Generate aconfig flag value map text file
 	aconfigTextRule = pctx.AndroidStaticRule("aconfig_text",
@@ -105,6 +109,9 @@
 	RegisterBuildComponents(android.InitRegistrationContext)
 	pctx.HostBinToolVariable("aconfig", "aconfig")
 	pctx.HostBinToolVariable("soong_zip", "soong_zip")
+
+	gob.Register(android.AconfigDeclarationsProviderData{})
+	gob.Register(android.ModuleOutPath{})
 }
 
 func RegisterBuildComponents(ctx android.RegistrationContext) {
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/android/Android.bp b/android/Android.bp
index 03619f4..774d24a 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -38,10 +38,13 @@
         "arch_list.go",
         "arch_module_context.go",
         "base_module_context.go",
+        "build_prop.go",
         "buildinfo_prop.go",
+        "compliance_metadata.go",
         "config.go",
+        "container.go",
         "test_config.go",
-        "config_bp2build.go",
+        "configurable_properties.go",
         "configured_jars.go",
         "csuite_config.go",
         "deapexer.go",
@@ -61,6 +64,7 @@
         "license_metadata.go",
         "license_sdk_member.go",
         "licenses.go",
+        "logtags.go",
         "makevars.go",
         "metrics.go",
         "module.go",
@@ -82,6 +86,7 @@
         "plugin.go",
         "prebuilt.go",
         "prebuilt_build_tool.go",
+        "product_config.go",
         "proto.go",
         "provider.go",
         "raw_files.go",
@@ -90,6 +95,7 @@
         "sandbox.go",
         "sdk.go",
         "sdk_version.go",
+        "shared_properties.go",
         "singleton.go",
         "singleton_module.go",
         "soong_config_modules.go",
@@ -103,10 +109,12 @@
         "visibility.go",
     ],
     testSrcs: [
+        "all_teams_test.go",
         "android_test.go",
         "androidmk_test.go",
         "apex_test.go",
         "arch_test.go",
+        "blueprint_e2e_test.go",
         "config_test.go",
         "configured_jars_test.go",
         "csuite_config_test.go",
@@ -137,6 +145,7 @@
         "selects_test.go",
         "singleton_module_test.go",
         "soong_config_modules_test.go",
+        "test_suites_test.go",
         "util_test.go",
         "variable_test.go",
         "visibility_test.go",
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index fcc57e1..a47e80f 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -43,13 +43,9 @@
 
 var AconfigDeclarationsProviderKey = blueprint.NewProvider[AconfigDeclarationsProviderData]()
 
-// This is used to collect the aconfig declarations info on the transitive closure,
-// the data is keyed on the container.
-type AconfigTransitiveDeclarationsInfo struct {
-	AconfigFiles map[string]Paths
-}
+type AconfigReleaseDeclarationsProviderData map[string]AconfigDeclarationsProviderData
 
-var AconfigTransitiveDeclarationsInfoProvider = blueprint.NewProvider[AconfigTransitiveDeclarationsInfo]()
+var AconfigReleaseDeclarationsProviderKey = blueprint.NewProvider[AconfigReleaseDeclarationsProviderData]()
 
 type ModeInfo struct {
 	Container string
@@ -80,53 +76,15 @@
 	}
 }
 
-// CollectDependencyAconfigFiles is used by some module types to provide finer dependency graphing than
-// we can do in ModuleBase.
-func CollectDependencyAconfigFiles(ctx ModuleContext, mergedAconfigFiles *map[string]Paths) {
-	if *mergedAconfigFiles == nil {
-		*mergedAconfigFiles = make(map[string]Paths)
-	}
-	ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) {
-		if dep, _ := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); dep.IntermediateCacheOutputPath != nil {
-			(*mergedAconfigFiles)[dep.Container] = append((*mergedAconfigFiles)[dep.Container], dep.IntermediateCacheOutputPath)
-			return
-		}
-		if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok {
-			for container, v := range dep.AconfigFiles {
-				(*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...)
-			}
-		}
-		// We process these last, so that they determine the final value, eliminating any duplicates that we picked up
-		// from UpdateAndroidBuildActions.
-		if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok {
-			for container, v := range dep.AconfigFiles {
-				(*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...)
-			}
-		}
-	})
-
-	for container, aconfigFiles := range *mergedAconfigFiles {
-		(*mergedAconfigFiles)[container] = mergeAconfigFiles(ctx, container, aconfigFiles, false)
-	}
-
-	SetProvider(ctx, AconfigTransitiveDeclarationsInfoProvider, AconfigTransitiveDeclarationsInfo{
-		AconfigFiles: *mergedAconfigFiles,
-	})
-}
-
-func SetAconfigFileMkEntries(m *ModuleBase, entries *AndroidMkEntries, aconfigFiles map[string]Paths) {
-	setAconfigFileMkEntries(m, entries, aconfigFiles)
-}
-
 type aconfigPropagatingDeclarationsInfo struct {
 	AconfigFiles map[string]Paths
 	ModeInfos    map[string]ModeInfo
 }
 
-var aconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]()
+var AconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]()
 
 func VerifyAconfigBuildMode(ctx ModuleContext, container string, module blueprint.Module, asError bool) {
-	if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok {
+	if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok {
 		for k, v := range dep.ModeInfos {
 			msg := fmt.Sprintf("%s/%s depends on %s/%s/%s across containers\n",
 				module.Name(), container, k, v.Container, v.Mode)
@@ -158,33 +116,32 @@
 		if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok {
 			mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath)
 		}
-		if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok {
+		// 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...)
 			}
 			propagateModeInfos(ctx, module, mergedModeInfos, dep.ModeInfos)
 		}
-		if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok {
-			for container, v := range dep.AconfigFiles {
-				mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...)
-			}
-		}
 	})
 	// We only need to set the provider if we have aconfig files.
 	if len(mergedAconfigFiles) > 0 {
-		for container, aconfigFiles := range mergedAconfigFiles {
+		for _, container := range SortedKeys(mergedAconfigFiles) {
+			aconfigFiles := mergedAconfigFiles[container]
 			mergedAconfigFiles[container] = mergeAconfigFiles(ctx, container, aconfigFiles, true)
 		}
 
-		SetProvider(ctx, aconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{
+		SetProvider(ctx, AconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{
 			AconfigFiles: mergedAconfigFiles,
 			ModeInfos:    mergedModeInfos,
 		})
+		ctx.Module().base().aconfigFilePaths = getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles)
 	}
 }
 
 func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) {
-	info, ok := SingletonModuleProvider(ctx, mod, aconfigPropagatingProviderKey)
+	info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
 	// If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done.
 	if !ok || len(info.AconfigFiles) == 0 {
 		return
@@ -215,7 +172,7 @@
 	if len(*entries) == 0 {
 		return
 	}
-	info, ok := SingletonModuleProvider(ctx, mod, aconfigPropagatingProviderKey)
+	info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
 	if !ok || len(info.AconfigFiles) == 0 {
 		return
 	}
@@ -223,7 +180,7 @@
 	for idx, _ := range *entries {
 		(*entries)[idx].ExtraEntries = append((*entries)[idx].ExtraEntries,
 			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
-				setAconfigFileMkEntries(mod.base(), entries, info.AconfigFiles)
+				entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
 			},
 		)
 
@@ -253,10 +210,6 @@
 	return Paths{output}
 }
 
-func setAconfigFileMkEntries(m *ModuleBase, entries *AndroidMkEntries, aconfigFiles map[string]Paths) {
-	entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(m, aconfigFiles))
-}
-
 func getAconfigFilePaths(m *ModuleBase, aconfigFiles map[string]Paths) (paths Paths) {
 	// TODO(b/311155208): The default container here should be system.
 	container := "system"
diff --git a/android/all_teams.go b/android/all_teams.go
index b177e20..d4bf7d0 100644
--- a/android/all_teams.go
+++ b/android/all_teams.go
@@ -23,9 +23,18 @@
 }
 
 // For each module, list the team or the bpFile the module is defined in.
-type moduleTeamInfo struct {
+type moduleTeamAndTestInfo struct {
+	// Name field from bp file for the team
 	teamName string
-	bpFile   string
+	// Blueprint file the module is located in.
+	bpFile string
+	// Is this module only used by tests.
+	testOnly bool
+	// Is this a directly testable target by running the module directly
+	// or via tradefed.
+	topLevelTestTarget bool
+	// String name indicating the module, like `java_library` for reporting.
+	kind string
 }
 
 type allTeamsSingleton struct {
@@ -37,16 +46,16 @@
 	// Map of all team modules we visit during GenerateBuildActions
 	teams map[string]teamProperties
 	// Keeps track of team information or bp file for each module we visit.
-	teams_for_mods map[string]moduleTeamInfo
+	teams_for_mods map[string]moduleTeamAndTestInfo
 }
 
 // See if there is a package module for the given bpFilePath with a team defined, if so return the team.
 // If not ascend up to the parent directory and do the same.
-func (this *allTeamsSingleton) lookupDefaultTeam(bpFilePath string) (teamProperties, bool) {
+func (t *allTeamsSingleton) lookupDefaultTeam(bpFilePath string) (teamProperties, bool) {
 	// return the Default_team listed in the package if is there.
-	if p, ok := this.packages[bpFilePath]; ok {
-		if t := p.Default_team; t != nil {
-			return this.teams[*p.Default_team], true
+	if p, ok := t.packages[bpFilePath]; ok {
+		if defaultTeam := p.Default_team; defaultTeam != nil {
+			return t.teams[*defaultTeam], true
 		}
 	}
 	// Strip a directory and go up.
@@ -57,15 +66,15 @@
 	if current == "." {
 		return teamProperties{}, false
 	}
-	return this.lookupDefaultTeam(filepath.Join(parent, base))
+	return t.lookupDefaultTeam(filepath.Join(parent, base))
 }
 
-// Create a rule to run a tool to collect all the intermediate files
-// which list the team per module into one proto file.
-func (this *allTeamsSingleton) GenerateBuildActions(ctx SingletonContext) {
-	this.packages = make(map[string]packageProperties)
-	this.teams = make(map[string]teamProperties)
-	this.teams_for_mods = make(map[string]moduleTeamInfo)
+// Visit all modules and collect all teams and use WriteFileRuleVerbatim
+// to write it out.
+func (t *allTeamsSingleton) GenerateBuildActions(ctx SingletonContext) {
+	t.packages = make(map[string]packageProperties)
+	t.teams = make(map[string]teamProperties)
+	t.teams_for_mods = make(map[string]moduleTeamAndTestInfo)
 
 	ctx.VisitAllModules(func(module Module) {
 		bpFile := ctx.BlueprintFile(module)
@@ -73,56 +82,75 @@
 		// Package Modules and Team Modules are stored in a map so we can look them up by name for
 		// modules without a team.
 		if pack, ok := module.(*packageModule); ok {
-			// Packages don't have names, use the blueprint file as the key. we can't get qualifiedModuleId in this context.
+			// Packages don't have names, use the blueprint file as the key. we can't get qualifiedModuleId in t context.
 			pkgKey := bpFile
-			this.packages[pkgKey] = pack.properties
+			t.packages[pkgKey] = pack.properties
 			return
 		}
 		if team, ok := module.(*teamModule); ok {
-			this.teams[team.Name()] = team.properties
+			t.teams[team.Name()] = team.properties
 			return
 		}
 
-		// If a team name is given for a module, store it.
-		// Otherwise store the bpFile so we can do a package walk later.
-		if module.base().Team() != "" {
-			this.teams_for_mods[module.Name()] = moduleTeamInfo{teamName: module.base().Team(), bpFile: bpFile}
-		} else {
-			this.teams_for_mods[module.Name()] = moduleTeamInfo{bpFile: bpFile}
+		testModInfo := TestModuleInformation{}
+		if tmi, ok := SingletonModuleProvider(ctx, module, TestOnlyProviderKey); ok {
+			testModInfo = tmi
 		}
+
+		// Some modules, like java_test_host don't set the provider when the module isn't enabled:
+		//                                                test_only, top_level
+		//     AVFHostTestCases{os:linux_glibc,arch:common} {true true}
+		//     AVFHostTestCases{os:windows,arch:common} {false false}
+		// Generally variant information of true override false or unset.
+		if testModInfo.TestOnly == false {
+			if prevValue, exists := t.teams_for_mods[module.Name()]; exists {
+				if prevValue.testOnly == true {
+					return
+				}
+			}
+		}
+		entry := moduleTeamAndTestInfo{
+			bpFile:             bpFile,
+			testOnly:           testModInfo.TestOnly,
+			topLevelTestTarget: testModInfo.TopLevelTarget,
+			kind:               ctx.ModuleType(module),
+			teamName:           module.base().Team(),
+		}
+		t.teams_for_mods[module.Name()] = entry
+
 	})
 
 	// Visit all modules again and lookup the team name in the package or parent package if the team
 	// isn't assignged at the module level.
-	allTeams := this.lookupTeamForAllModules()
+	allTeams := t.lookupTeamForAllModules()
 
-	this.outputPath = PathForOutput(ctx, ownershipDirectory, allTeamsFile)
+	t.outputPath = PathForOutput(ctx, ownershipDirectory, allTeamsFile)
 	data, err := proto.Marshal(allTeams)
 	if err != nil {
 		ctx.Errorf("Unable to marshal team data. %s", err)
 	}
 
-	WriteFileRuleVerbatim(ctx, this.outputPath, string(data))
-	ctx.Phony("all_teams", this.outputPath)
+	WriteFileRuleVerbatim(ctx, t.outputPath, string(data))
+	ctx.Phony("all_teams", t.outputPath)
 }
 
-func (this *allTeamsSingleton) MakeVars(ctx MakeVarsContext) {
-	ctx.DistForGoal("all_teams", this.outputPath)
+func (t *allTeamsSingleton) MakeVars(ctx MakeVarsContext) {
+	ctx.DistForGoal("all_teams", t.outputPath)
 }
 
 // Visit every (non-package, non-team) module and write out a proto containing
 // either the declared team data for that module or the package default team data for that module.
-func (this *allTeamsSingleton) lookupTeamForAllModules() *team_proto.AllTeams {
-	teamsProto := make([]*team_proto.Team, len(this.teams_for_mods))
-	for i, moduleName := range SortedKeys(this.teams_for_mods) {
-		m, _ := this.teams_for_mods[moduleName]
+func (t *allTeamsSingleton) lookupTeamForAllModules() *team_proto.AllTeams {
+	teamsProto := make([]*team_proto.Team, len(t.teams_for_mods))
+	for i, moduleName := range SortedKeys(t.teams_for_mods) {
+		m, _ := t.teams_for_mods[moduleName]
 		teamName := m.teamName
 		var teamProperties teamProperties
 		found := false
 		if teamName != "" {
-			teamProperties, found = this.teams[teamName]
+			teamProperties, found = t.teams[teamName]
 		} else {
-			teamProperties, found = this.lookupDefaultTeam(m.bpFile)
+			teamProperties, found = t.lookupDefaultTeam(m.bpFile)
 		}
 
 		trendy_team_id := ""
@@ -130,22 +158,18 @@
 			trendy_team_id = *teamProperties.Trendy_team_id
 		}
 
-		var files []string
 		teamData := new(team_proto.Team)
+		*teamData = team_proto.Team{
+			TargetName:     proto.String(moduleName),
+			Path:           proto.String(m.bpFile),
+			TestOnly:       proto.Bool(m.testOnly),
+			TopLevelTarget: proto.Bool(m.topLevelTestTarget),
+			Kind:           proto.String(m.kind),
+		}
 		if trendy_team_id != "" {
-			*teamData = team_proto.Team{
-				TrendyTeamId: proto.String(trendy_team_id),
-				TargetName:   proto.String(moduleName),
-				Path:         proto.String(m.bpFile),
-				File:         files,
-			}
+			teamData.TrendyTeamId = proto.String(trendy_team_id)
 		} else {
 			// Clients rely on the TrendyTeamId optional field not being set.
-			*teamData = team_proto.Team{
-				TargetName: proto.String(moduleName),
-				Path:       proto.String(m.bpFile),
-				File:       files,
-			}
 		}
 		teamsProto[i] = teamData
 	}
diff --git a/android/all_teams_test.go b/android/all_teams_test.go
index a02b86e..96ed92f 100644
--- a/android/all_teams_test.go
+++ b/android/all_teams_test.go
@@ -24,9 +24,10 @@
 func TestAllTeams(t *testing.T) {
 	t.Parallel()
 	ctx := GroupFixturePreparers(
-		PrepareForTestWithTeamBuildComponents,
+		prepareForTestWithTeamAndFakes,
+		// This adds two variants, one armv7-a-neon, one armv8-a
+		PrepareForTestWithArchMutator,
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
-			ctx.RegisterModuleType("fake", fakeModuleFactory)
 			ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
 		}),
 	).RunTestWithBp(t, `
@@ -51,6 +52,37 @@
 
 		fake {
 			name: "noteam",
+                        test_only: true,
+		}
+                // write the test-only provider value once
+		fake {
+                        name: "test-and-team-and-top1",
+                        test_only: true,
+                        team: "team2",
+                        arch: {arm: { skip: false},
+                               arm64: { skip: true}},
+		}
+                // write the test-only provider once, but on the other arch
+		fake {
+                        name: "test-and-team-and-top2",
+                        test_only: true,
+                        team: "team2",
+                        arch: {arm: { skip: true},
+                               arm64: { skip: false}},
+		}
+                // write the test-only provider value twice
+		fake {
+                        name: "test-and-team-and-top3",
+                        test_only: true,
+                        team: "team2",
+		}
+                // Don't write the test-only provider value
+		fake {
+                        name: "test-and-team-and-top4",
+                        test_only: true,
+                        team: "team2",
+                        arch: {arm: { skip: true},
+                               arm64: { skip: true}},
 		}
 	`)
 
@@ -58,17 +90,43 @@
 	teams = getTeamProtoOutput(t, ctx)
 
 	// map of module name -> trendy team name.
-	actualTeams := make(map[string]*string)
+	actualTeams := make(map[string]string)
+	actualTests := []string{}
+	actualTopLevelTests := []string{}
+
 	for _, teamProto := range teams.Teams {
-		actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
+		if teamProto.TrendyTeamId != nil {
+			actualTeams[teamProto.GetTargetName()] = *teamProto.TrendyTeamId
+		} else {
+			actualTeams[teamProto.GetTargetName()] = ""
+		}
+		if teamProto.GetTestOnly() {
+			actualTests = append(actualTests, teamProto.GetTargetName())
+		}
+		if teamProto.GetTopLevelTarget() {
+			actualTopLevelTests = append(actualTopLevelTests, teamProto.GetTargetName())
+		}
 	}
-	expectedTeams := map[string]*string{
-		"main_test": proto.String("cool_team"),
-		"tool":      proto.String("22222"),
-		"noteam":    nil,
+	expectedTeams := map[string]string{
+		"main_test":              "cool_team",
+		"tool":                   "22222",
+		"test-and-team-and-top1": "22222",
+		"test-and-team-and-top2": "22222",
+		"test-and-team-and-top3": "22222",
+		"test-and-team-and-top4": "22222",
+		"noteam":                 "",
 	}
 
+	expectedTests := []string{
+		"noteam",
+		"test-and-team-and-top1",
+		"test-and-team-and-top2",
+		"test-and-team-and-top3",
+		// There should be no test-and-team-top4 as we skip writing all variants
+		// test-only for all variants
+	}
 	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
+	AssertDeepEquals(t, "test matchup", expectedTests, actualTests)
 }
 
 func getTeamProtoOutput(t *testing.T, ctx *TestResult) *team_proto.AllTeams {
@@ -171,10 +229,9 @@
 		} `
 
 	ctx := GroupFixturePreparers(
-		PrepareForTestWithTeamBuildComponents,
+		prepareForTestWithTeamAndFakes,
 		PrepareForTestWithPackageModule,
 		FixtureRegisterWithContext(func(ctx RegistrationContext) {
-			ctx.RegisterModuleType("fake", fakeModuleFactory)
 			ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
 		}),
 		FixtureAddTextFile("Android.bp", rootBp),
@@ -206,3 +263,37 @@
 	}
 	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
 }
+
+type fakeForTests struct {
+	ModuleBase
+
+	sourceProperties SourceProperties
+	props            struct {
+		// If true, don't write test-only value in provider
+		Skip bool `android:"arch_variant"`
+	}
+}
+
+func fakeFactory() Module {
+	module := &fakeForTests{}
+	module.AddProperties(&module.sourceProperties, &module.props)
+	InitAndroidArchModule(module, HostAndDeviceSupported, MultilibBoth)
+
+	return module
+}
+
+var prepareForTestWithTeamAndFakes = GroupFixturePreparers(
+	FixtureRegisterWithContext(RegisterTeamBuildComponents),
+	FixtureRegisterWithContext(func(ctx RegistrationContext) {
+		ctx.RegisterModuleType("fake", fakeFactory)
+	}),
+)
+
+func (f *fakeForTests) GenerateAndroidBuildActions(ctx ModuleContext) {
+	if Bool(f.sourceProperties.Test_only) {
+		SetProvider(ctx, TestOnlyProviderKey, TestModuleInformation{
+			TestOnly:       Bool(f.sourceProperties.Test_only) && !f.props.Skip,
+			TopLevelTarget: false,
+		})
+	}
+}
diff --git a/android/androidmk.go b/android/androidmk.go
index 07f7c58..9699ce5 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -36,6 +36,7 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -498,6 +499,7 @@
 	Config() Config
 	moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 	ModuleType(module blueprint.Module) string
+	OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{})
 }
 
 func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
@@ -513,7 +515,7 @@
 	if a.Include == "" {
 		a.Include = "$(BUILD_PREBUILT)"
 	}
-	a.Required = append(a.Required, amod.RequiredModuleNames()...)
+	a.Required = append(a.Required, amod.RequiredModuleNames(ctx)...)
 	a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
 	a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
 
@@ -541,6 +543,11 @@
 		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())
+	} 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.
+		a.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
 	}
 
 	if len(base.testData) > 0 {
@@ -849,7 +856,7 @@
 	mod blueprint.Module, provider AndroidMkDataProvider) error {
 
 	amod := mod.(Module).base()
-	if shouldSkipAndroidMkProcessing(amod) {
+	if shouldSkipAndroidMkProcessing(ctx, amod) {
 		return nil
 	}
 
@@ -939,7 +946,7 @@
 
 func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
 	mod blueprint.Module, provider AndroidMkEntriesProvider) error {
-	if shouldSkipAndroidMkProcessing(mod.(Module).base()) {
+	if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) {
 		return nil
 	}
 
@@ -961,11 +968,11 @@
 	return nil
 }
 
-func ShouldSkipAndroidMkProcessing(module Module) bool {
-	return shouldSkipAndroidMkProcessing(module.base())
+func ShouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module Module) bool {
+	return shouldSkipAndroidMkProcessing(ctx, module.base())
 }
 
-func shouldSkipAndroidMkProcessing(module *ModuleBase) bool {
+func shouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, 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?
@@ -984,7 +991,7 @@
 		return true
 	}
 
-	return !module.Enabled() ||
+	return !module.Enabled(ctx) ||
 		module.commonProperties.HideFromMake ||
 		// Make does not understand LinuxBionic
 		module.Os() == LinuxBionic ||
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index ae2187f..72b8654 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 (
@@ -51,6 +47,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 +59,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 +68,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 +110,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{
 		{
diff --git a/android/apex.go b/android/apex.go
index 8759905..ecab8e3 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"slices"
 	"sort"
 	"strconv"
 	"strings"
@@ -36,11 +37,7 @@
 // Accessible via `ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)`
 type ApexInfo struct {
 	// Name of the apex variation that this module (i.e. the apex variant of the module) is
-	// mutated into, or "" for a platform (i.e. non-APEX) variant. Note that this name and the
-	// Soong module name of the APEX can be different. That happens when there is
-	// `override_apex` that overrides `apex`. In that case, both Soong modules have the same
-	// apex variation name which usually is `com.android.foo`. This name is also the `name`
-	// in the path `/apex/<name>` where this apex is activated on at runtime.
+	// mutated into, or "" for a platform (i.e. non-APEX) variant.
 	//
 	// Also note that a module can be included in multiple APEXes, in which case, the module is
 	// mutated into one or more variants, each of which is for an APEX. The variants then can
@@ -87,9 +84,18 @@
 
 	// 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
 }
 
-var ApexInfoProvider = blueprint.NewMutatorProvider[ApexInfo]("apex")
+// AllApexInfo holds the ApexInfo of all apexes that include this module.
+type AllApexInfo struct {
+	ApexInfos []ApexInfo
+}
+
+var ApexInfoProvider = blueprint.NewMutatorProvider[ApexInfo]("apex_mutate")
+var AllApexInfoProvider = blueprint.NewMutatorProvider[*AllApexInfo]("apex_info")
 
 func (i ApexInfo) AddJSONData(d *map[string]interface{}) {
 	(*d)["Apex"] = map[string]interface{}{
@@ -108,7 +114,7 @@
 // are configured to have the same alias variation named apex29. Whether platform APIs is allowed
 // or not also matters; if two APEXes don't have the same allowance, they get different names and
 // thus wouldn't be merged.
-func (i ApexInfo) mergedName(ctx PathContext) string {
+func (i ApexInfo) mergedName() string {
 	name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt())
 	return name
 }
@@ -347,7 +353,8 @@
 // ApexModuleBase provides the default implementation for the ApexModule interface. APEX-aware
 // modules are expected to include this struct and call InitApexModule().
 type ApexModuleBase struct {
-	ApexProperties ApexProperties
+	ApexProperties     ApexProperties
+	apexPropertiesLock sync.Mutex // protects ApexProperties during parallel apexDirectlyInAnyMutator
 
 	canHaveApexVariants bool
 
@@ -543,17 +550,10 @@
 	return true
 }
 
-type byApexName []ApexInfo
-
-func (a byApexName) Len() int           { return len(a) }
-func (a byApexName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a byApexName) Less(i, j int) bool { return a[i].ApexVariationName < a[j].ApexVariationName }
-
 // mergeApexVariations deduplicates apex variations that would build identically into a common
 // variation. It returns the reduced list of variations and a list of aliases from the original
 // variation names to the new variation names.
-func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
-	sort.Sort(byApexName(apexInfos))
+func mergeApexVariations(apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
 	seen := make(map[string]int)
 	for _, apexInfo := range apexInfos {
 		// If this is for a prebuilt apex then use the actual name of the apex variation to prevent this
@@ -567,7 +567,7 @@
 		// this one into it, otherwise create a new merged ApexInfo from this one and save it away so
 		// other ApexInfo instances can be merged into it.
 		variantName := apexInfo.ApexVariationName
-		mergedName := apexInfo.mergedName(ctx)
+		mergedName := apexInfo.mergedName()
 		if index, exists := seen[mergedName]; exists {
 			// Variants having the same mergedName are deduped
 			merged[index].InApexVariants = append(merged[index].InApexVariants, variantName)
@@ -592,73 +592,137 @@
 	return merged, aliases
 }
 
-// CreateApexVariations mutates a given module into multiple apex variants each of which is for an
-// apexBundle (and/or the platform) where the module is part of.
-func CreateApexVariations(mctx BottomUpMutatorContext, module ApexModule) []Module {
+// IncomingApexTransition is called by apexTransitionMutator.IncomingTransition on modules that can be in apexes.
+// The incomingVariation can be either the name of an apex if the dependency is coming directly from an apex
+// module, or it can be the name of an apex variation (e.g. apex10000) if it is coming from another module that
+// is in the apex.
+func IncomingApexTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	module := ctx.Module().(ApexModule)
 	base := module.apexModuleBase()
 
+	var apexInfos []ApexInfo
+	if allApexInfos, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
+		apexInfos = allApexInfos.ApexInfos
+	}
+
+	// Dependencies from platform variations go to the platform variation.
+	if incomingVariation == "" {
+		return ""
+	}
+
+	if len(apexInfos) == 0 {
+		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
+	// of apex variations and the aliases from apex names to apex variations.
+	var aliases [][2]string
+	if !module.UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
+		apexInfos, aliases = mergeApexVariations(apexInfos)
+	}
+
+	// Check if the incoming variation matches an apex name, and if so use the corresponding
+	// apex variation.
+	aliasIndex := slices.IndexFunc(aliases, func(alias [2]string) bool {
+		return alias[0] == incomingVariation
+	})
+	if aliasIndex >= 0 {
+		return aliases[aliasIndex][1]
+	}
+
+	// Check if the incoming variation matches an apex variation.
+	apexIndex := slices.IndexFunc(apexInfos, func(info ApexInfo) bool {
+		return info.ApexVariationName == incomingVariation
+	})
+	if apexIndex >= 0 {
+		return incomingVariation
+	}
+
+	return ""
+}
+
+func MutateApexTransition(ctx BaseModuleContext, variation string) {
+	module := ctx.Module().(ApexModule)
+	base := module.apexModuleBase()
+	platformVariation := variation == ""
+
+	var apexInfos []ApexInfo
+	if allApexInfos, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
+		apexInfos = allApexInfos.ApexInfos
+	}
+
 	// Shortcut
-	if len(base.apexInfos) == 0 {
-		return nil
+	if len(apexInfos) == 0 {
+		return
 	}
 
 	// Do some validity checks.
 	// TODO(jiyong): is this the right place?
-	base.checkApexAvailableProperty(mctx)
+	base.checkApexAvailableProperty(ctx)
 
-	var apexInfos []ApexInfo
-	var aliases [][2]string
-	if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
-		apexInfos, aliases = mergeApexVariations(mctx, base.apexInfos)
-	} else {
-		apexInfos = base.apexInfos
+	if !module.UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
+		apexInfos, _ = mergeApexVariations(apexInfos)
 	}
-	// base.apexInfos is only needed to propagate the list of apexes from apexInfoMutator to
-	// apexMutator. It is no longer accurate after mergeApexVariations, and won't be copied to
-	// all but the first created variant. Clear it so it doesn't accidentally get used later.
-	base.apexInfos = nil
-	sort.Sort(byApexName(apexInfos))
 
 	var inApex ApexMembership
 	for _, a := range apexInfos {
 		for _, apexContents := range a.ApexContents {
-			inApex = inApex.merge(apexContents.contents[mctx.ModuleName()])
+			inApex = inApex.merge(apexContents.contents[ctx.ModuleName()])
 		}
 	}
 	base.ApexProperties.InAnyApex = true
 	base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex
 
-	defaultVariation := ""
-	mctx.SetDefaultDependencyVariation(&defaultVariation)
+	if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) {
+		// 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
+		module.MakeUninstallable()
+	}
+	if !platformVariation {
+		var thisApexInfo ApexInfo
 
-	variations := []string{defaultVariation}
-	testApexes := []string{}
+		apexIndex := slices.IndexFunc(apexInfos, func(info ApexInfo) bool {
+			return info.ApexVariationName == variation
+		})
+		if apexIndex >= 0 {
+			thisApexInfo = apexInfos[apexIndex]
+		} else {
+			panic(fmt.Errorf("failed to find apexInfo for incoming variation %q", variation))
+		}
+
+		SetProvider(ctx, ApexInfoProvider, thisApexInfo)
+	}
+
+	// Set the value of TestApexes in every single apex variant.
+	// This allows each apex variant to be aware of the test apexes in the user provided apex_available.
+	var testApexes []string
 	for _, a := range apexInfos {
-		variations = append(variations, a.ApexVariationName)
 		testApexes = append(testApexes, a.TestApexes...)
 	}
-	modules := mctx.CreateVariations(variations...)
-	for i, mod := range modules {
-		platformVariation := i == 0
-		if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) {
-			// 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
-			mod.MakeUninstallable()
-		}
-		if !platformVariation {
-			mctx.SetVariationProvider(mod, ApexInfoProvider, apexInfos[i-1])
-		}
-		// Set the value of TestApexes in every single apex variant.
-		// This allows each apex variant to be aware of the test apexes in the user provided apex_available.
-		mod.(ApexModule).apexModuleBase().ApexProperties.TestApexes = testApexes
-	}
+	base.ApexProperties.TestApexes = testApexes
 
-	for _, alias := range aliases {
-		mctx.CreateAliasVariation(alias[0], alias[1])
-	}
+}
 
-	return modules
+func ApexInfoMutator(ctx TopDownMutatorContext, module ApexModule) {
+	base := module.apexModuleBase()
+	if len(base.apexInfos) > 0 {
+		apexInfos := slices.Clone(base.apexInfos)
+		slices.SortFunc(apexInfos, func(a, b ApexInfo) int {
+			return strings.Compare(a.ApexVariationName, b.ApexVariationName)
+		})
+		SetProvider(ctx, AllApexInfoProvider, &AllApexInfo{apexInfos})
+		// base.apexInfos is only needed to propagate the list of apexes from the apex module to its
+		// contents within apexInfoMutator. Clear it so it doesn't accidentally get used later.
+		base.apexInfos = nil
+	}
 }
 
 // UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies that are
@@ -669,13 +733,16 @@
 	// InApexVariants list in common. It is used instead of DepIsInSameApex because it needs to
 	// determine if the dep is in the same APEX due to being directly included, not only if it
 	// is included _because_ it is a dependency.
-	anyInSameApex := func(a, b []ApexInfo) bool {
-		collectApexes := func(infos []ApexInfo) []string {
-			var ret []string
-			for _, info := range infos {
-				ret = append(ret, info.InApexVariants...)
+	anyInSameApex := func(a, b ApexModule) bool {
+		collectApexes := func(m ApexModule) []string {
+			if allApexInfo, ok := OtherModuleProvider(mctx, m, AllApexInfoProvider); ok {
+				var ret []string
+				for _, info := range allApexInfo.ApexInfos {
+					ret = append(ret, info.InApexVariants...)
+				}
+				return ret
 			}
-			return ret
+			return nil
 		}
 
 		aApexes := collectApexes(a)
@@ -693,7 +760,7 @@
 	// If any of the dependencies requires unique apex variations, so does this module.
 	mctx.VisitDirectDeps(func(dep Module) {
 		if depApexModule, ok := dep.(ApexModule); ok {
-			if anyInSameApex(depApexModule.apexModuleBase().apexInfos, am.apexModuleBase().apexInfos) &&
+			if anyInSameApex(depApexModule, am) &&
 				(depApexModule.UniqueApexVariations() ||
 					depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) {
 				am.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps = true
@@ -704,18 +771,22 @@
 
 // UpdateDirectlyInAnyApex uses the final module to store if any variant of this module is directly
 // in any APEX, and then copies the final value to all the modules. It also copies the
-// DirectlyInAnyApex value to any direct dependencies with a CopyDirectlyInAnyApexTag dependency
-// tag.
+// DirectlyInAnyApex value to any transitive dependencies with a CopyDirectlyInAnyApexTag
+// dependency tag.
 func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) {
 	base := am.apexModuleBase()
-	// Copy DirectlyInAnyApex and InAnyApex from any direct dependencies with a
+	// Copy DirectlyInAnyApex and InAnyApex from any transitive dependencies with a
 	// CopyDirectlyInAnyApexTag dependency tag.
-	mctx.VisitDirectDeps(func(dep Module) {
-		if _, ok := mctx.OtherModuleDependencyTag(dep).(CopyDirectlyInAnyApexTag); ok {
-			depBase := dep.(ApexModule).apexModuleBase()
+	mctx.WalkDeps(func(child, parent Module) bool {
+		if _, ok := mctx.OtherModuleDependencyTag(child).(CopyDirectlyInAnyApexTag); ok {
+			depBase := child.(ApexModule).apexModuleBase()
+			depBase.apexPropertiesLock.Lock()
+			defer depBase.apexPropertiesLock.Unlock()
 			depBase.ApexProperties.DirectlyInAnyApex = base.ApexProperties.DirectlyInAnyApex
 			depBase.ApexProperties.InAnyApex = base.ApexProperties.InAnyApex
+			return true
 		}
+		return false
 	})
 
 	if base.ApexProperties.DirectlyInAnyApex {
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index c76d9c2..8b72f8e 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -15,8 +15,6 @@
 package android
 
 import (
-	"strings"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -103,31 +101,24 @@
 }
 
 var (
-	acDepTag = apexContributionsDepTag{}
+	AcDepTag = apexContributionsDepTag{}
 )
 
 // Creates a dep to each selected apex_contributions
 func (a *allApexContributions) DepsMutator(ctx BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), acDepTag, ctx.Config().AllApexContributions()...)
+	// Skip apex_contributions if BuildApexContributionContents is true
+	// This product config var allows some products in the same family to use mainline modules from source
+	// (e.g. shiba and shiba_fullmte)
+	// Eventually these product variants will have their own release config maps.
+	if !proptools.Bool(ctx.Config().BuildIgnoreApexContributionContents()) {
+		ctx.AddDependency(ctx.Module(), AcDepTag, ctx.Config().AllApexContributions()...)
+	}
 }
 
 // Set PrebuiltSelectionInfoProvider in post deps phase
 func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BaseModuleContext) {
 	addContentsToProvider := func(p *PrebuiltSelectionInfoMap, m *apexContributions) {
 		for _, content := range m.Contents() {
-			// Skip any apexes that have been added to the product specific ignore list
-			if InList(content, ctx.Config().BuildIgnoreApexContributionContents()) {
-				continue
-			}
-			// Coverage builds for TARGET_RELEASE=foo should always build from source,
-			// even if TARGET_RELEASE=foo uses prebuilt mainline modules.
-			// This is necessary because the checked-in prebuilts were generated with
-			// instrumentation turned off.
-			//
-			// Skip any prebuilt contents in coverage builds
-			if strings.HasPrefix(content, "prebuilt_") && (ctx.Config().JavaCoverageEnabled() || ctx.DeviceConfig().NativeCoverageEnabled()) {
-				continue
-			}
 			if !ctx.OtherModuleExists(content) && !ctx.Config().AllowMissingDependencies() {
 				ctx.ModuleErrorf("%s listed in apex_contributions %s does not exist\n", content, m.Name())
 			}
@@ -141,7 +132,7 @@
 	}
 
 	p := PrebuiltSelectionInfoMap{}
-	ctx.VisitDirectDepsWithTag(acDepTag, func(child Module) {
+	ctx.VisitDirectDepsWithTag(AcDepTag, func(child Module) {
 		if m, ok := child.(*apexContributions); ok {
 			addContentsToProvider(&p, m)
 		} else {
diff --git a/android/apex_test.go b/android/apex_test.go
index ddc730d..347bf7d 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -33,10 +33,22 @@
 		{
 			name: "single",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "foo",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"foo"},
+					InApexModules:     []string{"foo"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "apex10000",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"foo"},
+					InApexModules:     []string{"foo"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantAliases: [][2]string{
 				{"foo", "apex10000"},
@@ -45,98 +57,231 @@
 		{
 			name: "merge",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
-				{"bar", FutureApiLevel, false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "foo",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"foo"},
+					InApexModules:     []string{"foo"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
+				{
+					ApexVariationName: "bar",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"bar"},
+					InApexModules:     []string{"bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false, nil}},
+				{
+					ApexVariationName: "apex10000",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"foo", "bar"},
+					InApexModules:     []string{"foo", "bar"},
+				}},
 			wantAliases: [][2]string{
-				{"bar", "apex10000"},
 				{"foo", "apex10000"},
+				{"bar", "apex10000"},
 			},
 		},
 		{
 			name: "don't merge version",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
-				{"bar", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "foo",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"foo"},
+					InApexModules:     []string{"foo"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
+				{
+					ApexVariationName: "bar",
+					MinSdkVersion:     uncheckedFinalApiLevel(30),
+					InApexVariants:    []string{"bar"},
+					InApexModules:     []string{"bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantMerged: []ApexInfo{
-				{"apex30", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
-				{"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "apex10000",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"foo"},
+					InApexModules:     []string{"foo"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
+				{
+					ApexVariationName: "apex30",
+					MinSdkVersion:     uncheckedFinalApiLevel(30),
+					InApexVariants:    []string{"bar"},
+					InApexModules:     []string{"bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantAliases: [][2]string{
-				{"bar", "apex30"},
 				{"foo", "apex10000"},
+				{"bar", "apex30"},
 			},
 		},
 		{
 			name: "merge updatable",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
-				{"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "foo",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"foo"},
+					InApexModules:     []string{"foo"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
+				{
+					ApexVariationName: "bar",
+					MinSdkVersion:     FutureApiLevel,
+					Updatable:         true,
+					InApexVariants:    []string{"bar"},
+					InApexModules:     []string{"bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "apex10000",
+					MinSdkVersion:     FutureApiLevel,
+					Updatable:         true,
+					InApexVariants:    []string{"foo", "bar"},
+					InApexModules:     []string{"foo", "bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantAliases: [][2]string{
-				{"bar", "apex10000"},
 				{"foo", "apex10000"},
+				{"bar", "apex10000"},
 			},
 		},
 		{
 			name: "don't merge when for prebuilt_apex",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
-				{"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "foo",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"foo"},
+					InApexModules:     []string{"foo"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
+				{
+					ApexVariationName: "bar",
+					MinSdkVersion:     FutureApiLevel,
+					Updatable:         true,
+					InApexVariants:    []string{"bar"},
+					InApexModules:     []string{"bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 				// This one should not be merged in with the others because it is for
 				// a prebuilt_apex.
-				{"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex, nil},
+				{
+					ApexVariationName: "baz",
+					MinSdkVersion:     FutureApiLevel,
+					Updatable:         true,
+					InApexVariants:    []string{"baz"},
+					InApexModules:     []string{"baz"},
+					ForPrebuiltApex:   ForPrebuiltApex,
+				},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
-				{"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex, nil},
+				{
+					ApexVariationName: "apex10000",
+					MinSdkVersion:     FutureApiLevel,
+					Updatable:         true,
+					InApexVariants:    []string{"foo", "bar"},
+					InApexModules:     []string{"foo", "bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
+				{
+					ApexVariationName: "baz",
+					MinSdkVersion:     FutureApiLevel,
+					Updatable:         true,
+					InApexVariants:    []string{"baz"},
+					InApexModules:     []string{"baz"},
+					ForPrebuiltApex:   ForPrebuiltApex,
+				},
 			},
 			wantAliases: [][2]string{
-				{"bar", "apex10000"},
 				{"foo", "apex10000"},
+				{"bar", "apex10000"},
 			},
 		},
 		{
 			name: "merge different UsePlatformApis but don't allow using platform api",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
-				{"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "foo",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"foo"},
+					InApexModules:     []string{"foo"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
+				{
+					ApexVariationName: "bar",
+					MinSdkVersion:     FutureApiLevel,
+					UsePlatformApis:   true,
+					InApexVariants:    []string{"bar"},
+					InApexModules:     []string{"bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "apex10000",
+					MinSdkVersion:     FutureApiLevel,
+					InApexVariants:    []string{"foo", "bar"},
+					InApexModules:     []string{"foo", "bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantAliases: [][2]string{
-				{"bar", "apex10000"},
 				{"foo", "apex10000"},
+				{"bar", "apex10000"},
 			},
 		},
 		{
 			name: "merge same UsePlatformApis and allow using platform api",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, true, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
-				{"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "foo",
+					MinSdkVersion:     FutureApiLevel,
+					UsePlatformApis:   true,
+					InApexVariants:    []string{"foo"},
+					InApexModules:     []string{"foo"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
+				{
+					ApexVariationName: "bar",
+					MinSdkVersion:     FutureApiLevel,
+					UsePlatformApis:   true,
+					InApexVariants:    []string{"bar"},
+					InApexModules:     []string{"bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, false, true, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
+				{
+					ApexVariationName: "apex10000",
+					MinSdkVersion:     FutureApiLevel,
+					UsePlatformApis:   true,
+					InApexVariants:    []string{"foo", "bar"},
+					InApexModules:     []string{"foo", "bar"},
+					ForPrebuiltApex:   NotForPrebuiltApex,
+				},
 			},
 			wantAliases: [][2]string{
-				{"bar", "apex10000"},
 				{"foo", "apex10000"},
+				{"bar", "apex10000"},
 			},
 		},
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			config := TestConfig(t.TempDir(), nil, "", nil)
-			ctx := &configErrorWrapper{config: config}
-			gotMerged, gotAliases := mergeApexVariations(ctx, tt.in)
+			gotMerged, gotAliases := mergeApexVariations(tt.in)
 			if !reflect.DeepEqual(gotMerged, tt.wantMerged) {
 				t.Errorf("mergeApexVariations() gotMerged = %v, want %v", gotMerged, tt.wantMerged)
 			}
diff --git a/android/api_levels.go b/android/api_levels.go
index 1130c3e..dc17238 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -289,6 +289,10 @@
 
 var ApiLevelR = uncheckedFinalApiLevel(30)
 
+var ApiLevelUpsideDownCake = uncheckedFinalApiLevel(34)
+
+var ApiLevelVanillaIceCream = uncheckedFinalApiLevel(35)
+
 // ReplaceFinalizedCodenames returns the API level number associated with that API level
 // if the `raw` input is the codename of an API level has been finalized.
 // If the input is *not* a finalized codename, the input is returned unmodified.
diff --git a/android/arch.go b/android/arch.go
index 4fe4345..e0c6908 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -16,16 +16,11 @@
 
 import (
 	"encoding"
-	"encoding/json"
 	"fmt"
 	"reflect"
 	"runtime"
-	"sort"
 	"strings"
 
-	"android/soong/bazel"
-	"android/soong/starlark_fmt"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
@@ -491,7 +486,7 @@
 			// 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() {
+			if module.Enabled(mctx) {
 				mctx.AddInterVariantDependency(commonOsToOsSpecificVariantTag, commonOSVariant, module)
 			}
 		}
@@ -516,7 +511,7 @@
 	var variants []Module
 	mctx.VisitDirectDeps(func(m Module) {
 		if mctx.OtherModuleDependencyTag(m) == commonOsToOsSpecificVariantTag {
-			if m.Enabled() {
+			if m.Enabled(mctx) {
 				variants = append(variants, m)
 			}
 		}
@@ -693,6 +688,7 @@
 	m.base().commonProperties.CompileTarget = target
 	m.base().commonProperties.CompileMultiTargets = multiTargets
 	m.base().commonProperties.CompilePrimary = primaryTarget
+	m.base().commonProperties.ArchReady = true
 }
 
 // decodeMultilib returns the appropriate compile_multilib property for the module, or the default
@@ -979,12 +975,18 @@
 			panic(fmt.Errorf("unexpected tag format %q", field.Tag))
 		}
 		// these tags don't need to be present in the runtime generated struct type.
+		// However replace_instead_of_append does, because it's read by the blueprint
+		// property extending util functions, which can operate on these generated arch
+		// property structs.
 		values = RemoveListFromList(values, []string{"arch_variant", "variant_prepend", "path"})
 		if len(values) > 0 {
-			panic(fmt.Errorf("unknown tags %q in field %q", values, prefix+field.Name))
+			if values[0] != "replace_instead_of_append" || len(values) > 1 {
+				panic(fmt.Errorf("unknown tags %q in field %q", values, prefix+field.Name))
+			}
+			field.Tag = `android:"replace_instead_of_append"`
+		} else {
+			field.Tag = ``
 		}
-
-		field.Tag = ``
 		return true, field
 	}
 	return false, field
@@ -1898,428 +1900,8 @@
 	return buildTargets, nil
 }
 
-func (m *ModuleBase) getArchPropertySet(propertySet interface{}, archType ArchType) interface{} {
-	archString := archType.Field
-	for i := range m.archProperties {
-		if m.archProperties[i] == nil {
-			// Skip over nil properties
-			continue
-		}
-
-		// Not archProperties are usable; this function looks for properties of a very specific
-		// form, and ignores the rest.
-		for _, archProperty := range m.archProperties[i] {
-			// archPropValue is a property struct, we are looking for the form:
-			// `arch: { arm: { key: value, ... }}`
-			archPropValue := reflect.ValueOf(archProperty).Elem()
-
-			// Unwrap src so that it should looks like a pointer to `arm: { key: value, ... }`
-			src := archPropValue.FieldByName("Arch").Elem()
-
-			// Step into non-nil pointers to structs in the src value.
-			if src.Kind() == reflect.Ptr {
-				if src.IsNil() {
-					continue
-				}
-				src = src.Elem()
-			}
-
-			// Find the requested field (e.g. arm, x86) in the src struct.
-			src = src.FieldByName(archString)
-
-			// We only care about structs.
-			if !src.IsValid() || src.Kind() != reflect.Struct {
-				continue
-			}
-
-			// If the value of the field is a struct then step into the
-			// BlueprintEmbed field. The special "BlueprintEmbed" name is
-			// used by createArchPropTypeDesc to embed the arch properties
-			// in the parent struct, so the src arch prop should be in this
-			// field.
-			//
-			// See createArchPropTypeDesc for more details on how Arch-specific
-			// module properties are processed from the nested props and written
-			// into the module's archProperties.
-			src = src.FieldByName("BlueprintEmbed")
-
-			// Clone the destination prop, since we want a unique prop struct per arch.
-			propertySetClone := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-
-			// Copy the located property struct into the cloned destination property struct.
-			err := proptools.ExtendMatchingProperties([]interface{}{propertySetClone}, src.Interface(), nil, proptools.OrderReplace)
-			if err != nil {
-				// This is fine, it just means the src struct doesn't match the type of propertySet.
-				continue
-			}
-
-			return propertySetClone
-		}
-	}
-	// No property set was found specific to the given arch, so return an empty
-	// property set.
-	return reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-}
-
-// getMultilibPropertySet returns a property set struct matching the type of
-// `propertySet`, containing multilib-specific module properties for the given architecture.
-// If no multilib-specific properties exist for the given architecture, returns an empty property
-// set matching `propertySet`'s type.
-func (m *ModuleBase) getMultilibPropertySet(propertySet interface{}, archType ArchType) interface{} {
-	// archType.Multilib is lowercase (for example, lib32) but property struct field is
-	// capitalized, such as Lib32, so use strings.Title to capitalize it.
-	multiLibString := strings.Title(archType.Multilib)
-
-	for i := range m.archProperties {
-		if m.archProperties[i] == nil {
-			// Skip over nil properties
-			continue
-		}
-
-		// Not archProperties are usable; this function looks for properties of a very specific
-		// form, and ignores the rest.
-		for _, archProperties := range m.archProperties[i] {
-			// archPropValue is a property struct, we are looking for the form:
-			// `multilib: { lib32: { key: value, ... }}`
-			archPropValue := reflect.ValueOf(archProperties).Elem()
-
-			// Unwrap src so that it should looks like a pointer to `lib32: { key: value, ... }`
-			src := archPropValue.FieldByName("Multilib").Elem()
-
-			// Step into non-nil pointers to structs in the src value.
-			if src.Kind() == reflect.Ptr {
-				if src.IsNil() {
-					// Ignore nil pointers.
-					continue
-				}
-				src = src.Elem()
-			}
-
-			// Find the requested field (e.g. lib32) in the src struct.
-			src = src.FieldByName(multiLibString)
-
-			// We only care about valid struct pointers.
-			if !src.IsValid() || src.Kind() != reflect.Ptr || src.Elem().Kind() != reflect.Struct {
-				continue
-			}
-
-			// Get the zero value for the requested property set.
-			propertySetClone := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-
-			// Copy the located property struct into the "zero" property set struct.
-			err := proptools.ExtendMatchingProperties([]interface{}{propertySetClone}, src.Interface(), nil, proptools.OrderReplace)
-
-			if err != nil {
-				// This is fine, it just means the src struct doesn't match.
-				continue
-			}
-
-			return propertySetClone
-		}
-	}
-
-	// There were no multilib properties specifically matching the given archtype.
-	// Return zeroed value.
-	return reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-}
-
 // ArchVariantContext defines the limited context necessary to retrieve arch_variant properties.
 type ArchVariantContext interface {
 	ModuleErrorf(fmt string, args ...interface{})
 	PropertyErrorf(property, fmt string, args ...interface{})
 }
-
-// ArchVariantProperties represents a map of arch-variant config strings to a property interface{}.
-type ArchVariantProperties map[string]interface{}
-
-// ConfigurationAxisToArchVariantProperties represents a map of bazel.ConfigurationAxis to
-// ArchVariantProperties, such that each independent arch-variant axis maps to the
-// configs/properties for that axis.
-type ConfigurationAxisToArchVariantProperties map[bazel.ConfigurationAxis]ArchVariantProperties
-
-// GetArchVariantProperties returns a ConfigurationAxisToArchVariantProperties where the
-// arch-variant properties correspond to the values of the properties of the 'propertySet' struct
-// that are specific to that axis/configuration. Each axis is independent, containing
-// non-overlapping configs that correspond to the various "arch-variant" support, at this time:
-//
-//	arches (including multilib)
-//	oses
-//	arch+os combinations
-//
-// For example, passing a struct { Foo bool, Bar string } will return an interface{} that can be
-// type asserted back into the same struct, containing the config-specific property value specified
-// by the module if defined.
-//
-// Arch-specific properties may come from an arch stanza or a multilib stanza; properties
-// in these stanzas are combined.
-// For example: `arch: { x86: { Foo: ["bar"] } }, multilib: { lib32: {` Foo: ["baz"] } }`
-// will result in `Foo: ["bar", "baz"]` being returned for architecture x86, if the given
-// propertyset contains `Foo []string`.
-func (m *ModuleBase) GetArchVariantProperties(ctx ArchVariantContext, propertySet interface{}) ConfigurationAxisToArchVariantProperties {
-	// Return value of the arch types to the prop values for that arch.
-	axisToProps := ConfigurationAxisToArchVariantProperties{}
-
-	// Nothing to do for non-arch-specific modules.
-	if !m.ArchSpecific() {
-		return axisToProps
-	}
-
-	dstType := reflect.ValueOf(propertySet).Type()
-	var archProperties []interface{}
-
-	// First find the property set in the module that corresponds to the requested
-	// one. m.archProperties[i] corresponds to m.GetProperties()[i].
-	for i, generalProp := range m.GetProperties() {
-		srcType := reflect.ValueOf(generalProp).Type()
-		if srcType == dstType {
-			archProperties = m.archProperties[i]
-			axisToProps[bazel.NoConfigAxis] = ArchVariantProperties{"": generalProp}
-			break
-		}
-	}
-
-	if archProperties == nil {
-		// This module does not have the property set requested
-		return axisToProps
-	}
-
-	archToProp := ArchVariantProperties{}
-	// For each arch type (x86, arm64, etc.)
-	for _, arch := range ArchTypeList() {
-		// Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
-		// Iterate over every shard and extract a struct with the same type as the
-		// input one that contains the data specific to that arch.
-		propertyStructs := make([]reflect.Value, 0)
-		archFeaturePropertyStructs := make(map[string][]reflect.Value, 0)
-		for _, archProperty := range archProperties {
-			archTypeStruct, ok := getArchTypeStruct(ctx, archProperty, arch)
-			if ok {
-				propertyStructs = append(propertyStructs, archTypeStruct)
-
-				// For each feature this arch supports (arm: neon, x86: ssse3, sse4, ...)
-				for _, feature := range archFeatures[arch] {
-					prefix := "arch." + arch.Name + "." + feature
-					if featureProperties, ok := getChildPropertyStruct(ctx, archTypeStruct, feature, prefix); ok {
-						archFeaturePropertyStructs[feature] = append(archFeaturePropertyStructs[feature], featureProperties)
-					}
-				}
-			}
-			multilibStruct, ok := getMultilibStruct(ctx, archProperty, arch)
-			if ok {
-				propertyStructs = append(propertyStructs, multilibStruct)
-			}
-		}
-
-		archToProp[arch.Name] = mergeStructs(ctx, propertyStructs, propertySet)
-
-		// In soong, if multiple features match the current configuration, they're
-		// all used. In bazel, we have to have unambiguous select() statements, so
-		// we can't have two features that are both active in the same select().
-		// One alternative is to split out each feature into a separate select(),
-		// but then it's difficult to support exclude_srcs, which may need to
-		// exclude things from the regular arch select() statement if a certain
-		// feature is active. Instead, keep the features in the same select
-		// statement as the arches, but emit the power set of all possible
-		// combinations of features, so that bazel can match the most precise one.
-		allFeatures := make([]string, 0, len(archFeaturePropertyStructs))
-		for feature := range archFeaturePropertyStructs {
-			allFeatures = append(allFeatures, feature)
-		}
-		for _, features := range bazel.PowerSetWithoutEmptySet(allFeatures) {
-			sort.Strings(features)
-			propsForCurrentFeatureSet := make([]reflect.Value, 0)
-			propsForCurrentFeatureSet = append(propsForCurrentFeatureSet, propertyStructs...)
-			for _, feature := range features {
-				propsForCurrentFeatureSet = append(propsForCurrentFeatureSet, archFeaturePropertyStructs[feature]...)
-			}
-			archToProp[arch.Name+"-"+strings.Join(features, "-")] =
-				mergeStructs(ctx, propsForCurrentFeatureSet, propertySet)
-		}
-	}
-	axisToProps[bazel.ArchConfigurationAxis] = archToProp
-
-	osToProp := ArchVariantProperties{}
-	archOsToProp := ArchVariantProperties{}
-
-	linuxStructs := getTargetStructs(ctx, archProperties, "Linux")
-	bionicStructs := getTargetStructs(ctx, archProperties, "Bionic")
-	hostStructs := getTargetStructs(ctx, archProperties, "Host")
-	hostLinuxStructs := getTargetStructs(ctx, archProperties, "Host_linux")
-	hostNotWindowsStructs := getTargetStructs(ctx, archProperties, "Not_windows")
-
-	// For android, linux, ...
-	for _, os := range osTypeList {
-		if os == CommonOS {
-			// It looks like this OS value is not used in Blueprint files
-			continue
-		}
-		osStructs := make([]reflect.Value, 0)
-
-		osSpecificStructs := getTargetStructs(ctx, archProperties, os.Field)
-		if os.Class == Host {
-			osStructs = append(osStructs, hostStructs...)
-		}
-		if os.Linux() {
-			osStructs = append(osStructs, linuxStructs...)
-		}
-		if os.Bionic() {
-			osStructs = append(osStructs, bionicStructs...)
-		}
-		if os.Linux() && os.Class == Host {
-			osStructs = append(osStructs, hostLinuxStructs...)
-		}
-
-		if os == LinuxMusl {
-			osStructs = append(osStructs, getTargetStructs(ctx, archProperties, "Musl")...)
-		}
-		if os == Linux {
-			osStructs = append(osStructs, getTargetStructs(ctx, archProperties, "Glibc")...)
-		}
-
-		osStructs = append(osStructs, osSpecificStructs...)
-
-		if os.Class == Host && os != Windows {
-			osStructs = append(osStructs, hostNotWindowsStructs...)
-		}
-		osToProp[os.Name] = mergeStructs(ctx, osStructs, propertySet)
-
-		// For arm, x86, ...
-		for _, arch := range osArchTypeMap[os] {
-			osArchStructs := make([]reflect.Value, 0)
-
-			// Auto-combine with Linux_ and Bionic_ targets. This potentially results in
-			// repetition and select() bloat, but use of Linux_* and Bionic_* targets is rare.
-			// TODO(b/201423152): Look into cleanup.
-			if os.Linux() {
-				targetField := "Linux_" + arch.Name
-				targetStructs := getTargetStructs(ctx, archProperties, targetField)
-				osArchStructs = append(osArchStructs, targetStructs...)
-			}
-			if os.Bionic() {
-				targetField := "Bionic_" + arch.Name
-				targetStructs := getTargetStructs(ctx, archProperties, targetField)
-				osArchStructs = append(osArchStructs, targetStructs...)
-			}
-			if os == LinuxMusl {
-				targetField := "Musl_" + arch.Name
-				targetStructs := getTargetStructs(ctx, archProperties, targetField)
-				osArchStructs = append(osArchStructs, targetStructs...)
-			}
-			if os == Linux {
-				targetField := "Glibc_" + arch.Name
-				targetStructs := getTargetStructs(ctx, archProperties, targetField)
-				osArchStructs = append(osArchStructs, targetStructs...)
-			}
-
-			targetField := GetCompoundTargetField(os, arch)
-			targetName := fmt.Sprintf("%s_%s", os.Name, arch.Name)
-			targetStructs := getTargetStructs(ctx, archProperties, targetField)
-			osArchStructs = append(osArchStructs, targetStructs...)
-
-			archOsToProp[targetName] = mergeStructs(ctx, osArchStructs, propertySet)
-		}
-	}
-
-	axisToProps[bazel.OsConfigurationAxis] = osToProp
-	axisToProps[bazel.OsArchConfigurationAxis] = archOsToProp
-	return axisToProps
-}
-
-// Returns a struct matching the propertySet interface, containing properties specific to the targetName
-// For example, given these arguments:
-//
-//	propertySet = BaseCompilerProperties
-//	targetName = "android_arm"
-//
-// And given this Android.bp fragment:
-//
-//	target:
-//	   android_arm: {
-//	      srcs: ["foo.c"],
-//	   }
-//	   android_arm64: {
-//	      srcs: ["bar.c"],
-//	  }
-//	}
-//
-// This would return a BaseCompilerProperties with BaseCompilerProperties.Srcs = ["foo.c"]
-func getTargetStructs(ctx ArchVariantContext, archProperties []interface{}, targetName string) []reflect.Value {
-	var propertyStructs []reflect.Value
-	for _, archProperty := range archProperties {
-		archPropValues := reflect.ValueOf(archProperty).Elem()
-		targetProp := archPropValues.FieldByName("Target").Elem()
-		targetStruct, ok := getChildPropertyStruct(ctx, targetProp, targetName, targetName)
-		if ok {
-			propertyStructs = append(propertyStructs, targetStruct)
-		} else {
-			return []reflect.Value{}
-		}
-	}
-
-	return propertyStructs
-}
-
-func mergeStructs(ctx ArchVariantContext, propertyStructs []reflect.Value, propertySet interface{}) interface{} {
-	// Create a new instance of the requested property set
-	value := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-
-	// Merge all the structs together
-	for _, propertyStruct := range propertyStructs {
-		mergePropertyStruct(ctx, value, propertyStruct)
-	}
-
-	return value
-}
-
-func printArchTypeStarlarkDict(dict map[ArchType][]string) string {
-	valDict := make(map[string]string, len(dict))
-	for k, v := range dict {
-		valDict[k.String()] = starlark_fmt.PrintStringList(v, 1)
-	}
-	return starlark_fmt.PrintDict(valDict, 0)
-}
-
-func printArchTypeNestedStarlarkDict(dict map[ArchType]map[string][]string) string {
-	valDict := make(map[string]string, len(dict))
-	for k, v := range dict {
-		valDict[k.String()] = starlark_fmt.PrintStringListDict(v, 1)
-	}
-	return starlark_fmt.PrintDict(valDict, 0)
-}
-
-func printArchConfigList(arches []archConfig) string {
-	jsonOut, err := json.MarshalIndent(arches, "", starlark_fmt.Indention(1))
-	if err != nil {
-		panic(fmt.Errorf("Error converting arch configs %#v to json: %q", arches, err))
-	}
-	return fmt.Sprintf("json.decode('''%s''')", string(jsonOut))
-}
-
-func StarlarkArchConfigurations() string {
-	return fmt.Sprintf(`
-_arch_to_variants = %s
-
-_arch_to_cpu_variants = %s
-
-_arch_to_features = %s
-
-_android_arch_feature_for_arch_variant = %s
-
-_aml_arches = %s
-
-_ndk_arches = %s
-
-arch_to_variants = _arch_to_variants
-arch_to_cpu_variants = _arch_to_cpu_variants
-arch_to_features = _arch_to_features
-android_arch_feature_for_arch_variants = _android_arch_feature_for_arch_variant
-aml_arches = _aml_arches
-ndk_arches = _ndk_arches
-`, printArchTypeStarlarkDict(archVariants),
-		printArchTypeStarlarkDict(cpuVariants),
-		printArchTypeStarlarkDict(archFeatures),
-		printArchTypeNestedStarlarkDict(androidArchFeatureMap),
-		printArchConfigList(getAmlAbisConfig()),
-		printArchConfigList(getNdkAbisConfig()),
-	)
-}
diff --git a/android/arch_list.go b/android/arch_list.go
index f4409a9..4233456 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -103,6 +103,7 @@
 		"kryo385",
 		"exynos-m1",
 		"exynos-m2",
+		"oryon",
 	},
 	X86:    {},
 	X86_64: {},
diff --git a/android/arch_module_context.go b/android/arch_module_context.go
index 3cf4b41..a3a03af 100644
--- a/android/arch_module_context.go
+++ b/android/arch_module_context.go
@@ -35,6 +35,7 @@
 type archModuleContext struct {
 	// TODO: these should eventually go through a (possibly cached) provider like any other configuration instead
 	//  of being special cased.
+	ready         bool
 	os            OsType
 	target        Target
 	targetPrimary bool
@@ -42,6 +43,13 @@
 	primaryArch   bool
 }
 
+// ArchReady returns true if the arch mutator has run on the module. Before this returns
+// true, the module essentially doesn't have an arch and cannot make decisions based on
+// architecture.
+func (a *archModuleContext) ArchReady() bool {
+	return a.ready
+}
+
 func (a *archModuleContext) Target() Target {
 	return a.target
 }
diff --git a/android/arch_test.go b/android/arch_test.go
index 5021a67..f0a58a9 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -423,7 +423,7 @@
 		variants := ctx.ModuleVariantsForTests(name)
 		for _, variant := range variants {
 			m := ctx.ModuleForTests(name, variant)
-			if m.Module().Enabled() {
+			if m.Module().Enabled(PanickingConfigAndErrorContext(ctx)) {
 				ret = append(ret, variant)
 			}
 		}
@@ -533,7 +533,7 @@
 		variants := ctx.ModuleVariantsForTests(name)
 		for _, variant := range variants {
 			m := ctx.ModuleForTests(name, variant)
-			if m.Module().Enabled() {
+			if m.Module().Enabled(PanickingConfigAndErrorContext(ctx)) {
 				ret = append(ret, variant)
 			}
 		}
diff --git a/android/base_module_context.go b/android/base_module_context.go
index dd38a4e..5506000 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -20,7 +20,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/parser"
+	"github.com/google/blueprint/proptools"
 )
 
 // BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
@@ -219,7 +219,7 @@
 
 	// EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context
 	// can be used to evaluate the final value of Configurable properties.
-	EvaluateConfiguration(parser.SelectType, string) (string, bool)
+	EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue
 }
 
 type baseModuleContext struct {
@@ -305,6 +305,12 @@
 	AllowDisabledModuleDependency(target Module) bool
 }
 
+type AlwaysAllowDisabledModuleDependencyTag struct{}
+
+func (t AlwaysAllowDisabledModuleDependencyTag) AllowDisabledModuleDependency(Module) bool {
+	return true
+}
+
 func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool, ignoreBlueprint bool) Module {
 	aModule, _ := module.(Module)
 
@@ -319,7 +325,7 @@
 		return nil
 	}
 
-	if !aModule.Enabled() {
+	if !aModule.Enabled(b) {
 		if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) {
 			if b.Config().AllowMissingDependencies() {
 				b.AddMissingDependencies([]string{b.OtherModuleName(aModule)})
@@ -530,7 +536,7 @@
 		return true
 	} else if tag == licensesTag {
 		return true
-	} else if tag == acDepTag {
+	} else if tag == AcDepTag {
 		return true
 	}
 	return false
@@ -571,31 +577,6 @@
 	return sb.String()
 }
 
-func (m *baseModuleContext) EvaluateConfiguration(ty parser.SelectType, condition string) (string, bool) {
-	switch ty {
-	case parser.SelectTypeReleaseVariable:
-		if v, ok := m.Config().productVariables.BuildFlags[condition]; ok {
-			return v, true
-		}
-		return "", false
-	case parser.SelectTypeProductVariable:
-		// TODO(b/323382414): Might add these on a case-by-case basis
-		m.ModuleErrorf("TODO(b/323382414): Product variables are not yet supported in selects")
-		return "", false
-	case parser.SelectTypeSoongConfigVariable:
-		parts := strings.Split(condition, ":")
-		namespace := parts[0]
-		variable := parts[1]
-		if n, ok := m.Config().productVariables.VendorVars[namespace]; ok {
-			if v, ok := n[variable]; ok {
-				return v, true
-			}
-		}
-		return "", false
-	case parser.SelectTypeVariant:
-		m.ModuleErrorf("TODO(b/323382414): Variants are not yet supported in selects")
-		return "", false
-	default:
-		panic("Should be unreachable")
-	}
+func (m *baseModuleContext) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue {
+	return m.Module().ConfigurableEvaluator(m).EvaluateConfiguration(condition, property)
 }
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..45c17c3
--- /dev/null
+++ b/android/build_prop.go
@@ -0,0 +1,125 @@
+// 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
+
+	// Path to the input prop files. The contents of the files are directly
+	// emitted to the output
+	Prop_files []string `android:"path"`
+
+	// 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"`
+}
+
+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) 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")
+		return
+	}
+
+	partition := p.PartitionTag(ctx.DeviceConfig())
+	if partition != "system" {
+		ctx.PropertyErrorf("partition", "unsupported partition %q: only \"system\" is supported", partition)
+		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.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())
+	postProcessCmd.FlagWithInput("--kernel-version-file-for-uffd-gc ", PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt"))
+	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)
+	ctx.InstallFile(p.installPath, p.stem(), p.outputFilePath)
+
+	ctx.SetOutputFiles(Paths{p.outputFilePath}, "")
+}
+
+// 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
index 8e19ad5..bba4c0d 100644
--- a/android/buildinfo_prop.go
+++ b/android/buildinfo_prop.go
@@ -15,24 +15,23 @@
 package android
 
 import (
-	"fmt"
-	"strings"
-
 	"github.com/google/blueprint/proptools"
 )
 
 func init() {
 	ctx := InitRegistrationContext
-	ctx.RegisterParallelSingletonModuleType("buildinfo_prop", buildinfoPropFactory)
+	ctx.RegisterModuleType("buildinfo_prop", buildinfoPropFactory)
 }
 
 type buildinfoPropProperties struct {
 	// Whether this module is directly installable to one of the partitions. Default: true.
 	Installable *bool
+
+	Product_config *string `android:"path"`
 }
 
 type buildinfoPropModule struct {
-	SingletonModuleBase
+	ModuleBase
 
 	properties buildinfoPropProperties
 
@@ -40,108 +39,65 @@
 	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)
+func shouldAddBuildThumbprint(config Config) bool {
+	knownOemProperties := []string{
+		"ro.product.brand",
+		"ro.product.name",
+		"ro.product.device",
 	}
-	return Paths{p.outputFilePath}, nil
+
+	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
+	ctx.SetOutputFiles(Paths{p.outputFilePath}, "")
+
 	if !ctx.Config().KatiEnabled() {
 		WriteFileRule(ctx, p.outputFilePath, "# no buildinfo.prop if kati is disabled")
 		return
 	}
 
-	lines := make([]string, 0)
-
-	writeString := func(str string) {
-		lines = append(lines, str)
-	}
-
-	writeString("# begin build properties")
-	writeString("# autogenerated by build/soong/android/buildinfo_prop.go")
-
-	writeProp := func(key, value string) {
-		if strings.Contains(key, "=") {
-			panic(fmt.Errorf("wrong property key %q: key must not contain '='", key))
-		}
-		writeString(key + "=" + value)
-	}
+	rule := NewRuleBuilder(pctx, ctx)
 
 	config := ctx.Config()
 
-	writeProp("ro.build.version.sdk", config.PlatformSdkVersion().String())
-	writeProp("ro.build.version.preview_sdk", config.PlatformPreviewSdkVersion())
-	writeProp("ro.build.version.codename", config.PlatformSdkCodename())
-	writeProp("ro.build.version.all_codenames", strings.Join(config.PlatformVersionActiveCodenames(), ","))
-	writeProp("ro.build.version.release", config.PlatformVersionLastStable())
-	writeProp("ro.build.version.release_or_codename", config.PlatformVersionName())
-	writeProp("ro.build.version.security_patch", config.PlatformSecurityPatch())
-	writeProp("ro.build.version.base_os", config.PlatformBaseOS())
-	writeProp("ro.build.version.min_supported_target_sdk", config.PlatformMinSupportedTargetSdkVersion())
-	writeProp("ro.build.version.known_codenames", config.PlatformVersionKnownCodenames())
+	cmd := rule.Command().BuiltTool("buildinfo")
 
-	if config.Eng() {
-		writeProp("ro.build.type", "eng")
-	} else if config.Debuggable() {
-		writeProp("ro.build.type", "userdebug")
-	} else {
-		writeProp("ro.build.type", "user")
+	cmd.FlagWithInput("--build-hostname-file=", config.BuildHostnameFile(ctx))
+	// 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))
+	// 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"))
+	// 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"))
+	cmd.FlagWithInput("--platform-preview-sdk-fingerprint-file=", ApiFingerprintPath(ctx))
+	cmd.FlagWithInput("--product-config=", PathForModuleSrc(ctx, proptools.String(p.properties.Product_config)))
+	cmd.FlagWithOutput("--out=", p.outputFilePath)
 
-	// Currently, only a few properties are implemented to unblock microdroid use case.
-	// TODO(b/189164487): support below properties as well and replace build/make/tools/buildinfo.sh
-	/*
-		if $BOARD_USE_VBMETA_DIGTEST_IN_FINGERPRINT {
-			writeProp("ro.build.legacy.id", config.BuildID())
-		} else {
-			writeProp("ro.build.id", config.BuildId())
-		}
-		writeProp("ro.build.display.id", $BUILD_DISPLAY_ID)
-		writeProp("ro.build.version.incremental", $BUILD_NUMBER)
-		writeProp("ro.build.version.preview_sdk_fingerprint", $PLATFORM_PREVIEW_SDK_FINGERPRINT)
-		writeProp("ro.build.version.release_or_preview_display", $PLATFORM_DISPLAY_VERSION)
-		writeProp("ro.build.date", `$DATE`)
-		writeProp("ro.build.date.utc", `$DATE +%s`)
-		writeProp("ro.build.user", $BUILD_USERNAME)
-		writeProp("ro.build.host", $BUILD_HOSTNAME)
-		writeProp("ro.build.tags", $BUILD_VERSION_TAGS)
-		writeProp("ro.build.flavor", $TARGET_BUILD_FLAVOR)
-		// These values are deprecated, use "ro.product.cpu.abilist"
-		// instead (see below).
-		writeString("# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,")
-		writeString("# use ro.product.cpu.abilist instead.")
-		writeProp("ro.product.cpu.abi", $TARGET_CPU_ABI)
-		if [ -n "$TARGET_CPU_ABI2" ] {
-			writeProp("ro.product.cpu.abi2", $TARGET_CPU_ABI2)
-		}
-
-		if [ -n "$PRODUCT_DEFAULT_LOCALE" ] {
-			writeProp("ro.product.locale", $PRODUCT_DEFAULT_LOCALE)
-		}
-		writeProp("ro.wifi.channels", $PRODUCT_DEFAULT_WIFI_CHANNELS)
-		writeString("# ro.build.product is obsolete; use ro.product.device")
-		writeProp("ro.build.product", $TARGET_DEVICE)
-
-		writeString("# Do not try to parse description or thumbprint")
-		writeProp("ro.build.description", $PRIVATE_BUILD_DESC)
-		if [ -n "$BUILD_THUMBPRINT" ] {
-			writeProp("ro.build.thumbprint", $BUILD_THUMBPRINT)
-		}
-	*/
-
-	writeString("# end build properties")
-
-	WriteFileRule(ctx, p.outputFilePath, strings.Join(lines, "\n"))
+	rule.Build(ctx.ModuleName(), "generating buildinfo props")
 
 	if !p.installable() {
 		p.SkipInstall()
@@ -151,12 +107,8 @@
 	ctx.InstallFile(p.installPath, p.Name(), p.outputFilePath)
 }
 
-func (f *buildinfoPropModule) GenerateSingletonBuildActions(ctx SingletonContext) {
-	// does nothing; buildinfo_prop is a singeton because two buildinfo modules don't make sense.
-}
-
 func (p *buildinfoPropModule) AndroidMkEntries() []AndroidMkEntries {
-	return []AndroidMkEntries{AndroidMkEntries{
+	return []AndroidMkEntries{{
 		Class:      "ETC",
 		OutputFile: OptionalPathForPath(p.outputFilePath),
 		ExtraEntries: []AndroidMkExtraEntriesFunc{
@@ -172,7 +124,7 @@
 // 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() SingletonModule {
+func buildinfoPropFactory() Module {
 	module := &buildinfoPropModule{}
 	module.AddProperties(&module.properties)
 	InitAndroidModule(module)
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
new file mode 100644
index 0000000..6ea6654
--- /dev/null
+++ b/android/compliance_metadata.go
@@ -0,0 +1,314 @@
+// 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"
+	"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) 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, m.module.FilesToInstall()...)
+		installed = append(installed, m.katiInstalls.InstallPaths()...)
+		installed = append(installed, m.katiSymlinks.InstallPaths()...)
+		installed = append(installed, m.katiInitRcInstalls.InstallPaths()...)
+		installed = append(installed, m.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.moduleProvider(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 10c30d4..d16377d 100644
--- a/android/config.go
+++ b/android/config.go
@@ -22,7 +22,6 @@
 	"fmt"
 	"os"
 	"path/filepath"
-	"reflect"
 	"runtime"
 	"strconv"
 	"strings"
@@ -37,9 +36,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android/soongconfig"
-	"android/soong/bazel"
 	"android/soong/remoteexec"
-	"android/soong/starlark_fmt"
 )
 
 // Bool re-exports proptools.Bool for the android package.
@@ -114,6 +111,8 @@
 	GenerateDocFile
 )
 
+const testKeyDir = "build/make/target/product/security"
+
 // SoongOutDir returns the build output directory for the configuration.
 func (c Config) SoongOutDir() string {
 	return c.soongOutDir
@@ -199,6 +198,33 @@
 	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 {
@@ -227,6 +253,11 @@
 	return c.config.productVariables.GetBuildFlagBool("RELEASE_NDK_ABI_MONITORED")
 }
 
+// Enable read flag from new storage, for C/C++
+func (c Config) ReleaseReadFromNewStorageCc() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_READ_FROM_NEW_STORAGE_CC")
+}
+
 func (c Config) ReleaseHiddenApiExportableStubs() bool {
 	return c.config.productVariables.GetBuildFlagBool("RELEASE_HIDDEN_API_EXPORTABLE_STUBS") ||
 		Bool(c.config.productVariables.HiddenapiExportableStubs)
@@ -363,6 +394,7 @@
 	} else {
 		// Make a decoder for it
 		jsonDecoder := json.NewDecoder(configFileReader)
+		jsonDecoder.DisallowUnknownFields()
 		err = jsonDecoder.Decode(configurable)
 		if err != nil {
 			return fmt.Errorf("config file: %s did not parse correctly: %s", filename, err.Error())
@@ -405,7 +437,7 @@
 			proptools.StringPtr(String(configurable.Platform_sdk_codename))
 	}
 
-	return saveToBazelConfigFile(configurable, filepath.Dir(filename))
+	return nil
 }
 
 // atomically writes the config file in case two copies of soong_build are running simultaneously
@@ -439,81 +471,6 @@
 	return nil
 }
 
-type productVariableStarlarkRepresentation struct {
-	soongType   string
-	selectable  bool
-	archVariant bool
-}
-
-func saveToBazelConfigFile(config *ProductVariables, outDir string) error {
-	dir := filepath.Join(outDir, bazel.SoongInjectionDirName, "product_config")
-	err := createDirIfNonexistent(dir, os.ModePerm)
-	if err != nil {
-		return fmt.Errorf("Could not create dir %s: %s", dir, err)
-	}
-
-	allProductVariablesType := reflect.TypeOf((*ProductVariables)(nil)).Elem()
-	productVariablesInfo := make(map[string]productVariableStarlarkRepresentation)
-	p := variableProperties{}
-	t := reflect.TypeOf(p.Product_variables)
-	for i := 0; i < t.NumField(); i++ {
-		f := t.Field(i)
-		archVariant := proptools.HasTag(f, "android", "arch_variant")
-		if mainProductVariablesStructField, ok := allProductVariablesType.FieldByName(f.Name); ok {
-			productVariablesInfo[f.Name] = productVariableStarlarkRepresentation{
-				soongType:   stringRepresentationOfSimpleType(mainProductVariablesStructField.Type),
-				selectable:  true,
-				archVariant: archVariant,
-			}
-		} else {
-			panic("Unknown variable " + f.Name)
-		}
-	}
-
-	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variable_constants.bzl"), []byte(fmt.Sprintf(`
-# product_var_constant_info is a map of product variables to information about them. The fields are:
-# - soongType: The type of the product variable as it appears in soong's ProductVariables struct.
-#              examples are string, bool, int, *bool, *string, []string, etc. This may be an overly
-#              conservative estimation of the type, for example a *bool could oftentimes just be a
-#              bool that defaults to false.
-# - selectable: if this product variable can be selected on in Android.bp/build files. This means
-#               it's listed in the "variableProperties" soong struct. Currently all variables in
-#               this list are selectable because we only need the selectable ones at the moment,
-#               but the list may be expanded later.
-# - archVariant: If the variable is tagged as arch variant in the "variableProperties" struct.
-product_var_constant_info = %s
-product_var_constraints = [k for k, v in product_var_constant_info.items() if v.selectable]
-arch_variant_product_var_constraints = [k for k, v in product_var_constant_info.items() if v.selectable and v.archVariant]
-`, starlark_fmt.PrintAny(productVariablesInfo, 0))), 0644)
-	if err != nil {
-		return fmt.Errorf("Could not write .bzl config file %s", err)
-	}
-	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "BUILD"),
-		[]byte(bazel.GeneratedBazelFileWarning), 0644)
-	if err != nil {
-		return fmt.Errorf("Could not write BUILD config file %s", err)
-	}
-
-	return nil
-}
-
-func stringRepresentationOfSimpleType(ty reflect.Type) string {
-	switch ty.Kind() {
-	case reflect.String:
-		return "string"
-	case reflect.Bool:
-		return "bool"
-	case reflect.Int:
-		return "int"
-	case reflect.Slice:
-		return "[]" + stringRepresentationOfSimpleType(ty.Elem())
-	case reflect.Pointer:
-		return "*" + stringRepresentationOfSimpleType(ty.Elem())
-	default:
-		panic("unimplemented type: " + ty.Kind().String())
-	}
-}
-
 // NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that
 // use the android package.
 func NullConfig(outDir, soongOutDir string) Config {
@@ -811,15 +768,19 @@
 }
 
 func (c *config) IsEnvTrue(key string) bool {
-	value := c.Getenv(key)
+	value := strings.ToLower(c.Getenv(key))
 	return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true"
 }
 
 func (c *config) IsEnvFalse(key string) bool {
-	value := c.Getenv(key)
+	value := strings.ToLower(c.Getenv(key))
 	return value == "0" || value == "n" || value == "no" || value == "off" || value == "false"
 }
 
+func (c *config) TargetsJava21() bool {
+	return c.IsEnvTrue("EXPERIMENTAL_TARGET_JAVA_VERSION_21")
+}
+
 // EnvDeps returns the environment variables this build depends on. The first
 // call to this function blocks future reads from the environment.
 func (c *config) EnvDeps() map[string]string {
@@ -841,6 +802,21 @@
 	return String(c.productVariables.BuildId)
 }
 
+func (c *config) DisplayBuildNumber() bool {
+	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.
 //
@@ -852,6 +828,23 @@
 	return PathForOutput(ctx, String(c.productVariables.BuildNumberFile))
 }
 
+// BuildHostnameFile returns the path to a text file containing metadata
+// representing the current build's host name.
+func (c *config) BuildHostnameFile(ctx PathContext) Path {
+	return PathForOutput(ctx, String(c.productVariables.BuildHostnameFile))
+}
+
+// BuildThumbprintFile returns the path to a text file containing metadata
+// representing the current build's thumbprint.
+//
+// Rules that want to reference the build thumbprint 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 thumbprint. This ensures they
+// don't rebuild on every incremental build when the build thumbprint changes.
+func (c *config) BuildThumbprintFile(ctx PathContext) Path {
+	return PathForArbitraryOutput(ctx, "target", "product", c.DeviceName(), String(c.productVariables.BuildThumbprintFile))
+}
+
 // DeviceName returns the name of the current device target.
 // TODO: take an AndroidModuleContext to select the device name for multi-device builds
 func (c *config) DeviceName() string {
@@ -873,6 +866,10 @@
 	return c.productVariables.DeviceProduct != nil
 }
 
+func (c *config) DeviceAbi() []string {
+	return c.productVariables.DeviceAbi
+}
+
 func (c *config) DeviceResourceOverlays() []string {
 	return c.productVariables.DeviceResourceOverlays
 }
@@ -881,6 +878,10 @@
 	return c.productVariables.ProductResourceOverlays
 }
 
+func (c *config) PlatformDisplayVersionName() string {
+	return String(c.productVariables.Platform_display_version_name)
+}
+
 func (c *config) PlatformVersionName() string {
 	return String(c.productVariables.Platform_version_name)
 }
@@ -918,7 +919,11 @@
 }
 
 func (c *config) PlatformMinSupportedTargetSdkVersion() string {
-	return String(c.productVariables.Platform_min_supported_target_sdk_version)
+	var val, ok = c.productVariables.BuildFlags["RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION"]
+	if !ok {
+		return ""
+	}
+	return val
 }
 
 func (c *config) PlatformBaseOS() string {
@@ -1038,7 +1043,7 @@
 	if defaultCert != "" {
 		return PathForSource(ctx, filepath.Dir(defaultCert))
 	}
-	return PathForSource(ctx, "build/make/target/product/security")
+	return PathForSource(ctx, testKeyDir)
 }
 
 func (c *config) DefaultAppCertificate(ctx PathContext) (pem, key SourcePath) {
@@ -1050,10 +1055,34 @@
 	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") {
+		return "test-keys"
+	}
+	return "dev-keys"
+}
+
 func (c *config) ApexKeyDir(ctx ModuleContext) SourcePath {
 	// TODO(b/121224311): define another variable such as TARGET_APEX_KEY_OVERRIDE
 	defaultCert := String(c.productVariables.DefaultAppCertificate)
-	if defaultCert == "" || filepath.Dir(defaultCert) == "build/make/target/product/security" {
+	if defaultCert == "" || filepath.Dir(defaultCert) == testKeyDir {
 		// When defaultCert is unset or is set to the testkeys path, use the APEX keys
 		// that is under the module dir
 		return pathForModuleSrc(ctx)
@@ -1112,6 +1141,10 @@
 	return Bool(c.productVariables.Eng)
 }
 
+func (c *config) BuildType() string {
+	return String(c.productVariables.BuildType)
+}
+
 // DevicePrimaryArchType returns the ArchType for the first configured device architecture, or
 // Common if there are no device architectures.
 func (c *config) DevicePrimaryArchType() ArchType {
@@ -1277,10 +1310,6 @@
 	return c.productVariables.SourceRootDirs
 }
 
-func (c *config) IncludeTags() []string {
-	return c.productVariables.IncludeTags
-}
-
 func (c *config) HostStaticBinaries() bool {
 	return Bool(c.productVariables.HostStaticBinaries)
 }
@@ -1339,10 +1368,6 @@
 	return String(c.productVariables.PrebuiltHiddenApiDir)
 }
 
-func (c *config) IsVndkDeprecated() bool {
-	return !Bool(c.productVariables.KeepVndk)
-}
-
 func (c *config) VendorApiLevel() string {
 	return String(c.productVariables.VendorApiLevel)
 }
@@ -1366,6 +1391,11 @@
 	return strconv.Itoa(vendorApiLevel - 100)
 }
 
+func IsTrunkStableVendorApiLevel(level string) bool {
+	levelInt, err := strconv.Atoi(level)
+	return err == nil && levelInt >= 202404
+}
+
 func (c *config) VendorApiLevelFrozen() bool {
 	return c.productVariables.GetBuildFlagBool("RELEASE_BOARD_API_LEVEL_FROZEN")
 }
@@ -1393,10 +1423,6 @@
 	return "vendor"
 }
 
-func (c *deviceConfig) VndkVersion() string {
-	return String(c.config.productVariables.DeviceVndkVersion)
-}
-
 func (c *deviceConfig) RecoverySnapshotVersion() string {
 	return String(c.config.productVariables.RecoverySnapshotVersion)
 }
@@ -1405,18 +1431,10 @@
 	return StringDefault(c.config.productVariables.DeviceCurrentApiLevelForVendorModules, "current")
 }
 
-func (c *deviceConfig) PlatformVndkVersion() string {
-	return String(c.config.productVariables.Platform_vndk_version)
-}
-
 func (c *deviceConfig) ExtraVndkVersions() []string {
 	return c.config.productVariables.ExtraVndkVersions
 }
 
-func (c *deviceConfig) VndkUseCoreVariant() bool {
-	return Bool(c.config.productVariables.VndkUseCoreVariant) && Bool(c.config.productVariables.KeepVndk)
-}
-
 func (c *deviceConfig) SystemSdkVersions() []string {
 	return c.config.productVariables.DeviceSystemSdkVersions
 }
@@ -1863,10 +1881,10 @@
 }
 
 func (c *deviceConfig) ShippingApiLevel() ApiLevel {
-	if c.config.productVariables.ShippingApiLevel == nil {
+	if c.config.productVariables.Shipping_api_level == nil {
 		return NoneApiLevel
 	}
-	apiLevel, _ := strconv.Atoi(*c.config.productVariables.ShippingApiLevel)
+	apiLevel, _ := strconv.Atoi(*c.config.productVariables.Shipping_api_level)
 	return uncheckedFinalApiLevel(apiLevel)
 }
 
@@ -1914,6 +1932,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
 }
@@ -2055,16 +2077,19 @@
 		"RELEASE_APEX_CONTRIBUTIONS_IPSEC",
 		"RELEASE_APEX_CONTRIBUTIONS_MEDIA",
 		"RELEASE_APEX_CONTRIBUTIONS_MEDIAPROVIDER",
+		"RELEASE_APEX_CONTRIBUTIONS_MODULE_METADATA",
 		"RELEASE_APEX_CONTRIBUTIONS_NETWORKSTACKGOOGLE",
 		"RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS",
 		"RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION",
 		"RELEASE_APEX_CONTRIBUTIONS_PERMISSION",
+		"RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS",
 		"RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING",
 		"RELEASE_APEX_CONTRIBUTIONS_RESOLV",
 		"RELEASE_APEX_CONTRIBUTIONS_SCHEDULING",
 		"RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS",
 		"RELEASE_APEX_CONTRIBUTIONS_SWCODEC",
 		"RELEASE_APEX_CONTRIBUTIONS_STATSD",
+		"RELEASE_APEX_CONTRIBUTIONS_TELEMETRY_TVP",
 		"RELEASE_APEX_CONTRIBUTIONS_TZDATA",
 		"RELEASE_APEX_CONTRIBUTIONS_UWB",
 		"RELEASE_APEX_CONTRIBUTIONS_WIFI",
@@ -2083,6 +2108,22 @@
 	return ret
 }
 
-func (c *config) BuildIgnoreApexContributionContents() []string {
+func (c *config) BuildIgnoreApexContributionContents() *bool {
 	return c.productVariables.BuildIgnoreApexContributionContents
 }
+
+func (c *config) ProductLocales() []string {
+	return c.productVariables.ProductLocales
+}
+
+func (c *config) ProductDefaultWifiChannels() []string {
+	return c.productVariables.ProductDefaultWifiChannels
+}
+
+func (c *config) BoardUseVbmetaDigestInFingerprint() bool {
+	return Bool(c.productVariables.BoardUseVbmetaDigestInFingerprint)
+}
+
+func (c *config) OemProperties() []string {
+	return c.productVariables.OemProperties
+}
diff --git a/android/config_bp2build.go b/android/config_bp2build.go
deleted file mode 100644
index 4c2fb5e..0000000
--- a/android/config_bp2build.go
+++ /dev/null
@@ -1,157 +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 android
-
-import (
-	"strings"
-
-	"github.com/google/blueprint"
-)
-
-// ExportedVariables is a collection of interdependent configuration variables
-type ExportedVariables struct {
-	// Maps containing toolchain variables that are independent of the
-	// environment variables of the build.
-	exportedStringVars         ExportedStringVariables
-	exportedStringListVars     ExportedStringListVariables
-	exportedStringListDictVars ExportedStringListDictVariables
-
-	exportedVariableReferenceDictVars ExportedVariableReferenceDictVariables
-
-	/// Maps containing variables that are dependent on the build config.
-	exportedConfigDependingVars ExportedConfigDependingVariables
-
-	pctx PackageContext
-}
-
-// NewExportedVariables creats an empty ExportedVariables struct with non-nil maps
-func NewExportedVariables(pctx PackageContext) ExportedVariables {
-	return ExportedVariables{
-		exportedStringVars:                ExportedStringVariables{},
-		exportedStringListVars:            ExportedStringListVariables{},
-		exportedStringListDictVars:        ExportedStringListDictVariables{},
-		exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{},
-		exportedConfigDependingVars:       ExportedConfigDependingVariables{},
-		pctx:                              pctx,
-	}
-}
-
-// ExportStringStaticVariable declares a static string variable and exports it to
-// Bazel's toolchain.
-func (ev ExportedVariables) ExportStringStaticVariable(name string, value string) {
-	ev.pctx.StaticVariable(name, value)
-	ev.exportedStringVars.set(name, value)
-}
-
-// ExportStringListStaticVariable declares a static variable and exports it to
-// Bazel's toolchain.
-func (ev ExportedVariables) ExportStringListStaticVariable(name string, value []string) {
-	ev.pctx.StaticVariable(name, strings.Join(value, " "))
-	ev.exportedStringListVars.set(name, value)
-}
-
-// ExportVariableConfigMethod declares a variable whose value is evaluated at
-// runtime via a function with access to the Config and exports it to Bazel's
-// toolchain.
-func (ev ExportedVariables) ExportVariableConfigMethod(name string, method interface{}) blueprint.Variable {
-	ev.exportedConfigDependingVars.set(name, method)
-	return ev.pctx.VariableConfigMethod(name, method)
-}
-
-// ExportSourcePathVariable declares a static "source path" variable and exports
-// it to Bazel's toolchain.
-func (ev ExportedVariables) ExportSourcePathVariable(name string, value string) {
-	ev.pctx.SourcePathVariable(name, value)
-	ev.exportedStringVars.set(name, value)
-}
-
-// ExportVariableFuncVariable declares a variable whose value is evaluated at
-// runtime via a function and exports it to Bazel's toolchain.
-func (ev ExportedVariables) ExportVariableFuncVariable(name string, f func() string) {
-	ev.exportedConfigDependingVars.set(name, func(config Config) string {
-		return f()
-	})
-	ev.pctx.VariableFunc(name, func(PackageVarContext) string {
-		return f()
-	})
-}
-
-// ExportString only exports a variable to Bazel, but does not declare it in Soong
-func (ev ExportedVariables) ExportString(name string, value string) {
-	ev.exportedStringVars.set(name, value)
-}
-
-// ExportStringList only exports a variable to Bazel, but does not declare it in Soong
-func (ev ExportedVariables) ExportStringList(name string, value []string) {
-	ev.exportedStringListVars.set(name, value)
-}
-
-// ExportStringListDict only exports a variable to Bazel, but does not declare it in Soong
-func (ev ExportedVariables) ExportStringListDict(name string, value map[string][]string) {
-	ev.exportedStringListDictVars.set(name, value)
-}
-
-// ExportVariableReferenceDict only exports a variable to Bazel, but does not declare it in Soong
-func (ev ExportedVariables) ExportVariableReferenceDict(name string, value map[string]string) {
-	ev.exportedVariableReferenceDictVars.set(name, value)
-}
-
-// ExportedConfigDependingVariables is a mapping of variable names to functions
-// of type func(config Config) string which return the runtime-evaluated string
-// value of a particular variable
-type ExportedConfigDependingVariables map[string]interface{}
-
-func (m ExportedConfigDependingVariables) set(k string, v interface{}) {
-	m[k] = v
-}
-
-// ExportedStringVariables is a mapping of variable names to string values
-type ExportedStringVariables map[string]string
-
-func (m ExportedStringVariables) set(k string, v string) {
-	m[k] = v
-}
-
-// ExportedStringListVariables is a mapping of variable names to a list of strings
-type ExportedStringListVariables map[string][]string
-
-func (m ExportedStringListVariables) set(k string, v []string) {
-	m[k] = v
-}
-
-// ExportedStringListDictVariables is a mapping from variable names to a
-// dictionary which maps keys to lists of strings
-type ExportedStringListDictVariables map[string]map[string][]string
-
-func (m ExportedStringListDictVariables) set(k string, v map[string][]string) {
-	m[k] = v
-}
-
-// ExportedVariableReferenceDictVariables is a mapping from variable names to a
-// dictionary which references previously defined variables. This is used to
-// create a Starlark output such as:
-//
-//	string_var1 = "string1
-//	var_ref_dict_var1 = {
-//		"key1": string_var1
-//	}
-//
-// This type of variable collection must be expanded last so that it recognizes
-// previously defined variables.
-type ExportedVariableReferenceDictVariables map[string]map[string]string
-
-func (m ExportedVariableReferenceDictVariables) set(k string, v map[string]string) {
-	m[k] = v
-}
diff --git a/android/config_test.go b/android/config_test.go
index 7d327a2..ca7c7f8 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -125,6 +125,43 @@
 	}
 }
 
+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(
+			FixtureModifyProductVariables(func(vars FixtureProductVariables) {
+				if vars.BuildFlags == nil {
+					vars.BuildFlags = make(map[string]string)
+				}
+				vars.BuildFlags["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
new file mode 100644
index 0000000..dad42fa
--- /dev/null
+++ b/android/configurable_properties.go
@@ -0,0 +1,28 @@
+package android
+
+import "github.com/google/blueprint/proptools"
+
+// CreateSelectOsToBool is a utility function that makes it easy to create a
+// Configurable property value that maps from os to a bool. Use an empty string
+// to indicate a "default" case.
+func CreateSelectOsToBool(cases map[string]*bool) proptools.Configurable[bool] {
+	var resultCases []proptools.ConfigurableCase[bool]
+	for pattern, value := range cases {
+		if pattern == "" {
+			resultCases = append(resultCases, proptools.NewConfigurableCase(
+				[]proptools.ConfigurablePattern{proptools.NewDefaultConfigurablePattern()},
+				value,
+			))
+		} else {
+			resultCases = append(resultCases, proptools.NewConfigurableCase(
+				[]proptools.ConfigurablePattern{proptools.NewStringConfigurablePattern(pattern)},
+				value,
+			))
+		}
+	}
+
+	return proptools.NewConfigurable(
+		[]proptools.ConfigurableCondition{proptools.NewConfigurableCondition("os", nil)},
+		resultCases,
+	)
+}
diff --git a/android/container.go b/android/container.go
new file mode 100644
index 0000000..c4fdd9c
--- /dev/null
+++ b/android/container.go
@@ -0,0 +1,233 @@
+// 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 (
+	"reflect"
+	"slices"
+
+	"github.com/google/blueprint"
+)
+
+type StubsAvailableModule interface {
+	IsStubsModule() bool
+}
+
+// Returns true if the dependency module is a stubs module
+var depIsStubsModule = func(_ ModuleContext, _, dep Module) bool {
+	if stubsModule, ok := dep.(StubsAvailableModule); ok {
+		return stubsModule.IsStubsModule()
+	}
+	return false
+}
+
+// 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
+)
+
+// Functions cannot be used as a value passed in providers, because functions are not
+// hashable. As a workaround, the exceptionHandleFunc enum values are passed using providers,
+// and the corresponding functions are called from this map.
+var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]func(ModuleContext, Module, Module) bool{
+	checkStubs: depIsStubsModule,
+}
+
+type InstallableModule interface {
+	EnforceApiContainerChecks() bool
+}
+
+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{},
+			},
+		},
+	}
+	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{},
+			},
+		},
+	}
+	ApexContainer = initializeApexContainer()
+	CtsContainer  = &container{
+		name: "cts",
+		restricted: []restriction{
+			{
+				dependency: SystemContainer,
+				errorMessage: "CTS module should not depend on the modules belonging to the " +
+					"system partition, including \"framework\". Depending on the system " +
+					"partition may lead to disclosure of implementation details and regression " +
+					"due to API changes across platform versions. Try depending on the stubs instead.",
+				allowedExceptions: []exceptionHandleFuncLabel{checkStubs},
+			},
+		},
+	}
+)
+
+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},
+			},
+		},
+	}
+
+	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},
+	})
+
+	return apexContainer
+}
+
+type ContainersInfo struct {
+	belongingContainers []*container
+
+	belongingApexes []ApexInfo
+}
+
+func (c *ContainersInfo) BelongingContainers() []*container {
+	return c.belongingContainers
+}
+
+var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]()
+
+// Determines if the module can be installed in the system partition or not.
+// Logic is identical to that of modulePartition(...) defined in paths.go
+func installInSystemPartition(ctx ModuleContext) bool {
+	module := ctx.Module()
+	return !module.InstallInTestcases() &&
+		!module.InstallInData() &&
+		!module.InstallInRamdisk() &&
+		!module.InstallInVendorRamdisk() &&
+		!module.InstallInDebugRamdisk() &&
+		!module.InstallInRecovery() &&
+		!module.InstallInVendor() &&
+		!module.InstallInOdm() &&
+		!module.InstallInProduct() &&
+		determineModuleKind(module.base(), ctx.blueprintBaseModuleContext()) == platformModule
+}
+
+func generateContainerInfo(ctx ModuleContext) ContainersInfo {
+	inSystem := installInSystemPartition(ctx)
+	inProduct := ctx.Module().InstallInProduct()
+	inVendor := ctx.Module().InstallInVendor()
+	inCts := false
+	inApex := false
+
+	if m, ok := ctx.Module().(ImageInterface); ok {
+		inProduct = inProduct || m.ProductVariantNeeded(ctx)
+		inVendor = inVendor || m.VendorVariantNeeded(ctx)
+	}
+
+	props := ctx.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") {
+				inCts = true
+			}
+		}
+	}
+
+	var belongingApexes []ApexInfo
+	if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
+		belongingApexes = apexInfo.ApexInfos
+		inApex = true
+	}
+
+	containers := []*container{}
+	if inSystem {
+		containers = append(containers, SystemContainer)
+	}
+	if inProduct {
+		containers = append(containers, ProductContainer)
+	}
+	if inVendor {
+		containers = append(containers, VendorContainer)
+	}
+	if inCts {
+		containers = append(containers, CtsContainer)
+	}
+	if inApex {
+		containers = append(containers, ApexContainer)
+	}
+
+	return ContainersInfo{
+		belongingContainers: containers,
+		belongingApexes:     belongingApexes,
+	}
+}
+
+func setContainerInfo(ctx ModuleContext) {
+	if _, ok := ctx.Module().(InstallableModule); ok {
+		containersInfo := generateContainerInfo(ctx)
+		SetProvider(ctx, ContainersInfoProvider, containersInfo)
+	}
+}
diff --git a/android/defs.go b/android/defs.go
index dab012d..78cdea2 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -20,8 +20,7 @@
 )
 
 var (
-	pctx         = NewPackageContext("android/soong/android")
-	exportedVars = NewExportedVariables(pctx)
+	pctx = NewPackageContext("android/soong/android")
 
 	cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks",
 		Config.CpPreserveSymlinksFlags)
@@ -104,16 +103,6 @@
 			Description: "concatenate files to $out",
 		})
 
-	// ubuntu 14.04 offcially use dash for /bin/sh, and its builtin echo command
-	// doesn't support -e option. Therefore we force to use /bin/bash when writing out
-	// content to file.
-	writeFile = pctx.AndroidStaticRule("writeFile",
-		blueprint.RuleParams{
-			Command:     `rm -f $out && /bin/bash -c 'echo -e -n "$$0" > $out' $content`,
-			Description: "writing file $out",
-		},
-		"content")
-
 	// Used only when USE_GOMA=true is set, to restrict non-goma jobs to the local parallelism value
 	localPool = blueprint.NewBuiltinPool("local_pool")
 
@@ -130,9 +119,6 @@
 	pctx.VariableFunc("RBEWrapper", func(ctx PackageVarContext) string {
 		return ctx.Config().RBEWrapper()
 	})
-
-	exportedVars.ExportStringList("NeverAllowNotInIncludeDir", neverallowNotInIncludeDir)
-	exportedVars.ExportStringList("NeverAllowNoUseIncludeDir", neverallowNoUseIncludeDir)
 }
 
 // GlobToListFileRule creates a rule that writes a list of files matching a pattern to a file.
diff --git a/android/early_module_context.go b/android/early_module_context.go
index 8f75773..23f4c90 100644
--- a/android/early_module_context.go
+++ b/android/early_module_context.go
@@ -15,9 +15,10 @@
 package android
 
 import (
-	"github.com/google/blueprint"
 	"os"
 	"text/scanner"
+
+	"github.com/google/blueprint"
 )
 
 // EarlyModuleContext provides methods that can be called early, as soon as the properties have
@@ -54,6 +55,9 @@
 	// PropertyErrorf reports an error at the line number of a property in the module definition.
 	PropertyErrorf(property, fmt string, args ...interface{})
 
+	// OtherModulePropertyErrorf reports an error at the line number of a property in the given module definition.
+	OtherModulePropertyErrorf(module Module, property, fmt string, args ...interface{})
+
 	// Failed returns true if any errors have been reported.  In most cases the module can continue with generating
 	// build rules after an error, allowing it to report additional errors in a single run, but in cases where the error
 	// has prevented the module from creating necessary data it can return early when Failed returns true.
@@ -167,3 +171,7 @@
 func (e *earlyModuleContext) Namespace() *Namespace {
 	return e.EarlyModuleContext.Namespace().(*Namespace)
 }
+
+func (e *earlyModuleContext) OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{}) {
+	e.EarlyModuleContext.OtherModulePropertyErrorf(module, property, fmt, args...)
+}
diff --git a/android/filegroup.go b/android/filegroup.go
index 86d7b4b..ff0f74e 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -19,6 +19,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -36,9 +37,9 @@
 
 type fileGroupProperties struct {
 	// srcs lists files that will be included in this filegroup
-	Srcs []string `android:"path"`
+	Srcs proptools.Configurable[[]string] `android:"path"`
 
-	Exclude_srcs []string `android:"path"`
+	Exclude_srcs proptools.Configurable[[]string] `android:"path"`
 
 	// The base path to the files.  May be used by other modules to determine which portion
 	// of the path to use.  For example, when a filegroup is used as data in a cc_test rule,
@@ -56,9 +57,6 @@
 	DefaultableModuleBase
 	properties fileGroupProperties
 	srcs       Paths
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]Paths
 }
 
 var _ SourceFileProducer = (*fileGroup)(nil)
@@ -92,12 +90,11 @@
 }
 
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
-	fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
+	fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs.GetOrDefault(ctx, nil), fg.properties.Exclude_srcs.GetOrDefault(ctx, nil))
 	if fg.properties.Path != nil {
 		fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
 	}
 	SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: fg.srcs.Strings()})
-	CollectDependencyAconfigFiles(ctx, &fg.mergedAconfigFiles)
 
 	var aconfigDeclarations []string
 	var intermediateCacheOutputPaths Paths
diff --git a/android/gen_notice.go b/android/gen_notice.go
index 1acc638..9adde9e 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -62,7 +62,7 @@
 				if mod == nil {
 					continue
 				}
-				if !mod.Enabled() { // don't depend on variants without build rules
+				if !mod.Enabled(ctx) { // don't depend on variants without build rules
 					continue
 				}
 				modules = append(modules, mod)
@@ -176,6 +176,7 @@
 	}
 	out := m.getStem() + m.getSuffix()
 	m.output = PathForModuleOut(ctx, out).OutputPath
+	ctx.SetOutputFiles(Paths{m.output}, "")
 }
 
 func GenNoticeFactory() Module {
@@ -193,16 +194,6 @@
 	return module
 }
 
-var _ OutputFileProducer = (*genNoticeModule)(nil)
-
-// Implements OutputFileProducer
-func (m *genNoticeModule) OutputFiles(tag string) (Paths, error) {
-	if tag == "" {
-		return Paths{m.output}, nil
-	}
-	return nil, fmt.Errorf("unrecognized tag %q", tag)
-}
-
 var _ AndroidMkEntriesProvider = (*genNoticeModule)(nil)
 
 // Implements AndroidMkEntriesProvider
diff --git a/android/image.go b/android/image.go
index bc6b8cd..0f03107 100644
--- a/android/image.go
+++ b/android/image.go
@@ -19,6 +19,12 @@
 	// 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
 
@@ -44,12 +50,19 @@
 	ExtraImageVariations(ctx BaseModuleContext) []string
 
 	// SetImageVariation is called for each newly created image variant. The receiver is the original
-	// module, "variation" is the name of the newly created variant and "module" is the newly created
-	// variant itself.
-	SetImageVariation(ctx BaseModuleContext, variation string, module Module)
+	// module, "variation" is the name of the newly created variant. "variation" is set on the receiver.
+	SetImageVariation(ctx BaseModuleContext, variation string)
 }
 
 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.
@@ -95,6 +108,12 @@
 		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...)
@@ -106,7 +125,7 @@
 		mod := ctx.CreateVariations(variations...)
 		for i, v := range variations {
 			mod[i].base().setImageVariation(v)
-			m.SetImageVariation(ctx, v, mod[i])
+			mod[i].(ImageInterface).SetImageVariation(ctx, v)
 		}
 	}
 }
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 463fd07..8056189 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -36,7 +36,7 @@
 func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
 	base := ctx.Module().base()
 
-	if !base.Enabled() {
+	if !base.Enabled(ctx) {
 		return
 	}
 
@@ -45,8 +45,7 @@
 	}
 
 	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...)
 	}
 
@@ -69,7 +68,7 @@
 		if dep == nil {
 			return
 		}
-		if !dep.Enabled() {
+		if !dep.Enabled(ctx) {
 			return
 		}
 
@@ -77,6 +76,11 @@
 		if ctx.OtherModuleDependencyTag(dep) == DefaultsDepTag {
 			return
 		}
+		// The required dependencies just say modules A and B should be installed together.
+		// It doesn't mean that one is built using the other.
+		if ctx.OtherModuleDependencyTag(dep) == RequiredDepTag {
+			return
+		}
 
 		if info, ok := OtherModuleProvider(ctx, dep, LicenseMetadataProvider); ok {
 			allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
@@ -190,7 +194,7 @@
 
 	for _, path := range paths {
 		switch path.Ext() {
-		case ".zip", ".tar", ".tgz", ".tar.gz", ".img", ".srcszip", ".apex":
+		case ".zip", ".tar", ".tgz", ".tar.gz", ".img", ".srcszip", ".apex", ".capex":
 			return true
 		}
 	}
diff --git a/android/logtags.go b/android/logtags.go
new file mode 100644
index 0000000..d11cccf
--- /dev/null
+++ b/android/logtags.go
@@ -0,0 +1,56 @@
+// 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"
+
+func init() {
+	RegisterParallelSingletonType("logtags", LogtagsSingleton)
+}
+
+type LogtagsInfo struct {
+	Logtags Paths
+}
+
+var LogtagsProviderKey = blueprint.NewProvider[*LogtagsInfo]()
+
+func LogtagsSingleton() Singleton {
+	return &logtagsSingleton{}
+}
+
+type logtagsSingleton struct{}
+
+func MergedLogtagsPath(ctx PathContext) OutputPath {
+	return PathForIntermediates(ctx, "all-event-log-tags.txt")
+}
+
+func (l *logtagsSingleton) GenerateBuildActions(ctx SingletonContext) {
+	var allLogtags Paths
+	ctx.VisitAllModules(func(module Module) {
+		if !module.ExportedToMake() {
+			return
+		}
+		if logtagsInfo, ok := SingletonModuleProvider(ctx, module, LogtagsProviderKey); ok {
+			allLogtags = append(allLogtags, logtagsInfo.Logtags...)
+		}
+	})
+
+	builder := NewRuleBuilder(pctx, ctx)
+	builder.Command().
+		BuiltTool("merge-event-log-tags").
+		FlagWithOutput("-o ", MergedLogtagsPath(ctx)).
+		Inputs(SortedUniquePaths(allLogtags))
+	builder.Build("all-event-log-tags.txt", "merge logtags")
+}
diff --git a/android/makevars.go b/android/makevars.go
index 4039e7e..f92f458 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -98,6 +98,7 @@
 	BlueprintFile(module blueprint.Module) string
 
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
+	OtherModulePropertyErrorf(module Module, property, format string, args ...interface{})
 	Errorf(format string, args ...interface{})
 
 	VisitAllModules(visit func(Module))
@@ -265,7 +266,7 @@
 	}
 
 	ctx.VisitAllModules(func(m Module) {
-		if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled() {
+		if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled(ctx) {
 			mctx := &makeVarsContext{
 				SingletonContext: ctx,
 			}
@@ -472,7 +473,7 @@
 
 # Values written by Soong to generate install rules that can be amended by Kati.
 
-
+EXTRA_INSTALL_ZIPS :=
 `)
 
 	preserveSymlinksFlag := "-d"
@@ -506,9 +507,13 @@
 		if extraFiles := install.extraFiles; extraFiles != nil {
 			fmt.Fprintf(buf, "\t( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} ) || \\\n", extraFiles.dir.String(), extraFiles.zip.String())
 			fmt.Fprintf(buf, "\t  ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )\n")
+			fmt.Fprintf(buf, "EXTRA_INSTALL_ZIPS += %s:%s:%s\n", install.to.String(), extraFiles.dir.String(), extraFiles.zip.String())
 		}
+
 		fmt.Fprintln(buf)
 	}
+	fmt.Fprintf(buf, ".KATI_READONLY := EXTRA_INSTALL_ZIPS\n")
+	fmt.Fprintf(buf, "$(KATI_visibility_prefix EXTRA_INSTALL_ZIPS,build/make/core/Makefile)\n")
 
 	for _, symlink := range symlinks {
 		fmt.Fprintf(buf, "%s:", symlink.to.String())
diff --git a/android/module.go b/android/module.go
index 000476c..dd56031 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,9 +15,6 @@
 package android
 
 import (
-	"crypto/md5"
-	"encoding/hex"
-	"encoding/json"
 	"fmt"
 	"net/url"
 	"path/filepath"
@@ -26,8 +23,6 @@
 	"sort"
 	"strings"
 
-	"android/soong/bazel"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -60,7 +55,7 @@
 
 	base() *ModuleBase
 	Disable()
-	Enabled() bool
+	Enabled(ctx ConfigAndErrorContext) bool
 	Target() Target
 	MultiTargets() []Target
 
@@ -113,7 +108,7 @@
 	// Get information about the properties that can contain visibility rules.
 	visibilityProperties() []visibilityProperty
 
-	RequiredModuleNames() []string
+	RequiredModuleNames(ctx ConfigAndErrorContext) []string
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
 
@@ -123,6 +118,8 @@
 	// 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
 }
 
 // Qualified id for a module
@@ -247,31 +244,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
@@ -285,7 +257,7 @@
 	// but are not usually required (e.g. superceded by a prebuilt) should not be
 	// disabled as that will prevent them from being built by the checkbuild target
 	// and so prevent early detection of changes that have broken those modules.
-	Enabled *bool `android:"arch_variant"`
+	Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 
 	// Controls the visibility of this module to other modules. Allowable values are one or more of
 	// these formats:
@@ -420,7 +392,7 @@
 	Vintf_fragments []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"`
@@ -433,6 +405,10 @@
 	// Set by osMutator
 	CompileOS OsType `blueprint:"mutated"`
 
+	// Set to true after the arch mutator has run on this module and set CompileTarget,
+	// CompileMultiTargets, and CompilePrimary
+	ArchReady bool `blueprint:"mutated"`
+
 	// The Target of artifacts that this module variant is responsible for creating.
 	//
 	// Set by archMutator
@@ -478,6 +454,11 @@
 	// 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.
+	No_full_install *bool
+
 	// When HideFromMake is set to true, no entry for this variant will be emitted in the
 	// generated Android.mk file.
 	HideFromMake bool `blueprint:"mutated"`
@@ -514,14 +495,6 @@
 	// 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"`
 }
@@ -542,6 +515,15 @@
 
 var teamDepTag = TeamDepTagType{}
 
+// Dependency tag for required, host_required, and target_required modules.
+var RequiredDepTag = struct {
+	blueprint.BaseDependencyTag
+	InstallAlwaysNeededDependencyTag
+	// Requiring disabled module has been supported (as a side effect of this being implemented
+	// in Make). We may want to make it an error, but for now, let's keep the existing behavior.
+	AlwaysAllowDisabledModuleDependencyTag
+}{}
+
 // CommonTestOptions represents the common `test_options` properties in
 // Android.bp.
 type CommonTestOptions struct {
@@ -829,9 +811,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
@@ -882,6 +861,9 @@
 	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
@@ -892,6 +874,14 @@
 	// 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
+
+	// complianceMetadataInfo is for different module types to dump metadata.
+	// See android.ModuleContext interface.
+	complianceMetadataInfo *ComplianceMetadataInfo
 }
 
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
@@ -1007,6 +997,102 @@
 	if m.Team() != "" {
 		ctx.AddDependency(ctx.Module(), teamDepTag, m.Team())
 	}
+
+	// TODO(jiyong): remove below case. This is to work around build errors happening
+	// on branches with reduced manifest like aosp_kernel-build-tools.
+	// In the branch, a build error occurs as follows.
+	// 1. aosp_kernel-build-tools is a reduced manifest branch. It doesn't have some git
+	// projects like external/bouncycastle
+	// 2. `boot_signer` is `required` by modules like `build_image` which is explicitly list as
+	// the top-level build goal (in the shell file that invokes Soong).
+	// 3. `boot_signer` depends on `bouncycastle-unbundled` which is in the missing git project.
+	// 4. aosp_kernel-build-tools invokes soong with `--skip-make`. Therefore, the absence of
+	// ALLOW_MISSING_DEPENDENCIES didn't cause a problem.
+	// 5. Now, since Soong understands `required` deps, it tries to build `boot_signer` and the
+	// absence of external/bouncycastle fails the build.
+	//
+	// Unfortunately, there's no way for Soong to correctly determine if it's running in a
+	// reduced manifest branch. Instead, here, we use the absence of DeviceArch or DeviceName as
+	// a strong signal, because that's very common across reduced manifest branches.
+	pv := ctx.Config().productVariables
+	fullManifest := pv.DeviceArch != nil && pv.DeviceName != nil
+	if fullManifest {
+		addRequiredDeps(ctx)
+	}
+}
+
+// addRequiredDeps adds required, target_required, and host_required as dependencies.
+func addRequiredDeps(ctx BottomUpMutatorContext) {
+	addDep := func(target Target, depName string) {
+		if !ctx.OtherModuleExists(depName) {
+			if ctx.Config().AllowMissingDependencies() {
+				return
+			}
+		}
+
+		// If Android native module requires another Android native module, ensure that
+		// they have the same bitness. This mimics the policy in select-bitness-of-required-modules
+		// in build/make/core/main.mk.
+		// TODO(jiyong): the Make-side does this only when the required module is a shared
+		// library or a native test.
+		bothInAndroid := ctx.Device() && target.Os.Class == Device
+		nativeArch := InList(ctx.Arch().ArchType.Multilib, []string{"lib32", "lib64"}) &&
+			InList(target.Arch.ArchType.Multilib, []string{"lib32", "lib64"})
+		sameBitness := ctx.Arch().ArchType.Multilib == target.Arch.ArchType.Multilib
+		if bothInAndroid && nativeArch && !sameBitness {
+			return
+		}
+
+		// ... also don't make a dependency between native bridge arch and non-native bridge
+		// arches. b/342945184
+		if ctx.Target().NativeBridge != target.NativeBridge {
+			return
+		}
+
+		variation := target.Variations()
+		if ctx.OtherModuleFarDependencyVariantExists(variation, depName) {
+			ctx.AddFarVariationDependencies(variation, RequiredDepTag, depName)
+		}
+	}
+
+	var deviceTargets []Target
+	deviceTargets = append(deviceTargets, ctx.Config().Targets[Android]...)
+	deviceTargets = append(deviceTargets, ctx.Config().AndroidCommonTarget)
+
+	var hostTargets []Target
+	hostTargets = append(hostTargets, ctx.Config().Targets[ctx.Config().BuildOS]...)
+	hostTargets = append(hostTargets, ctx.Config().BuildOSCommonTarget)
+
+	if ctx.Device() {
+		for _, depName := range ctx.Module().RequiredModuleNames(ctx) {
+			for _, target := range deviceTargets {
+				addDep(target, depName)
+			}
+		}
+		for _, depName := range ctx.Module().HostRequiredModuleNames() {
+			for _, target := range hostTargets {
+				addDep(target, depName)
+			}
+		}
+	}
+
+	if ctx.Host() {
+		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).
+				if ctx.Target().HostCross != target.HostCross {
+					continue
+				}
+				addDep(target, depName)
+			}
+		}
+		for _, depName := range ctx.Module().TargetRequiredModuleNames() {
+			for _, target := range deviceTargets {
+				addDep(target, depName)
+			}
+		}
+	}
 }
 
 // AddProperties "registers" the provided props
@@ -1112,30 +1198,23 @@
 		// 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
 }
 
+func (m *ModuleBase) ArchReady() bool {
+	return m.commonProperties.ArchReady
+}
+
 func (m *ModuleBase) Target() Target {
 	return m.commonProperties.CompileTarget
 }
@@ -1284,14 +1363,11 @@
 	return partition
 }
 
-func (m *ModuleBase) Enabled() bool {
+func (m *ModuleBase) Enabled(ctx ConfigAndErrorContext) bool {
 	if m.commonProperties.ForcedDisabled {
 		return false
 	}
-	if m.commonProperties.Enabled == nil {
-		return !m.Os().DefaultDisabled
-	}
-	return *m.commonProperties.Enabled
+	return m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled)
 }
 
 func (m *ModuleBase) Disable() {
@@ -1495,8 +1571,8 @@
 	return m.base().commonProperties.ImageVariation == RecoveryVariation
 }
 
-func (m *ModuleBase) RequiredModuleNames() []string {
-	return m.base().commonProperties.Required
+func (m *ModuleBase) RequiredModuleNames(ctx ConfigAndErrorContext) []string {
+	return m.base().commonProperties.Required.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
 }
 
 func (m *ModuleBase) HostRequiredModuleNames() []string {
@@ -1535,7 +1611,7 @@
 		// 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(a) {
+		if !ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(ctx, a) {
 			allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
 		}
 	})
@@ -1648,7 +1724,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
@@ -1659,6 +1739,7 @@
 	}
 
 	return archModuleContext{
+		ready:         m.commonProperties.ArchReady,
 		os:            m.commonProperties.CompileOS,
 		target:        m.commonProperties.CompileTarget,
 		targetPrimary: m.commonProperties.CompilePrimary,
@@ -1676,6 +1757,8 @@
 		variables:         make(map[string]string),
 	}
 
+	setContainerInfo(ctx)
+
 	m.licenseMetadataFile = PathForModuleOut(ctx, "meta_lic")
 
 	dependencyInstallFiles, dependencyPackagingSpecs := m.computeInstallDeps(ctx)
@@ -1726,7 +1809,7 @@
 		checkDistProperties(ctx, fmt.Sprintf("dists[%d]", i), &m.distProperties.Dists[i])
 	}
 
-	if m.Enabled() {
+	if m.Enabled(ctx) {
 		// ensure all direct android.Module deps are enabled
 		ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) {
 			if m, ok := bm.(Module); ok {
@@ -1781,14 +1864,61 @@
 			}
 		}
 
-		m.module.GenerateAndroidBuildActions(ctx)
+		// Call aconfigUpdateAndroidBuildActions to collect merged aconfig files before being used
+		// in m.module.GenerateAndroidBuildActions
+		aconfigUpdateAndroidBuildActions(ctx)
 		if ctx.Failed() {
 			return
 		}
 
-		aconfigUpdateAndroidBuildActions(ctx)
-		if ctx.Failed() {
-			return
+		incrementalAnalysis := false
+		incrementalEnabled := false
+		var cacheKey *blueprint.BuildActionCacheKey = nil
+		var incrementalModule *blueprint.Incremental = nil
+		if ctx.bp.GetIncrementalEnabled() {
+			if im, ok := m.module.(blueprint.Incremental); ok {
+				incrementalModule = &im
+				incrementalEnabled = im.IncrementalSupported()
+				incrementalAnalysis = ctx.bp.GetIncrementalAnalysis() && incrementalEnabled
+			}
+		}
+		if incrementalEnabled {
+			hash, err := proptools.CalculateHash(m.GetProperties())
+			if err != nil {
+				ctx.ModuleErrorf("failed to calculate properties hash: %s", err)
+				return
+			}
+			cacheInput := new(blueprint.BuildActionCacheInput)
+			cacheInput.PropertiesHash = hash
+			ctx.VisitDirectDeps(func(module Module) {
+				cacheInput.ProvidersHash =
+					append(cacheInput.ProvidersHash, ctx.bp.OtherModuleProviderInitialValueHashes(module))
+			})
+			hash, err = proptools.CalculateHash(&cacheInput)
+			if err != nil {
+				ctx.ModuleErrorf("failed to calculate cache input hash: %s", err)
+				return
+			}
+			cacheKey = &blueprint.BuildActionCacheKey{
+				Id:        ctx.bp.ModuleId(),
+				InputHash: hash,
+			}
+		}
+
+		restored := false
+		if incrementalAnalysis && cacheKey != nil {
+			restored = ctx.bp.RestoreBuildActions(cacheKey, incrementalModule)
+		}
+
+		if !restored {
+			m.module.GenerateAndroidBuildActions(ctx)
+			if ctx.Failed() {
+				return
+			}
+		}
+
+		if incrementalEnabled && cacheKey != nil {
+			ctx.bp.CacheBuildActions(cacheKey, incrementalModule)
 		}
 
 		// Create the set of tagged dist files after calling GenerateAndroidBuildActions
@@ -1865,6 +1995,7 @@
 			TargetDependencies: targetRequired,
 			HostDependencies:   hostRequired,
 			Data:               data,
+			Required:           m.RequiredModuleNames(ctx),
 		}
 		SetProvider(ctx, ModuleInfoJSONProvider, m.moduleInfoJSON)
 	}
@@ -1872,6 +2003,12 @@
 	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)
+	}
+
+	buildComplianceMetadataProvider(ctx, m)
 }
 
 func SetJarJarPrefixHandler(handler func(ModuleContext)) {
@@ -2009,6 +2146,139 @@
 	return proptools.Bool(m.commonProperties.Native_bridge_supported)
 }
 
+type ConfigAndErrorContext interface {
+	Config() Config
+	OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{})
+}
+
+type configurationEvalutor struct {
+	ctx ConfigAndErrorContext
+	m   Module
+}
+
+func (m *ModuleBase) ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator {
+	return configurationEvalutor{
+		ctx: ctx,
+		m:   m.module,
+	}
+}
+
+func (e configurationEvalutor) PropertyErrorf(property string, fmt string, args ...interface{}) {
+	e.ctx.OtherModulePropertyErrorf(e.m, property, fmt, args...)
+}
+
+func (e configurationEvalutor) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue {
+	ctx := e.ctx
+	m := e.m
+	switch condition.FunctionName() {
+	case "release_flag":
+		if condition.NumArgs() != 1 {
+			ctx.OtherModulePropertyErrorf(m, property, "release_flag requires 1 argument, found %d", condition.NumArgs())
+			return proptools.ConfigurableValueUndefined()
+		}
+		if ty, ok := ctx.Config().productVariables.BuildFlagTypes[condition.Arg(0)]; ok {
+			v := ctx.Config().productVariables.BuildFlags[condition.Arg(0)]
+			switch ty {
+			case "unspecified", "obsolete":
+				return proptools.ConfigurableValueUndefined()
+			case "string":
+				return proptools.ConfigurableValueString(v)
+			case "bool":
+				return proptools.ConfigurableValueBool(v == "true")
+			default:
+				panic("unhandled release flag type: " + ty)
+			}
+		}
+		return proptools.ConfigurableValueUndefined()
+	case "product_variable":
+		if condition.NumArgs() != 1 {
+			ctx.OtherModulePropertyErrorf(m, property, "product_variable requires 1 argument, found %d", condition.NumArgs())
+			return proptools.ConfigurableValueUndefined()
+		}
+		variable := condition.Arg(0)
+		switch variable {
+		case "debuggable":
+			return proptools.ConfigurableValueBool(ctx.Config().Debuggable())
+		default:
+			// TODO(b/323382414): Might add these on a case-by-case basis
+			ctx.OtherModulePropertyErrorf(m, property, fmt.Sprintf("TODO(b/323382414): Product variable %q is not yet supported in selects", variable))
+			return proptools.ConfigurableValueUndefined()
+		}
+	case "soong_config_variable":
+		if condition.NumArgs() != 2 {
+			ctx.OtherModulePropertyErrorf(m, property, "soong_config_variable requires 2 arguments, found %d", condition.NumArgs())
+			return proptools.ConfigurableValueUndefined()
+		}
+		namespace := condition.Arg(0)
+		variable := condition.Arg(1)
+		if n, ok := ctx.Config().productVariables.VendorVars[namespace]; ok {
+			if v, ok := n[variable]; ok {
+				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()
+	case "arch":
+		if condition.NumArgs() != 0 {
+			ctx.OtherModulePropertyErrorf(m, property, "arch requires no arguments, found %d", condition.NumArgs())
+			return proptools.ConfigurableValueUndefined()
+		}
+		if !m.base().ArchReady() {
+			ctx.OtherModulePropertyErrorf(m, property, "A select on arch was attempted before the arch mutator ran")
+			return proptools.ConfigurableValueUndefined()
+		}
+		return proptools.ConfigurableValueString(m.base().Arch().ArchType.Name)
+	case "os":
+		if condition.NumArgs() != 0 {
+			ctx.OtherModulePropertyErrorf(m, property, "os requires no arguments, found %d", condition.NumArgs())
+			return proptools.ConfigurableValueUndefined()
+		}
+		// the arch mutator runs after the os mutator, we can just use this to enforce that os is ready.
+		if !m.base().ArchReady() {
+			ctx.OtherModulePropertyErrorf(m, property, "A select on os was attempted before the arch mutator ran (arch runs after os, we use it to lazily detect that os is ready)")
+			return proptools.ConfigurableValueUndefined()
+		}
+		return proptools.ConfigurableValueString(m.base().Os().Name)
+	case "boolean_var_for_testing":
+		// We currently don't have any other boolean variables (we should add support for typing
+		// the soong config variables), so add this fake one for testing the boolean select
+		// functionality.
+		if condition.NumArgs() != 0 {
+			ctx.OtherModulePropertyErrorf(m, property, "boolean_var_for_testing requires 0 arguments, found %d", condition.NumArgs())
+			return proptools.ConfigurableValueUndefined()
+		}
+
+		if n, ok := ctx.Config().productVariables.VendorVars["boolean_var"]; ok {
+			if v, ok := n["for_testing"]; ok {
+				switch v {
+				case "true":
+					return proptools.ConfigurableValueBool(true)
+				case "false":
+					return proptools.ConfigurableValueBool(false)
+				default:
+					ctx.OtherModulePropertyErrorf(m, property, "testing:my_boolean_var can only be true or false, found %q", v)
+				}
+			}
+		}
+		return proptools.ConfigurableValueUndefined()
+	default:
+		ctx.OtherModulePropertyErrorf(m, property, "Unknown select condition %s", condition.FunctionName)
+		return proptools.ConfigurableValueUndefined()
+	}
+}
+
 // ModuleNameWithPossibleOverride returns the name of the OverrideModule that overrides the current
 // variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule
 // or if this variant is not overridden.
@@ -2093,7 +2363,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
 }
 
@@ -2147,14 +2417,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)
@@ -2165,7 +2428,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)
@@ -2201,24 +2464,83 @@
 }
 
 func outputFilesForModule(ctx PathContext, module blueprint.Module, tag string) (Paths, error) {
-	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: %s",
-				pathContextName(ctx, module), err.Error())
-		}
-		return paths, nil
-	} else if sourceFileProducer, ok := module.(SourceFileProducer); ok {
+	outputFilesFromProvider, err := outputFilesForModuleFromProvider(ctx, module, tag)
+	if outputFilesFromProvider != nil || err != OutputFilesProviderNotSet {
+		return outputFilesFromProvider, err
+	}
+	if sourceFileProducer, ok := module.(SourceFileProducer); ok {
 		if tag != "" {
-			return nil, fmt.Errorf("module %q is a SourceFileProducer, 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", 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)
 	}
 }
 
+// This method uses OutputFilesProvider for output files
+// *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.
+// If a module doesn't have the OutputFilesProvider, nil is returned.
+func outputFilesForModuleFromProvider(ctx PathContext, module blueprint.Module, tag string) (Paths, error) {
+	var outputFiles OutputFilesInfo
+	fromProperty := false
+
+	type OutputFilesProviderModuleContext interface {
+		OtherModuleProviderContext
+		Module() Module
+	}
+
+	if mctx, isMctx := ctx.(OutputFilesProviderModuleContext); isMctx {
+		if mctx.Module() != module {
+			outputFiles, _ = OtherModuleProvider(mctx, module, OutputFilesProvider)
+		} else {
+			outputFiles = mctx.Module().base().outputFiles
+			fromProperty = true
+		}
+	} else if cta, isCta := ctx.(*singletonContextAdaptor); isCta {
+		providerData, _ := cta.moduleProvider(module, OutputFilesProvider)
+		outputFiles, _ = providerData.(OutputFilesInfo)
+	}
+	// TODO: Add a check for skipped context
+
+	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)
+		}
+	}
+}
+
+func (o OutputFilesInfo) isEmpty() bool {
+	return o.DefaultOutputFiles == nil && o.TaggedOutputFiles == nil
+}
+
+type OutputFilesInfo struct {
+	// default output files when tag is an empty string ""
+	DefaultOutputFiles Paths
+
+	// the corresponding output files for given tags
+	TaggedOutputFiles map[string]Paths
+}
+
+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 {
@@ -2230,8 +2552,6 @@
 
 func init() {
 	RegisterParallelSingletonType("buildtarget", BuildTargetSingleton)
-	RegisterParallelSingletonType("soongconfigtrace", soongConfigTraceSingletonFunc)
-	FinalDepsMutators(registerSoongConfigTraceMutator)
 }
 
 func BuildTargetSingleton() Singleton {
@@ -2326,7 +2646,7 @@
 	}
 	osDeps := map[osAndCross]Paths{}
 	ctx.VisitAllModules(func(module Module) {
-		if module.Enabled() {
+		if module.Enabled(ctx) {
 			key := osAndCross{os: module.Target().Os, hostCross: module.Target().HostCross}
 			osDeps[key] = append(osDeps[key], module.base().checkbuildFiles...)
 		}
@@ -2393,54 +2713,3 @@
 	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 1cab630..253bebd 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -183,12 +183,11 @@
 	InstallInVendor() bool
 	InstallForceOS() (*OsType, *ArchType)
 
-	RequiredModuleNames() []string
+	RequiredModuleNames(ctx ConfigAndErrorContext) []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
@@ -212,6 +211,15 @@
 	// GenerateAndroidBuildActions.  If it is called then the struct will be written out and included in
 	// the module-info.json generated by Make, and Make will not generate its own data for this module.
 	ModuleInfoJSON() *ModuleInfoJSON
+
+	// SetOutputFiles stores the outputFiles to outputFiles property, which is used
+	// to set the OutputFilesProvider later.
+	SetOutputFiles(outputFiles Paths, tag 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
 }
 
 type moduleContext struct {
@@ -234,6 +242,8 @@
 	variables   map[string]string
 }
 
+var _ ModuleContext = &moduleContext{}
+
 func (m *moduleContext) ninjaError(params BuildParams, err error) (PackageContext, BuildParams) {
 	return pctx, BuildParams{
 		Rule:            ErrorRule,
@@ -371,10 +381,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()
 }
@@ -442,6 +448,21 @@
 	return false
 }
 
+// Tells whether this module is installed to the full install path (ex:
+// out/target/product/<name>/<partition>) or not. If this returns false, the install build rule is
+// not created and this module can only be installed to packaging modules like android_filesystem.
+func (m *moduleContext) requiresFullInstall() bool {
+	if m.skipInstall() {
+		return false
+	}
+
+	if proptools.Bool(m.module.base().commonProperties.No_full_install) {
+		return false
+	}
+
+	return true
+}
+
 func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
 	deps ...InstallPath) InstallPath {
 	return m.installFile(installPath, name, srcPath, deps, false, true, nil)
@@ -465,6 +486,10 @@
 	return m.packageFile(fullInstallPath, srcPath, false)
 }
 
+func (m *moduleContext) getAconfigPaths() *Paths {
+	return &m.module.base().aconfigFilePaths
+}
+
 func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec {
 	licenseFiles := m.Module().EffectiveLicenseFiles()
 	spec := PackagingSpec{
@@ -474,6 +499,9 @@
 		executable:            executable,
 		effectiveLicenseFiles: &licenseFiles,
 		partition:             fullInstallPath.partition,
+		skipInstall:           m.skipInstall(),
+		aconfigPaths:          m.getAconfigPaths(),
+		archType:              m.target.Arch.ArchType,
 	}
 	m.packagingSpecs = append(m.packagingSpecs, spec)
 	return spec
@@ -487,7 +515,7 @@
 		m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
 	}
 
-	if !m.skipInstall() {
+	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...)
@@ -560,7 +588,7 @@
 	if err != nil {
 		panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
 	}
-	if !m.skipInstall() {
+	if m.requiresFullInstall() {
 
 		if m.Config().KatiEnabled() {
 			// When creating the symlink rule in Soong but embedding in Make, write the rule to a
@@ -597,6 +625,9 @@
 		symlinkTarget:    relPath,
 		executable:       false,
 		partition:        fullInstallPath.partition,
+		skipInstall:      m.skipInstall(),
+		aconfigPaths:     m.getAconfigPaths(),
+		archType:         m.target.Arch.ArchType,
 	})
 
 	return fullInstallPath
@@ -608,7 +639,7 @@
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
 
-	if !m.skipInstall() {
+	if m.requiresFullInstall() {
 		if m.Config().KatiEnabled() {
 			// When creating the symlink rule in Soong but embedding in Make, write the rule to a
 			// makefile instead of directly to the ninja file so that main.mk can add the
@@ -638,6 +669,9 @@
 		symlinkTarget:    absPath,
 		executable:       false,
 		partition:        fullInstallPath.partition,
+		skipInstall:      m.skipInstall(),
+		aconfigPaths:     m.getAconfigPaths(),
+		archType:         m.target.Arch.ArchType,
 	})
 
 	return fullInstallPath
@@ -677,6 +711,33 @@
 	return moduleInfoJSON
 }
 
+func (m *moduleContext) SetOutputFiles(outputFiles Paths, tag string) {
+	if tag == "" {
+		if len(m.module.base().outputFiles.DefaultOutputFiles) > 0 {
+			m.ModuleErrorf("Module %s default OutputFiles cannot be overwritten", m.ModuleName())
+		}
+		m.module.base().outputFiles.DefaultOutputFiles = outputFiles
+	} else {
+		if m.module.base().outputFiles.TaggedOutputFiles == nil {
+			m.module.base().outputFiles.TaggedOutputFiles = make(map[string]Paths)
+		}
+		if _, exists := m.module.base().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
+		}
+	}
+}
+
+func (m *moduleContext) ComplianceMetadataInfo() *ComplianceMetadataInfo {
+	if complianceMetadataInfo := m.module.base().complianceMetadataInfo; complianceMetadataInfo != nil {
+		return complianceMetadataInfo
+	}
+	complianceMetadataInfo := NewComplianceMetadataInfo()
+	m.module.base().complianceMetadataInfo = complianceMetadataInfo
+	return 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.
 //
@@ -703,8 +764,8 @@
 	return OptionalPath{}
 }
 
-func (m *moduleContext) RequiredModuleNames() []string {
-	return m.module.RequiredModuleNames()
+func (m *moduleContext) RequiredModuleNames(ctx ConfigAndErrorContext) []string {
+	return m.module.RequiredModuleNames(ctx)
 }
 
 func (m *moduleContext) HostRequiredModuleNames() []string {
diff --git a/android/module_info_json.go b/android/module_info_json.go
index 1c0a38e..ee552dc 100644
--- a/android/module_info_json.go
+++ b/android/module_info_json.go
@@ -17,6 +17,7 @@
 	HostDependencies   []string `json:"host_dependencies,omitempty"`   // $(sort $(ALL_MODULES.$(m).HOST_REQUIRED_FROM_TARGET))
 	TargetDependencies []string `json:"target_dependencies,omitempty"` // $(sort $(ALL_MODULES.$(m).TARGET_REQUIRED_FROM_HOST))
 	Data               []string `json:"data,omitempty"`                // $(sort $(ALL_MODULES.$(m).TEST_DATA))
+	Required           []string `json:"required,omitempty"`            // $(sort $(ALL_MODULES.$(m).REQUIRED_FROM_TARGET))
 }
 
 type ModuleInfoJSON struct {
@@ -77,6 +78,7 @@
 	sortAndUnique(&moduleInfoJSONCopy.core.HostDependencies)
 	sortAndUnique(&moduleInfoJSONCopy.core.TargetDependencies)
 	sortAndUnique(&moduleInfoJSONCopy.core.Data)
+	sortAndUnique(&moduleInfoJSONCopy.core.Required)
 
 	sortAndUnique(&moduleInfoJSONCopy.Class)
 	sortAndUnique(&moduleInfoJSONCopy.Tags)
diff --git a/android/module_test.go b/android/module_test.go
index 1f3db5c..922ea21 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -935,31 +935,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 +993,87 @@
 	return module.Name()
 }
 
+func (p *pathContextAddMissingDependenciesWrapper) Module() Module { return nil }
+
 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 0ff4f48..b81dd12 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -293,15 +293,14 @@
 	// WalkDeps, etc.
 	AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module)
 
-	// ReplaceDependencies replaces all dependencies on the identical variant of the module with the
-	// specified name with the current variant of this module.  Replacements don't take effect until
-	// after the mutator pass is finished.
+	// ReplaceDependencies finds all the variants of the module with the specified name, then
+	// replaces all dependencies onto those variants with the current variant of this module.
+	// Replacements don't take effect until after the mutator pass is finished.
 	ReplaceDependencies(string)
 
-	// ReplaceDependencies replaces all dependencies on the identical variant of the module with the
-	// specified name with the current variant of this module as long as the supplied predicate returns
-	// true.
-	//
+	// ReplaceDependenciesIf finds all the variants of the module with the specified name, then
+	// replaces all dependencies onto those variants with the current variant of this module
+	// as long as the supplied predicate returns true.
 	// Replacements don't take effect until after the mutator pass is finished.
 	ReplaceDependenciesIf(string, blueprint.ReplaceDependencyPredicate)
 
@@ -391,6 +390,7 @@
 
 type IncomingTransitionContext interface {
 	ArchModuleContext
+	ModuleProviderContext
 
 	// Module returns the target of the dependency edge for which the transition
 	// is being computed
@@ -400,10 +400,17 @@
 	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 {
 	ArchModuleContext
+	ModuleProviderContext
 
 	// Module returns the target of the dependency edge for which the transition
 	// is being computed
@@ -505,6 +512,7 @@
 type androidTransitionMutator struct {
 	finalPhase bool
 	mutator    TransitionMutator
+	name       string
 }
 
 func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string {
@@ -537,6 +545,10 @@
 	return DeviceConfig{c.bp.Config().(Config).deviceConfig}
 }
 
+func (c *outgoingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
+	return c.bp.Provider(provider)
+}
+
 func (a *androidTransitionMutator) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext, sourceVariation string) string {
 	if m, ok := bpctx.Module().(Module); ok {
 		ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl)
@@ -568,6 +580,14 @@
 	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)
+}
+
 func (a *androidTransitionMutator) IncomingTransition(bpctx blueprint.IncomingTransitionContext, incomingVariation string) string {
 	if m, ok := bpctx.Module().(Module); ok {
 		ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl)
@@ -584,6 +604,14 @@
 
 func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, variation string) {
 	if am, ok := ctx.Module().(Module); ok {
+		if variation != "" {
+			// TODO: this should really be checking whether the TransitionMutator affected this module, not
+			//  the empty variant, but TransitionMutator has no concept of skipping a module.
+			base := am.base()
+			base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, a.name)
+			base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variation)
+		}
+
 		mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase)
 		defer bottomUpMutatorContextPool.Put(mctx)
 		a.mutator.Mutate(mctx, variation)
@@ -594,6 +622,7 @@
 	atm := &androidTransitionMutator{
 		finalPhase: x.finalPhase,
 		mutator:    m,
+		name:       name,
 	}
 	mutator := &mutator{
 		name:              name,
@@ -659,13 +688,11 @@
 // on component modules to be added so that they can depend directly on a prebuilt
 // module.
 func componentDepsMutator(ctx BottomUpMutatorContext) {
-	if m := ctx.Module(); m.Enabled() {
-		m.ComponentDepsMutator(ctx)
-	}
+	ctx.Module().ComponentDepsMutator(ctx)
 }
 
 func depsMutator(ctx BottomUpMutatorContext) {
-	if m := ctx.Module(); m.Enabled() {
+	if m := ctx.Module(); m.Enabled(ctx) {
 		m.base().baseDepsMutator(ctx)
 		m.DepsMutator(ctx)
 	}
diff --git a/android/neverallow.go b/android/neverallow.go
index 62c5e59..ef4b8b8 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -237,6 +237,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."),
 	}
 }
diff --git a/android/override_module.go b/android/override_module.go
index 1341f53..f69f963 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -28,6 +28,7 @@
 // module based on it.
 
 import (
+	"fmt"
 	"sort"
 	"sync"
 
@@ -120,7 +121,7 @@
 	addOverride(o OverrideModule)
 	getOverrides() []OverrideModule
 
-	override(ctx BaseModuleContext, m Module, o OverrideModule)
+	override(ctx BaseModuleContext, bm OverridableModule, o OverrideModule)
 	GetOverriddenBy() string
 	GetOverriddenByModuleDir() string
 
@@ -191,15 +192,14 @@
 }
 
 // Overrides a base module with the given OverrideModule.
-func (b *OverridableModuleBase) override(ctx BaseModuleContext, m Module, o OverrideModule) {
-
+func (b *OverridableModuleBase) override(ctx BaseModuleContext, bm OverridableModule, o OverrideModule) {
 	for _, p := range b.overridableProperties {
 		for _, op := range o.getOverridingProperties() {
 			if proptools.TypeEqual(p, op) {
 				err := proptools.ExtendProperties(p, op, nil, proptools.OrderReplace)
 				if err != nil {
 					if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-						ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+						ctx.OtherModulePropertyErrorf(bm, propertyErr.Property, "%s", propertyErr.Err.Error())
 					} else {
 						panic(err)
 					}
@@ -210,7 +210,7 @@
 	// Adds the base module to the overrides property, if exists, of the overriding module. See the
 	// comment on OverridableModuleBase.overridesProperty for details.
 	if b.overridesProperty != nil {
-		*b.overridesProperty = append(*b.overridesProperty, ctx.ModuleName())
+		*b.overridesProperty = append(*b.overridesProperty, ctx.OtherModuleName(bm))
 	}
 	b.overridableModuleProperties.OverriddenBy = o.Name()
 	b.overridableModuleProperties.OverriddenByModuleDir = o.ModuleDir()
@@ -235,7 +235,7 @@
 // to keep them in this order and not put any order mutators between them.
 func RegisterOverridePostDepsMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel()
-	ctx.BottomUp("perform_override", performOverrideMutator).Parallel()
+	ctx.Transition("override", &overrideTransitionMutator{})
 	// overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
 	// add deps from overridable properties.
 	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel()
@@ -253,6 +253,15 @@
 
 var overrideBaseDepTag overrideBaseDependencyTag
 
+// Override module should always override the source module.
+// Overrides are implemented as a variant of the overridden module, and the build actions are created in the
+// module context of the overridden module.
+// If we replace override module with the prebuilt of the overridden module, `GenerateAndroidBuildActions` for
+// the override module will have a very different meaning.
+func (tag overrideBaseDependencyTag) ReplaceSourceWithPrebuilt() bool {
+	return false
+}
+
 // Adds dependency on the base module to the overriding module so that they can be visited in the
 // next phase.
 func overrideModuleDepsMutator(ctx BottomUpMutatorContext) {
@@ -262,18 +271,6 @@
 			ctx.PropertyErrorf("base", "%q is not a valid module name", base)
 			return
 		}
-		// See if there's a prebuilt module that overrides this override module with prefer flag,
-		// in which case we call HideFromMake on the corresponding variant later.
-		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) {
-			prebuilt := GetEmbeddedPrebuilt(dep)
-			if prebuilt == nil {
-				panic("PrebuiltDepTag leads to a non-prebuilt module " + dep.Name())
-			}
-			if prebuilt.UsePrebuilt() {
-				module.setOverriddenByPrebuilt(dep)
-				return
-			}
-		})
 		baseModule := ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)[0]
 		if o, ok := baseModule.(OverridableModule); ok {
 			overrideModule := ctx.Module().(OverrideModule)
@@ -285,11 +282,13 @@
 
 // Now, goes through all overridable modules, finds all modules overriding them, creates a local
 // variant for each of them, and performs the actual overriding operation by calling override().
-func performOverrideMutator(ctx BottomUpMutatorContext) {
+type overrideTransitionMutator struct{}
+
+func (overrideTransitionMutator) Split(ctx BaseModuleContext) []string {
 	if b, ok := ctx.Module().(OverridableModule); ok {
 		overrides := b.getOverrides()
 		if len(overrides) == 0 {
-			return
+			return []string{""}
 		}
 		variants := make([]string, len(overrides)+1)
 		// The first variant is for the original, non-overridden, base module.
@@ -297,32 +296,74 @@
 		for i, o := range overrides {
 			variants[i+1] = o.(Module).Name()
 		}
-		mods := ctx.CreateLocalVariations(variants...)
-		// Make the original variation the default one to depend on if no other override module variant
-		// is specified.
-		ctx.AliasVariation(variants[0])
-		for i, o := range overrides {
-			mods[i+1].(OverridableModule).override(ctx, mods[i+1], o)
-			if prebuilt := o.getOverriddenByPrebuilt(); prebuilt != nil {
-				// The overriding module itself, too, is overridden by a prebuilt.
-				// Perform the same check for replacement
-				checkInvariantsForSourceAndPrebuilt(ctx, mods[i+1], prebuilt)
-				// Copy the flag and hide it in make
-				mods[i+1].ReplacedByPrebuilt()
-			}
-		}
+		return variants
 	} else if o, ok := ctx.Module().(OverrideModule); ok {
 		// Create a variant of the overriding module with its own name. This matches the above local
 		// variant name rule for overridden modules, and thus allows ReplaceDependencies to match the
 		// two.
-		ctx.CreateLocalVariations(o.Name())
-		// To allow dependencies to be added without having to know the above variation.
-		ctx.AliasVariation(o.Name())
+		return []string{o.Name()}
+	}
+
+	return []string{""}
+}
+
+func (overrideTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
+	if o, ok := ctx.Module().(OverrideModule); ok {
+		if ctx.DepTag() == overrideBaseDepTag {
+			return o.Name()
+		}
+	}
+
+	// Variations are always local and shouldn't affect the variant used for dependencies
+	return ""
+}
+
+func (overrideTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	if _, ok := ctx.Module().(OverridableModule); ok {
+		return incomingVariation
+	} else if o, ok := ctx.Module().(OverrideModule); ok {
+		// To allow dependencies to be added without having to know the variation.
+		return o.Name()
+	}
+
+	return ""
+}
+
+func (overrideTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+	if o, ok := ctx.Module().(OverrideModule); ok {
+		overridableDeps := ctx.GetDirectDepsWithTag(overrideBaseDepTag)
+		if len(overridableDeps) > 1 {
+			panic(fmt.Errorf("expected a single dependency with overrideBaseDepTag, found %q", overridableDeps))
+		} else if len(overridableDeps) == 1 {
+			b := overridableDeps[0].(OverridableModule)
+			b.override(ctx, b, o)
+
+			checkPrebuiltReplacesOverride(ctx, b)
+		}
+	}
+}
+
+func checkPrebuiltReplacesOverride(ctx BottomUpMutatorContext, b OverridableModule) {
+	// See if there's a prebuilt module that overrides this override module with prefer flag,
+	// in which case we call HideFromMake on the corresponding variant later.
+	prebuiltDeps := ctx.GetDirectDepsWithTag(PrebuiltDepTag)
+	for _, prebuiltDep := range prebuiltDeps {
+		prebuilt := GetEmbeddedPrebuilt(prebuiltDep)
+		if prebuilt == nil {
+			panic("PrebuiltDepTag leads to a non-prebuilt module " + prebuiltDep.Name())
+		}
+		if prebuilt.UsePrebuilt() {
+			// The overriding module itself, too, is overridden by a prebuilt.
+			// Perform the same check for replacement
+			checkInvariantsForSourceAndPrebuilt(ctx, b, prebuiltDep)
+			// Copy the flag and hide it in make
+			b.ReplacedByPrebuilt()
+		}
 	}
 }
 
 func overridableModuleDepsMutator(ctx BottomUpMutatorContext) {
-	if b, ok := ctx.Module().(OverridableModule); ok && b.Enabled() {
+	if b, ok := ctx.Module().(OverridableModule); ok && b.Enabled(ctx) {
 		b.OverridablePropertiesDepsMutator(ctx)
 	}
 }
diff --git a/android/packaging.go b/android/packaging.go
index a8fb28d..c247ed2 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -17,9 +17,11 @@
 import (
 	"fmt"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 // PackagingSpec abstracts a request to place a built artifact at a certain path in a package. A
@@ -43,6 +45,36 @@
 	effectiveLicenseFiles *Paths
 
 	partition string
+
+	// Whether this packaging spec represents an installation of the srcPath (i.e. this struct
+	// is created via InstallFile or InstallSymlink) or a simple packaging (i.e. created via
+	// PackageFile).
+	skipInstall bool
+
+	// Paths of aconfig files for the built artifact
+	aconfigPaths *Paths
+
+	// ArchType of the module which produced this packaging spec
+	archType ArchType
+}
+
+func (p *PackagingSpec) Equals(other *PackagingSpec) bool {
+	if other == nil {
+		return false
+	}
+	if p.relPathInPackage != other.relPathInPackage {
+		return false
+	}
+	if p.srcPath != other.srcPath || p.symlinkTarget != other.symlinkTarget {
+		return false
+	}
+	if p.executable != other.executable {
+		return false
+	}
+	if p.partition != other.partition {
+		return false
+	}
+	return true
 }
 
 // Get file name of installed package
@@ -74,6 +106,15 @@
 	return p.partition
 }
 
+func (p *PackagingSpec) SkipInstall() bool {
+	return p.skipInstall
+}
+
+// Paths of aconfig files for the built artifact
+func (p *PackagingSpec) GetAconfigPaths() Paths {
+	return *p.aconfigPaths
+}
+
 type PackageModule interface {
 	Module
 	packagingBase() *PackagingBase
@@ -103,18 +144,24 @@
 	// for rare cases like when there's a dependency to a module which exists in certain repo
 	// checkouts, this is needed.
 	IgnoreMissingDependencies bool
+
+	// If this is set to true by a module type inheriting PackagingBase, the deps property
+	// collects the first target only even with compile_multilib: true.
+	DepsCollectFirstTargetOnly bool
 }
 
 type depsProperty struct {
 	// Modules to include in this package
-	Deps []string `android:"arch_variant"`
+	Deps proptools.Configurable[[]string] `android:"arch_variant"`
 }
 
 type packagingMultilibProperties struct {
-	First  depsProperty `android:"arch_variant"`
-	Common depsProperty `android:"arch_variant"`
-	Lib32  depsProperty `android:"arch_variant"`
-	Lib64  depsProperty `android:"arch_variant"`
+	First    depsProperty `android:"arch_variant"`
+	Common   depsProperty `android:"arch_variant"`
+	Lib32    depsProperty `android:"arch_variant"`
+	Lib64    depsProperty `android:"arch_variant"`
+	Both     depsProperty `android:"arch_variant"`
+	Prefer32 depsProperty `android:"arch_variant"`
 }
 
 type packagingArchProperties struct {
@@ -125,8 +172,8 @@
 }
 
 type PackagingProperties struct {
-	Deps     []string                    `android:"arch_variant"`
-	Multilib packagingMultilibProperties `android:"arch_variant"`
+	Deps     proptools.Configurable[[]string] `android:"arch_variant"`
+	Multilib packagingMultilibProperties      `android:"arch_variant"`
 	Arch     packagingArchProperties
 }
 
@@ -144,22 +191,55 @@
 // multi target, deps is selected for each of the targets and is NOT selected for the current
 // architecture which would be Common.
 func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []string {
-	var ret []string
-	if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
-		ret = append(ret, p.properties.Deps...)
-	} else if arch.Multilib == "lib32" {
-		ret = append(ret, p.properties.Multilib.Lib32.Deps...)
-	} else if arch.Multilib == "lib64" {
-		ret = append(ret, p.properties.Multilib.Lib64.Deps...)
-	} else if arch == Common {
-		ret = append(ret, p.properties.Multilib.Common.Deps...)
+	get := func(prop proptools.Configurable[[]string]) []string {
+		return prop.GetOrDefault(ctx, nil)
 	}
 
-	for i, t := range ctx.MultiTargets() {
-		if t.Arch.ArchType == arch {
-			ret = append(ret, p.properties.Deps...)
-			if i == 0 {
-				ret = append(ret, p.properties.Multilib.First.Deps...)
+	var ret []string
+	if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
+		ret = append(ret, get(p.properties.Deps)...)
+	} else if arch.Multilib == "lib32" {
+		ret = append(ret, get(p.properties.Multilib.Lib32.Deps)...)
+		// multilib.prefer32.deps are added for lib32 only when they support 32-bit arch
+		for _, dep := range get(p.properties.Multilib.Prefer32.Deps) {
+			if checkIfOtherModuleSupportsLib32(ctx, dep) {
+				ret = append(ret, dep)
+			}
+		}
+	} else if arch.Multilib == "lib64" {
+		ret = append(ret, get(p.properties.Multilib.Lib64.Deps)...)
+		// multilib.prefer32.deps are added for lib64 only when they don't support 32-bit arch
+		for _, dep := range get(p.properties.Multilib.Prefer32.Deps) {
+			if !checkIfOtherModuleSupportsLib32(ctx, dep) {
+				ret = append(ret, dep)
+			}
+		}
+	} else if arch == Common {
+		ret = append(ret, get(p.properties.Multilib.Common.Deps)...)
+	}
+
+	if p.DepsCollectFirstTargetOnly {
+		if len(get(p.properties.Multilib.First.Deps)) > 0 {
+			ctx.PropertyErrorf("multilib.first.deps", "not supported. use \"deps\" instead")
+		}
+		for i, t := range ctx.MultiTargets() {
+			if t.Arch.ArchType == arch {
+				ret = append(ret, get(p.properties.Multilib.Both.Deps)...)
+				if i == 0 {
+					ret = append(ret, get(p.properties.Deps)...)
+				}
+			}
+		}
+	} else {
+		if len(get(p.properties.Multilib.Both.Deps)) > 0 {
+			ctx.PropertyErrorf("multilib.both.deps", "not supported. use \"deps\" instead")
+		}
+		for i, t := range ctx.MultiTargets() {
+			if t.Arch.ArchType == arch {
+				ret = append(ret, get(p.properties.Deps)...)
+				if i == 0 {
+					ret = append(ret, get(p.properties.Multilib.First.Deps)...)
+				}
 			}
 		}
 	}
@@ -167,20 +247,20 @@
 	if ctx.Arch().ArchType == Common {
 		switch arch {
 		case Arm64:
-			ret = append(ret, p.properties.Arch.Arm64.Deps...)
+			ret = append(ret, get(p.properties.Arch.Arm64.Deps)...)
 		case Arm:
-			ret = append(ret, p.properties.Arch.Arm.Deps...)
+			ret = append(ret, get(p.properties.Arch.Arm.Deps)...)
 		case X86_64:
-			ret = append(ret, p.properties.Arch.X86_64.Deps...)
+			ret = append(ret, get(p.properties.Arch.X86_64.Deps)...)
 		case X86:
-			ret = append(ret, p.properties.Arch.X86.Deps...)
+			ret = append(ret, get(p.properties.Arch.X86.Deps)...)
 		}
 	}
 
 	return FirstUniqueStrings(ret)
 }
 
-func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target {
+func getSupportedTargets(ctx BaseModuleContext) []Target {
 	var ret []Target
 	// The current and the common OS targets are always supported
 	ret = append(ret, ctx.Target())
@@ -192,6 +272,28 @@
 	return ret
 }
 
+// getLib32Target returns the 32-bit target from the list of targets this module supports. If this
+// module doesn't support 32-bit target, nil is returned.
+func getLib32Target(ctx BaseModuleContext) *Target {
+	for _, t := range getSupportedTargets(ctx) {
+		if t.Arch.ArchType.Multilib == "lib32" {
+			return &t
+		}
+	}
+	return nil
+}
+
+// checkIfOtherModuleSUpportsLib32 returns true if 32-bit variant of dep exists.
+func checkIfOtherModuleSupportsLib32(ctx BaseModuleContext, dep string) bool {
+	t := getLib32Target(ctx)
+	if t == nil {
+		// This packaging module doesn't support 32bit. No point of checking if dep supports 32-bit
+		// or not.
+		return false
+	}
+	return ctx.OtherModuleFarDependencyVariantExists(t.Variations(), dep)
+}
+
 // PackagingItem is a marker interface for dependency tags.
 // Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip().
 type PackagingItem interface {
@@ -212,7 +314,7 @@
 
 // See PackageModule.AddDeps
 func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
-	for _, t := range p.getSupportedTargets(ctx) {
+	for _, t := range getSupportedTargets(ctx) {
 		for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) {
 			if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
 				continue
@@ -224,19 +326,45 @@
 
 func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec {
 	m := make(map[string]PackagingSpec)
+
+	var arches []ArchType
+	for _, target := range getSupportedTargets(ctx) {
+		arches = append(arches, target.Arch.ArchType)
+	}
+
+	// filter out packaging specs for unsupported architecture
+	filterArch := func(ps PackagingSpec) bool {
+		for _, arch := range arches {
+			if arch == ps.archType {
+				return true
+			}
+		}
+		return false
+	}
+
 	ctx.VisitDirectDeps(func(child Module) {
 		if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
 			return
 		}
 		for _, ps := range child.TransitivePackagingSpecs() {
+			if !filterArch(ps) {
+				continue
+			}
+
 			if filter != nil {
 				if !filter(ps) {
 					continue
 				}
 			}
-			if _, ok := m[ps.relPathInPackage]; !ok {
-				m[ps.relPathInPackage] = ps
+			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
@@ -250,31 +378,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 3833437..19b46fe 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"strings"
 	"testing"
 
 	"github.com/google/blueprint"
@@ -67,18 +68,15 @@
 	entries []string
 }
 
-func packageMultiTargetTestModuleFactory() Module {
+func packageTestModuleFactory(multiTarget bool, depsCollectFirstTargetOnly bool) Module {
 	module := &packageTestModule{}
 	InitPackageModule(module)
-	InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon)
-	module.AddProperties(&module.properties)
-	return module
-}
-
-func packageTestModuleFactory() Module {
-	module := &packageTestModule{}
-	InitPackageModule(module)
-	InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
+	module.DepsCollectFirstTargetOnly = depsCollectFirstTargetOnly
+	if multiTarget {
+		InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon)
+	} else {
+		InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
+	}
 	module.AddProperties(&module.properties)
 	return module
 }
@@ -98,17 +96,24 @@
 	m.entries = m.CopyDepsToZip(ctx, m.GatherPackagingSpecs(ctx), zipFile)
 }
 
-func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []string) {
+type testConfig struct {
+	multiTarget                bool
+	depsCollectFirstTargetOnly bool
+	debuggable                 bool
+}
+
+func runPackagingTest(t *testing.T, config testConfig, bp string, expected []string) {
 	t.Helper()
 
 	var archVariant string
-	var moduleFactory ModuleFactory
-	if multitarget {
+	if config.multiTarget {
 		archVariant = "android_common"
-		moduleFactory = packageMultiTargetTestModuleFactory
 	} else {
 		archVariant = "android_arm64_armv8-a"
-		moduleFactory = packageTestModuleFactory
+	}
+
+	moduleFactory := func() Module {
+		return packageTestModuleFactory(config.multiTarget, config.depsCollectFirstTargetOnly)
 	}
 
 	result := GroupFixturePreparers(
@@ -117,6 +122,9 @@
 			ctx.RegisterModuleType("component", componentTestModuleFactory)
 			ctx.RegisterModuleType("package_module", moduleFactory)
 		}),
+		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.Debuggable = proptools.BoolPtr(config.debuggable)
+		}),
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
 
@@ -128,8 +136,11 @@
 }
 
 func TestPackagingBaseMultiTarget(t *testing.T) {
-	multiTarget := true
-	runPackagingTest(t, multiTarget,
+	config := testConfig{
+		multiTarget:                true,
+		depsCollectFirstTargetOnly: false,
+	}
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -141,7 +152,7 @@
 		}
 		`, []string{"lib64/foo"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -158,7 +169,7 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -176,7 +187,7 @@
 		}
 		`, []string{"lib32/foo", "lib32/bar", "lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -199,7 +210,7 @@
 		}
 		`, []string{"lib32/foo", "lib32/bar", "lib64/foo"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -221,7 +232,7 @@
 		}
 		`, []string{"lib32/foo", "lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -252,8 +263,11 @@
 }
 
 func TestPackagingBaseSingleTarget(t *testing.T) {
-	multiTarget := false
-	runPackagingTest(t, multiTarget,
+	config := testConfig{
+		multiTarget:                false,
+		depsCollectFirstTargetOnly: false,
+	}
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -265,7 +279,7 @@
 		}
 		`, []string{"lib64/foo"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -282,7 +296,7 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -304,7 +318,7 @@
 		}
 		`, []string{"lib64/foo"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -325,7 +339,7 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -353,7 +367,7 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar"})
 
-	runPackagingTest(t, multiTarget,
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -374,8 +388,11 @@
 func TestPackagingWithSkipInstallDeps(t *testing.T) {
 	// package -[dep]-> foo -[dep]-> bar      -[dep]-> baz
 	// Packaging should continue transitively through modules that are not installed.
-	multiTarget := false
-	runPackagingTest(t, multiTarget,
+	config := testConfig{
+		multiTarget:                false,
+		depsCollectFirstTargetOnly: false,
+	}
+	runPackagingTest(t, config,
 		`
 		component {
 			name: "foo",
@@ -398,3 +415,238 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar", "lib64/baz"})
 }
+
+func TestPackagingWithDepsCollectFirstTargetOnly(t *testing.T) {
+	config := testConfig{
+		multiTarget:                true,
+		depsCollectFirstTargetOnly: true,
+	}
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+		}
+		`, []string{"lib64/foo"})
+
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+			deps: ["bar"],
+		}
+
+		component {
+			name: "bar",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+		}
+		`, []string{"lib64/foo", "lib64/bar"})
+
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+			deps: ["bar"],
+		}
+
+		component {
+			name: "bar",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+			compile_multilib: "both",
+		}
+		`, []string{"lib64/foo", "lib64/bar"})
+
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+		}
+
+		component {
+			name: "bar",
+			compile_multilib: "32",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+			multilib: {
+				lib32: {
+					deps: ["bar"],
+				},
+			},
+			compile_multilib: "both",
+		}
+		`, []string{"lib32/bar", "lib64/foo"})
+
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+		}
+
+		component {
+			name: "bar",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+			multilib: {
+				both: {
+					deps: ["bar"],
+				},
+			},
+			compile_multilib: "both",
+		}
+		`, []string{"lib64/foo", "lib32/bar", "lib64/bar"})
+
+	runPackagingTest(t, config,
+		`
+		component {
+			name: "foo",
+		}
+
+		component {
+			name: "bar",
+		}
+
+		component {
+			name: "baz",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+			arch: {
+				arm64: {
+					deps: ["bar"],
+				},
+				x86_64: {
+					deps: ["baz"],
+				},
+			},
+			compile_multilib: "both",
+		}
+		`, []string{"lib64/foo", "lib64/bar"})
+}
+
+func TestDebuggableDeps(t *testing.T) {
+	bp := `
+		component {
+			name: "foo",
+		}
+
+		component {
+			name: "bar",
+			deps: ["baz"],
+		}
+
+		component {
+			name: "baz",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"] + select(product_variable("debuggable"), {
+				true: ["bar"],
+				default: [],
+			}),
+		}`
+	testcases := []struct {
+		debuggable bool
+		expected   []string
+	}{
+		{
+			debuggable: true,
+			expected:   []string{"lib64/foo", "lib64/bar", "lib64/baz"},
+		},
+		{
+			debuggable: false,
+			expected:   []string{"lib64/foo"},
+		},
+	}
+	for _, tc := range testcases {
+		config := testConfig{
+			debuggable: tc.debuggable,
+		}
+		runPackagingTest(t, config, bp, tc.expected)
+	}
+}
+
+func TestPrefer32Deps(t *testing.T) {
+	bpTemplate := `
+		component {
+			name: "foo",
+			compile_multilib: "both", // not needed but for clarity
+		}
+
+		component {
+			name: "foo_32only",
+			compile_multilib: "prefer32",
+		}
+
+		component {
+			name: "foo_64only",
+			compile_multilib: "64",
+		}
+
+		package_module {
+			name: "package",
+			compile_multilib: "%COMPILE_MULTILIB%",
+			multilib: {
+				prefer32: {
+					deps: %DEPS%,
+				},
+			},
+		}
+	`
+
+	testcases := []struct {
+		compileMultilib string
+		deps            []string
+		expected        []string
+	}{
+		{
+			compileMultilib: "first",
+			deps:            []string{"foo", "foo_64only"},
+			expected:        []string{"lib64/foo", "lib64/foo_64only"},
+		},
+		{
+			compileMultilib: "64",
+			deps:            []string{"foo", "foo_64only"},
+			expected:        []string{"lib64/foo", "lib64/foo_64only"},
+		},
+		{
+			compileMultilib: "32",
+			deps:            []string{"foo", "foo_32only"},
+			expected:        []string{"lib32/foo", "lib32/foo_32only"},
+		},
+		{
+			compileMultilib: "both",
+			deps:            []string{"foo", "foo_32only", "foo_64only"},
+			expected:        []string{"lib32/foo", "lib32/foo_32only", "lib64/foo_64only"},
+		},
+	}
+	for _, tc := range testcases {
+		config := testConfig{
+			multiTarget:                true,
+			depsCollectFirstTargetOnly: true,
+		}
+		bp := strings.Replace(bpTemplate, "%COMPILE_MULTILIB%", tc.compileMultilib, -1)
+		bp = strings.Replace(bp, "%DEPS%", `["`+strings.Join(tc.deps, `", "`)+`"]`, -1)
+		runPackagingTest(t, config, bp, tc.expected)
+	}
+}
diff --git a/android/path_properties.go b/android/path_properties.go
index ea92565..6210aee 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -109,9 +109,9 @@
 			case reflect.Struct:
 				intf := sv.Interface()
 				if configurable, ok := intf.(proptools.Configurable[string]); ok {
-					ret = append(ret, proptools.String(configurable.Evaluate(ctx)))
+					ret = append(ret, configurable.GetOrDefault(ctx, ""))
 				} else if configurable, ok := intf.(proptools.Configurable[[]string]); ok {
-					ret = append(ret, proptools.Slice(configurable.Evaluate(ctx))...)
+					ret = append(ret, configurable.GetOrDefault(ctx, nil)...)
 				} else {
 					panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`,
 						v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
diff --git a/android/paths.go b/android/paths.go
index 61c1258..dda48dd 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -15,6 +15,9 @@
 package android
 
 import (
+	"bytes"
+	"encoding/gob"
+	"errors"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -60,6 +63,7 @@
 
 	ModuleDir() string
 	ModuleErrorf(fmt string, args ...interface{})
+	OtherModulePropertyErrorf(module Module, property, fmt string, args ...interface{})
 }
 
 var _ EarlyModulePathContext = ModuleContext(nil)
@@ -277,6 +281,7 @@
 
 type genPathProvider interface {
 	genPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleGenPath
+	genPathWithExtAndTrimExt(ctx ModuleOutPathContext, subdir, ext string, trimExt string) ModuleGenPath
 }
 type objPathProvider interface {
 	objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath
@@ -295,6 +300,16 @@
 	return PathForModuleGen(ctx)
 }
 
+// GenPathWithExtAndTrimExt derives a new file path in ctx's generated sources directory
+// from the current path, but with the new extension and trim the suffix.
+func GenPathWithExtAndTrimExt(ctx ModuleOutPathContext, subdir string, p Path, ext string, trimExt string) ModuleGenPath {
+	if path, ok := p.(genPathProvider); ok {
+		return path.genPathWithExtAndTrimExt(ctx, subdir, ext, trimExt)
+	}
+	ReportPathErrorf(ctx, "Tried to create generated file from unsupported path: %s(%s)", reflect.TypeOf(p).Name(), p)
+	return PathForModuleGen(ctx)
+}
+
 // ObjPathWithExt derives a new file path in ctx's object directory from the
 // current path, but with the new extension.
 func ObjPathWithExt(ctx ModuleOutPathContext, subdir string, p Path, ext string) ModuleObjPath {
@@ -448,8 +463,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
@@ -476,8 +491,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
@@ -550,24 +565,18 @@
 	if module == nil {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
-	if aModule, ok := module.(Module); ok && !aModule.Enabled() {
+	if aModule, ok := module.(Module); ok && !aModule.Enabled(ctx) {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
-	if outProducer, ok := module.(OutputFileProducer); ok {
-		outputFiles, err := outProducer.OutputFiles(tag)
-		if err != nil {
-			return nil, fmt.Errorf("path dependency %q: %s", path, err)
-		}
-		return outputFiles, nil
-	} else if tag != "" {
-		return nil, fmt.Errorf("path dependency %q is not an output file producing module", path)
-	} else if goBinary, ok := module.(bootstrap.GoBinaryTool); ok {
+	if goBinary, ok := module.(bootstrap.GoBinaryTool); ok && tag == "" {
 		goBinaryPath := PathForGoBinary(ctx, goBinary)
 		return Paths{goBinaryPath}, nil
-	} else if srcProducer, ok := module.(SourceFileProducer); ok {
-		return srcProducer.Srcs(), nil
+	}
+	outputFiles, err := outputFilesForModule(ctx, module, tag)
+	if outputFiles != nil && err == nil {
+		return outputFiles, nil
 	} else {
-		return nil, fmt.Errorf("path dependency %q is not a source file producing module", path)
+		return nil, err
 	}
 }
 
@@ -611,8 +620,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
@@ -673,7 +682,8 @@
 		expandedSrcFiles = append(expandedSrcFiles, srcFiles...)
 	}
 
-	return expandedSrcFiles, append(missingDeps, missingExcludeDeps...)
+	// TODO: b/334169722 - Replace with an error instead of implicitly removing duplicates.
+	return FirstUniquePaths(expandedSrcFiles), append(missingDeps, missingExcludeDeps...)
 }
 
 type missingDependencyError struct {
@@ -1061,6 +1071,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)
 }
@@ -1299,6 +1331,28 @@
 	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.soongOutDir), 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.soongOutDir), 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)
@@ -1506,6 +1560,17 @@
 	return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
 }
 
+func (p SourcePath) genPathWithExtAndTrimExt(ctx ModuleOutPathContext, subdir, ext string, trimExt string) ModuleGenPath {
+	// If Trim_extension being set, force append Output_extension without replace original extension.
+	if trimExt != "" {
+		if ext != "" {
+			return PathForModuleGen(ctx, subdir, strings.TrimSuffix(p.path, trimExt)+"."+ext)
+		}
+		return PathForModuleGen(ctx, subdir, strings.TrimSuffix(p.path, trimExt))
+	}
+	return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
 func (p SourcePath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
 	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
 }
@@ -1539,11 +1604,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
@@ -1593,6 +1657,17 @@
 	return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
 }
 
+func (p ModuleGenPath) genPathWithExtAndTrimExt(ctx ModuleOutPathContext, subdir, ext string, trimExt string) ModuleGenPath {
+	// If Trim_extension being set, force append Output_extension without replace original extension.
+	if trimExt != "" {
+		if ext != "" {
+			return PathForModuleGen(ctx, subdir, strings.TrimSuffix(p.path, trimExt)+"."+ext)
+		}
+		return PathForModuleGen(ctx, subdir, strings.TrimSuffix(p.path, trimExt))
+	}
+	return PathForModuleGen(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
 func (p ModuleGenPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
 	return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
 }
@@ -1831,17 +1906,13 @@
 	return base.Join(ctx, pathComponents...)
 }
 
-func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath {
-	base := pathForPartitionInstallDir(ctx, "", prefix, false)
-	return base.Join(ctx, paths...)
-}
-
-func PathForNdkInstall(ctx PathContext, paths ...string) InstallPath {
-	return pathForNdkOrSdkInstall(ctx, "ndk", paths)
+func PathForNdkInstall(ctx PathContext, paths ...string) OutputPath {
+	return PathForOutput(ctx, append([]string{"ndk"}, paths...)...)
 }
 
 func PathForMainlineSdksInstall(ctx PathContext, paths ...string) InstallPath {
-	return pathForNdkOrSdkInstall(ctx, "mainline-sdks", paths)
+	base := pathForPartitionInstallDir(ctx, "", "mainline-sdks", false)
+	return base.Join(ctx, paths...)
 }
 
 func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
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/prebuilt.go b/android/prebuilt.go
index 5a94a0f..51b86a5 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -71,6 +71,9 @@
 	//
 	// If specified then the prefer property is ignored in favor of the value of the Soong config
 	// variable.
+	//
+	// DEPRECATED: This property is being deprecated b/308188211.
+	// Use RELEASE_APEX_CONTRIBUTIONS build flags to select prebuilts of mainline modules.
 	Use_source_config_var *ConfigVarProperties
 }
 
@@ -272,7 +275,7 @@
 	srcPropertyName := proptools.PropertyNameForField(srcField)
 
 	srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
-		if !module.Enabled() {
+		if !module.Enabled(ctx) {
 			return nil
 		}
 		value := srcPropsValue.FieldByIndex(srcFieldIndex)
@@ -422,7 +425,7 @@
 	m := ctx.Module()
 	// If this module is a prebuilt, is enabled and has not been renamed to source then add a
 	// dependency onto the source if it is present.
-	if p := GetEmbeddedPrebuilt(m); p != nil && m.Enabled() && !p.properties.PrebuiltRenamedToSource {
+	if p := GetEmbeddedPrebuilt(m); p != nil && m.Enabled(ctx) && !p.properties.PrebuiltRenamedToSource {
 		bmn, _ := m.(baseModuleName)
 		name := bmn.BaseModuleName()
 		if ctx.OtherModuleReverseDependencyVariantExists(name) {
@@ -434,7 +437,7 @@
 		// TODO: When all branches contain this singleton module, make this strict
 		// TODO: Add this dependency only for mainline prebuilts and not every prebuilt module
 		if ctx.OtherModuleExists("all_apex_contributions") {
-			ctx.AddDependency(m, acDepTag, "all_apex_contributions")
+			ctx.AddDependency(m, AcDepTag, "all_apex_contributions")
 		}
 
 	}
@@ -471,7 +474,7 @@
 		}
 		// Propagate the provider received from `all_apex_contributions`
 		// to the source module
-		ctx.VisitDirectDepsWithTag(acDepTag, func(am Module) {
+		ctx.VisitDirectDepsWithTag(AcDepTag, func(am Module) {
 			psi, _ := OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
 			SetProvider(ctx, PrebuiltSelectionInfoProvider, psi)
 		})
@@ -577,7 +580,7 @@
 		bmn, _ := m.(baseModuleName)
 		name := bmn.BaseModuleName()
 		psi := PrebuiltSelectionInfoMap{}
-		ctx.VisitDirectDepsWithTag(acDepTag, func(am Module) {
+		ctx.VisitDirectDepsWithTag(AcDepTag, func(am Module) {
 			psi, _ = OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
 		})
 
@@ -699,15 +702,10 @@
 	}
 
 	// If source is not available or is disabled then always use the prebuilt.
-	if source == nil || !source.Enabled() {
+	if source == nil || !source.Enabled(ctx) {
 		return true
 	}
 
-	// If the use_source_config_var property is set then it overrides the prefer property setting.
-	if configVar := p.properties.Use_source_config_var; configVar != nil {
-		return !ctx.Config().VendorConfig(proptools.String(configVar.Config_namespace)).Bool(proptools.String(configVar.Var_name))
-	}
-
 	// TODO: use p.Properties.Name and ctx.ModuleDir to override preference
 	return Bool(p.properties.Prefer)
 }
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 2241b08..6e4fc0c 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -15,7 +15,6 @@
 package android
 
 import (
-	"fmt"
 	"testing"
 
 	"github.com/google/blueprint"
@@ -295,158 +294,6 @@
 				}`,
 			prebuilt: []OsType{Android, buildOS},
 		},
-		{
-			name: "prebuilt use_source_config_var={acme, use_source} - no var specified",
-			modules: `
-				source {
-					name: "bar",
-				}
-
-				prebuilt {
-					name: "bar",
-					use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
-					srcs: ["prebuilt_file"],
-				}`,
-			// When use_source_env is specified then it will use the prebuilt by default if the environment
-			// variable is not set.
-			prebuilt: []OsType{Android, buildOS},
-		},
-		{
-			name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=false",
-			modules: `
-				source {
-					name: "bar",
-				}
-
-				prebuilt {
-					name: "bar",
-					use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
-					srcs: ["prebuilt_file"],
-				}`,
-			preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
-				variables.VendorVars = map[string]map[string]string{
-					"acme": {
-						"use_source": "false",
-					},
-				}
-			}),
-			// Setting the environment variable named in use_source_env to false will cause the prebuilt to
-			// be used.
-			prebuilt: []OsType{Android, buildOS},
-		},
-		{
-			name: "apex_contributions supersedes any source preferred via use_source_config_var",
-			modules: `
-				source {
-					name: "bar",
-				}
-
-				prebuilt {
-					name: "bar",
-					use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
-					srcs: ["prebuilt_file"],
-				}
-				apex_contributions {
-					name: "my_mainline_module_contribution",
-					api_domain: "apexfoo",
-					// this metadata module contains prebuilt
-					contents: ["prebuilt_bar"],
-				}
-				all_apex_contributions {
-					name: "all_apex_contributions",
-				}
-				`,
-			preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
-				variables.VendorVars = map[string]map[string]string{
-					"acme": {
-						"use_source": "true",
-					},
-				}
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contribution",
-				}
-			}),
-			// use_source_config_var indicates that source should be used
-			// but this is superseded by `my_mainline_module_contribution`
-			prebuilt: []OsType{Android, buildOS},
-		},
-		{
-			name: "apex_contributions supersedes any prebuilt preferred via use_source_config_var",
-			modules: `
-				source {
-					name: "bar",
-				}
-
-				prebuilt {
-					name: "bar",
-					use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
-					srcs: ["prebuilt_file"],
-				}
-				apex_contributions {
-					name: "my_mainline_module_contribution",
-					api_domain: "apexfoo",
-					// this metadata module contains source
-					contents: ["bar"],
-				}
-				all_apex_contributions {
-					name: "all_apex_contributions",
-				}
-				`,
-			preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
-				variables.VendorVars = map[string]map[string]string{
-					"acme": {
-						"use_source": "false",
-					},
-				}
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contribution",
-				}
-			}),
-			// use_source_config_var indicates that prebuilt should be used
-			// but this is superseded by `my_mainline_module_contribution`
-			prebuilt: nil,
-		},
-		{
-			name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=true",
-			modules: `
-				source {
-					name: "bar",
-				}
-
-				prebuilt {
-					name: "bar",
-					use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
-					srcs: ["prebuilt_file"],
-				}`,
-			preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
-				variables.VendorVars = map[string]map[string]string{
-					"acme": {
-						"use_source": "true",
-					},
-				}
-			}),
-			// Setting the environment variable named in use_source_env to true will cause the source to be
-			// used.
-			prebuilt: nil,
-		},
-		{
-			name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=true, no source",
-			modules: `
-				prebuilt {
-					name: "bar",
-					use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
-					srcs: ["prebuilt_file"],
-				}`,
-			preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
-				variables.VendorVars = map[string]map[string]string{
-					"acme": {
-						"use_source": "true",
-					},
-				}
-			}),
-			// Although the environment variable says to use source there is no source available.
-			prebuilt: []OsType{Android, buildOS},
-		},
 	}
 
 	fs := MockFS{
@@ -503,7 +350,7 @@
 						}
 					})
 
-					moduleIsDisabled := !foo.Module().Enabled()
+					moduleIsDisabled := !foo.Module().Enabled(PanickingConfigAndErrorContext(result.TestContext))
 					deps := foo.Module().(*sourceModule).deps
 					if moduleIsDisabled {
 						if len(deps) > 0 {
@@ -646,7 +493,6 @@
 	properties struct {
 		Srcs []string `android:"path,arch_variant"`
 	}
-	src Path
 }
 
 func newPrebuiltModule() Module {
@@ -662,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"`
 }
@@ -762,45 +601,3 @@
 		}
 		`, selectMainlineModuleContritbutions)
 }
-
-// Test that apex_contributions of prebuilt modules are ignored in coverage builds
-func TestSourceIsSelectedInCoverageBuilds(t *testing.T) {
-	prebuiltMainlineContributions := GroupFixturePreparers(
-		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_prebuilt_apex_contributions",
-			}
-		}),
-		FixtureMergeEnv(map[string]string{
-			"EMMA_INSTRUMENT_FRAMEWORK": "true",
-		}),
-	)
-	bp := `
-		source {
-			name: "foo",
-		}
-		prebuilt {
-			name: "foo",
-			srcs: ["prebuilt_file"],
-		}
-		apex_contributions {
-			name: "my_prebuilt_apex_contributions",
-			api_domain: "my_mainline_module",
-			contents: [
-			  "prebuilt_foo",
-			],
-		}
-		all_apex_contributions {
-			name: "all_apex_contributions",
-		}
-		`
-	ctx := GroupFixturePreparers(
-		PrepareForTestWithArchMutator,
-		PrepareForTestWithPrebuilts,
-		FixtureRegisterWithContext(registerTestPrebuiltModules),
-		prebuiltMainlineContributions).RunTestWithBp(t, bp)
-	source := ctx.ModuleForTests("foo", "android_common").Module()
-	AssertBoolEquals(t, "Source should be preferred in coverage builds", true, !source.IsHideFromMake())
-	prebuilt := ctx.ModuleForTests("prebuilt_foo", "android_common").Module()
-	AssertBoolEquals(t, "Prebuilt should not be preferred in coverage builds", false, !prebuilt.IsHideFromMake())
-}
diff --git a/android/product_config.go b/android/product_config.go
new file mode 100644
index 0000000..20b29a7
--- /dev/null
+++ b/android/product_config.go
@@ -0,0 +1,58 @@
+// 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 += "."
+	}
+	soongVariablesPath := PathForOutput(ctx, "soong."+targetProduct+"variables")
+	extraVariablesPath := PathForOutput(ctx, "soong."+targetProduct+"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/register.go b/android/register.go
index d00c15f..eb6a35e 100644
--- a/android/register.go
+++ b/android/register.go
@@ -16,8 +16,9 @@
 
 import (
 	"fmt"
-	"github.com/google/blueprint"
 	"reflect"
+
+	"github.com/google/blueprint"
 )
 
 // A sortable component is one whose registration order affects the order in which it is executed
@@ -155,7 +156,6 @@
 func NewContext(config Config) *Context {
 	ctx := &Context{blueprint.NewContext(), config}
 	ctx.SetSrcDir(absSrcDir)
-	ctx.AddIncludeTags(config.IncludeTags()...)
 	ctx.AddSourceRootDirs(config.SourceRootDirs()...)
 	return ctx
 }
diff --git a/android/sdk.go b/android/sdk.go
index 6d5293e..4bcbe2e 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{}
@@ -868,3 +878,11 @@
 }
 
 var AdditionalSdkInfoProvider = blueprint.NewProvider[AdditionalSdkInfo]()
+
+var apiFingerprintPathKey = NewOnceKey("apiFingerprintPathKey")
+
+func ApiFingerprintPath(ctx PathContext) OutputPath {
+	return ctx.Config().Once(apiFingerprintPathKey, func() interface{} {
+		return PathForOutput(ctx, "api_fingerprint.txt")
+	}).(OutputPath)
+}
diff --git a/android/sdk_version.go b/android/sdk_version.go
index b2ff960..01b55d0 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -40,9 +40,15 @@
 // SdkKind represents a particular category of an SDK spec like public, system, test, etc.
 type SdkKind int
 
+// These are generally ordered from the narrower sdk version to the wider sdk version,
+// but not all entries have a strict subset/superset relationship.
+// For example, SdkTest and SdkModule do not have a strict subset/superset relationship but both
+// are supersets of SdkSystem.
+// The general trend should be kept when an additional sdk kind is added.
 const (
 	SdkInvalid SdkKind = iota
 	SdkNone
+	SdkToolchain // API surface provided by ART to compile other API domains
 	SdkCore
 	SdkCorePlatform
 	SdkIntraCore // API surface provided by one core module to another
@@ -53,7 +59,6 @@
 	SdkModule
 	SdkSystemServer
 	SdkPrivate
-	SdkToolchain // API surface provided by ART to compile other API domains
 )
 
 // String returns the string representation of this SdkKind
diff --git a/android/selects_test.go b/android/selects_test.go
index aa9c521..fc020a4 100644
--- a/android/selects_test.go
+++ b/android/selects_test.go
@@ -25,11 +25,14 @@
 
 func TestSelects(t *testing.T) {
 	testCases := []struct {
-		name          string
-		bp            string
-		provider      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",
@@ -39,7 +42,7 @@
 				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
 					"a": ["a.cpp"],
 					"b": ["b.cpp"],
-					_: ["c.cpp"],
+					default: ["c.cpp"],
 				}),
 			}
 			`,
@@ -55,7 +58,7 @@
 				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
 					"a": "a.cpp",
 					"b": "b.cpp",
-					_: "c.cpp",
+					default: "c.cpp",
 				}),
 			}
 			`,
@@ -71,7 +74,7 @@
 				my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
 					"a": true,
 					"b": false,
-					_: true,
+					default: true,
 				}),
 			}
 			`,
@@ -87,7 +90,7 @@
 				my_paths: select(soong_config_variable("my_namespace", "my_variable"), {
 					"a": ["foo.txt"],
 					"b": ["bar.txt"],
-					_: ["baz.txt"],
+					default: ["baz.txt"],
 				}),
 			}
 			`,
@@ -96,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 {
@@ -103,25 +126,25 @@
 				my_paths: select(soong_config_variable("my_namespace", "my_variable"), {
 					"a": [":a"],
 					"b": [":b"],
-					_: [":c"],
+					default: [":c"],
 				}),
 			}
 			`,
 			expectedError: `"foo" depends on undefined module "c"`,
 		},
 		{
-			name: "Differing types",
+			name: "Select type doesn't match property type",
 			bp: `
 			my_module_type {
 				name: "foo",
 				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
-					"a": "a.cpp",
+					"a": false,
 					"b": true,
-					_: "c.cpp",
+					default: true,
 				}),
 			}
 			`,
-			expectedError: `can't assign bool value to string property "my_string\[1\]"`,
+			expectedError: `can't assign bool value to string property`,
 		},
 		{
 			name: "String list non-default",
@@ -131,7 +154,7 @@
 				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
 					"a": ["a.cpp"],
 					"b": ["b.cpp"],
-					_: ["c.cpp"],
+					default: ["c.cpp"],
 				}),
 			}
 			`,
@@ -152,11 +175,11 @@
 				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
 					"a": ["a.cpp"],
 					"b": ["b.cpp"],
-					_: ["c.cpp"],
+					default: ["c.cpp"],
 				}) + select(soong_config_variable("my_namespace", "my_variable_2"), {
 					"a2": ["a2.cpp"],
 					"b2": ["b2.cpp"],
-					_: ["c2.cpp"],
+					default: ["c2.cpp"],
 				}),
 			}
 			`,
@@ -177,7 +200,7 @@
 				my_string_list: ["literal.cpp"] + select(soong_config_variable("my_namespace", "my_variable"), {
 					"a2": ["a2.cpp"],
 					"b2": ["b2.cpp"],
-					_: ["c2.cpp"],
+					default: ["c2.cpp"],
 				}),
 			}
 			`,
@@ -193,7 +216,7 @@
 				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
 					"a2": ["a2.cpp"],
 					"b2": ["b2.cpp"],
-					_: ["c2.cpp"],
+					default: ["c2.cpp"],
 				}) + ["literal.cpp"],
 			}
 			`,
@@ -202,18 +225,41 @@
 			},
 		},
 		{
-			name: "Can't append bools",
+			name: "true + false = true",
 			bp: `
 			my_module_type {
 				name: "foo",
 				my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
 					"a": true,
 					"b": false,
-					_: true,
+					default: true,
 				}) + false,
 			}
 			`,
-			expectedError: "my_bool: Cannot append bools",
+			provider: selectsTestProvider{
+				my_bool: proptools.BoolPtr(true),
+			},
+		},
+		{
+			name: "false + false = false",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": true,
+					"b": false,
+					default: true,
+				}) + false,
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "b",
+				},
+			},
+			provider: selectsTestProvider{
+				my_bool: proptools.BoolPtr(false),
+			},
 		},
 		{
 			name: "Append string",
@@ -223,7 +269,7 @@
 				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
 					"a": "a",
 					"b": "b",
-					_: "c",
+					default: "c",
 				}) + ".cpp",
 			}
 			`,
@@ -231,28 +277,782 @@
 				my_string: proptools.StringPtr("c.cpp"),
 			},
 		},
+		{
+			name: "Select on arch",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(arch(), {
+					"x86": "my_x86",
+					"x86_64": "my_x86_64",
+					"arm": "my_arm",
+					"arm64": "my_arm64",
+					default: "my_default",
+				}),
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("my_arm64"),
+			},
+		},
+		{
+			name: "Select on os",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(os(), {
+					"android": "my_android",
+					"linux": "my_linux",
+					default: "my_default",
+				}),
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("my_android"),
+			},
+		},
+		{
+			name: "Unset value",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": unset,
+					"b": "b",
+					default: "c",
+				})
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "a",
+				},
+			},
+			provider: selectsTestProvider{},
+		},
+		{
+			name: "Unset value on different branch",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": unset,
+					"b": "b",
+					default: "c",
+				})
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("c"),
+			},
+		},
+		{
+			name: "unset + unset = unset",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					"foo": "bar",
+					default: unset,
+				}) + select(soong_config_variable("my_namespace", "my_variable2"), {
+					"baz": "qux",
+					default: unset,
+				})
+			}
+			`,
+			provider: selectsTestProvider{},
+		},
+		{
+			name: "unset + string = string",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					"foo": "bar",
+					default: unset,
+				}) + select(soong_config_variable("my_namespace", "my_variable2"), {
+					default: "a",
+				})
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("a"),
+			},
+		},
+		{
+			name: "unset + bool = bool",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_bool: select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": true,
+					default: unset,
+				}) + select(soong_config_variable("my_namespace", "my_variable2"), {
+					default: true,
+				})
+			}
+			`,
+			provider: selectsTestProvider{
+				my_bool: proptools.BoolPtr(true),
+			},
+		},
+		{
+			name: "defaults with lists are appended",
+			bp: `
+			my_module_type {
+				name: "foo",
+				defaults: ["bar"],
+				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": ["a1"],
+					default: ["b1"],
+				}),
+			}
+			my_defaults {
+				name: "bar",
+				my_string_list: select(soong_config_variable("my_namespace", "my_variable2"), {
+					"a": ["a2"],
+					default: ["b2"],
+				}),
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string_list: &[]string{"b2", "b1"},
+			},
+		},
+		{
+			name: "defaults applied to multiple modules",
+			bp: `
+			my_module_type {
+				name: "foo2",
+				defaults: ["bar"],
+				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": ["a1"],
+					default: ["b1"],
+				}),
+			}
+			my_module_type {
+				name: "foo",
+				defaults: ["bar"],
+				my_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": ["a1"],
+					default: ["b1"],
+				}),
+			}
+			my_defaults {
+				name: "bar",
+				my_string_list: select(soong_config_variable("my_namespace", "my_variable2"), {
+					"a": ["a2"],
+					default: ["b2"],
+				}),
+			}
+			`,
+			providers: map[string]selectsTestProvider{
+				"foo": {
+					my_string_list: &[]string{"b2", "b1"},
+				},
+				"foo2": {
+					my_string_list: &[]string{"b2", "b1"},
+				},
+			},
+		},
+		{
+			name: "Replacing string list",
+			bp: `
+			my_module_type {
+				name: "foo",
+				defaults: ["bar"],
+				replacing_string_list: select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": ["a1"],
+					default: ["b1"],
+				}),
+			}
+			my_defaults {
+				name: "bar",
+				replacing_string_list: select(soong_config_variable("my_namespace", "my_variable2"), {
+					"a": ["a2"],
+					default: ["b2"],
+				}),
+			}
+			`,
+			provider: selectsTestProvider{
+				replacing_string_list: &[]string{"b1"},
+			},
+		},
+		{
+			name: "Multi-condition string 1",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select((
+					soong_config_variable("my_namespace", "my_variable"),
+					soong_config_variable("my_namespace", "my_variable2"),
+				), {
+					("a", "b"): "a+b",
+					("a", default): "a+default",
+					(default, default): "default",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable":  "a",
+					"my_variable2": "b",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("a+b"),
+			},
+		},
+		{
+			name: "Multi-condition string 2",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select((
+					soong_config_variable("my_namespace", "my_variable"),
+					soong_config_variable("my_namespace", "my_variable2"),
+				), {
+					("a", "b"): "a+b",
+					("a", default): "a+default",
+					(default, default): "default",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable":  "a",
+					"my_variable2": "c",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("a+default"),
+			},
+		},
+		{
+			name: "Multi-condition string 3",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select((
+					soong_config_variable("my_namespace", "my_variable"),
+					soong_config_variable("my_namespace", "my_variable2"),
+				), {
+					("a", "b"): "a+b",
+					("a", default): "a+default",
+					(default, default): "default",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable":  "c",
+					"my_variable2": "b",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("default"),
+			},
+		},
+		{
+			name: "Unhandled string value",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+					"foo": "a",
+					"bar": "b",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "baz",
+				},
+			},
+			expectedError: `my_string: soong_config_variable\("my_namespace", "my_variable"\) had value "baz", which was not handled by the select statement`,
+		},
+		{
+			name: "Select on boolean",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(boolean_var_for_testing(), {
+					true: "t",
+					false: "f",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"boolean_var": {
+					"for_testing": "true",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("t"),
+			},
+		},
+		{
+			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 {
+				name: "foo",
+				my_string: select(boolean_var_for_testing(), {
+					true: "t",
+					false: "f",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"boolean_var": {
+					"for_testing": "false",
+				},
+			},
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("f"),
+			},
+		},
+		{
+			name: "Select on boolean undefined",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(boolean_var_for_testing(), {
+					true: "t",
+					false: "f",
+				}),
+			}
+			`,
+			expectedError: `my_string: boolean_var_for_testing\(\) had value undefined, which was not handled by the select statement`,
+		},
+		{
+			name: "Select on boolean undefined with default",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(boolean_var_for_testing(), {
+					true: "t",
+					false: "f",
+					default: "default",
+				}),
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string: proptools.StringPtr("default"),
+			},
+		},
+		{
+			name: "Mismatched condition types",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string: select(boolean_var_for_testing(), {
+					"true": "t",
+					"false": "f",
+					default: "default",
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"boolean_var": {
+					"for_testing": "false",
+				},
+			},
+			expectedError: "Expected all branches of a select on condition boolean_var_for_testing\\(\\) to have type bool, found string",
+		},
+		{
+			name: "Assigning select to nonconfigurable bool",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_nonconfigurable_bool: select(arch(), {
+					"x86_64": true,
+					default: false,
+				}),
+			}
+			`,
+			expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_bool"`,
+		},
+		{
+			name: "Assigning select to nonconfigurable string",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_nonconfigurable_string: select(arch(), {
+					"x86_64": "x86!",
+					default: "unknown!",
+				}),
+			}
+			`,
+			expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string"`,
+		},
+		{
+			name: "Assigning appended selects to nonconfigurable string",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_nonconfigurable_string: select(arch(), {
+					"x86_64": "x86!",
+					default: "unknown!",
+				}) + select(os(), {
+					"darwin": "_darwin!",
+					default: "unknown!",
+				}),
+			}
+			`,
+			expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string"`,
+		},
+		{
+			name: "Assigning select to nonconfigurable string list",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_nonconfigurable_string_list: select(arch(), {
+					"x86_64": ["foo", "bar"],
+					default: ["baz", "qux"],
+				}),
+			}
+			`,
+			expectedError: `can't assign select statement to non-configurable property "my_nonconfigurable_string_list"`,
+		},
+		{
+			name: "Select in variable",
+			bp: `
+			my_second_variable = ["after.cpp"]
+			my_variable = select(soong_config_variable("my_namespace", "my_variable"), {
+				"a": ["a.cpp"],
+				"b": ["b.cpp"],
+				default: ["c.cpp"],
+			}) + my_second_variable
+			my_module_type {
+				name: "foo",
+				my_string_list: ["before.cpp"] + my_variable,
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string_list: &[]string{"before.cpp", "a.cpp", "after.cpp"},
+			},
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "a",
+				},
+			},
+		},
+		{
+			name: "Soong config value variable on configurable property",
+			bp: `
+			soong_config_module_type {
+				name: "soong_config_my_module_type",
+				module_type: "my_module_type",
+				config_namespace: "my_namespace",
+				value_variables: ["my_variable"],
+				properties: ["my_string", "my_string_list"],
+			}
+
+			soong_config_my_module_type {
+				name: "foo",
+				my_string_list: ["before.cpp"],
+				soong_config_variables: {
+					my_variable: {
+						my_string_list: ["after_%s.cpp"],
+						my_string: "%s.cpp",
+					},
+				},
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string:      proptools.StringPtr("foo.cpp"),
+				my_string_list: &[]string{"before.cpp", "after_foo.cpp"},
+			},
+			vendorVars: map[string]map[string]string{
+				"my_namespace": {
+					"my_variable": "foo",
+				},
+			},
+		},
+		{
+			name: "Property appending with variable",
+			bp: `
+			my_variable = ["b.cpp"]
+			my_module_type {
+				name: "foo",
+				my_string_list: ["a.cpp"] + my_variable + select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": ["a.cpp"],
+					"b": ["b.cpp"],
+					default: ["c.cpp"],
+				}),
+			}
+			`,
+			provider: selectsTestProvider{
+				my_string_list: &[]string{"a.cpp", "b.cpp", "c.cpp"},
+			},
+		},
+		{
+			name: "Test AppendSimpleValue",
+			bp: `
+			my_module_type {
+				name: "foo",
+				my_string_list: ["a.cpp"] + select(soong_config_variable("my_namespace", "my_variable"), {
+					"a": ["a.cpp"],
+					"b": ["b.cpp"],
+					default: ["c.cpp"],
+				}),
+			}
+			`,
+			vendorVars: map[string]map[string]string{
+				"selects_test": {
+					"append_to_string_list": "foo.cpp",
+				},
+			},
+			provider: selectsTestProvider{
+				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:`,
+		},
 	}
 
 	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,
+				PrepareForTestWithSoongConfigModuleBuildComponents,
 				FixtureRegisterWithContext(func(ctx RegistrationContext) {
 					ctx.RegisterModuleType("my_module_type", newSelectsMockModule)
+					ctx.RegisterModuleType("my_defaults", newSelectsMockModuleDefaults)
 				}),
 				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 == "" {
-				m := result.ModuleForTests("foo", "")
-				p, _ := OtherModuleProvider[selectsTestProvider](result.testContext.OtherModuleProviderAdaptor(), m.Module(), selectsTestProviderKey)
-				if !reflect.DeepEqual(p, tc.provider) {
-					t.Errorf("Expected:\n  %q\ngot:\n  %q", tc.provider.String(), p.String())
+				if len(tc.providers) == 0 {
+					tc.providers = map[string]selectsTestProvider{
+						"foo": tc.provider,
+					}
+				}
+
+				for moduleName := range tc.providers {
+					expected := tc.providers[moduleName]
+					m := result.ModuleForTests(moduleName, "android_arm64_armv8-a")
+					p, _ := OtherModuleProvider(result.testContext.OtherModuleProviderAdaptor(), m.Module(), selectsTestProviderKey)
+					if !reflect.DeepEqual(p, expected) {
+						t.Errorf("Expected:\n  %q\ngot:\n  %q", expected.String(), p.String())
+					}
 				}
 			}
 		})
@@ -260,10 +1060,15 @@
 }
 
 type selectsTestProvider struct {
-	my_bool        *bool
-	my_string      *string
-	my_string_list *[]string
-	my_paths       *[]string
+	my_bool                        *bool
+	my_string                      *string
+	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
 }
 
 func (p *selectsTestProvider) String() string {
@@ -275,21 +1080,45 @@
 	if p.my_string != nil {
 		myStringStr = *p.my_string
 	}
+	myNonconfigurableStringStr := "nil"
+	if p.my_nonconfigurable_string != nil {
+		myNonconfigurableStringStr = *p.my_nonconfigurable_string
+	}
 	return fmt.Sprintf(`selectsTestProvider {
 	my_bool: %v,
 	my_string: %s,
     my_string_list: %s,
     my_paths: %s,
-}`, myBoolStr, myStringStr, p.my_string_list, p.my_paths)
+	replacing_string_list %s,
+	arch_variant_configurable_bool %v
+	my_nonconfigurable_bool: %v,
+	my_nonconfigurable_string: %s,
+	my_nonconfigurable_string_list: %s,
+}`,
+		myBoolStr,
+		myStringStr,
+		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,
+	)
 }
 
 var selectsTestProviderKey = blueprint.NewProvider[selectsTestProvider]()
 
 type selectsMockModuleProperties struct {
-	My_bool        proptools.Configurable[bool]
-	My_string      proptools.Configurable[string]
-	My_string_list proptools.Configurable[[]string]
-	My_paths       proptools.Configurable[[]string] `android:"path"`
+	My_bool                        proptools.Configurable[bool]
+	My_string                      proptools.Configurable[string]
+	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
 }
 
 type selectsMockModule struct {
@@ -298,19 +1127,56 @@
 	properties selectsMockModuleProperties
 }
 
+func optionalToPtr[T any](o proptools.ConfigurableOptional[T]) *T {
+	if o.IsEmpty() {
+		return nil
+	}
+	x := o.Get()
+	return &x
+}
+
 func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	toAppend := ctx.Config().VendorConfig("selects_test").String("append_to_string_list")
+	if toAppend != "" {
+		p.properties.My_string_list.AppendSimpleValue([]string{toAppend})
+	}
 	SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{
-		my_bool:        p.properties.My_bool.Evaluate(ctx),
-		my_string:      p.properties.My_string.Evaluate(ctx),
-		my_string_list: p.properties.My_string_list.Evaluate(ctx),
-		my_paths:       p.properties.My_paths.Evaluate(ctx),
+		my_bool:                        optionalToPtr(p.properties.My_bool.Get(ctx)),
+		my_string:                      optionalToPtr(p.properties.My_string.Get(ctx)),
+		my_string_list:                 optionalToPtr(p.properties.My_string_list.Get(ctx)),
+		my_paths:                       optionalToPtr(p.properties.My_paths.Get(ctx)),
+		replacing_string_list:          optionalToPtr(p.properties.Replacing_string_list.Get(ctx)),
+		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,
 	})
 }
 
 func newSelectsMockModule() Module {
 	m := &selectsMockModule{}
 	m.AddProperties(&m.properties)
-	InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
+	InitAndroidArchModule(m, HostAndDeviceSupported, MultilibFirst)
 	InitDefaultableModule(m)
 	return m
 }
+
+type selectsMockModuleDefaults struct {
+	ModuleBase
+	DefaultsModuleBase
+}
+
+func (d *selectsMockModuleDefaults) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func newSelectsMockModuleDefaults() Module {
+	module := &selectsMockModuleDefaults{}
+
+	module.AddProperties(
+		&selectsMockModuleProperties{},
+	)
+
+	InitDefaultsModule(module)
+
+	return module
+}
diff --git a/android/shared_properties.go b/android/shared_properties.go
new file mode 100644
index 0000000..84d68fa
--- /dev/null
+++ b/android/shared_properties.go
@@ -0,0 +1,26 @@
+// 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
+
+// For storing user-supplied properties about source code on a module to be queried later.
+type SourceProperties struct {
+	// Indicates that the module and its source code are only used in tests, not
+	// production code. Used by coverage reports and potentially other tools.
+	Test_only *bool
+	// Used internally to write if this is a top level test target.
+	// i.e. something that can be run directly or through tradefed as a test.
+	// `java_library` would be false, `java_test` would be true.
+	Top_level_test_target bool `blueprint:"mutated"`
+}
diff --git a/android/singleton.go b/android/singleton.go
index ccddeaf..d364384 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -87,6 +87,9 @@
 	// builder whenever a file matching the pattern as added or removed, without rerunning if a
 	// file that does not match the pattern is added to a searched directory.
 	GlobWithDeps(pattern string, excludes []string) ([]string, error)
+
+	// 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{})
 }
 
 type singletonAdaptor struct {
@@ -251,7 +254,7 @@
 
 func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name string) []Module {
 	// get module reference for visibility enforcement
-	qualified := createVisibilityModuleReference(s.ModuleName(referer), s.ModuleDir(referer), s.ModuleType(referer))
+	qualified := createVisibilityModuleReference(s.ModuleName(referer), s.ModuleDir(referer), referer)
 
 	modules := s.SingletonContext.ModuleVariantsFromName(referer, name)
 	result := make([]Module, 0, len(modules))
@@ -279,3 +282,7 @@
 func (s *singletonContextAdaptor) moduleProvider(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...)
+}
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 90b49eb..e0b1d7c 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -64,6 +64,7 @@
 // specified in `conditions_default` will only be used under the following conditions:
 //   bool variable: the variable is unspecified or not set to a true value
 //   value variable: the variable is unspecified
+//   list variable: the variable is unspecified
 //   string variable: the variable is unspecified or the variable is set to a string unused in the
 //                    given module. For example, string variable `test` takes values: "a" and "b",
 //                    if the module contains a property `a` and `conditions_default`, when test=b,
@@ -104,6 +105,12 @@
 //                     cflags: ["-DWIDTH=DEFAULT"],
 //                 },
 //             },
+//             impl: {
+//                 srcs: ["impl/%s"],
+//                 conditions_default: {
+//                     srcs: ["impl/default.cpp"],
+//                 },
+//             },
 //         },
 //     }
 //
@@ -122,6 +129,7 @@
 //         variables: ["board"],
 //         bool_variables: ["feature"],
 //         value_variables: ["width"],
+//         list_variables: ["impl"],
 //         properties: ["cflags", "srcs"],
 //     }
 //
@@ -135,8 +143,10 @@
 //     $(call add_soong_config_var_value, acme, board, soc_a)
 //     $(call add_soong_config_var_value, acme, feature, true)
 //     $(call add_soong_config_var_value, acme, width, 200)
+//     $(call add_soong_config_var_value, acme, impl, foo.cpp bar.cpp)
 //
-// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
+// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200" and srcs
+// ["*.cpp", "impl/foo.cpp", "impl/bar.cpp"].
 //
 // Alternatively, if acme BoardConfig.mk file contained:
 //
@@ -148,7 +158,9 @@
 //     SOONG_CONFIG_acme_feature := false
 //
 // Then libacme_foo would build with cflags:
-//   "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
+//   "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT"
+// and with srcs:
+//   ["*.cpp", "impl/default.cpp"].
 //
 // Similarly, if acme BoardConfig.mk file contained:
 //
@@ -158,9 +170,13 @@
 //         feature \
 //
 //     SOONG_CONFIG_acme_board := soc_c
+//     SOONG_CONFIG_acme_impl := foo.cpp bar.cpp
 //
 // Then libacme_foo would build with cflags:
-//   "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
+//   "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT"
+// and with srcs:
+//   ["*.cpp", "impl/foo.cpp", "impl/bar.cpp"].
+//
 
 func SoongConfigModuleTypeImportFactory() Module {
 	module := &soongConfigModuleTypeImport{}
@@ -201,6 +217,7 @@
 //
 //	bool variable: the variable is unspecified or not set to a true value
 //	value variable: the variable is unspecified
+//	list variable: the variable is unspecified
 //	string variable: the variable is unspecified or the variable is set to a string unused in the
 //	                 given module. For example, string variable `test` takes values: "a" and "b",
 //	                 if the module contains a property `a` and `conditions_default`, when test=b,
@@ -209,56 +226,63 @@
 //
 // For example, an Android.bp file could have:
 //
-//	    soong_config_module_type {
-//	        name: "acme_cc_defaults",
-//	        module_type: "cc_defaults",
-//	        config_namespace: "acme",
-//	        variables: ["board"],
-//	        bool_variables: ["feature"],
-//	        value_variables: ["width"],
-//	        properties: ["cflags", "srcs"],
-//	    }
+//	soong_config_module_type {
+//	    name: "acme_cc_defaults",
+//	    module_type: "cc_defaults",
+//	    config_namespace: "acme",
+//	    variables: ["board"],
+//	    bool_variables: ["feature"],
+//	    value_variables: ["width"],
+//	    list_variables: ["impl"],
+//	    properties: ["cflags", "srcs"],
+//	}
 //
-//	    soong_config_string_variable {
-//	        name: "board",
-//	        values: ["soc_a", "soc_b"],
-//	    }
+//	soong_config_string_variable {
+//	    name: "board",
+//	    values: ["soc_a", "soc_b"],
+//	}
 //
-//	    acme_cc_defaults {
-//	        name: "acme_defaults",
-//	        cflags: ["-DGENERIC"],
-//	        soong_config_variables: {
-//	            board: {
-//	                soc_a: {
-//	                    cflags: ["-DSOC_A"],
-//	                },
-//	                soc_b: {
-//	                    cflags: ["-DSOC_B"],
-//	                },
-//	                conditions_default: {
-//	                    cflags: ["-DSOC_DEFAULT"],
-//	                },
+//	acme_cc_defaults {
+//	    name: "acme_defaults",
+//	    cflags: ["-DGENERIC"],
+//	    soong_config_variables: {
+//	        board: {
+//	            soc_a: {
+//	                cflags: ["-DSOC_A"],
 //	            },
-//	            feature: {
-//	                cflags: ["-DFEATURE"],
-//	                conditions_default: {
-//	                    cflags: ["-DFEATURE_DEFAULT"],
-//	                },
+//	            soc_b: {
+//	                cflags: ["-DSOC_B"],
 //	            },
-//	            width: {
-//		               cflags: ["-DWIDTH=%s"],
-//	                conditions_default: {
-//	                    cflags: ["-DWIDTH=DEFAULT"],
-//	                },
+//	            conditions_default: {
+//	                cflags: ["-DSOC_DEFAULT"],
 //	            },
 //	        },
-//	    }
+//	        feature: {
+//	            cflags: ["-DFEATURE"],
+//	            conditions_default: {
+//	                cflags: ["-DFEATURE_DEFAULT"],
+//	            },
+//	        },
+//	        width: {
+//	            cflags: ["-DWIDTH=%s"],
+//	            conditions_default: {
+//	                cflags: ["-DWIDTH=DEFAULT"],
+//	            },
+//	        },
+//	        impl: {
+//	            srcs: ["impl/%s"],
+//	            conditions_default: {
+//	                srcs: ["impl/default.cpp"],
+//	            },
+//	        },
+//	    },
+//	}
 //
-//	    cc_library {
-//	        name: "libacme_foo",
-//	        defaults: ["acme_defaults"],
-//	        srcs: ["*.cpp"],
-//	    }
+//	cc_library {
+//	    name: "libacme_foo",
+//	    defaults: ["acme_defaults"],
+//	    srcs: ["*.cpp"],
+//	}
 //
 // If an acme BoardConfig.mk file contained:
 //
@@ -270,8 +294,10 @@
 //	SOONG_CONFIG_acme_board := soc_a
 //	SOONG_CONFIG_acme_feature := true
 //	SOONG_CONFIG_acme_width := 200
+//	SOONG_CONFIG_acme_impl := foo.cpp bar.cpp
 //
-// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
+// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE" and srcs
+// ["*.cpp", "impl/foo.cpp", "impl/bar.cpp"].
 func SoongConfigModuleTypeFactory() Module {
 	module := &soongConfigModuleTypeModule{}
 
@@ -437,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 {
@@ -535,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
@@ -544,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 c78b726..f6046d0 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -117,6 +117,10 @@
 	// inserted into the properties with %s substitution.
 	Value_variables []string
 
+	// the list of SOONG_CONFIG list variables that this module type will read. Each value will be
+	// inserted into the properties with %s substitution.
+	List_variables []string
+
 	// the list of properties that this module type will extend.
 	Properties []string
 }
@@ -468,6 +472,18 @@
 		})
 	}
 
+	for _, name := range props.List_variables {
+		if err := checkVariableName(name); err != nil {
+			return nil, []error{fmt.Errorf("list_variables %s", err)}
+		}
+
+		mt.Variables = append(mt.Variables, &listVariable{
+			baseVariable: baseVariable{
+				variable: name,
+			},
+		})
+	}
+
 	return mt, nil
 }
 
@@ -717,11 +733,107 @@
 		case reflect.Bool:
 			// Nothing to do
 		case reflect.Struct:
-			fieldName = append(fieldName, propStruct.Type().Field(i).Name)
-			if err := s.printfIntoPropertyRecursive(fieldName, field, configValue); err != nil {
-				return err
+			if proptools.IsConfigurable(field.Type()) {
+				if err := proptools.PrintfIntoConfigurable(field.Interface(), configValue); err != nil {
+					fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+					return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
+				}
+			} else {
+				fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+				if err := s.printfIntoPropertyRecursive(fieldName, field, configValue); err != nil {
+					return err
+				}
+				fieldName = fieldName[:len(fieldName)-1]
 			}
-			fieldName = fieldName[:len(fieldName)-1]
+		default:
+			fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+			return fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, strings.Join(fieldName, "."), kind)
+		}
+	}
+	return nil
+}
+
+// Struct to allow conditions set based on a list variable, supporting string substitution.
+type listVariable struct {
+	baseVariable
+}
+
+func (s *listVariable) variableValuesType() reflect.Type {
+	return emptyInterfaceType
+}
+
+// initializeProperties initializes a property to zero value of typ with an additional conditions
+// default field.
+func (s *listVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
+	initializePropertiesWithDefault(v, typ)
+}
+
+// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
+// the module. If the variable was not set, conditions_default interface will be returned;
+// otherwise, the interface in values, without conditions_default will be returned with all
+// appropriate string substitutions based on variable being set.
+func (s *listVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
+	// If this variable was not referenced in the module, there are no properties to apply.
+	if !values.IsValid() || values.Elem().IsZero() {
+		return nil, nil
+	}
+	if !config.IsSet(s.variable) {
+		return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
+	}
+	configValues := strings.Split(config.String(s.variable), " ")
+
+	values = removeDefault(values)
+	propStruct := values.Elem()
+	if !propStruct.IsValid() {
+		return nil, nil
+	}
+	if err := s.printfIntoPropertyRecursive(nil, propStruct, configValues); err != nil {
+		return nil, err
+	}
+
+	return values.Interface(), nil
+}
+
+func (s *listVariable) printfIntoPropertyRecursive(fieldName []string, propStruct reflect.Value, configValues []string) error {
+	for i := 0; i < propStruct.NumField(); i++ {
+		field := propStruct.Field(i)
+		kind := field.Kind()
+		if kind == reflect.Ptr {
+			if field.IsNil() {
+				continue
+			}
+			field = field.Elem()
+			kind = field.Kind()
+		}
+		switch kind {
+		case reflect.Slice:
+			elemType := field.Type().Elem()
+			newLen := field.Len() * len(configValues)
+			newField := reflect.MakeSlice(field.Type(), 0, newLen)
+			for j := 0; j < field.Len(); j++ {
+				for _, configValue := range configValues {
+					res := reflect.Indirect(reflect.New(elemType))
+					res.Set(field.Index(j))
+					err := printfIntoProperty(res, configValue)
+					if err != nil {
+						fieldName = append(fieldName, propStruct.Type().Field(i).Name)
+						return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
+					}
+					newField = reflect.Append(newField, res)
+				}
+			}
+			field.Set(newField)
+		case reflect.Struct:
+			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]
+			}
 		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)
@@ -739,7 +851,7 @@
 	}
 
 	if count > 1 {
-		return fmt.Errorf("value variable properties only support a single '%%'")
+		return fmt.Errorf("list/value variable properties only support a single '%%'")
 	}
 
 	if !strings.Contains(s, "%s") {
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index 1da0b49..d76794c 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -291,11 +291,13 @@
 type properties struct {
 	A *string
 	B bool
+	C []string
 }
 
-type boolVarProps struct {
+type varProps struct {
 	A                  *string
 	B                  bool
+	C                  []string
 	Conditions_default *properties
 }
 
@@ -311,6 +313,19 @@
 	My_value_var interface{}
 }
 
+type listProperties struct {
+	C []string
+}
+
+type listVarProps struct {
+	C                  []string
+	Conditions_default *listProperties
+}
+
+type listSoongConfigVars struct {
+	List_var interface{}
+}
+
 func Test_PropertiesToApply_Bool(t *testing.T) {
 	mt, _ := newModuleType(&ModuleTypeProperties{
 		Module_type:      "foo",
@@ -330,7 +345,7 @@
 		Soong_config_variables boolSoongConfigVars
 	}{
 		Soong_config_variables: boolSoongConfigVars{
-			Bool_var: &boolVarProps{
+			Bool_var: &varProps{
 				A:                  boolVarPositive.A,
 				B:                  boolVarPositive.B,
 				Conditions_default: conditionsDefault,
@@ -373,6 +388,59 @@
 	}
 }
 
+func Test_PropertiesToApply_List(t *testing.T) {
+	mt, _ := newModuleType(&ModuleTypeProperties{
+		Module_type:      "foo",
+		Config_namespace: "bar",
+		List_variables:   []string{"my_list_var"},
+		Properties:       []string{"c"},
+	})
+	conditionsDefault := &listProperties{
+		C: []string{"default"},
+	}
+	actualProps := &struct {
+		Soong_config_variables listSoongConfigVars
+	}{
+		Soong_config_variables: listSoongConfigVars{
+			List_var: &listVarProps{
+				C:                  []string{"A=%s", "B=%s"},
+				Conditions_default: conditionsDefault,
+			},
+		},
+	}
+	props := reflect.ValueOf(actualProps)
+
+	testCases := []struct {
+		name      string
+		config    SoongConfig
+		wantProps []interface{}
+	}{
+		{
+			name:      "no_vendor_config",
+			config:    Config(map[string]string{}),
+			wantProps: []interface{}{conditionsDefault},
+		},
+		{
+			name:   "value_var_set",
+			config: Config(map[string]string{"my_list_var": "hello there"}),
+			wantProps: []interface{}{&listProperties{
+				C: []string{"A=hello", "A=there", "B=hello", "B=there"},
+			}},
+		},
+	}
+
+	for _, tc := range testCases {
+		gotProps, err := PropertiesToApply(mt, props, tc.config)
+		if err != nil {
+			t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err)
+		}
+
+		if !reflect.DeepEqual(gotProps, tc.wantProps) {
+			t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps)
+		}
+	}
+}
+
 func Test_PropertiesToApply_Value(t *testing.T) {
 	mt, _ := newModuleType(&ModuleTypeProperties{
 		Module_type:      "foo",
@@ -388,7 +456,7 @@
 		Soong_config_variables valueSoongConfigVars
 	}{
 		Soong_config_variables: valueSoongConfigVars{
-			My_value_var: &boolVarProps{
+			My_value_var: &varProps{
 				A:                  proptools.StringPtr("A=%s"),
 				B:                  true,
 				Conditions_default: conditionsDefault,
@@ -524,7 +592,7 @@
 		Soong_config_variables stringSoongConfigVars
 	}{
 		Soong_config_variables: stringSoongConfigVars{
-			String_var: &boolVarProps{
+			String_var: &varProps{
 				A:                  stringVarPositive.A,
 				B:                  stringVarPositive.B,
 				Conditions_default: conditionsDefault,
diff --git a/android/team.go b/android/team.go
index df61f40..c273dc6 100644
--- a/android/team.go
+++ b/android/team.go
@@ -14,6 +14,8 @@
 
 package android
 
+import "github.com/google/blueprint"
+
 func init() {
 	RegisterTeamBuildComponents(InitRegistrationContext)
 }
@@ -37,6 +39,13 @@
 	properties teamProperties
 }
 
+type TestModuleInformation struct {
+	TestOnly       bool
+	TopLevelTarget bool
+}
+
+var TestOnlyProviderKey = blueprint.NewProvider[TestModuleInformation]()
+
 // Real work is done for the module that depends on us.
 // If needed, the team can serialize the config to json/proto file as well.
 func (t *teamModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
diff --git a/android/team_proto/team.pb.go b/android/team_proto/team.pb.go
index 61260cf..8414d17 100644
--- a/android/team_proto/team.pb.go
+++ b/android/team_proto/team.pb.go
@@ -46,6 +46,13 @@
 	TrendyTeamId *string `protobuf:"bytes,3,opt,name=trendy_team_id,json=trendyTeamId" json:"trendy_team_id,omitempty"`
 	// OPTIONAL: Files directly owned by this module.
 	File []string `protobuf:"bytes,4,rep,name=file" json:"file,omitempty"`
+	// OPTIONAL: Is this a test-only module.
+	TestOnly *bool `protobuf:"varint,5,opt,name=test_only,json=testOnly" json:"test_only,omitempty"`
+	// OPTIONAL: Is this intended to be run as a test target.
+	// This target can be run directly as a test or passed to tradefed.
+	TopLevelTarget *bool `protobuf:"varint,6,opt,name=top_level_target,json=topLevelTarget" json:"top_level_target,omitempty"`
+	// OPTIONAL: Name of module kind, i.e. java_library
+	Kind *string `protobuf:"bytes,7,opt,name=kind" json:"kind,omitempty"`
 }
 
 func (x *Team) Reset() {
@@ -108,6 +115,27 @@
 	return nil
 }
 
+func (x *Team) GetTestOnly() bool {
+	if x != nil && x.TestOnly != nil {
+		return *x.TestOnly
+	}
+	return false
+}
+
+func (x *Team) GetTopLevelTarget() bool {
+	if x != nil && x.TopLevelTarget != nil {
+		return *x.TopLevelTarget
+	}
+	return false
+}
+
+func (x *Team) GetKind() string {
+	if x != nil && x.Kind != nil {
+		return *x.Kind
+	}
+	return ""
+}
+
 type AllTeams struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -159,20 +187,26 @@
 
 var file_team_proto_rawDesc = []byte{
 	0x0a, 0x0a, 0x74, 0x65, 0x61, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x74, 0x65,
-	0x61, 0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x75, 0x0a, 0x04, 0x54, 0x65, 0x61, 0x6d,
-	0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d,
-	0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x5f,
-	0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74,
-	0x72, 0x65, 0x6e, 0x64, 0x79, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66,
-	0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22,
-	0x32, 0x0a, 0x08, 0x41, 0x6c, 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x74,
-	0x65, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x61,
-	0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x05, 0x74, 0x65,
-	0x61, 0x6d, 0x73, 0x42, 0x22, 0x5a, 0x20, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
-	0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x74, 0x65, 0x61,
-	0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x61, 0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd0, 0x01, 0x0a, 0x04, 0x54, 0x65, 0x61,
+	0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61,
+	0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79,
+	0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
+	0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04,
+	0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65,
+	0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x28, 0x0a,
+	0x10, 0x74, 0x6f, 0x70, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65,
+	0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, 0x6f, 0x70, 0x4c, 0x65, 0x76, 0x65,
+	0x6c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18,
+	0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x32, 0x0a, 0x08, 0x41,
+	0x6c, 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x74, 0x65, 0x61, 0x6d, 0x73,
+	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x05, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x42,
+	0x22, 0x5a, 0x20, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f,
 }
 
 var (
diff --git a/android/team_proto/team.proto b/android/team_proto/team.proto
index 401eccc..69ebf43 100644
--- a/android/team_proto/team.proto
+++ b/android/team_proto/team.proto
@@ -27,6 +27,16 @@
 
   // OPTIONAL: Files directly owned by this module.
   repeated string file = 4;
+
+  // OPTIONAL: Is this a test-only module.
+  optional bool test_only = 5;
+
+  // OPTIONAL: Is this intended to be run as a test target.
+  // This target can be run directly as a test or passed to tradefed.
+  optional bool top_level_target = 6;
+
+  // OPTIONAL: Name of module kind, i.e. java_library
+  optional string kind = 7;
 }
 
 message AllTeams {
diff --git a/android/team_test.go b/android/team_test.go
index 75b3e9f..ccfcaaa 100644
--- a/android/team_test.go
+++ b/android/team_test.go
@@ -27,16 +27,19 @@
 	return module
 }
 
+var prepareForTestWithTeamAndFakeBuildComponents = GroupFixturePreparers(
+	FixtureRegisterWithContext(RegisterTeamBuildComponents),
+	FixtureRegisterWithContext(func(ctx RegistrationContext) {
+		ctx.RegisterModuleType("fake", fakeModuleFactory)
+	}),
+)
+
 func (*fakeModuleForTests) GenerateAndroidBuildActions(ModuleContext) {}
 
 func TestTeam(t *testing.T) {
 	t.Parallel()
-	ctx := GroupFixturePreparers(
-		PrepareForTestWithTeamBuildComponents,
-		FixtureRegisterWithContext(func(ctx RegistrationContext) {
-			ctx.RegisterModuleType("fake", fakeModuleFactory)
-		}),
-	).RunTestWithBp(t, `
+	ctx := prepareForTestWithTeamAndFakeBuildComponents.
+		RunTestWithBp(t, `
 		fake {
 			name: "main_test",
 			team: "someteam",
@@ -66,12 +69,7 @@
 
 func TestMissingTeamFails(t *testing.T) {
 	t.Parallel()
-	GroupFixturePreparers(
-		PrepareForTestWithTeamBuildComponents,
-		FixtureRegisterWithContext(func(ctx RegistrationContext) {
-			ctx.RegisterModuleType("fake", fakeModuleFactory)
-		}),
-	).
+	prepareForTestWithTeamAndFakeBuildComponents.
 		ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern("depends on undefined module \"ring-bearer")).
 		RunTestWithBp(t, `
 		fake {
@@ -86,9 +84,6 @@
 	GroupFixturePreparers(
 		PrepareForTestWithTeamBuildComponents,
 		PrepareForTestWithPackageModule,
-		FixtureRegisterWithContext(func(ctx RegistrationContext) {
-			ctx.RegisterModuleType("fake", fakeModuleFactory)
-		}),
 	).
 		ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern("depends on undefined module \"ring-bearer")).
 		RunTestWithBp(t, `
diff --git a/android/test_config.go b/android/test_config.go
index a15343a..f251038 100644
--- a/android/test_config.go
+++ b/android/test_config.go
@@ -50,7 +50,7 @@
 			AAPTCharacteristics:                 stringPtr("nosdcard"),
 			AAPTPrebuiltDPI:                     []string{"xhdpi", "xxhdpi"},
 			UncompressPrivAppDex:                boolPtr(true),
-			ShippingApiLevel:                    stringPtr("30"),
+			Shipping_api_level:                  stringPtr("30"),
 		},
 
 		outDir:       buildDir,
diff --git a/android/test_suites.go b/android/test_suites.go
index adcc15a..ff75f26 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -14,6 +14,11 @@
 
 package android
 
+import (
+	"path/filepath"
+	"strings"
+)
+
 func init() {
 	RegisterParallelSingletonType("testsuites", testSuiteFilesFactory)
 }
@@ -23,8 +28,8 @@
 }
 
 type testSuiteFiles struct {
-	robolectric WritablePath
-	ravenwood   WritablePath
+	robolectric []Path
+	ravenwood   []Path
 }
 
 type TestSuiteModule interface {
@@ -48,53 +53,107 @@
 	})
 
 	t.robolectric = robolectricTestSuite(ctx, files["robolectric-tests"])
-	ctx.Phony("robolectric-tests", t.robolectric)
+	ctx.Phony("robolectric-tests", t.robolectric...)
 
 	t.ravenwood = ravenwoodTestSuite(ctx, files["ravenwood-tests"])
-	ctx.Phony("ravenwood-tests", t.ravenwood)
+	ctx.Phony("ravenwood-tests", t.ravenwood...)
 }
 
 func (t *testSuiteFiles) MakeVars(ctx MakeVarsContext) {
-	ctx.DistForGoal("robolectric-tests", t.robolectric)
-	ctx.DistForGoal("ravenwood-tests", t.ravenwood)
+	ctx.DistForGoal("robolectric-tests", t.robolectric...)
+	ctx.DistForGoal("ravenwood-tests", t.ravenwood...)
 }
 
-func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) WritablePath {
+func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) []Path {
 	var installedPaths InstallPaths
 	for _, module := range SortedKeys(files) {
 		installedPaths = append(installedPaths, files[module]...)
 	}
-	testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases")
 
-	outputFile := PathForOutput(ctx, "packaging", "robolectric-tests.zip")
+	outputFile := pathForPackaging(ctx, "robolectric-tests.zip")
 	rule := NewRuleBuilder(pctx, ctx)
 	rule.Command().BuiltTool("soong_zip").
 		FlagWithOutput("-o ", outputFile).
 		FlagWithArg("-P ", "host/testcases").
-		FlagWithArg("-C ", testCasesDir.String()).
+		FlagWithArg("-C ", pathForTestCases(ctx).String()).
 		FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths()).
+		Flag("-sha256") // necessary to save cas_uploader's time
+
+	testList := buildTestList(ctx, "robolectric-tests_list", installedPaths)
+	testListZipOutputFile := pathForPackaging(ctx, "robolectric-tests_list.zip")
+
+	rule.Command().BuiltTool("soong_zip").
+		FlagWithOutput("-o ", testListZipOutputFile).
+		FlagWithArg("-C ", pathForPackaging(ctx).String()).
+		FlagWithInput("-f ", testList).
 		Flag("-sha256")
+
 	rule.Build("robolectric_tests_zip", "robolectric-tests.zip")
 
-	return outputFile
+	return []Path{outputFile, testListZipOutputFile}
 }
 
-func ravenwoodTestSuite(ctx SingletonContext, files map[string]InstallPaths) WritablePath {
+func ravenwoodTestSuite(ctx SingletonContext, files map[string]InstallPaths) []Path {
 	var installedPaths InstallPaths
 	for _, module := range SortedKeys(files) {
 		installedPaths = append(installedPaths, files[module]...)
 	}
-	testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases")
 
-	outputFile := PathForOutput(ctx, "packaging", "ravenwood-tests.zip")
+	outputFile := pathForPackaging(ctx, "ravenwood-tests.zip")
 	rule := NewRuleBuilder(pctx, ctx)
 	rule.Command().BuiltTool("soong_zip").
 		FlagWithOutput("-o ", outputFile).
 		FlagWithArg("-P ", "host/testcases").
-		FlagWithArg("-C ", testCasesDir.String()).
+		FlagWithArg("-C ", pathForTestCases(ctx).String()).
 		FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths()).
+		Flag("-sha256") // necessary to save cas_uploader's time
+
+	testList := buildTestList(ctx, "ravenwood-tests_list", installedPaths)
+	testListZipOutputFile := pathForPackaging(ctx, "ravenwood-tests_list.zip")
+
+	rule.Command().BuiltTool("soong_zip").
+		FlagWithOutput("-o ", testListZipOutputFile).
+		FlagWithArg("-C ", pathForPackaging(ctx).String()).
+		FlagWithInput("-f ", testList).
 		Flag("-sha256")
+
 	rule.Build("ravenwood_tests_zip", "ravenwood-tests.zip")
 
+	return []Path{outputFile, testListZipOutputFile}
+}
+
+func buildTestList(ctx SingletonContext, listFile string, installedPaths InstallPaths) Path {
+	buf := &strings.Builder{}
+	for _, p := range installedPaths {
+		if p.Ext() != ".config" {
+			continue
+		}
+		pc, err := toTestListPath(p.String(), pathForTestCases(ctx).String(), "host/testcases")
+		if err != nil {
+			ctx.Errorf("Failed to convert path: %s, %v", p.String(), err)
+			continue
+		}
+		buf.WriteString(pc)
+		buf.WriteString("\n")
+	}
+	outputFile := pathForPackaging(ctx, listFile)
+	WriteFileRuleVerbatim(ctx, outputFile, buf.String())
 	return outputFile
 }
+
+func toTestListPath(path, relativeRoot, prefix string) (string, error) {
+	dest, err := filepath.Rel(relativeRoot, path)
+	if err != nil {
+		return "", err
+	}
+	return filepath.Join(prefix, dest), nil
+}
+
+func pathForPackaging(ctx PathContext, pathComponents ...string) OutputPath {
+	pathComponents = append([]string{"packaging"}, pathComponents...)
+	return PathForOutput(ctx, pathComponents...)
+}
+
+func pathForTestCases(ctx PathContext) InstallPath {
+	return pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases")
+}
diff --git a/android/test_suites_test.go b/android/test_suites_test.go
new file mode 100644
index 0000000..db9a34d
--- /dev/null
+++ b/android/test_suites_test.go
@@ -0,0 +1,117 @@
+// 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 (
+	"path/filepath"
+	"testing"
+)
+
+func TestBuildTestList(t *testing.T) {
+	t.Parallel()
+	ctx := GroupFixturePreparers(
+		prepareForFakeTestSuite,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterParallelSingletonType("testsuites", testSuiteFilesFactory)
+		}),
+	).RunTestWithBp(t, `
+		fake_module {
+			name: "module1",
+			outputs: [
+				"Test1/Test1.config",
+				"Test1/Test1.apk",
+			],
+			test_suites: ["ravenwood-tests"],
+		}
+		fake_module {
+			name: "module2",
+			outputs: [
+				"Test2/Test21/Test21.config",
+				"Test2/Test21/Test21.apk",
+			],
+			test_suites: ["ravenwood-tests", "robolectric-tests"],
+		}
+		fake_module {
+			name: "module_without_config",
+			outputs: [
+				"BadTest/BadTest.jar",
+			],
+			test_suites: ["robolectric-tests"],
+		}
+	`)
+
+	config := ctx.SingletonForTests("testsuites")
+	allOutputs := config.AllOutputs()
+
+	wantContents := map[string]string{
+		"robolectric-tests.zip":      "",
+		"robolectric-tests_list.zip": "",
+		"robolectric-tests_list": `host/testcases/Test2/Test21/Test21.config
+`,
+		"ravenwood-tests.zip":      "",
+		"ravenwood-tests_list.zip": "",
+		"ravenwood-tests_list": `host/testcases/Test1/Test1.config
+host/testcases/Test2/Test21/Test21.config
+`,
+	}
+	for _, output := range allOutputs {
+		want, ok := wantContents[filepath.Base(output)]
+		if !ok {
+			t.Errorf("unexpected output: %q", output)
+			continue
+		}
+
+		got := ""
+		if want != "" {
+			got = ContentFromFileRuleForTests(t, ctx.TestContext, config.MaybeOutput(output))
+		}
+
+		if want != got {
+			t.Errorf("want %q, got %q", want, got)
+		}
+	}
+}
+
+type fake_module struct {
+	ModuleBase
+	props struct {
+		Outputs     []string
+		Test_suites []string
+	}
+}
+
+func fakeTestSuiteFactory() Module {
+	module := &fake_module{}
+	base := module.base()
+	module.AddProperties(&base.nameProperties, &module.props)
+	InitAndroidModule(module)
+	return module
+}
+
+var prepareForFakeTestSuite = GroupFixturePreparers(
+	FixtureRegisterWithContext(func(ctx RegistrationContext) {
+		ctx.RegisterModuleType("fake_module", fakeTestSuiteFactory)
+	}),
+)
+
+func (f *fake_module) GenerateAndroidBuildActions(ctx ModuleContext) {
+	for _, output := range f.props.Outputs {
+		ctx.InstallFile(pathForTestCases(ctx), output, nil)
+	}
+}
+
+func (f *fake_module) TestSuites() []string {
+	return f.props.Test_suites
+}
diff --git a/android/testing.go b/android/testing.go
index 7b4411e..e39a1a7 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -224,6 +224,10 @@
 	})
 }
 
+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.
@@ -1014,20 +1018,21 @@
 	return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests())
 }
 
-// OutputFiles calls OutputFileProducer.OutputFiles on the encapsulated module, exits the test
-// immediately if there is an error and otherwise returns the result of calling Paths.RelativeToTop
+// OutputFiles checks if module base outputFiles property has any output
+// files can be used to return.
+// 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 {
-	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)
+	outputFiles := m.Module().base().outputFiles
+	if tag == "" && outputFiles.DefaultOutputFiles != nil {
+		return outputFiles.DefaultOutputFiles.RelativeToTop()
+	} else if taggedOutputFiles, hasTag := outputFiles.TaggedOutputFiles[tag]; hasTag {
+		return taggedOutputFiles.RelativeToTop()
 	}
 
-	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
@@ -1122,7 +1127,7 @@
 
 	entriesList := p.AndroidMkEntries()
 	aconfigUpdateAndroidMkEntries(ctx, mod.(Module), &entriesList)
-	for i, _ := range entriesList {
+	for i := range entriesList {
 		entriesList[i].fillInEntries(ctx, mod)
 	}
 	return entriesList
@@ -1287,3 +1292,21 @@
 		t.Errorf("%q is not found in %v", expected, result)
 	}
 }
+
+type panickingConfigAndErrorContext struct {
+	ctx *TestContext
+}
+
+func (ctx *panickingConfigAndErrorContext) OtherModulePropertyErrorf(module Module, property, fmt string, args ...interface{}) {
+	panic(ctx.ctx.PropertyErrorf(module, property, fmt, args...).Error())
+}
+
+func (ctx *panickingConfigAndErrorContext) Config() Config {
+	return ctx.ctx.Config()
+}
+
+func PanickingConfigAndErrorContext(ctx *TestContext) ConfigAndErrorContext {
+	return &panickingConfigAndErrorContext{
+		ctx: ctx,
+	}
+}
diff --git a/android/updatable_modules.go b/android/updatable_modules.go
index 1548170..dd7dc2c 100644
--- a/android/updatable_modules.go
+++ b/android/updatable_modules.go
@@ -33,4 +33,4 @@
 // * AOSP            - xx9990000
 // * x-mainline-prod - xx9990000
 // * master          - 990090000
-const DefaultUpdatableModuleVersion = "990090000"
+const DefaultUpdatableModuleVersion = "350090000"
diff --git a/android/util.go b/android/util.go
index 363b31c..3c0af2f 100644
--- a/android/util.go
+++ b/android/util.go
@@ -24,6 +24,8 @@
 	"sort"
 	"strings"
 	"sync"
+
+	"github.com/google/blueprint/proptools"
 )
 
 // CopyOf returns a new slice that has the same contents as s.
@@ -199,6 +201,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 {
@@ -302,6 +310,24 @@
 	return removed, result
 }
 
+// FirstUniqueFunc returns all unique elements of a slice, keeping the first copy of
+// each.  It does not modify the input slice. The eq function should return true
+// if two elements can be considered equal.
+func FirstUniqueFunc[SortableList ~[]Sortable, Sortable any](list SortableList, eq func(a, b Sortable) bool) SortableList {
+	k := 0
+outer:
+	for i := 0; i < len(list); i++ {
+		for j := 0; j < k; j++ {
+			if eq(list[i], list[j]) {
+				continue outer
+			}
+		}
+		list[k] = list[i]
+		k++
+	}
+	return list[:k]
+}
+
 // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
 // each.  It does not modify the input slice.
 func FirstUniqueStrings(list []string) []string {
@@ -526,18 +552,7 @@
 
 // ShardPaths takes a Paths, and returns a slice of Paths where each one has at most shardSize paths.
 func ShardPaths(paths Paths, shardSize int) []Paths {
-	if len(paths) == 0 {
-		return nil
-	}
-	ret := make([]Paths, 0, (len(paths)+shardSize-1)/shardSize)
-	for len(paths) > shardSize {
-		ret = append(ret, paths[0:shardSize])
-		paths = paths[shardSize:]
-	}
-	if len(paths) > 0 {
-		ret = append(ret, paths)
-	}
-	return ret
+	return proptools.ShardBySize(paths, shardSize)
 }
 
 // ShardString takes a string and returns a slice of strings where the length of each one is
@@ -560,18 +575,7 @@
 // ShardStrings takes a slice of strings, and returns a slice of slices of strings where each one has at most shardSize
 // elements.
 func ShardStrings(s []string, shardSize int) [][]string {
-	if len(s) == 0 {
-		return nil
-	}
-	ret := make([][]string, 0, (len(s)+shardSize-1)/shardSize)
-	for len(s) > shardSize {
-		ret = append(ret, s[0:shardSize])
-		s = s[shardSize:]
-	}
-	if len(s) > 0 {
-		ret = append(ret, s)
-	}
-	return ret
+	return proptools.ShardBySize(s, shardSize)
 }
 
 // CheckDuplicate checks if there are duplicates in given string list.
diff --git a/android/util_test.go b/android/util_test.go
index 8e73d83..6537d69 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -818,3 +818,52 @@
 		})
 	}
 }
+
+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)
+			}
+		})
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index 73f5bfd..3b02bc7 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -20,8 +20,6 @@
 	"runtime"
 	"strings"
 
-	"android/soong/bazel"
-
 	"github.com/google/blueprint/proptools"
 )
 
@@ -57,19 +55,23 @@
 			Base_dir *string
 		}
 
+		Shipping_api_level struct {
+			Cflags []string
+		}
+
 		// unbundled_build is a catch-all property to annotate modules that don't build in one or
 		// more unbundled branches, usually due to dependencies missing from the manifest.
 		Unbundled_build struct {
-			Enabled *bool `android:"arch_variant"`
+			Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 		} `android:"arch_variant"`
 
 		// similar to `Unbundled_build`, but `Always_use_prebuilt_sdks` means that it uses prebuilt
 		// sdk specifically.
 		Always_use_prebuilt_sdks struct {
-			Enabled *bool `android:"arch_variant"`
+			Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 		} `android:"arch_variant"`
 
-		Malloc_not_svelte struct {
+		Malloc_low_memory struct {
 			Cflags              []string `android:"arch_variant"`
 			Shared_libs         []string `android:"arch_variant"`
 			Whole_static_libs   []string `android:"arch_variant"`
@@ -130,15 +132,19 @@
 				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
 
 			Srcs         []string
 			Exclude_srcs []string
 			Cmd          *string
+
+			Deps []string
 		}
 
 		// eng is true for -eng builds, and can be used to turn on additional heavyweight debugging
@@ -181,8 +187,10 @@
 		// release_aidl_use_unfrozen is "true" when a device can
 		// use the unfrozen versions of AIDL interfaces.
 		Release_aidl_use_unfrozen struct {
-			Cflags []string
-			Cmd    *string
+			Cflags          []string
+			Cmd             *string
+			Required        []string
+			Vintf_fragments []string
 		}
 	} `android:"arch_variant"`
 }
@@ -193,26 +201,29 @@
 	// Suffix to add to generated Makefiles
 	Make_suffix *string `json:",omitempty"`
 
-	BuildId         *string `json:",omitempty"`
-	BuildNumberFile *string `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_version_name                     *string  `json:",omitempty"`
-	Platform_sdk_version                      *int     `json:",omitempty"`
-	Platform_sdk_codename                     *string  `json:",omitempty"`
-	Platform_sdk_version_or_codename          *string  `json:",omitempty"`
-	Platform_sdk_final                        *bool    `json:",omitempty"`
-	Platform_sdk_extension_version            *int     `json:",omitempty"`
-	Platform_base_sdk_extension_version       *int     `json:",omitempty"`
-	Platform_version_active_codenames         []string `json:",omitempty"`
-	Platform_version_all_preview_codenames    []string `json:",omitempty"`
-	Platform_vndk_version                     *string  `json:",omitempty"`
-	Platform_systemsdk_versions               []string `json:",omitempty"`
-	Platform_security_patch                   *string  `json:",omitempty"`
-	Platform_preview_sdk_version              *string  `json:",omitempty"`
-	Platform_min_supported_target_sdk_version *string  `json:",omitempty"`
-	Platform_base_os                          *string  `json:",omitempty"`
-	Platform_version_last_stable              *string  `json:",omitempty"`
-	Platform_version_known_codenames          *string  `json:",omitempty"`
+	Platform_display_version_name          *string  `json:",omitempty"`
+	Platform_version_name                  *string  `json:",omitempty"`
+	Platform_sdk_version                   *int     `json:",omitempty"`
+	Platform_sdk_codename                  *string  `json:",omitempty"`
+	Platform_sdk_version_or_codename       *string  `json:",omitempty"`
+	Platform_sdk_final                     *bool    `json:",omitempty"`
+	Platform_sdk_extension_version         *int     `json:",omitempty"`
+	Platform_base_sdk_extension_version    *int     `json:",omitempty"`
+	Platform_version_active_codenames      []string `json:",omitempty"`
+	Platform_version_all_preview_codenames []string `json:",omitempty"`
+	Platform_systemsdk_versions            []string `json:",omitempty"`
+	Platform_security_patch                *string  `json:",omitempty"`
+	Platform_preview_sdk_version           *string  `json:",omitempty"`
+	Platform_base_os                       *string  `json:",omitempty"`
+	Platform_version_last_stable           *string  `json:",omitempty"`
+	Platform_version_known_codenames       *string  `json:",omitempty"`
 
 	DeviceName                            *string  `json:",omitempty"`
 	DeviceProduct                         *string  `json:",omitempty"`
@@ -265,8 +276,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"`
 
@@ -276,7 +289,7 @@
 	Unbundled_build_image        *bool    `json:",omitempty"`
 	Always_use_prebuilt_sdks     *bool    `json:",omitempty"`
 	Skip_boot_jars_check         *bool    `json:",omitempty"`
-	Malloc_not_svelte            *bool    `json:",omitempty"`
+	Malloc_low_memory            *bool    `json:",omitempty"`
 	Malloc_zero_contents         *bool    `json:",omitempty"`
 	Malloc_pattern_fill_contents *bool    `json:",omitempty"`
 	Safestack                    *bool    `json:",omitempty"`
@@ -296,6 +309,8 @@
 	MinimizeJavaDebugInfo        *bool    `json:",omitempty"`
 	Build_from_text_stub         *bool    `json:",omitempty"`
 
+	BuildType *string `json:",omitempty"`
+
 	Check_elf_files *bool `json:",omitempty"`
 
 	UncompressPrivAppDex             *bool    `json:",omitempty"`
@@ -358,7 +373,6 @@
 
 	PgoAdditionalProfileDirs []string `json:",omitempty"`
 
-	VndkUseCoreVariant         *bool `json:",omitempty"`
 	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
 
 	DirectedVendorSnapshot bool            `json:",omitempty"`
@@ -389,7 +403,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"`
 
@@ -435,7 +450,7 @@
 
 	PrebuiltHiddenApiDir *string `json:",omitempty"`
 
-	ShippingApiLevel *string `json:",omitempty"`
+	Shipping_api_level *string `json:",omitempty"`
 
 	BuildBrokenPluginValidation         []string `json:",omitempty"`
 	BuildBrokenClangAsFlags             bool     `json:",omitempty"`
@@ -449,6 +464,7 @@
 	BuildBrokenIncorrectPartitionImages bool     `json:",omitempty"`
 	BuildBrokenInputDirModules          []string `json:",omitempty"`
 	BuildBrokenDontCheckSystemSdk       bool     `json:",omitempty"`
+	BuildBrokenDupSysprop               bool     `json:",omitempty"`
 
 	BuildWarningBadOptionalUsesLibsAllowlist []string `json:",omitempty"`
 
@@ -467,14 +483,12 @@
 
 	IgnorePrefer32OnDevice bool `json:",omitempty"`
 
-	IncludeTags    []string `json:",omitempty"`
 	SourceRootDirs []string `json:",omitempty"`
 
 	AfdoProfiles []string `json:",omitempty"`
 
-	ProductManufacturer string   `json:",omitempty"`
-	ProductBrand        string   `json:",omitempty"`
-	BuildVersionTags    []string `json:",omitempty"`
+	ProductManufacturer string `json:",omitempty"`
+	ProductBrand        string `json:",omitempty"`
 
 	ReleaseVersion          string   `json:",omitempty"`
 	ReleaseAconfigValueSets []string `json:",omitempty"`
@@ -483,25 +497,29 @@
 
 	ReleaseDefaultModuleBuildFromSource *bool `json:",omitempty"`
 
-	KeepVndk *bool `json:",omitempty"`
-
 	CheckVendorSeappViolations *bool `json:",omitempty"`
 
-	// PartitionVarsForBazelMigrationOnlyDoNotUse are extra variables that are used to define the
-	// partition images. They should not be read from soong modules.
-	PartitionVarsForBazelMigrationOnlyDoNotUse PartitionVariables `json:",omitempty"`
-
 	BuildFlags map[string]string `json:",omitempty"`
 
+	BuildFlagTypes map[string]string `json:",omitempty"`
+
 	BuildFromSourceStub *bool `json:",omitempty"`
 
-	BuildIgnoreApexContributionContents []string `json:",omitempty"`
+	BuildIgnoreApexContributionContents *bool `json:",omitempty"`
 
 	HiddenapiExportableStubs *bool `json:",omitempty"`
 
 	ExportRuntimeApis *bool `json:",omitempty"`
 
 	AconfigContainerValidation string `json:",omitempty"`
+
+	ProductLocales []string `json:",omitempty"`
+
+	ProductDefaultWifiChannels []string `json:",omitempty"`
+
+	BoardUseVbmetaDigestInFingerprint *bool `json:",omitempty"`
+
+	OemProperties []string `json:",omitempty"`
 }
 
 type PartitionQualifiedVariablesType struct {
@@ -584,7 +602,6 @@
 		Platform_sdk_final:                     boolPtr(false),
 		Platform_version_active_codenames:      []string{"S"},
 		Platform_version_all_preview_codenames: []string{"S"},
-		Platform_vndk_version:                  stringPtr("S"),
 
 		HostArch:                    stringPtr("x86_64"),
 		HostSecondaryArch:           stringPtr("x86"),
@@ -606,7 +623,7 @@
 		AAPTCharacteristics: stringPtr("nosdcard"),
 		AAPTPrebuiltDPI:     []string{"xhdpi", "xxhdpi"},
 
-		Malloc_not_svelte:            boolPtr(true),
+		Malloc_low_memory:            boolPtr(false),
 		Malloc_zero_contents:         boolPtr(true),
 		Malloc_pattern_fill_contents: boolPtr(false),
 		Safestack:                    boolPtr(false),
@@ -632,387 +649,6 @@
 	return val == "true"
 }
 
-// ProductConfigContext requires the access to the Module to get product config properties.
-type ProductConfigContext interface {
-	Module() Module
-}
-
-// ProductConfigOrSoongConfigProperty represents either a soong config variable + its value
-// or a product config variable. You can get both a ConfigurationAxis and a SelectKey from it
-// for use in bazel attributes. ProductVariableProperties() will return a map from properties ->
-// this interface -> property structs for use in bp2build converters
-type ProductConfigOrSoongConfigProperty interface {
-	// Name of the product variable or soong config variable
-	Name() string
-	// AlwaysEmit returns true for soong config variables but false for product variables. This
-	// is intended to indicate if we need to always emit empty lists in the select statements.
-	AlwaysEmit() bool
-	// ConfigurationAxis returns the bazel.ConfigurationAxis that represents this variable. The
-	// configuration axis will change depending on the variable and whether it's arch/os variant
-	// as well.
-	ConfigurationAxis() bazel.ConfigurationAxis
-	// SelectKey returns a string that represents the key of a select branch, however, it is not
-	// actually the real label written out to the build file.
-	// this.ConfigurationAxis().SelectKey(this.SelectKey()) will give the actual label.
-	SelectKey() string
-}
-
-// ProductConfigProperty represents a product config variable, and if it is arch-variant or not.
-type ProductConfigProperty struct {
-	// The name of the product variable, e.g. "safestack", "malloc_not_svelte",
-	// "board"
-	name string
-
-	arch string
-}
-
-func (p ProductConfigProperty) Name() string {
-	return p.name
-}
-
-func (p ProductConfigProperty) AlwaysEmit() bool {
-	return false
-}
-
-func (p ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
-	return bazel.ProductVariableConfigurationAxis(p.arch != "", p.name+"__"+p.arch)
-}
-
-func (p ProductConfigProperty) SelectKey() string {
-	if p.arch == "" {
-		return strings.ToLower(p.name)
-	} else {
-		return strings.ToLower(p.name + "-" + p.arch)
-	}
-}
-
-// SoongConfigProperty represents a soong config variable, its value if it's a string variable,
-// and if it's dependent on the OS or not
-type SoongConfigProperty struct {
-	name      string
-	namespace string
-	// Can be an empty string for bool/value soong config variables
-	value string
-	// If there is a target: field inside a soong config property struct, the os that it selects
-	// on will be represented here.
-	os string
-}
-
-func (p SoongConfigProperty) Name() string {
-	return p.name
-}
-
-func (p SoongConfigProperty) AlwaysEmit() bool {
-	return true
-}
-
-func (p SoongConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
-	return bazel.ProductVariableConfigurationAxis(false, p.namespace+"__"+p.name+"__"+p.os)
-}
-
-// SelectKey returns the literal string that represents this variable in a BUILD
-// select statement.
-func (p SoongConfigProperty) SelectKey() string {
-	// p.value being conditions_default can happen with or without a desired os. When not using
-	// an os, we want to emit literally just //conditions:default in the select statement, but
-	// when using an os, we want to emit namespace__name__conditions_default__os, so that
-	// the branch is only taken if the variable is not set, and we're on the desired os.
-	// ConfigurationAxis#SelectKey will map the conditions_default result of this function to
-	// //conditions:default.
-	if p.value == bazel.ConditionsDefaultConfigKey && p.os == "" {
-		return bazel.ConditionsDefaultConfigKey
-	}
-
-	parts := []string{p.namespace, p.name}
-	if p.value != "" && p.value != bazel.ConditionsDefaultSelectKey {
-		parts = append(parts, p.value)
-	}
-	if p.os != "" {
-		parts = append(parts, p.os)
-	}
-
-	// e.g. acme__feature1, android__board__soc_a, my_namespace__my_variables__my_value__my_os
-	return strings.ToLower(strings.Join(parts, "__"))
-}
-
-// ProductConfigProperties is a map of maps to group property values according
-// their property name and the product config variable they're set under.
-//
-// The outer map key is the name of the property, like "cflags".
-//
-// The inner map key is a ProductConfigProperty, which is a struct of product
-// variable name, namespace, and the "full configuration" of the product
-// variable.
-//
-// e.g. product variable name: board, namespace: acme, full config: vendor_chip_foo
-//
-// The value of the map is the interface{} representing the value of the
-// property, like ["-DDEFINES"] for cflags.
-type ProductConfigProperties map[string]map[ProductConfigOrSoongConfigProperty]interface{}
-
-func (p *ProductConfigProperties) AddProductConfigProperty(
-	propertyName, productVariableName, arch string, propertyValue interface{}) {
-
-	productConfigProp := ProductConfigProperty{
-		name: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
-		arch: arch,                // e.g. "", x86, arm64
-	}
-
-	p.AddEitherProperty(propertyName, productConfigProp, propertyValue)
-}
-
-func (p *ProductConfigProperties) AddSoongConfigProperty(
-	propertyName, namespace, variableName, value, os string, propertyValue interface{}) {
-
-	soongConfigProp := SoongConfigProperty{
-		namespace: namespace,
-		name:      variableName, // e.g. size, feature1, feature2, FEATURE3, board
-		value:     value,
-		os:        os, // e.g. android, linux_x86
-	}
-
-	p.AddEitherProperty(propertyName, soongConfigProp, propertyValue)
-}
-
-func (p *ProductConfigProperties) AddEitherProperty(
-	propertyName string, key ProductConfigOrSoongConfigProperty, propertyValue interface{}) {
-	if (*p)[propertyName] == nil {
-		(*p)[propertyName] = make(map[ProductConfigOrSoongConfigProperty]interface{})
-	}
-
-	if existing, ok := (*p)[propertyName][key]; ok {
-		switch dst := existing.(type) {
-		case []string:
-			src, ok := propertyValue.([]string)
-			if !ok {
-				panic("Conflicting types")
-			}
-			dst = append(dst, src...)
-			(*p)[propertyName][key] = dst
-		default:
-			if existing != propertyValue {
-				panic(fmt.Errorf("TODO: handle merging value %#v", existing))
-			}
-		}
-	} else {
-		(*p)[propertyName][key] = propertyValue
-	}
-}
-
-// maybeExtractConfigVarProp attempts to read this value as a config var struct
-// wrapped by interfaces and ptrs. If it's not the right type, the second return
-// value is false.
-func maybeExtractConfigVarProp(v reflect.Value) (reflect.Value, bool) {
-	if v.Kind() == reflect.Interface {
-		// The conditions_default value can be either
-		// 1) an ptr to an interface of a struct (bool config variables and product variables)
-		// 2) an interface of 1) (config variables with nested structs, like string vars)
-		v = v.Elem()
-	}
-	if v.Kind() != reflect.Ptr {
-		return v, false
-	}
-	v = reflect.Indirect(v)
-	if v.Kind() == reflect.Interface {
-		// Extract the struct from the interface
-		v = v.Elem()
-	}
-
-	if !v.IsValid() {
-		return v, false
-	}
-
-	if v.Kind() != reflect.Struct {
-		return v, false
-	}
-	return v, true
-}
-
-func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(variableValues reflect.Value, arch string) {
-	// Example of product_variables:
-	//
-	// product_variables: {
-	//     malloc_not_svelte: {
-	//         shared_libs: ["malloc_not_svelte_shared_lib"],
-	//         whole_static_libs: ["malloc_not_svelte_whole_static_lib"],
-	//         exclude_static_libs: [
-	//             "malloc_not_svelte_static_lib_excludes",
-	//             "malloc_not_svelte_whole_static_lib_excludes",
-	//         ],
-	//     },
-	// },
-
-	for i := 0; i < variableValues.NumField(); i++ {
-		// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
-		productVariableName := variableValues.Type().Field(i).Name
-
-		variableValue := variableValues.Field(i)
-		// Check if any properties were set for the module
-		if variableValue.IsZero() {
-			// e.g. feature1: {}, malloc_not_svelte: {}
-			continue
-		}
-
-		for j := 0; j < variableValue.NumField(); j++ {
-			property := variableValue.Field(j)
-			// e.g. Asflags, Cflags, Enabled, etc.
-			propertyName := variableValue.Type().Field(j).Name
-			if property.Kind() != reflect.Interface {
-				productConfigProperties.AddProductConfigProperty(propertyName, productVariableName, arch, property.Interface())
-			}
-		}
-	}
-
-}
-
-func (productConfigProperties *ProductConfigProperties) AddSoongConfigProperties(namespace string, soongConfigVariablesStruct reflect.Value) error {
-	//
-	// Example of soong_config_variables:
-	//
-	// soong_config_variables: {
-	//      feature1: {
-	//          conditions_default: {
-	//               ...
-	//          },
-	//          cflags: ...
-	//      },
-	//      feature2: {
-	//          cflags: ...
-	//          conditions_default: {
-	//               ...
-	//          },
-	//      },
-	//      board: {
-	//         soc_a: {
-	//             ...
-	//         },
-	//         soc_b: {
-	//             ...
-	//         },
-	//         soc_c: {},
-	//         conditions_default: {
-	//              ...
-	//         },
-	//      },
-	// }
-	for i := 0; i < soongConfigVariablesStruct.NumField(); i++ {
-		// e.g. feature1, feature2, board
-		variableName := soongConfigVariablesStruct.Type().Field(i).Name
-		variableStruct := soongConfigVariablesStruct.Field(i)
-		// Check if any properties were set for the module
-		if variableStruct.IsZero() {
-			// e.g. feature1: {}
-			continue
-		}
-
-		// Unlike product variables, config variables require a few more
-		// indirections to extract the struct from the reflect.Value.
-		if v, ok := maybeExtractConfigVarProp(variableStruct); ok {
-			variableStruct = v
-		} else if !v.IsValid() {
-			// Skip invalid variables which may not used, else leads to panic
-			continue
-		}
-
-		for j := 0; j < variableStruct.NumField(); j++ {
-			propertyOrStruct := variableStruct.Field(j)
-			// propertyOrValueName can either be:
-			//  - A property, like: Asflags, Cflags, Enabled, etc.
-			//  - A soong config string variable's value, like soc_a, soc_b, soc_c in the example above
-			//  - "conditions_default"
-			propertyOrValueName := variableStruct.Type().Field(j).Name
-
-			// If the property wasn't set, no need to pass it along
-			if propertyOrStruct.IsZero() {
-				continue
-			}
-
-			if v, ok := maybeExtractConfigVarProp(propertyOrStruct); ok {
-				// The field is a struct, which is used by:
-				// 1) soong_config_string_variables
-				//
-				// soc_a: {
-				//     cflags: ...,
-				// }
-				//
-				// soc_b: {
-				//     cflags: ...,
-				// }
-				//
-				// 2) conditions_default structs for all soong config variable types.
-				//
-				// conditions_default: {
-				//     cflags: ...,
-				//     static_libs: ...
-				// }
-				//
-				// This means that propertyOrValueName is either conditions_default, or a soong
-				// config string variable's value.
-				field := v
-				// Iterate over fields of this struct prop.
-				for k := 0; k < field.NumField(); k++ {
-					// For product variables, zero values are irrelevant; however, for soong config variables,
-					// empty values are relevant because there can also be a conditions default which is not
-					// applied for empty variables.
-					if field.Field(k).IsZero() && namespace == "" {
-						continue
-					}
-
-					propertyName := field.Type().Field(k).Name
-					if propertyName == "Target" {
-						productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), field.Field(k))
-					} else if propertyName == "Arch" || propertyName == "Multilib" {
-						return fmt.Errorf("Arch/Multilib are not currently supported in soong config variable structs")
-					} else {
-						productConfigProperties.AddSoongConfigProperty(propertyName, namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), "", field.Field(k).Interface())
-					}
-				}
-			} else if propertyOrStruct.Kind() != reflect.Interface {
-				// If not an interface, then this is not a conditions_default or
-				// a struct prop. That is, this is a bool/value config variable.
-				if propertyOrValueName == "Target" {
-					productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, "", propertyOrStruct)
-				} else if propertyOrValueName == "Arch" || propertyOrValueName == "Multilib" {
-					return fmt.Errorf("Arch/Multilib are not currently supported in soong config variable structs")
-				} else {
-					productConfigProperties.AddSoongConfigProperty(propertyOrValueName, namespace, variableName, "", "", propertyOrStruct.Interface())
-				}
-			}
-		}
-	}
-	return nil
-}
-
-func (productConfigProperties *ProductConfigProperties) AddSoongConfigPropertiesFromTargetStruct(namespace, soongConfigVariableName string, soongConfigVariableValue string, targetStruct reflect.Value) {
-	// targetStruct will be a struct with fields like "android", "host", "arm", "x86",
-	// "android_arm", etc. The values of each of those fields will be a regular property struct.
-	for i := 0; i < targetStruct.NumField(); i++ {
-		targetFieldName := targetStruct.Type().Field(i).Name
-		archOrOsSpecificStruct := targetStruct.Field(i)
-		for j := 0; j < archOrOsSpecificStruct.NumField(); j++ {
-			property := archOrOsSpecificStruct.Field(j)
-			// e.g. Asflags, Cflags, Enabled, etc.
-			propertyName := archOrOsSpecificStruct.Type().Field(j).Name
-
-			if targetFieldName == "Android" {
-				productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, "android", property.Interface())
-			} else if targetFieldName == "Host" {
-				for _, os := range osTypeList {
-					if os.Class == Host {
-						productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, os.Name, property.Interface())
-					}
-				}
-			} else if !archOrOsSpecificStruct.IsZero() {
-				// One problem with supporting additional fields is that if multiple branches of
-				// "target" overlap, we don't want them to be in the same select statement (aka
-				// configuration axis). "android" and "host" are disjoint, so it's ok that we only
-				// have 2 axes right now. (soongConfigVariables and soongConfigVariablesPlusOs)
-				panic("TODO: support other target types in soong config variable structs: " + targetFieldName)
-			}
-		}
-	}
-}
-
 func VariableMutator(mctx BottomUpMutatorContext) {
 	var module Module
 	var ok bool
diff --git a/android/visibility.go b/android/visibility.go
index 79a534f..89c0adc 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -58,19 +58,14 @@
 var visibilityRuleRegexp = regexp.MustCompile(visibilityRulePattern)
 
 type visibilityModuleReference struct {
-	name              qualifiedModuleName
-	isPartitionModule bool
+	name   qualifiedModuleName
+	module Module
 }
 
-func createVisibilityModuleReference(name, dir, typ string) visibilityModuleReference {
-	isPartitionModule := false
-	switch typ {
-	case "android_filesystem", "android_system_image":
-		isPartitionModule = true
-	}
+func createVisibilityModuleReference(name, dir string, module Module) visibilityModuleReference {
 	return visibilityModuleReference{
-		name:              createQualifiedModuleName(name, dir),
-		isPartitionModule: isPartitionModule,
+		name:   createQualifiedModuleName(name, dir),
+		module: module,
 	}
 }
 
@@ -214,21 +209,37 @@
 	return "//visibility:private"
 }
 
+var anyPartitionRegex = regexp.MustCompile("^any_(system|system_ext|vendor|product|data|odm)_partition$")
+
 // visibilityRule for //visibility:any_partition
-type anyPartitionRule struct{}
+type anyPartitionRule struct {
+	partitionType string
+}
 
 var _ visibilityRule = anyPartitionRule{}
 
+type PartitionTypeInterface interface {
+	PartitionType() string
+}
+
 func (r anyPartitionRule) matches(m visibilityModuleReference) bool {
-	return m.isPartitionModule
+	if m2, ok := m.module.(PartitionTypeInterface); ok {
+		return m2.PartitionType() == r.partitionType
+	}
+	return false
 }
 
 func (r anyPartitionRule) String() string {
-	return "//visibility:any_partition"
+	return "//visibility:any_" + r.partitionType + "_partition"
 }
 
 var visibilityRuleMap = NewOnceKey("visibilityRuleMap")
 
+type visibilityRulesForModule struct {
+	rule                   compositeRule
+	implicitPartitionRules compositeRule
+}
+
 // The map from qualifiedModuleName to visibilityRule.
 func moduleToVisibilityRuleMap(config Config) *sync.Map {
 	return config.Once(visibilityRuleMap, func() interface{} {
@@ -304,9 +315,6 @@
 		if pkg == "visibility" {
 			switch name {
 			case "private", "public":
-			case "any_partition":
-				// any_partition can be used with another visibility fields
-				continue
 			case "legacy_public":
 				ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used")
 				continue
@@ -314,6 +322,10 @@
 				// This keyword does not create a rule so pretend it does not exist.
 				ruleCount -= 1
 			default:
+				if anyPartitionRegex.MatchString(name) {
+					// any_*_partition can be used with another visibility fields
+					continue
+				}
 				ctx.PropertyErrorf(property, "unrecognized visibility rule %q", v)
 				continue
 			}
@@ -352,15 +364,20 @@
 
 	// Parse the visibility rules that control access to the module and store them by id
 	// for use when enforcing the rules.
+	var rule compositeRule
 	primaryProperty := m.base().primaryVisibilityProperty
 	if primaryProperty != nil {
 		if visibility := primaryProperty.getStrings(); visibility != nil {
-			rule := parseRules(ctx, currentPkg, primaryProperty.getName(), visibility)
-			if rule != nil {
-				moduleToVisibilityRuleMap(ctx.Config()).Store(qualifiedModuleId, rule)
-			}
+			rule = parseRules(ctx, currentPkg, primaryProperty.getName(), visibility)
 		}
 	}
+	ipr := implicitPartitionRules(ctx)
+	if rule != nil || ipr != nil {
+		moduleToVisibilityRuleMap(ctx.Config()).Store(qualifiedModuleId, visibilityRulesForModule{
+			rule:                   rule,
+			implicitPartitionRules: ipr,
+		})
+	}
 }
 
 func parseRules(ctx BaseModuleContext, currentPkg, property string, visibility []string) compositeRule {
@@ -392,8 +409,13 @@
 				hasNonPrivateRule = false
 				// This does not actually create a rule so continue onto the next rule.
 				continue
-			case "any_partition":
-				r = anyPartitionRule{}
+			default:
+				match := anyPartitionRegex.FindStringSubmatch(name)
+				if match != nil {
+					r = anyPartitionRule{
+						partitionType: match[1],
+					}
+				}
 			}
 		} else {
 			switch name {
@@ -432,6 +454,22 @@
 	return rules
 }
 
+func implicitPartitionRules(ctx BaseModuleContext) compositeRule {
+	var result compositeRule
+	if ctx.SocSpecific() {
+		result = append(result, anyPartitionRule{partitionType: "vendor"})
+	} else if ctx.ProductSpecific() {
+		result = append(result, anyPartitionRule{partitionType: "product"})
+	} else if ctx.Module().InstallInData() {
+		result = append(result, anyPartitionRule{partitionType: "data"})
+	} else if ctx.SystemExtSpecific() {
+		result = append(result, anyPartitionRule{partitionType: "system_ext"})
+	} else if ctx.DeviceSpecific() {
+		result = append(result, anyPartitionRule{partitionType: "odm"})
+	}
+	return result
+}
+
 func isAllowedFromOutsideVendor(pkg string, name string) bool {
 	if pkg == "vendor" {
 		return name == "__subpackages__"
@@ -470,7 +508,7 @@
 }
 
 func visibilityRuleEnforcer(ctx TopDownMutatorContext) {
-	qualified := createVisibilityModuleReference(ctx.ModuleName(), ctx.ModuleDir(), ctx.ModuleType())
+	qualified := createVisibilityModuleReference(ctx.ModuleName(), ctx.ModuleDir(), ctx.Module())
 
 	// Visit all the dependencies making sure that this module has access to them all.
 	ctx.VisitDirectDeps(func(dep Module) {
@@ -505,10 +543,13 @@
 // which is currently //visibility:public.
 func effectiveVisibilityRules(config Config, qualified qualifiedModuleName) compositeRule {
 	moduleToVisibilityRule := moduleToVisibilityRuleMap(config)
-	value, ok := moduleToVisibilityRule.Load(qualified)
+	value := visibilityRulesForModule{}
+	if valueRaw, ok := moduleToVisibilityRule.Load(qualified); ok {
+		value = valueRaw.(visibilityRulesForModule)
+	}
 	var rule compositeRule
-	if ok {
-		rule = value.(compositeRule)
+	if value.rule != nil {
+		rule = value.rule
 	} else {
 		rule = packageDefaultVisibility(moduleToVisibilityRule, qualified)
 	}
@@ -518,6 +559,20 @@
 	if rule == nil {
 		rule = defaultVisibility
 	}
+
+	// If a partition rule wasn't specified, add implicit partition visibility
+	// rules based on the partition properties like vendor: true.
+	foundPartitionRule := false
+	for _, r := range rule {
+		if _, ok := r.(anyPartitionRule); ok {
+			foundPartitionRule = true
+			break
+		}
+	}
+	if !foundPartitionRule {
+		rule = append(rule, value.implicitPartitionRules...)
+	}
+
 	return rule
 }
 
@@ -531,7 +586,7 @@
 	for {
 		value, ok := moduleToVisibilityRule.Load(packageQualifiedId)
 		if ok {
-			return value.(compositeRule)
+			return value.(visibilityRulesForModule).rule
 		}
 
 		if packageQualifiedId.isRootPackage() {
@@ -605,7 +660,7 @@
 
 	rule := effectiveVisibilityRules(ctx.Config(), qualified)
 
-	currentModule := createVisibilityModuleReference(moduleName, dir, ctx.OtherModuleType(module))
+	currentModule := createVisibilityModuleReference(moduleName, dir, module)
 
 	// Modules are implicitly visible to other modules in the same package,
 	// without checking the visibility rules. Here we need to add that visibility
diff --git a/android/visibility_test.go b/android/visibility_test.go
index bb43b1f..1a2eeca 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -1905,7 +1905,7 @@
 		},
 	},
 	{
-		name: "any_partition visibility works",
+		name: "any_system_partition visibility works",
 		fs: MockFS{
 			"top/Android.bp": []byte(`
 				android_filesystem {
@@ -1916,12 +1916,12 @@
 				package(default_visibility=["//visibility:private"])
 				mock_library {
 					name: "bar",
-					visibility: ["//visibility:any_partition"],
+					visibility: ["//visibility:any_system_partition"],
 				}`),
 		},
 	},
 	{
-		name: "any_partition visibility works with the other visibility",
+		name: "any_system_partition visibility works with the other visibility",
 		fs: MockFS{
 			"top/Android.bp": []byte(`
 				android_filesystem {
@@ -1935,13 +1935,13 @@
 					name: "bar",
 					visibility: [
 						"//top2",
-						"//visibility:any_partition"
+						"//visibility:any_system_partition"
 					],
 				}`),
 		},
 	},
 	{
-		name: "any_partition visibility doesn't work for non-partitions",
+		name: "any_system_partition visibility doesn't work for non-partitions",
 		fs: MockFS{
 			"top/Android.bp": []byte(`
 				mock_library {
@@ -1951,11 +1951,77 @@
 			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "bar",
-					visibility: ["//visibility:any_partition"],
+					visibility: ["//visibility:any_system_partition"],
 				}`),
 		},
 		expectedErrors: []string{`module "foo" variant "android_common": depends on //top/nested:bar which is not visible to this module`},
 	},
+	{
+		name: "any_system_partition visibility doesn't work for vendor partitions",
+		fs: MockFS{
+			"top/Android.bp": []byte(`
+				android_filesystem {
+					name: "foo",
+					partition_type: "vendor",
+					deps: ["bar"],
+				}`),
+			"top/nested/Android.bp": []byte(`
+				package(default_visibility=["//visibility:private"])
+				mock_library {
+					name: "bar",
+					visibility: ["//visibility:any_system_partition"],
+				}`),
+		},
+		expectedErrors: []string{`module "foo" variant "android_common": depends on //top/nested:bar which is not visible to this module`},
+	},
+	{
+		name: "Vendor modules are visible to any vendor partition by default",
+		fs: MockFS{
+			"top/Android.bp": []byte(`
+				android_filesystem {
+					name: "foo",
+					partition_type: "vendor",
+					deps: ["bar"],
+				}`),
+			"top/nested/Android.bp": []byte(`
+				package(default_visibility=["//visibility:private"])
+				mock_library {
+					name: "bar",
+					vendor: true,
+				}`),
+		},
+	},
+	{
+		name: "Not visible to vendor partitions when using any_system_partiton, even if vendor: true",
+		fs: MockFS{
+			"top/Android.bp": []byte(`
+				android_filesystem {
+					name: "foo",
+					partition_type: "vendor",
+					deps: ["bar"],
+				}`),
+			"top/nested/Android.bp": []byte(`
+				package(default_visibility=["//visibility:private"])
+				mock_library {
+					name: "bar",
+					vendor: true,
+					visibility: ["//visibility:any_system_partition"],
+				}`),
+		},
+		expectedErrors: []string{`module "foo" variant "android_common": depends on //top/nested:bar which is not visible to this module`},
+	},
+	{
+		name: "unknown any_partition specs throw errors",
+		fs: MockFS{
+			"top/nested/Android.bp": []byte(`
+				package(default_visibility=["//visibility:private"])
+				mock_library {
+					name: "bar",
+					visibility: ["//visibility:any_unknown_partition"],
+				}`),
+		},
+		expectedErrors: []string{`unrecognized visibility rule "//visibility:any_unknown_partition"`},
+	},
 }
 
 func TestVisibility(t *testing.T) {
@@ -1977,8 +2043,7 @@
 					ctx.RegisterModuleType("mock_library", newMockLibraryModule)
 					ctx.RegisterModuleType("mock_parent", newMockParentFactory)
 					ctx.RegisterModuleType("mock_defaults", defaultsFactory)
-					// For testing //visibility:any_partition. The module type doesn't matter, just that it's registered under the name "android_filesystem"
-					ctx.RegisterModuleType("android_filesystem", newMockLibraryModule)
+					ctx.RegisterModuleType("android_filesystem", newMockFilesystemModule)
 				}),
 				prepareForTestWithFakePrebuiltModules,
 				// Add additional files to the mock filesystem
@@ -2032,6 +2097,37 @@
 func (p *mockLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
 }
 
+type mockFilesystemModuleProperties struct {
+	Partition_type *string
+	Deps           []string
+}
+
+type mockFilesystemModule struct {
+	ModuleBase
+	properties mockFilesystemModuleProperties
+}
+
+func (j *mockFilesystemModule) DepsMutator(ctx BottomUpMutatorContext) {
+	ctx.AddVariationDependencies(nil, dependencyTag{name: "mockdeps"}, j.properties.Deps...)
+}
+
+func (p *mockFilesystemModule) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+func (p *mockFilesystemModule) PartitionType() string {
+	if p.properties.Partition_type == nil {
+		return "system"
+	}
+	return *p.properties.Partition_type
+}
+
+func newMockFilesystemModule() Module {
+	m := &mockFilesystemModule{}
+	m.AddProperties(&m.properties)
+	InitAndroidArchModule(m, DeviceSupported, MultilibCommon)
+	return m
+}
+
 type mockDefaults struct {
 	ModuleBase
 	DefaultsModuleBase
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 9d61e1c..570f36c 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -151,7 +151,7 @@
 			"LOCAL_CLANG_CFLAGS":                  "clang_cflags",
 			"LOCAL_YACCFLAGS":                     "yacc.flags",
 			"LOCAL_SANITIZE_RECOVER":              "sanitize.recover",
-			"LOCAL_LOGTAGS_FILES":                 "logtags",
+			"LOCAL_SOONG_LOGTAGS_FILES":           "logtags",
 			"LOCAL_EXPORT_HEADER_LIBRARY_HEADERS": "export_header_lib_headers",
 			"LOCAL_EXPORT_SHARED_LIBRARY_HEADERS": "export_shared_lib_headers",
 			"LOCAL_EXPORT_STATIC_LIBRARY_HEADERS": "export_static_lib_headers",
@@ -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
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/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 460f1ff..1dd479c 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -511,6 +511,22 @@
 		`,
 	},
 	{
+		// Unsupported function case because that doesn't work in bp
+		desc: "error for unsupported functions",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(filter-out filter-out-file.java ,$(call all-java-files-under, src))
+LOCAL_PACKAGE_NAME := foo
+include $(BUILD_PACKAGE)
+`,
+		expected: `
+android_app {
+	name: "foo",
+	srcs: ["UNSUPPORTED FUNCTION:filter-out filter-out-file.java  src/**/*.java"],
+}
+		`,
+	},
+	{
 		desc: "ignore all-makefiles-under",
 		in: `
 include $(call all-makefiles-under,$(LOCAL_PATH))
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/scope.go b/androidmk/parser/scope.go
index 8111c89..e1a523a 100644
--- a/androidmk/parser/scope.go
+++ b/androidmk/parser/scope.go
@@ -14,9 +14,7 @@
 
 package parser
 
-import (
-	"strings"
-)
+import "strings"
 
 type Scope interface {
 	Get(name string) string
@@ -88,7 +86,7 @@
 			if fname == "call" {
 				return scope.Call(argVals[0], argVals[1:]), true
 			} else {
-				return []string{"__builtin_func:" + fname + " " + strings.Join(argVals, " ")}, true
+				return []string{"UNSUPPORTED FUNCTION:" + fname + " " + strings.Join(argVals, " ")}, true
 			}
 		}
 	}
diff --git a/apex/Android.bp b/apex/Android.bp
index 27017ae..17fdfc3 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -37,10 +37,10 @@
         "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",
-        "vndk_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/apex/aconfig_test.go b/apex/aconfig_test.go
index a179dbf..14c0b63 100644
--- a/apex/aconfig_test.go
+++ b/apex/aconfig_test.go
@@ -23,6 +23,7 @@
 	"android/soong/genrule"
 	"android/soong/java"
 	"android/soong/rust"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -162,6 +163,17 @@
 					name: "server_configurable_flags",
 					srcs: ["server_configurable_flags.cc"],
 				}
+				cc_library {
+					name: "libbase",
+					srcs: ["libbase.cc"],
+			                apex_available: [
+				            "myapex",
+			                ],
+				}
+				cc_library {
+					name: "libaconfig_storage_read_api_cc",
+					srcs: ["libaconfig_storage_read_api_cc.cc"],
+				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_bar",
 					package: "com.example.package",
@@ -217,6 +229,24 @@
 				srcs: ["src/lib.rs"],
 				apex_available: ["myapex"],
 			}
+			rust_library {
+				name: "libaconfig_storage_read_api", // test mock
+				crate_name: "aconfig_storage_read_api",
+				srcs: ["src/lib.rs"],
+				apex_available: ["myapex"],
+			}
+			rust_library {
+				name: "liblogger", // test mock
+				crate_name: "logger",
+				srcs: ["src/lib.rs"],
+				apex_available: ["myapex"],
+			}
+			rust_library {
+				name: "liblog_rust", // test mock
+				crate_name: "log_rust",
+				srcs: ["src/lib.rs"],
+				apex_available: ["myapex"],
+                        }
 			rust_ffi_shared {
 				name: "libmy_rust_library",
 				srcs: ["src/lib.rs"],
@@ -392,6 +422,17 @@
 					name: "server_configurable_flags",
 					srcs: ["server_configurable_flags.cc"],
 				}
+				cc_library {
+					name: "libbase",
+					srcs: ["libbase.cc"],
+			                apex_available: [
+				            "myapex",
+			                ],
+				}
+				cc_library {
+					name: "libaconfig_storage_read_api_cc",
+					srcs: ["libaconfig_storage_read_api_cc.cc"],
+				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_foo",
 					package: "com.example.package",
@@ -442,6 +483,17 @@
 					name: "server_configurable_flags",
 					srcs: ["server_configurable_flags.cc"],
 				}
+				cc_library {
+					name: "libbase",
+					srcs: ["libbase.cc"],
+			                apex_available: [
+				            "myapex",
+			                ],
+				}
+				cc_library {
+					name: "libaconfig_storage_read_api_cc",
+					srcs: ["libaconfig_storage_read_api_cc.cc"],
+				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_foo",
 					package: "com.example.package",
@@ -480,6 +532,24 @@
 				srcs: ["src/lib.rs"],
 				apex_available: ["myapex"],
 			}
+			rust_library {
+				name: "libaconfig_storage_read_api", // test mock
+				crate_name: "aconfig_storage_read_api",
+				srcs: ["src/lib.rs"],
+				apex_available: ["myapex"],
+			}
+			rust_library {
+				name: "liblogger", // test mock
+				crate_name: "logger",
+				srcs: ["src/lib.rs"],
+				apex_available: ["myapex"],
+			}
+			rust_library {
+				name: "liblog_rust", // test mock
+				crate_name: "log_rust",
+				srcs: ["src/lib.rs"],
+				apex_available: ["myapex"],
+			}
 			rust_ffi_shared {
 				name: "libmy_rust_library",
 				srcs: ["src/lib.rs"],
@@ -524,6 +594,24 @@
 				srcs: ["src/lib.rs"],
 				apex_available: ["myapex"],
 			}
+			rust_library {
+				name: "libaconfig_storage_read_api", // test mock
+				crate_name: "aconfig_storage_read_api",
+				srcs: ["src/lib.rs"],
+				apex_available: ["myapex"],
+			}
+			rust_library {
+				name: "liblogger", // test mock
+				crate_name: "logger",
+				srcs: ["src/lib.rs"],
+				apex_available: ["myapex"],
+			}
+			rust_library {
+				name: "liblog_rust", // test mock
+				crate_name: "log_rust",
+				srcs: ["src/lib.rs"],
+				apex_available: ["myapex"],
+			}
 			rust_binary {
 				name: "my_rust_binary",
 				srcs: ["foo/bar/MyClass.rs"],
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 619be8d..4112108 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -218,7 +218,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 32a3638..f9b30d4 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -18,7 +18,6 @@
 
 import (
 	"fmt"
-	"log"
 	"path/filepath"
 	"regexp"
 	"sort"
@@ -73,7 +72,7 @@
 	// Run mark_platform_availability before the apexMutator as the apexMutator needs to know whether
 	// it should create a platform variant.
 	ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
-	ctx.BottomUp("apex", apexMutator).Parallel()
+	ctx.Transition("apex", &apexTransitionMutator{})
 	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
 	ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel()
 	// Register after apex_info mutator so that it can use ApexVariationName
@@ -137,10 +136,6 @@
 	// Rust binaries with prefer_rlib:true add unnecessary dependencies.
 	Unwanted_transitive_deps []string
 
-	// The minimum SDK version that this APEX must support at minimum. This is usually set to
-	// the SDK version that the APEX was first introduced.
-	Min_sdk_version *string
-
 	// Whether this APEX is considered updatable or not. When set to true, this will enforce
 	// additional rules for making sure that the APEX is truly updatable. To be updatable,
 	// min_sdk_version should be set as well. This will also disable the size optimizations like
@@ -162,8 +157,7 @@
 	// 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.
+	// Deprecated. Do not use. TODO(b/350644693) remove this after removing all usage
 	Use_vndk_as_stable *bool
 
 	// The type of filesystem to use. Either 'ext4', 'f2fs' or 'erofs'. Default 'ext4'.
@@ -358,6 +352,8 @@
 	// be removed from PRODUCT_PACKAGES.
 	Overrides []string
 
+	Multilib apexMultilibProperties
+
 	// Logging parent value.
 	Logging_parent string
 
@@ -386,6 +382,10 @@
 
 	// Trim against a specific Dynamic Common Lib APEX
 	Trim_against *string
+
+	// The minimum SDK version that this APEX must support at minimum. This is usually set to
+	// the SDK version that the APEX was first introduced.
+	Min_sdk_version *string
 }
 
 type apexBundle struct {
@@ -489,8 +489,8 @@
 
 	aconfigFiles []android.Path
 
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
+	// Required modules, filled out during GenerateAndroidBuildActions and used in AndroidMk
+	required []string
 }
 
 // apexFileClass represents a type of file that can be included in APEX.
@@ -569,7 +569,7 @@
 	if module != nil {
 		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
@@ -700,7 +700,12 @@
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) {
 	binVariations := target.Variations()
 	libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
-	rustLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"})
+	rustLibVariations := append(
+		target.Variations(), []blueprint.Variation{
+			{Mutator: "rust_libraries", Variation: "dylib"},
+			{Mutator: "link", Variation: ""},
+		}...,
+	)
 
 	// Append "image" variation
 	binVariations = append(binVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation})
@@ -735,39 +740,25 @@
 // suffix indicates the vndk version for vendor/product if vndk is enabled.
 // getImageVariation can simply join the result of this function to get the
 // image variation name.
-func (a *apexBundle) getImageVariationPair(deviceConfig android.DeviceConfig) (string, string) {
+func (a *apexBundle) getImageVariationPair() (string, string) {
 	if a.vndkApex {
-		return cc.VendorVariationPrefix, a.vndkVersion(deviceConfig)
+		return cc.VendorVariationPrefix, a.vndkVersion()
 	}
 
 	prefix := android.CoreVariation
-	vndkVersion := ""
-	if deviceConfig.VndkVersion() != "" {
-		if a.SocSpecific() || a.DeviceSpecific() {
-			prefix = cc.VendorVariationPrefix
-			vndkVersion = deviceConfig.VndkVersion()
-		} else if a.ProductSpecific() {
-			prefix = cc.ProductVariationPrefix
-			vndkVersion = deviceConfig.PlatformVndkVersion()
-		}
-	} else {
-		if a.SocSpecific() || a.DeviceSpecific() {
-			prefix = cc.VendorVariation
-		} else if a.ProductSpecific() {
-			prefix = cc.ProductVariation
-		}
-	}
-	if vndkVersion == "current" {
-		vndkVersion = deviceConfig.PlatformVndkVersion()
+	if a.SocSpecific() || a.DeviceSpecific() {
+		prefix = android.VendorVariation
+	} else if a.ProductSpecific() {
+		prefix = android.ProductVariation
 	}
 
-	return prefix, vndkVersion
+	return prefix, ""
 }
 
 // getImageVariation returns the image variant name for this apexBundle. In most cases, it's simply
 // android.CoreVariation, but gets complicated for the vendor APEXes and the VNDK APEX.
-func (a *apexBundle) getImageVariation(ctx android.BottomUpMutatorContext) string {
-	prefix, vndkVersion := a.getImageVariationPair(ctx.DeviceConfig())
+func (a *apexBundle) getImageVariation() string {
+	prefix, vndkVersion := a.getImageVariationPair()
 	return prefix + vndkVersion
 }
 
@@ -777,7 +768,7 @@
 	// each target os/architectures, appropriate dependencies are selected by their
 	// target.<os>.multilib.<type> groups and are added as (direct) dependencies.
 	targets := ctx.MultiTargets()
-	imageVariation := a.getImageVariation(ctx)
+	imageVariation := a.getImageVariation()
 
 	a.combineProperties(ctx)
 
@@ -958,30 +949,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())
-	excludeVndkLibs := useVndk && a.useVndkAsStable(mctx)
-	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")
-		}
-		mctx.VisitDirectDepsWithTag(sharedLibTag, func(dep android.Module) {
-			if c, ok := dep.(*cc.Module); ok && c.IsVndk() {
-				mctx.PropertyErrorf("use_vndk_as_stable", "Trying to include a VNDK library(%s) while use_vndk_as_stable is true.", dep.Name())
-			}
-		})
-		if mctx.Failed() {
-			return
-		}
-	}
-
 	continueApexDepsWalk := func(child, parent android.Module) bool {
 		am, ok := child.(android.ApexModule)
 		if !ok || !am.CanHaveApexVariants() {
@@ -998,17 +965,6 @@
 		if !android.IsDepInSameApex(mctx, parent, child) {
 			return false
 		}
-		if excludeVndkLibs {
-			if c, ok := child.(*cc.Module); ok && c.IsVndk() {
-				return false
-			}
-		}
-
-		//TODO: b/296491928 Vendor APEX should use libbinder.ndk instead of libbinder once VNDK is fully deprecated.
-		if useVndk && mctx.Config().IsVndkDeprecated() && child.Name() == "libbinder" {
-			log.Print("Libbinder is linked from Vendor APEX ", a.Name(), " with module ", parent.Name())
-			return false
-		}
 
 		// By default, all the transitive dependencies are collected, unless filtered out
 		// above.
@@ -1045,6 +1001,11 @@
 	// be built for this apexBundle.
 
 	apexVariationName := mctx.ModuleName() // could be com.android.foo
+	if overridable, ok := mctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" {
+		// use the overridden name com.mycompany.android.foo
+		apexVariationName = overridable.GetOverriddenBy()
+	}
+
 	a.properties.ApexVariationName = apexVariationName
 	testApexes := []string{}
 	if a.testApex {
@@ -1059,6 +1020,7 @@
 		InApexModules:     []string{a.Name()}, // could be com.mycompany.android.foo
 		ApexContents:      []*android.ApexContents{apexContents},
 		TestApexes:        testApexes,
+		BaseApexName:      mctx.ModuleName(),
 	}
 	mctx.WalkDeps(func(child, parent android.Module) bool {
 		if !continueApexDepsWalk(child, parent) {
@@ -1089,23 +1051,27 @@
 // specific variant to modules that support the ApexInfoMutator.
 // It also propagates updatable=true to apps of updatable apexes
 func apexInfoMutator(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 
 	if a, ok := mctx.Module().(ApexInfoMutator); ok {
 		a.ApexInfoMutator(mctx)
 	}
+
+	if am, ok := mctx.Module().(android.ApexModule); ok {
+		android.ApexInfoMutator(mctx, am)
+	}
 	enforceAppUpdatability(mctx)
 }
 
 // apexStrictUpdatibilityLintMutator propagates strict_updatability_linting to transitive deps of a mainline module
 // This check is enforced for updatable modules
 func apexStrictUpdatibilityLintMutator(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
-	if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting() {
+	if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting(mctx) {
 		mctx.WalkDeps(func(child, parent android.Module) bool {
 			// b/208656169 Do not propagate strict updatability linting to libcore/
 			// These libs are available on the classpath during compilation
@@ -1129,7 +1095,7 @@
 
 // enforceAppUpdatability propagates updatable=true to apps of updatable apexes
 func enforceAppUpdatability(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if apex, ok := mctx.Module().(*apexBundle); ok && apex.Updatable() {
@@ -1183,6 +1149,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
 	}
@@ -1199,15 +1166,16 @@
 	}
 )
 
-func (a *apexBundle) checkStrictUpdatabilityLinting() bool {
-	return a.Updatable() && !android.InList(a.ApexVariationName(), skipStrictUpdatabilityLintAllowlist)
+func (a *apexBundle) checkStrictUpdatabilityLinting(mctx android.TopDownMutatorContext) bool {
+	// The allowlist contains the base apex name, so use that instead of the ApexVariationName
+	return a.Updatable() && !android.InList(mctx.ModuleName(), skipStrictUpdatabilityLintAllowlist)
 }
 
 // apexUniqueVariationsMutator checks if any dependencies use unique apex variations. If so, use
 // unique apex variations for this module. See android/apex.go for more about unique apex variant.
 // TODO(jiyong): move this to android/apex.go?
 func apexUniqueVariationsMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if am, ok := mctx.Module().(android.ApexModule); ok {
@@ -1219,7 +1187,7 @@
 // the apex in order to retrieve its contents later.
 // TODO(jiyong): move this to android/apex.go?
 func apexTestForDepsMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if am, ok := mctx.Module().(android.ApexModule); ok {
@@ -1234,7 +1202,7 @@
 
 // TODO(jiyong): move this to android/apex.go?
 func apexTestForMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if _, ok := mctx.Module().(android.ApexModule); ok {
@@ -1296,44 +1264,43 @@
 	}
 }
 
-// apexMutator visits each module and creates apex variations if the module was marked in the
-// previous run of apexInfoMutator.
-func apexMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled() {
-		return
-	}
+type apexTransitionMutator struct{}
 
-	// This is the usual path.
-	if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
-		android.CreateApexVariations(mctx, am)
-		return
-	}
-
+func (a *apexTransitionMutator) Split(ctx android.BaseModuleContext) []string {
 	// apexBundle itself is mutated so that it and its dependencies have the same apex variant.
-	if ai, ok := mctx.Module().(ApexInfoMutator); ok && apexModuleTypeRequiresVariant(ai) {
-		apexBundleName := ai.ApexVariationName()
-		mctx.CreateVariations(apexBundleName)
-		if strings.HasPrefix(apexBundleName, "com.android.art") {
-			// Create an alias from the platform variant. This is done to make
-			// test_for dependencies work for modules that are split by the APEX
-			// mutator, since test_for dependencies always go to the platform variant.
-			// This doesn't happen for normal APEXes that are disjunct, so only do
-			// this for the overlapping ART APEXes.
-			// TODO(b/183882457): Remove this if the test_for functionality is
-			// refactored to depend on the proper APEX variants instead of platform.
-			mctx.CreateAliasVariation("", apexBundleName)
+	if ai, ok := ctx.Module().(ApexInfoMutator); ok && apexModuleTypeRequiresVariant(ai) {
+		if overridable, ok := ctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" {
+			return []string{overridable.GetOverriddenBy()}
 		}
-	} else if o, ok := mctx.Module().(*OverrideApex); ok {
-		apexBundleName := o.GetOverriddenModuleName()
-		if apexBundleName == "" {
-			mctx.ModuleErrorf("base property is not set")
-			return
+		return []string{ai.ApexVariationName()}
+	} else if _, ok := ctx.Module().(*OverrideApex); ok {
+		return []string{ctx.ModuleName()}
+	}
+	return []string{""}
+}
+
+func (a *apexTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (a *apexTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	if am, ok := ctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
+		return android.IncomingApexTransition(ctx, incomingVariation)
+	} else if ai, ok := ctx.Module().(ApexInfoMutator); ok {
+		if overridable, ok := ctx.Module().(android.OverridableModule); ok && overridable.GetOverriddenBy() != "" {
+			return overridable.GetOverriddenBy()
 		}
-		mctx.CreateVariations(apexBundleName)
-		if strings.HasPrefix(apexBundleName, "com.android.art") {
-			// TODO(b/183882457): See note for CreateAliasVariation above.
-			mctx.CreateAliasVariation("", apexBundleName)
-		}
+		return ai.ApexVariationName()
+	} else if _, ok := ctx.Module().(*OverrideApex); ok {
+		return ctx.Module().Name()
+	}
+
+	return ""
+}
+
+func (a *apexTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if am, ok := ctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
+		android.MutateApexTransition(ctx, variation)
 	}
 }
 
@@ -1351,7 +1318,7 @@
 // See android.UpdateDirectlyInAnyApex
 // TODO(jiyong): move this to android/apex.go?
 func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if am, ok := mctx.Module().(android.ApexModule); ok {
@@ -1381,25 +1348,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 {
@@ -1415,7 +1363,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()
 }
 
@@ -1538,7 +1486,7 @@
 	// TODO(jiyong): move this info (the sanitizer name, the lib name, etc.) to cc/sanitize.go
 	// Keep only the mechanism here.
 	if sanitizerName == "hwaddress" && strings.HasPrefix(a.Name(), "com.android.runtime") {
-		imageVariation := a.getImageVariation(ctx)
+		imageVariation := a.getImageVariation()
 		for _, target := range ctx.MultiTargets() {
 			if target.Arch.ArchType.Multilib == "lib64" {
 				addDependenciesForNativeModules(ctx, ApexNativeDependencies{
@@ -1648,10 +1596,10 @@
 	return af
 }
 
-func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, depName string) apexFile {
+func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, outputFile android.Path) apexFile {
 	dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir())
-	fileToCopy := prebuilt.OutputFile()
-	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt)
+	makeModuleName := strings.ReplaceAll(filepath.Join(dirInApex, outputFile.Base()), "/", "_")
+	return newApexFile(ctx, outputFile, makeModuleName, dirInApex, etc, prebuilt)
 }
 
 func apexFileForCompatConfig(ctx android.BaseModuleContext, config java.PlatformCompatConfigIntf, depName string) apexFile {
@@ -1678,20 +1626,28 @@
 var _ javaModule = (*java.SdkLibraryImport)(nil)
 
 // apexFileForJavaModule creates an apexFile for a java module's dex implementation jar.
-func apexFileForJavaModule(ctx android.BaseModuleContext, module javaModule) apexFile {
+func apexFileForJavaModule(ctx android.ModuleContext, module javaModule) apexFile {
 	return apexFileForJavaModuleWithFile(ctx, module, module.DexJarBuildPath(ctx).PathOrNil())
 }
 
 // apexFileForJavaModuleWithFile creates an apexFile for a java module with the supplied file.
-func apexFileForJavaModuleWithFile(ctx android.BaseModuleContext, module javaModule, dexImplementationJar android.Path) apexFile {
+func apexFileForJavaModuleWithFile(ctx android.ModuleContext, module javaModule, dexImplementationJar android.Path) apexFile {
 	dirInApex := "javalib"
 	af := newApexFile(ctx, dexImplementationJar, module.BaseModuleName(), dirInApex, javaSharedLib, module)
 	af.jacocoReportClassesFile = module.JacocoReportClassesFile()
 	af.lintDepSets = module.LintDepSets()
 	af.customStem = module.Stem() + ".jar"
-	if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
+	// TODO: b/338641779 - Remove special casing of sdkLibrary once bcpf and sscpf depends
+	// on the implementation library
+	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
@@ -1821,6 +1777,9 @@
 		if dt, ok := depTag.(*dependencyTag); ok && !dt.payload {
 			return false
 		}
+		if depTag == android.RequiredDepTag {
+			return false
+		}
 
 		ai, _ := android.OtherModuleProvider(ctx, child, android.ApexInfoProvider)
 		externalDep := !android.InList(ctx.ModuleName(), ai.InApexVariants)
@@ -1977,7 +1936,7 @@
 	if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
 		return false
 	}
-	if mod, ok := child.(android.Module); ok && !mod.Enabled() {
+	if mod, ok := child.(android.Module); ok && !mod.Enabled(ctx) {
 		return false
 	}
 	depName := ctx.OtherModuleName(child)
@@ -2104,7 +2063,7 @@
 			}
 		case bpfTag:
 			if bpfProgram, ok := child.(bpf.BpfModule); ok {
-				filesToCopy, _ := bpfProgram.OutputFiles("")
+				filesToCopy := android.OutputFilesForModule(ctx, bpfProgram, "")
 				apex_sub_dir := bpfProgram.SubDir()
 				for _, bpfFile := range filesToCopy {
 					vctx.filesInfo = append(vctx.filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
@@ -2120,7 +2079,10 @@
 			}
 		case prebuiltTag:
 			if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+				filesToCopy := android.OutputFilesForModule(ctx, prebuilt, "")
+				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)
@@ -2133,20 +2095,10 @@
 			}
 		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)
+				addAconfigFiles(vctx, ctx, child)
 				return true // track transitive dependencies
 			} else {
 				ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
@@ -2182,15 +2134,6 @@
 	// tags used below are private (e.g. `cc.sharedDepTag`).
 	if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
 		if ch, ok := child.(*cc.Module); ok {
-			if ch.UseVndk() && a.useVndkAsStable(ctx) && ch.IsVndk() {
-				vctx.requireNativeLibs = append(vctx.requireNativeLibs, ":vndk")
-				return false
-			}
-
-			//TODO: b/296491928 Vendor APEX should use libbinder.ndk instead of libbinder once VNDK is fully deprecated.
-			if ch.InVendorOrProduct() && ctx.Config().IsVndkDeprecated() && child.Name() == "libbinder" {
-				return false
-			}
 			af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
 			af.transitiveDep = true
 
@@ -2244,26 +2187,16 @@
 			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) {
 		// nothing
 	} else if java.IsJniDepTag(depTag) {
 		// Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
 	} else if java.IsXmlPermissionsFileDepTag(depTag) {
 		if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
-			vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+			filesToCopy := android.OutputFilesForModule(ctx, prebuilt, "")
+			for _, etcFile := range filesToCopy {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile))
+			}
 		}
 	} else if rust.IsDylibDepTag(depTag) {
 		if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
@@ -2314,6 +2247,8 @@
 		// nothing
 	} else if depTag == android.DarwinUniversalVariantTag {
 		// nothing
+	} else if depTag == android.RequiredDepTag {
+		// nothing
 	} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
 		ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
 	}
@@ -2321,7 +2256,7 @@
 }
 
 func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) {
-	if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigTransitiveDeclarationsInfoProvider); ok {
+	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()]...)
 		}
@@ -2409,7 +2344,6 @@
 			return
 		}
 	}
-	android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles)
 
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 3) some fields in apexBundle struct are configured
@@ -2432,6 +2366,10 @@
 	a.provideApexExportsInfo(ctx)
 
 	a.providePrebuiltInfo(ctx)
+
+	a.required = a.RequiredModuleNames(ctx)
+
+	a.setOutputFiles(ctx)
 }
 
 // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@@ -2460,6 +2398,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 {
@@ -2581,9 +2531,6 @@
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
-
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
 }
 
 // apex_defaults provides defaultable properties to other apex modules.
@@ -2606,10 +2553,6 @@
 	android.OverrideModuleBase
 }
 
-func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles)
-}
-
 func (o *OverrideApex) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 }
@@ -2652,7 +2595,7 @@
 	// Only override the minSdkVersion value on Apexes which already specify
 	// a min_sdk_version (it's optional for non-updatable apexes), and that its
 	// min_sdk_version value is lower than the one to override with.
-	minApiLevel := android.MinSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
+	minApiLevel := android.MinSdkVersionFromValue(ctx, proptools.String(a.overridableProperties.Min_sdk_version))
 	if minApiLevel.IsNone() {
 		return ""
 	}
@@ -2720,18 +2663,23 @@
 	})
 }
 
+// TODO (b/221087384): Remove this allowlist
+var (
+	updatableApexesWithCurrentMinSdkVersionAllowlist = []string{"com.android.profiling"}
+)
+
 // checkUpdatable enforces APEX and its transitive dep properties to have desired values for updatable APEXes.
 func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
 	if a.Updatable() {
 		if a.minSdkVersionValue(ctx) == "" {
 			ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
 		}
+		if a.minSdkVersion(ctx).IsCurrent() && !android.InList(ctx.ModuleName(), updatableApexesWithCurrentMinSdkVersionAllowlist) {
+			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:`")
 		}
@@ -3014,12 +2962,3 @@
 func (a *apexBundle) IsTestApex() bool {
 	return a.testApex
 }
-
-func (a *apexBundle) useVndkAsStable(ctx android.BaseModuleContext) bool {
-	// VNDK cannot be linked if it is deprecated
-	if ctx.Config().IsVndkDeprecated() {
-		return false
-	}
-
-	return proptools.Bool(a.properties.Use_vndk_as_stable)
-}
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index e6ebff2..a8d89b1 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",
diff --git a/apex/apex_test.go b/apex/apex_test.go
index add6083..a2dbbfc 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -218,7 +218,6 @@
 	),
 
 	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.DeviceVndkVersion = proptools.StringPtr("current")
 		variables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
 		variables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
 		variables.Platform_sdk_codename = proptools.StringPtr("Q")
@@ -226,7 +225,6 @@
 		// "Tiramisu" needs to be in the next line for compatibility with soong code,
 		// not because of these tests specifically (it's not used by the tests)
 		variables.Platform_version_active_codenames = []string{"Q", "Tiramisu"}
-		variables.Platform_vndk_version = proptools.StringPtr("29")
 		variables.BuildId = proptools.StringPtr("TEST.BUILD_ID")
 	}),
 )
@@ -2062,9 +2060,9 @@
 		}
 	`)
 
-	vendorVariant := "android_vendor.29_arm64_armv8-a"
+	vendorVariant := "android_vendor_arm64_armv8-a"
 
-	mylib := ctx.ModuleForTests("mylib", vendorVariant+"_shared_myapex")
+	mylib := ctx.ModuleForTests("mylib", vendorVariant+"_shared_apex29")
 
 	// Ensure that mylib links with "current" LLNDK
 	libFlags := names(mylib.Rule("ld").Args["libFlags"])
@@ -3025,158 +3023,6 @@
 	ensureListNotContains(t, requireNativeLibs, ":vndk")
 }
 
-func TestVendorApex_use_vndk_as_stable_TryingToIncludeVNDKLib(t *testing.T) {
-	testApexError(t, `Trying to include a VNDK library`, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			native_shared_libs: ["libc++"], // libc++ is a VNDK lib
-			vendor: true,
-			use_vndk_as_stable: true,
-			updatable: false,
-		}
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}`)
-}
-
-func TestVendorApex_use_vndk_as_stable(t *testing.T) {
-	//   myapex                  myapex2
-	//    |                       |
-	//  mybin ------.           mybin2
-	//   \           \          /  |
-	// (stable)   .---\--------`   |
-	//     \     /     \           |
-	//      \   /       \         /
-	//      libvndk       libvendor
-	//      (vndk)
-	ctx := testApex(t, `
-		apex {
-			name: "myapex",
-			key: "myapex.key",
-			binaries: ["mybin"],
-			vendor: true,
-			use_vndk_as_stable: true,
-			updatable: false,
-		}
-		apex_key {
-			name: "myapex.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-		cc_binary {
-			name: "mybin",
-			vendor: true,
-			shared_libs: ["libvndk", "libvendor"],
-		}
-		cc_library {
-			name: "libvndk",
-			vndk: {
-				enabled: true,
-			},
-			vendor_available: true,
-			product_available: true,
-		}
-		cc_library {
-			name: "libvendor",
-			vendor: true,
-			stl: "none",
-		}
-		apex {
-			name: "myapex2",
-			key: "myapex.key",
-			binaries: ["mybin2"],
-			vendor: true,
-			use_vndk_as_stable: false,
-			updatable: false,
-		}
-		cc_binary {
-			name: "mybin2",
-			vendor: true,
-			shared_libs: ["libvndk", "libvendor"],
-		}
-	`,
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.TestProductVariables.KeepVndk = proptools.BoolPtr(true)
-		}),
-	)
-
-	vendorVariant := "android_vendor.29_arm64_armv8-a"
-
-	for _, tc := range []struct {
-		name                 string
-		apexName             string
-		moduleName           string
-		moduleVariant        string
-		libs                 []string
-		contents             []string
-		requireVndkNamespace bool
-	}{
-		{
-			name:          "use_vndk_as_stable",
-			apexName:      "myapex",
-			moduleName:    "mybin",
-			moduleVariant: vendorVariant + "_apex10000",
-			libs: []string{
-				// should link with vendor variants of VNDK libs(libvndk/libc++)
-				"out/soong/.intermediates/libvndk/" + vendorVariant + "_shared/libvndk.so",
-				"out/soong/.intermediates/" + cc.DefaultCcCommonTestModulesDir + "libc++/" + vendorVariant + "_shared/libc++.so",
-				// unstable Vendor libs as APEX variant
-				"out/soong/.intermediates/libvendor/" + vendorVariant + "_shared_apex10000/libvendor.so",
-			},
-			contents: []string{
-				"bin/mybin",
-				"lib64/libvendor.so",
-				// VNDK libs (libvndk/libc++) are not included
-			},
-			requireVndkNamespace: true,
-		},
-		{
-			name:          "!use_vndk_as_stable",
-			apexName:      "myapex2",
-			moduleName:    "mybin2",
-			moduleVariant: vendorVariant + "_myapex2",
-			libs: []string{
-				// should link with "unique" APEX(myapex2) variant of VNDK libs(libvndk/libc++)
-				"out/soong/.intermediates/libvndk/" + vendorVariant + "_shared_myapex2/libvndk.so",
-				"out/soong/.intermediates/" + cc.DefaultCcCommonTestModulesDir + "libc++/" + vendorVariant + "_shared_myapex2/libc++.so",
-				// unstable vendor libs have "merged" APEX variants
-				"out/soong/.intermediates/libvendor/" + vendorVariant + "_shared_apex10000/libvendor.so",
-			},
-			contents: []string{
-				"bin/mybin2",
-				"lib64/libvendor.so",
-				// VNDK libs are included as well
-				"lib64/libvndk.so",
-				"lib64/libc++.so",
-			},
-			requireVndkNamespace: false,
-		},
-	} {
-		t.Run(tc.name, func(t *testing.T) {
-			// Check linked libs
-			ldRule := ctx.ModuleForTests(tc.moduleName, tc.moduleVariant).Rule("ld")
-			libs := names(ldRule.Args["libFlags"])
-			for _, lib := range tc.libs {
-				ensureListContains(t, libs, lib)
-			}
-			// Check apex contents
-			ensureExactContents(t, ctx, tc.apexName, "android_common_"+tc.apexName, tc.contents)
-
-			// Check "requireNativeLibs"
-			apexManifestRule := ctx.ModuleForTests(tc.apexName, "android_common_"+tc.apexName).Rule("apexManifestRule")
-			requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
-			if tc.requireVndkNamespace {
-				ensureListContains(t, requireNativeLibs, ":vndk")
-			} else {
-				ensureListNotContains(t, requireNativeLibs, ":vndk")
-			}
-		})
-	}
-}
-
 func TestProductVariant(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -3202,7 +3048,7 @@
 	`)
 
 	cflags := strings.Fields(
-		ctx.ModuleForTests("foo", "android_product.29_arm64_armv8-a_myapex").Rule("cc").Args["cFlags"])
+		ctx.ModuleForTests("foo", "android_product_arm64_armv8-a_apex10000").Rule("cc").Args["cFlags"])
 	ensureListContains(t, cflags, "-D__ANDROID_VNDK__")
 	ensureListContains(t, cflags, "-D__ANDROID_APEX__")
 	ensureListContains(t, cflags, "-D__ANDROID_PRODUCT__")
@@ -3823,196 +3669,15 @@
 	assertFileListEquals(t, files, actualFiles)
 }
 
-func TestVndkApexCurrent(t *testing.T) {
-	commonFiles := []string{
-		"lib/libc++.so",
-		"lib64/libc++.so",
-		"etc/llndk.libraries.29.txt",
-		"etc/vndkcore.libraries.29.txt",
-		"etc/vndksp.libraries.29.txt",
-		"etc/vndkprivate.libraries.29.txt",
-		"etc/vndkproduct.libraries.29.txt",
-	}
-	testCases := []struct {
-		vndkVersion   string
-		expectedFiles []string
-	}{
-		{
-			vndkVersion: "current",
-			expectedFiles: append(commonFiles,
-				"lib/libvndk.so",
-				"lib/libvndksp.so",
-				"lib64/libvndk.so",
-				"lib64/libvndksp.so"),
-		},
-	}
-	for _, tc := range testCases {
-		t.Run("VNDK.current with DeviceVndkVersion="+tc.vndkVersion, func(t *testing.T) {
-			ctx := testApex(t, `
-			apex_vndk {
-				name: "com.android.vndk.current",
-				key: "com.android.vndk.current.key",
-				updatable: false,
-			}
-
-			apex_key {
-				name: "com.android.vndk.current.key",
-				public_key: "testkey.avbpubkey",
-				private_key: "testkey.pem",
-			}
-
-			cc_library {
-				name: "libvndk",
-				srcs: ["mylib.cpp"],
-				vendor_available: true,
-				product_available: true,
-				vndk: {
-					enabled: true,
-				},
-				system_shared_libs: [],
-				stl: "none",
-				apex_available: [ "com.android.vndk.current" ],
-			}
-
-			cc_library {
-				name: "libvndksp",
-				srcs: ["mylib.cpp"],
-				vendor_available: true,
-				product_available: true,
-				vndk: {
-					enabled: true,
-					support_system_process: true,
-				},
-				system_shared_libs: [],
-				stl: "none",
-				apex_available: [ "com.android.vndk.current" ],
-			}
-
-			// VNDK-Ext should not cause any problems
-
-			cc_library {
-				name: "libvndk.ext",
-				srcs: ["mylib2.cpp"],
-				vendor: true,
-				vndk: {
-					enabled: true,
-					extends: "libvndk",
-				},
-				system_shared_libs: [],
-				stl: "none",
-			}
-
-			cc_library {
-				name: "libvndksp.ext",
-				srcs: ["mylib2.cpp"],
-				vendor: true,
-				vndk: {
-					enabled: true,
-					support_system_process: true,
-					extends: "libvndksp",
-				},
-				system_shared_libs: [],
-				stl: "none",
-			}
-		`+vndkLibrariesTxtFiles("current"), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.DeviceVndkVersion = proptools.StringPtr(tc.vndkVersion)
-				variables.KeepVndk = proptools.BoolPtr(true)
-			}))
-			ensureExactContents(t, ctx, "com.android.vndk.current", "android_common", tc.expectedFiles)
-		})
-	}
-}
-
-func TestVndkApexWithPrebuilt(t *testing.T) {
-	ctx := testApex(t, `
-		apex_vndk {
-			name: "com.android.vndk.current",
-			key: "com.android.vndk.current.key",
-			updatable: false,
-		}
-
-		apex_key {
-			name: "com.android.vndk.current.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_prebuilt_library_shared {
-			name: "libvndk",
-			srcs: ["libvndk.so"],
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: [ "com.android.vndk.current" ],
-		}
-
-		cc_prebuilt_library_shared {
-			name: "libvndk.arm",
-			srcs: ["libvndk.arm.so"],
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			enabled: false,
-			arch: {
-				arm: {
-					enabled: true,
-				},
-			},
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: [ "com.android.vndk.current" ],
-		}
-		`+vndkLibrariesTxtFiles("current"),
-		withFiles(map[string][]byte{
-			"libvndk.so":     nil,
-			"libvndk.arm.so": nil,
-		}))
-	ensureExactContents(t, ctx, "com.android.vndk.current", "android_common", []string{
-		"lib/libvndk.so",
-		"lib/libvndk.arm.so",
-		"lib64/libvndk.so",
-		"lib/libc++.so",
-		"lib64/libc++.so",
-		"etc/*",
-	})
-}
-
 func vndkLibrariesTxtFiles(vers ...string) (result string) {
 	for _, v := range vers {
-		if v == "current" {
-			for _, txt := range []string{"vndkcore", "vndksp", "vndkprivate", "vndkproduct"} {
-				result += `
-					` + txt + `_libraries_txt {
-						name: "` + txt + `.libraries.txt",
-						insert_vndk_version: true,
-					}
-				`
-			}
+		for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkproduct"} {
 			result += `
-				llndk_libraries_txt {
-					name: "llndk.libraries.txt",
-				}
-				llndk_libraries_txt_for_apex {
-					name: "llndk.libraries.txt.apex",
-					stem: "llndk.libraries.txt",
-					insert_vndk_version: true,
-				}
-			`
-		} else {
-			for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkproduct"} {
-				result += `
 					prebuilt_etc {
 						name: "` + txt + `.libraries.` + v + `.txt",
 						src: "dummy.txt",
 					}
 				`
-			}
 		}
 	}
 	return
@@ -4090,9 +3755,10 @@
 func TestVndkApexNameRule(t *testing.T) {
 	ctx := testApex(t, `
 		apex_vndk {
-			name: "com.android.vndk.current",
+			name: "com.android.vndk.v29",
 			key: "myapex.key",
 			file_contexts: ":myapex-file_contexts",
+			vndk_version: "29",
 			updatable: false,
 		}
 		apex_vndk {
@@ -4106,7 +3772,7 @@
 			name: "myapex.key",
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
-		}`+vndkLibrariesTxtFiles("28", "current"))
+		}`+vndkLibrariesTxtFiles("28", "29"))
 
 	assertApexName := func(expected, moduleName string) {
 		module := ctx.ModuleForTests(moduleName, "android_common")
@@ -4114,78 +3780,36 @@
 		ensureContains(t, apexManifestRule.Args["opt"], "-v name "+expected)
 	}
 
-	assertApexName("com.android.vndk.v29", "com.android.vndk.current")
+	assertApexName("com.android.vndk.v29", "com.android.vndk.v29")
 	assertApexName("com.android.vndk.v28", "com.android.vndk.v28")
 }
 
-func TestVndkApexSkipsNativeBridgeSupportedModules(t *testing.T) {
-	ctx := testApex(t, `
-		apex_vndk {
-			name: "com.android.vndk.current",
-			key: "com.android.vndk.current.key",
-			file_contexts: ":myapex-file_contexts",
-			updatable: false,
-		}
-
-		apex_key {
-			name: "com.android.vndk.current.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "libvndk",
-			srcs: ["mylib.cpp"],
-			vendor_available: true,
-			product_available: true,
-			native_bridge_supported: true,
-			host_supported: true,
-			vndk: {
-				enabled: true,
-			},
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: [ "com.android.vndk.current" ],
-		}
-		`+vndkLibrariesTxtFiles("current"),
-		withNativeBridgeEnabled)
-
-	ensureExactContents(t, ctx, "com.android.vndk.current", "android_common", []string{
-		"lib/libvndk.so",
-		"lib64/libvndk.so",
-		"lib/libc++.so",
-		"lib64/libc++.so",
-		"etc/*",
-	})
-}
-
 func TestVndkApexDoesntSupportNativeBridgeSupported(t *testing.T) {
-	testApexError(t, `module "com.android.vndk.current" .*: native_bridge_supported: .* doesn't support native bridge binary`, `
+	testApexError(t, `module "com.android.vndk.v30" .*: native_bridge_supported: .* doesn't support native bridge binary`, `
 		apex_vndk {
-			name: "com.android.vndk.current",
-			key: "com.android.vndk.current.key",
+			name: "com.android.vndk.v30",
+			key: "com.android.vndk.v30.key",
 			file_contexts: ":myapex-file_contexts",
 			native_bridge_supported: true,
 		}
 
 		apex_key {
-			name: "com.android.vndk.current.key",
+			name: "com.android.vndk.v30.key",
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
 
-		cc_library {
+		vndk_prebuilt_shared {
 			name: "libvndk",
+			version: "30",
+			target_arch: "arm",
 			srcs: ["mylib.cpp"],
 			vendor_available: true,
 			product_available: true,
 			native_bridge_supported: true,
-			host_supported: true,
 			vndk: {
 				enabled: true,
 			},
-			system_shared_libs: [],
-			stl: "none",
 		}
 	`)
 }
@@ -4259,215 +3883,6 @@
 	})
 }
 
-func TestVndkApexShouldNotProvideNativeLibs(t *testing.T) {
-	ctx := testApex(t, `
-		apex_vndk {
-			name: "com.android.vndk.current",
-			key: "com.android.vndk.current.key",
-			file_contexts: ":myapex-file_contexts",
-			updatable: false,
-		}
-
-		apex_key {
-			name: "com.android.vndk.current.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "libz",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			stubs: {
-				symbol_file: "libz.map.txt",
-				versions: ["30"],
-			}
-		}
-	`+vndkLibrariesTxtFiles("current"), withFiles(map[string][]byte{
-		"libz.map.txt": nil,
-	}))
-
-	apexManifestRule := ctx.ModuleForTests("com.android.vndk.current", "android_common").Rule("apexManifestRule")
-	provideNativeLibs := names(apexManifestRule.Args["provideNativeLibs"])
-	ensureListEmpty(t, provideNativeLibs)
-	ensureExactContents(t, ctx, "com.android.vndk.current", "android_common", []string{
-		"out/soong/.intermediates/libz/android_vendor.29_arm64_armv8-a_shared/libz.so:lib64/libz.so",
-		"out/soong/.intermediates/libz/android_vendor.29_arm_armv7-a-neon_shared/libz.so:lib/libz.so",
-		"*/*",
-	})
-}
-
-func TestVendorApexWithVndkPrebuilts(t *testing.T) {
-	ctx := testApex(t, "",
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.DeviceVndkVersion = proptools.StringPtr("27")
-		}),
-		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-			cc.RegisterVendorSnapshotModules(ctx)
-		}),
-		withFiles(map[string][]byte{
-			"vendor/foo/Android.bp": []byte(`
-				apex {
-					name: "myapex",
-					binaries: ["foo"],
-					key: "myapex.key",
-					min_sdk_version: "27",
-					vendor: true,
-				}
-
-				cc_binary {
-					name: "foo",
-					vendor: true,
-					srcs: ["abc.cpp"],
-					shared_libs: [
-						"libllndk",
-						"libvndk",
-					],
-					nocrt: true,
-					system_shared_libs: [],
-					min_sdk_version: "27",
-				}
-
-				apex_key {
-					name: "myapex.key",
-					public_key: "testkey.avbpubkey",
-					private_key: "testkey.pem",
-				}
-			`),
-			// Simulate VNDK prebuilts with vendor_snapshot
-			"prebuilts/vndk/Android.bp": []byte(`
-				vndk_prebuilt_shared {
-					name: "libllndk",
-					version: "27",
-					vendor_available: true,
-					product_available: true,
-					target_arch: "arm64",
-					arch: {
-						arm64: {
-							srcs: ["libllndk.so"],
-						},
-					},
-				}
-
-				vndk_prebuilt_shared {
-					name: "libvndk",
-					version: "27",
-					vendor_available: true,
-					product_available: true,
-					target_arch: "arm64",
-					arch: {
-						arm64: {
-							srcs: ["libvndk.so"],
-						},
-					},
-					vndk: {
-						enabled: true,
-					},
-					min_sdk_version: "27",
-				}
-
-				vndk_prebuilt_shared {
-					name: "libc++",
-					version: "27",
-					target_arch: "arm64",
-					vendor_available: true,
-					product_available: true,
-					vndk: {
-						enabled: true,
-						support_system_process: true,
-					},
-					arch: {
-						arm64: {
-							srcs: ["libc++.so"],
-						},
-					},
-					min_sdk_version: "apex_inherit",
-				}
-
-				vendor_snapshot {
-					name: "vendor_snapshot",
-					version: "27",
-					arch: {
-						arm64: {
-							vndk_libs: [
-								"libc++",
-								"libllndk",
-								"libvndk",
-							],
-							static_libs: [
-								"libc++demangle",
-								"libclang_rt.builtins",
-								"libunwind",
-							],
-						},
-					}
-				}
-
-				vendor_snapshot_static {
-					name: "libclang_rt.builtins",
-					version: "27",
-					target_arch: "arm64",
-					vendor: true,
-					arch: {
-						arm64: {
-							src: "libclang_rt.builtins-aarch64-android.a",
-						},
-					},
-				}
-
-				vendor_snapshot_static {
-					name: "libc++demangle",
-					version: "27",
-					target_arch: "arm64",
-					compile_multilib: "64",
-					vendor: true,
-					arch: {
-						arm64: {
-							src: "libc++demangle.a",
-						},
-					},
-					min_sdk_version: "apex_inherit",
-				}
-
-				vendor_snapshot_static {
-					name: "libunwind",
-					version: "27",
-					target_arch: "arm64",
-					compile_multilib: "64",
-					vendor: true,
-					arch: {
-						arm64: {
-							src: "libunwind.a",
-						},
-					},
-					min_sdk_version: "apex_inherit",
-				}
-			`),
-		}))
-
-	// Should embed the prebuilt VNDK libraries in the apex
-	ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{
-		"bin/foo",
-		"prebuilts/vndk/libc++.so:lib64/libc++.so",
-		"prebuilts/vndk/libvndk.so:lib64/libvndk.so",
-	})
-
-	// Should link foo with prebuilt libraries (shared/static)
-	ldRule := ctx.ModuleForTests("foo", "android_vendor.27_arm64_armv8-a_myapex").Rule("ld")
-	android.AssertStringDoesContain(t, "should link to prebuilt llndk", ldRule.Args["libFlags"], "prebuilts/vndk/libllndk.so")
-	android.AssertStringDoesContain(t, "should link to prebuilt vndk", ldRule.Args["libFlags"], "prebuilts/vndk/libvndk.so")
-	android.AssertStringDoesContain(t, "should link to prebuilt libc++demangle", ldRule.Args["libFlags"], "prebuilts/vndk/libc++demangle.a")
-	android.AssertStringDoesContain(t, "should link to prebuilt libunwind", ldRule.Args["libFlags"], "prebuilts/vndk/libunwind.a")
-
-	// Should declare the LLNDK library as a "required" external dependency
-	manifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule")
-	requireNativeLibs := names(manifestRule.Args["requireNativeLibs"])
-	ensureListContains(t, requireNativeLibs, "libllndk.so")
-}
-
 func TestDependenciesInApexManifest(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -5633,6 +5048,20 @@
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
+
+		// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
+		// file creation.
+		java.FixtureConfigureBootJars("platform:foo"),
+		android.FixtureModifyMockFS(func(fs android.MockFS) {
+			fs["platform/Android.bp"] = []byte(`
+		java_library {
+			name: "foo",
+			srcs: ["Test.java"],
+			compile_dex: true,
+		}
+		`)
+			fs["platform/Test.java"] = nil
+		}),
 	)
 
 	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
@@ -5727,7 +5156,7 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
@@ -5805,7 +5234,7 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
@@ -5994,7 +5423,7 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
@@ -6091,7 +5520,7 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 			out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/modular-hiddenapi/index.csv
@@ -6185,13 +5614,26 @@
 			compile_dex: true,
 		}
 	`
+		// This test disables libbar, which causes the ComponentDepsMutator to add
+		// deps on libbar.stubs and other sub-modules that don't exist. We can
+		// enable AllowMissingDependencies to work around that, but enabling that
+		// causes extra checks for missing source files to dex_bootjars, so add those
+		// to the mock fs as well.
+		preparer2 := android.GroupFixturePreparers(
+			preparer,
+			android.PrepareForTestWithAllowMissingDependencies,
+			android.FixtureMergeMockFs(map[string][]byte{
+				"build/soong/scripts/check_boot_jars/package_allowed_list.txt": nil,
+				"frameworks/base/config/boot-profile.txt":                      nil,
+			}),
+		)
 
-		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
+		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, ``)
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`)
 		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
@@ -6286,7 +5728,6 @@
 			updatable: false,
 			tests: [
 				"mytest",
-				"mytests",
 			],
 		}
 
@@ -6329,25 +5770,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")
@@ -6361,11 +5783,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)
@@ -6375,9 +5792,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")
 }
 
@@ -6479,6 +5893,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			sdk_version: "current",
 			system_modules: "none",
+			use_embedded_native_libs: true,
 			jni_libs: ["libjni"],
 			stl: "none",
 			apex_available: [ "myapex" ],
@@ -7033,7 +6448,7 @@
 		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
 	}
 
-	overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_bar").Rule("apexManifestRule")
+	overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_myoverrideapex").Rule("apexManifestRule")
 	overrideBarActualDefaultVersion := overrideBarManifestRule.Args["default_version"]
 	if overrideBarActualDefaultVersion != barExpectedDefaultVersion {
 		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
@@ -7334,6 +6749,15 @@
 			bpfs: ["overrideBpf"],
 			prebuilts: ["override_myetc"],
 			overrides: ["unknownapex"],
+			compile_multilib: "first",
+			multilib: {
+				lib32: {
+					native_shared_libs: ["mylib32"],
+				},
+				lib64: {
+					native_shared_libs: ["mylib64"],
+				},
+			},
 			logging_parent: "com.foo.bar",
 			package_name: "test.overridden.package",
 			key: "mynewapex.key",
@@ -7391,10 +6815,20 @@
 			name: "override_myetc",
 			src: "override_myprebuilt",
 		}
+
+		cc_library {
+			name: "mylib32",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "mylib64",
+			apex_available: [ "myapex" ],
+		}
 	`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
 
 	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(android.OverridableModule)
-	overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex").Module().(android.OverridableModule)
+	overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex").Module().(android.OverridableModule)
 	if originalVariant.GetOverriddenBy() != "" {
 		t.Errorf("GetOverriddenBy should be empty, but was %q", originalVariant.GetOverriddenBy())
 	}
@@ -7402,7 +6836,7 @@
 		t.Errorf("GetOverriddenBy should be \"override_myapex\", but was %q", overriddenVariant.GetOverriddenBy())
 	}
 
-	module := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex")
+	module := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex")
 	apexRule := module.Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -7658,8 +7092,49 @@
 		"etc/permissions/foo.xml",
 	})
 	// Permission XML should point to the activated path of impl jar of java_sdk_library
-	sdkLibrary := ctx.ModuleForTests("foo.xml", "android_common_myapex").Rule("java_sdk_xml")
-	ensureMatches(t, sdkLibrary.RuleParams.Command, `<library\\n\s+name=\\\"foo\\\"\\n\s+file=\\\"/apex/myapex/javalib/foo.jar\\\"`)
+	sdkLibrary := ctx.ModuleForTests("foo.xml", "android_common_myapex").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 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) {
@@ -7851,7 +7326,7 @@
 
 	// The bar library should depend on the implementation jar.
 	barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
-	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -8775,60 +8250,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 {
@@ -9493,7 +8914,7 @@
 		t.Errorf("allowed_files_file: expected %q but got %q", expected, actual)
 	}
 
-	rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex").Rule("diffApexContentRule")
+	rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex").Rule("diffApexContentRule")
 	if expected, actual := "sub/allowed.txt", rule2.Args["allowed_files_file"]; expected != actual {
 		t.Errorf("allowed_files_file: expected %q but got %q", expected, actual)
 	}
@@ -9784,7 +9205,7 @@
 								continue
 							}
 							mod := ctx.ModuleForTests(modName, variant).Module().(*cc.Module)
-							if !mod.Enabled() || mod.IsHideFromMake() {
+							if !mod.Enabled(android.PanickingConfigAndErrorContext(ctx)) || mod.IsHideFromMake() {
 								continue
 							}
 							for _, ent := range android.AndroidMkEntriesForTest(t, ctx, mod) {
@@ -10047,7 +9468,6 @@
 			key: "myapex.key",
 			updatable: false,
 			java_libs: ["foo"],
-			required: ["otherapex"],
 		}
 
 		apex_key {
@@ -10344,188 +9764,246 @@
 	}
 }
 
-// TODO(b/193460475): Re-enable this test
-//func TestApexStrictUpdtabilityLint(t *testing.T) {
-//	bpTemplate := `
-//		apex {
-//			name: "myapex",
-//			key: "myapex.key",
-//			java_libs: ["myjavalib"],
-//			updatable: %v,
-//			min_sdk_version: "29",
-//		}
-//		apex_key {
-//			name: "myapex.key",
-//		}
-//		java_library {
-//			name: "myjavalib",
-//			srcs: ["MyClass.java"],
-//			apex_available: [ "myapex" ],
-//			lint: {
-//				strict_updatability_linting: %v,
-//			},
-//			sdk_version: "current",
-//			min_sdk_version: "29",
-//		}
-//		`
-//	fs := android.MockFS{
-//		"lint-baseline.xml": nil,
-//	}
-//
-//	testCases := []struct {
-//		testCaseName              string
-//		apexUpdatable             bool
-//		javaStrictUpdtabilityLint bool
-//		lintFileExists            bool
-//		disallowedFlagExpected    bool
-//	}{
-//		{
-//			testCaseName:              "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
-//			apexUpdatable:             true,
-//			javaStrictUpdtabilityLint: true,
-//			lintFileExists:            false,
-//			disallowedFlagExpected:    false,
-//		},
-//		{
-//			testCaseName:              "non-updatable apex respects strict_updatability of javalib",
-//			apexUpdatable:             false,
-//			javaStrictUpdtabilityLint: false,
-//			lintFileExists:            true,
-//			disallowedFlagExpected:    false,
-//		},
-//		{
-//			testCaseName:              "non-updatable apex respects strict updatability of javalib",
-//			apexUpdatable:             false,
-//			javaStrictUpdtabilityLint: true,
-//			lintFileExists:            true,
-//			disallowedFlagExpected:    true,
-//		},
-//		{
-//			testCaseName:              "updatable apex sets strict updatability of javalib to true",
-//			apexUpdatable:             true,
-//			javaStrictUpdtabilityLint: false, // will be set to true by mutator
-//			lintFileExists:            true,
-//			disallowedFlagExpected:    true,
-//		},
-//	}
-//
-//	for _, testCase := range testCases {
-//		bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint)
-//		fixtures := []android.FixturePreparer{}
-//		if testCase.lintFileExists {
-//			fixtures = append(fixtures, fs.AddToFixture())
-//		}
-//
-//		result := testApex(t, bp, fixtures...)
-//		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-//		sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
-//		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi")
-//
-//		if disallowedFlagActual != testCase.disallowedFlagExpected {
-//			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
-//		}
-//	}
-//}
-//
-//func TestUpdatabilityLintSkipLibcore(t *testing.T) {
-//	bp := `
-//		apex {
-//			name: "myapex",
-//			key: "myapex.key",
-//			java_libs: ["myjavalib"],
-//			updatable: true,
-//			min_sdk_version: "29",
-//		}
-//		apex_key {
-//			name: "myapex.key",
-//		}
-//		java_library {
-//			name: "myjavalib",
-//			srcs: ["MyClass.java"],
-//			apex_available: [ "myapex" ],
-//			sdk_version: "current",
-//			min_sdk_version: "29",
-//		}
-//		`
-//
-//	testCases := []struct {
-//		testCaseName           string
-//		moduleDirectory        string
-//		disallowedFlagExpected bool
-//	}{
-//		{
-//			testCaseName:           "lintable module defined outside libcore",
-//			moduleDirectory:        "",
-//			disallowedFlagExpected: true,
-//		},
-//		{
-//			testCaseName:           "lintable module defined in libcore root directory",
-//			moduleDirectory:        "libcore/",
-//			disallowedFlagExpected: false,
-//		},
-//		{
-//			testCaseName:           "lintable module defined in libcore child directory",
-//			moduleDirectory:        "libcore/childdir/",
-//			disallowedFlagExpected: true,
-//		},
-//	}
-//
-//	for _, testCase := range testCases {
-//		lintFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"lint-baseline.xml", "")
-//		bpFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"Android.bp", bp)
-//		result := testApex(t, "", lintFileCreator, bpFileCreator)
-//		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-//		sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
-//		cmdFlags := fmt.Sprintf("--baseline %vlint-baseline.xml --disallowed_issues NewApi", testCase.moduleDirectory)
-//		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, cmdFlags)
-//
-//		if disallowedFlagActual != testCase.disallowedFlagExpected {
-//			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
-//		}
-//	}
-//}
-//
-//// checks transtive deps of an apex coming from bootclasspath_fragment
-//func TestApexStrictUpdtabilityLintBcpFragmentDeps(t *testing.T) {
-//	bp := `
-//		apex {
-//			name: "myapex",
-//			key: "myapex.key",
-//			bootclasspath_fragments: ["mybootclasspathfragment"],
-//			updatable: true,
-//			min_sdk_version: "29",
-//		}
-//		apex_key {
-//			name: "myapex.key",
-//		}
-//		bootclasspath_fragment {
-//			name: "mybootclasspathfragment",
-//			contents: ["myjavalib"],
-//			apex_available: ["myapex"],
-//			hidden_api: {
-//				split_packages: ["*"],
-//			},
-//		}
-//		java_library {
-//			name: "myjavalib",
-//			srcs: ["MyClass.java"],
-//			apex_available: [ "myapex" ],
-//			sdk_version: "current",
-//			min_sdk_version: "29",
-//			compile_dex: true,
-//		}
-//		`
-//	fs := android.MockFS{
-//		"lint-baseline.xml": nil,
-//	}
-//
-//	result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture())
-//	myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
-//	sboxProto := android.RuleBuilderSboxProtoForTests(t, myjavalib.Output("lint.sbox.textproto"))
-//	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") {
-//		t.Errorf("Strict updabality lint missing in myjavalib coming from bootclasspath_fragment mybootclasspath-fragment\nActual lint cmd: %v", *sboxProto.Commands[0].Command)
-//	}
-//}
+func TestApexStrictUpdtabilityLint(t *testing.T) {
+	bpTemplate := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["myjavalib"],
+			updatable: %v,
+			min_sdk_version: "29",
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		java_library {
+			name: "myjavalib",
+			srcs: ["MyClass.java"],
+			apex_available: [ "myapex" ],
+			lint: {
+				strict_updatability_linting: %v,
+				%s
+			},
+			sdk_version: "current",
+			min_sdk_version: "29",
+		}
+		`
+	fs := android.MockFS{
+		"lint-baseline.xml": nil,
+	}
+
+	testCases := []struct {
+		testCaseName              string
+		apexUpdatable             bool
+		javaStrictUpdtabilityLint bool
+		lintFileExists            bool
+		disallowedFlagExpected    bool
+	}{
+		{
+			testCaseName:              "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd",
+			apexUpdatable:             true,
+			javaStrictUpdtabilityLint: true,
+			lintFileExists:            false,
+			disallowedFlagExpected:    false,
+		},
+		{
+			testCaseName:              "non-updatable apex respects strict_updatability of javalib",
+			apexUpdatable:             false,
+			javaStrictUpdtabilityLint: false,
+			lintFileExists:            true,
+			disallowedFlagExpected:    false,
+		},
+		{
+			testCaseName:              "non-updatable apex respects strict updatability of javalib",
+			apexUpdatable:             false,
+			javaStrictUpdtabilityLint: true,
+			lintFileExists:            true,
+			disallowedFlagExpected:    true,
+		},
+		{
+			testCaseName:              "updatable apex sets strict updatability of javalib to true",
+			apexUpdatable:             true,
+			javaStrictUpdtabilityLint: false, // will be set to true by mutator
+			lintFileExists:            true,
+			disallowedFlagExpected:    true,
+		},
+	}
+
+	for _, testCase := range testCases {
+		fixtures := []android.FixturePreparer{}
+		baselineProperty := ""
+		if testCase.lintFileExists {
+			fixtures = append(fixtures, fs.AddToFixture())
+			baselineProperty = "baseline_filename: \"lint-baseline.xml\""
+		}
+		bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint, baselineProperty)
+
+		result := testApex(t, bp, fixtures...)
+		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
+		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi")
+
+		if disallowedFlagActual != testCase.disallowedFlagExpected {
+			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
+		}
+	}
+}
+
+func TestUpdatabilityLintSkipLibcore(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["myjavalib"],
+			updatable: true,
+			min_sdk_version: "29",
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		java_library {
+			name: "myjavalib",
+			srcs: ["MyClass.java"],
+			apex_available: [ "myapex" ],
+			sdk_version: "current",
+			min_sdk_version: "29",
+			lint: {
+				baseline_filename: "lint-baseline.xml",
+			}
+		}
+		`
+
+	testCases := []struct {
+		testCaseName           string
+		moduleDirectory        string
+		disallowedFlagExpected bool
+	}{
+		{
+			testCaseName:           "lintable module defined outside libcore",
+			moduleDirectory:        "",
+			disallowedFlagExpected: true,
+		},
+		{
+			testCaseName:           "lintable module defined in libcore root directory",
+			moduleDirectory:        "libcore/",
+			disallowedFlagExpected: false,
+		},
+		{
+			testCaseName:           "lintable module defined in libcore child directory",
+			moduleDirectory:        "libcore/childdir/",
+			disallowedFlagExpected: true,
+		},
+	}
+
+	for _, testCase := range testCases {
+		lintFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"lint-baseline.xml", "")
+		bpFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"Android.bp", bp)
+		result := testApex(t, "", lintFileCreator, bpFileCreator)
+		myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+		sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
+		cmdFlags := fmt.Sprintf("--baseline %vlint-baseline.xml --disallowed_issues NewApi", testCase.moduleDirectory)
+		disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, cmdFlags)
+
+		if disallowedFlagActual != testCase.disallowedFlagExpected {
+			t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command)
+		}
+	}
+}
+
+// checks transtive deps of an apex coming from bootclasspath_fragment
+func TestApexStrictUpdtabilityLintBcpFragmentDeps(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			bootclasspath_fragments: ["mybootclasspathfragment"],
+			updatable: true,
+			min_sdk_version: "29",
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		bootclasspath_fragment {
+			name: "mybootclasspathfragment",
+			contents: ["myjavalib"],
+			apex_available: ["myapex"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+		java_library {
+			name: "myjavalib",
+			srcs: ["MyClass.java"],
+			apex_available: [ "myapex" ],
+			sdk_version: "current",
+			min_sdk_version: "29",
+			compile_dex: true,
+			lint: {
+				baseline_filename: "lint-baseline.xml",
+			}
+		}
+		`
+	fs := android.MockFS{
+		"lint-baseline.xml": nil,
+	}
+
+	result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture())
+	myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29")
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto"))
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") {
+		t.Errorf("Strict updabality lint missing in myjavalib coming from bootclasspath_fragment mybootclasspath-fragment\nActual lint cmd: %v", *sboxProto.Commands[0].Command)
+	}
+}
+
+func TestApexLintBcpFragmentSdkLibDeps(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			bootclasspath_fragments: ["mybootclasspathfragment"],
+			min_sdk_version: "29",
+		}
+		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.FixtureModifyConfig(func(config android.Config) {
+			config.SetApiLibraries([]string{"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) {
@@ -11235,6 +10713,17 @@
 			name: "server_configurable_flags",
 			srcs: ["server_configurable_flags.cc"],
 		}
+		cc_library {
+			name: "libbase",
+			srcs: ["libbase.cc"],
+			apex_available: [
+				"myapex",
+			],
+		}
+		cc_library {
+			name: "libaconfig_storage_read_api_cc",
+			srcs: ["libaconfig_storage_read_api_cc.cc"],
+		}
 	`)
 
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
@@ -11297,6 +10786,33 @@
 			],
 		}
 
+		rust_library {
+			name: "libaconfig_storage_read_api", // test mock
+			crate_name: "aconfig_storage_read_api",
+			srcs: ["src/lib.rs"],
+			apex_available: [
+				"myapex",
+			],
+		}
+
+		rust_library {
+			name: "liblogger", // test mock
+			crate_name: "logger",
+			srcs: ["src/lib.rs"],
+			apex_available: [
+				"myapex",
+			],
+		}
+
+		rust_library {
+			name: "liblog_rust", // test mock
+			crate_name: "log_rust",
+			srcs: ["src/lib.rs"],
+			apex_available: [
+				"myapex",
+			],
+		}
+
 		rust_ffi_shared {
 			name: "libmy_rust_library",
 			srcs: ["src/lib.rs"],
@@ -11381,14 +10897,14 @@
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
 	s := mod.Rule("apexRule").Args["copy_commands"]
 	copyCmds := regexp.MustCompile(" *&& *").Split(s, -1)
-	if len(copyCmds) != 26 {
-		t.Fatalf("Expected 26 commands, got %d in:\n%s", len(copyCmds), s)
+	if len(copyCmds) != 32 {
+		t.Fatalf("Expected 28 commands, got %d in:\n%s", len(copyCmds), s)
 	}
 
-	ensureMatches(t, copyCmds[22], "^cp -f .*/aconfig_flags.pb .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[23], "^cp -f .*/package.map .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[24], "^cp -f .*/flag.map .*/image.apex/etc$")
-	ensureMatches(t, copyCmds[25], "^cp -f .*/flag.val .*/image.apex/etc$")
+	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$")
 
 	inputs := []string{
 		"my_aconfig_declarations_foo/intermediate.pb",
@@ -11748,6 +11264,20 @@
 			android.FixtureMergeMockFs(map[string][]byte{
 				"system/sepolicy/apex/com.android.foo-file_contexts": nil,
 			}),
+			// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
+			// file creation.
+			java.FixtureConfigureBootJars("platform:foo"),
+			android.FixtureModifyMockFS(func(fs android.MockFS) {
+				fs["platform/Android.bp"] = []byte(`
+		java_library {
+			name: "foo",
+			srcs: ["Test.java"],
+			compile_dex: true,
+		}
+		`)
+				fs["platform/Test.java"] = nil
+			}),
+
 			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 				variables.BuildFlags = map[string]string{
 					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions,
@@ -11776,7 +11306,7 @@
 		variation := func(moduleName string) string {
 			ret := "android_common_com.android.foo"
 			if moduleName == "com.google.android.foo" {
-				ret = "android_common_com.google.android.foo_com.android.foo"
+				ret = "android_common_com.google.android.foo_com.google.android.foo"
 			}
 			return ret
 		}
@@ -11922,3 +11452,289 @@
 		checkHideFromMake(t, ctx, tc.expectedVisibleModuleName, tc.expectedHiddenModuleNames)
 	}
 }
+
+func TestAconfifDeclarationsValidation(t *testing.T) {
+	aconfigDeclarationLibraryString := func(moduleNames []string) (ret string) {
+		for _, moduleName := range moduleNames {
+			ret += fmt.Sprintf(`
+			aconfig_declarations {
+				name: "%[1]s",
+				package: "com.example.package",
+				container: "system",
+				srcs: [
+					"%[1]s.aconfig",
+				],
+			}
+			java_aconfig_library {
+				name: "%[1]s-lib",
+				aconfig_declarations: "%[1]s",
+			}
+			`, moduleName)
+		}
+		return ret
+	}
+
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("foo"),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetApiLibraries([]string{"foo"})
+		}),
+	).RunTestWithBp(t, `
+		java_library {
+			name: "baz-java-lib",
+			static_libs: [
+				"baz-lib",
+			],
+		}
+		filegroup {
+			name: "qux-filegroup",
+			srcs: [
+				":qux-lib{.generated_srcjars}",
+			],
+		}
+		filegroup {
+			name: "qux-another-filegroup",
+			srcs: [
+				":qux-filegroup",
+			],
+		}
+		java_library {
+			name: "quux-java-lib",
+			srcs: [
+				"a.java",
+			],
+			libs: [
+				"quux-lib",
+			],
+		}
+		java_sdk_library {
+			name: "foo",
+			srcs: [
+				":qux-another-filegroup",
+			],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
+			test: {
+				enabled: true,
+			},
+			static_libs: [
+				"bar-lib",
+			],
+			libs: [
+				"baz-java-lib",
+				"quux-java-lib",
+			],
+			aconfig_declarations: [
+				"bar",
+			],
+		}
+	`+aconfigDeclarationLibraryString([]string{"bar", "baz", "qux", "quux"}))
+
+	m := result.ModuleForTests("foo.stubs.source", "android_common")
+	outDir := "out/soong/.intermediates"
+
+	// Arguments passed to aconfig to retrieve the state of the flags defined in the
+	// textproto files
+	aconfigFlagArgs := m.Output("released-flagged-apis-exportable.txt").Args["flags_path"]
+
+	// "bar-lib" is a static_lib of "foo" and is passed to metalava as classpath. Thus the
+	// cache file provided by the associated aconfig_declarations module "bar" should be passed
+	// to aconfig.
+	android.AssertStringDoesContain(t, "cache file of a java_aconfig_library static_lib "+
+		"passed as an input",
+		aconfigFlagArgs, fmt.Sprintf("%s/%s/intermediate.pb", outDir, "bar"))
+
+	// "baz-java-lib", which statically depends on "baz-lib", is a lib of "foo" and is passed
+	// to metalava as classpath. Thus the cache file provided by the associated
+	// aconfig_declarations module "baz" should be passed to aconfig.
+	android.AssertStringDoesContain(t, "cache file of a lib that statically depends on "+
+		"java_aconfig_library passed as an input",
+		aconfigFlagArgs, fmt.Sprintf("%s/%s/intermediate.pb", outDir, "baz"))
+
+	// "qux-lib" is passed to metalava as src via the filegroup, thus the cache file provided by
+	// the associated aconfig_declarations module "qux" should be passed to aconfig.
+	android.AssertStringDoesContain(t, "cache file of srcs java_aconfig_library passed as an "+
+		"input",
+		aconfigFlagArgs, fmt.Sprintf("%s/%s/intermediate.pb", outDir, "qux"))
+
+	// "quux-java-lib" is a lib of "foo" and is passed to metalava as classpath, but does not
+	// statically depend on "quux-lib". Therefore, the cache file provided by the associated
+	// aconfig_declarations module "quux" should not be passed to aconfig.
+	android.AssertStringDoesNotContain(t, "cache file of a lib that does not statically "+
+		"depend on java_aconfig_library not passed as an input",
+		aconfigFlagArgs, fmt.Sprintf("%s/%s/intermediate.pb", outDir, "quux"))
+}
+
+func TestMultiplePrebuiltsWithSameBase(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			prebuilts: ["myetc", "myetc2"],
+			min_sdk_version: "29",
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		prebuilt_etc {
+			name: "myetc",
+			src: "myprebuilt",
+			filename: "myfilename",
+		}
+		prebuilt_etc {
+			name: "myetc2",
+			sub_dir: "mysubdir",
+			src: "myprebuilt",
+			filename: "myfilename",
+		}
+	`, withFiles(android.MockFS{
+		"packages/modules/common/build/allowed_deps.txt": nil,
+	}))
+
+	ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, ab)
+	var builder strings.Builder
+	data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
+	androidMk := builder.String()
+
+	android.AssertStringDoesContain(t, "not found", androidMk, "LOCAL_MODULE := etc_myfilename.myapex")
+	android.AssertStringDoesContain(t, "not found", androidMk, "LOCAL_MODULE := etc_mysubdir_myfilename.myapex")
+}
+
+func TestApexMinSdkVersionOverride(t *testing.T) {
+	checkMinSdkVersion := func(t *testing.T, module android.TestingModule, expectedMinSdkVersion string) {
+		args := module.Rule("apexRule").Args
+		optFlags := args["opt_flags"]
+		if !strings.Contains(optFlags, "--min_sdk_version "+expectedMinSdkVersion) {
+			t.Errorf("%s: Expected min_sdk_version=%s, got: %s", module.Module(), expectedMinSdkVersion, optFlags)
+		}
+	}
+
+	checkHasDep := func(t *testing.T, ctx *android.TestContext, m android.Module, wantDep android.Module) {
+		t.Helper()
+		found := false
+		ctx.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		if !found {
+			t.Errorf("Could not find a dependency from %v to %v\n", m, wantDep)
+		}
+	}
+
+	ctx := testApex(t, `
+		apex {
+			name: "com.android.apex30",
+			min_sdk_version: "30",
+			key: "apex30.key",
+			java_libs: ["javalib"],
+		}
+
+		java_library {
+			name: "javalib",
+			srcs: ["A.java"],
+			apex_available: ["com.android.apex30"],
+			min_sdk_version: "30",
+			sdk_version: "current",
+		}
+
+		override_apex {
+			name: "com.mycompany.android.apex30",
+			base: "com.android.apex30",
+		}
+
+		override_apex {
+			name: "com.mycompany.android.apex31",
+			base: "com.android.apex30",
+			min_sdk_version: "31",
+		}
+
+		apex_key {
+			name: "apex30.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+	`, android.FixtureMergeMockFs(android.MockFS{
+		"system/sepolicy/apex/com.android.apex30-file_contexts": nil,
+	}),
+	)
+
+	baseModule := ctx.ModuleForTests("com.android.apex30", "android_common_com.android.apex30")
+	checkMinSdkVersion(t, baseModule, "30")
+
+	// Override module, but uses same min_sdk_version
+	overridingModuleSameMinSdkVersion := ctx.ModuleForTests("com.android.apex30", "android_common_com.mycompany.android.apex30_com.mycompany.android.apex30")
+	javalibApex30Variant := ctx.ModuleForTests("javalib", "android_common_apex30")
+	checkMinSdkVersion(t, overridingModuleSameMinSdkVersion, "30")
+	checkHasDep(t, ctx, overridingModuleSameMinSdkVersion.Module(), javalibApex30Variant.Module())
+
+	// Override module, uses different min_sdk_version
+	overridingModuleDifferentMinSdkVersion := ctx.ModuleForTests("com.android.apex30", "android_common_com.mycompany.android.apex31_com.mycompany.android.apex31")
+	javalibApex31Variant := ctx.ModuleForTests("javalib", "android_common_apex31")
+	checkMinSdkVersion(t, overridingModuleDifferentMinSdkVersion, "31")
+	checkHasDep(t, ctx, overridingModuleDifferentMinSdkVersion.Module(), javalibApex31Variant.Module())
+}
+
+func TestOverrideApexWithPrebuiltApexPreferred(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		PrepareForTestWithApexBuildComponents,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/foo-file_contexts": nil,
+		}),
+	)
+	res := context.RunTestWithBp(t, `
+		apex {
+			name: "foo",
+			key: "myapex.key",
+			apex_available_name: "com.android.foo",
+			variant_version: "0",
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+		prebuilt_apex {
+			name: "foo",
+			src: "foo.apex",
+			prefer: true,
+		}
+		override_apex {
+			name: "myoverrideapex",
+			base: "foo",
+		}
+	`)
+
+	java.CheckModuleHasDependency(t, res.TestContext, "myoverrideapex", "android_common_myoverrideapex_myoverrideapex", "foo")
+}
+
+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",
+		}
+	`)
+}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 778c20a..919cb01 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -197,6 +197,12 @@
 			updatable: false,
 		}
 
+		override_apex {
+			name: "com.mycompany.android.art",
+			base: "com.android.art",
+			min_sdk_version: "33", // mycompany overrides the min_sdk_version
+		}
+
 		apex_key {
 			name: "com.android.art.key",
 			public_key: "testkey.avbpubkey",
@@ -325,6 +331,26 @@
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
+	t.Run("boot image files from source of override apex", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			commonPreparer,
+
+			// Configure some libraries in the art bootclasspath_fragment that match the source
+			// bootclasspath_fragment's contents property.
+			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+			dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"),
+			addSource("foo", "bar"),
+			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
+		).RunTest(t)
+
+		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.mycompany.android.art_com.mycompany.android.art", []string{
+			"etc/boot-image.prof",
+			"etc/classpaths/bootclasspath.pb",
+			"javalib/bar.jar",
+			"javalib/foo.jar",
+		})
+	})
+
 	t.Run("generate boot image profile even if dexpreopt is disabled", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
 			commonPreparer,
@@ -1340,4 +1366,89 @@
 	android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+nonUpdatableTestStubs)
 }
 
+func TestBootclasspathFragmentProtoContainsMinSdkVersion(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootclasspathFragment,
+		prepareForTestWithMyapex,
+		// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
+		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
+		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
+		// is disabled.
+		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
+
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("foo", "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",
+		}
+
+		java_sdk_library {
+			name: "foo",
+			srcs: ["b.java"],
+			shared_library: false,
+			public: {enabled: true},
+			apex_available: [
+				"myapex",
+			],
+			min_sdk_version: "33",
+		}
+
+		java_sdk_library {
+			name: "bar",
+			srcs: ["b.java"],
+			shared_library: false,
+			public: {enabled: true},
+			apex_available: [
+				"myapex",
+			],
+			min_sdk_version: "34",
+		}
+
+		bootclasspath_fragment {
+			name: "mybootclasspathfragment",
+			contents: [
+				"foo",
+				"bar",
+			],
+			apex_available: [
+				"myapex",
+			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+	`)
+
+	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+	classPathProtoContent := android.ContentFromFileRuleForTests(t, result.TestContext, fragment.Output("bootclasspath.pb.textproto"))
+	// foo
+	ensureContains(t, classPathProtoContent, `jars {
+path: "/apex/myapex/javalib/foo.jar"
+classpath: BOOTCLASSPATH
+min_sdk_version: "33"
+max_sdk_version: ""
+}
+`)
+	// bar
+	ensureContains(t, classPathProtoContent, `jars {
+path: "/apex/myapex/javalib/bar.jar"
+classpath: BOOTCLASSPATH
+min_sdk_version: "34"
+max_sdk_version: ""
+}
+`)
+}
+
 // TODO(b/177892522) - add test for host apex.
diff --git a/apex/builder.go b/apex/builder.go
index 6ad282a..763ce4d 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -277,7 +277,7 @@
 	// VNDK APEX name is determined at runtime, so update "name" in apex_manifest
 	optCommands := []string{}
 	if a.vndkApex {
-		apexName := vndkApexNamePrefix + a.vndkVersion(ctx.DeviceConfig())
+		apexName := vndkApexNamePrefix + a.vndkVersion()
 		optCommands = append(optCommands, "-v name "+apexName)
 	}
 
@@ -293,7 +293,7 @@
 	}
 
 	if android.InList(":vndk", requireNativeLibs) {
-		if _, vndkVersion := a.getImageVariationPair(ctx.DeviceConfig()); vndkVersion != "" {
+		if _, vndkVersion := a.getImageVariationPair(); vndkVersion != "" {
 			optCommands = append(optCommands, "-v vndkVersion "+vndkVersion)
 		}
 	}
@@ -485,7 +485,7 @@
 }
 
 func isVintfFragment(fi apexFile) bool {
-	isVintfFragment, _ := path.Match("etc/vintf/*.xml", fi.path())
+	isVintfFragment, _ := path.Match("etc/vintf/*", fi.path())
 	return isVintfFragment
 }
 
@@ -1043,7 +1043,7 @@
 	if a.vndkApex {
 		overrideName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(vndkApexName)
 		if overridden {
-			return overrideName + ".v" + a.vndkVersion(ctx.DeviceConfig())
+			return overrideName + ".v" + a.vndkVersion()
 		}
 		return ""
 	}
diff --git a/apex/container_test.go b/apex/container_test.go
new file mode 100644
index 0000000..3931174
--- /dev/null
+++ b/apex/container_test.go
@@ -0,0 +1,329 @@
+// 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"),
+	).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: [
+				"foo",
+				"baz",
+			],
+			libs: [
+				"bar",
+			],
+			min_sdk_version: "30",
+		}
+		java_library {
+			name: "foo",
+			srcs:[
+				"A.java",
+			],
+			apex_available: [
+				"myapex",
+			],
+			min_sdk_version: "30",
+		}
+		java_library {
+			name: "bar",
+			srcs:[
+				"A.java",
+			],
+			min_sdk_version: "30",
+		}
+		java_library {
+			name: "baz",
+			srcs:[
+				"A.java",
+			],
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+			min_sdk_version: "30",
+		}
+	`)
+	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:        "foo",
+			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"),
+	).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: [
+				"foo",
+			],
+			libs: [
+				"bar",
+			],
+		}
+		java_library {
+			name: "foo",
+			srcs:[
+				"A.java",
+			],
+			apex_available: [
+				"myapex",
+			],
+		}
+		java_library {
+			name: "bar",
+			srcs:[
+				"A.java",
+			],
+		}
+	`)
+	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:        "foo",
+			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/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 2be9c10..4a20cf0 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -795,3 +795,157 @@
 			}
 		`)
 }
+
+// Source and prebuilt apex provide different set of boot jars
+func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			bootclasspath_fragments: ["apex-fragment"],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: ["myapex"],
+			permitted_packages: ["bar"],
+		}
+
+		bootclasspath_fragment {
+			name: "apex-fragment",
+			contents: ["foo", "bar"],
+			apex_available:[ "myapex" ],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		prebuilt_apex {
+			name: "com.google.android.myapex", // mainline prebuilt selection logic in soong relies on the naming convention com.google.android
+			apex_name: "myapex",
+			source_apex_name: "myapex",
+			src: "myapex.apex",
+			exported_bootclasspath_fragments: ["apex-fragment"],
+		}
+
+		java_import {
+			name: "foo",
+			jars: ["foo.jar"],
+			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "apex-fragment",
+			contents: ["foo"], // Unlike the source fragment, this is missing bar
+			apex_available:[ "myapex" ],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
+		}
+
+		// Another prebuilt apex, but this is not selected during the build.
+		prebuilt_apex {
+			name: "com.google.android.myapex.v2", // mainline prebuilt selection logic in soong relies on the naming convention com.google.android
+			apex_name: "myapex",
+			source_apex_name: "myapex",
+			src: "myapex.apex",
+			exported_bootclasspath_fragments: ["apex-fragment.v2"],
+		}
+
+		java_import {
+			name: "bar",
+			jars: ["bar.jar"],
+			apex_available: ["myapex"],
+			permitted_packages: ["bar"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "apex-fragment.v2",
+			contents: ["bar"], // Unlike the source fragment, this is missing foo
+			apex_available:[ "myapex" ],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
+		}
+
+
+		apex_contributions {
+			name: "my_apex_contributions",
+			api_domain: "myapex",
+			contents: [%v],
+		}
+	`
+	testCases := []struct {
+		desc                     string
+		configuredBootJars       []string
+		apexContributionContents string
+		errorExpected            bool
+	}{
+		{
+			desc:               "Source apex is selected, and APEX_BOOT_JARS is correctly configured for source apex builds",
+			configuredBootJars: []string{"myapex:foo", "myapex:bar"},
+		},
+		{
+			desc:               "Source apex is selected, and APEX_BOOT_JARS is missing bar",
+			configuredBootJars: []string{"myapex:foo"},
+			errorExpected:      true,
+		},
+		{
+			desc:                     "Prebuilt apex is selected, and APEX_BOOT_JARS is correctly configured for prebuilt apex build",
+			configuredBootJars:       []string{"myapex:foo"},
+			apexContributionContents: `"prebuilt_com.google.android.myapex"`,
+		},
+		{
+			desc:                     "Prebuilt apex is selected, and APEX_BOOT_JARS is missing foo",
+			configuredBootJars:       []string{"myapex:bar"},
+			apexContributionContents: `"prebuilt_com.google.android.myapex"`,
+			errorExpected:            true,
+		},
+	}
+
+	for _, tc := range testCases {
+		fixture := android.GroupFixturePreparers(
+			prepareForTestWithPlatformBootclasspath,
+			PrepareForTestWithApexBuildComponents,
+			prepareForTestWithMyapex,
+			java.FixtureConfigureApexBootJars(tc.configuredBootJars...),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ART": "my_apex_contributions",
+				}
+			}),
+		)
+		if tc.errorExpected {
+			fixture = fixture.ExtendWithErrorHandler(
+				android.FixtureExpectsAtLeastOneErrorMatchingPattern(`in contents.*must also be declared in PRODUCT_APEX_BOOT_JARS`),
+			)
+		}
+		fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.apexContributionContents))
+	}
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index ea847e1..65c23d3 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -197,6 +197,7 @@
 	// 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)
 	}
 }
 
@@ -503,9 +504,6 @@
 	inputApex android.Path
 
 	provenanceMetaDataFile android.OutputPath
-
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
 }
 
 type ApexFileProperties struct {
@@ -590,15 +588,6 @@
 	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{}
@@ -838,7 +827,23 @@
 	android.SetProvider(ctx, android.PrebuiltInfoProvider, info)
 }
 
+// Uses an object provided by its deps to validate that the contents of bcpf have been added to the global
+// PRODUCT_APEX_BOOT_JARS
+// This validation will only run on the apex which is active for this product/release_config
+func validateApexClasspathFragments(ctx android.ModuleContext) {
+	ctx.VisitDirectDeps(func(m android.Module) {
+		if info, exists := android.OtherModuleProvider(ctx, m, java.ClasspathFragmentValidationInfoProvider); exists {
+			ctx.ModuleErrorf("%s in contents of %s must also be declared in PRODUCT_APEX_BOOT_JARS", info.UnknownJars, info.ClasspathFragmentModuleName)
+		}
+	})
+}
+
 func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Validate contents of classpath fragments
+	if !p.IsHideFromMake() {
+		validateApexClasspathFragments(ctx)
+	}
+
 	p.apexKeysPath = writeApexKeys(ctx, p)
 	// TODO(jungjw): Check the key validity.
 	p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path()
@@ -882,7 +887,7 @@
 		p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile)
 	}
 
-	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
+	ctx.SetOutputFiles(android.Paths{p.outputApex}, "")
 }
 
 func (p *Prebuilt) ProvenanceMetaDataFile() android.OutputPath {
@@ -998,15 +1003,6 @@
 	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{}
@@ -1064,6 +1060,11 @@
 }
 
 func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Validate contents of classpath fragments
+	if !a.IsHideFromMake() {
+		validateApexClasspathFragments(ctx)
+	}
+
 	a.apexKeysPath = writeApexKeys(ctx, a)
 	a.installFilename = a.InstallFilename()
 	if !strings.HasSuffix(a.installFilename, imageApexSuffix) && !strings.HasSuffix(a.installFilename, imageCapexSuffix) {
@@ -1105,6 +1106,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/vndk.go b/apex/vndk.go
index 377c1c0..781aa3c 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -45,16 +45,12 @@
 	return bundle
 }
 
-func (a *apexBundle) vndkVersion(config android.DeviceConfig) string {
-	vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current")
-	if vndkVersion == "current" {
-		vndkVersion = config.PlatformVndkVersion()
-	}
-	return vndkVersion
+func (a *apexBundle) vndkVersion() string {
+	return proptools.StringDefault(a.vndkProperties.Vndk_version, "current")
 }
 
 type apexVndkProperties struct {
-	// Indicates VNDK version of which this VNDK APEX bundles VNDK libs. Default is Platform VNDK Version.
+	// Indicates VNDK version of which this VNDK APEX bundles VNDK libs.
 	Vndk_version *string
 }
 
@@ -64,7 +60,7 @@
 			mctx.PropertyErrorf("native_bridge_supported", "%q doesn't support native bridge binary.", mctx.ModuleType())
 		}
 
-		vndkVersion := ab.vndkVersion(mctx.DeviceConfig())
+		vndkVersion := ab.vndkVersion()
 		if vndkVersion != "" {
 			apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion)
 			if err != nil {
@@ -73,17 +69,9 @@
 			}
 
 			targets := mctx.MultiTargets()
-			if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) &&
-				vndkVersion != mctx.DeviceConfig().PlatformVndkVersion() {
+			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. This validation is skipped if the VNDK
-				// version matches the platform VNDK version, which can occur when the device
-				// config targets the 'current' VNDK (see `vndkVersion`).
-				ab.Disable()
-			}
-			if proptools.String(ab.vndkProperties.Vndk_version) != "" &&
-				mctx.DeviceConfig().PlatformVndkVersion() != "" &&
-				apiLevel.GreaterThanOrEqualTo(android.ApiLevelOrPanic(mctx, mctx.DeviceConfig().PlatformVndkVersion())) {
+				// level for the primary architecture.
 				ab.Disable()
 			}
 		}
@@ -93,20 +81,11 @@
 func apexVndkDepsMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*cc.Module); ok && cc.IsForVndkApex(mctx, m) {
 		vndkVersion := m.VndkVersion()
-		// For VNDK-Lite device, we gather core-variants of VNDK-Sp libraries, which doesn't have VNDK version defined
-		if vndkVersion == "" {
-			vndkVersion = mctx.DeviceConfig().PlatformVndkVersion()
-		}
 
 		if vndkVersion == "" {
 			return
 		}
-
-		if vndkVersion == mctx.DeviceConfig().PlatformVndkVersion() {
-			vndkVersion = "current"
-		} else {
-			vndkVersion = "v" + vndkVersion
-		}
+		vndkVersion = "v" + vndkVersion
 
 		vndkApexName := "com.android.vndk." + vndkVersion
 
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
deleted file mode 100644
index 894aece..0000000
--- a/apex/vndk_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package apex
-
-import (
-	"testing"
-
-	"github.com/google/blueprint/proptools"
-
-	"android/soong/android"
-)
-
-func TestVndkApexUsesVendorVariant(t *testing.T) {
-	bp := `
-		apex_vndk {
-			name: "com.android.vndk.current",
-			key: "mykey",
-			updatable: false,
-		}
-		apex_key {
-			name: "mykey",
-		}
-		cc_library {
-			name: "libfoo",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			system_shared_libs: [],
-			stl: "none",
-		}
-		` + vndkLibrariesTxtFiles("current")
-
-	ensureFileSrc := func(t *testing.T, files []fileInApex, path, src string) {
-		t.Helper()
-		for _, f := range files {
-			if f.path == path {
-				ensureContains(t, f.src, src)
-				return
-			}
-		}
-		t.Errorf("expected path %q not found", path)
-	}
-
-	t.Run("VNDK lib doesn't have an apex variant", func(t *testing.T) {
-		ctx := testApex(t, bp)
-
-		// libfoo doesn't have apex variants
-		for _, variant := range ctx.ModuleVariantsForTests("libfoo") {
-			ensureNotContains(t, variant, "_myapex")
-		}
-
-		// VNDK APEX doesn't create apex variant
-		files := getFiles(t, ctx, "com.android.vndk.current", "android_common")
-		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared/libfoo.so")
-	})
-
-	t.Run("VNDK APEX gathers only vendor variants even if product variants are available", func(t *testing.T) {
-		ctx := testApex(t, bp)
-
-		files := getFiles(t, ctx, "com.android.vndk.current", "android_common")
-		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared/libfoo.so")
-	})
-
-	t.Run("VNDK APEX supports coverage variants", func(t *testing.T) {
-		ctx := testApex(t, bp,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.GcovCoverage = proptools.BoolPtr(true)
-				variables.Native_coverage = proptools.BoolPtr(true)
-			}),
-		)
-
-		files := getFiles(t, ctx, "com.android.vndk.current", "android_common")
-		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared/libfoo.so")
-
-		files = getFiles(t, ctx, "com.android.vndk.current", "android_common_cov")
-		ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.29_arm_armv7-a-neon_shared_cov/libfoo.so")
-	})
-}
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/Android.bp b/bin/Android.bp
new file mode 100644
index 0000000..4d6d911
--- /dev/null
+++ b/bin/Android.bp
@@ -0,0 +1,26 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+    default_team: "trendy_team_build",
+}
+
+filegroup {
+    name: "run_tool_with_logging_script",
+    visibility: [
+        "//build/soong/tests:__subpackages__",
+    ],
+    srcs: ["run_tool_with_logging"],
+}
diff --git a/bin/afind b/bin/afind
new file mode 100755
index 0000000..080f06a
--- /dev/null
+++ b/bin/afind
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+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/allmod b/bin/allmod
new file mode 100755
index 0000000..f7d25e5
--- /dev/null
+++ b/bin/allmod
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# List all modules for the current device, as cached in all_modules.txt. If any build change is
+# made and it should be reflected in the output, you should run 'refreshmod' first.
+
+cat $ANDROID_PRODUCT_OUT/all_modules.txt 2>/dev/null
+
diff --git a/bin/aninja b/bin/aninja
new file mode 100755
index 0000000..5acb968
--- /dev/null
+++ b/bin/aninja
@@ -0,0 +1,38 @@
+#!/bin/bash -e
+
+# 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.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+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/${host_arch}/bin/ninja -f out/combined-${TARGET_PRODUCT}.ninja "$@"
+
diff --git a/bin/build-flag b/bin/build-flag
new file mode 100755
index 0000000..dc404bc
--- /dev/null
+++ b/bin/build-flag
@@ -0,0 +1,28 @@
+#!/bin/bash -eu
+#
+# Copyright 2017 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+require_top
+
+# Save the current PWD for use in soong_ui
+export ORIGINAL_PWD=${PWD}
+export TOP=$(gettop)
+source ${TOP}/build/soong/scripts/microfactory.bash
+
+soong_build_go build-flag android/soong/cmd/release_config/build_flag
+
+cd ${TOP}
+exec "$(getoutdir)/build-flag" "$@"
diff --git a/bin/build-flag-declarations b/bin/build-flag-declarations
new file mode 100755
index 0000000..222f083
--- /dev/null
+++ b/bin/build-flag-declarations
@@ -0,0 +1,28 @@
+#!/bin/bash -eu
+#
+# Copyright 2017 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+require_top
+
+# Save the current PWD for use in soong_ui
+export ORIGINAL_PWD=${PWD}
+export TOP=$(gettop)
+source ${TOP}/build/soong/scripts/microfactory.bash
+
+soong_build_go build-flag-declarations android/soong/cmd/release_config/build_flag_declarations
+
+cd ${TOP}
+exec "$(getoutdir)/build-flag-declarations" "$@"
diff --git a/bin/cgrep b/bin/cgrep
new file mode 100755
index 0000000..6c9130c
--- /dev/null
+++ b/bin/cgrep
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.c' \
+        -o -name '*.cc' \
+        -o -name '*.cpp' \
+        -o -name '*.h' \
+        -o -name '*.hpp' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/core b/bin/core
new file mode 100755
index 0000000..01dbd13
--- /dev/null
+++ b/bin/core
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# core - send SIGV and pull the core for process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must be called once per boot for core dumps to be
+#       enabled globally.
+
+set -e
+
+PID=$1;
+
+if [ -z "$PID" ]; then
+    printf "Expecting a PID!\n";
+    exit 1
+fi;
+
+CORENAME=core.$PID;
+COREPATH=/cores/$CORENAME;
+SIG=SEGV;
+
+coredump_enable $1;
+
+done=0;
+while [ $(adb shell "[ -d /proc/$PID ] && echo -n yes") ]; do
+    printf "\tSending SIG%s to %d...\n" $SIG $PID;
+    adb shell kill -$SIG $PID;
+    sleep 1;
+done;
+
+adb shell "while [ ! -f $COREPATH ] ; do echo waiting for $COREPATH to be generated; sleep 1; done"
+echo "Done: core is under $COREPATH on device.";
+
+
diff --git a/bin/coredump_enable b/bin/coredump_enable
new file mode 100755
index 0000000..da63a0c
--- /dev/null
+++ b/bin/coredump_enable
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+PID=$1;
+if [ -z "$PID" ]; then
+    printf "Expecting a PID!\n";
+    exit 1
+fi;
+echo "Setting core limit for $PID to infinite...";
+adb shell /system/bin/ulimit -P $PID -c unlimited
+
diff --git a/bin/coredump_setup b/bin/coredump_setup
new file mode 100755
index 0000000..7647659
--- /dev/null
+++ b/bin/coredump_setup
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_setup - enable core dumps globally for any process
+#                  that has the core-file-size limit set correctly
+#
+# NOTE: You must call also coredump_enable for a specific process
+#       if its core-file-size limit is not set already.
+# NOTE: Core dumps are written to ramdisk; they will not survive a reboot!
+
+set -e
+
+echo "Getting root...";
+adb root;
+adb wait-for-device;
+
+echo "Remounting root partition read-write...";
+adb shell mount -w -o remount -t rootfs rootfs;
+sleep 1;
+adb wait-for-device;
+adb shell mkdir -p /cores;
+adb shell mount -t tmpfs tmpfs /cores;
+adb shell chmod 0777 /cores;
+
+echo "Granting SELinux permission to dump in /cores...";
+adb shell restorecon -R /cores;
+
+echo "Set core pattern.";
+adb shell 'echo /cores/core.%p > /proc/sys/kernel/core_pattern';
+
+echo "Done."
+
diff --git a/bin/dirmods b/bin/dirmods
new file mode 100755
index 0000000..52d935a
--- /dev/null
+++ b/bin/dirmods
@@ -0,0 +1,54 @@
+#!/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.
+
+'''
+Lists all modules in the given directory or its decendant directories, as cached
+in module-info.json. If any build change is made, and it should be reflected in
+the output, you should run 'refreshmod' first.
+'''
+
+import sys
+sys.dont_write_bytecode = True
+
+import argparse
+import os
+
+import modinfo
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('path')
+    args = parser.parse_args()
+
+    d = os.path.normpath(args.path)
+    prefix = d + '/'
+
+    module_info = modinfo.ReadModuleInfo()
+
+    results = set()
+    for m in module_info.values():
+        for path in m.get(u'path', []):
+            if path == d or path.startswith(prefix):
+                name = m.get(u'module_name')
+                if name:
+                    results.add(name)
+
+    for name in sorted(results):
+        print(name)
+
+if __name__ == "__main__":
+    main()
diff --git a/bin/get_abs_build_var b/bin/get_abs_build_var
new file mode 100755
index 0000000..8072cf1
--- /dev/null
+++ b/bin/get_abs_build_var
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Get the value of a build variable as an absolute path.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+$TOP/build/soong/soong_ui.bash --dumpvar-mode --abs $1
+
+exit $?
diff --git a/bin/get_build_var b/bin/get_build_var
new file mode 100755
index 0000000..9fdf55f
--- /dev/null
+++ b/bin/get_build_var
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Get the exact value of a build variable.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+$TOP/build/soong/soong_ui.bash --dumpvar-mode $1
+
+exit $?
diff --git a/bin/getlastscreenshot b/bin/getlastscreenshot
new file mode 100755
index 0000000..dfe9a6b
--- /dev/null
+++ b/bin/getlastscreenshot
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+screenshot_path=$(getscreenshotpath)
+screenshot=`adb ${adbOptions} ls ${screenshot_path} | grep Screenshot_[0-9-]*.*\.png | sort -rk 3 | cut -d " " -f 4 | head -n 1`
+if [ "$screenshot" = "" ]; then
+    echo "No screenshots found."
+    exit 1
+fi
+echo "${screenshot}"
+adb ${adbOptions} pull ${screenshot_path}/${screenshot}
+
diff --git a/bin/getprebuilt b/bin/getprebuilt
new file mode 100755
index 0000000..68e65b4
--- /dev/null
+++ b/bin/getprebuilt
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+get_abs_build_var ANDROID_PREBUILTS
+
diff --git a/bin/getscreenshotpath b/bin/getscreenshotpath
new file mode 100755
index 0000000..ff8e327
--- /dev/null
+++ b/bin/getscreenshotpath
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+echo "$(getsdcardpath)/Pictures/Screenshots"
+
diff --git a/bin/getsdcardpath b/bin/getsdcardpath
new file mode 100755
index 0000000..655659a
--- /dev/null
+++ b/bin/getsdcardpath
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb ${adbOptions} shell echo -n \$\{EXTERNAL_STORAGE\}
+
diff --git a/bin/gettargetarch b/bin/gettargetarch
new file mode 100755
index 0000000..e53ce3f
--- /dev/null
+++ b/bin/gettargetarch
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+get_build_var TARGET_ARCH
+
diff --git a/bin/ggrep b/bin/ggrep
new file mode 100755
index 0000000..fce8c84
--- /dev/null
+++ b/bin/ggrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.gradle' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/gogrep b/bin/gogrep
new file mode 100755
index 0000000..0265ccf
--- /dev/null
+++ b/bin/gogrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.go' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/hmm b/bin/hmm
new file mode 100755
index 0000000..161bad6
--- /dev/null
+++ b/bin/hmm
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+cat <<EOF
+
+Run "m help" for help with the build system itself.
+
+Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
+- lunch:      lunch <product_name>-<release_type>-<build_variant>
+              Selects <product_name> as the product to build, and <build_variant> as the variant to
+              build, and stores those selections in the environment to be read by subsequent
+              invocations of 'm' etc.
+- tapas:      tapas [<App1> <App2> ...] [arm|x86|arm64|x86_64] [eng|userdebug|user]
+              Sets up the build environment for building unbundled apps (APKs).
+- banchan:    banchan <module1> [<module2> ...] \\
+                      [arm|x86|arm64|riscv64|x86_64|arm64_only|x86_64only] [eng|userdebug|user]
+              Sets up the build environment for building unbundled modules (APEXes).
+- croot:      Changes directory to the top of the tree, or a subdirectory thereof.
+- m:          Makes from the top of the tree.
+- mm:         Builds and installs all of the modules in the current directory, and their
+              dependencies.
+- mmm:        Builds and installs all of the modules in the supplied directories, and their
+              dependencies.
+              To limit the modules being built use the syntax: mmm dir/:target1,target2.
+- mma:        Same as 'mm'
+- mmma:       Same as 'mmm'
+- provision:  Flash device with all required partitions. Options will be passed on to fastboot.
+- cgrep:      Greps on all local C/C++ files.
+- ggrep:      Greps on all local Gradle files.
+- gogrep:     Greps on all local Go files.
+- jgrep:      Greps on all local Java files.
+- jsongrep:   Greps on all local Json files.
+- ktgrep:     Greps on all local Kotlin files.
+- resgrep:    Greps on all local res/*.xml files.
+- mangrep:    Greps on all local AndroidManifest.xml files.
+- mgrep:      Greps on all local Makefiles and *.bp files.
+- owngrep:    Greps on all local OWNERS files.
+- rsgrep:     Greps on all local Rust files.
+- sepgrep:    Greps on all local sepolicy files.
+- sgrep:      Greps on all local source files.
+- tomlgrep:   Greps on all local Toml files.
+- pygrep:     Greps on all local Python files.
+- godir:      Go to the directory containing a file.
+- allmod:     List all modules.
+- gomod:      Go to the directory containing a module.
+- pathmod:    Get the directory containing a module.
+- outmod:     Gets the location of a module's installed outputs with a certain extension.
+- dirmods:    Gets the modules defined in a given directory.
+- installmod: Adb installs a module's built APK.
+- refreshmod: Refresh list of modules for allmod/gomod/pathmod/outmod/installmod.
+- syswrite:   Remount partitions (e.g. system.img) as writable, rebooting if necessary.
+
+Environment options:
+- SANITIZE_HOST: Set to 'address' to use ASAN for all host modules.
+- ANDROID_QUIET_BUILD: set to 'true' to display only the essential messages.
+
+Look at the source to view more functions. The complete list is:
+EOF
+    T=$(gettop)
+    A=""
+    for i in `cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; do
+      A="$A $i"
+    done
+    echo $A
+
diff --git a/bin/installmod b/bin/installmod
new file mode 100755
index 0000000..1d0d836
--- /dev/null
+++ b/bin/installmod
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# adb install a module's apk, as cached in module-info.json. If any build change
+# is made, and it should be reflected in the output, you should run 'refreshmod' first.
+# Usage: installmod [adb install arguments] <module>
+# For example: installmod -r Dialer -> adb install -r /path/to/Dialer.apk
+
+if [[ $# -eq 0 ]]; then
+    echo "usage: installmod [adb install arguments] <module>" >&2
+    echo "" >&2
+    echo "Only flags to be passed after the \"install\" in adb install are supported," >&2
+    echo "with the exception of -s. If -s is passed it will be placed before the \"install\"." >&2
+    echo "-s must be the first flag passed if it exists." >&2
+    return 1
+fi
+
+local _path
+_path=$(outmod ${@:$#:1})
+if [ $? -ne 0 ]; then
+    return 1
+fi
+
+_path=$(echo "$_path" | grep -E \\.apk$ | head -n 1)
+if [ -z "$_path" ]; then
+    echo "Module '$1' does not produce a file ending with .apk (try 'refreshmod' if there have been build changes?)" >&2
+    return 1
+fi
+local serial_device=""
+if [[ "$1" == "-s" ]]; then
+    if [[ $# -le 2 ]]; then
+        echo "-s requires an argument" >&2
+        return 1
+    fi
+    serial_device="-s $2"
+    shift 2
+fi
+local length=$(( $# - 1 ))
+echo adb $serial_device install ${@:1:$length} $_path
+adb $serial_device install ${@:1:$length} $_path
+
diff --git a/bin/is64bit b/bin/is64bit
new file mode 100755
index 0000000..35bbcc3
--- /dev/null
+++ b/bin/is64bit
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Read the ELF header from /proc/$PID/exe to determine if the process is
+# 64-bit.
+
+set -e
+
+local PID="$1"
+if [ "$PID" ] ; then
+    if [[ "$(adb shell cat /proc/$PID/exe | xxd -l 1 -s 4 -p)" -eq "02" ]] ; then
+        echo "64"
+    else
+        echo ""
+    fi
+else
+    echo ""
+fi
+
diff --git a/bin/isviewserverstarted b/bin/isviewserverstarted
new file mode 100755
index 0000000..c7c82af
--- /dev/null
+++ b/bin/isviewserverstarted
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell service call window 3
+
diff --git a/bin/jgrep b/bin/jgrep
new file mode 100755
index 0000000..afe70db
--- /dev/null
+++ b/bin/jgrep
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.java' \
+        -o -name '*.kt' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/jsongrep b/bin/jsongrep
new file mode 100755
index 0000000..6e14d0c
--- /dev/null
+++ b/bin/jsongrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.json' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/key_back b/bin/key_back
new file mode 100755
index 0000000..2de8d07
--- /dev/null
+++ b/bin/key_back
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell input keyevent 4
+
diff --git a/bin/key_home b/bin/key_home
new file mode 100755
index 0000000..653a5f9
--- /dev/null
+++ b/bin/key_home
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell input keyevent 3
+
diff --git a/bin/key_menu b/bin/key_menu
new file mode 100755
index 0000000..29b2bc6
--- /dev/null
+++ b/bin/key_menu
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell input keyevent 82
+
diff --git a/bin/ktgrep b/bin/ktgrep
new file mode 120000
index 0000000..9b51491
--- /dev/null
+++ b/bin/ktgrep
@@ -0,0 +1 @@
+jgrep
\ No newline at end of file
diff --git a/bin/list_products b/bin/list_products
new file mode 100755
index 0000000..cd8dd5c
--- /dev/null
+++ b/bin/list_products
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+# In almost call cases including get_build_var, TARGET_RELEASE is required,
+# but the list of available products is not dependent on the release config
+# (but note that the list of available release configs is dependent on the
+# product). So for list_products, we'll just set it to trunk_staging, which
+# exists everwhere, so we don't trigger the unspecified TARGET_RELEASE error.
+
+# We also unset TARGET_BUILD_APPS, so it doesn't interfere.
+
+TARGET_RELEASE=trunk_staging TARGET_BUILD_APPS= $TOP/build/soong/soong_ui.bash --dumpvar-mode all_named_products | sed 's/ /\n/g'
+
+exit $?
diff --git a/bin/list_releases b/bin/list_releases
new file mode 100755
index 0000000..ca18110
--- /dev/null
+++ b/bin/list_releases
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [[ $# -eq 1 ]]; then
+    # Override anything that's already set
+    export TARGET_PRODUCT=$1
+elif [[ -z $TARGET_PRODUCT ]]; then
+    echo "Usage: list_releases [PRODUCT]" 1>&2
+    echo "" 1>&2
+    echo "If the optional PRODUCT parameter is bit provided, then TARGET_PRODUCT" 1>&2
+    echo "must have been set, for example by lunch or banchan." 1>&2
+    exit 1
+fi
+
+
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+# In almost call cases including get_build_var, TARGET_RELEASE is required,
+# but the list of available products is not dependent on the release config
+# (but note that the list of available release configs is dependent on the
+# product). So for list_products, we'll just set it to trunk_staging, which
+# exists everwhere, so we don't trigger the unspecified TARGET_RELEASE error.
+
+# We also unset TARGET_BUILD_APPS, so it doesn't interfere.
+
+TARGET_RELEASE=trunk_staging TARGET_BUILD_APPS= $TOP/build/soong/soong_ui.bash --dumpvar-mode ALL_RELEASE_CONFIGS_FOR_PRODUCT | sed 's/ /\n/g'
+
+exit $?
diff --git a/bin/list_variants b/bin/list_variants
new file mode 100755
index 0000000..ac89e6a
--- /dev/null
+++ b/bin/list_variants
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+echo user
+echo userdebug
+echo eng
+
diff --git a/bin/m b/bin/m
new file mode 100755
index 0000000..edcfce5
--- /dev/null
+++ b/bin/m
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --all-modules --dir="$(pwd)" "$@"
+
+exit $?
diff --git a/bin/mangrep b/bin/mangrep
new file mode 100755
index 0000000..a343000
--- /dev/null
+++ b/bin/mangrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name 'AndroidManifest.xml' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/mgrep b/bin/mgrep
new file mode 100755
index 0000000..793730d
--- /dev/null
+++ b/bin/mgrep
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name 'Makefile' \
+        -o -name 'Makefile.*' \
+        -o -name '*.make' \
+        -o -name '*.mak' \
+        -o -name '*.mk' \
+        -o -name '*.bp' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/mm b/bin/mm
new file mode 100755
index 0000000..6461b1e
--- /dev/null
+++ b/bin/mm
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-a-dir-no-deps --dir="$(pwd)" "$@"
+
+exit $?
diff --git a/bin/mma b/bin/mma
new file mode 100755
index 0000000..6f1c934
--- /dev/null
+++ b/bin/mma
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-a-dir --dir="$(pwd)" "$@"
+
+exit $?
diff --git a/bin/mmm b/bin/mmm
new file mode 100755
index 0000000..ab3a632
--- /dev/null
+++ b/bin/mmm
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-dirs-no-deps --dir="$(pwd)" "$@"
+
+exit $?
diff --git a/bin/mmma b/bin/mmma
new file mode 100755
index 0000000..d9190e5
--- /dev/null
+++ b/bin/mmma
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+_wrap_build "$TOP/build/soong/soong_ui.bash" --build-mode --modules-in-dirs --dir="$(pwd)" "$@"
+
+exit $?
diff --git a/bin/modinfo.py b/bin/modinfo.py
new file mode 100644
index 0000000..015129f
--- /dev/null
+++ b/bin/modinfo.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import os
+import pathlib
+import sys
+
+
+def OpenModuleInfoFile():
+    product_out = os.getenv("ANDROID_PRODUCT_OUT")
+    if not product_out:
+        if os.getenv("QUIET_VERIFYMODINFO") != "true":
+            sys.stderr.write("No ANDROID_PRODUCT_OUT. Try running 'lunch' first.\n")
+        sys.exit(1)
+    try:
+        return open(pathlib.Path(product_out) / "module-info.json")
+    except (FileNotFoundError, PermissionError):
+        if os.getenv("QUIET_VERIFYMODINFO") != "true":
+            sys.stderr.write("Could not find module-info.json. Please run 'refreshmod' first.\n")
+        sys.exit(1)
+
+
+def ReadModuleInfo():
+    with OpenModuleInfoFile() as f:
+        return json.load(f)
+
+def GetModule(modules, module_name):
+    if module_name not in modules:
+        sys.stderr.write(f"Could not find module '{module_name}' (try 'refreshmod' if there have been build changes?)\n")
+        sys.exit(1)
+    return modules[module_name]
+
diff --git a/bin/outmod b/bin/outmod
new file mode 100755
index 0000000..022ff36
--- /dev/null
+++ b/bin/outmod
@@ -0,0 +1,42 @@
+#!/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.
+
+'''
+Lists the output files of a specific module in the android tree, as cached in
+module-info.json. If any build change is made, and it should be reflected in the
+output, you should run 'refreshmod' first.
+'''
+
+import sys
+sys.dont_write_bytecode = True
+
+import argparse
+import os
+
+import modinfo
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('module')
+    args = parser.parse_args()
+
+    for output in modinfo.GetModule(modinfo.ReadModuleInfo(), args.module)['installed']:
+        print(os.path.join(os.getenv("ANDROID_BUILD_TOP", ""), output))
+
+if __name__ == "__main__":
+    main()
+
diff --git a/bin/overrideflags b/bin/overrideflags
new file mode 100755
index 0000000..e16537b
--- /dev/null
+++ b/bin/overrideflags
@@ -0,0 +1,100 @@
+#!/bin/bash -e
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+function print_help() {
+    echo -e "overrideflags is used to set default value for local build."
+    echo -e "\nOptions:"
+    echo -e "\t--release-config  \tPath to release configuration directory. Required"
+    echo -e "\t--no-edit         \tIf present, skip editing flag value file."
+    echo -e "\t-h/--help         \tShow this help."
+}
+
+function main() {
+    while (($# > 0)); do
+        case $1 in
+        --release-config)
+            if [[ $# -le 1 ]]; then
+                echo "--release-config requires a path"
+                return 1
+            fi
+            local release_config_dir="$2"
+            shift 2
+            ;;
+        --no-edit)
+            local no_edit="true"
+            shift 1
+            ;;
+        -h|--help)
+            print_help
+            return
+            ;;
+        *)
+            echo "$1 is unrecognized"
+            print_help
+            return 1
+            ;;
+        esac
+    done
+
+
+
+    case $(uname -s) in
+        Darwin)
+            local host_arch=darwin-x86
+            ;;
+        Linux)
+            local host_arch=linux-x86
+            ;;
+        *)
+            >&2 echo Unknown host $(uname -s)
+            return
+            ;;
+    esac
+
+    if [[ -z "${release_config_dir}" ]]; then
+        echo "Please provide release configuration path by --release-config"
+        exit 1
+    elif [ ! -d "${release_config_dir}" ]; then
+        echo "${release_config_dir} is an invalid directory"
+        exit 1
+    fi
+    local T="$(gettop)"
+    local aconfig_dir="${T}"/build/make/tools/aconfig/
+    local overrideflag_py="${aconfig_dir}"/overrideflags/overrideflags.py
+    local overridefile="${release_config_dir}/aconfig/override_values.textproto"
+
+    # Edit override file
+    if [[ -z "${no_edit}" ]]; then
+        editor="${EDITOR:-$(which vim)}"
+
+        eval "${editor} ${overridefile}"
+        if [ $? -ne 0 ]; then
+            echo "Fail to set override values"
+            return 1
+        fi
+    fi
+
+    ${T}/prebuilts/build-tools/${host_arch}/bin/py3-cmd -u "${overrideflag_py}" \
+        --overrides "${overridefile}" \
+        --out "${release_config_dir}/aconfig"
+}
+
+
+main "$@"
diff --git a/bin/owngrep b/bin/owngrep
new file mode 100755
index 0000000..26ce6e8
--- /dev/null
+++ b/bin/owngrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name 'OWNERS' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/pathmod b/bin/pathmod
new file mode 100755
index 0000000..70cf958
--- /dev/null
+++ b/bin/pathmod
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+'''
+Get the path of a specific module in the android tree, as cached in module-info.json.
+If any build change is made, and it should be reflected in the output, you should run
+'refreshmod' first.  Note: This is the inverse of dirmods.
+'''
+
+import sys
+sys.dont_write_bytecode = True
+
+import argparse
+import os
+
+import modinfo
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('module')
+    args = parser.parse_args()
+
+    path = modinfo.GetModule(modinfo.ReadModuleInfo(), args.module)['path'][0]
+    print(os.path.join(os.getenv("ANDROID_BUILD_TOP", ""), path))
+
+if __name__ == "__main__":
+    main()
diff --git a/bin/pygrep b/bin/pygrep
new file mode 100755
index 0000000..e072289
--- /dev/null
+++ b/bin/pygrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.py' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/qpid b/bin/qpid
new file mode 100755
index 0000000..b47cb6b
--- /dev/null
+++ b/bin/qpid
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# adb install a module's apk, as cached in module-info.json. If any build change
+# is made, and it should be reflected in the output, you should run 'refreshmod' first.
+# Usage: installmod [adb install arguments] <module>
+# For example: installmod -r Dialer -> adb install -r /path/to/Dialer.apk
+
+function _impl() {
+    local prepend=''
+    local append=''
+    if [ "$1" = "--exact" ]; then
+        prepend=' '
+        append='$'
+        shift
+    elif [ "$1" = "--help" -o "$1" = "-h" ]; then
+        echo "usage: qpid [[--exact] <process name|pid>"
+        return 255
+    fi
+
+    local EXE="$1"
+    if [ "$EXE" ] ; then
+        _impl | \grep "$prepend$EXE$append"
+    else
+        adb shell ps \
+            | tr -d '\r' \
+            | sed -e 1d -e 's/^[^ ]* *\([0-9]*\).* \([^ ]*\)$/\1 \2/'
+    fi
+}
+
+_impl "$@"
diff --git a/bin/rcgrep b/bin/rcgrep
new file mode 100755
index 0000000..ff93e51
--- /dev/null
+++ b/bin/rcgrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.rc' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/refreshmod b/bin/refreshmod
new file mode 100755
index 0000000..f511846
--- /dev/null
+++ b/bin/refreshmod
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Update module-info.json in out.
+
+# Common script utilities
+source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../../make/shell_utils.sh
+
+require_top
+
+if [ ! "$ANDROID_PRODUCT_OUT" ]; then
+    echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2
+    return 1
+fi
+
+echo "Refreshing modules (building module-info.json)" >&2
+
+_wrap_build $TOP/build/soong/soong_ui.bash --build-mode --all-modules --dir="$(pwd)" module-info
diff --git a/bin/resgrep b/bin/resgrep
new file mode 100755
index 0000000..600091f
--- /dev/null
+++ b/bin/resgrep
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+for dir in `find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -name res -type d`; do
+    find $dir -type f -name '*\.xml' -exec grep --color -n "$@" {} +
+done
diff --git a/bin/rsgrep b/bin/rsgrep
new file mode 100755
index 0000000..8c24151
--- /dev/null
+++ b/bin/rsgrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.rs' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/run_tool_with_logging b/bin/run_tool_with_logging
new file mode 100755
index 0000000..2b2c8d8
--- /dev/null
+++ b/bin/run_tool_with_logging
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Run commands in a subshell for us to handle forced terminations with a trap
+# handler.
+(
+tool_tag="$1"
+shift
+tool_binary="$1"
+shift
+
+# If the logger is not configured, run the original command and return.
+if [[ -z "${ANDROID_TOOL_LOGGER}" ]]; then
+  "${tool_binary}" "${@}"
+   exit $?
+fi
+
+# Otherwise, run the original command and call the logger when done.
+start_time=$(date +%s.%N)
+logger=${ANDROID_TOOL_LOGGER}
+
+# Install a trap to call the logger even when the process terminates abnormally.
+# The logger is run in the background and its output suppressed to avoid
+# interference with the user flow.
+trap '
+exit_code=$?;
+# Remove the trap to prevent duplicate log.
+trap - EXIT;
+"${logger}" \
+  --tool_tag="${tool_tag}" \
+  --start_timestamp="${start_time}" \
+  --end_timestamp="$(date +%s.%N)" \
+  --tool_args="$*" \
+  --exit_code="${exit_code}" \
+  ${ANDROID_TOOL_LOGGER_EXTRA_ARGS} \
+  > /dev/null 2>&1 &
+exit ${exit_code}
+' SIGINT SIGTERM SIGQUIT EXIT
+
+# Run the original command.
+"${tool_binary}" "${@}"
+)
diff --git a/bin/sepgrep b/bin/sepgrep
new file mode 100755
index 0000000..0e0d1ba
--- /dev/null
+++ b/bin/sepgrep
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -name sepolicy -type d \
+    -exec grep --color -n -r --exclude-dir=\.git "$@" {} +
+exit $?
diff --git a/bin/sgrep b/bin/sgrep
new file mode 100755
index 0000000..f186553
--- /dev/null
+++ b/bin/sgrep
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.c' \
+        -o -name '*.cc' \
+        -o -name '*.cpp' \
+        -o -name '*.h' \
+        -o -name '*.hpp' \
+        -o -name '*.S' \
+        -o -name '*.java' \
+        -o -name '*.kt' \
+        -o -name '*.xml' \
+        -o -name '*.sh' \
+        -o -name '*.mk' \
+        -o -name '*.bp' \
+        -o -name '*.aidl' \
+        -o -name '*.vts' \
+        -o -name '*.proto' \
+        -o -name '*.rs' \
+        -o -name '*.go' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/soongdbg b/bin/soongdbg
index bfdbbde..a73bdf9 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",
+                        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/bin/startviewserver b/bin/startviewserver
new file mode 100755
index 0000000..4d612b8
--- /dev/null
+++ b/bin/startviewserver
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+port=4939
+if [ $# -gt 0 ]; then
+    port=$1
+fi
+adb shell service call window 1 i32 $port
+
diff --git a/bin/stopviewserver b/bin/stopviewserver
new file mode 100755
index 0000000..a734e4b
--- /dev/null
+++ b/bin/stopviewserver
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# coredump_enable - enable core dumps for the specified process
+# $1 = PID of process (e.g., $(pid mediaserver))
+#
+# NOTE: coredump_setup must have been called as well for a core
+#       dump to actually be generated.
+
+set -e
+
+adb shell service call window 2
+
diff --git a/bin/systemstack b/bin/systemstack
new file mode 100755
index 0000000..b259133
--- /dev/null
+++ b/bin/systemstack
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# systemstack - dump the current stack trace of all threads in the system process
+# to the usual ANR traces file
+
+stacks system_server
+
diff --git a/bin/syswrite b/bin/syswrite
new file mode 100755
index 0000000..46201e3
--- /dev/null
+++ b/bin/syswrite
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# syswrite - disable verity, reboot if needed, and remount image
+# Easy way to make system.img/etc writable
+
+adb wait-for-device && adb root && adb wait-for-device || exit 1
+if [[ $(adb disable-verity | grep -i "reboot") ]]; then
+  echo "rebooting"
+  adb reboot && adb wait-for-device && adb root && adb wait-for-device || exit 1
+fi
+adb remount || exit 1
diff --git a/bin/tomlgrep b/bin/tomlgrep
new file mode 100755
index 0000000..636ef22
--- /dev/null
+++ b/bin/tomlgrep
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.toml' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bin/treegrep b/bin/treegrep
new file mode 100755
index 0000000..b83d419
--- /dev/null
+++ b/bin/treegrep
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( \
+        -name '*.c' \
+        -o -name '*.cc' \
+        -o -name '*.cpp' \
+        -o -name '*.h' \
+        -o -name '*.hpp' \
+        -o -name '*.S' \
+        -o -name '*.java' \
+        -o -name '*.kt' \
+        -o -name '*.xml' \
+    \) -exec grep --color -n "$@" {} +
+exit $?
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 38fbd88..09262e5 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -65,8 +65,6 @@
 type BpfModule interface {
 	android.Module
 
-	OutputFiles(tag string) (android.Paths, error)
-
 	// Returns the sub install directory if the bpf module is included by apex.
 	SubDir() string
 }
@@ -106,6 +104,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)
 }
@@ -127,13 +133,10 @@
 }
 
 func (bpf *bpf) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	if proptools.Bool(bpf.properties.Vendor) {
-		return []string{"vendor"}
-	}
 	return nil
 }
 
-func (bpf *bpf) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+func (bpf *bpf) SetImageVariation(ctx android.BaseModuleContext, variation string) {
 	bpf.properties.VendorInternal = variation == "vendor"
 }
 
@@ -213,6 +216,8 @@
 	}
 
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
+
+	ctx.SetOutputFiles(bpf.objs, "")
 }
 
 func (bpf *bpf) AndroidMk() android.AndroidMkData {
@@ -255,23 +260,10 @@
 	}
 }
 
-// Implements OutputFileFileProducer interface so that the obj output can be used in the data property
-// of other modules.
-func (bpf *bpf) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return bpf.objs, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (bpf *bpf) SubDir() string {
 	return bpf.properties.Sub_dir
 }
 
-var _ android.OutputFileProducer = (*bpf)(nil)
-
 func BpfFactory() android.Module {
 	module := &bpf{}
 
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 672e852..f487d3c 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -19,6 +19,7 @@
 import (
 	"bytes"
 	"fmt"
+	"os"
 	"reflect"
 	"strings"
 	"testing"
@@ -45,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...)
 	}
@@ -166,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
 	}
@@ -2215,3 +2216,9 @@
 		})
 	}
 }
+
+func TestMain(m *testing.M) {
+	// Skip checking Android.mk path with cleaning "ANDROID_BUILD_TOP"
+	os.Setenv("ANDROID_BUILD_TOP", "")
+	os.Exit(m.Run())
+}
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/cc/Android.bp b/cc/Android.bp
index 9e4b763..3bbcaa9 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -17,7 +17,6 @@
         "soong-fuzz",
         "soong-genrule",
         "soong-multitree",
-        "soong-snapshot",
         "soong-testing",
         "soong-tradefed",
     ],
@@ -45,15 +44,14 @@
         "sabi.go",
         "sdk.go",
         "snapshot_prebuilt.go",
-        "snapshot_utils.go",
         "stl.go",
         "strip.go",
         "tidy.go",
         "util.go",
-        "vendor_snapshot.go",
         "vndk.go",
         "vndk_prebuilt.go",
 
+        "cmake_snapshot.go",
         "cmakelists.go",
         "compdb.go",
         "compiler.go",
@@ -94,11 +92,12 @@
         "afdo_test.go",
         "binary_test.go",
         "cc_test.go",
+        "cc_test_only_property_test.go",
+        "cmake_snapshot_test.go",
         "compiler_test.go",
         "gen_test.go",
         "genrule_test.go",
         "library_headers_test.go",
-        "library_stub_test.go",
         "library_test.go",
         "lto_test.go",
         "ndk_test.go",
@@ -111,7 +110,13 @@
         "test_data_test.go",
         "tidy_test.go",
         "vendor_public_library_test.go",
-        "vendor_snapshot_test.go",
+    ],
+    embedSrcs: [
+        "cmake_ext_add_aidl_library.txt",
+        "cmake_ext_append_flags.txt",
+        "cmake_main.txt",
+        "cmake_module_aidl.txt",
+        "cmake_module_cc.txt",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/cc/afdo.go b/cc/afdo.go
index 00b2245..6921edf 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -176,6 +176,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 20673e8..4134653 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -15,8 +15,6 @@
 package cc
 
 import (
-	"github.com/google/blueprint/proptools"
-
 	"fmt"
 	"io"
 	"path/filepath"
@@ -90,7 +88,7 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				if len(c.Properties.Logtags) > 0 {
-					entries.AddStrings("LOCAL_LOGTAGS_FILES", c.Properties.Logtags...)
+					entries.AddStrings("LOCAL_SOONG_LOGTAGS_FILES", c.logtagsPaths.Strings()...)
 				}
 				// Note: Pass the exact value of AndroidMkSystemSharedLibs to the Make
 				// world, even if it is an empty list. In the Make world,
@@ -106,31 +104,18 @@
 					entries.AddStrings("LOCAL_RUNTIME_LIBRARIES", c.Properties.AndroidMkRuntimeLibs...)
 				}
 				entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
-				if c.InVendorOrProduct() {
-					if c.IsVndk() && !c.static() {
-						entries.SetString("LOCAL_SOONG_VNDK_VERSION", c.VndkVersion())
-						// VNDK libraries available to vendor are not installed because
-						// they are packaged in VNDK APEX and installed by APEX packages (apex/apex.go)
-						if !c.IsVndkExt() {
-							entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-						}
-					}
-				}
 				if c.InVendor() {
 					entries.SetBool("LOCAL_IN_VENDOR", true)
 				} else if c.InProduct() {
 					entries.SetBool("LOCAL_IN_PRODUCT", true)
 				}
-				if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
-					// Make the SDK variant uninstallable so that there are not two rules to install
-					// to the same location.
-					entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+				if c.Properties.SdkAndPlatformVariantVisibleToMake {
 					// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
 					// dependencies to the .sdk suffix when building a module that uses the SDK.
 					entries.SetString("SOONG_SDK_VARIANT_MODULES",
 						"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
 				}
-				android.SetAconfigFileMkEntries(c.AndroidModuleBase(), entries, c.mergedAconfigFiles)
+				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", c.IsSkipInstall())
 			},
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -266,15 +251,6 @@
 		if library.coverageOutputFile.Valid() {
 			entries.SetString("LOCAL_PREBUILT_COVERAGE_ARCHIVE", library.coverageOutputFile.String())
 		}
-
-		if library.useCoreVariant {
-			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-			entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
-			entries.SetBool("LOCAL_VNDK_DEPEND_ON_CORE_VARIANT", true)
-		}
-		if library.checkSameCoreVariant {
-			entries.SetBool("LOCAL_CHECK_SAME_VNDK_VARIANTS", true)
-		}
 	})
 
 	if library.shared() && !library.buildStubs() {
@@ -375,9 +351,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())
@@ -448,6 +421,7 @@
 		if c.parsedCoverageXmlPath.String() != "" {
 			entries.SetString("SOONG_NDK_API_XML", "$(SOONG_NDK_API_XML) "+c.parsedCoverageXmlPath.String())
 		}
+		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) // Stubs should not be installed
 	})
 }
 
@@ -477,79 +451,6 @@
 	})
 }
 
-func (c *snapshotLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	// Each vendor snapshot is exported to androidMk only when BOARD_VNDK_VERSION != current
-	// and the version of the prebuilt is same as BOARD_VNDK_VERSION.
-	if c.shared() {
-		entries.Class = "SHARED_LIBRARIES"
-	} else if c.static() {
-		entries.Class = "STATIC_LIBRARIES"
-	} else if c.header() {
-		entries.Class = "HEADER_LIBRARIES"
-	}
-
-	entries.SubName = ""
-
-	if c.IsSanitizerEnabled(cfi) {
-		entries.SubName += ".cfi"
-	} else if c.IsSanitizerEnabled(Hwasan) {
-		entries.SubName += ".hwasan"
-	}
-
-	entries.SubName += c.baseProperties.Androidmk_suffix
-
-	entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		c.libraryDecorator.androidMkWriteExportedFlags(entries)
-
-		if c.shared() || c.static() {
-			src := c.path.String()
-			// For static libraries which aren't installed, directly use Src to extract filename.
-			// This is safe: generated snapshot modules have a real path as Src, not a module
-			if c.static() {
-				src = proptools.String(c.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)
-			if c.shared() {
-				entries.SetString("LOCAL_MODULE_PATH", path)
-			}
-			if c.tocFile.Valid() {
-				entries.SetString("LOCAL_SOONG_TOC", c.tocFile.String())
-			}
-
-			if c.shared() && len(c.Properties.Overrides) > 0 {
-				entries.SetString("LOCAL_OVERRIDES_MODULES", strings.Join(makeOverrideModuleNames(ctx, c.Properties.Overrides), " "))
-			}
-		}
-
-		if !c.shared() { // static or header
-			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-		}
-	})
-}
-
-func (c *snapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "EXECUTABLES"
-	entries.SubName = c.baseProperties.Androidmk_suffix
-}
-
-func (c *snapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "STATIC_LIBRARIES"
-	entries.SubName = c.baseProperties.Androidmk_suffix
-
-	entries.ExtraFooters = append(entries.ExtraFooters,
-		func(w io.Writer, name, prefix, moduleDir string) {
-			out := entries.OutputFile.Path()
-			varname := fmt.Sprintf("SOONG_%sOBJECT_%s%s", prefix, name, entries.SubName)
-
-			fmt.Fprintf(w, "\n%s := %s\n", varname, out.String())
-			fmt.Fprintln(w, ".KATI_READONLY: "+varname)
-		})
-}
-
 func (c *ndkPrebuiltStlLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	entries.Class = "SHARED_LIBRARIES"
 }
@@ -572,14 +473,14 @@
 	ctx.subAndroidMk(entries, p.libraryDecorator)
 	if p.shared() {
 		ctx.subAndroidMk(entries, &p.prebuiltLinker)
-		androidMkWriteAllowUndefinedSymbols(p.baseLinker, entries)
+		androidMkWritePrebuiltOptions(p.baseLinker, entries)
 	}
 }
 
 func (p *prebuiltBinaryLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	ctx.subAndroidMk(entries, p.binaryDecorator)
 	ctx.subAndroidMk(entries, &p.prebuiltLinker)
-	androidMkWriteAllowUndefinedSymbols(p.baseLinker, entries)
+	androidMkWritePrebuiltOptions(p.baseLinker, entries)
 }
 
 func (a *apiLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
@@ -610,11 +511,17 @@
 	})
 }
 
-func androidMkWriteAllowUndefinedSymbols(linker *baseLinker, entries *android.AndroidMkEntries) {
+func androidMkWritePrebuiltOptions(linker *baseLinker, entries *android.AndroidMkEntries) {
 	allow := linker.Properties.Allow_undefined_symbols
 	if allow != nil {
 		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 			entries.SetBool("LOCAL_ALLOW_UNDEFINED_SYMBOLS", *allow)
 		})
 	}
+	ignore := linker.Properties.Ignore_max_page_size
+	if ignore != nil {
+		entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.SetBool("LOCAL_IGNORE_MAX_PAGE_SIZE", *ignore)
+		})
+	}
 }
diff --git a/cc/binary.go b/cc/binary.go
index 7aa8e20..2ac9a45 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
@@ -425,6 +426,10 @@
 	validations = append(validations, objs.tidyDepFiles...)
 	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 
+	if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil {
+		deps.StaticLibs = append(deps.StaticLibs, generatedLib)
+	}
+
 	// Register link action.
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
 		deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
@@ -446,7 +451,7 @@
 }
 
 func (binary *binaryDecorator) strippedAllOutputFilePath() android.Path {
-	panic("Not implemented.")
+	return nil
 }
 
 func (binary *binaryDecorator) setSymlinkList(ctx ModuleContext) {
diff --git a/cc/builder.go b/cc/builder.go
index e4d5be2..367bda3 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -19,6 +19,7 @@
 // functions.
 
 import (
+	"fmt"
 	"path/filepath"
 	"runtime"
 	"strconv"
@@ -45,18 +46,18 @@
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
-			Command:     "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -MD -MF ${out}.d -o $out $in",
+			Command:     "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -MD -MF ${out}.d -o $out $in$postCmd",
 			CommandDeps: []string{"$ccCmd"},
 		},
-		"ccCmd", "cFlags")
+		"ccCmd", "cFlags", "postCmd")
 
 	// Rule to invoke gcc with given command and flags, but no dependencies.
 	ccNoDeps = pctx.AndroidStaticRule("ccNoDeps",
 		blueprint.RuleParams{
-			Command:     "$relPwd $ccCmd -c $cFlags -o $out $in",
+			Command:     "$relPwd $ccCmd -c $cFlags -o $out $in$postCmd",
 			CommandDeps: []string{"$ccCmd"},
 		},
-		"ccCmd", "cFlags")
+		"ccCmd", "cFlags", "postCmd")
 
 	// Rules to invoke ld to link binaries. Uses a .rsp file to list dependencies, as there may
 	// be many.
@@ -330,6 +331,15 @@
 			CommandDeps: []string{"$cxxExtractor", "$kytheVnames"},
 		},
 		"cFlags")
+
+	// Function pointer for producting staticlibs from rlibs. Corresponds to
+	// rust.TransformRlibstoStaticlib(), initialized in soong-rust (rust/builder.go init())
+	//
+	// This is required since soong-rust depends on soong-cc, so soong-cc cannot depend on soong-rust
+	// without resulting in a circular dependency. Setting this function pointer in soong-rust allows
+	// soong-cc to call into this particular function.
+	TransformRlibstoStaticlib (func(ctx android.ModuleContext, mainSrc android.Path, deps []RustRlibDep,
+		outputFile android.WritablePath) android.Path) = nil
 )
 
 func PwdPrefix() string {
@@ -375,13 +385,14 @@
 	localCppFlags        string
 	localLdFlags         string
 
-	libFlags      string // Flags to add to the linker directly after specifying libraries to link.
-	extraLibFlags string // Flags to add to the linker last.
-	tidyFlags     string // Flags that apply to clang-tidy
-	sAbiFlags     string // Flags that apply to header-abi-dumps
-	aidlFlags     string // Flags that apply to aidl source files
-	rsFlags       string // Flags that apply to renderscript source files
-	toolchain     config.Toolchain
+	noOverrideFlags string // Flags appended at the end so they are not overridden.
+	libFlags        string // Flags to add to the linker directly after specifying libraries to link.
+	extraLibFlags   string // Flags to add to the linker last.
+	tidyFlags       string // Flags that apply to clang-tidy
+	sAbiFlags       string // Flags that apply to header-abi-dumps
+	aidlFlags       string // Flags that apply to aidl source files
+	rsFlags         string // Flags that apply to renderscript source files
+	toolchain       config.Toolchain
 
 	// True if these extra features are enabled.
 	tidy          bool
@@ -389,6 +400,7 @@
 	gcovCoverage  bool
 	sAbiDump      bool
 	emitXrefs     bool
+	clangVerify   bool
 
 	assemblerWithCpp bool // True if .s files should be processed with the c preprocessor.
 
@@ -473,7 +485,7 @@
 		coverageFiles = make(android.Paths, 0, len(srcFiles))
 	}
 	var kytheFiles android.Paths
-	if flags.emitXrefs {
+	if flags.emitXrefs && ctx.Module() == ctx.PrimaryModule() {
 		kytheFiles = make(android.Paths, 0, len(srcFiles))
 	}
 
@@ -485,7 +497,8 @@
 		flags.localCommonFlags + " " +
 		flags.localToolingCFlags + " " +
 		flags.localConlyFlags + " " +
-		flags.systemIncludeFlags
+		flags.systemIncludeFlags + " " +
+		flags.noOverrideFlags
 
 	cflags := flags.globalCommonFlags + " " +
 		flags.globalCFlags + " " +
@@ -493,7 +506,8 @@
 		flags.localCommonFlags + " " +
 		flags.localCFlags + " " +
 		flags.localConlyFlags + " " +
-		flags.systemIncludeFlags
+		flags.systemIncludeFlags + " " +
+		flags.noOverrideFlags
 
 	toolingCppflags := flags.globalCommonFlags + " " +
 		flags.globalToolingCFlags + " " +
@@ -501,7 +515,8 @@
 		flags.localCommonFlags + " " +
 		flags.localToolingCFlags + " " +
 		flags.localToolingCppFlags + " " +
-		flags.systemIncludeFlags
+		flags.systemIncludeFlags + " " +
+		flags.noOverrideFlags
 
 	cppflags := flags.globalCommonFlags + " " +
 		flags.globalCFlags + " " +
@@ -509,7 +524,8 @@
 		flags.localCommonFlags + " " +
 		flags.localCFlags + " " +
 		flags.localCppFlags + " " +
-		flags.systemIncludeFlags
+		flags.systemIncludeFlags + " " +
+		flags.noOverrideFlags
 
 	asflags := flags.globalCommonFlags + " " +
 		flags.globalAsFlags + " " +
@@ -522,26 +538,6 @@
 		sAbiDumpFiles = make(android.Paths, 0, len(srcFiles))
 	}
 
-	cflags += " ${config.NoOverrideGlobalCflags}"
-	toolingCflags += " ${config.NoOverrideGlobalCflags}"
-	cppflags += " ${config.NoOverrideGlobalCflags}"
-	toolingCppflags += " ${config.NoOverrideGlobalCflags}"
-
-	if flags.toolchain.Is64Bit() {
-		cflags += " ${config.NoOverride64GlobalCflags}"
-		toolingCflags += " ${config.NoOverride64GlobalCflags}"
-		cppflags += " ${config.NoOverride64GlobalCflags}"
-		toolingCppflags += " ${config.NoOverride64GlobalCflags}"
-	}
-
-	modulePath := ctx.ModuleDir()
-	if android.IsThirdPartyPath(modulePath) {
-		cflags += " ${config.NoOverrideExternalGlobalCflags}"
-		toolingCflags += " ${config.NoOverrideExternalGlobalCflags}"
-		cppflags += " ${config.NoOverrideExternalGlobalCflags}"
-		toolingCppflags += " ${config.NoOverrideExternalGlobalCflags}"
-	}
-
 	// Multiple source files have build rules usually share the same cFlags or tidyFlags.
 	// Define only one version in this module and share it in multiple build rules.
 	// To simplify the code, the shared variables are all named as $flags<nnn>.
@@ -596,6 +592,7 @@
 		var moduleToolingFlags string
 
 		var ccCmd string
+		var postCmd string
 		tidy := flags.tidy
 		coverage := flags.gcovCoverage
 		dump := flags.sAbiDump
@@ -623,6 +620,10 @@
 			ccCmd = "clang++"
 			moduleFlags = cppflags
 			moduleToolingFlags = toolingCppflags
+		case ".rs":
+			// A source provider (e.g. rust_bindgen) may provide both rs and c files.
+			// Ignore the rs files.
+			continue
 		case ".h", ".hpp":
 			ctx.PropertyErrorf("srcs", "Header file %s is not supported, instead use export_include_dirs or local_include_dirs.", srcFile)
 			continue
@@ -636,6 +637,10 @@
 
 		ccCmd = "${config.ClangBin}/" + ccCmd
 
+		if flags.clangVerify {
+			postCmd = " && touch " + objFile.String()
+		}
+
 		var implicitOutputs android.WritablePaths
 		if coverage {
 			gcnoFile := android.ObjPathWithExt(ctx, subdir, srcFile, "gcno")
@@ -652,13 +657,14 @@
 			Implicits:       cFlagsDeps,
 			OrderOnly:       pathDeps,
 			Args: map[string]string{
-				"cFlags": shareFlags("cFlags", moduleFlags),
-				"ccCmd":  ccCmd, // short and not shared
+				"cFlags":  shareFlags("cFlags", moduleFlags),
+				"ccCmd":   ccCmd, // short and not shared
+				"postCmd": postCmd,
 			},
 		})
 
 		// Register post-process build statements (such as for tidy or kythe).
-		if emitXref {
+		if emitXref && ctx.Module() == ctx.PrimaryModule() {
 			kytheFile := android.ObjPathWithExt(ctx, subdir, srcFile, "kzip")
 			ctx.Build(pctx, android.BuildParams{
 				Rule:        kytheExtract,
@@ -789,6 +795,51 @@
 	}
 }
 
+// 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 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 rust deps are defined in static_libs for %s",
+			ctx.ModuleName()))
+
+	} else if len(rlibDeps) == 0 {
+		return nil
+	}
+
+	output := android.PathForModuleOut(ctx, "generated_rust_staticlib", "lib"+ctx.ModuleName()+"_rust_staticlib.a")
+	stemFile := output.ReplaceExtension(ctx, "rs")
+	crateNames := []string{}
+
+	// Collect crate names
+	for _, lib := range rlibDeps {
+		// Exclude libstd so this can support no_std builds.
+		if lib.CrateName != "libstd" {
+			crateNames = append(crateNames, lib.CrateName)
+		}
+	}
+
+	// Deduplicate any crateNames just to be safe
+	crateNames = android.FirstUniqueStrings(crateNames)
+
+	// Write the source file
+	android.WriteFileRule(ctx, stemFile, genRustStaticlibSrcFile(crateNames))
+
+	return TransformRlibstoStaticlib(ctx, stemFile, rlibDeps, output)
+}
+
+func genRustStaticlibSrcFile(crateNames []string) string {
+	lines := []string{
+		"// @Soong generated Source",
+		"#![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))
+	}
+	return strings.Join(lines, "\n")
+}
+
 // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
 // and shared libraries, to a shared library (.so) or dynamic executable
 func transformObjToDynamicBinary(ctx android.ModuleContext,
@@ -869,14 +920,15 @@
 // Generate a rule to combine .dump sAbi dump files from multiple source files
 // into a single .ldump sAbi dump file
 func transformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Paths, soFile android.Path,
-	baseName, exportedHeaderFlags string, symbolFile android.OptionalPath,
-	excludedSymbolVersions, excludedSymbolTags []string,
-	api string) android.OptionalPath {
+	baseName string, exportedIncludeDirs []string, symbolFile android.OptionalPath,
+	excludedSymbolVersions, excludedSymbolTags, includedSymbolTags []string,
+	api string, isLlndk bool) android.Path {
 
 	outputFile := android.PathForModuleOut(ctx, baseName+".lsdump")
 
 	implicits := android.Paths{soFile}
 	symbolFilterStr := "-so " + soFile.String()
+	exportedHeaderFlags := android.JoinWithPrefix(exportedIncludeDirs, "-I")
 
 	if symbolFile.Valid() {
 		implicits = append(implicits, symbolFile.Path())
@@ -888,6 +940,12 @@
 	for _, tag := range excludedSymbolTags {
 		symbolFilterStr += " --exclude-symbol-tag " + tag
 	}
+	for _, tag := range includedSymbolTags {
+		symbolFilterStr += " --include-symbol-tag " + tag
+	}
+	if isLlndk {
+		symbolFilterStr += " --symbol-tag-policy MatchTagOnly"
+	}
 	apiLevelsJson := android.GetApiLevelsJson(ctx)
 	implicits = append(implicits, apiLevelsJson)
 	symbolFilterStr += " --api-map " + apiLevelsJson.String()
@@ -901,13 +959,7 @@
 	}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_ABI_LINKER") {
 		rule = sAbiLinkRE
-		rbeImplicits := implicits.Strings()
-		for _, p := range strings.Split(exportedHeaderFlags, " ") {
-			if len(p) > 2 {
-				// Exclude the -I prefix.
-				rbeImplicits = append(rbeImplicits, p[2:])
-			}
-		}
+		rbeImplicits := append(implicits.Strings(), exportedIncludeDirs...)
 		args["implicitInputs"] = strings.Join(rbeImplicits, ",")
 	}
 	ctx.Build(pctx, android.BuildParams{
@@ -918,7 +970,7 @@
 		Implicits:   implicits,
 		Args:        args,
 	})
-	return android.OptionalPathForPath(outputFile)
+	return outputFile
 }
 
 func transformAbiDumpToAbiDiff(ctx android.ModuleContext, inputDump, referenceDump android.Path,
diff --git a/cc/cc.go b/cc/cc.go
index 0fa3457..d307be6 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -35,7 +35,6 @@
 	"android/soong/fuzz"
 	"android/soong/genrule"
 	"android/soong/multitree"
-	"android/soong/snapshot"
 )
 
 func init() {
@@ -50,9 +49,8 @@
 
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("sdk", sdkMutator).Parallel()
-		ctx.BottomUp("vndk", VndkMutator).Parallel()
+		ctx.BottomUp("llndk", llndkMutator).Parallel()
 		ctx.BottomUp("link", LinkageMutator).Parallel()
-		ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel()
 		ctx.BottomUp("version", versionMutator).Parallel()
 		ctx.BottomUp("begin", BeginMutator).Parallel()
 	})
@@ -137,6 +135,23 @@
 	ExcludeLibsForApex []string
 	// List of libs that need to be excluded for non-APEX variant
 	ExcludeLibsForNonApex []string
+
+	// LLNDK headers for the ABI checker to check LLNDK implementation library.
+	// An LLNDK implementation is the core variant. LLNDK header libs are reexported by the vendor variant.
+	// The core variant cannot depend on the vendor variant because of the order of CreateVariations.
+	// Instead, the LLNDK implementation depends on the LLNDK header libs.
+	LlndkHeaderLibs []string
+}
+
+// A struct which to collect flags for rlib dependencies
+type RustRlibDep struct {
+	LibPath   android.Path // path to the rlib
+	LinkDirs  []string     // flags required for dependency (e.g. -L flags)
+	CrateName string       // crateNames associated with rlibDeps
+}
+
+func EqRustRlibDeps(a RustRlibDep, b RustRlibDep) bool {
+	return a.LibPath == b.LibPath
 }
 
 // PathDeps is a struct containing file paths to dependencies of a module.
@@ -151,6 +166,8 @@
 	SharedLibsDeps, EarlySharedLibsDeps, LateSharedLibsDeps android.Paths
 	// Paths to .a files
 	StaticLibs, LateStaticLibs, WholeStaticLibs android.Paths
+	// Paths and crateNames for RustStaticLib dependencies
+	RustRlibDeps []RustRlibDep
 
 	// Transitive static library dependencies of static libraries for use in ordering.
 	TranstiveStaticLibrariesForOrdering *android.DepSet[android.Path]
@@ -179,6 +196,7 @@
 	ReexportedFlags            []string
 	ReexportedGeneratedHeaders android.Paths
 	ReexportedDeps             android.Paths
+	ReexportedRustRlibDeps     []RustRlibDep
 
 	// Paths to crt*.o files
 	CrtBegin, CrtEnd android.Paths
@@ -192,6 +210,10 @@
 
 	// Paths to direct srcs and transitive include dirs from direct aidl_library deps
 	AidlLibraryInfos []aidl_library.AidlLibraryInfo
+
+	// LLNDK headers for the ABI checker to check LLNDK implementation library.
+	LlndkIncludeDirs       android.Paths
+	LlndkSystemIncludeDirs android.Paths
 }
 
 // LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
@@ -215,7 +237,8 @@
 	// Local flags (which individual modules are responsible for). These may override global flags.
 	Local LocalOrGlobalFlags
 	// Global flags (which build system or toolchain is responsible for).
-	Global LocalOrGlobalFlags
+	Global          LocalOrGlobalFlags
+	NoOverrideFlags []string // Flags applied to the end of list of flags so they are not overridden
 
 	aidlFlags     []string // Flags that apply to aidl source files
 	rsFlags       []string // Flags that apply to renderscript source files
@@ -234,6 +257,7 @@
 	GcovCoverage  bool // True if coverage files should be generated.
 	SAbiDump      bool // True if header abi dumps should be generated.
 	EmitXrefs     bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
+	ClangVerify   bool // If true, append cflags "-Xclang -verify" and append "&& touch $out" to the clang command line.
 
 	// The instruction set required for clang ("arm" or "thumb").
 	RequiredInstructionSet string
@@ -259,6 +283,11 @@
 	// Deprecated. true is the default, false is invalid.
 	Clang *bool `android:"arch_variant"`
 
+	// Aggresively trade performance for smaller binary size.
+	// This should only be used for on-device binaries that are rarely executed and not
+	// performance critical.
+	Optimize_for_size *bool `android:"arch_variant"`
+
 	// The API level that this module is built against. The APIs of this API level will be
 	// visible at build time, but use of any APIs newer than min_sdk_version will render the
 	// module unloadable on older devices.  In the future it will be possible to weakly-link new
@@ -287,6 +316,7 @@
 
 	AndroidMkSharedLibs       []string `blueprint:"mutated"`
 	AndroidMkStaticLibs       []string `blueprint:"mutated"`
+	AndroidMkRlibs            []string `blueprint:"mutated"`
 	AndroidMkRuntimeLibs      []string `blueprint:"mutated"`
 	AndroidMkWholeStaticLibs  []string `blueprint:"mutated"`
 	AndroidMkHeaderLibs       []string `blueprint:"mutated"`
@@ -309,7 +339,7 @@
 
 	// *.logtags files, to combine together in order to generate the /system/etc/event-log-tags
 	// file
-	Logtags []string
+	Logtags []string `android:"path"`
 
 	// Make this module available when building for ramdisk.
 	// On device without a dedicated recovery partition, the module is only
@@ -329,6 +359,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"`
@@ -342,14 +374,9 @@
 	// for building binaries that are started before APEXes are activated.
 	Bootstrap *bool
 
-	// Even if DeviceConfig().VndkUseCoreVariant() is set, this module must use vendor variant.
-	// see soong/cc/config/vndk.go
-	MustUseVendorVariant bool `blueprint:"mutated"`
-
-	// Used by vendor snapshot to record dependencies from snapshot modules.
-	SnapshotSharedLibs  []string `blueprint:"mutated"`
-	SnapshotStaticLibs  []string `blueprint:"mutated"`
-	SnapshotRuntimeLibs []string `blueprint:"mutated"`
+	// Allows this module to be included in CMake release snapshots to be built outside of Android
+	// build system and source tree.
+	Cmake_snapshot_supported *bool
 
 	Installable *bool `android:"arch_variant"`
 
@@ -363,20 +390,6 @@
 	// variant to have a ".sdk" suffix.
 	SdkAndPlatformVariantVisibleToMake bool `blueprint:"mutated"`
 
-	// Normally Soong uses the directory structure to decide which modules
-	// should be included (framework) or excluded (non-framework) from the
-	// different snapshots (vendor, recovery, etc.), but this property
-	// allows a partner to exclude a module normally thought of as a
-	// framework module from the vendor snapshot.
-	Exclude_from_vendor_snapshot *bool
-
-	// Normally Soong uses the directory structure to decide which modules
-	// should be included (framework) or excluded (non-framework) from the
-	// different snapshots (vendor, recovery, etc.), but this property
-	// allows a partner to exclude a module normally thought of as a
-	// framework module from the recovery snapshot.
-	Exclude_from_recovery_snapshot *bool
-
 	// List of APEXes that this module has private access to for testing purpose. The module
 	// can depend on libraries that are not exported by the APEXes and use private symbols
 	// from the exported libraries.
@@ -466,23 +479,6 @@
 	// IsLLNDK is set to true for the vendor variant of a cc_library module that has LLNDK stubs.
 	IsLLNDK bool `blueprint:"mutated"`
 
-	// IsVNDKUsingCoreVariant is true for VNDK modules if the global VndkUseCoreVariant option is
-	// set and the module is not listed in VndkMustUseVendorVariantList.
-	IsVNDKUsingCoreVariant bool `blueprint:"mutated"`
-
-	// IsVNDKCore is set if a VNDK module does not set the vndk.support_system_process property.
-	IsVNDKCore bool `blueprint:"mutated"`
-
-	// IsVNDKSP is set if a VNDK module sets the vndk.support_system_process property.
-	IsVNDKSP bool `blueprint:"mutated"`
-
-	// IsVNDKPrivate is set if a VNDK module sets the vndk.private property or an LLNDK
-	// module sets the llndk.private property.
-	IsVNDKPrivate bool `blueprint:"mutated"`
-
-	// IsVNDKProduct is set if a VNDK module sets the product_available property.
-	IsVNDKProduct bool `blueprint:"mutated"`
-
 	// IsVendorPublicLibrary is set for the core and product variants of a library that has
 	// vendor_public_library stubs.
 	IsVendorPublicLibrary bool `blueprint:"mutated"`
@@ -509,12 +505,7 @@
 	useVndk() bool
 	isNdk(config android.Config) bool
 	IsLlndk() bool
-	IsLlndkPublic() bool
 	isImplementationForLLNDKPublic() bool
-	IsVndkPrivate() bool
-	isVndk() bool
-	isVndkSp() bool
-	IsVndkExt() bool
 	IsVendorPublicLibrary() bool
 	inProduct() bool
 	inVendor() bool
@@ -524,7 +515,6 @@
 	InVendorOrProduct() bool
 	selectedStl() string
 	baseModuleName() string
-	getVndkExtendsModuleName() string
 	isAfdoCompile(ctx ModuleContext) bool
 	isOrderfileCompile() bool
 	isCfi() bool
@@ -535,13 +525,13 @@
 	apexVariationName() string
 	apexSdkVersion() android.ApiLevel
 	bootstrap() bool
-	mustUseVendorVariant() bool
 	nativeCoverage() bool
 	directlyInAnyApex() bool
 	isPreventInstall() bool
 	isCfiAssemblySupportEnabled() bool
 	getSharedFlags() *SharedFlags
 	notInPlatform() bool
+	optimizeForSize() bool
 }
 
 type SharedFlags struct {
@@ -596,6 +586,7 @@
 	compilerDeps(ctx DepsContext, deps Deps) Deps
 	compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags
 	compilerProps() []interface{}
+	baseCompilerProps() BaseCompilerProperties
 
 	appendCflags([]string)
 	appendAsflags([]string)
@@ -610,6 +601,7 @@
 	linkerDeps(ctx DepsContext, deps Deps) Deps
 	linkerFlags(ctx ModuleContext, flags Flags) Flags
 	linkerProps() []interface{}
+	baseLinkerProps() BaseLinkerProperties
 	useClangLld(actx ModuleContext) bool
 
 	link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path
@@ -662,6 +654,7 @@
 	headerLibraryDependency = iota
 	sharedLibraryDependency
 	staticLibraryDependency
+	rlibLibraryDependency
 )
 
 func (k libraryDependencyKind) String() string {
@@ -672,6 +665,8 @@
 		return "sharedLibraryDependency"
 	case staticLibraryDependency:
 		return "staticLibraryDependency"
+	case rlibLibraryDependency:
+		return "rlibLibraryDependency"
 	default:
 		panic(fmt.Errorf("unknown libraryDependencyKind %d", k))
 	}
@@ -803,11 +798,11 @@
 	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"}
 	aidlLibraryTag        = dependencyTag{name: "aidl_library"}
+	llndkHeaderLibTag     = dependencyTag{name: "llndk_header_lib"}
 )
 
 func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
@@ -829,11 +824,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",
@@ -853,6 +843,7 @@
 
 	VendorProperties VendorProperties
 	Properties       BaseProperties
+	sourceProperties android.SourceProperties
 
 	// initialize before calling Init
 	hod        android.HostOrDeviceSupported
@@ -878,7 +869,6 @@
 	coverage  *coverage
 	fuzzer    *fuzzer
 	sabi      *sabi
-	vndkdep   *vndkdep
 	lto       *lto
 	afdo      *afdo
 	orderfile *orderfile
@@ -913,8 +903,9 @@
 
 	hideApexVariantFromMake bool
 
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
+	logtagsPaths android.Paths
+
+	WholeRustStaticlib bool
 }
 
 func (c *Module) AddJSONData(d *map[string]interface{}) {
@@ -953,14 +944,7 @@
 		"InstallInVendorRamdisk": c.InstallInVendorRamdisk(),
 		"InstallInRecovery":      c.InstallInRecovery(),
 		"InstallInRoot":          c.InstallInRoot(),
-		"IsVndk":                 c.IsVndk(),
-		"IsVndkExt":              c.IsVndkExt(),
-		"IsVndkPrivate":          c.IsVndkPrivate(),
-		"IsVndkSp":               c.IsVndkSp(),
 		"IsLlndk":                c.IsLlndk(),
-		"IsLlndkPublic":          c.IsLlndkPublic(),
-		"IsSnapshotLibrary":      c.IsSnapshotLibrary(),
-		"IsSnapshotPrebuilt":     c.IsSnapshotPrebuilt(),
 		"IsVendorPublicLibrary":  c.IsVendorPublicLibrary(),
 		"ApexSdkVersion":         c.apexSdkVersion,
 		"TestFor":                c.TestFor(),
@@ -972,6 +956,7 @@
 		"WinMsgSrcs":             hasWinMsg,
 		"YaccSrsc":               hasYacc,
 		"OnlyCSrcs":              !(hasAidl || hasLex || hasProto || hasRenderscript || hasSysprop || hasWinMsg || hasYacc),
+		"OptimizeForSize":        c.OptimizeForSize(),
 	}
 }
 
@@ -987,8 +972,8 @@
 	return c.Properties.HideFromMake
 }
 
-func (c *Module) RequiredModuleNames() []string {
-	required := android.CopyOf(c.ModuleBase.RequiredModuleNames())
+func (c *Module) RequiredModuleNames(ctx android.ConfigAndErrorContext) []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)
@@ -1057,6 +1042,10 @@
 	return false
 }
 
+func (c *Module) OptimizeForSize() bool {
+	return Bool(c.Properties.Optimize_for_size)
+}
+
 func (c *Module) SdkVersion() string {
 	return String(c.Properties.Sdk_version)
 }
@@ -1117,6 +1106,14 @@
 	return false
 }
 
+func (c *Module) CrateName() string {
+	panic(fmt.Errorf("CrateName called on non-Rust module: %q", c.BaseModuleName()))
+}
+
+func (c *Module) ExportedCrateLinkDirs() []string {
+	panic(fmt.Errorf("ExportedCrateLinkDirs called on non-Rust module: %q", c.BaseModuleName()))
+}
+
 func (c *Module) IsFuzzModule() bool {
 	if _, ok := c.compiler.(*fuzzBinary); ok {
 		return true
@@ -1184,6 +1181,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
 }
@@ -1257,9 +1264,6 @@
 	if c.sabi != nil {
 		c.AddProperties(c.sabi.props()...)
 	}
-	if c.vndkdep != nil {
-		c.AddProperties(c.vndkdep.props()...)
-	}
 	if c.lto != nil {
 		c.AddProperties(c.lto.props()...)
 	}
@@ -1272,6 +1276,10 @@
 	for _, feature := range c.features {
 		c.AddProperties(feature.props()...)
 	}
+	// Allow test-only on libraries that are not cc_test_library
+	if c.library != nil && !c.testLibrary() {
+		c.AddProperties(&c.sourceProperties)
+	}
 
 	android.InitAndroidArchModule(c, c.hod, c.multilib)
 	android.InitApexModule(c)
@@ -1310,10 +1318,6 @@
 	return c.VendorProperties.IsLLNDK
 }
 
-func (c *Module) IsLlndkPublic() bool {
-	return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsVNDKPrivate
-}
-
 func (m *Module) NeedsLlndkVariants() bool {
 	lib := moduleLibraryInterface(m)
 	return lib != nil && (lib.hasLLNDKStubs() || lib.hasLLNDKHeaders())
@@ -1360,31 +1364,6 @@
 		!Bool(library.Properties.Llndk.Private)
 }
 
-// Returns true for LLNDK-private, VNDK-SP-private, and VNDK-core-private.
-func (c *Module) IsVndkPrivate() bool {
-	// Check if VNDK-core-private or VNDK-SP-private
-	if c.IsVndk() {
-		return Bool(c.vndkdep.Properties.Vndk.Private)
-	}
-
-	// Check if LLNDK-private
-	if library, ok := c.library.(*libraryDecorator); ok && c.IsLlndk() {
-		return Bool(library.Properties.Llndk.Private)
-	}
-
-	return false
-}
-
-// IsVndk() returns true if this module has a vndk variant.
-// Note that IsVndk() returns true for all variants of vndk-enabled libraries. Not only vendor variant,
-// but also platform and product variants of vndk-enabled libraries return true for IsVndk().
-func (c *Module) IsVndk() bool {
-	if vndkdep := c.vndkdep; vndkdep != nil {
-		return vndkdep.isVndk()
-	}
-	return false
-}
-
 func (c *Module) isAfdoCompile(ctx ModuleContext) bool {
 	if afdo := c.afdo; afdo != nil {
 		return afdo.isAfdoCompile(ctx)
@@ -1400,17 +1379,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 {
@@ -1420,35 +1393,10 @@
 	return false
 }
 
-func (c *Module) IsVndkSp() bool {
-	if vndkdep := c.vndkdep; vndkdep != nil {
-		return vndkdep.isVndkSp()
-	}
-	return false
-}
-
-func (c *Module) IsVndkExt() bool {
-	if vndkdep := c.vndkdep; vndkdep != nil {
-		return vndkdep.isVndkExt()
-	}
-	return false
-}
-
 func (c *Module) SubName() string {
 	return c.Properties.SubName
 }
 
-func (c *Module) MustUseVendorVariant() bool {
-	return c.IsVndkSp() || c.Properties.MustUseVendorVariant
-}
-
-func (c *Module) getVndkExtendsModuleName() string {
-	if vndkdep := c.vndkdep; vndkdep != nil {
-		return vndkdep.getVndkExtendsModuleName()
-	}
-	return ""
-}
-
 func (c *Module) IsStubs() bool {
 	if lib := c.library; lib != nil {
 		return lib.buildStubs()
@@ -1512,14 +1460,6 @@
 	return false
 }
 
-func (c *Module) ExcludeFromVendorSnapshot() bool {
-	return Bool(c.Properties.Exclude_from_vendor_snapshot)
-}
-
-func (c *Module) ExcludeFromRecoverySnapshot() bool {
-	return Bool(c.Properties.Exclude_from_recovery_snapshot)
-}
-
 func isBionic(name string) bool {
 	switch name {
 	case "libc", "libm", "libdl", "libdl_android", "linker":
@@ -1600,6 +1540,10 @@
 	return ctx.mod.Object()
 }
 
+func (ctx *moduleContextImpl) optimizeForSize() bool {
+	return ctx.mod.OptimizeForSize()
+}
+
 func (ctx *moduleContextImpl) canUseSdk() bool {
 	return ctx.mod.canUseSdk()
 }
@@ -1610,14 +1554,6 @@
 
 func (ctx *moduleContextImpl) sdkVersion() string {
 	if ctx.ctx.Device() {
-		config := ctx.ctx.Config()
-		if !config.IsVndkDeprecated() && ctx.useVndk() {
-			vndkVer := ctx.mod.VndkVersion()
-			if inList(vndkVer, config.PlatformVersionActiveCodenames()) {
-				return "current"
-			}
-			return vndkVer
-		}
 		return String(ctx.mod.Properties.Sdk_version)
 	}
 	return ""
@@ -1634,7 +1570,7 @@
 
 	if ctx.ctx.Device() {
 		config := ctx.ctx.Config()
-		if config.IsVndkDeprecated() && ctx.inVendor() {
+		if ctx.inVendor() {
 			// If building for vendor with final API, then use the latest _stable_ API as "current".
 			if config.VendorApiLevelFrozen() && (ver == "" || ver == "current") {
 				ver = config.PlatformSdkVersion().String()
@@ -1694,22 +1630,10 @@
 	return ctx.mod.IsLlndk()
 }
 
-func (ctx *moduleContextImpl) IsLlndkPublic() bool {
-	return ctx.mod.IsLlndkPublic()
-}
-
 func (ctx *moduleContextImpl) isImplementationForLLNDKPublic() bool {
 	return ctx.mod.isImplementationForLLNDKPublic()
 }
 
-func (ctx *moduleContextImpl) IsVndkPrivate() bool {
-	return ctx.mod.IsVndkPrivate()
-}
-
-func (ctx *moduleContextImpl) isVndk() bool {
-	return ctx.mod.IsVndk()
-}
-
 func (ctx *moduleContextImpl) isAfdoCompile(mctx ModuleContext) bool {
 	return ctx.mod.isAfdoCompile(mctx)
 }
@@ -1730,22 +1654,10 @@
 	return ctx.mod.isNDKStubLibrary()
 }
 
-func (ctx *moduleContextImpl) isVndkSp() bool {
-	return ctx.mod.IsVndkSp()
-}
-
-func (ctx *moduleContextImpl) IsVndkExt() bool {
-	return ctx.mod.IsVndkExt()
-}
-
 func (ctx *moduleContextImpl) IsVendorPublicLibrary() bool {
 	return ctx.mod.IsVendorPublicLibrary()
 }
 
-func (ctx *moduleContextImpl) mustUseVendorVariant() bool {
-	return ctx.mod.MustUseVendorVariant()
-}
-
 func (ctx *moduleContextImpl) selectedStl() string {
 	if stl := ctx.mod.stl; stl != nil {
 		return stl.Properties.SelectedStl
@@ -1761,10 +1673,6 @@
 	return ctx.mod.BaseModuleName()
 }
 
-func (ctx *moduleContextImpl) getVndkExtendsModuleName() string {
-	return ctx.mod.getVndkExtendsModuleName()
-}
-
 func (ctx *moduleContextImpl) isForPlatform() bool {
 	apexInfo, _ := android.ModuleProvider(ctx.ctx, android.ApexInfoProvider)
 	return apexInfo.IsForPlatform()
@@ -1829,7 +1737,6 @@
 	module.coverage = &coverage{}
 	module.fuzzer = &fuzzer{}
 	module.sabi = &sabi{}
-	module.vndkdep = &vndkdep{}
 	module.lto = &lto{}
 	module.afdo = &afdo{}
 	module.orderfile = &orderfile{}
@@ -1866,11 +1773,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
@@ -1883,7 +1785,6 @@
 func getNameSuffixWithVndkVersion(ctx android.ModuleContext, c LinkableInterface) string {
 	// Returns the name suffix for product and vendor variants. If the VNDK version is not
 	// "current", it will append the VNDK version to the name suffix.
-	var vndkVersion string
 	var nameSuffix string
 	if c.InProduct() {
 		if c.ProductSpecific() {
@@ -1893,13 +1794,9 @@
 		}
 		return ProductSuffix
 	} else {
-		vndkVersion = ctx.DeviceConfig().VndkVersion()
 		nameSuffix = VendorSuffix
 	}
-	if vndkVersion == "current" {
-		vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
-	}
-	if c.VndkVersion() != vndkVersion && c.VndkVersion() != "" {
+	if c.VndkVersion() != "" {
 		// add version suffix only if the module is using different vndk version than the
 		// version in product or vendor partition.
 		nameSuffix += "." + c.VndkVersion()
@@ -1970,8 +1867,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,
@@ -2008,20 +1907,25 @@
 	return false
 }
 
-func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles)
-}
-
 func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
-	// 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
+	ctx := moduleContextFromAndroidModuleContext(actx, c)
+
+	c.logtagsPaths = android.PathsForModuleSrc(actx, c.Properties.Logtags)
+	android.SetProvider(ctx, android.LogtagsProviderKey, &android.LogtagsInfo{
+		Logtags: c.logtagsPaths,
+	})
+
+	// If Test_only is set on a module in bp file, respect the setting, otherwise
+	// see if is a known test module type.
+	testOnly := c.testModule || c.testLibrary()
+	if c.sourceProperties.Test_only != nil {
+		testOnly = Bool(c.sourceProperties.Test_only)
 	}
+	// Keep before any early returns.
+	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+		TestOnly:       testOnly,
+		TopLevelTarget: c.testModule,
+	})
 
 	c.Properties.SubName = GetSubnameProperty(actx, c)
 	apexInfo, _ := android.ModuleProvider(actx, android.ApexInfoProvider)
@@ -2031,8 +1935,6 @@
 
 	c.makeLinkType = GetMakeLinkType(actx, c)
 
-	ctx := moduleContextFromAndroidModuleContext(actx, c)
-
 	deps := c.depsToPaths(ctx)
 	if ctx.Failed() {
 		return
@@ -2154,21 +2056,16 @@
 		c.outputFile = android.OptionalPathForPath(outputFile)
 
 		c.maybeUnhideFromMake()
-
-		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
-		// RECOVERY_SNAPSHOT_VERSION is current.
-		if i, ok := c.linker.(snapshotLibraryInterface); ok {
-			if ShouldCollectHeadersForSnapshot(ctx, c, apexInfo) {
-				i.collectHeadersForSnapshot(ctx)
-			}
-		}
 	}
 	if c.testModule {
 		android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 	}
+
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
 
-	android.CollectDependencyAconfigFiles(ctx, &c.mergedAconfigFiles)
+	if Bool(c.Properties.Cmake_snapshot_supported) {
+		android.SetProvider(ctx, cmakeSnapshotSourcesProvider, android.GlobFiles(ctx, ctx.ModuleDir()+"/**/*", nil))
+	}
 
 	c.maybeInstall(ctx, apexInfo)
 
@@ -2202,8 +2099,58 @@
 		if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
 			moduleInfoJSON.Uninstallable = true
 		}
-
 	}
+
+	buildComplianceMetadataInfo(ctx, c, deps)
+
+	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() {
@@ -2318,6 +2265,7 @@
 	deps.LateSharedLibs = android.LastUniqueStrings(deps.LateSharedLibs)
 	deps.HeaderLibs = android.LastUniqueStrings(deps.HeaderLibs)
 	deps.RuntimeLibs = android.LastUniqueStrings(deps.RuntimeLibs)
+	deps.LlndkHeaderLibs = android.LastUniqueStrings(deps.LlndkHeaderLibs)
 
 	for _, lib := range deps.ReexportSharedLibHeaders {
 		if !inList(lib, deps.SharedLibs) {
@@ -2440,31 +2388,6 @@
 	return apiImportInfo
 }
 
-func GetSnapshot(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.BottomUpMutatorContext) SnapshotInfo {
-	// Only device modules with BOARD_VNDK_VERSION uses snapshot.  Others use the zero value of
-	// SnapshotInfo, which provides no mappings.
-	if *snapshotInfo == nil && c.Device() {
-		// Only retrieve the snapshot on demand in order to avoid circular dependencies
-		// between the modules in the snapshot and the snapshot itself.
-		var snapshotModule []blueprint.Module
-		if c.InVendor() && c.VndkVersion() == actx.DeviceConfig().VndkVersion() && actx.OtherModuleExists("vendor_snapshot") {
-			snapshotModule = actx.AddVariationDependencies(nil, nil, "vendor_snapshot")
-		} else if recoverySnapshotVersion := actx.DeviceConfig().RecoverySnapshotVersion(); recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" && c.InRecovery() && actx.OtherModuleExists("recovery_snapshot") {
-			snapshotModule = actx.AddVariationDependencies(nil, nil, "recovery_snapshot")
-		}
-		if len(snapshotModule) > 0 && snapshotModule[0] != nil {
-			snapshot, _ := android.OtherModuleProvider(actx, snapshotModule[0], SnapshotInfoProvider)
-			*snapshotInfo = &snapshot
-			// republish the snapshot for use in later mutators on this module
-			android.SetProvider(actx, SnapshotInfoProvider, snapshot)
-		}
-	}
-	if *snapshotInfo == nil {
-		*snapshotInfo = &SnapshotInfo{}
-	}
-	return **snapshotInfo
-}
-
 func GetReplaceModuleName(lib string, replaceMap map[string]string) string {
 	if snapshot, ok := replaceMap[lib]; ok {
 		return snapshot
@@ -2473,44 +2396,35 @@
 	return lib
 }
 
-// RewriteLibs takes a list of names of shared libraries and scans it for three types
+// FilterNdkLibs takes a list of names of shared libraries and scans it for two types
 // of names:
 //
-// 1. Name of an NDK library that refers to a prebuilt module.
-//
-//	For each of these, it adds the name of the prebuilt module (which will be in
-//	prebuilts/ndk) to the list of nonvariant libs.
-//
-// 2. Name of an NDK library that refers to an ndk_library module.
+// 1. Name of an NDK library that refers to an ndk_library module.
 //
 //	For each of these, it adds the name of the ndk_library module to the list of
 //	variant libs.
 //
-// 3. Anything else (so anything that isn't an NDK library).
+// 2. Anything else (so anything that isn't an NDK library).
 //
 //	It adds these to the nonvariantLibs list.
 //
 // The caller can then know to add the variantLibs dependencies differently from the
 // nonvariantLibs
-func RewriteLibs(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.BottomUpMutatorContext, config android.Config, list []string) (nonvariantLibs []string, variantLibs []string) {
+func FilterNdkLibs(c LinkableInterface, config android.Config, list []string) (nonvariantLibs []string, variantLibs []string) {
 	variantLibs = []string{}
 
 	nonvariantLibs = []string{}
 	for _, entry := range list {
 		// strip #version suffix out
 		name, _ := StubsLibNameAndVersion(entry)
-		if c.InRecovery() {
-			nonvariantLibs = append(nonvariantLibs, GetReplaceModuleName(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
-		} else if c.UseSdk() && inList(name, *getNDKKnownLibs(config)) {
+		if c.UseSdk() && inList(name, *getNDKKnownLibs(config)) {
 			variantLibs = append(variantLibs, name+ndkLibrarySuffix)
-		} else if c.UseVndk() {
-			nonvariantLibs = append(nonvariantLibs, GetReplaceModuleName(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
 		} else {
-			// put name#version back
 			nonvariantLibs = append(nonvariantLibs, entry)
 		}
 	}
 	return nonvariantLibs, variantLibs
+
 }
 
 func rewriteLibsForApiImports(c LinkableInterface, libs []string, replaceList map[string]string, config android.Config) ([]string, []string) {
@@ -2554,7 +2468,7 @@
 }
 
 func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
-	if !c.Enabled() {
+	if !c.Enabled(actx) {
 		return
 	}
 
@@ -2582,18 +2496,12 @@
 
 	c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
 
-	var snapshotInfo *SnapshotInfo
-
 	variantNdkLibs := []string{}
 	variantLateNdkLibs := []string{}
 	if ctx.Os() == android.Android {
-		deps.SharedLibs, variantNdkLibs = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.SharedLibs)
-		deps.LateSharedLibs, variantLateNdkLibs = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.LateSharedLibs)
-		deps.ReexportSharedLibHeaders, _ = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.ReexportSharedLibHeaders)
-
-		for idx, lib := range deps.RuntimeLibs {
-			deps.RuntimeLibs[idx] = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).SharedLibs)
-		}
+		deps.SharedLibs, variantNdkLibs = FilterNdkLibs(c, ctx.Config(), deps.SharedLibs)
+		deps.LateSharedLibs, variantLateNdkLibs = FilterNdkLibs(c, ctx.Config(), deps.LateSharedLibs)
+		deps.ReexportSharedLibHeaders, _ = FilterNdkLibs(c, ctx.Config(), deps.ReexportSharedLibHeaders)
 	}
 
 	for _, lib := range deps.HeaderLibs {
@@ -2606,7 +2514,6 @@
 		if c.shouldUseApiSurface() {
 			lib = GetReplaceModuleName(lib, apiImportInfo.HeaderLibs)
 		}
-		lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).HeaderLibs)
 
 		if c.isNDKStubLibrary() {
 			// ndk_headers do not have any variations
@@ -2629,27 +2536,33 @@
 		), stubImplementation, c.BaseModuleName())
 	}
 
+	// If this module is an LLNDK implementation library, let it depend on LlndkHeaderLibs.
+	if c.ImageVariation().Variation == android.CoreVariation && c.Device() &&
+		c.Target().NativeBridge == android.NativeBridgeDisabled {
+		actx.AddVariationDependencies(
+			[]blueprint.Variation{{Mutator: "image", Variation: android.VendorVariation}},
+			llndkHeaderLibTag,
+			deps.LlndkHeaderLibs...)
+	}
+
 	for _, lib := range deps.WholeStaticLibs {
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true, reexportFlags: true}
 
-		lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
-
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
 		}, depTag, lib)
 	}
 
 	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
 		}
-
-		lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
-
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
 		}, depTag, lib)
@@ -2662,7 +2575,7 @@
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency, staticUnwinder: true}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, depTag, GetReplaceModuleName(staticUnwinder(actx), GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+		}, depTag, staticUnwinder(actx))
 	}
 
 	// shared lib names without the #version suffix
@@ -2703,14 +2616,14 @@
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, depTag, GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+		}, depTag, lib)
 	}
 
 	for _, lib := range deps.UnexportedStaticLibs {
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency, unexportedSymbols: true}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, depTag, GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+		}, depTag, lib)
 	}
 
 	for _, lib := range deps.LateSharedLibs {
@@ -2751,11 +2664,11 @@
 	actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
 	for _, crt := range deps.CrtBegin {
 		actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
-			GetReplaceModuleName(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
+			crt)
 	}
 	for _, crt := range deps.CrtEnd {
 		actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
-			GetReplaceModuleName(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
+			crt)
 	}
 	if deps.DynamicLinker != "" {
 		actx.AddDependency(c, dynamicLinkerDepTag, deps.DynamicLinker)
@@ -2783,15 +2696,6 @@
 		{Mutator: "link", Variation: "shared"},
 	}, ndkLateStubDepTag, apiLateNdkLibs...)
 
-	if vndkdep := c.vndkdep; vndkdep != nil {
-		if vndkdep.isVndkExt() {
-			actx.AddVariationDependencies([]blueprint.Variation{
-				c.ImageVariation(),
-				{Mutator: "link", Variation: "shared"},
-			}, vndkExtDepTag, GetReplaceModuleName(vndkdep.getVndkExtendsModuleName(), GetSnapshot(c, &snapshotInfo, actx).SharedLibs))
-		}
-	}
-
 	if len(deps.AidlLibs) > 0 {
 		actx.AddDependency(
 			c,
@@ -2804,7 +2708,7 @@
 }
 
 func BeginMutator(ctx android.BottomUpMutatorContext) {
-	if c, ok := ctx.Module().(*Module); ok && c.Enabled() {
+	if c, ok := ctx.Module().(*Module); ok && c.Enabled(ctx) {
 		c.beginMutator(ctx)
 	}
 }
@@ -2829,20 +2733,6 @@
 		return
 	}
 
-	// VNDK is cc.Module supported only for now.
-	if ccFrom, ok := from.(*Module); ok && from.UseVndk() {
-		// Though allowed dependency is limited by the image mutator,
-		// each vendor and product module needs to check link-type
-		// for VNDK.
-		if ccTo, ok := to.(*Module); ok {
-			if ccFrom.vndkdep != nil {
-				ccFrom.vndkdep.vndkCheckLinkType(ctx, ccTo, tag)
-			}
-		} else if _, ok := to.(LinkableInterface); !ok {
-			ctx.ModuleErrorf("Attempting to link VNDK cc.Module with unsupported module type")
-		}
-		return
-	}
 	// TODO(b/244244438) : Remove this once all variants are implemented
 	if ccFrom, ok := from.(*Module); ok && ccFrom.isImportedApiLibrary() {
 		return
@@ -2980,6 +2870,9 @@
 		if depTag == stubImplDepTag {
 			return false
 		}
+		if depTag == android.RequiredDepTag {
+			return false
+		}
 
 		// Even if target lib has no vendor variant, keep checking dependency
 		// graph in case it depends on vendor_available or product_available
@@ -2994,7 +2887,7 @@
 			return true
 		}
 
-		if to.IsVndkSp() || to.IsLlndk() {
+		if to.IsLlndk() {
 			return false
 		}
 
@@ -3157,6 +3050,10 @@
 			return
 		}
 
+		if depTag == android.RequiredDepTag {
+			return
+		}
+
 		if dep.Target().Os != ctx.Os() {
 			ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
 			return
@@ -3182,6 +3079,12 @@
 			return
 		}
 
+		if depTag == llndkHeaderLibTag {
+			depExporterInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
+			depPaths.LlndkIncludeDirs = append(depPaths.LlndkIncludeDirs, depExporterInfo.IncludeDirs...)
+			depPaths.LlndkSystemIncludeDirs = append(depPaths.LlndkSystemIncludeDirs, depExporterInfo.SystemIncludeDirs...)
+		}
+
 		linkFile := ccDep.OutputFile()
 
 		if libDepTag, ok := depTag.(libraryDependencyTag); ok {
@@ -3251,65 +3154,87 @@
 				default:
 					panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order))
 				}
+
 			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
 					}
-				}
-				if libDepTag.unexportedSymbols {
-					depPaths.LdFlags = append(depPaths.LdFlags,
-						"-Wl,--exclude-libs="+staticLibraryInfo.StaticLibrary.Base())
+
+					// 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())
+						}
+						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
@@ -3355,16 +3280,22 @@
 			depPaths.SystemIncludeDirs = append(depPaths.SystemIncludeDirs, depExporterInfo.SystemIncludeDirs...)
 			depPaths.GeneratedDeps = append(depPaths.GeneratedDeps, depExporterInfo.Deps...)
 			depPaths.Flags = append(depPaths.Flags, depExporterInfo.Flags...)
+			depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...)
+
+			// Only re-export RustRlibDeps for cc static libs
+			if c.static() {
+				depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, depExporterInfo.RustRlibDeps...)
+			}
 
 			if libDepTag.reexportFlags {
 				reexportExporter(depExporterInfo)
 				// Add these re-exported flags to help header-abi-dumper to infer the abi exported by a library.
 				// Re-exported shared library headers must be included as well since they can help us with type information
 				// about template instantiations (instantiated from their headers).
-				// -isystem headers are not included since for bionic libraries, abi-filtering is taken care of by version
-				// scripts.
 				c.sabi.Properties.ReexportedIncludes = append(
 					c.sabi.Properties.ReexportedIncludes, depExporterInfo.IncludeDirs.Strings()...)
+				c.sabi.Properties.ReexportedSystemIncludes = append(
+					c.sabi.Properties.ReexportedSystemIncludes, depExporterInfo.SystemIncludeDirs.Strings()...)
 			}
 
 			makeLibName := MakeLibName(ctx, c, ccDep, ccDep.BaseModuleName()) + libDepTag.makeSuffix
@@ -3389,18 +3320,16 @@
 				// they merely serve as Make dependencies and do not affect this lib itself.
 				c.Properties.AndroidMkSharedLibs = append(
 					c.Properties.AndroidMkSharedLibs, makeLibName)
-				// Record BaseLibName for snapshots.
-				c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, BaseLibName(depName))
 			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)
+					}
 				}
-				// Record BaseLibName for snapshots.
-				c.Properties.SnapshotStaticLibs = append(c.Properties.SnapshotStaticLibs, BaseLibName(depName))
 			}
 		} else if !c.IsStubs() {
 			// Stubs lib doesn't link to the runtime lib, object, crt, etc. dependencies.
@@ -3409,8 +3338,6 @@
 			case runtimeDepTag:
 				c.Properties.AndroidMkRuntimeLibs = append(
 					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ctx, c, ccDep, ccDep.BaseModuleName())+libDepTag.makeSuffix)
-				// Record BaseLibName for snapshots.
-				c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, BaseLibName(depName))
 			case objDepTag:
 				depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
 			case CrtBeginDepTag:
@@ -3433,14 +3360,18 @@
 	depPaths.IncludeDirs = android.FirstUniquePaths(depPaths.IncludeDirs)
 	depPaths.SystemIncludeDirs = android.FirstUniquePaths(depPaths.SystemIncludeDirs)
 	depPaths.GeneratedDeps = android.FirstUniquePaths(depPaths.GeneratedDeps)
+	depPaths.RustRlibDeps = android.FirstUniqueFunc(depPaths.RustRlibDeps, EqRustRlibDeps)
+
 	depPaths.ReexportedDirs = android.FirstUniquePaths(depPaths.ReexportedDirs)
 	depPaths.ReexportedSystemDirs = android.FirstUniquePaths(depPaths.ReexportedSystemDirs)
 	depPaths.ReexportedFlags = android.FirstUniqueStrings(depPaths.ReexportedFlags)
 	depPaths.ReexportedDeps = android.FirstUniquePaths(depPaths.ReexportedDeps)
 	depPaths.ReexportedGeneratedHeaders = android.FirstUniquePaths(depPaths.ReexportedGeneratedHeaders)
+	depPaths.ReexportedRustRlibDeps = android.FirstUniqueFunc(depPaths.ReexportedRustRlibDeps, EqRustRlibDeps)
 
 	if c.sabi != nil {
 		c.sabi.Properties.ReexportedIncludes = android.FirstUniqueStrings(c.sabi.Properties.ReexportedIncludes)
+		c.sabi.Properties.ReexportedSystemIncludes = android.FirstUniqueStrings(c.sabi.Properties.ReexportedSystemIncludes)
 	}
 
 	return depPaths
@@ -3613,12 +3544,7 @@
 		}
 	}
 
-	if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() &&
-		!c.InRamdisk() && !c.InVendorRamdisk() && !c.InRecovery() {
-		// The vendor module is a no-vendor-variant VNDK library.  Depend on the
-		// core module instead.
-		return libName
-	} else if ccDep.InVendorOrProduct() && nonSystemVariantsExist {
+	if ccDep.InVendorOrProduct() && nonSystemVariantsExist {
 		// The vendor and product modules in Make will have been renamed to not conflict with the
 		// core module, so update the dependency name here accordingly.
 		return libName + ccDep.SubName()
@@ -3683,28 +3609,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
@@ -3794,18 +3698,17 @@
 	return false
 }
 
+func (m *Module) Dylib() bool {
+	return false
+}
+
+func (m *Module) Rlib() bool {
+	return false
+}
+
 func GetMakeLinkType(actx android.ModuleContext, c LinkableInterface) string {
 	if c.InVendorOrProduct() {
 		if c.IsLlndk() {
-			if !c.IsLlndkPublic() {
-				return "native:vndk_private"
-			}
-			return "native:vndk"
-		}
-		if c.IsVndk() && !c.IsVndkExt() {
-			if c.IsVndkPrivate() {
-				return "native:vndk_private"
-			}
 			return "native:vndk"
 		}
 		if c.InProduct() {
@@ -3823,22 +3726,18 @@
 		// TODO(b/114741097): use the correct ndk stl once build errors have been fixed
 		//family, link := getNdkStlFamilyAndLinkType(c)
 		//return fmt.Sprintf("native:ndk:%s:%s", family, link)
-	} else if actx.DeviceConfig().VndkUseCoreVariant() && !c.MustUseVendorVariant() {
-		return "native:platform_vndk"
 	} else {
 		return "native:platform"
 	}
 }
 
 // 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
 }
@@ -4009,15 +3908,6 @@
 	return c.IsStubs() || c.Target().NativeBridge == android.NativeBridgeEnabled
 }
 
-// Overrides android.ApexModuleBase.UniqueApexVariations
-func (c *Module) UniqueApexVariations() bool {
-	// When a vendor APEX needs a VNDK lib in it (use_vndk_as_stable: false), it should be a unique
-	// APEX variation. Otherwise, another vendor APEX with use_vndk_as_stable:true may use a wrong
-	// variation of the VNDK lib because APEX variations are merged/grouped.
-	// TODO(b/274401041) Find a way to merge APEX variations for vendor apexes.
-	return c.UseVndk() && c.IsVndk()
-}
-
 func (c *Module) overriddenModules() []string {
 	if o, ok := c.linker.(overridable); ok {
 		return o.overriddenModules()
@@ -4025,8 +3915,6 @@
 	return nil
 }
 
-var _ snapshot.RelativeInstallPath = (*Module)(nil)
-
 type moduleType int
 
 const (
@@ -4089,9 +3977,6 @@
 	android.ModuleBase
 	android.DefaultsModuleBase
 	android.ApexModuleBase
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
 }
 
 // cc_defaults provides a set of properties that can be inherited by other cc
@@ -4130,7 +4015,6 @@
 		&TidyProperties{},
 		&CoverageProperties{},
 		&SAbiProperties{},
-		&VndkProperties{},
 		&LTOProperties{},
 		&AfdoProperties{},
 		&OrderfileProperties{},
diff --git a/cc/cc_test.go b/cc/cc_test.go
index d1b728e..ccdaae5 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"os"
-	"path/filepath"
 	"reflect"
 	"regexp"
 	"runtime"
@@ -39,17 +38,6 @@
 }
 
 var prepareForCcTest = android.GroupFixturePreparers(
-	PrepareForTestWithCcIncludeVndk,
-	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.VendorApiLevel = StringPtr("202404")
-		variables.DeviceVndkVersion = StringPtr("current")
-		variables.KeepVndk = BoolPtr(true)
-		variables.Platform_vndk_version = StringPtr("29")
-	}),
-)
-
-// TODO(b/316829758) Update prepareForCcTest with this configuration and remove prepareForCcTestWithoutVndk
-var prepareForCcTestWithoutVndk = android.GroupFixturePreparers(
 	PrepareForIntegrationTestWithCc,
 	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 		variables.VendorApiLevel = StringPtr("202404")
@@ -123,30 +111,14 @@
 func testCcError(t *testing.T, pattern string, bp string) {
 	t.Helper()
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	testCcErrorWithConfig(t, pattern, config)
-	return
-}
-
-// testCcErrorProductVndk runs tests using the prepareForCcTest
-//
-// See testCc for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testCcErrorProductVndk(t *testing.T, pattern string, bp string) {
-	t.Helper()
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
 	testCcErrorWithConfig(t, pattern, config)
 	return
 }
 
 const (
 	coreVariant     = "android_arm64_armv8-a_shared"
-	vendorVariant   = "android_vendor.29_arm64_armv8-a_shared"
-	productVariant  = "android_product.29_arm64_armv8-a_shared"
+	vendorVariant   = "android_vendor_arm64_armv8-a_shared"
+	productVariant  = "android_product_arm64_armv8-a_shared"
 	recoveryVariant = "android_recovery_arm64_armv8-a_shared"
 )
 
@@ -302,44 +274,6 @@
 	checkInstallPartition(t, ctx, "libproduct_odmavailable", vendorVariant, "odm")
 }
 
-func checkVndkModule(t *testing.T, ctx *android.TestContext, name, subDir string,
-	isVndkSp bool, extends string, variant string) {
-
-	t.Helper()
-
-	mod := ctx.ModuleForTests(name, variant).Module().(*Module)
-
-	// Check library properties.
-	lib, ok := mod.compiler.(*libraryDecorator)
-	if !ok {
-		t.Errorf("%q must have libraryDecorator", name)
-	} else if lib.baseInstaller.subDir != subDir {
-		t.Errorf("%q must use %q as subdir but it is using %q", name, subDir,
-			lib.baseInstaller.subDir)
-	}
-
-	// Check VNDK properties.
-	if mod.vndkdep == nil {
-		t.Fatalf("%q must have `vndkdep`", name)
-	}
-	if !mod.IsVndk() {
-		t.Errorf("%q IsVndk() must equal to true", name)
-	}
-	if mod.IsVndkSp() != isVndkSp {
-		t.Errorf("%q IsVndkSp() must equal to %t", name, isVndkSp)
-	}
-
-	// Check VNDK extension properties.
-	isVndkExt := extends != ""
-	if mod.IsVndkExt() != isVndkExt {
-		t.Errorf("%q IsVndkExt() must equal to %t", name, isVndkExt)
-	}
-
-	if actualExtends := mod.getVndkExtendsModuleName(); actualExtends != extends {
-		t.Errorf("%q must extend from %q but get %q", name, extends, actualExtends)
-	}
-}
-
 func checkWriteFileOutput(t *testing.T, ctx *android.TestContext, params android.TestingBuildParams, expected []string) {
 	t.Helper()
 	content := android.ContentFromFileRuleForTests(t, ctx, params)
@@ -347,344 +281,6 @@
 	assertArrayString(t, actual, expected)
 }
 
-func checkVndkOutput(t *testing.T, ctx *android.TestContext, output string, expected []string) {
-	t.Helper()
-	vndkSnapshot := ctx.SingletonForTests("vndk-snapshot")
-	checkWriteFileOutput(t, ctx, vndkSnapshot.Output(output), expected)
-}
-
-func checkVndkLibrariesOutput(t *testing.T, ctx *android.TestContext, module string, expected []string) {
-	t.Helper()
-	got := ctx.ModuleForTests(module, "android_common").Module().(*vndkLibrariesTxt).fileNames
-	assertArrayString(t, got, expected)
-}
-
-func TestVndk(t *testing.T) {
-	t.Parallel()
-	bp := `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_private",
-			vendor_available: true,
-			vndk: {
-				enabled: true,
-				private: true,
-			},
-			nocrt: true,
-			stem: "libvndk-private",
-		}
-
-		cc_library {
-			name: "libvndk_product",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-			target: {
-				vendor: {
-					cflags: ["-DTEST"],
-				},
-				product: {
-					cflags: ["-DTEST"],
-				},
-			},
-		}
-
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			nocrt: true,
-			suffix: "-x",
-		}
-
-		cc_library {
-			name: "libvndk_sp_private",
-			vendor_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-				private: true,
-			},
-			nocrt: true,
-			target: {
-				vendor: {
-					suffix: "-x",
-				},
-			},
-		}
-
-		cc_library {
-			name: "libvndk_sp_product_private",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-				private: true,
-			},
-			nocrt: true,
-			target: {
-				vendor: {
-					suffix: "-x",
-				},
-				product: {
-					suffix: "-x",
-				},
-			},
-		}
-
-		cc_library {
-			name: "libllndk",
-			llndk: {
-				symbol_file: "libllndk.map.txt",
-				export_llndk_headers: ["libllndk_headers"],
-			}
-		}
-
-		cc_library {
-			name: "libclang_rt.hwasan-llndk",
-			llndk: {
-				symbol_file: "libclang_rt.hwasan.map.txt",
-			}
-		}
-
-		cc_library_headers {
-			name: "libllndk_headers",
-			llndk: {
-				llndk_headers: true,
-			},
-			export_include_dirs: ["include"],
-		}
-
-		llndk_libraries_txt {
-			name: "llndk.libraries.txt",
-		}
-		vndkcore_libraries_txt {
-			name: "vndkcore.libraries.txt",
-		}
-		vndksp_libraries_txt {
-			name: "vndksp.libraries.txt",
-		}
-		vndkprivate_libraries_txt {
-			name: "vndkprivate.libraries.txt",
-		}
-		vndkproduct_libraries_txt {
-			name: "vndkproduct.libraries.txt",
-		}
-		vndkcorevariant_libraries_txt {
-			name: "vndkcorevariant.libraries.txt",
-			insert_vndk_version: false,
-		}
-	`
-
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-
-	ctx := testCcWithConfig(t, config)
-
-	// subdir == "" because VNDK libs are not supposed to be installed separately.
-	// They are installed as part of VNDK APEX instead.
-	checkVndkModule(t, ctx, "libvndk", "", false, "", vendorVariant)
-	checkVndkModule(t, ctx, "libvndk_private", "", false, "", vendorVariant)
-	checkVndkModule(t, ctx, "libvndk_product", "", false, "", vendorVariant)
-	checkVndkModule(t, ctx, "libvndk_sp", "", true, "", vendorVariant)
-	checkVndkModule(t, ctx, "libvndk_sp_private", "", true, "", vendorVariant)
-	checkVndkModule(t, ctx, "libvndk_sp_product_private", "", true, "", vendorVariant)
-
-	checkVndkModule(t, ctx, "libvndk_product", "", false, "", productVariant)
-	checkVndkModule(t, ctx, "libvndk_sp_product_private", "", true, "", productVariant)
-
-	// Check VNDK snapshot output.
-	snapshotDir := "vndk-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-
-	vndkLibPath := filepath.Join(snapshotVariantPath, fmt.Sprintf("arch-%s-%s",
-		"arm64", "armv8-a"))
-	vndkLib2ndPath := filepath.Join(snapshotVariantPath, fmt.Sprintf("arch-%s-%s",
-		"arm", "armv7-a-neon"))
-
-	vndkCoreLibPath := filepath.Join(vndkLibPath, "shared", "vndk-core")
-	vndkSpLibPath := filepath.Join(vndkLibPath, "shared", "vndk-sp")
-	llndkLibPath := filepath.Join(vndkLibPath, "shared", "llndk-stub")
-
-	vndkCoreLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-core")
-	vndkSpLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "vndk-sp")
-	llndkLib2ndPath := filepath.Join(vndkLib2ndPath, "shared", "llndk-stub")
-
-	variant := "android_vendor.29_arm64_armv8-a_shared"
-	variant2nd := "android_vendor.29_arm_armv7-a-neon_shared"
-
-	snapshotSingleton := ctx.SingletonForTests("vndk-snapshot")
-
-	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLibPath, variant)
-	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
-	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLibPath, variant)
-	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLib2ndPath, variant2nd)
-	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLibPath, variant)
-	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLib2ndPath, variant2nd)
-	CheckSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLibPath, variant)
-	CheckSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLib2ndPath, variant2nd)
-
-	snapshotConfigsPath := filepath.Join(snapshotVariantPath, "configs")
-	CheckSnapshot(t, ctx, snapshotSingleton, "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "android_common")
-	CheckSnapshot(t, ctx, snapshotSingleton, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "android_common")
-	CheckSnapshot(t, ctx, snapshotSingleton, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "android_common")
-	CheckSnapshot(t, ctx, snapshotSingleton, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "android_common")
-	CheckSnapshot(t, ctx, snapshotSingleton, "vndkproduct.libraries.txt", "vndkproduct.libraries.txt", snapshotConfigsPath, "android_common")
-
-	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
-		"LLNDK: libc.so",
-		"LLNDK: libdl.so",
-		"LLNDK: libft2.so",
-		"LLNDK: libllndk.so",
-		"LLNDK: libm.so",
-		"VNDK-SP: libc++.so",
-		"VNDK-SP: libvndk_sp-x.so",
-		"VNDK-SP: libvndk_sp_private-x.so",
-		"VNDK-SP: libvndk_sp_product_private-x.so",
-		"VNDK-core: libvndk-private.so",
-		"VNDK-core: libvndk.so",
-		"VNDK-core: libvndk_product.so",
-		"VNDK-private: libft2.so",
-		"VNDK-private: libvndk-private.so",
-		"VNDK-private: libvndk_sp_private-x.so",
-		"VNDK-private: libvndk_sp_product_private-x.so",
-		"VNDK-product: libc++.so",
-		"VNDK-product: libvndk_product.so",
-		"VNDK-product: libvndk_sp_product_private-x.so",
-	})
-	checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt", []string{"libc.so", "libclang_rt.hwasan-llndk.so", "libdl.so", "libft2.so", "libllndk.so", "libm.so"})
-	checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt", []string{"libvndk-private.so", "libvndk.so", "libvndk_product.so"})
-	checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt", []string{"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so", "libvndk_sp_product_private-x.so"})
-	checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt", []string{"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so", "libvndk_sp_product_private-x.so"})
-	checkVndkLibrariesOutput(t, ctx, "vndkproduct.libraries.txt", []string{"libc++.so", "libvndk_product.so", "libvndk_sp_product_private-x.so"})
-	checkVndkLibrariesOutput(t, ctx, "vndkcorevariant.libraries.txt", nil)
-}
-
-func TestVndkWithHostSupported(t *testing.T) {
-	t.Parallel()
-	ctx := testCc(t, `
-		cc_library {
-			name: "libvndk_host_supported",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			host_supported: true,
-		}
-
-		cc_library {
-			name: "libvndk_host_supported_but_disabled_on_device",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			host_supported: true,
-			enabled: false,
-			target: {
-				host: {
-					enabled: true,
-				}
-			}
-		}
-
-		vndkcore_libraries_txt {
-			name: "vndkcore.libraries.txt",
-		}
-	`)
-
-	checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt", []string{"libvndk_host_supported.so"})
-}
-
-func TestVndkLibrariesTxtAndroidMk(t *testing.T) {
-	t.Parallel()
-	bp := `
-		llndk_libraries_txt {
-			name: "llndk.libraries.txt",
-			insert_vndk_version: true,
-		}`
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	config.TestProductVariables.KeepVndk = BoolPtr(true)
-	ctx := testCcWithConfig(t, config)
-
-	module := ctx.ModuleForTests("llndk.libraries.txt", "android_common")
-	entries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0]
-	assertArrayString(t, entries.EntryMap["LOCAL_MODULE_STEM"], []string{"llndk.libraries.29.txt"})
-}
-
-func TestVndkUsingCoreVariant(t *testing.T) {
-	t.Parallel()
-	bp := `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk2",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				private: true,
-			},
-			nocrt: true,
-		}
-
-		vndkcorevariant_libraries_txt {
-			name: "vndkcorevariant.libraries.txt",
-			insert_vndk_version: false,
-		}
-	`
-
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
-	config.TestProductVariables.KeepVndk = BoolPtr(true)
-
-	setVndkMustUseVendorVariantListForTest(config, []string{"libvndk"})
-
-	ctx := testCcWithConfig(t, config)
-
-	checkVndkLibrariesOutput(t, ctx, "vndkcorevariant.libraries.txt", []string{"libc++.so", "libvndk2.so", "libvndk_sp.so"})
-}
-
 func TestDataLibs(t *testing.T) {
 	t.Parallel()
 	bp := `
@@ -702,18 +298,11 @@
  `
 
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
 
 	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(t, "")
 	if len(outputFiles) != 1 {
 		t.Errorf("expected exactly one output file. output files: [%s]", outputFiles)
 		return
@@ -761,17 +350,12 @@
  `
 
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
 
 	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(t, "")
 	if len(outputFiles) != 1 {
 		t.Fatalf("expected exactly one output file. output files: [%s]", outputFiles)
 	}
@@ -859,247 +443,9 @@
 	}
 }
 
-func TestVndkModuleError(t *testing.T) {
-	t.Parallel()
-	// Check the error message for vendor_available and product_available properties.
-	testCcErrorProductVndk(t, "vndk: vendor_available must be set to true when `vndk: {enabled: true}`", `
-		cc_library {
-			name: "libvndk",
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-	`)
-
-	testCcErrorProductVndk(t, "vndk: vendor_available must be set to true when `vndk: {enabled: true}`", `
-		cc_library {
-			name: "libvndk",
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-	`)
-
-	testCcErrorProductVndk(t, "product properties must have the same values with the vendor properties for VNDK modules", `
-		cc_library {
-			name: "libvndkprop",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-			target: {
-				vendor: {
-					cflags: ["-DTEST",],
-				},
-			},
-		}
-	`)
-}
-
-func TestVndkDepError(t *testing.T) {
-	t.Parallel()
-	// Check whether an error is emitted when a VNDK lib depends on a system lib.
-	testCcError(t, "dependency \".*\" of \".*\" missing variant", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			shared_libs: ["libfwk"],  // Cause error
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libfwk",
-			nocrt: true,
-		}
-	`)
-
-	// Check whether an error is emitted when a VNDK lib depends on a vendor lib.
-	testCcError(t, "dependency \".*\" of \".*\" missing variant", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			shared_libs: ["libvendor"],  // Cause error
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvendor",
-			vendor: true,
-			nocrt: true,
-		}
-	`)
-
-	// Check whether an error is emitted when a VNDK-SP lib depends on a system lib.
-	testCcError(t, "dependency \".*\" of \".*\" missing variant", `
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			shared_libs: ["libfwk"],  // Cause error
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libfwk",
-			nocrt: true,
-		}
-	`)
-
-	// Check whether an error is emitted when a VNDK-SP lib depends on a vendor lib.
-	testCcError(t, "dependency \".*\" of \".*\" missing variant", `
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			shared_libs: ["libvendor"],  // Cause error
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvendor",
-			vendor: true,
-			nocrt: true,
-		}
-	`)
-
-	// Check whether an error is emitted when a VNDK-SP lib depends on a VNDK lib.
-	testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			shared_libs: ["libvndk"],  // Cause error
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-	`)
-
-	// Check whether an error is emitted when a VNDK lib depends on a non-VNDK lib.
-	testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			shared_libs: ["libnonvndk"],
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libnonvndk",
-			vendor_available: true,
-			product_available: true,
-			nocrt: true,
-		}
-	`)
-
-	// Check whether an error is emitted when a VNDK-private lib depends on a non-VNDK lib.
-	testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
-		cc_library {
-			name: "libvndkprivate",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				private: true,
-			},
-			shared_libs: ["libnonvndk"],
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libnonvndk",
-			vendor_available: true,
-			product_available: true,
-			nocrt: true,
-		}
-	`)
-
-	// Check whether an error is emitted when a VNDK-sp lib depends on a non-VNDK lib.
-	testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
-		cc_library {
-			name: "libvndksp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			shared_libs: ["libnonvndk"],
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libnonvndk",
-			vendor_available: true,
-			product_available: true,
-			nocrt: true,
-		}
-	`)
-
-	// Check whether an error is emitted when a VNDK-sp-private lib depends on a non-VNDK lib.
-	testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
-		cc_library {
-			name: "libvndkspprivate",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-				private: true,
-			},
-			shared_libs: ["libnonvndk"],
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libnonvndk",
-			vendor_available: true,
-			product_available: true,
-			nocrt: true,
-		}
-	`)
-}
-
 func TestDoubleLoadbleDep(t *testing.T) {
 	t.Parallel()
-	// okay to link : LLNDK -> double_loadable VNDK
+	// okay to link : LLNDK -> double_loadable
 	testCc(t, `
 		cc_library {
 			name: "libllndk",
@@ -1113,32 +459,9 @@
 			name: "libdoubleloadable",
 			vendor_available: true,
 			product_available: true,
-			vndk: {
-				enabled: true,
-			},
 			double_loadable: true,
 		}
 	`)
-	// okay to link : LLNDK -> VNDK-SP
-	testCc(t, `
-		cc_library {
-			name: "libllndk",
-			shared_libs: ["libvndksp"],
-			llndk: {
-				symbol_file: "libllndk.map.txt",
-			}
-		}
-
-		cc_library {
-			name: "libvndksp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-		}
-	`)
 	// okay to link : double_loadable -> double_loadable
 	testCc(t, `
 		cc_library {
@@ -1154,15 +477,12 @@
 			double_loadable: true,
 		}
 	`)
-	// okay to link : double_loadable VNDK -> double_loadable VNDK private
+	// okay to link : double_loadable -> double_loadable
 	testCc(t, `
 		cc_library {
 			name: "libdoubleloadable",
 			vendor_available: true,
 			product_available: true,
-			vndk: {
-				enabled: true,
-			},
 			double_loadable: true,
 			shared_libs: ["libnondoubleloadable"],
 		}
@@ -1171,10 +491,6 @@
 			name: "libnondoubleloadable",
 			vendor_available: true,
 			product_available: true,
-			vndk: {
-				enabled: true,
-				private: true,
-			},
 			double_loadable: true,
 		}
 	`)
@@ -1204,7 +520,7 @@
 
 func TestDoubleLoadableDepError(t *testing.T) {
 	t.Parallel()
-	// Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
+	// Check whether an error is emitted when a LLNDK depends on a non-double_loadable lib.
 	testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
 		cc_library {
 			name: "libllndk",
@@ -1218,9 +534,6 @@
 			name: "libnondoubleloadable",
 			vendor_available: true,
 			product_available: true,
-			vndk: {
-				enabled: true,
-			},
 		}
 	`)
 
@@ -1285,999 +598,13 @@
 	`)
 }
 
-func TestCheckVndkMembershipBeforeDoubleLoadable(t *testing.T) {
-	t.Parallel()
-	testCcError(t, "module \"libvndksp\" variant .*: .*: VNDK-SP must only depend on VNDK-SP", `
-		cc_library {
-			name: "libvndksp",
-			shared_libs: ["libanothervndksp"],
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			}
-		}
-
-		cc_library {
-			name: "libllndk",
-			shared_libs: ["libanothervndksp"],
-		}
-
-		cc_library {
-			name: "libanothervndksp",
-			vendor_available: true,
-			product_available: true,
-		}
-	`)
-}
-
-func TestVndkExt(t *testing.T) {
-	t.Parallel()
-	// This test checks the VNDK-Ext properties.
-	bp := `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-		cc_library {
-			name: "libvndk2",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			target: {
-				vendor: {
-					suffix: "-suffix",
-				},
-				product: {
-					suffix: "-suffix",
-				},
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk2_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk2",
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext_product",
-			product_specific: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk2_ext_product",
-			product_specific: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk2",
-			},
-			nocrt: true,
-		}
-	`
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-
-	ctx := testCcWithConfig(t, config)
-
-	checkVndkModule(t, ctx, "libvndk_ext", "vndk", false, "libvndk", vendorVariant)
-	checkVndkModule(t, ctx, "libvndk_ext_product", "vndk", false, "libvndk", productVariant)
-
-	mod_vendor := ctx.ModuleForTests("libvndk2_ext", vendorVariant).Module().(*Module)
-	assertString(t, mod_vendor.outputFile.Path().Base(), "libvndk2-suffix.so")
-
-	mod_product := ctx.ModuleForTests("libvndk2_ext_product", productVariant).Module().(*Module)
-	assertString(t, mod_product.outputFile.Path().Base(), "libvndk2-suffix.so")
-}
-
-func TestVndkExtError(t *testing.T) {
-	t.Parallel()
-	// This test ensures an error is emitted in ill-formed vndk-ext definition.
-	testCcError(t, "must set `vendor: true` or `product_specific: true` to set `extends: \".*\"`", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext",
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			nocrt: true,
-		}
-	`)
-
-	testCcError(t, "must set `extends: \"\\.\\.\\.\"` to vndk extension", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-	`)
-
-	testCcErrorProductVndk(t, "must set `extends: \"\\.\\.\\.\"` to vndk extension", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext_product",
-			product_specific: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-	`)
-
-	testCcErrorProductVndk(t, "must not set at the same time as `vndk: {extends: \"\\.\\.\\.\"}`", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext_product",
-			product_specific: true,
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			nocrt: true,
-		}
-	`)
-}
-
-func TestVndkExtInconsistentSupportSystemProcessError(t *testing.T) {
-	t.Parallel()
-	// This test ensures an error is emitted for inconsistent support_system_process.
-	testCcError(t, "module \".*\" with mismatched support_system_process", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-	`)
-
-	testCcError(t, "module \".*\" with mismatched support_system_process", `
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk_sp",
-			},
-			nocrt: true,
-		}
-	`)
-}
-
-func TestVndkExtVendorAvailableFalseError(t *testing.T) {
-	t.Parallel()
-	// This test ensures an error is emitted when a VNDK-Ext library extends a VNDK library
-	// with `private: true`.
-	testCcError(t, "`extends` refers module \".*\" which has `private: true`", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				private: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			nocrt: true,
-		}
-	`)
-
-	testCcErrorProductVndk(t, "`extends` refers module \".*\" which has `private: true`", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				private: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext_product",
-			product_specific: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			nocrt: true,
-		}
-	`)
-}
-
-func TestVendorModuleUseVndkExt(t *testing.T) {
-	t.Parallel()
-	// This test ensures a vendor module can depend on a VNDK-Ext library.
-	testCc(t, `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk_sp",
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvendor",
-			vendor: true,
-			shared_libs: ["libvndk_ext", "libvndk_sp_ext"],
-			nocrt: true,
-		}
-	`)
-}
-
-func TestVndkExtUseVendorLib(t *testing.T) {
-	t.Parallel()
-	// This test ensures a VNDK-Ext library can depend on a vendor library.
-	testCc(t, `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			shared_libs: ["libvendor"],
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvendor",
-			vendor: true,
-			nocrt: true,
-		}
-	`)
-
-	// This test ensures a VNDK-SP-Ext library can depend on a vendor library.
-	testCc(t, `
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk_sp",
-				support_system_process: true,
-			},
-			shared_libs: ["libvendor"],  // Cause an error
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvendor",
-			vendor: true,
-			nocrt: true,
-		}
-	`)
-}
-
-func TestProductVndkExtDependency(t *testing.T) {
-	t.Parallel()
-	bp := `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext_product",
-			product_specific: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			shared_libs: ["libproduct_for_vndklibs"],
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp_ext_product",
-			product_specific: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk_sp",
-				support_system_process: true,
-			},
-			shared_libs: ["libproduct_for_vndklibs"],
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libproduct",
-			product_specific: true,
-			shared_libs: ["libvndk_ext_product", "libvndk_sp_ext_product"],
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libproduct_for_vndklibs",
-			product_specific: true,
-			nocrt: true,
-		}
-	`
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-
-	testCcWithConfig(t, config)
-}
-
-func TestVndkSpExtUseVndkError(t *testing.T) {
-	t.Parallel()
-	// This test ensures an error is emitted if a VNDK-SP-Ext library depends on a VNDK
-	// library.
-	testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk_sp",
-				support_system_process: true,
-			},
-			shared_libs: ["libvndk"],  // Cause an error
-			nocrt: true,
-		}
-	`)
-
-	// This test ensures an error is emitted if a VNDK-SP-Ext library depends on a VNDK-Ext
-	// library.
-	testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk_sp",
-				support_system_process: true,
-			},
-			shared_libs: ["libvndk_ext"],  // Cause an error
-			nocrt: true,
-		}
-	`)
-}
-
-func TestVndkUseVndkExtError(t *testing.T) {
-	t.Parallel()
-	// This test ensures an error is emitted if a VNDK/VNDK-SP library depends on a
-	// VNDK-Ext/VNDK-SP-Ext library.
-	testCcError(t, "dependency \".*\" of \".*\" missing variant", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk2",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			shared_libs: ["libvndk_ext"],
-			nocrt: true,
-		}
-	`)
-
-	testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk2",
-			vendor_available: true,
-			vndk: {
-				enabled: true,
-			},
-			target: {
-				vendor: {
-					shared_libs: ["libvndk_ext"],
-				},
-			},
-			nocrt: true,
-		}
-	`)
-
-	testCcError(t, "dependency \".*\" of \".*\" missing variant", `
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk_sp",
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp_2",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			shared_libs: ["libvndk_sp_ext"],
-			nocrt: true,
-		}
-	`)
-
-	testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp_ext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk_sp",
-			},
-			nocrt: true,
-		}
-
-		cc_library {
-			name: "libvndk_sp2",
-			vendor_available: true,
-			vndk: {
-				enabled: true,
-			},
-			target: {
-				vendor: {
-					shared_libs: ["libvndk_sp_ext"],
-				},
-			},
-			nocrt: true,
-		}
-	`)
-}
-
-func TestEnforceProductVndkVersion(t *testing.T) {
-	t.Parallel()
-	bp := `
-		cc_library {
-			name: "libllndk",
-			llndk: {
-				symbol_file: "libllndk.map.txt",
-			}
-		}
-		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			nocrt: true,
-		}
-		cc_library {
-			name: "libvndk_sp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			nocrt: true,
-		}
-		cc_library {
-			name: "libva",
-			vendor_available: true,
-			nocrt: true,
-		}
-		cc_library {
-			name: "libpa",
-			product_available: true,
-			nocrt: true,
-		}
-		cc_library {
-			name: "libboth_available",
-			vendor_available: true,
-			product_available: true,
-			nocrt: true,
-			srcs: ["foo.c"],
-			target: {
-				vendor: {
-					suffix: "-vendor",
-				},
-				product: {
-					suffix: "-product",
-				},
-			}
-		}
-		cc_library {
-			name: "libproduct_va",
-			product_specific: true,
-			vendor_available: true,
-			nocrt: true,
-		}
-		cc_library {
-			name: "libprod",
-			product_specific: true,
-			shared_libs: [
-				"libllndk",
-				"libvndk",
-				"libvndk_sp",
-				"libpa",
-				"libboth_available",
-				"libproduct_va",
-			],
-			nocrt: true,
-		}
-		cc_library {
-			name: "libvendor",
-			vendor: true,
-			shared_libs: [
-				"libllndk",
-				"libvndk",
-				"libvndk_sp",
-				"libva",
-				"libboth_available",
-				"libproduct_va",
-			],
-			nocrt: true,
-		}
-	`
-
-	ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
-
-	checkVndkModule(t, ctx, "libvndk", "", false, "", productVariant)
-	checkVndkModule(t, ctx, "libvndk_sp", "", true, "", productVariant)
-
-	mod_vendor := ctx.ModuleForTests("libboth_available", vendorVariant).Module().(*Module)
-	assertString(t, mod_vendor.outputFile.Path().Base(), "libboth_available-vendor.so")
-
-	mod_product := ctx.ModuleForTests("libboth_available", productVariant).Module().(*Module)
-	assertString(t, mod_product.outputFile.Path().Base(), "libboth_available-product.so")
-
-	ensureStringContains := func(t *testing.T, str string, substr string) {
-		t.Helper()
-		if !strings.Contains(str, substr) {
-			t.Errorf("%q is not found in %v", substr, str)
-		}
-	}
-	ensureStringNotContains := func(t *testing.T, str string, substr string) {
-		t.Helper()
-		if strings.Contains(str, substr) {
-			t.Errorf("%q is found in %v", substr, str)
-		}
-	}
-
-	// _static variant is used since _shared reuses *.o from the static variant
-	vendor_static := ctx.ModuleForTests("libboth_available", strings.Replace(vendorVariant, "_shared", "_static", 1))
-	product_static := ctx.ModuleForTests("libboth_available", strings.Replace(productVariant, "_shared", "_static", 1))
-
-	vendor_cflags := vendor_static.Rule("cc").Args["cFlags"]
-	ensureStringContains(t, vendor_cflags, "-D__ANDROID_VNDK__")
-	ensureStringContains(t, vendor_cflags, "-D__ANDROID_VENDOR__")
-	ensureStringNotContains(t, vendor_cflags, "-D__ANDROID_PRODUCT__")
-	ensureStringContains(t, vendor_cflags, "-D__ANDROID_VENDOR_API__=202404")
-
-	product_cflags := product_static.Rule("cc").Args["cFlags"]
-	ensureStringContains(t, product_cflags, "-D__ANDROID_VNDK__")
-	ensureStringContains(t, product_cflags, "-D__ANDROID_PRODUCT__")
-	ensureStringNotContains(t, product_cflags, "-D__ANDROID_VENDOR__")
-	ensureStringNotContains(t, product_cflags, "-D__ANDROID_VENDOR_API__=202404")
-}
-
-func TestEnforceProductVndkVersionErrors(t *testing.T) {
-	t.Parallel()
-	testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.29", `
-		cc_library {
-			name: "libprod",
-			product_specific: true,
-			shared_libs: [
-				"libvendor",
-			],
-			nocrt: true,
-		}
-		cc_library {
-			name: "libvendor",
-			vendor: true,
-			nocrt: true,
-		}
-	`)
-	testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.29", `
-		cc_library {
-			name: "libprod",
-			product_specific: true,
-			shared_libs: [
-				"libsystem",
-			],
-			nocrt: true,
-		}
-		cc_library {
-			name: "libsystem",
-			nocrt: true,
-		}
-	`)
-	testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.29", `
-		cc_library {
-			name: "libprod",
-			product_specific: true,
-			shared_libs: [
-				"libva",
-			],
-			nocrt: true,
-		}
-		cc_library {
-			name: "libva",
-			vendor_available: true,
-			nocrt: true,
-		}
-	`)
-	testCcErrorProductVndk(t, "non-VNDK module should not link to \".*\" which has `private: true`", `
-		cc_library {
-			name: "libprod",
-			product_specific: true,
-			shared_libs: [
-				"libvndk_private",
-			],
-			nocrt: true,
-		}
-		cc_library {
-			name: "libvndk_private",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				private: true,
-			},
-			nocrt: true,
-		}
-	`)
-	testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.29", `
-		cc_library {
-			name: "libprod",
-			product_specific: true,
-			shared_libs: [
-				"libsystem_ext",
-			],
-			nocrt: true,
-		}
-		cc_library {
-			name: "libsystem_ext",
-			system_ext_specific: true,
-			nocrt: true,
-		}
-	`)
-	testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:", `
-		cc_library {
-			name: "libsystem",
-			shared_libs: [
-				"libproduct_va",
-			],
-			nocrt: true,
-		}
-		cc_library {
-			name: "libproduct_va",
-			product_specific: true,
-			vendor_available: true,
-			nocrt: true,
-		}
-	`)
-}
-
 func TestMakeLinkType(t *testing.T) {
 	t.Parallel()
 	bp := `
 		cc_library {
-			name: "libvndk",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-		}
-		cc_library {
-			name: "libvndksp",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-		}
-		cc_library {
-			name: "libvndkprivate",
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				private: true,
-			},
-		}
-		cc_library {
 			name: "libvendor",
 			vendor: true,
 		}
-		cc_library {
-			name: "libvndkext",
-			vendor: true,
-			vndk: {
-				enabled: true,
-				extends: "libvndk",
-			},
-		}
 		vndk_prebuilt_shared {
 			name: "prevndk",
 			version: "27",
@@ -2307,40 +634,15 @@
 				private: true,
 			}
 		}
-
 		llndk_libraries_txt {
 			name: "llndk.libraries.txt",
 		}
-		vndkcore_libraries_txt {
-			name: "vndkcore.libraries.txt",
-		}
-		vndksp_libraries_txt {
-			name: "vndksp.libraries.txt",
-		}
-		vndkprivate_libraries_txt {
-			name: "vndkprivate.libraries.txt",
-		}
-		vndkcorevariant_libraries_txt {
-			name: "vndkcorevariant.libraries.txt",
-			insert_vndk_version: false,
-		}
 	`
 
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
 	// native:vndk
 	ctx := testCcWithConfig(t, config)
 
-	checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt",
-		[]string{"libvndk.so", "libvndkprivate.so"})
-	checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt",
-		[]string{"libc++.so", "libvndksp.so"})
-	checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt",
-		[]string{"libc.so", "libdl.so", "libft2.so", "libllndk.so", "libllndkprivate.so", "libm.so"})
-	checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt",
-		[]string{"libft2.so", "libllndkprivate.so", "libvndkprivate.so"})
-
 	vendorVariant27 := "android_vendor.27_arm64_armv8-a_shared"
 
 	tests := []struct {
@@ -2348,15 +650,9 @@
 		name     string
 		expected string
 	}{
-		{vendorVariant, "libvndk", "native:vndk"},
-		{vendorVariant, "libvndksp", "native:vndk"},
-		{vendorVariant, "libvndkprivate", "native:vndk_private"},
 		{vendorVariant, "libvendor", "native:vendor"},
-		{vendorVariant, "libvndkext", "native:vendor"},
 		{vendorVariant, "libllndk", "native:vndk"},
-		{vendorVariant27, "prevndk.vndk.27.arm.binder32", "native:vndk"},
-		{coreVariant, "libvndk", "native:platform"},
-		{coreVariant, "libvndkprivate", "native:platform"},
+		{vendorVariant27, "prevndk.vndk.27.arm.binder32", "native:vendor"},
 		{coreVariant, "libllndk", "native:platform"},
 	}
 	for _, test := range tests {
@@ -2656,6 +952,7 @@
 	cc_library_headers {
 		name: "libexternal_llndk_headers",
 		export_include_dirs: ["include_llndk"],
+		export_system_include_dirs: ["include_system_llndk"],
 		llndk: {
 			symbol_file: "libllndk.map.txt",
 		},
@@ -2671,37 +968,74 @@
 		},
 		export_include_dirs: ["include"],
 	}
+
+	cc_library {
+		name: "libllndk_with_system_headers",
+		llndk: {
+			symbol_file: "libllndk.map.txt",
+			export_llndk_headers: ["libexternal_llndk_headers"],
+			export_headers_as_system: true,
+		},
+		export_include_dirs: ["include"],
+		export_system_include_dirs: ["include_system"],
+	}
 	`)
 	actual := result.ModuleVariantsForTests("libllndk")
 	for i := 0; i < len(actual); i++ {
-		if !strings.HasPrefix(actual[i], "android_vendor.29_") {
+		if !strings.HasPrefix(actual[i], "android_vendor_") {
 			actual = append(actual[:i], actual[i+1:]...)
 			i--
 		}
 	}
 	expected := []string{
-		"android_vendor.29_arm64_armv8-a_shared",
-		"android_vendor.29_arm_armv7-a-neon_shared",
+		"android_vendor_arm64_armv8-a_shared",
+		"android_vendor_arm_armv7-a-neon_shared",
 	}
 	android.AssertArrayString(t, "variants for llndk stubs", expected, actual)
 
-	params := result.ModuleForTests("libllndk", "android_vendor.29_arm_armv7-a-neon_shared").Description("generate stub")
+	params := result.ModuleForTests("libllndk", "android_vendor_arm_armv7-a-neon_shared").Description("generate stub")
 	android.AssertSame(t, "use Vendor API level for default stubs", "202404", params.Args["apiLevel"])
 
-	checkExportedIncludeDirs := func(module, variant string, expectedDirs ...string) {
+	checkExportedIncludeDirs := func(module, variant string, expectedSystemDirs []string, expectedDirs ...string) {
 		t.Helper()
 		m := result.ModuleForTests(module, variant).Module()
 		f, _ := android.SingletonModuleProvider(result, m, FlagExporterInfoProvider)
 		android.AssertPathsRelativeToTopEquals(t, "exported include dirs for "+module+"["+variant+"]",
 			expectedDirs, f.IncludeDirs)
+		android.AssertPathsRelativeToTopEquals(t, "exported include dirs for "+module+"["+variant+"]",
+			expectedSystemDirs, f.SystemIncludeDirs)
 	}
 
-	checkExportedIncludeDirs("libllndk", "android_arm64_armv8-a_shared", "include")
-	checkExportedIncludeDirs("libllndk", "android_vendor.29_arm64_armv8-a_shared", "include")
-	checkExportedIncludeDirs("libllndk_with_external_headers", "android_arm64_armv8-a_shared", "include")
-	checkExportedIncludeDirs("libllndk_with_external_headers", "android_vendor.29_arm64_armv8-a_shared", "include_llndk")
-	checkExportedIncludeDirs("libllndk_with_override_headers", "android_arm64_armv8-a_shared", "include")
-	checkExportedIncludeDirs("libllndk_with_override_headers", "android_vendor.29_arm64_armv8-a_shared", "include_llndk")
+	checkExportedIncludeDirs("libllndk", coreVariant, nil, "include")
+	checkExportedIncludeDirs("libllndk", vendorVariant, nil, "include")
+	checkExportedIncludeDirs("libllndk_with_external_headers", coreVariant, nil, "include")
+	checkExportedIncludeDirs("libllndk_with_external_headers", vendorVariant,
+		[]string{"include_system_llndk"}, "include_llndk")
+	checkExportedIncludeDirs("libllndk_with_override_headers", coreVariant, nil, "include")
+	checkExportedIncludeDirs("libllndk_with_override_headers", vendorVariant, nil, "include_llndk")
+	checkExportedIncludeDirs("libllndk_with_system_headers", coreVariant, []string{"include_system"}, "include")
+	checkExportedIncludeDirs("libllndk_with_system_headers", vendorVariant,
+		[]string{"include_system", "include", "include_system_llndk"}, "include_llndk")
+
+	checkAbiLinkerIncludeDirs := func(module string) {
+		t.Helper()
+		coreModule := result.ModuleForTests(module, coreVariant)
+		abiCheckFlags := ""
+		for _, output := range coreModule.AllOutputs() {
+			if strings.HasSuffix(output, ".so.llndk.lsdump") {
+				abiCheckFlags = coreModule.Output(output).Args["exportedHeaderFlags"]
+			}
+		}
+		vendorModule := result.ModuleForTests(module, vendorVariant).Module()
+		vendorInfo, _ := android.SingletonModuleProvider(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)
+	}
+	checkAbiLinkerIncludeDirs("libllndk")
+	checkAbiLinkerIncludeDirs("libllndk_with_override_headers")
+	checkAbiLinkerIncludeDirs("libllndk_with_external_headers")
+	checkAbiLinkerIncludeDirs("libllndk_with_system_headers")
 }
 
 func TestLlndkHeaders(t *testing.T) {
@@ -2733,7 +1067,7 @@
 	`)
 
 	// _static variant is used since _shared reuses *.o from the static variant
-	cc := ctx.ModuleForTests("libvendor", "android_vendor.29_arm_armv7-a-neon_static").Rule("cc")
+	cc := ctx.ModuleForTests("libvendor", "android_vendor_arm_armv7-a-neon_static").Rule("cc")
 	cflags := cc.Args["cFlags"]
 	if !strings.Contains(cflags, "-Imy_include") {
 		t.Errorf("cflags for libvendor must contain -Imy_include, but was %#v.", cflags)
@@ -2855,7 +1189,7 @@
 
 	// runtime_libs for vendor variants have '.vendor' suffixes if the modules have both core
 	// and vendor variants.
-	variant = "android_vendor.29_arm64_armv8-a_shared"
+	variant = "android_vendor_arm64_armv8-a_shared"
 
 	module = ctx.ModuleForTests("libvendor_available1", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available.vendor"}, module)
@@ -2865,7 +1199,7 @@
 
 	// runtime_libs for product variants have '.product' suffixes if the modules have both core
 	// and product variants.
-	variant = "android_product.29_arm64_armv8-a_shared"
+	variant = "android_product_arm64_armv8-a_shared"
 
 	module = ctx.ModuleForTests("libproduct_available1", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available.product"}, module)
@@ -2882,7 +1216,7 @@
 	module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
 	checkRuntimeLibs(t, []string{"liball_available"}, module)
 
-	variant = "android_vendor.29_arm64_armv8-a_shared"
+	variant = "android_vendor_arm64_armv8-a_shared"
 	module = ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
 	checkRuntimeLibs(t, nil, module)
 }
@@ -3065,17 +1399,12 @@
  `
 
 	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
 
 	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(t, "")
 	if len(outputFiles) != 1 {
 		t.Errorf("expected exactly one output file. output files: [%s]", outputFiles)
 	}
@@ -4571,8 +2900,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
@@ -4783,20 +3110,15 @@
  `
 	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(t, "stripped_all")
 	if !strings.HasSuffix(outputFile.Strings()[0], "/stripped_all/test_lib.so") {
 		t.Errorf("Unexpected output file: %s", outputFile.Strings()[0])
 		return
 	}
 }
 
-// TODO(b/316829758) Remove this test and do not set VNDK version from other tests
-func TestImageVariantsWithoutVndk(t *testing.T) {
+func TestImageVariants(t *testing.T) {
 	t.Parallel()
 
 	bp := `
@@ -4815,7 +3137,7 @@
 	}
 	`
 
-	ctx := prepareForCcTestWithoutVndk.RunTestWithBp(t, bp)
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
 
 	hasDep := func(m android.Module, wantDep android.Module) bool {
 		t.Helper()
@@ -4843,7 +3165,7 @@
 	testDepWithVariant("product")
 }
 
-func TestVendorSdkVersionWithoutVndk(t *testing.T) {
+func TestVendorSdkVersion(t *testing.T) {
 	t.Parallel()
 
 	bp := `
@@ -4861,7 +3183,7 @@
 		}
 	`
 
-	ctx := prepareForCcTestWithoutVndk.RunTestWithBp(t, bp)
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
 	testSdkVersionFlag := func(module, version string) {
 		flags := ctx.ModuleForTests(module, "android_vendor_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
 		android.AssertStringDoesContain(t, "min sdk version", flags, "-target aarch64-linux-android"+version)
@@ -4871,7 +3193,7 @@
 	testSdkVersionFlag("libbar", "29")
 
 	ctx = android.GroupFixturePreparers(
-		prepareForCcTestWithoutVndk,
+		prepareForCcTest,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			if variables.BuildFlags == nil {
 				variables.BuildFlags = make(map[string]string)
@@ -4882,3 +3204,32 @@
 	testSdkVersionFlag("libfoo", "30")
 	testSdkVersionFlag("libbar", "29")
 }
+
+func TestClangVerify(t *testing.T) {
+	t.Parallel()
+
+	ctx := testCc(t, `
+		cc_library {
+			name: "lib_no_clang_verify",
+			srcs: ["libnocv.cc"],
+		}
+
+		cc_library {
+			name: "lib_clang_verify",
+			srcs: ["libcv.cc"],
+			clang_verify: true,
+		}
+	`)
+
+	module := ctx.ModuleForTests("lib_no_clang_verify", "android_arm64_armv8-a_shared")
+
+	cFlags_no_cv := module.Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags_no_cv, "-Xclang") || strings.Contains(cFlags_no_cv, "-verify") {
+		t.Errorf("expected %q not in cflags, got %q", "-Xclang -verify", cFlags_no_cv)
+	}
+
+	cFlags_cv := ctx.ModuleForTests("lib_clang_verify", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags_cv, "-Xclang") && strings.Contains(cFlags_cv, "-verify") {
+		t.Errorf("expected %q in cflags, got %q", "-Xclang -verify", cFlags_cv)
+	}
+}
diff --git a/cc/cc_test_only_property_test.go b/cc/cc_test_only_property_test.go
new file mode 100644
index 0000000..972e86b
--- /dev/null
+++ b/cc/cc_test_only_property_test.go
@@ -0,0 +1,187 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"android/soong/android"
+	"android/soong/android/team_proto"
+	"log"
+	"strings"
+	"testing"
+
+	"github.com/google/blueprint"
+	"google.golang.org/protobuf/proto"
+)
+
+func TestTestOnlyProvider(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			ctx.RegisterModuleType("cc_test_host", TestHostFactory)
+		}),
+	).RunTestWithBp(t, `
+                // These should be test-only
+                cc_fuzz { name: "cc-fuzz" }
+                cc_test { name: "cc-test", gtest:false }
+                cc_benchmark { name: "cc-benchmark" }
+                cc_library { name: "cc-library-forced",
+                             test_only: true }
+                cc_test_library {name: "cc-test-library", gtest: false}
+                cc_test_host {name: "cc-test-host", gtest: false}
+
+                // These should not be.
+                cc_genrule { name: "cc_genrule", cmd: "echo foo", out: ["out"] }
+                cc_library { name: "cc_library" }
+                cc_library { name: "cc_library_false", test_only: false }
+                cc_library_static { name: "cc_static" }
+                cc_library_shared { name: "cc_library_shared" }
+
+                cc_object { name: "cc-object" }
+	`)
+
+	// Visit all modules and ensure only the ones that should
+	// marked as test-only are marked as test-only.
+
+	actualTestOnly := []string{}
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok {
+			if provider.TestOnly {
+				actualTestOnly = append(actualTestOnly, m.Name())
+			}
+		}
+	})
+	expectedTestOnlyModules := []string{
+		"cc-test",
+		"cc-library-forced",
+		"cc-fuzz",
+		"cc-benchmark",
+		"cc-test-library",
+		"cc-test-host",
+	}
+
+	notEqual, left, right := android.ListSetDifference(expectedTestOnlyModules, actualTestOnly)
+	if notEqual {
+		t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
+	}
+}
+
+func TestTestOnlyInTeamsProto(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		android.PrepareForTestWithTeamBuildComponents,
+		prepareForCcTest,
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			ctx.RegisterParallelSingletonType("all_teams", android.AllTeamsFactory)
+			ctx.RegisterModuleType("cc_test_host", TestHostFactory)
+
+		}),
+	).RunTestWithBp(t, `
+                package { default_team: "someteam"}
+
+                // These should be test-only
+                cc_fuzz { name: "cc-fuzz" }
+                cc_test { name: "cc-test", gtest:false }
+                cc_benchmark { name: "cc-benchmark" }
+                cc_library { name: "cc-library-forced",
+                             test_only: true }
+                cc_test_library {name: "cc-test-library", gtest: false}
+                cc_test_host {name: "cc-test-host", gtest: false}
+
+                // These should not be.
+                cc_genrule { name: "cc_genrule", cmd: "echo foo", out: ["out"] }
+                cc_library { name: "cc_library" }
+                cc_library_static { name: "cc_static" }
+                cc_library_shared { name: "cc_library_shared" }
+
+                cc_object { name: "cc-object" }
+		team {
+			name: "someteam",
+			trendy_team_id: "cool_team",
+		}
+	`)
+
+	var teams *team_proto.AllTeams
+	teams = getTeamProtoOutput(t, ctx)
+
+	// map of module name -> trendy team name.
+	actualTrueModules := []string{}
+	for _, teamProto := range teams.Teams {
+		if Bool(teamProto.TestOnly) {
+			actualTrueModules = append(actualTrueModules, teamProto.GetTargetName())
+		}
+	}
+	expectedTestOnlyModules := []string{
+		"cc-test",
+		"cc-library-forced",
+		"cc-fuzz",
+		"cc-benchmark",
+		"cc-test-library",
+		"cc-test-host",
+	}
+
+	notEqual, left, right := android.ListSetDifference(expectedTestOnlyModules, actualTrueModules)
+	if notEqual {
+		t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
+	}
+}
+
+// Don't allow setting test-only on things that are always tests or never tests.
+func TestInvalidTestOnlyTargets(t *testing.T) {
+	testCases := []string{
+		` cc_test {  name: "cc-test", test_only: true, gtest: false, srcs: ["foo.cc"],  } `,
+		` cc_binary {  name: "cc-binary", test_only: true, srcs: ["foo.cc"],  } `,
+		` cc_test_library {name: "cc-test-library", test_only: true, gtest: false} `,
+		` cc_test_host {name: "cc-test-host", test_only: true, gtest: false} `,
+		` cc_defaults {name: "cc-defaults", test_only: true} `,
+	}
+
+	for i, bp := range testCases {
+		ctx := android.GroupFixturePreparers(
+			prepareForCcTest,
+			android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+				ctx.RegisterModuleType("cc_test_host", TestHostFactory)
+			})).
+			ExtendWithErrorHandler(android.FixtureIgnoreErrors).
+			RunTestWithBp(t, bp)
+		if len(ctx.Errs) == 0 {
+			t.Errorf("Expected err setting test_only in testcase #%d", i)
+		}
+		if len(ctx.Errs) > 1 {
+			t.Errorf("Too many errs: [%s] %v", bp, ctx.Errs)
+		}
+
+		if len(ctx.Errs) == 1 {
+			if !strings.Contains(ctx.Errs[0].Error(), "unrecognized property \"test_only\"") {
+				t.Errorf("ERR: %s bad bp: %s", ctx.Errs[0], bp)
+			}
+		}
+	}
+}
+
+func getTeamProtoOutput(t *testing.T, ctx *android.TestResult) *team_proto.AllTeams {
+	teams := new(team_proto.AllTeams)
+	config := ctx.SingletonForTests("all_teams")
+	allOutputs := config.AllOutputs()
+
+	protoPath := allOutputs[0]
+
+	out := config.MaybeOutput(protoPath)
+	outProto := []byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, out))
+	if err := proto.Unmarshal(outProto, teams); err != nil {
+		log.Fatalln("Failed to parse teams proto:", err)
+	}
+	return teams
+}
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 32d1f06..e3af3b2 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -45,7 +45,8 @@
 				ctx.PropertyErrorf(prop, "-Weverything is not allowed in Android.bp files.  "+
 					"Build with `m ANDROID_TEMPORARILY_ALLOW_WEVERYTHING=true` to experiment locally with -Weverything.")
 			}
-		} else if strings.HasPrefix(flag, "-target") || strings.HasPrefix(flag, "--target") {
+		} else if strings.HasPrefix(flag, "-target ") || strings.HasPrefix(flag, "--target ") ||
+			strings.HasPrefix(flag, "-target=") || strings.HasPrefix(flag, "--target=") {
 			ctx.PropertyErrorf(prop, "Bad flag: `%s`, use the correct target soong rule.", flag)
 		} else if strings.Contains(flag, " ") {
 			args := strings.Split(flag, " ")
@@ -63,6 +64,10 @@
 				if len(args) > 2 {
 					ctx.PropertyErrorf(prop, "`-mllvm` only takes one argument: `%s`", flag)
 				}
+			} else if args[0] == "-Xclang" {
+				if len(args) > 2 {
+					ctx.PropertyErrorf(prop, "`-Xclang` only takes one argument: `%s`", flag)
+				}
 			} else if strings.HasPrefix(flag, "-D") && strings.Contains(flag, "=") {
 				// Do nothing in this case.
 				// For now, we allow space characters in -DNAME=def form to allow use cases
diff --git a/cc/cmake_ext_add_aidl_library.txt b/cc/cmake_ext_add_aidl_library.txt
new file mode 100644
index 0000000..d5c134e
--- /dev/null
+++ b/cc/cmake_ext_add_aidl_library.txt
@@ -0,0 +1,77 @@
+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)
+    endif()
+
+    # Strip trailing slash
+    get_filename_component(AIDLROOT_TRAILING "${AIDLROOT}" NAME)
+    if ("${AIDLROOT_TRAILING}" STREQUAL "")
+        get_filename_component(AIDLROOT "${AIDLROOT}foo" DIRECTORY)
+    endif()
+
+    set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/.intermediates/${NAME}-source")
+    set(GEN_SOURCES)
+    foreach (SOURCE ${SOURCES})
+        set(SOURCE_FULL ${AIDLROOT}/${SOURCE})
+        get_filename_component(SOURCE_WLE ${SOURCE} NAME_WLE)
+        get_filename_component(SOURCE_SUBDIR ${SOURCE} DIRECTORY)
+        set(GEN_SOURCE "${GEN_DIR}/${SOURCE_SUBDIR}/${SOURCE_WLE}.cpp")
+
+        file(READ "${SOURCE}" SOURCE_CONTENTS)
+        string(FIND "${SOURCE_CONTENTS}" "@VintfStability" VINTF_MATCH)
+        set(STABILITY_FLAG)
+        if (${VINTF_MATCH} GREATER_EQUAL 0)
+            set(STABILITY_FLAG --stability vintf)
+        endif()
+
+        set(DEPFILE_ARG)
+        if (NOT ${CMAKE_GENERATOR} STREQUAL "Unix Makefiles")
+            set(DEPFILE_ARG DEPFILE "${GEN_SOURCE}.d")
+        endif()
+
+        add_custom_command(
+            OUTPUT "${GEN_SOURCE}"
+            MAIN_DEPENDENCY "${SOURCE_FULL}"
+            ${DEPFILE_ARG}
+            COMMAND "${AIDL_BIN}"
+            ARGS
+            --lang=${LANG}
+            --include="${AIDLROOT}"
+            --dep="${GEN_SOURCE}.d"
+            --out="${GEN_DIR}"
+            --header_out="${GEN_DIR}/include"
+            --ninja
+            --structured
+            --min_sdk_version=current
+            ${STABILITY_FLAG}
+            ${AIDLFLAGS}
+            "${SOURCE_FULL}"
+        )
+        list(APPEND GEN_SOURCES "${GEN_SOURCE}")
+    endforeach()
+
+    add_library(${NAME} ${GEN_SOURCES})
+
+    target_include_directories(${NAME}
+        PUBLIC
+        "${GEN_DIR}/include"
+    )
+
+    if (${LANG} STREQUAL "ndk")
+        set(BINDER_LIB_NAME "libbinder_ndk_sdk")
+    else()
+        set(BINDER_LIB_NAME "libbinder_sdk")
+    endif()
+    target_link_libraries(${NAME}
+        ${BINDER_LIB_NAME}
+    )
+endfunction()
diff --git a/cc/cmake_ext_append_flags.txt b/cc/cmake_ext_append_flags.txt
new file mode 100644
index 0000000..2cfb1ac
--- /dev/null
+++ b/cc/cmake_ext_append_flags.txt
@@ -0,0 +1,10 @@
+include(CheckCXXCompilerFlag)
+
+macro(append_cxx_flags_if_supported VAR)
+  foreach(FLAG ${ARGN})
+    check_cxx_compiler_flag(${FLAG} HAS_FLAG${FLAG})
+    if(${HAS_FLAG${FLAG}})
+      list(APPEND ${VAR} ${FLAG})
+    endif()
+  endforeach()
+endmacro()
diff --git a/cc/cmake_main.txt b/cc/cmake_main.txt
new file mode 100644
index 0000000..eeabf53
--- /dev/null
+++ b/cc/cmake_main.txt
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.18)
+project(<<.M.Name>> CXX)
+set(CMAKE_CXX_STANDARD 20)
+enable_testing()
+
+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()
+
+<<cflagsList .M.Name "_CFLAGS" .M.Properties.Cflags .M.Properties.Unportable_flags .M.Properties.Cflags_ignored>>
+
+<<range .Pprop.SystemPackages ->>
+find_package(<<.>> REQUIRED)
+<<end >>
+<<range .Pprop.PregeneratedPackages ->>
+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_aidl.txt b/cc/cmake_module_aidl.txt
new file mode 100644
index 0000000..84755a3
--- /dev/null
+++ b/cc/cmake_module_aidl.txt
@@ -0,0 +1,11 @@
+# <<.M.Name>>
+
+<<setList .M.Name "_SRCS" "" (getAidlSources .M)>>
+
+<<setList .M.Name "_AIDLFLAGS" "" (getCompilerProperties .M).AidlInterface.Flags>>
+
+add_aidl_library(<<.M.Name>> <<(getCompilerProperties .M).AidlInterface.Lang>>
+    "${ANDROID_BUILD_TOP}/<<.Ctx.OtherModuleDir .M>>/<<(getCompilerProperties .M).AidlInterface.AidlRoot>>"
+    "${<<.M.Name>>_SRCS}"
+    "${<<.M.Name>>_AIDLFLAGS}")
+add_library(android::<<.M.Name>> ALIAS <<.M.Name>>)
diff --git a/cc/cmake_module_cc.txt b/cc/cmake_module_cc.txt
new file mode 100644
index 0000000..0dc45ae
--- /dev/null
+++ b/cc/cmake_module_cc.txt
@@ -0,0 +1,44 @@
+<<$srcs := getSources .M>>
+<<$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
+(getExtraLibs .M)
+) .Pprop.LibraryMapping>>
+<<$moduleType := getModuleType .M>>
+<<$moduleTypeCmake := "executable">>
+<<if eq $moduleType "library">>
+<<$moduleTypeCmake = "library">>
+<<end>>
+
+# <<.M.Name>>
+<<if $srcs>>
+<<setList .M.Name "_SRCS" "${ANDROID_BUILD_TOP}/" (toStrings $srcs)>>
+add_<<$moduleTypeCmake>>(<<.M.Name>> ${<<.M.Name>>_SRCS})
+<<- else>>
+add_<<$moduleTypeCmake>>(<<.M.Name>> INTERFACE)
+<<- end>>
+<<- if eq $moduleType "library">>
+add_library(android::<<.M.Name>> ALIAS <<.M.Name>>)
+<<- else if eq $moduleType "test">>
+add_test(NAME <<.M.Name>> COMMAND <<.M.Name>>)
+<<- end>>
+<<print "">>
+
+<<- if $includeDirs>>
+<<setList .M.Name "_INCLUDES" "${ANDROID_BUILD_TOP}/" $includeDirs>>
+target_include_directories(<<.M.Name>> <<if $srcs>>PUBLIC<<else>>INTERFACE<<end>> ${<<.M.Name>>_INCLUDES})
+<<end>>
+
+<<- if and $srcs $cflags>>
+<<cflagsList .M.Name "_CFLAGS" $cflags .Snapshot.Properties.Unportable_flags .Snapshot.Properties.Cflags_ignored>>
+target_compile_options(<<.M.Name>> PRIVATE ${<<.M.Name>>_CFLAGS})
+<<end>>
+
+<<- if $deps>>
+<<setList .M.Name "_DEPENDENCIES" "" $deps>>
+target_link_libraries(<<.M.Name>> <<if not $srcs>>INTERFACE <<end ->> ${<<.M.Name>>_DEPENDENCIES})
+<<end>>
diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go
new file mode 100644
index 0000000..fb2924a
--- /dev/null
+++ b/cc/cmake_snapshot.go
@@ -0,0 +1,573 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"bytes"
+	_ "embed"
+	"fmt"
+	"path/filepath"
+	"slices"
+	"sort"
+	"strings"
+	"text/template"
+
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+const veryVerbose bool = false
+
+//go:embed cmake_main.txt
+var templateCmakeMainRaw string
+var templateCmakeMain *template.Template = parseTemplate(templateCmakeMainRaw)
+
+//go:embed cmake_module_cc.txt
+var templateCmakeModuleCcRaw string
+var templateCmakeModuleCc *template.Template = parseTemplate(templateCmakeModuleCcRaw)
+
+//go:embed cmake_module_aidl.txt
+var templateCmakeModuleAidlRaw string
+var templateCmakeModuleAidl *template.Template = parseTemplate(templateCmakeModuleAidlRaw)
+
+//go:embed cmake_ext_add_aidl_library.txt
+var cmakeExtAddAidlLibrary string
+
+//go:embed cmake_ext_append_flags.txt
+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",
+	"-Wno-restrict",
+	"-Wno-stringop-overread",
+	"-Wno-subobject-linkage",
+}
+
+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",
+}
+
+// Mapping entry between Android's library name and the one used when building outside Android tree.
+type LibraryMappingProperty struct {
+	// Android library name.
+	Android_name string
+
+	// Library name used when building outside Android.
+	Mapped_name string
+
+	// If the make file is already present in Android source tree, specify its location.
+	Package_pregenerated string
+
+	// If the package is expected to be installed on the build host OS, specify its name.
+	Package_system string
+}
+
+type CmakeSnapshotProperties struct {
+	// 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
+
+	// Global cflags to add when building outside Android.
+	Cflags []string
+
+	// Flags to skip when building outside Android.
+	Cflags_ignored []string
+
+	// Mapping between library names used in Android tree and externally.
+	Library_mapping []LibraryMappingProperty
+
+	// List of cflags that are not portable between compilers that could potentially be used to
+	// build a generated package. If left empty, it's initialized with a default list.
+	Unportable_flags []string
+
+	// Whether to include source code as part of the snapshot package.
+	Include_sources bool
+}
+
+var cmakeSnapshotSourcesProvider = blueprint.NewProvider[android.Paths]()
+
+type CmakeSnapshot struct {
+	android.ModuleBase
+
+	Properties CmakeSnapshotProperties
+
+	zipPath android.WritablePath
+}
+
+type cmakeProcessedProperties struct {
+	LibraryMapping       map[string]LibraryMappingProperty
+	PregeneratedPackages []string
+	SystemPackages       []string
+}
+
+type cmakeSnapshotDependencyTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
+var (
+	cmakeSnapshotModuleTag   = cmakeSnapshotDependencyTag{name: "cmake-snapshot-module"}
+	cmakeSnapshotPrebuiltTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-prebuilt"}
+)
+
+func parseTemplate(templateContents string) *template.Template {
+	funcMap := template.FuncMap{
+		"setList": func(name string, nameSuffix string, itemPrefix string, items []string) string {
+			var list strings.Builder
+			list.WriteString("set(" + name + nameSuffix)
+			templateListBuilder(&list, itemPrefix, items)
+			return list.String()
+		},
+		"toStrings": func(files android.Paths) []string {
+			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...)
+		},
+		"cflagsList": func(name string, nameSuffix string, flags []string,
+			unportableFlags []string, ignoredFlags []string) string {
+			if len(unportableFlags) == 0 {
+				unportableFlags = defaultUnportableFlags
+			}
+
+			var filteredPortable []string
+			var filteredUnportable []string
+			for _, flag := range flags {
+				if slices.Contains(ignoredFlags, flag) {
+					continue
+				} else if slices.Contains(unportableFlags, flag) {
+					filteredUnportable = append(filteredUnportable, flag)
+				} else {
+					filteredPortable = append(filteredPortable, flag)
+				}
+			}
+
+			var list strings.Builder
+
+			list.WriteString("set(" + name + nameSuffix)
+			templateListBuilder(&list, "", filteredPortable)
+
+			if len(filteredUnportable) > 0 {
+				list.WriteString("\nappend_cxx_flags_if_supported(" + name + nameSuffix)
+				templateListBuilder(&list, "", filteredUnportable)
+			}
+
+			return list.String()
+		},
+		"getSources": func(m *Module) android.Paths {
+			return m.compiler.(CompiledInterface).Srcs()
+		},
+		"getModuleType": getModuleType,
+		"getCompilerProperties": func(m *Module) BaseCompilerProperties {
+			return m.compiler.baseCompilerProps()
+		},
+		"getCflagsProperty": func(ctx android.ModuleContext, m *Module) []string {
+			cflags := m.compiler.baseCompilerProps().Cflags
+			return cflags.GetOrDefault(ctx, nil)
+		},
+		"getLinkerProperties": func(m *Module) BaseLinkerProperties {
+			return m.linker.baseLinkerProps()
+		},
+		"getExtraLibs":   getExtraLibs,
+		"getIncludeDirs": getIncludeDirs,
+		"mapLibraries": func(ctx android.ModuleContext, m *Module, libs []string, mapping map[string]LibraryMappingProperty) []string {
+			var mappedLibs []string
+			for _, lib := range libs {
+				mappedLib, exists := mapping[lib]
+				if exists {
+					lib = mappedLib.Mapped_name
+				} else {
+					if !ctx.OtherModuleExists(lib) {
+						ctx.OtherModuleErrorf(m, "Dependency %s doesn't exist", lib)
+					}
+					lib = "android::" + lib
+				}
+				if lib == "" {
+					continue
+				}
+				mappedLibs = append(mappedLibs, lib)
+			}
+			sort.Strings(mappedLibs)
+			mappedLibs = slices.Compact(mappedLibs)
+			return mappedLibs
+		},
+		"getAidlSources": func(m *Module) []string {
+			aidlInterface := m.compiler.baseCompilerProps().AidlInterface
+			aidlRoot := aidlInterface.AidlRoot + string(filepath.Separator)
+			if aidlInterface.AidlRoot == "" {
+				aidlRoot = ""
+			}
+			var sources []string
+			for _, src := range aidlInterface.Sources {
+				if !strings.HasPrefix(src, aidlRoot) {
+					panic(fmt.Sprintf("Aidl source '%v' doesn't start with '%v'", src, aidlRoot))
+				}
+				sources = append(sources, src[len(aidlRoot):])
+			}
+			return sources
+		},
+	}
+
+	return template.Must(template.New("").Delims("<<", ">>").Funcs(funcMap).Parse(templateContents))
+}
+
+func sliceWithPrefix(prefix string, slice []string) []string {
+	output := make([]string, len(slice))
+	for i, elem := range slice {
+		output[i] = prefix + elem
+	}
+	return output
+}
+
+func templateListBuilder(builder *strings.Builder, itemPrefix string, items []string) {
+	if len(items) > 0 {
+		builder.WriteString("\n")
+		for _, item := range items {
+			builder.WriteString("    " + itemPrefix + item + "\n")
+		}
+	}
+	builder.WriteString(")")
+}
+
+func executeTemplate(templ *template.Template, buffer *bytes.Buffer, data any) string {
+	buffer.Reset()
+	if err := templ.Execute(buffer, data); err != nil {
+		panic(err)
+	}
+	output := strings.TrimSpace(buffer.String())
+	buffer.Reset()
+	return output
+}
+
+func (m *CmakeSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
+	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...)
+	}
+}
+
+func (m *CmakeSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var templateBuffer bytes.Buffer
+	var pprop cmakeProcessedProperties
+	m.zipPath = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
+
+	// Process Library_mapping for more efficient lookups
+	pprop.LibraryMapping = map[string]LibraryMappingProperty{}
+	for _, elem := range m.Properties.Library_mapping {
+		pprop.LibraryMapping[elem.Android_name] = elem
+
+		if elem.Package_pregenerated != "" {
+			pprop.PregeneratedPackages = append(pprop.PregeneratedPackages, elem.Package_pregenerated)
+		}
+		sort.Strings(pprop.PregeneratedPackages)
+		pprop.PregeneratedPackages = slices.Compact(pprop.PregeneratedPackages)
+
+		if elem.Package_system != "" {
+			pprop.SystemPackages = append(pprop.SystemPackages, elem.Package_system)
+		}
+		sort.Strings(pprop.SystemPackages)
+		pprop.SystemPackages = slices.Compact(pprop.SystemPackages)
+	}
+
+	// Generating CMakeLists.txt rules for all modules in dependency tree
+	moduleDirs := map[string][]string{}
+	sourceFiles := map[string]android.Path{}
+	visitedModules := map[string]bool{}
+	var pregeneratedModules []*Module
+	ctx.WalkDeps(func(dep_a android.Module, parent android.Module) bool {
+		moduleName := ctx.OtherModuleName(dep_a)
+		if visited := visitedModules[moduleName]; visited {
+			return false // visit only once
+		}
+		visitedModules[moduleName] = true
+		dep, ok := dep_a.(*Module)
+		if !ok {
+			return false // not a cc module
+		}
+		if mapping, ok := pprop.LibraryMapping[moduleName]; ok {
+			if mapping.Package_pregenerated != "" {
+				pregeneratedModules = append(pregeneratedModules, dep)
+			}
+			return false // mapped to system or pregenerated (we'll handle these later)
+		}
+		if ctx.OtherModuleDependencyTag(dep) == cmakeSnapshotPrebuiltTag {
+			return false // we'll handle cmakeSnapshotPrebuiltTag later
+		}
+		if slices.Contains(ignoredSystemLibs, moduleName) {
+			return false // system libs built in-tree for Android
+		}
+		if dep.compiler == nil {
+			return false // unsupported module type (e.g. prebuilt)
+		}
+		isAidlModule := dep.compiler.baseCompilerProps().AidlInterface.Lang != ""
+
+		if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
+			ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
+				"CMake snapshots not supported, despite being a dependency for %s",
+				ctx.OtherModuleName(parent))
+			return false
+		}
+
+		if veryVerbose {
+			fmt.Println("WalkDeps: " + ctx.OtherModuleName(parent) + " -> " + moduleName)
+		}
+
+		// Generate CMakeLists.txt fragment for this module
+		templateToUse := templateCmakeModuleCc
+		if isAidlModule {
+			templateToUse = templateCmakeModuleAidl
+		}
+		moduleFragment := executeTemplate(templateToUse, &templateBuffer, struct {
+			Ctx      *android.ModuleContext
+			M        *Module
+			Snapshot *CmakeSnapshot
+			Pprop    *cmakeProcessedProperties
+		}{
+			&ctx,
+			dep,
+			m,
+			&pprop,
+		})
+		moduleDir := ctx.OtherModuleDir(dep)
+		moduleDirs[moduleDir] = append(moduleDirs[moduleDir], moduleFragment)
+
+		if m.Properties.Include_sources {
+			files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
+			for _, file := range files {
+				sourceFiles[file.String()] = file
+			}
+		}
+
+		// if it's AIDL module, no need to dive into their dependencies
+		return !isAidlModule
+	})
+
+	// Enumerate sources for pregenerated modules
+	if m.Properties.Include_sources {
+		for _, dep := range pregeneratedModules {
+			if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
+				ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
+					"Pregenerated CMake snapshots not supported, despite being requested for %s",
+					ctx.ModuleName())
+				continue
+			}
+
+			files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
+			for _, file := range files {
+				sourceFiles[file.String()] = file
+			}
+		}
+	}
+
+	// Merging CMakeLists.txt contents for every module directory
+	var makefilesList android.Paths
+	for _, moduleDir := range android.SortedKeys(moduleDirs) {
+		fragments := moduleDirs[moduleDir]
+		moduleCmakePath := android.PathForModuleGen(ctx, moduleDir, "CMakeLists.txt")
+		makefilesList = append(makefilesList, moduleCmakePath)
+		sort.Strings(fragments)
+		android.WriteFileRule(ctx, moduleCmakePath, strings.Join(fragments, "\n\n\n"))
+	}
+
+	// Generating top-level CMakeLists.txt
+	mainCmakePath := android.PathForModuleGen(ctx, "CMakeLists.txt")
+	makefilesList = append(makefilesList, mainCmakePath)
+	mainContents := executeTemplate(templateCmakeMain, &templateBuffer, struct {
+		Ctx        *android.ModuleContext
+		M          *CmakeSnapshot
+		ModuleDirs map[string][]string
+		Pprop      *cmakeProcessedProperties
+	}{
+		&ctx,
+		m,
+		moduleDirs,
+		&pprop,
+	})
+	android.WriteFileRule(ctx, mainCmakePath, mainContents)
+
+	// Generating CMake extensions
+	extPath := android.PathForModuleGen(ctx, "cmake", "AppendCxxFlagsIfSupported.cmake")
+	makefilesList = append(makefilesList, extPath)
+	android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAppendFlags)
+	extPath = android.PathForModuleGen(ctx, "cmake", "AddAidlLibrary.cmake")
+	makefilesList = append(makefilesList, extPath)
+	android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAddAidlLibrary)
+
+	// Generating the final zip file
+	zipRule := android.NewRuleBuilder(pctx, ctx)
+	zipCmd := zipRule.Command().
+		BuiltTool("soong_zip").
+		FlagWithOutput("-o ", m.zipPath)
+
+	// Packaging all sources into the zip file
+	if m.Properties.Include_sources {
+		var sourcesList android.Paths
+		for _, file := range android.SortedKeys(sourceFiles) {
+			path := sourceFiles[file]
+			sourcesList = append(sourcesList, path)
+		}
+
+		sourcesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_sources.rsp")
+		zipCmd.FlagWithRspFileInputList("-r ", sourcesRspFile, sourcesList)
+	}
+
+	// Packaging all make files into the zip file
+	makefilesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_makefiles.rsp")
+	zipCmd.
+		FlagWithArg("-C ", android.PathForModuleGen(ctx).OutputPath.String()).
+		FlagWithRspFileInputList("-r ", makefilesRspFile, makefilesList)
+
+	// Packaging all prebuilts into the zip file
+	if len(m.Properties.Prebuilts) > 0 {
+		var prebuiltsList android.Paths
+
+		ctx.VisitDirectDepsWithTag(cmakeSnapshotPrebuiltTag, func(dep android.Module) {
+			for _, file := range dep.FilesToInstall() {
+				prebuiltsList = append(prebuiltsList, file)
+			}
+		})
+
+		prebuiltsRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_prebuilts.rsp")
+		zipCmd.
+			FlagWithArg("-C ", android.PathForArbitraryOutput(ctx).String()).
+			FlagWithArg("-P ", "prebuilts").
+			FlagWithRspFileInputList("-r ", prebuiltsRspFile, prebuiltsList)
+	}
+
+	// Finish generating the final zip file
+	zipRule.Build(m.zipPath.String(), "archiving "+ctx.ModuleName())
+
+	ctx.SetOutputFiles(android.Paths{m.zipPath}, "")
+}
+
+func (m *CmakeSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "DATA",
+		OutputFile: android.OptionalPathForPath(m.zipPath),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+			},
+		},
+	}}
+}
+
+func getModuleType(m *Module) string {
+	switch m.linker.(type) {
+	case *binaryDecorator:
+		return "executable"
+	case *libraryDecorator:
+		return "library"
+	case *testBinary:
+		return "test"
+	case *benchmarkDecorator:
+		return "test"
+	}
+	panic(fmt.Sprintf("Unexpected module type: %T", m.linker))
+}
+
+func getExtraLibs(m *Module) []string {
+	switch decorator := m.linker.(type) {
+	case *testBinary:
+		if decorator.testDecorator.gtest() {
+			return []string{
+				"libgtest",
+				"libgtest_main",
+			}
+		}
+	case *benchmarkDecorator:
+		return []string{"libgoogle-benchmark"}
+	}
+	return nil
+}
+
+func getIncludeDirs(ctx android.ModuleContext, m *Module) []string {
+	moduleDir := ctx.OtherModuleDir(m) + string(filepath.Separator)
+	switch decorator := m.compiler.(type) {
+	case *libraryDecorator:
+		return sliceWithPrefix(moduleDir, decorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil))
+	}
+	return nil
+}
+
+func cmakeSnapshotLoadHook(ctx android.LoadHookContext) {
+	props := struct {
+		Target struct {
+			Darwin struct {
+				Enabled *bool
+			}
+			Windows struct {
+				Enabled *bool
+			}
+		}
+	}{}
+	props.Target.Darwin.Enabled = proptools.BoolPtr(false)
+	props.Target.Windows.Enabled = proptools.BoolPtr(false)
+	ctx.AppendProperties(&props)
+}
+
+// cmake_snapshot allows defining source packages for release outside of Android build tree.
+// As a result of cmake_snapshot module build, a zip file is generated with CMake build definitions
+// for selected source modules, their dependencies and optionally also the source code itself.
+func CmakeSnapshotFactory() android.Module {
+	module := &CmakeSnapshot{}
+	module.AddProperties(&module.Properties)
+	android.AddLoadHook(module, cmakeSnapshotLoadHook)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
+func init() {
+	android.InitRegistrationContext.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory)
+}
diff --git a/cc/cmake_snapshot_test.go b/cc/cmake_snapshot_test.go
new file mode 100644
index 0000000..b6f4369
--- /dev/null
+++ b/cc/cmake_snapshot_test.go
@@ -0,0 +1,117 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"runtime"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func wasGenerated(t *testing.T, m *android.TestingModule, fileName string, ruleType string) {
+	t.Helper()
+	ruleName := "<nil>"
+	if rule := m.MaybeOutput(fileName).Rule; rule != nil {
+		ruleName = rule.String()
+	}
+	if !strings.HasSuffix(ruleName, ruleType) {
+		t.Errorf("Main Cmake file wasn't generated properly, expected rule %v, found %v", ruleType, ruleName)
+	}
+}
+
+func TestEmptyCmakeSnapshot(t *testing.T) {
+	t.Parallel()
+	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+		cc_cmake_snapshot {
+			name: "foo",
+			modules_host: [],
+			modules_system: [],
+			modules_vendor: [],
+			prebuilts: ["libc++"],
+			include_sources: true,
+		}`)
+
+	if runtime.GOOS != "linux" {
+		t.Skip("CMake snapshots are only supported on Linux")
+	}
+
+	snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64")
+
+	wasGenerated(t, &snapshotModule, "CMakeLists.txt", "rawFileCopy")
+	wasGenerated(t, &snapshotModule, "foo.zip", "")
+}
+
+func TestCmakeSnapshotWithBinary(t *testing.T) {
+	t.Parallel()
+	xtra := android.FixtureAddTextFile("some/module/Android.bp", `
+		cc_binary {
+			name: "foo_binary",
+			host_supported: true,
+			cmake_snapshot_supported: true,
+		}
+	`)
+	result := android.GroupFixturePreparers(PrepareForIntegrationTestWithCc, xtra).RunTestWithBp(t, `
+		cc_cmake_snapshot {
+			name: "foo",
+			modules_system: [
+				"foo_binary",
+			],
+			include_sources: true,
+		}`)
+
+	if runtime.GOOS != "linux" {
+		t.Skip("CMake snapshots are only supported on Linux")
+	}
+
+	snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64")
+
+	wasGenerated(t, &snapshotModule, "some/module/CMakeLists.txt", "rawFileCopy")
+}
+
+func TestCmakeSnapshotAsTestData(t *testing.T) {
+	t.Parallel()
+	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+		cc_test {
+			name: "foo_test",
+			gtest: false,
+			srcs: [
+				"foo_test.c",
+			],
+			data: [
+				":foo",
+			],
+			target: {
+				android: {enabled: false},
+			},
+		}
+
+		cc_cmake_snapshot {
+			name: "foo",
+			modules_system: [],
+			prebuilts: ["libc++"],
+			include_sources: true,
+		}`)
+
+	if runtime.GOOS != "linux" {
+		t.Skip("CMake snapshots are only supported on Linux")
+	}
+
+	snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64")
+
+	wasGenerated(t, &snapshotModule, "CMakeLists.txt", "rawFileCopy")
+	wasGenerated(t, &snapshotModule, "foo.zip", "")
+}
diff --git a/cc/compdb.go b/cc/compdb.go
index 617be1a..da28183 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -164,6 +164,7 @@
 		args = append(args, expandAllVars(ctx, ccModule.flags.Local.ConlyFlags)...)
 	}
 	args = append(args, expandAllVars(ctx, ccModule.flags.SystemIncludeFlags)...)
+	args = append(args, expandAllVars(ctx, ccModule.flags.NoOverrideFlags)...)
 	args = append(args, src.String())
 	return args
 }
diff --git a/cc/compiler.go b/cc/compiler.go
index de1ae71..03f9899 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -50,7 +50,7 @@
 	Exclude_srcs []string `android:"path,arch_variant"`
 
 	// list of module-specific flags that will be used for C and C++ compiles.
-	Cflags []string `android:"arch_variant"`
+	Cflags proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of module-specific flags that will be used for C++ compiles
 	Cppflags []string `android:"arch_variant"`
@@ -98,10 +98,10 @@
 
 	// list of generated headers to add to the include path. These are the names
 	// of genrule modules.
-	Generated_headers []string `android:"arch_variant,variant_prepend"`
+	Generated_headers proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// pass -frtti instead of -fno-rtti
-	Rtti *bool
+	Rtti *bool `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),
@@ -120,6 +120,10 @@
 	// ban targeting bpf in cc rules instead use bpf_rules. (b/323415017)
 	Bpf_target *bool
 
+	// Add "-Xclang -verify" to the cflags and appends "touch $out" to
+	// the clang command line.
+	Clang_verify bool
+
 	Yacc *YaccProperties
 	Lex  *LexProperties
 
@@ -141,6 +145,22 @@
 		Flags []string
 	}
 
+	// Populated by aidl_interface CPP backend to let other modules (e.g. cc_cmake_snapshot)
+	// access actual source files and not generated cpp intermediary sources.
+	AidlInterface struct {
+		// list of aidl_interface sources
+		Sources []string `blueprint:"mutated"`
+
+		// root directory of AIDL sources
+		AidlRoot string `blueprint:"mutated"`
+
+		// AIDL backend language (e.g. "cpp", "ndk")
+		Lang string `blueprint:"mutated"`
+
+		// list of flags passed to AIDL generator
+		Flags []string `blueprint:"mutated"`
+	} `blueprint:"mutated"`
+
 	Renderscript struct {
 		// list of directories that will be added to the llvm-rs-cc include paths
 		Include_dirs []string
@@ -152,12 +172,6 @@
 		Target_api *string
 	}
 
-	Debug, Release struct {
-		// list of module-specific flags that will be used for C and C++ compiles in debug or
-		// release builds
-		Cflags []string `android:"arch_variant"`
-	} `android:"arch_variant"`
-
 	Target struct {
 		Vendor, Product struct {
 			// list of source files that should only be used in vendor or
@@ -254,7 +268,7 @@
 }
 
 func (compiler *baseCompiler) appendCflags(flags []string) {
-	compiler.Properties.Cflags = append(compiler.Properties.Cflags, flags...)
+	compiler.Properties.Cflags.AppendSimpleValue(flags)
 }
 
 func (compiler *baseCompiler) appendAsflags(flags []string) {
@@ -265,6 +279,10 @@
 	return []interface{}{&compiler.Properties, &compiler.Proto}
 }
 
+func (compiler *baseCompiler) baseCompilerProps() BaseCompilerProperties {
+	return compiler.Properties
+}
+
 func includeBuildDirectory(prop *bool) bool {
 	return proptools.BoolDefault(prop, true)
 }
@@ -278,7 +296,7 @@
 func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.GeneratedSources = append(deps.GeneratedSources, compiler.Properties.Generated_sources...)
 	deps.GeneratedSources = removeListFromList(deps.GeneratedSources, compiler.Properties.Exclude_generated_sources)
-	deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers...)
+	deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers.GetOrDefault(ctx, nil)...)
 	deps.AidlLibs = append(deps.AidlLibs, compiler.Properties.Aidl.Libs...)
 
 	android.ProtoDeps(ctx, &compiler.Proto)
@@ -348,7 +366,8 @@
 	compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, compiler.Properties.Srcs, compiler.Properties.Exclude_srcs)
 	compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...)
 
-	CheckBadCompilerFlags(ctx, "cflags", compiler.Properties.Cflags)
+	cflags := compiler.Properties.Cflags.GetOrDefault(ctx, nil)
+	CheckBadCompilerFlags(ctx, "cflags", cflags)
 	CheckBadCompilerFlags(ctx, "cppflags", compiler.Properties.Cppflags)
 	CheckBadCompilerFlags(ctx, "conlyflags", compiler.Properties.Conlyflags)
 	CheckBadCompilerFlags(ctx, "asflags", compiler.Properties.Asflags)
@@ -361,7 +380,7 @@
 
 	esc := proptools.NinjaAndShellEscapeList
 
-	flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Cflags)...)
+	flags.Local.CFlags = append(flags.Local.CFlags, esc(cflags)...)
 	flags.Local.CppFlags = append(flags.Local.CppFlags, esc(compiler.Properties.Cppflags)...)
 	flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, esc(compiler.Properties.Conlyflags)...)
 	flags.Local.AsFlags = append(flags.Local.AsFlags, esc(compiler.Properties.Asflags)...)
@@ -370,6 +389,11 @@
 	flags.Yacc = compiler.Properties.Yacc
 	flags.Lex = compiler.Properties.Lex
 
+	flags.ClangVerify = compiler.Properties.Clang_verify
+	if compiler.Properties.Clang_verify {
+		flags.Local.CFlags = append(flags.Local.CFlags, "-Xclang", "-verify")
+	}
+
 	// Include dir cflags
 	localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs)
 	if len(localIncludeDirs) > 0 {
@@ -449,11 +473,6 @@
 		ctx.ModuleErrorf("%s", err)
 	}
 
-	CheckBadCompilerFlags(ctx, "release.cflags", compiler.Properties.Release.Cflags)
-
-	// TODO: debug
-	flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Release.Cflags)...)
-
 	if !ctx.DeviceConfig().BuildBrokenClangCFlags() && len(compiler.Properties.Clang_cflags) != 0 {
 		ctx.PropertyErrorf("clang_cflags", "property is deprecated, see Changes.md file")
 	} else {
@@ -520,12 +539,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}")
@@ -539,7 +556,6 @@
 		flags.Global.CommonFlags = append(flags.Global.CommonFlags, tc.ToolchainCflags())
 	}
 
-
 	cStd := parseCStd(compiler.Properties.C_std)
 	cppStd := parseCppStd(compiler.Properties.Cpp_std)
 
@@ -665,12 +681,27 @@
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fopenmp")
 	}
 
+	if ctx.optimizeForSize() {
+		flags.Local.CFlags = append(flags.Local.CFlags, "-Oz")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-enable-ml-inliner=release")
+	}
+
 	// Exclude directories from manual binder interface allowed list.
 	//TODO(b/145621474): Move this check into IInterface.h when clang-tidy no longer uses absolute paths.
 	if android.HasAnyPrefix(ctx.ModuleDir(), allowedManualInterfacePaths) {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-DDO_NOT_CHECK_MANUAL_BINDER_INTERFACES")
 	}
 
+	flags.NoOverrideFlags = append(flags.NoOverrideFlags, "${config.NoOverrideGlobalCflags}")
+
+	if flags.Toolchain.Is64Bit() {
+		flags.NoOverrideFlags = append(flags.NoOverrideFlags, "${config.NoOverride64GlobalCflags}")
+	}
+
+	if android.IsThirdPartyPath(ctx.ModuleDir()) {
+		flags.NoOverrideFlags = append(flags.NoOverrideFlags, "${config.NoOverrideExternalGlobalCflags}")
+	}
+
 	return flags
 }
 
@@ -773,7 +804,7 @@
 	Header_libs []string `android:"arch_variant,variant_prepend"`
 
 	// list of clang flags required to correctly interpret the headers.
-	Cflags []string `android:"arch_variant"`
+	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.
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index fdc94ad..289409f 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -15,7 +15,6 @@
         "global.go",
         "tidy.go",
         "toolchain.go",
-        "vndk.go",
 
         "bionic.go",
 
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 0de9e05..beb68e1 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -49,7 +49,6 @@
 	}
 
 	arm64Ldflags = []string{
-		"-Wl,--hash-style=gnu",
 		"-Wl,-z,separate-code",
 		"-Wl,-z,separate-loadable-segments",
 	}
@@ -92,43 +91,39 @@
 )
 
 func init() {
-	exportedVars.ExportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
+	pctx.StaticVariable("Arm64Ldflags", strings.Join(arm64Ldflags, " "))
 
-	exportedVars.ExportStringList("Arm64Lldflags", arm64Lldflags)
 	pctx.VariableFunc("Arm64Lldflags", func(ctx android.PackageVarContext) string {
 		maxPageSizeFlag := "-Wl,-z,max-page-size=" + ctx.Config().MaxPageSizeSupported()
 		flags := append(arm64Lldflags, maxPageSizeFlag)
 		return strings.Join(flags, " ")
 	})
 
-	exportedVars.ExportStringList("Arm64Cflags", arm64Cflags)
 	pctx.VariableFunc("Arm64Cflags", func(ctx android.PackageVarContext) string {
 		flags := arm64Cflags
 		if ctx.Config().NoBionicPageSizeMacro() {
 			flags = append(flags, "-D__BIONIC_NO_PAGE_SIZE_MACRO")
+		} else {
+			flags = append(flags, "-D__BIONIC_DEPRECATED_PAGE_SIZE_MACRO")
 		}
 		return strings.Join(flags, " ")
 	})
 
-	exportedVars.ExportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
+	pctx.StaticVariable("Arm64Cppflags", strings.Join(arm64Cppflags, " "))
 
-	exportedVars.ExportVariableReferenceDict("Arm64ArchVariantCflags", arm64ArchVariantCflagsVar)
-	exportedVars.ExportVariableReferenceDict("Arm64CpuVariantCflags", arm64CpuVariantCflagsVar)
-	exportedVars.ExportVariableReferenceDict("Arm64CpuVariantLdflags", arm64CpuVariantLdflags)
+	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"], " "))
 
-	exportedVars.ExportStringListStaticVariable("Arm64Armv8ACflags", arm64ArchVariantCflags["armv8-a"])
-	exportedVars.ExportStringListStaticVariable("Arm64Armv8ABranchProtCflags", arm64ArchVariantCflags["armv8-a-branchprot"])
-	exportedVars.ExportStringListStaticVariable("Arm64Armv82ACflags", arm64ArchVariantCflags["armv8-2a"])
-	exportedVars.ExportStringListStaticVariable("Arm64Armv82ADotprodCflags", arm64ArchVariantCflags["armv8-2a-dotprod"])
-	exportedVars.ExportStringListStaticVariable("Arm64Armv9ACflags", arm64ArchVariantCflags["armv9-a"])
+	pctx.StaticVariable("Arm64CortexA53Cflags", strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
+	pctx.StaticVariable("Arm64CortexA55Cflags", strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
+	pctx.StaticVariable("Arm64KryoCflags", strings.Join(arm64CpuVariantCflags["kryo"], " "))
+	pctx.StaticVariable("Arm64ExynosM1Cflags", strings.Join(arm64CpuVariantCflags["exynos-m1"], " "))
+	pctx.StaticVariable("Arm64ExynosM2Cflags", strings.Join(arm64CpuVariantCflags["exynos-m2"], " "))
 
-	exportedVars.ExportStringListStaticVariable("Arm64CortexA53Cflags", arm64CpuVariantCflags["cortex-a53"])
-	exportedVars.ExportStringListStaticVariable("Arm64CortexA55Cflags", arm64CpuVariantCflags["cortex-a55"])
-	exportedVars.ExportStringListStaticVariable("Arm64KryoCflags", arm64CpuVariantCflags["kryo"])
-	exportedVars.ExportStringListStaticVariable("Arm64ExynosM1Cflags", arm64CpuVariantCflags["exynos-m1"])
-	exportedVars.ExportStringListStaticVariable("Arm64ExynosM2Cflags", arm64CpuVariantCflags["exynos-m2"])
-
-	exportedVars.ExportStringListStaticVariable("Arm64FixCortexA53Ldflags", []string{"-Wl,--fix-cortex-a53-843419"})
+	pctx.StaticVariable("Arm64FixCortexA53Ldflags", "-Wl,--fix-cortex-a53-843419")
 }
 
 var (
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
index 335ad56..438e0e6 100644
--- a/cc/config/arm64_linux_host.go
+++ b/cc/config/arm64_linux_host.go
@@ -42,7 +42,6 @@
 		"-Wl,-z,now",
 		"-Wl,--build-id=md5",
 		"-Wl,--fatal-warnings",
-		"-Wl,--hash-style=gnu",
 		"-Wl,--no-undefined-version",
 	}
 
@@ -62,9 +61,9 @@
 )
 
 func init() {
-	exportedVars.ExportStringListStaticVariable("LinuxBionicArm64Cflags", linuxCrossCflags)
-	exportedVars.ExportStringListStaticVariable("LinuxBionicArm64Ldflags", linuxCrossLdflags)
-	exportedVars.ExportStringListStaticVariable("LinuxBionicArm64Lldflags", linuxCrossLldflags)
+	pctx.StaticVariable("LinuxBionicArm64Cflags", strings.Join(linuxCrossCflags, " "))
+	pctx.StaticVariable("LinuxBionicArm64Ldflags", strings.Join(linuxCrossLdflags, " "))
+	pctx.StaticVariable("LinuxBionicArm64Lldflags", strings.Join(linuxCrossLldflags, " "))
 }
 
 // toolchain config for ARM64 Linux CrossHost. Almost everything is the same as the ARM64 Android
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 603bc6d..3cb1909 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -35,10 +35,9 @@
 	armCppflags = []string{
 		// Revert this after b/322359235 is fixed
 		"-mllvm", "-enable-shrink-wrap=false",
-  }
+	}
 
 	armLdflags = []string{
-		"-Wl,--hash-style=gnu",
 		"-Wl,-m,armelf",
 		// Revert this after b/322359235 is fixed
 		"-Wl,-mllvm", "-Wl,-enable-shrink-wrap=false",
@@ -186,43 +185,37 @@
 )
 
 func init() {
-	// Just exported. Not created as a Ninja static variable.
-	exportedVars.ExportString("ArmClangTriple", clangTriple)
+	pctx.StaticVariable("ArmLdflags", strings.Join(armLdflags, " "))
+	pctx.StaticVariable("ArmLldflags", strings.Join(armLldflags, " "))
 
-	exportedVars.ExportStringListStaticVariable("ArmLdflags", armLdflags)
-	exportedVars.ExportStringListStaticVariable("ArmLldflags", armLldflags)
-
-	exportedVars.ExportStringListStaticVariable("ArmFixCortexA8LdFlags", armFixCortexA8LdFlags)
-	exportedVars.ExportStringListStaticVariable("ArmNoFixCortexA8LdFlags", armNoFixCortexA8LdFlags)
+	pctx.StaticVariable("ArmFixCortexA8LdFlags", strings.Join(armFixCortexA8LdFlags, " "))
+	pctx.StaticVariable("ArmNoFixCortexA8LdFlags", strings.Join(armNoFixCortexA8LdFlags, " "))
 
 	// Clang cflags
-	exportedVars.ExportStringListStaticVariable("ArmToolchainCflags", armToolchainCflags)
-	exportedVars.ExportStringListStaticVariable("ArmCflags", armCflags)
-	exportedVars.ExportStringListStaticVariable("ArmCppflags", armCppflags)
+	pctx.StaticVariable("ArmToolchainCflags", strings.Join(armToolchainCflags, " "))
+	pctx.StaticVariable("ArmCflags", strings.Join(armCflags, " "))
+	pctx.StaticVariable("ArmCppflags", strings.Join(armCppflags, " "))
 
 	// Clang ARM vs. Thumb instruction set cflags
-	exportedVars.ExportStringListStaticVariable("ArmArmCflags", armArmCflags)
-	exportedVars.ExportStringListStaticVariable("ArmThumbCflags", armThumbCflags)
-
-	exportedVars.ExportVariableReferenceDict("ArmArchVariantCflags", armArchVariantCflagsVar)
-	exportedVars.ExportVariableReferenceDict("ArmCpuVariantCflags", armCpuVariantCflagsVar)
+	pctx.StaticVariable("ArmArmCflags", strings.Join(armArmCflags, " "))
+	pctx.StaticVariable("ArmThumbCflags", strings.Join(armThumbCflags, " "))
 
 	// Clang arch variant cflags
-	exportedVars.ExportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"])
-	exportedVars.ExportStringListStaticVariable("ArmArmv7ANeonCflags", armArchVariantCflags["armv7-a-neon"])
-	exportedVars.ExportStringListStaticVariable("ArmArmv8ACflags", armArchVariantCflags["armv8-a"])
-	exportedVars.ExportStringListStaticVariable("ArmArmv82ACflags", armArchVariantCflags["armv8-2a"])
+	pctx.StaticVariable("ArmArmv7ACflags", strings.Join(armArchVariantCflags["armv7-a"], " "))
+	pctx.StaticVariable("ArmArmv7ANeonCflags", strings.Join(armArchVariantCflags["armv7-a-neon"], " "))
+	pctx.StaticVariable("ArmArmv8ACflags", strings.Join(armArchVariantCflags["armv8-a"], " "))
+	pctx.StaticVariable("ArmArmv82ACflags", strings.Join(armArchVariantCflags["armv8-2a"], " "))
 
 	// Clang cpu variant cflags
-	exportedVars.ExportStringListStaticVariable("ArmGenericCflags", armCpuVariantCflags[""])
-	exportedVars.ExportStringListStaticVariable("ArmCortexA7Cflags", armCpuVariantCflags["cortex-a7"])
-	exportedVars.ExportStringListStaticVariable("ArmCortexA8Cflags", armCpuVariantCflags["cortex-a8"])
-	exportedVars.ExportStringListStaticVariable("ArmCortexA15Cflags", armCpuVariantCflags["cortex-a15"])
-	exportedVars.ExportStringListStaticVariable("ArmCortexA32Cflags", armCpuVariantCflags["cortex-a32"])
-	exportedVars.ExportStringListStaticVariable("ArmCortexA53Cflags", armCpuVariantCflags["cortex-a53"])
-	exportedVars.ExportStringListStaticVariable("ArmCortexA55Cflags", armCpuVariantCflags["cortex-a55"])
-	exportedVars.ExportStringListStaticVariable("ArmKraitCflags", armCpuVariantCflags["krait"])
-	exportedVars.ExportStringListStaticVariable("ArmKryoCflags", armCpuVariantCflags["kryo"])
+	pctx.StaticVariable("ArmGenericCflags", strings.Join(armCpuVariantCflags[""], " "))
+	pctx.StaticVariable("ArmCortexA7Cflags", strings.Join(armCpuVariantCflags["cortex-a7"], " "))
+	pctx.StaticVariable("ArmCortexA8Cflags", strings.Join(armCpuVariantCflags["cortex-a8"], " "))
+	pctx.StaticVariable("ArmCortexA15Cflags", strings.Join(armCpuVariantCflags["cortex-a15"], " "))
+	pctx.StaticVariable("ArmCortexA32Cflags", strings.Join(armCpuVariantCflags["cortex-a32"], " "))
+	pctx.StaticVariable("ArmCortexA53Cflags", strings.Join(armCpuVariantCflags["cortex-a53"], " "))
+	pctx.StaticVariable("ArmCortexA55Cflags", strings.Join(armCpuVariantCflags["cortex-a55"], " "))
+	pctx.StaticVariable("ArmKraitCflags", strings.Join(armCpuVariantCflags["krait"], " "))
+	pctx.StaticVariable("ArmKryoCflags", strings.Join(armCpuVariantCflags["kryo"], " "))
 }
 
 var (
diff --git a/cc/config/arm_linux_host.go b/cc/config/arm_linux_host.go
index e21c60d..e7c7bc4 100644
--- a/cc/config/arm_linux_host.go
+++ b/cc/config/arm_linux_host.go
@@ -14,7 +14,10 @@
 
 package config
 
-import "android/soong/android"
+import (
+	"android/soong/android"
+	"strings"
+)
 
 var (
 	linuxArmCflags = []string{
@@ -39,15 +42,15 @@
 )
 
 func init() {
-	exportedVars.ExportStringListStaticVariable("LinuxArmCflags", linuxArmCflags)
-	exportedVars.ExportStringListStaticVariable("LinuxArm64Cflags", linuxArm64Cflags)
-	exportedVars.ExportStringListStaticVariable("LinuxArmLdflags", linuxArmLdflags)
-	exportedVars.ExportStringListStaticVariable("LinuxArmLldflags", linuxArmLldflags)
-	exportedVars.ExportStringListStaticVariable("LinuxArm64Ldflags", linuxArm64Ldflags)
-	exportedVars.ExportStringListStaticVariable("LinuxArm64Lldflags", linuxArm64Lldflags)
+	pctx.StaticVariable("LinuxArmCflags", strings.Join(linuxArmCflags, " "))
+	pctx.StaticVariable("LinuxArm64Cflags", strings.Join(linuxArm64Cflags, " "))
+	pctx.StaticVariable("LinuxArmLdflags", strings.Join(linuxArmLdflags, " "))
+	pctx.StaticVariable("LinuxArmLldflags", strings.Join(linuxArmLldflags, " "))
+	pctx.StaticVariable("LinuxArm64Ldflags", strings.Join(linuxArm64Ldflags, " "))
+	pctx.StaticVariable("LinuxArm64Lldflags", strings.Join(linuxArm64Lldflags, " "))
 
-	exportedVars.ExportStringListStaticVariable("LinuxArmYasmFlags", []string{"-f elf32 -m arm"})
-	exportedVars.ExportStringListStaticVariable("LinuxArm64YasmFlags", []string{"-f elf64 -m aarch64"})
+	pctx.StaticVariable("LinuxArmYasmFlags", "-f elf32 -m arm")
+	pctx.StaticVariable("LinuxArm64YasmFlags", "-f elf64 -m aarch64")
 
 }
 
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 47c61b0..4856669 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -50,6 +50,8 @@
 	darwinSupportedSdkVersions = []string{
 		"11",
 		"12",
+		"13",
+		"14",
 	}
 
 	darwinAvailableLibraries = append(
diff --git a/cc/config/global.go b/cc/config/global.go
index b21d56c..bf2502f 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -24,8 +24,7 @@
 )
 
 var (
-	pctx         = android.NewPackageContext("android/soong/cc/config")
-	exportedVars = android.NewExportedVariables(pctx)
+	pctx = android.NewPackageContext("android/soong/cc/config")
 
 	// Flags used by lots of devices.  Putting them in package static variables
 	// will save bytes in build.ninja so they aren't repeated for every file
@@ -137,6 +136,11 @@
 		// displaying logs in web browsers.
 		"-fmessage-length=0",
 
+		// Disable C++17 "relaxed template template argument matching" as a workaround for
+		// our out-dated libcxx.
+		// http://b/341084395
+		"-fno-relaxed-template-template-args",
+
 		// Using simple template names reduces the size of debug builds.
 		"-gsimple-template-names",
 
@@ -145,6 +149,9 @@
 
 		// Make paths in deps files relative.
 		"-no-canonical-prefixes",
+
+		// http://b/315250603 temporarily disabled
+		"-Wno-error=format",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -255,8 +262,6 @@
 		"-Werror=fortify-source",
 		// http://b/315246135 temporarily disabled
 		"-Wno-unused-variable",
-		// http://b/315250603 temporarily disabled
-		"-Wno-error=format",
 		// Disabled because it produces many false positives. http://b/323050926
 		"-Wno-missing-field-initializers",
 		// http://b/323050889
@@ -295,6 +300,9 @@
 		// New warnings to be fixed after clang-r475365
 		"-Wno-error=single-bit-bitfield-constant-conversion", // http://b/243965903
 		"-Wno-error=enum-constexpr-conversion",               // http://b/243964282
+		// New warnings to be fixed after clang-r522817
+		"-Wno-error=invalid-offsetof",
+		"-Wno-error=thread-safety-reference-return",
 
 		// Irrelevant on Android because _we_ don't use exceptions, but causes
 		// lots of build noise because libcxx/libcxxabi do. This can probably
@@ -302,6 +310,9 @@
 		// until then because it causes warnings in the _callers_, not the
 		// project itself.
 		"-Wno-deprecated-dynamic-exception-spec",
+
+		// Allow using VLA CXX extension.
+		"-Wno-vla-cxx-extension",
 	}
 
 	noOverride64GlobalCflags = []string{}
@@ -386,8 +397,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r510928"
-	ClangDefaultShortVersion = "18"
+	ClangDefaultVersion      = "clang-r530567"
+	ClangDefaultShortVersion = "19"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -401,26 +412,19 @@
 	VisibilityDefaultFlag = "-fvisibility=default"
 )
 
-func ExportStringList(name string, value []string) {
-	exportedVars.ExportStringList(name, value)
-}
-
 func init() {
 	if runtime.GOOS == "linux" {
 		commonGlobalCflags = append(commonGlobalCflags, "-fdebug-prefix-map=/proc/self/cwd=")
 	}
 
-	exportedVars.ExportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags)
-	exportedVars.ExportStringListStaticVariable("CommonGlobalAsflags", commonGlobalAsflags)
-	exportedVars.ExportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags)
-	exportedVars.ExportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags)
-	exportedVars.ExportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags)
-	exportedVars.ExportStringListStaticVariable("HostGlobalCppflags", hostGlobalCppflags)
-	exportedVars.ExportStringListStaticVariable("HostGlobalLdflags", hostGlobalLdflags)
-	exportedVars.ExportStringListStaticVariable("HostGlobalLldflags", hostGlobalLldflags)
-
-	// Export the static default CommonGlobalCflags to Bazel.
-	exportedVars.ExportStringList("CommonGlobalCflags", commonGlobalCflags)
+	pctx.StaticVariable("CommonGlobalConlyflags", strings.Join(commonGlobalConlyflags, " "))
+	pctx.StaticVariable("CommonGlobalAsflags", strings.Join(commonGlobalAsflags, " "))
+	pctx.StaticVariable("DeviceGlobalCppflags", strings.Join(deviceGlobalCppflags, " "))
+	pctx.StaticVariable("DeviceGlobalLdflags", strings.Join(deviceGlobalLdflags, " "))
+	pctx.StaticVariable("DeviceGlobalLldflags", strings.Join(deviceGlobalLldflags, " "))
+	pctx.StaticVariable("HostGlobalCppflags", strings.Join(hostGlobalCppflags, " "))
+	pctx.StaticVariable("HostGlobalLdflags", strings.Join(hostGlobalLdflags, " "))
+	pctx.StaticVariable("HostGlobalLldflags", strings.Join(hostGlobalLldflags, " "))
 
 	pctx.VariableFunc("CommonGlobalCflags", func(ctx android.PackageVarContext) string {
 		flags := slices.Clone(commonGlobalCflags)
@@ -467,16 +471,10 @@
 		return strings.Join(flags, " ")
 	})
 
-	// Export the static default DeviceGlobalCflags to Bazel.
-	// TODO(187086342): handle cflags that are set in VariableFuncs.
-	exportedVars.ExportStringList("DeviceGlobalCflags", deviceGlobalCflags)
-
 	pctx.VariableFunc("DeviceGlobalCflags", func(ctx android.PackageVarContext) string {
 		return strings.Join(deviceGlobalCflags, " ")
 	})
 
-	// Export the static default NoOverrideGlobalCflags to Bazel.
-	exportedVars.ExportStringList("NoOverrideGlobalCflags", noOverrideGlobalCflags)
 	pctx.VariableFunc("NoOverrideGlobalCflags", func(ctx android.PackageVarContext) string {
 		flags := noOverrideGlobalCflags
 		if ctx.Config().IsEnvTrue("LLVM_NEXT") {
@@ -486,21 +484,11 @@
 		return strings.Join(flags, " ")
 	})
 
-	exportedVars.ExportStringListStaticVariable("NoOverride64GlobalCflags", noOverride64GlobalCflags)
-	exportedVars.ExportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
-	exportedVars.ExportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
-	exportedVars.ExportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
-	exportedVars.ExportStringListStaticVariable("ExternalCflags", extraExternalCflags)
-
-	exportedVars.ExportString("CStdVersion", CStdVersion)
-	exportedVars.ExportString("CppStdVersion", CppStdVersion)
-	exportedVars.ExportString("ExperimentalCStdVersion", ExperimentalCStdVersion)
-	exportedVars.ExportString("ExperimentalCppStdVersion", ExperimentalCppStdVersion)
-
-	exportedVars.ExportString("VersionScriptFlagPrefix", VersionScriptFlagPrefix)
-
-	exportedVars.ExportString("VisibilityHiddenFlag", VisibilityHiddenFlag)
-	exportedVars.ExportString("VisibilityDefaultFlag", VisibilityDefaultFlag)
+	pctx.StaticVariable("NoOverride64GlobalCflags", strings.Join(noOverride64GlobalCflags, " "))
+	pctx.StaticVariable("HostGlobalCflags", strings.Join(hostGlobalCflags, " "))
+	pctx.StaticVariable("NoOverrideExternalGlobalCflags", strings.Join(noOverrideExternalGlobalCflags, " "))
+	pctx.StaticVariable("CommonGlobalCppflags", strings.Join(commonGlobalCppflags, " "))
+	pctx.StaticVariable("ExternalCflags", strings.Join(extraExternalCflags, " "))
 
 	// Everything in these lists is a crime against abstraction and dependency tracking.
 	// Do not add anything to this list.
@@ -515,11 +503,10 @@
 		"frameworks/native/opengl/include",
 		"frameworks/av/include",
 	}
-	exportedVars.ExportStringList("CommonGlobalIncludes", commonGlobalIncludes)
 	pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes)
 
-	exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
-	exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
+	pctx.StaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
+	pctx.StaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
 
 	pctx.StaticVariableWithEnvOverride("ClangBase", "LLVM_PREBUILTS_BASE", ClangDefaultBase)
 	pctx.StaticVariableWithEnvOverride("ClangVersion", "LLVM_PREBUILTS_VERSION", ClangDefaultVersion)
@@ -529,7 +516,7 @@
 	pctx.StaticVariableWithEnvOverride("ClangShortVersion", "LLVM_RELEASE_VERSION", ClangDefaultShortVersion)
 	pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib/clang/${ClangShortVersion}/lib/linux")
 
-	exportedVars.ExportStringListStaticVariable("WarningAllowedProjects", WarningAllowedProjects)
+	pctx.StaticVariable("WarningAllowedProjects", strings.Join(WarningAllowedProjects, " "))
 
 	// These are tied to the version of LLVM directly in external/llvm, so they might trail the host prebuilts
 	// being used for the rest of the build process.
@@ -544,7 +531,6 @@
 		"frameworks/rs/script_api/include",
 	}
 	pctx.PrefixedExistentPathsForSourcesVariable("RsGlobalIncludes", "-I", rsGlobalIncludes)
-	exportedVars.ExportStringList("RsGlobalIncludes", rsGlobalIncludes)
 
 	pctx.VariableFunc("CcWrapper", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("CC_WRAPPER"); override != "" {
@@ -562,7 +548,7 @@
 	pctx.StaticVariableWithEnvOverride("REAbiLinkerExecStrategy", "RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
 }
 
-var HostPrebuiltTag = exportedVars.ExportVariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
 
 func ClangPath(ctx android.PathContext, file string) android.SourcePath {
 	type clangToolKey string
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index a5dea55..6a5293f 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -29,29 +29,18 @@
 		// 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{}
 
 	riscv64Ldflags = []string{
-		// TODO: sysv hashes are the default for other architectures because gnu
-		// hashes weren't supported until api level 23, but riscv64 didn't exist
-		// back then, and could move today...
-		// https://android.googlesource.com/platform/bionic/+/main/android-changes-for-ndk-developers.md#gnu-hashes-availible-in-api-level-23
-		"-Wl,--hash-style=gnu",
 		// This is already the driver's Android default, but duplicated here (and
 		// above) 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 clang default changed (https://github.com/google/android-riscv64/issues/124)
 		"-Wl,-mllvm -Wl,-jump-is-expensive=false",
 	}
@@ -69,15 +58,11 @@
 
 func init() {
 
-	exportedVars.ExportStringListStaticVariable("Riscv64Ldflags", riscv64Ldflags)
-	exportedVars.ExportStringListStaticVariable("Riscv64Lldflags", riscv64Lldflags)
+	pctx.StaticVariable("Riscv64Ldflags", strings.Join(riscv64Ldflags, " "))
+	pctx.StaticVariable("Riscv64Lldflags", strings.Join(riscv64Lldflags, " "))
 
-	exportedVars.ExportStringListStaticVariable("Riscv64Cflags", riscv64Cflags)
-	exportedVars.ExportStringListStaticVariable("Riscv64Cppflags", riscv64Cppflags)
-
-	exportedVars.ExportVariableReferenceDict("Riscv64ArchVariantCflags", riscv64ArchVariantCflagsVar)
-	exportedVars.ExportVariableReferenceDict("Riscv64CpuVariantCflags", riscv64CpuVariantCflagsVar)
-	exportedVars.ExportVariableReferenceDict("Riscv64CpuVariantLdflags", riscv64CpuVariantLdflags)
+	pctx.StaticVariable("Riscv64Cflags", strings.Join(riscv64Cflags, " "))
+	pctx.StaticVariable("Riscv64Cppflags", strings.Join(riscv64Cppflags, " "))
 }
 
 var (
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index b40557a..46d5d90 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -87,7 +87,7 @@
 	// The global default tidy checks should include clang-tidy
 	// default checks and tested groups, but exclude known noisy checks.
 	// See https://clang.llvm.org/extra/clang-tidy/checks/list.html
-	exportedVars.ExportVariableConfigMethod("TidyDefaultGlobalChecks", func(config android.Config) string {
+	pctx.VariableConfigMethod("TidyDefaultGlobalChecks", func(config android.Config) string {
 		if override := config.Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
 			return override
 		}
@@ -149,7 +149,7 @@
 	// There are too many clang-tidy warnings in external and vendor projects, so we only
 	// enable some google checks for these projects. Users can add more checks locally with the
 	// "tidy_checks" list in .bp files, or the "Checks" list in .clang-tidy config files.
-	exportedVars.ExportVariableConfigMethod("TidyExternalVendorChecks", func(config android.Config) string {
+	pctx.VariableConfigMethod("TidyExternalVendorChecks", func(config android.Config) string {
 		if override := config.Getenv("DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS"); override != "" {
 			return override
 		}
@@ -163,25 +163,21 @@
 		}, ",")
 	})
 
-	exportedVars.ExportVariableFuncVariable("TidyGlobalNoChecks", func() string {
-		return strings.Join(globalNoCheckList, ",")
-	})
+	pctx.StaticVariable("TidyGlobalNoChecks", strings.Join(globalNoCheckList, ","))
 
-	exportedVars.ExportVariableFuncVariable("TidyGlobalNoErrorChecks", func() string {
-		return strings.Join(globalNoErrorCheckList, ",")
-	})
+	pctx.StaticVariable("TidyGlobalNoErrorChecks", strings.Join(globalNoErrorCheckList, ","))
 
-	exportedVars.ExportStringListStaticVariable("TidyExtraArgFlags", extraArgFlags)
+	pctx.StaticVariable("TidyExtraArgFlags", strings.Join(extraArgFlags, " "))
 
 	// To reduce duplicate warnings from the same header files,
 	// header-filter will contain only the module directory and
 	// those specified by DEFAULT_TIDY_HEADER_DIRS.
-	exportedVars.ExportVariableConfigMethod("TidyDefaultHeaderDirs", func(config android.Config) string {
+	pctx.VariableConfigMethod("TidyDefaultHeaderDirs", func(config android.Config) string {
 		return config.Getenv("DEFAULT_TIDY_HEADER_DIRS")
 	})
 
 	// Use WTIH_TIDY_FLAGS to pass extra global default clang-tidy flags.
-	exportedVars.ExportVariableConfigMethod("TidyWithTidyFlags", func(config android.Config) string {
+	pctx.VariableConfigMethod("TidyWithTidyFlags", func(config android.Config) string {
 		return config.Getenv("WITH_TIDY_FLAGS")
 	})
 }
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 71e98fe..5d8c351 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -16,14 +16,15 @@
 
 import (
 	"fmt"
+	"strings"
 
 	"android/soong/android"
 )
 
 func init() {
-	exportedVars.ExportStringListStaticVariable("DarwinAvailableLibraries", darwinAvailableLibraries)
-	exportedVars.ExportStringListStaticVariable("LinuxAvailableLibraries", linuxAvailableLibraries)
-	exportedVars.ExportStringListStaticVariable("WindowsAvailableLibraries", windowsAvailableLibraries)
+	pctx.StaticVariable("DarwinAvailableLibraries", strings.Join(darwinAvailableLibraries, " "))
+	pctx.StaticVariable("LinuxAvailableLibraries", strings.Join(linuxAvailableLibraries, " "))
+	pctx.StaticVariable("WindowsAvailableLibraries", strings.Join(windowsAvailableLibraries, " "))
 }
 
 type toolchainFactory func(arch android.Arch) Toolchain
@@ -208,58 +209,58 @@
 	return list
 }
 
-func LibclangRuntimeLibrary(t Toolchain, library string) string {
+func LibclangRuntimeLibrary(library string) string {
 	return "libclang_rt." + library
 }
 
-func BuiltinsRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "builtins")
+func BuiltinsRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("builtins")
 }
 
-func AddressSanitizerRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "asan")
+func AddressSanitizerRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("asan")
 }
 
-func AddressSanitizerStaticRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "asan.static")
+func AddressSanitizerStaticRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("asan.static")
 }
 
-func AddressSanitizerCXXStaticRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "asan_cxx.static")
+func AddressSanitizerCXXStaticRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("asan_cxx.static")
 }
 
-func HWAddressSanitizerRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "hwasan")
+func HWAddressSanitizerRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("hwasan")
 }
 
-func HWAddressSanitizerStaticLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "hwasan_static")
+func HWAddressSanitizerStaticLibrary() string {
+	return LibclangRuntimeLibrary("hwasan_static")
 }
 
-func UndefinedBehaviorSanitizerRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "ubsan_standalone")
+func UndefinedBehaviorSanitizerRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("ubsan_standalone")
 }
 
-func UndefinedBehaviorSanitizerMinimalRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "ubsan_minimal")
+func UndefinedBehaviorSanitizerMinimalRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("ubsan_minimal")
 }
 
-func ThreadSanitizerRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "tsan")
+func ThreadSanitizerRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("tsan")
 }
 
-func ScudoRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "scudo")
+func ScudoRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("scudo")
 }
 
-func ScudoMinimalRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "scudo_minimal")
+func ScudoMinimalRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("scudo_minimal")
 }
 
-func LibFuzzerRuntimeLibrary(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "fuzzer")
+func LibFuzzerRuntimeLibrary() string {
+	return LibclangRuntimeLibrary("fuzzer")
 }
 
-func LibFuzzerRuntimeInterceptors(t Toolchain) string {
-	return LibclangRuntimeLibrary(t, "fuzzer_interceptors")
+func LibFuzzerRuntimeInterceptors() string {
+	return LibclangRuntimeLibrary("fuzzer_interceptors")
 }
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
deleted file mode 100644
index dd612ce..0000000
--- a/cc/config/vndk.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package config
-
-// List of VNDK libraries that have different core variant and vendor variant.
-// For these libraries, the vendor variants must be installed even if the device
-// has VndkUseCoreVariant set.
-// Note that AIDL-generated modules must use vendor variants by default.
-var VndkMustUseVendorVariantList = []string{
-	"android.hardware.nfc@1.2",
-	"libbinder",
-	"libcrypto",
-	"libexpat",
-	"libgatekeeper",
-	"libgui",
-	"libhidlcache",
-	"libkeymaster_messages",
-	"libkeymaster_portable",
-	"libmedia_omx",
-	"libpuresoftkeymasterdevice",
-	"libselinux",
-	"libsoftkeymasterdevice",
-	"libsqlite",
-	"libssl",
-	"libstagefright_bufferpool@2.0",
-	"libstagefright_bufferqueue_helper",
-	"libstagefright_foundation",
-	"libstagefright_omx",
-	"libstagefright_omx_utils",
-	"libstagefright_xmlparser",
-	"libui",
-	"libxml2",
-}
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 12119a7..5aa2a7e 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -30,7 +30,6 @@
 	x86_64Cppflags = []string{}
 
 	x86_64Ldflags = []string{
-		"-Wl,--hash-style=gnu",
 		"-Wl,-z,separate-loadable-segments",
 	}
 
@@ -98,11 +97,10 @@
 )
 
 func init() {
-	exportedVars.ExportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"})
-	exportedVars.ExportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"})
+	pctx.StaticVariable("X86_64ToolchainCflags", "-m64")
+	pctx.StaticVariable("X86_64ToolchainLdflags", "-m64")
 
-	exportedVars.ExportStringListStaticVariable("X86_64Ldflags", x86_64Ldflags)
-	exportedVars.ExportStringList("X86_64Lldflags", X86_64Lldflags)
+	pctx.StaticVariable("X86_64Ldflags", strings.Join(x86_64Ldflags, " "))
 	pctx.VariableFunc("X86_64Lldflags", func(ctx android.PackageVarContext) string {
 		maxPageSizeFlag := "-Wl,-z,max-page-size=" + ctx.Config().MaxPageSizeSupported()
 		flags := append(X86_64Lldflags, maxPageSizeFlag)
@@ -110,27 +108,20 @@
 	})
 
 	// Clang cflags
-	exportedVars.ExportStringList("X86_64Cflags", x86_64Cflags)
 	pctx.VariableFunc("X86_64Cflags", func(ctx android.PackageVarContext) string {
 		flags := x86_64Cflags
 		if ctx.Config().NoBionicPageSizeMacro() {
 			flags = append(flags, "-D__BIONIC_NO_PAGE_SIZE_MACRO")
+		} else {
+			flags = append(flags, "-D__BIONIC_DEPRECATED_PAGE_SIZE_MACRO")
 		}
 		return strings.Join(flags, " ")
 	})
 
-	exportedVars.ExportStringListStaticVariable("X86_64Cppflags", x86_64Cppflags)
+	pctx.StaticVariable("X86_64Cppflags", strings.Join(x86_64Cppflags, " "))
 
 	// Yasm flags
-	exportedVars.ExportStringListStaticVariable("X86_64YasmFlags", []string{
-		"-f elf64",
-		"-m amd64",
-	})
-
-	// Extended cflags
-
-	exportedVars.ExportStringListDict("X86_64ArchVariantCflags", x86_64ArchVariantCflags)
-	exportedVars.ExportStringListDict("X86_64ArchFeatureCflags", x86_64ArchFeatureCflags)
+	pctx.StaticVariable("X86_64YasmFlags", "-f elf64 -m amd64")
 
 	// Architecture variant cflags
 	for variant, cflags := range x86_64ArchVariantCflags {
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 2faa670..4b0041c 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -33,9 +33,7 @@
 
 	x86Cppflags = []string{}
 
-	x86Ldflags = []string{
-		"-Wl,--hash-style=gnu",
-	}
+	x86Ldflags = []string{}
 
 	x86ArchVariantCflags = map[string][]string{
 		"": []string{
@@ -103,25 +101,18 @@
 )
 
 func init() {
-	exportedVars.ExportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"})
-	exportedVars.ExportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"})
+	pctx.StaticVariable("X86ToolchainCflags", "-m32")
+	pctx.StaticVariable("X86ToolchainLdflags", "-m32")
 
-	exportedVars.ExportStringListStaticVariable("X86Ldflags", x86Ldflags)
-	exportedVars.ExportStringListStaticVariable("X86Lldflags", x86Ldflags)
+	pctx.StaticVariable("X86Ldflags", strings.Join(x86Ldflags, " "))
+	pctx.StaticVariable("X86Lldflags", strings.Join(x86Ldflags, " "))
 
 	// Clang cflags
-	exportedVars.ExportStringListStaticVariable("X86Cflags", x86Cflags)
-	exportedVars.ExportStringListStaticVariable("X86Cppflags", x86Cppflags)
+	pctx.StaticVariable("X86Cflags", strings.Join(x86Cflags, " "))
+	pctx.StaticVariable("X86Cppflags", strings.Join(x86Cppflags, " "))
 
 	// Yasm flags
-	exportedVars.ExportStringListStaticVariable("X86YasmFlags", []string{
-		"-f elf32",
-		"-m x86",
-	})
-
-	// Extended cflags
-	exportedVars.ExportStringListDict("X86ArchVariantCflags", x86ArchVariantCflags)
-	exportedVars.ExportStringListDict("X86ArchFeatureCflags", x86ArchFeatureCflags)
+	pctx.StaticVariable("X86YasmFlags", "-f elf32 -m x86")
 
 	// Architecture variant cflags
 	for variant, cflags := range x86ArchVariantCflags {
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index f80be99..515cb21 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"strings"
 )
 
 var (
@@ -46,7 +47,6 @@
 		"-Wl,-z,now",
 		"-Wl,--build-id=md5",
 		"-Wl,--fatal-warnings",
-		"-Wl,--hash-style=gnu",
 		"-Wl,--no-undefined-version",
 
 		// Use the device gcc toolchain
@@ -73,13 +73,13 @@
 )
 
 func init() {
-	exportedVars.ExportStringListStaticVariable("LinuxBionicCflags", linuxBionicCflags)
-	exportedVars.ExportStringListStaticVariable("LinuxBionicLdflags", linuxBionicLdflags)
-	exportedVars.ExportStringListStaticVariable("LinuxBionicLldflags", linuxBionicLldflags)
+	pctx.StaticVariable("LinuxBionicCflags", strings.Join(linuxBionicCflags, " "))
+	pctx.StaticVariable("LinuxBionicLdflags", strings.Join(linuxBionicLdflags, " "))
+	pctx.StaticVariable("LinuxBionicLldflags", strings.Join(linuxBionicLldflags, " "))
 
 	// Use the device gcc toolchain for now
-	exportedVars.ExportStringStaticVariable("LinuxBionicGccVersion", x86_64GccVersion)
-	exportedVars.ExportSourcePathVariable("LinuxBionicGccRoot",
+	pctx.StaticVariable("LinuxBionicGccVersion", x86_64GccVersion)
+	pctx.SourcePathVariable("LinuxBionicGccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${LinuxBionicGccVersion}")
 }
 
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 9bc54d6..7f22377 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -120,40 +120,40 @@
 )
 
 func init() {
-	exportedVars.ExportStringStaticVariable("LinuxGccVersion", linuxGccVersion)
-	exportedVars.ExportStringStaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
+	pctx.StaticVariable("LinuxGccVersion", linuxGccVersion)
+	pctx.StaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
 
 	// Most places use the full GCC version. A few only use up to the first two numbers.
 	if p := strings.Split(linuxGccVersion, "."); len(p) > 2 {
-		exportedVars.ExportStringStaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
+		pctx.StaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
 	} else {
-		exportedVars.ExportStringStaticVariable("ShortLinuxGccVersion", linuxGccVersion)
+		pctx.StaticVariable("ShortLinuxGccVersion", linuxGccVersion)
 	}
 
-	exportedVars.ExportSourcePathVariable("LinuxGccRoot",
+	pctx.SourcePathVariable("LinuxGccRoot",
 		"prebuilts/gcc/linux-x86/host/x86_64-linux-glibc${LinuxGlibcVersion}-${ShortLinuxGccVersion}")
 
-	exportedVars.ExportStringListStaticVariable("LinuxGccTriple", []string{"x86_64-linux"})
+	pctx.StaticVariable("LinuxGccTriple", "x86_64-linux")
 
-	exportedVars.ExportStringListStaticVariable("LinuxCflags", linuxCflags)
-	exportedVars.ExportStringListStaticVariable("LinuxLdflags", linuxLdflags)
-	exportedVars.ExportStringListStaticVariable("LinuxLldflags", linuxLldflags)
-	exportedVars.ExportStringListStaticVariable("LinuxGlibcCflags", linuxGlibcCflags)
-	exportedVars.ExportStringListStaticVariable("LinuxGlibcLdflags", linuxGlibcLdflags)
-	exportedVars.ExportStringListStaticVariable("LinuxGlibcLldflags", linuxGlibcLdflags)
-	exportedVars.ExportStringListStaticVariable("LinuxMuslCflags", linuxMuslCflags)
-	exportedVars.ExportStringListStaticVariable("LinuxMuslLdflags", linuxMuslLdflags)
-	exportedVars.ExportStringListStaticVariable("LinuxMuslLldflags", linuxMuslLdflags)
+	pctx.StaticVariable("LinuxCflags", strings.Join(linuxCflags, " "))
+	pctx.StaticVariable("LinuxLdflags", strings.Join(linuxLdflags, " "))
+	pctx.StaticVariable("LinuxLldflags", strings.Join(linuxLldflags, " "))
+	pctx.StaticVariable("LinuxGlibcCflags", strings.Join(linuxGlibcCflags, " "))
+	pctx.StaticVariable("LinuxGlibcLdflags", strings.Join(linuxGlibcLdflags, " "))
+	pctx.StaticVariable("LinuxGlibcLldflags", strings.Join(linuxGlibcLdflags, " "))
+	pctx.StaticVariable("LinuxMuslCflags", strings.Join(linuxMuslCflags, " "))
+	pctx.StaticVariable("LinuxMuslLdflags", strings.Join(linuxMuslLdflags, " "))
+	pctx.StaticVariable("LinuxMuslLldflags", strings.Join(linuxMuslLdflags, " "))
 
-	exportedVars.ExportStringListStaticVariable("LinuxX86Cflags", linuxX86Cflags)
-	exportedVars.ExportStringListStaticVariable("LinuxX8664Cflags", linuxX8664Cflags)
-	exportedVars.ExportStringListStaticVariable("LinuxX86Ldflags", linuxX86Ldflags)
-	exportedVars.ExportStringListStaticVariable("LinuxX86Lldflags", linuxX86Ldflags)
-	exportedVars.ExportStringListStaticVariable("LinuxX8664Ldflags", linuxX8664Ldflags)
-	exportedVars.ExportStringListStaticVariable("LinuxX8664Lldflags", linuxX8664Ldflags)
+	pctx.StaticVariable("LinuxX86Cflags", strings.Join(linuxX86Cflags, " "))
+	pctx.StaticVariable("LinuxX8664Cflags", strings.Join(linuxX8664Cflags, " "))
+	pctx.StaticVariable("LinuxX86Ldflags", strings.Join(linuxX86Ldflags, " "))
+	pctx.StaticVariable("LinuxX86Lldflags", strings.Join(linuxX86Ldflags, " "))
+	pctx.StaticVariable("LinuxX8664Ldflags", strings.Join(linuxX8664Ldflags, " "))
+	pctx.StaticVariable("LinuxX8664Lldflags", strings.Join(linuxX8664Ldflags, " "))
 	// Yasm flags
-	exportedVars.ExportStringListStaticVariable("LinuxX86YasmFlags", []string{"-f elf32 -m x86"})
-	exportedVars.ExportStringListStaticVariable("LinuxX8664YasmFlags", []string{"-f elf64 -m amd64"})
+	pctx.StaticVariable("LinuxX86YasmFlags", "-f elf32 -m x86")
+	pctx.StaticVariable("LinuxX8664YasmFlags", "-f elf64 -m amd64")
 }
 
 type toolchainLinux struct {
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 2436f33..92f2c5e 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -128,13 +128,13 @@
 	if ctx.Config().Getenv("FUZZ_FRAMEWORK") == "AFL" {
 		deps.HeaderLibs = append(deps.HeaderLibs, "libafl_headers")
 	} else {
-		deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeLibrary(ctx.toolchain()))
+		deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeLibrary())
 		// Fuzzers built with HWASAN should use the interceptors for better
 		// mutation based on signals in strcmp, memcpy, etc. This is only needed for
 		// fuzz targets, not generic HWASAN-ified binaries or libraries.
 		if module, ok := ctx.Module().(*Module); ok {
 			if module.IsSanitizerEnabled(Hwasan) {
-				deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeInterceptors(ctx.toolchain()))
+				deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeInterceptors())
 			}
 		}
 	}
@@ -433,7 +433,7 @@
 			return
 		}
 		// Discard non-fuzz targets.
-		if ok := fuzz.IsValid(ccModule.FuzzModuleStruct()); !ok {
+		if ok := fuzz.IsValid(ctx, ccModule.FuzzModuleStruct()); !ok {
 			return
 		}
 
@@ -597,7 +597,7 @@
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 
 		// If this is a Rust module which is not rust_ffi_shared, we still want to bundle any transitive
-		// shared dependencies (even for rust_ffi_static)
+		// shared dependencies (even for rust_ffi_rlib or rust_ffi_static)
 		if rustmod, ok := child.(LinkableInterface); ok && rustmod.RustLibraryInterface() && !rustmod.Shared() {
 			if recursed[ctx.OtherModuleName(child)] {
 				return false
diff --git a/cc/genrule.go b/cc/genrule.go
index 0fb3e2d..fe3b127 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -19,7 +19,6 @@
 
 	"android/soong/android"
 	"android/soong/genrule"
-	"android/soong/snapshot"
 )
 
 func init() {
@@ -63,6 +62,8 @@
 
 	android.InitApexModule(module)
 
+	android.InitDefaultableModule(module)
+
 	return module
 }
 
@@ -78,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())
 }
@@ -97,47 +106,12 @@
 func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	// If the build is using a snapshot, the recovery variant under AOSP directories
 	// is not needed.
-	recoverySnapshotVersion := ctx.DeviceConfig().RecoverySnapshotVersion()
-	if recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" &&
-		!snapshot.IsRecoveryProprietaryModule(ctx) {
-		return false
-	} else {
-		return Bool(g.Recovery_available)
-	}
+	return Bool(g.Recovery_available)
 }
 
 func (g *GenruleExtraProperties) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	var variants []string
-	vndkVersion := ctx.DeviceConfig().VndkVersion()
-	vendorVariantRequired := Bool(g.Vendor_available) || Bool(g.Odm_available) || ctx.SocSpecific() || ctx.DeviceSpecific()
-	productVariantRequired := Bool(g.Product_available) || ctx.ProductSpecific()
-
-	if vndkVersion == "" {
-		if vendorVariantRequired {
-			variants = append(variants, VendorVariation)
-		}
-		if productVariantRequired {
-			variants = append(variants, ProductVariation)
-		}
-	} else {
-		if vendorVariantRequired {
-			// If vndkVersion is current, we can always use PlatformVndkVersion.
-			// If not, we assume modules under proprietary paths are compatible for
-			// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, that is
-			// PLATFORM_VNDK_VERSION.
-			if vndkVersion == "current" || !snapshot.IsVendorProprietaryModule(ctx) {
-				variants = append(variants, VendorVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
-			} else {
-				variants = append(variants, VendorVariationPrefix+vndkVersion)
-			}
-		}
-		if productVariantRequired {
-			variants = append(variants, ProductVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
-		}
-	}
-
-	return variants
+	return nil
 }
 
-func (g *GenruleExtraProperties) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+func (g *GenruleExtraProperties) SetImageVariation(ctx android.BaseModuleContext, variation string) {
 }
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index 0896206..b3d5116 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -200,7 +200,7 @@
 	}
 	`
 	t.Helper()
-	ctx := PrepareForTestWithCcIncludeVndk.RunTestWithBp(t, bp)
+	ctx := PrepareForIntegrationTestWithCc.RunTestWithBp(t, bp)
 
 	variants := ctx.ModuleVariantsForTests("gen")
 	if !slices.Contains(variants, "android_vendor_arm64_armv8-a") {
diff --git a/cc/image.go b/cc/image.go
index d02a2f3..2e52ccc 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -17,12 +17,9 @@
 // functions to determine where a module is installed, etc.
 
 import (
-	"fmt"
-	"reflect"
 	"strings"
 
 	"android/soong/android"
-	"android/soong/snapshot"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -42,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."
@@ -120,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
@@ -158,52 +147,6 @@
 	return c.ModuleBase.InstallInRecovery()
 }
 
-func visitPropsAndCompareVendorAndProductProps(v reflect.Value) bool {
-	if v.Kind() != reflect.Struct {
-		return true
-	}
-	for i := 0; i < v.NumField(); i++ {
-		prop := v.Field(i)
-		if prop.Kind() == reflect.Struct && v.Type().Field(i).Name == "Target" {
-			vendor_prop := prop.FieldByName("Vendor")
-			product_prop := prop.FieldByName("Product")
-			if vendor_prop.Kind() != reflect.Struct && product_prop.Kind() != reflect.Struct {
-				// Neither Target.Vendor nor Target.Product is defined
-				continue
-			}
-			if vendor_prop.Kind() != reflect.Struct || product_prop.Kind() != reflect.Struct ||
-				!reflect.DeepEqual(vendor_prop.Interface(), product_prop.Interface()) {
-				// If only one of either Target.Vendor or Target.Product is
-				// defined or they have different values, it fails the build
-				// since VNDK must have the same properties for both vendor
-				// and product variants.
-				return false
-			}
-		} else if !visitPropsAndCompareVendorAndProductProps(prop) {
-			// Visit the substructures to find Target.Vendor and Target.Product
-			return false
-		}
-	}
-	return true
-}
-
-// In the case of VNDK, vendor and product variants must have the same properties.
-// VNDK installs only one file and shares it for both vendor and product modules on
-// runtime. We may not define different versions of a VNDK lib for each partition.
-// This function is used only for the VNDK modules that is available to both vendor
-// and product partitions.
-func (c *Module) compareVendorAndProductProps() bool {
-	if !c.IsVndk() && !Bool(c.VendorProperties.Product_available) {
-		panic(fmt.Errorf("This is only for product available VNDK libs. %q is not a VNDK library or not product available", c.Name()))
-	}
-	for _, properties := range c.GetProperties() {
-		if !visitPropsAndCompareVendorAndProductProps(reflect.ValueOf(properties).Elem()) {
-			return false
-		}
-	}
-	return true
-}
-
 // ImageMutatableModule provides a common image mutation interface for  LinkableInterface modules.
 type ImageMutatableModule interface {
 	android.Module
@@ -256,67 +199,20 @@
 
 	// 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)
 
 func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
-	m.CheckVndkProperties(mctx)
 	MutateImage(mctx, m)
 }
 
-// CheckVndkProperties checks whether the VNDK-related properties are set correctly.
-// If properties are not set correctly, results in a module context property error.
-func (m *Module) CheckVndkProperties(mctx android.BaseModuleContext) {
-	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
-	productSpecific := mctx.ProductSpecific()
-
-	if vndkdep := m.vndkdep; vndkdep != nil {
-		if vndkdep.isVndk() {
-			if vendorSpecific || productSpecific {
-				if !vndkdep.isVndkExt() {
-					mctx.PropertyErrorf("vndk",
-						"must set `extends: \"...\"` to vndk extension")
-				} else if Bool(m.VendorProperties.Vendor_available) {
-					mctx.PropertyErrorf("vendor_available",
-						"must not set at the same time as `vndk: {extends: \"...\"}`")
-				} else if Bool(m.VendorProperties.Product_available) {
-					mctx.PropertyErrorf("product_available",
-						"must not set at the same time as `vndk: {extends: \"...\"}`")
-				}
-			} else {
-				if vndkdep.isVndkExt() {
-					mctx.PropertyErrorf("vndk",
-						"must set `vendor: true` or `product_specific: true` to set `extends: %q`",
-						m.getVndkExtendsModuleName())
-				}
-				if !Bool(m.VendorProperties.Vendor_available) {
-					mctx.PropertyErrorf("vndk",
-						"vendor_available must be set to true when `vndk: {enabled: true}`")
-				}
-				if Bool(m.VendorProperties.Product_available) {
-					// If a VNDK module creates both product and vendor variants, they
-					// must have the same properties since they share a single VNDK
-					// library on runtime.
-					if !m.compareVendorAndProductProps() {
-						mctx.ModuleErrorf("product properties must have the same values with the vendor properties for VNDK modules")
-					}
-				}
-			}
-		} else {
-			if vndkdep.isVndkSp() {
-				mctx.PropertyErrorf("vndk",
-					"must set `enabled: true` to set `support_system_process: true`")
-			}
-			if vndkdep.isVndkExt() {
-				mctx.PropertyErrorf("vndk",
-					"must set `enabled: true` to set `extends: %q`",
-					m.getVndkExtendsModuleName())
-			}
-		}
-	}
-}
-
 func (m *Module) VendorAvailable() bool {
 	return Bool(m.VendorProperties.Vendor_available)
 }
@@ -369,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()
@@ -421,60 +325,36 @@
 		}
 	}
 
+	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
-
-	platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
-	boardVndkVersion := mctx.DeviceConfig().VndkVersion()
-	recoverySnapshotVersion := mctx.DeviceConfig().RecoverySnapshotVersion()
-	usingRecoverySnapshot := recoverySnapshotVersion != "current" &&
-		recoverySnapshotVersion != ""
-	needVndkVersionVendorVariantForLlndk := false
-	if boardVndkVersion != "" {
-		boardVndkApiLevel, err := android.ApiLevelFromUser(mctx, boardVndkVersion)
-		if err == nil && !boardVndkApiLevel.IsPreview() {
-			// VNDK snapshot newer than v30 has LLNDK stub libraries.
-			// Only the VNDK version less than or equal to v30 requires generating the vendor
-			// variant of the VNDK version from the source tree.
-			needVndkVersionVendorVariantForLlndk = boardVndkApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(mctx, "30"))
-		}
-	}
-	if boardVndkVersion == "current" {
-		boardVndkVersion = platformVndkVersion
-	}
-
 	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, platformVndkVersion)
-		productVariants = append(productVariants, platformVndkVersion)
-		// Generate vendor variants for boardVndkVersion only if the VNDK snapshot does not
-		// provide the LLNDK stub libraries.
-		if needVndkVersionVendorVariantForLlndk {
-			vendorVariants = append(vendorVariants, boardVndkVersion)
-		}
+		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, boardVndkVersion)
-		productVariants = append(productVariants, platformVndkVersion)
+		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() && !m.IsVndkExt() {
+	} else if m.HasNonSystemVariants() {
 		// This will be available to /system unless it is product_specific
 		// which will be handled later.
 		coreVariantNeeded = true
@@ -483,36 +363,16 @@
 		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
 		// PLATFORM_VNDK_VERSION.
 		if m.HasVendorVariant() {
-			if snapshot.IsVendorProprietaryModule(mctx) {
-				vendorVariants = append(vendorVariants, boardVndkVersion)
-			} else {
-				vendorVariants = append(vendorVariants, platformVndkVersion)
-			}
+			vendorVariantNeeded = true
 		}
 
 		// product_available modules are available to /product.
 		if m.HasProductVariant() {
-			productVariants = append(productVariants, platformVndkVersion)
+			productVariantNeeded = true
 		}
 	} else if vendorSpecific && m.SdkVersion() == "" {
 		// This will be available in /vendor (or /odm) only
-
-		// kernel_headers is a special module type whose exported headers
-		// are coming from DeviceKernelHeaders() which is always vendor
-		// dependent. They'll always have both vendor variants.
-		// For other modules, we assume that modules under proprietary
-		// paths are compatible for BOARD_VNDK_VERSION. The other modules
-		// are regarded as AOSP, which is PLATFORM_VNDK_VERSION.
-		if m.KernelHeadersDecorator() {
-			vendorVariants = append(vendorVariants,
-				platformVndkVersion,
-				boardVndkVersion,
-			)
-		} else if snapshot.IsVendorProprietaryModule(mctx) {
-			vendorVariants = append(vendorVariants, boardVndkVersion)
-		} else {
-			vendorVariants = append(vendorVariants, platformVndkVersion)
-		}
+		vendorVariantNeeded = true
 	} else {
 		// This is either in /system (or similar: /data), or is a
 		// module built with the NDK. Modules built with the NDK
@@ -523,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, platformVndkVersion)
+		productVariantNeeded = true
 	}
 
 	if m.RamdiskAvailable() {
@@ -553,45 +413,32 @@
 		coreVariantNeeded = false
 	}
 
-	// If using a snapshot, the recovery variant under AOSP directories is not needed,
-	// except for kernel headers, which needs all variants.
-	if !m.KernelHeadersDecorator() &&
-		!m.IsSnapshotPrebuilt() &&
-		usingRecoverySnapshot &&
-		!snapshot.IsRecoveryProprietaryModule(mctx) {
-		recoveryVariantNeeded = 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
 }
@@ -675,38 +522,29 @@
 	}
 }
 
-func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
-	m := module.(*Module)
+func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string) {
 	if variant == android.RamdiskVariation {
-		m.MakeAsPlatform()
-		squashRamdiskSrcs(m)
+		c.MakeAsPlatform()
+		squashRamdiskSrcs(c)
 	} else if variant == android.VendorRamdiskVariation {
-		m.MakeAsPlatform()
-		squashVendorRamdiskSrcs(m)
+		c.MakeAsPlatform()
+		squashVendorRamdiskSrcs(c)
 	} else if variant == android.RecoveryVariation {
-		m.MakeAsPlatform()
-		squashRecoverySrcs(m)
-	} else if strings.HasPrefix(variant, VendorVariation) {
-		m.Properties.ImageVariation = VendorVariation
+		c.MakeAsPlatform()
+		squashRecoverySrcs(c)
+	} else if strings.HasPrefix(variant, android.VendorVariation) {
+		c.Properties.ImageVariation = android.VendorVariation
 
 		if strings.HasPrefix(variant, VendorVariationPrefix) {
-			m.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
+			c.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
 		}
-		squashVendorSrcs(m)
-
-		// Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
-		// Hide other vendor variants to avoid collision.
-		vndkVersion := ctx.DeviceConfig().VndkVersion()
-		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
-			m.Properties.HideFromMake = true
-			m.HideFromMake()
-		}
-	} else if strings.HasPrefix(variant, ProductVariation) {
-		m.Properties.ImageVariation = ProductVariation
+		squashVendorSrcs(c)
+	} else if strings.HasPrefix(variant, android.ProductVariation) {
+		c.Properties.ImageVariation = android.ProductVariation
 		if strings.HasPrefix(variant, ProductVariationPrefix) {
-			m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
+			c.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
 		}
-		squashProductSrcs(m)
+		squashProductSrcs(c)
 	}
 
 	if c.NeedsVendorPublicLibraryVariants() &&
diff --git a/cc/library.go b/cc/library.go
index 5607632..560b1ae 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"io"
-	"log"
 	"path/filepath"
 	"regexp"
 	"strconv"
@@ -150,7 +149,7 @@
 
 	Sanitized Sanitized `android:"arch_variant"`
 
-	Cflags []string `android:"arch_variant"`
+	Cflags proptools.Configurable[[]string] `android:"arch_variant"`
 
 	Enabled            *bool    `android:"arch_variant"`
 	Whole_static_libs  []string `android:"arch_variant"`
@@ -191,7 +190,7 @@
 	// be added to the include path (using -I) for this module and any module that links
 	// against this module.  Directories listed in export_include_dirs do not need to be
 	// listed in local_include_dirs.
-	Export_include_dirs []string `android:"arch_variant,variant_prepend"`
+	Export_include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of directories that will be added to the system include path
 	// using -isystem for this module and any module that links against this module.
@@ -275,11 +274,12 @@
 type flagExporter struct {
 	Properties FlagExporterProperties
 
-	dirs       android.Paths // Include directories to be included with -I
-	systemDirs android.Paths // System include directories to be included with -isystem
-	flags      []string      // Exported raw flags.
-	deps       android.Paths
-	headers    android.Paths
+	dirs         android.Paths // Include directories to be included with -I
+	systemDirs   android.Paths // System include directories to be included with -isystem
+	flags        []string      // Exported raw flags.
+	deps         android.Paths
+	headers      android.Paths
+	rustRlibDeps []RustRlibDep
 }
 
 // exportedIncludes returns the effective include paths for this module and
@@ -292,7 +292,11 @@
 	if ctx.inProduct() && f.Properties.Target.Product.Override_export_include_dirs != nil {
 		return android.PathsForModuleSrc(ctx, f.Properties.Target.Product.Override_export_include_dirs)
 	}
-	return android.PathsForModuleSrc(ctx, f.Properties.Export_include_dirs)
+	return android.PathsForModuleSrc(ctx, f.Properties.Export_include_dirs.GetOrDefault(ctx, nil))
+}
+
+func (f *flagExporter) exportedSystemIncludes(ctx ModuleContext) android.Paths {
+	return android.PathsForModuleSrc(ctx, f.Properties.Export_system_include_dirs)
 }
 
 // exportIncludes registers the include directories and system include directories to be exported
@@ -336,6 +340,10 @@
 	f.deps = append(f.deps, deps...)
 }
 
+func (f *flagExporter) reexportRustStaticDeps(deps ...RustRlibDep) {
+	f.rustRlibDeps = append(f.rustRlibDeps, deps...)
+}
+
 // addExportedGeneratedHeaders does nothing but collects generated header files.
 // This can be differ to exportedDeps which may contain phony files to minimize ninja.
 func (f *flagExporter) addExportedGeneratedHeaders(headers ...android.Path) {
@@ -353,6 +361,8 @@
 		// Used sparingly, for extra files that need to be explicitly exported to dependers,
 		// or for phony files to minimize ninja.
 		Deps: f.deps,
+		// Used for exporting rlib deps of static libraries to dependents.
+		RustRlibDeps: f.rustRlibDeps,
 		// For exported generated headers, such as exported aidl headers, proto headers, or
 		// sysprop headers.
 		GeneratedHeaders: f.headers,
@@ -390,9 +400,6 @@
 	// Output archive of gcno coverage information files
 	coverageOutputFile android.OptionalPath
 
-	// linked Source Abi Dump
-	sAbiOutputFile android.OptionalPath
-
 	// Source Abi Diff
 	sAbiDiff android.Paths
 
@@ -412,11 +419,6 @@
 
 	postInstallCmds []string
 
-	// If useCoreVariant is true, the vendor variant of a VNDK library is
-	// not installed.
-	useCoreVariant       bool
-	checkSameCoreVariant bool
-
 	skipAPIDefine bool
 
 	// Decorated interfaces
@@ -424,126 +426,9 @@
 	*baseLinker
 	*baseInstaller
 
-	collectedSnapshotHeaders android.Paths
-
 	apiListCoverageXmlPath android.ModuleOutPath
 }
 
-func GlobHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths {
-	ret := android.Paths{}
-
-	// Headers in the source tree should be globbed. On the contrast, generated headers
-	// can't be globbed, and they should be manually collected.
-	// So, we first filter out intermediate directories (which contains generated headers)
-	// from exported directories, and then glob headers under remaining directories.
-	for _, path := range paths {
-		dir := path.String()
-		// Skip if dir is for generated headers
-		if strings.HasPrefix(dir, ctx.Config().OutDir()) {
-			continue
-		}
-
-		// libeigen wrongly exports the root directory "external/eigen". But only two
-		// subdirectories "Eigen" and "unsupported" contain exported header files. Even worse
-		// some of them have no extension. So we need special treatment for libeigen in order
-		// to glob correctly.
-		if dir == "external/eigen" {
-			// Only these two directories contains exported headers.
-			for _, subdir := range []string{"Eigen", "unsupported/Eigen"} {
-				globDir := "external/eigen/" + subdir + "/**/*"
-				glob, err := ctx.GlobWithDeps(globDir, nil)
-				if err != nil {
-					ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
-					return nil
-				}
-				for _, header := range glob {
-					if strings.HasSuffix(header, "/") {
-						continue
-					}
-					ext := filepath.Ext(header)
-					if ext != "" && ext != ".h" {
-						continue
-					}
-					ret = append(ret, android.PathForSource(ctx, header))
-				}
-			}
-			continue
-		}
-		globDir := dir + "/**/*"
-		glob, err := ctx.GlobWithDeps(globDir, nil)
-		if err != nil {
-			ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
-			return nil
-		}
-		isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
-		for _, header := range glob {
-			if isLibcxx {
-				// Glob all files under this special directory, because of C++ headers with no
-				// extension.
-				if strings.HasSuffix(header, "/") {
-					continue
-				}
-			} else {
-				// Filter out only the files with extensions that are headers.
-				found := false
-				for _, ext := range HeaderExts {
-					if strings.HasSuffix(header, ext) {
-						found = true
-						break
-					}
-				}
-				if !found {
-					continue
-				}
-			}
-			ret = append(ret, android.PathForSource(ctx, header))
-		}
-	}
-	return ret
-}
-
-func GlobGeneratedHeadersForSnapshot(_ android.ModuleContext, paths android.Paths) android.Paths {
-	ret := android.Paths{}
-	for _, header := range paths {
-		// TODO(b/148123511): remove exportedDeps after cleaning up genrule
-		if strings.HasSuffix(header.Base(), "-phony") {
-			continue
-		}
-		ret = append(ret, header)
-	}
-	return ret
-}
-
-// collectHeadersForSnapshot collects all exported headers from library.
-// It globs header files in the source tree for exported include directories,
-// and tracks generated header files separately.
-//
-// This is to be called from GenerateAndroidBuildActions, and then collected
-// header files can be retrieved by snapshotHeaders().
-func (l *libraryDecorator) collectHeadersForSnapshot(ctx android.ModuleContext) {
-	ret := android.Paths{}
-
-	// Headers in the source tree should be globbed. On the contrast, generated headers
-	// can't be globbed, and they should be manually collected.
-	// So, we first filter out intermediate directories (which contains generated headers)
-	// from exported directories, and then glob headers under remaining directories.
-	ret = append(ret, GlobHeadersForSnapshot(ctx, append(android.CopyOfPaths(l.flagExporter.dirs), l.flagExporter.systemDirs...))...)
-
-	// Collect generated headers
-	ret = append(ret, GlobGeneratedHeadersForSnapshot(ctx, append(android.CopyOfPaths(l.flagExporter.headers), l.flagExporter.deps...))...)
-
-	l.collectedSnapshotHeaders = ret
-}
-
-// This returns all exported header files, both generated ones and headers from source tree.
-// collectHeadersForSnapshot() must be called before calling this.
-func (l *libraryDecorator) snapshotHeaders() android.Paths {
-	if l.collectedSnapshotHeaders == nil {
-		panic("snapshotHeaders() must be called after collectHeadersForSnapshot()")
-	}
-	return l.collectedSnapshotHeaders
-}
-
 // linkerProps returns the list of properties structs relevant for this library. (For example, if
 // the library is cc_shared_library, then static-library properties are omitted.)
 func (library *libraryDecorator) linkerProps() []interface{} {
@@ -579,9 +464,9 @@
 	}
 
 	if library.static() {
-		flags.Local.CFlags = append(flags.Local.CFlags, library.StaticProperties.Static.Cflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, library.StaticProperties.Static.Cflags.GetOrDefault(ctx, nil)...)
 	} else if library.shared() {
-		flags.Local.CFlags = append(flags.Local.CFlags, library.SharedProperties.Shared.Cflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, library.SharedProperties.Shared.Cflags.GetOrDefault(ctx, nil)...)
 	}
 
 	if library.shared() {
@@ -756,15 +641,11 @@
 		return Objects{}
 	}
 	if library.sabi.shouldCreateSourceAbiDump() {
-		exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
-		var SourceAbiFlags []string
-		for _, dir := range exportIncludeDirs.Strings() {
-			SourceAbiFlags = append(SourceAbiFlags, "-I"+dir)
+		dirs := library.exportedIncludeDirsForAbiCheck(ctx)
+		flags.SAbiFlags = make([]string, 0, len(dirs))
+		for _, dir := range dirs {
+			flags.SAbiFlags = append(flags.SAbiFlags, "-I"+dir)
 		}
-		for _, reexportedInclude := range library.sabi.Properties.ReexportedIncludes {
-			SourceAbiFlags = append(SourceAbiFlags, "-I"+reexportedInclude)
-		}
-		flags.SAbiFlags = SourceAbiFlags
 		totalLength := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) +
 			len(library.SharedProperties.Shared.Srcs) + len(library.StaticProperties.Static.Srcs)
 		if totalLength > 0 {
@@ -870,20 +751,6 @@
 func (library *libraryDecorator) getLibName(ctx BaseModuleContext) string {
 	name := library.getLibNameHelper(ctx.baseModuleName(), ctx.inVendor(), ctx.inProduct())
 
-	// Replace name with VNDK ext as original lib only when VNDK is enabled
-	if ctx.IsVndkExt() {
-		if ctx.DeviceConfig().VndkVersion() != "" {
-			// vndk-ext lib should have the same name with original lib
-			ctx.VisitDirectDepsWithTag(vndkExtDepTag, func(module android.Module) {
-				originalName := module.(*Module).outputFile.Path()
-				name = strings.TrimSuffix(originalName.Base(), originalName.Ext())
-			})
-		} else {
-			// TODO(b/320208784) : Suggest a solution for former VNDK-ext libraries before VNDK deprecation.
-			log.Printf("VNDK Extension on module %s will not be available once VNDK is deprecated", ctx.baseModuleName())
-		}
-	}
-
 	if ctx.Host() && Bool(library.Properties.Unique_host_soname) {
 		if !strings.HasSuffix(name, "-host") {
 			name = name + "-host"
@@ -983,6 +850,8 @@
 
 		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.SharedProperties.Shared.Export_shared_lib_headers...)
 		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.SharedProperties.Shared.Export_static_lib_headers...)
+
+		deps.LlndkHeaderLibs = append(deps.LlndkHeaderLibs, library.Properties.Llndk.Export_llndk_headers...)
 	}
 	if ctx.inVendor() {
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
@@ -1265,9 +1134,18 @@
 	linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
+
+	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,
-		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
-		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles)
+		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin,
+		deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles)
 
 	objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
 	objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -1275,7 +1153,7 @@
 	objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.WholeStaticLibObjs.sAbiDumpFiles...)
 
 	library.coverageOutputFile = transformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
-	library.linkSAbiDumpFiles(ctx, objs, fileName, unstrippedOutputFile)
+	library.linkSAbiDumpFiles(ctx, deps, objs, fileName, unstrippedOutputFile)
 
 	var transitiveStaticLibrariesForOrdering *android.DepSet[android.Path]
 	if static := ctx.GetDirectDepsWithTag(staticVariantTag); len(static) > 0 {
@@ -1338,6 +1216,75 @@
 	return library.coverageOutputFile
 }
 
+func (library *libraryDecorator) exportedIncludeDirsForAbiCheck(ctx ModuleContext) []string {
+	exportIncludeDirs := library.flagExporter.exportedIncludes(ctx).Strings()
+	exportIncludeDirs = append(exportIncludeDirs, library.sabi.Properties.ReexportedIncludes...)
+	exportSystemIncludeDirs := library.flagExporter.exportedSystemIncludes(ctx).Strings()
+	exportSystemIncludeDirs = append(exportSystemIncludeDirs, library.sabi.Properties.ReexportedSystemIncludes...)
+	// The ABI checker does not distinguish normal and system headers.
+	return append(exportIncludeDirs, exportSystemIncludeDirs...)
+}
+
+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()...)
+	} else {
+		includeDirs = append(includeDirs, library.flagExporter.exportedIncludes(ctx).Strings()...)
+		// Ignore library.sabi.Properties.ReexportedIncludes because
+		// LLNDK does not reexport the implementation's dependencies, such as export_header_libs.
+	}
+
+	systemIncludeDirs = append(systemIncludeDirs,
+		library.flagExporter.exportedSystemIncludes(ctx).Strings()...)
+	if Bool(library.Properties.Llndk.Export_headers_as_system) {
+		systemIncludeDirs = append(systemIncludeDirs, includeDirs...)
+		includeDirs = nil
+	}
+	// Header libs.
+	includeDirs = append(includeDirs, deps.LlndkIncludeDirs.Strings()...)
+	systemIncludeDirs = append(systemIncludeDirs, deps.LlndkSystemIncludeDirs.Strings()...)
+	// The ABI checker does not distinguish normal and system headers.
+	return append(includeDirs, systemIncludeDirs...)
+}
+
+func (library *libraryDecorator) linkLlndkSAbiDumpFiles(ctx ModuleContext,
+	deps PathDeps, sAbiDumpFiles android.Paths, soFile android.Path, libFileName string,
+	excludeSymbolVersions, excludeSymbolTags []string,
+	vendorApiLevel string) android.Path {
+	// NDK symbols in version 34 are LLNDK symbols. Those in version 35 are not.
+	return transformDumpToLinkedDump(ctx,
+		sAbiDumpFiles, soFile, libFileName+".llndk",
+		library.llndkIncludeDirsForAbiCheck(ctx, deps),
+		android.OptionalPathForModuleSrc(ctx, library.Properties.Llndk.Symbol_file),
+		append([]string{"*_PLATFORM", "*_PRIVATE"}, excludeSymbolVersions...),
+		append([]string{"platform-only"}, excludeSymbolTags...),
+		[]string{"llndk=" + vendorApiLevel}, "34", true /* isLlndk */)
+}
+
+func (library *libraryDecorator) linkApexSAbiDumpFiles(ctx ModuleContext,
+	deps PathDeps, sAbiDumpFiles android.Paths, soFile android.Path, libFileName string,
+	excludeSymbolVersions, excludeSymbolTags []string,
+	sdkVersion string) android.Path {
+	return transformDumpToLinkedDump(ctx,
+		sAbiDumpFiles, soFile, libFileName+".apex",
+		library.exportedIncludeDirsForAbiCheck(ctx),
+		android.OptionalPathForModuleSrc(ctx, library.Properties.Stubs.Symbol_file),
+		append([]string{"*_PLATFORM", "*_PRIVATE"}, excludeSymbolVersions...),
+		append([]string{"platform-only"}, excludeSymbolTags...),
+		[]string{"apex", "systemapi"}, sdkVersion, false /* isLlndk */)
+}
+
 func getRefAbiDumpFile(ctx android.ModuleInstallPathContext,
 	versionedDumpDir, fileName string) android.OptionalPath {
 
@@ -1353,21 +1300,21 @@
 }
 
 // Return the previous and current SDK versions for cross-version ABI diff.
-func crossVersionAbiDiffSdkVersions(ctx ModuleContext, dumpDir string) (string, string) {
+func crossVersionAbiDiffSdkVersions(ctx ModuleContext, dumpDir string) (int, int) {
 	sdkVersionInt := ctx.Config().PlatformSdkVersion().FinalInt()
-	sdkVersionStr := ctx.Config().PlatformSdkVersion().String()
 
 	if ctx.Config().PlatformSdkFinal() {
-		return strconv.Itoa(sdkVersionInt - 1), sdkVersionStr
+		return sdkVersionInt - 1, sdkVersionInt
 	} else {
 		// The platform SDK version can be upgraded before finalization while the corresponding abi dumps hasn't
 		// been generated. Thus the Cross-Version Check chooses PLATFORM_SDK_VERION - 1 as previous version.
 		// This situation could be identified by checking the existence of the PLATFORM_SDK_VERION dump directory.
-		versionedDumpDir := android.ExistentPathForSource(ctx, dumpDir, sdkVersionStr)
+		versionedDumpDir := android.ExistentPathForSource(ctx,
+			dumpDir, ctx.Config().PlatformSdkVersion().String())
 		if versionedDumpDir.Valid() {
-			return sdkVersionStr, strconv.Itoa(sdkVersionInt + 1)
+			return sdkVersionInt, sdkVersionInt + 1
 		} else {
-			return strconv.Itoa(sdkVersionInt - 1), sdkVersionStr
+			return sdkVersionInt - 1, sdkVersionInt
 		}
 	}
 }
@@ -1384,12 +1331,11 @@
 }
 
 // sourceAbiDiff registers a build statement to compare linked sAbi dump files (.lsdump).
-func (library *libraryDecorator) sourceAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
-	baseName, nameExt string, isLlndkOrNdk, allowExtensions bool,
+func (library *libraryDecorator) sourceAbiDiff(ctx android.ModuleContext,
+	sourceDump, referenceDump android.Path,
+	baseName, nameExt string, isLlndk, allowExtensions bool,
 	sourceVersion, errorMessage string) {
 
-	sourceDump := library.sAbiOutputFile.Path()
-
 	extraFlags := []string{"-target-version", sourceVersion}
 	headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
 	if Bool(headerAbiChecker.Check_all_apis) {
@@ -1399,7 +1345,7 @@
 			"-allow-unreferenced-changes",
 			"-allow-unreferenced-elf-symbol-changes")
 	}
-	if isLlndkOrNdk {
+	if isLlndk {
 		extraFlags = append(extraFlags, "-consider-opaque-types-different")
 	}
 	if allowExtensions {
@@ -1413,102 +1359,174 @@
 			baseName, nameExt, extraFlags, errorMessage))
 }
 
-func (library *libraryDecorator) crossVersionAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
-	baseName string, isLlndkOrNdk bool, sourceVersion, prevVersion string) {
+func (library *libraryDecorator) crossVersionAbiDiff(ctx android.ModuleContext,
+	sourceDump, referenceDump android.Path,
+	baseName, nameExt string, isLlndk bool, sourceVersion, prevDumpDir string) {
 
-	errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/main/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the ABI difference between your source code and version " + prevVersion + "."
+	errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/main/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the difference between your source code and the ABI dumps in " + prevDumpDir
 
-	library.sourceAbiDiff(ctx, referenceDump, baseName, prevVersion,
-		isLlndkOrNdk, true /* allowExtensions */, sourceVersion, errorMessage)
+	library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt,
+		isLlndk, true /* allowExtensions */, sourceVersion, errorMessage)
 }
 
-func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
-	baseName, nameExt string, isLlndkOrNdk bool) {
+func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext,
+	sourceDump, referenceDump android.Path,
+	baseName, nameExt string, isLlndk bool, lsdumpTagName string) {
 
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
-	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName
+	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py --lib " + libName + " --lib-variant " + lsdumpTagName
 
-	library.sourceAbiDiff(ctx, referenceDump, baseName, nameExt,
-		isLlndkOrNdk, false /* allowExtensions */, "current", errorMessage)
-}
-
-func (library *libraryDecorator) optInAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
-	baseName, nameExt string, refDumpDir string) {
-
-	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
-	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName + " -ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir
-	// Most opt-in libraries do not have dumps for all default architectures.
-	if ctx.Config().HasDeviceProduct() {
-		errorMessage += " -products " + ctx.Config().DeviceProduct()
+	targetRelease := ctx.Config().Getenv("TARGET_RELEASE")
+	if targetRelease != "" {
+		errorMessage += " --release " + targetRelease
 	}
 
-	library.sourceAbiDiff(ctx, referenceDump, baseName, nameExt,
-		false /* isLlndkOrNdk */, false /* allowExtensions */, "current", errorMessage)
+	library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt,
+		isLlndk, false /* allowExtensions */, "current", errorMessage)
 }
 
-func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
+func (library *libraryDecorator) optInAbiDiff(ctx android.ModuleContext,
+	sourceDump, referenceDump android.Path,
+	baseName, nameExt string, refDumpDir string, lsdumpTagName string) {
+
+	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
+	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py --lib " + libName + " --lib-variant " + lsdumpTagName + " --ref-dump-dir $$ANDROID_BUILD_TOP/" + refDumpDir
+
+	targetRelease := ctx.Config().Getenv("TARGET_RELEASE")
+	if targetRelease != "" {
+		errorMessage += " --release " + targetRelease
+	}
+
+	// Most opt-in libraries do not have dumps for all default architectures.
+	if ctx.Config().HasDeviceProduct() {
+		errorMessage += " --product " + ctx.Config().DeviceProduct()
+	}
+
+	library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt,
+		false /* isLlndk */, false /* allowExtensions */, "current", errorMessage)
+}
+
+func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathDeps, objs Objects, fileName string, soFile android.Path) {
 	if library.sabi.shouldCreateSourceAbiDump() {
-		exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
-		var SourceAbiFlags []string
-		for _, dir := range exportIncludeDirs.Strings() {
-			SourceAbiFlags = append(SourceAbiFlags, "-I"+dir)
-		}
-		for _, reexportedInclude := range library.sabi.Properties.ReexportedIncludes {
-			SourceAbiFlags = append(SourceAbiFlags, "-I"+reexportedInclude)
-		}
-		exportedHeaderFlags := strings.Join(SourceAbiFlags, " ")
+		exportedIncludeDirs := library.exportedIncludeDirsForAbiCheck(ctx)
 		headerAbiChecker := library.getHeaderAbiCheckerProperties(ctx)
 		currSdkVersion := currRefAbiDumpSdkVersion(ctx)
 		currVendorVersion := ctx.Config().VendorApiLevel()
-		library.sAbiOutputFile = transformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags,
+
+		// Generate source dumps.
+		implDump := transformDumpToLinkedDump(ctx,
+			objs.sAbiDumpFiles, soFile, fileName,
+			exportedIncludeDirs,
 			android.OptionalPathForModuleSrc(ctx, library.symbolFileForAbiCheck(ctx)),
 			headerAbiChecker.Exclude_symbol_versions,
 			headerAbiChecker.Exclude_symbol_tags,
-			currSdkVersion)
+			[]string{} /* includeSymbolTags */, currSdkVersion, false /* isLlndk */)
 
-		for _, tag := range classifySourceAbiDump(ctx) {
-			addLsdumpPath(string(tag) + ":" + library.sAbiOutputFile.String())
+		var llndkDump, apexVariantDump android.Path
+		tags := classifySourceAbiDump(ctx)
+		optInTags := []lsdumpTag{}
+		for _, tag := range tags {
+			if tag == llndkLsdumpTag && currVendorVersion != "" {
+				if llndkDump == nil {
+					// TODO(b/323447559): Evaluate if replacing sAbiDumpFiles with implDump is faster
+					llndkDump = library.linkLlndkSAbiDumpFiles(ctx,
+						deps, objs.sAbiDumpFiles, soFile, fileName,
+						headerAbiChecker.Exclude_symbol_versions,
+						headerAbiChecker.Exclude_symbol_tags,
+						currVendorVersion)
+				}
+				addLsdumpPath(string(tag) + ":" + llndkDump.String())
+			} else if tag == apexLsdumpTag {
+				if apexVariantDump == nil {
+					apexVariantDump = library.linkApexSAbiDumpFiles(ctx,
+						deps, objs.sAbiDumpFiles, soFile, fileName,
+						headerAbiChecker.Exclude_symbol_versions,
+						headerAbiChecker.Exclude_symbol_tags,
+						currSdkVersion)
+				}
+				addLsdumpPath(string(tag) + ":" + apexVariantDump.String())
+			} else {
+				if tag.dirName() == "" {
+					optInTags = append(optInTags, tag)
+				}
+				addLsdumpPath(string(tag) + ":" + implDump.String())
+			}
+		}
 
+		// Diff source dumps and reference dumps.
+		for _, tag := range tags {
 			dumpDirName := tag.dirName()
 			if dumpDirName == "" {
 				continue
 			}
 			dumpDir := filepath.Join("prebuilts", "abi-dumps", dumpDirName)
 			isLlndk := (tag == llndkLsdumpTag)
-			isNdk := (tag == ndkLsdumpTag)
+			isApex := (tag == apexLsdumpTag)
 			binderBitness := ctx.DeviceConfig().BinderBitness()
 			nameExt := ""
 			if isLlndk {
 				nameExt = "llndk"
+			} else if isApex {
+				nameExt = "apex"
 			}
 			// Check against the previous version.
 			var prevVersion, currVersion string
+			sourceDump := implDump
 			// If this release config does not define VendorApiLevel, fall back to the old policy.
 			if isLlndk && currVendorVersion != "" {
 				prevVersion = ctx.Config().PrevVendorApiLevel()
 				currVersion = currVendorVersion
+				// LLNDK dumps are generated by different rules after trunk stable.
+				if android.IsTrunkStableVendorApiLevel(prevVersion) {
+					sourceDump = llndkDump
+				}
 			} else {
-				prevVersion, currVersion = crossVersionAbiDiffSdkVersions(ctx, dumpDir)
+				prevVersionInt, currVersionInt := crossVersionAbiDiffSdkVersions(ctx, dumpDir)
+				prevVersion = strconv.Itoa(prevVersionInt)
+				currVersion = strconv.Itoa(currVersionInt)
+				// APEX dumps are generated by different rules after trunk stable.
+				if isApex && prevVersionInt > 34 {
+					sourceDump = apexVariantDump
+				}
 			}
 			prevDumpDir := filepath.Join(dumpDir, prevVersion, binderBitness)
 			prevDumpFile := getRefAbiDumpFile(ctx, prevDumpDir, fileName)
 			if prevDumpFile.Valid() {
-				library.crossVersionAbiDiff(ctx, prevDumpFile.Path(),
-					fileName, isLlndk || isNdk, currVersion, nameExt+prevVersion)
+				library.crossVersionAbiDiff(ctx, sourceDump, prevDumpFile.Path(),
+					fileName, nameExt+prevVersion, isLlndk, currVersion, prevDumpDir)
 			}
 			// Check against the current version.
+			sourceDump = implDump
 			if isLlndk && currVendorVersion != "" {
 				currVersion = currVendorVersion
+				if android.IsTrunkStableVendorApiLevel(currVersion) {
+					sourceDump = llndkDump
+				}
 			} else {
 				currVersion = currSdkVersion
+				if isApex && (!ctx.Config().PlatformSdkFinal() ||
+					ctx.Config().PlatformSdkVersion().FinalInt() > 34) {
+					sourceDump = apexVariantDump
+				}
 			}
 			currDumpDir := filepath.Join(dumpDir, currVersion, binderBitness)
 			currDumpFile := getRefAbiDumpFile(ctx, currDumpDir, fileName)
 			if currDumpFile.Valid() {
-				library.sameVersionAbiDiff(ctx, currDumpFile.Path(),
-					fileName, nameExt, isLlndk || isNdk)
+				library.sameVersionAbiDiff(ctx, sourceDump, currDumpFile.Path(),
+					fileName, nameExt, isLlndk, string(tag))
 			}
 		}
+
+		// Assert that a module is tagged with at most one of platformLsdumpTag, productLsdumpTag, or vendorLsdumpTag.
+		if len(headerAbiChecker.Ref_dump_dirs) > 0 && len(optInTags) != 1 {
+			ctx.ModuleErrorf("Expect exactly one opt-in lsdump tag when ref_dump_dirs are specified: %s", optInTags)
+			return
+		}
+		// Ensure that a module tagged with only platformLsdumpTag has ref_dump_dirs.
+		// Android.bp in vendor projects should be cleaned up before this is enforced for vendorLsdumpTag and productLsdumpTag.
+		if len(headerAbiChecker.Ref_dump_dirs) == 0 && len(tags) == 1 && tags[0] == platformLsdumpTag {
+			ctx.ModuleErrorf("header_abi_checker is explicitly enabled, but no ref_dump_dirs are specified.")
+		}
 		// Check against the opt-in reference dumps.
 		for i, optInDumpDir := range headerAbiChecker.Ref_dump_dirs {
 			optInDumpDirPath := android.PathForModuleSrc(ctx, optInDumpDir)
@@ -1518,8 +1536,9 @@
 			if !optInDumpFile.Valid() {
 				continue
 			}
-			library.optInAbiDiff(ctx, optInDumpFile.Path(),
-				fileName, "opt"+strconv.Itoa(i), optInDumpDirPath.String())
+			library.optInAbiDiff(ctx,
+				implDump, optInDumpFile.Path(),
+				fileName, "opt"+strconv.Itoa(i), optInDumpDirPath.String(), string(optInTags[0]))
 		}
 	}
 }
@@ -1573,14 +1592,19 @@
 		// 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 {
-			library.flagExporter.Properties.Export_include_dirs = override
+			library.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](
+				nil,
+				[]proptools.ConfigurableCase[[]string]{
+					proptools.NewConfigurableCase[[]string](nil, &override),
+				},
+			)
 		}
 
 		if Bool(library.Properties.Llndk.Export_headers_as_system) {
 			library.flagExporter.Properties.Export_system_include_dirs = append(
 				library.flagExporter.Properties.Export_system_include_dirs,
-				library.flagExporter.Properties.Export_include_dirs...)
-			library.flagExporter.Properties.Export_include_dirs = nil
+				library.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil)...)
+			library.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](nil, nil)
 		}
 	}
 
@@ -1588,7 +1612,12 @@
 		// override the module's export_include_dirs with vendor_public_library.override_export_include_dirs
 		// if it is set.
 		if override := library.Properties.Vendor_public_library.Override_export_include_dirs; override != nil {
-			library.flagExporter.Properties.Export_include_dirs = override
+			library.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](
+				nil,
+				[]proptools.ConfigurableCase[[]string]{
+					proptools.NewConfigurableCase[[]string](nil, &override),
+				},
+			)
 		}
 	}
 
@@ -1611,6 +1640,10 @@
 	library.reexportDeps(deps.ReexportedDeps...)
 	library.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
 
+	if library.static() && len(deps.ReexportedRustRlibDeps) > 0 {
+		library.reexportRustStaticDeps(deps.ReexportedRustRlibDeps...)
+	}
+
 	// Optionally export aidl headers.
 	if Bool(library.Properties.Aidl.Export_aidl_headers) {
 		if library.baseCompiler.hasAidl(deps) {
@@ -1733,43 +1766,7 @@
 
 func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) {
 	if library.shared() {
-		if ctx.Device() && ctx.useVndk() {
-			// set subDir for VNDK extensions
-			if ctx.IsVndkExt() {
-				if ctx.isVndkSp() {
-					library.baseInstaller.subDir = "vndk-sp"
-				} else {
-					library.baseInstaller.subDir = "vndk"
-				}
-			}
-
-			// In some cases we want to use core variant for VNDK-Core libs.
-			// Skip product variant since VNDKs use only the vendor variant.
-			if ctx.isVndk() && !ctx.isVndkSp() && !ctx.IsVndkExt() && !ctx.inProduct() {
-				mayUseCoreVariant := true
-
-				if ctx.mustUseVendorVariant() {
-					mayUseCoreVariant = false
-				}
-
-				if ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) {
-					mayUseCoreVariant = false
-				}
-
-				if mayUseCoreVariant {
-					library.checkSameCoreVariant = true
-					if ctx.DeviceConfig().VndkUseCoreVariant() {
-						library.useCoreVariant = true
-					}
-				}
-			}
-
-			// do not install vndk libs
-			// vndk libs are packaged into VNDK APEX
-			if ctx.isVndk() && !ctx.IsVndkExt() && !ctx.Config().IsVndkDeprecated() && !ctx.inProduct() {
-				return
-			}
-		} else if library.hasStubsVariants() && !ctx.Host() && ctx.directlyInAnyApex() {
+		if library.hasStubsVariants() && !ctx.Host() && ctx.directlyInAnyApex() {
 			// Bionic libraries (e.g. libc.so) is installed to the bootstrap subdirectory.
 			// The original path becomes a symlink to the corresponding file in the
 			// runtime APEX.
@@ -1885,9 +1882,6 @@
 	if props := library.getHeaderAbiCheckerProperties(ctx); props.Symbol_file != nil {
 		return props.Symbol_file
 	}
-	if ctx.Module().(*Module).IsLlndk() {
-		return library.Properties.Llndk.Symbol_file
-	}
 	if library.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
 		return library.Properties.Stubs.Symbol_file
 	}
@@ -2076,8 +2070,8 @@
 
 		// Check libraries in addition to cflags, since libraries may be exporting different
 		// include directories.
-		if len(staticCompiler.StaticProperties.Static.Cflags) == 0 &&
-			len(sharedCompiler.SharedProperties.Shared.Cflags) == 0 &&
+		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 &&
@@ -2141,14 +2135,12 @@
 			// Header only
 		}
 
-	} else if library, ok := mctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() {
-
+	} else if library, ok := mctx.Module().(LinkableInterface); ok && (library.CcLibraryInterface() || library.RustLibraryInterface()) {
 		// Non-cc.Modules may need an empty variant for their mutators.
 		variations := []string{}
 		if library.NonCcVariants() {
 			variations = append(variations, "")
 		}
-
 		isLLNDK := false
 		if m, ok := mctx.Module().(*Module); ok {
 			isLLNDK = m.IsLlndk()
@@ -2161,7 +2153,6 @@
 			modules := mctx.CreateLocalVariations(variations...)
 			static := modules[0].(LinkableInterface)
 			shared := modules[1].(LinkableInterface)
-
 			static.SetStatic()
 			shared.SetShared()
 
@@ -2185,6 +2176,12 @@
 			mctx.CreateLocalVariations(variations...)
 			mctx.AliasVariation(variations[0])
 		}
+		if library.BuildRlibVariant() && library.IsRustFFI() && !buildStatic {
+			// 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.
+			mctx.CreateAliasVariation("static", "")
+		}
 	}
 }
 
@@ -2223,8 +2220,12 @@
 		if variants[i] != "" || isLLNDK || isVendorPublicLibrary || isImportedApiLibrary {
 			// A stubs or LLNDK stubs variant.
 			c := m.(*Module)
-			c.sanitize = nil
-			c.stl = nil
+			if c.sanitize != nil {
+				c.sanitize.Properties.ForceDisable = true
+			}
+			if c.stl != nil {
+				c.stl.Properties.Stl = StringPtr("none")
+			}
 			c.Properties.PreventInstall = true
 			lib := moduleLibraryInterface(m)
 			isLatest := i == (len(versions) - 1)
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index a65b1ba..1f71c19 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",
 }
diff --git a/cc/library_stub.go b/cc/library_stub.go
index aab6664..6f06333 100644
--- a/cc/library_stub.go
+++ b/cc/library_stub.go
@@ -20,6 +20,8 @@
 
 	"android/soong/android"
 	"android/soong/multitree"
+
+	"github.com/google/blueprint/proptools"
 )
 
 var (
@@ -122,7 +124,7 @@
 // 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 {
+	for _, dir := range exporterProps.Export_include_dirs.GetOrDefault(ctx, nil) {
 		d.dirs = append(d.dirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
 	}
 	// system headers
@@ -178,16 +180,21 @@
 				in = variantMod.Src()
 
 				// Copy LLDNK properties to cc_api_library module
-				d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append(
-					d.libraryDecorator.flagExporter.Properties.Export_include_dirs,
+				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...)
-					d.libraryDecorator.flagExporter.Properties.Export_include_dirs = nil
+						d.libraryDecorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil)...)
+					d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](nil, nil)
 				}
 			}
 		}
@@ -487,6 +494,12 @@
 
 // Implement ImageInterface to generate image variants
 func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+func (v *CcApiVariant) VendorVariantNeeded(ctx android.BaseModuleContext) bool {
+	return String(v.properties.Variant) == "llndk"
+}
+func (v *CcApiVariant) ProductVariantNeeded(ctx android.BaseModuleContext) bool {
+	return String(v.properties.Variant) == "llndk"
+}
 func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return inList(String(v.properties.Variant), []string{"ndk", "apex"})
 }
@@ -494,16 +507,6 @@
 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
-	platformVndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
-
-	if String(v.properties.Variant) == "llndk" {
-		variations = append(variations, VendorVariationPrefix+platformVndkVersion)
-		variations = append(variations, ProductVariationPrefix+platformVndkVersion)
-	}
-
-	return variations
-}
-func (v *CcApiVariant) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+func (v *CcApiVariant) ExtraImageVariations(ctx android.BaseModuleContext) []string   { return nil }
+func (v *CcApiVariant) SetImageVariation(ctx android.BaseModuleContext, variation string) {
 }
diff --git a/cc/library_stub_test.go b/cc/library_stub_test.go
deleted file mode 100644
index 528577a..0000000
--- a/cc/library_stub_test.go
+++ /dev/null
@@ -1,459 +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 (
-	_ "fmt"
-	_ "sort"
-
-	"testing"
-
-	"android/soong/android"
-
-	"github.com/google/blueprint"
-)
-
-func hasDirectDependency(t *testing.T, ctx *android.TestResult, from android.Module, to android.Module) bool {
-	t.Helper()
-	var found bool
-	ctx.VisitDirectDeps(from, func(dep blueprint.Module) {
-		if dep == to {
-			found = true
-		}
-	})
-	return found
-}
-
-func TestApiLibraryReplacesExistingModule(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libfoo",
-			shared_libs: ["libbar"],
-			vendor_available: true,
-		}
-
-		cc_library {
-			name: "libbar",
-		}
-
-		cc_api_library {
-			name: "libbar",
-			vendor_available: true,
-			src: "libbar.so",
-		}
-
-		api_imports {
-			name: "api_imports",
-			shared_libs: [
-				"libbar",
-			],
-		}
-	`
-
-	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
-	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
-	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
-
-	android.AssertBoolEquals(t, "original library should be linked with non-stub variant", true, hasDirectDependency(t, ctx, libfoo, libbar))
-	android.AssertBoolEquals(t, "Stub library from API surface should be not linked with non-stub variant", false, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
-
-	libfooVendor := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
-	libbarApiImportVendor := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
-
-	android.AssertBoolEquals(t, "original library should not be linked", false, hasDirectDependency(t, ctx, libfooVendor, libbar))
-	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfooVendor, libbarApiImportVendor))
-}
-
-func TestApiLibraryDoNotRequireOriginalModule(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libfoo",
-			shared_libs: ["libbar"],
-			vendor: true,
-		}
-
-		cc_api_library {
-			name: "libbar",
-			src: "libbar.so",
-			vendor_available: true,
-		}
-
-		api_imports {
-			name: "api_imports",
-			shared_libs: [
-				"libbar",
-			],
-		}
-	`
-
-	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-
-	libfoo := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
-	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
-
-	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
-}
-
-func TestApiLibraryShouldNotReplaceWithoutApiImport(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libfoo",
-			shared_libs: ["libbar"],
-			vendor_available: true,
-		}
-
-		cc_library {
-			name: "libbar",
-			vendor_available: true,
-		}
-
-		cc_api_library {
-			name: "libbar",
-			src: "libbar.so",
-			vendor_available: true,
-		}
-
-		api_imports {
-			name: "api_imports",
-			shared_libs: [],
-		}
-	`
-
-	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-
-	libfoo := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
-	libbar := ctx.ModuleForTests("libbar", "android_vendor.29_arm64_armv8-a_shared").Module()
-	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
-
-	android.AssertBoolEquals(t, "original library should be linked", true, hasDirectDependency(t, ctx, libfoo, libbar))
-	android.AssertBoolEquals(t, "Stub library from API surface should not be linked", false, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
-}
-
-func TestExportDirFromStubLibrary(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libfoo",
-			export_include_dirs: ["source_include_dir"],
-			export_system_include_dirs: ["source_system_include_dir"],
-			vendor_available: true,
-		}
-		cc_api_library {
-			name: "libfoo",
-			export_include_dirs: ["stub_include_dir"],
-			export_system_include_dirs: ["stub_system_include_dir"],
-			vendor_available: true,
-			src: "libfoo.so",
-		}
-		api_imports {
-			name: "api_imports",
-			shared_libs: [
-				"libfoo",
-			],
-			header_libs: [],
-		}
-		// vendor binary
-		cc_binary {
-			name: "vendorbin",
-			vendor: true,
-			srcs: ["vendor.cc"],
-			shared_libs: ["libfoo"],
-		}
-	`
-	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-	vendorCFlags := ctx.ModuleForTests("vendorbin", "android_vendor.29_arm64_armv8-a").Rule("cc").Args["cFlags"]
-	android.AssertStringDoesContain(t, "Vendor binary should compile using headers provided by stub", vendorCFlags, "-Istub_include_dir")
-	android.AssertStringDoesNotContain(t, "Vendor binary should not compile using headers of source", vendorCFlags, "-Isource_include_dir")
-	android.AssertStringDoesContain(t, "Vendor binary should compile using system headers provided by stub", vendorCFlags, "-isystem stub_system_include_dir")
-	android.AssertStringDoesNotContain(t, "Vendor binary should not compile using system headers of source", vendorCFlags, "-isystem source_system_include_dir")
-
-	vendorImplicits := ctx.ModuleForTests("vendorbin", "android_vendor.29_arm64_armv8-a").Rule("cc").OrderOnly.Strings()
-	// Building the stub.so file first assembles its .h files in multi-tree out.
-	// These header files are required for compiling the other API domain (vendor in this case)
-	android.AssertStringListContains(t, "Vendor binary compilation should have an implicit dep on the stub .so file", vendorImplicits, "libfoo.so")
-}
-
-func TestApiLibraryWithLlndkVariant(t *testing.T) {
-	bp := `
-		cc_binary {
-			name: "binfoo",
-			vendor: true,
-			srcs: ["binfoo.cc"],
-			shared_libs: ["libbar"],
-		}
-
-		cc_api_library {
-			name: "libbar",
-			// TODO(b/244244438) Remove src property once all variants are implemented.
-			src: "libbar.so",
-			vendor_available: true,
-			variants: [
-				"llndk",
-			],
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "llndk",
-			src: "libbar_llndk.so",
-			export_include_dirs: ["libbar_llndk_include"]
-		}
-
-		api_imports {
-			name: "api_imports",
-			shared_libs: [
-				"libbar",
-			],
-			header_libs: [],
-		}
-	`
-
-	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-
-	binfoo := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Module()
-	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
-	libbarApiVariant := ctx.ModuleForTests("libbar.llndk.apiimport", "android_vendor.29_arm64_armv8-a").Module()
-
-	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, binfoo, libbarApiImport))
-	android.AssertBoolEquals(t, "Stub library variant from API surface should be linked", true, hasDirectDependency(t, ctx, libbarApiImport, libbarApiVariant))
-
-	binFooLibFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("ld").Args["libFlags"]
-	android.AssertStringDoesContain(t, "Vendor binary should be linked with LLNDK variant source", binFooLibFlags, "libbar.llndk.apiimport.so")
-
-	binFooCFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("cc").Args["cFlags"]
-	android.AssertStringDoesContain(t, "Vendor binary should include headers from the LLNDK variant source", binFooCFlags, "-Ilibbar_llndk_include")
-}
-
-func TestApiLibraryWithNdkVariant(t *testing.T) {
-	bp := `
-		cc_binary {
-			name: "binfoo",
-			sdk_version: "29",
-			srcs: ["binfoo.cc"],
-			shared_libs: ["libbar"],
-			stl: "c++_shared",
-		}
-
-		cc_binary {
-			name: "binbaz",
-			sdk_version: "30",
-			srcs: ["binbaz.cc"],
-			shared_libs: ["libbar"],
-			stl: "c++_shared",
-		}
-
-		cc_binary {
-			name: "binqux",
-			srcs: ["binfoo.cc"],
-			shared_libs: ["libbar"],
-		}
-
-		cc_library {
-			name: "libbar",
-			srcs: ["libbar.cc"],
-		}
-
-		cc_api_library {
-			name: "libbar",
-			// TODO(b/244244438) Remove src property once all variants are implemented.
-			src: "libbar.so",
-			variants: [
-				"ndk.29",
-				"ndk.30",
-				"ndk.current",
-			],
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "ndk",
-			version: "29",
-			src: "libbar_ndk_29.so",
-			export_include_dirs: ["libbar_ndk_29_include"]
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "ndk",
-			version: "30",
-			src: "libbar_ndk_30.so",
-			export_include_dirs: ["libbar_ndk_30_include"]
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "ndk",
-			version: "current",
-			src: "libbar_ndk_current.so",
-			export_include_dirs: ["libbar_ndk_current_include"]
-		}
-
-		api_imports {
-			name: "api_imports",
-			shared_libs: [
-				"libbar",
-			],
-			header_libs: [],
-		}
-	`
-
-	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-
-	binfoo := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Module()
-	libbarApiImportv29 := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_sdk_shared_29").Module()
-	libbarApiVariantv29 := ctx.ModuleForTests("libbar.ndk.29.apiimport", "android_arm64_armv8-a_sdk").Module()
-	libbarApiImportv30 := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_sdk_shared_30").Module()
-	libbarApiVariantv30 := ctx.ModuleForTests("libbar.ndk.30.apiimport", "android_arm64_armv8-a_sdk").Module()
-
-	android.AssertBoolEquals(t, "Stub library from API surface should be linked with target version", true, hasDirectDependency(t, ctx, binfoo, libbarApiImportv29))
-	android.AssertBoolEquals(t, "Stub library variant from API surface should be linked with target version", true, hasDirectDependency(t, ctx, libbarApiImportv29, libbarApiVariantv29))
-	android.AssertBoolEquals(t, "Stub library from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, binfoo, libbarApiImportv30))
-	android.AssertBoolEquals(t, "Stub library variant from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, libbarApiImportv29, libbarApiVariantv30))
-
-	binbaz := ctx.ModuleForTests("binbaz", "android_arm64_armv8-a_sdk").Module()
-
-	android.AssertBoolEquals(t, "Stub library from API surface should be linked with target version", true, hasDirectDependency(t, ctx, binbaz, libbarApiImportv30))
-	android.AssertBoolEquals(t, "Stub library from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, binbaz, libbarApiImportv29))
-
-	binFooLibFlags := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Rule("ld").Args["libFlags"]
-	android.AssertStringDoesContain(t, "Binary using sdk should be linked with NDK variant source", binFooLibFlags, "libbar.ndk.29.apiimport.so")
-
-	binFooCFlags := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Rule("cc").Args["cFlags"]
-	android.AssertStringDoesContain(t, "Binary using sdk should include headers from the NDK variant source", binFooCFlags, "-Ilibbar_ndk_29_include")
-
-	binQux := ctx.ModuleForTests("binqux", "android_arm64_armv8-a").Module()
-	android.AssertBoolEquals(t, "NDK Stub library from API surface should not be linked with nonSdk binary", false,
-		(hasDirectDependency(t, ctx, binQux, libbarApiImportv30) || hasDirectDependency(t, ctx, binQux, libbarApiImportv29)))
-}
-
-func TestApiLibraryWithMultipleVariants(t *testing.T) {
-	bp := `
-		cc_binary {
-			name: "binfoo",
-			sdk_version: "29",
-			srcs: ["binfoo.cc"],
-			shared_libs: ["libbar"],
-			stl: "c++_shared",
-		}
-
-		cc_binary {
-			name: "binbaz",
-			vendor: true,
-			srcs: ["binbaz.cc"],
-			shared_libs: ["libbar"],
-		}
-
-		cc_library {
-			name: "libbar",
-			srcs: ["libbar.cc"],
-		}
-
-		cc_api_library {
-			name: "libbar",
-			// TODO(b/244244438) Remove src property once all variants are implemented.
-			src: "libbar.so",
-			vendor_available: true,
-			variants: [
-				"llndk",
-				"ndk.29",
-				"ndk.30",
-				"ndk.current",
-				"apex.29",
-				"apex.30",
-				"apex.current",
-			],
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "ndk",
-			version: "29",
-			src: "libbar_ndk_29.so",
-			export_include_dirs: ["libbar_ndk_29_include"]
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "ndk",
-			version: "30",
-			src: "libbar_ndk_30.so",
-			export_include_dirs: ["libbar_ndk_30_include"]
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "ndk",
-			version: "current",
-			src: "libbar_ndk_current.so",
-			export_include_dirs: ["libbar_ndk_current_include"]
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "apex",
-			version: "29",
-			src: "libbar_apex_29.so",
-			export_include_dirs: ["libbar_apex_29_include"]
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "apex",
-			version: "30",
-			src: "libbar_apex_30.so",
-			export_include_dirs: ["libbar_apex_30_include"]
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "apex",
-			version: "current",
-			src: "libbar_apex_current.so",
-			export_include_dirs: ["libbar_apex_current_include"]
-		}
-
-		cc_api_variant {
-			name: "libbar",
-			variant: "llndk",
-			src: "libbar_llndk.so",
-			export_include_dirs: ["libbar_llndk_include"]
-		}
-
-		api_imports {
-			name: "api_imports",
-			shared_libs: [
-				"libbar",
-			],
-			apex_shared_libs: [
-				"libbar",
-			],
-		}
-	`
-	ctx := prepareForCcTest.RunTestWithBp(t, bp)
-
-	binfoo := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Module()
-	libbarApiImportv29 := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_sdk_shared_29").Module()
-	libbarApiImportLlndk := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
-
-	android.AssertBoolEquals(t, "Binary using SDK should be linked with API library from NDK variant", true, hasDirectDependency(t, ctx, binfoo, libbarApiImportv29))
-	android.AssertBoolEquals(t, "Binary using SDK should not be linked with API library from LLNDK variant", false, hasDirectDependency(t, ctx, binfoo, libbarApiImportLlndk))
-
-	binbaz := ctx.ModuleForTests("binbaz", "android_vendor.29_arm64_armv8-a").Module()
-
-	android.AssertBoolEquals(t, "Vendor binary should be linked with API library from LLNDK variant", true, hasDirectDependency(t, ctx, binbaz, libbarApiImportLlndk))
-	android.AssertBoolEquals(t, "Vendor binary should not be linked with API library from NDK variant", false, hasDirectDependency(t, ctx, binbaz, libbarApiImportv29))
-
-}
diff --git a/cc/linkable.go b/cc/linkable.go
index 6f22091..1672366 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -3,7 +3,6 @@
 import (
 	"android/soong/android"
 	"android/soong/fuzz"
-	"android/soong/snapshot"
 
 	"github.com/google/blueprint"
 )
@@ -63,55 +62,9 @@
 // implementation should handle tags from both.
 type SantizableDependencyTagChecker func(tag blueprint.DependencyTag) bool
 
-// Snapshottable defines those functions necessary for handling module snapshots.
-type Snapshottable interface {
-	snapshot.VendorSnapshotModuleInterface
-	snapshot.RecoverySnapshotModuleInterface
-
-	// SnapshotHeaders returns a list of header paths provided by this module.
-	SnapshotHeaders() android.Paths
-
-	// SnapshotLibrary returns true if this module is a snapshot library.
-	IsSnapshotLibrary() bool
-
-	// EffectiveLicenseFiles returns the list of License files for this module.
-	EffectiveLicenseFiles() android.Paths
-
-	// SnapshotRuntimeLibs returns a list of libraries needed by this module at runtime but which aren't build dependencies.
-	SnapshotRuntimeLibs() []string
-
-	// SnapshotSharedLibs returns the list of shared library dependencies for this module.
-	SnapshotSharedLibs() []string
-
-	// SnapshotStaticLibs returns the list of static library dependencies for this module.
-	SnapshotStaticLibs() []string
-
-	// SnapshotDylibs returns the list of dylib library dependencies for this module.
-	SnapshotDylibs() []string
-
-	// SnapshotRlibs returns the list of rlib library dependencies for this module.
-	SnapshotRlibs() []string
-
-	// IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt.
-	IsSnapshotPrebuilt() bool
-
-	// IsSnapshotSanitizer returns true if this snapshot module implements SnapshotSanitizer.
-	IsSnapshotSanitizer() bool
-
-	// IsSnapshotSanitizerAvailable returns true if this snapshot module has a sanitizer source available (cfi, hwasan).
-	IsSnapshotSanitizerAvailable(t SanitizerType) bool
-
-	// SetSnapshotSanitizerVariation sets the sanitizer variation type for this snapshot module.
-	SetSnapshotSanitizerVariation(t SanitizerType, enabled bool)
-
-	// IsSnapshotUnsanitizedVariant returns true if this is the unsanitized snapshot module variant.
-	IsSnapshotUnsanitizedVariant() bool
-}
-
 // LinkableInterface is an interface for a type of module that is linkable in a C++ library.
 type LinkableInterface interface {
 	android.Module
-	Snapshottable
 
 	Module() android.Module
 	CcLibrary() bool
@@ -120,6 +73,12 @@
 	// RustLibraryInterface returns true if this is a Rust library module
 	RustLibraryInterface() bool
 
+	// CrateName returns the crateName for a Rust library, panics if not a Rust library.
+	CrateName() string
+
+	// DepFlags returns a slice of Rustc string flags, panics if not a Rust library
+	ExportedCrateLinkDirs() []string
+
 	// BaseModuleName returns the android.ModuleBase.BaseModuleName() value for this module.
 	BaseModuleName() string
 
@@ -135,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
 
@@ -177,9 +140,6 @@
 	// IsLlndk returns true for both LLNDK (public) and LLNDK-private libs.
 	IsLlndk() bool
 
-	// IsLlndkPublic returns true only for LLNDK (public) libs.
-	IsLlndkPublic() bool
-
 	// HasLlndkStubs returns true if this library has a variant that will build LLNDK stubs.
 	HasLlndkStubs() bool
 
@@ -203,13 +163,6 @@
 	// Bootstrap tests if this module is allowed to use non-APEX version of libraries.
 	Bootstrap() bool
 
-	// IsVndkSp returns true if this is a VNDK-SP module.
-	IsVndkSp() bool
-
-	MustUseVendorVariant() bool
-	IsVndk() bool
-	IsVndkExt() bool
-	IsVndkPrivate() bool
 	IsVendorPublicLibrary() bool
 	IsVndkPrebuiltLibrary() bool
 	HasVendorVariant() bool
@@ -427,6 +380,7 @@
 	SystemIncludeDirs android.Paths // System include directories to be included with -isystem
 	Flags             []string      // Exported raw flags.
 	Deps              android.Paths
+	RustRlibDeps      []RustRlibDep
 	GeneratedHeaders  android.Paths
 }
 
diff --git a/cc/linker.go b/cc/linker.go
index 2c50db2..d2974c2 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -62,6 +62,10 @@
 	// This flag should only be necessary for compiling low-level libraries like libc.
 	Allow_undefined_symbols *bool `android:"arch_variant"`
 
+	// ignore max page size. By default, max page size must be the
+	// max page size set for the target.
+	Ignore_max_page_size *bool `android:"arch_variant"`
+
 	// don't link in libclang_rt.builtins-*.a
 	No_libcrt *bool `android:"arch_variant"`
 
@@ -116,7 +120,7 @@
 			// product variant of the C/C++ module.
 			Static_libs []string
 
-			// list of ehader libs that only should be used to build vendor or product
+			// list of header libs that only should be used to build vendor or product
 			// variant of the C/C++ module.
 			Header_libs []string
 
@@ -165,7 +169,7 @@
 			Exclude_runtime_libs []string
 		}
 		Ramdisk struct {
-			// list of static libs that only should be used to build the recovery
+			// list of static libs that only should be used to build the ramdisk
 			// variant of the C/C++ module.
 			Static_libs []string
 
@@ -183,7 +187,7 @@
 		}
 		Vendor_ramdisk struct {
 			// list of shared libs that should not be used to build
-			// the recovery variant of the C/C++ module.
+			// the vendor ramdisk variant of the C/C++ module.
 			Exclude_shared_libs []string
 
 			// list of static libs that should not be used to build
@@ -287,6 +291,10 @@
 	return []interface{}{&linker.Properties, &linker.dynamicProperties}
 }
 
+func (linker *baseLinker) baseLinkerProps() BaseLinkerProperties {
+	return linker.Properties
+}
+
 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...)
@@ -330,6 +338,7 @@
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs)
 		deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Target.Vendor.Header_libs...)
 		deps.HeaderLibs = removeListFromList(deps.HeaderLibs, linker.Properties.Target.Vendor.Exclude_header_libs)
+		deps.ReexportHeaderLibHeaders = removeListFromList(deps.ReexportHeaderLibHeaders, linker.Properties.Target.Vendor.Exclude_header_libs)
 		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)
@@ -342,6 +351,7 @@
 		deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Target.Product.Static_libs...)
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Product.Exclude_static_libs)
 		deps.HeaderLibs = removeListFromList(deps.HeaderLibs, linker.Properties.Target.Product.Exclude_header_libs)
+		deps.ReexportHeaderLibHeaders = removeListFromList(deps.ReexportHeaderLibHeaders, linker.Properties.Target.Product.Exclude_header_libs)
 		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)
@@ -396,7 +406,7 @@
 	if ctx.toolchain().Bionic() {
 		// libclang_rt.builtins has to be last on the command line
 		if linker.Properties.libCrt() && !ctx.header() {
-			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary())
 		}
 
 		if inList("libdl", deps.SharedLibs) {
@@ -419,7 +429,7 @@
 		}
 	} else if ctx.toolchain().Musl() {
 		if linker.Properties.libCrt() && !ctx.header() {
-			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+			deps.UnexportedStaticLibs = append(deps.UnexportedStaticLibs, config.BuiltinsRuntimeLibrary())
 		}
 	}
 
@@ -530,13 +540,6 @@
 		flags.Global.LdFlags = append(flags.Global.LdFlags, RpathFlags(ctx)...)
 	}
 
-	if ctx.useSdk() {
-		// The bionic linker now has support gnu style hashes (which are much faster!), but shipping
-		// to older devices requires the old style hash. Fortunately, we can build with both and
-		// it'll work anywhere.
-		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--hash-style=both")
-	}
-
 	flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ToolchainLdflags())
 
 	// Version_script is not needed when linking stubs lib where the version
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index c307410..85c3edf 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -14,9 +14,16 @@
 
 package cc
 
+import (
+	"fmt"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/etc"
+)
+
 var (
 	llndkLibrarySuffix = ".llndk"
-	llndkHeadersSuffix = ".llndk"
 )
 
 // Holds properties to describe a stub shared library based on the provided version file.
@@ -55,3 +62,165 @@
 	// llndk.symbol_file.
 	Llndk_headers *bool
 }
+
+func makeLlndkVars(ctx android.MakeVarsContext) {
+	// Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to avoid installing libraries on /system if
+	// they been moved to an apex.
+	movedToApexLlndkLibraries := make(map[string]bool)
+	ctx.VisitAllModules(func(module android.Module) {
+		if library := moduleLibraryInterface(module); library != nil && library.hasLLNDKStubs() {
+			// Skip bionic libs, they are handled in different manner
+			name := library.implementationModuleName(module.(*Module).BaseModuleName())
+			if module.(android.ApexModule).DirectlyInAnyApex() && !isBionic(name) {
+				movedToApexLlndkLibraries[name] = true
+			}
+		}
+	})
+
+	ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES",
+		strings.Join(android.SortedKeys(movedToApexLlndkLibraries), " "))
+}
+
+func init() {
+	RegisterLlndkLibraryTxtType(android.InitRegistrationContext)
+}
+
+func RegisterLlndkLibraryTxtType(ctx android.RegistrationContext) {
+	ctx.RegisterParallelSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
+}
+
+type llndkLibrariesTxtModule struct {
+	android.SingletonModuleBase
+
+	outputFile  android.OutputPath
+	moduleNames []string
+	fileNames   []string
+}
+
+var _ etc.PrebuiltEtcModule = &llndkLibrariesTxtModule{}
+
+// llndk_libraries_txt is a singleton module whose content is a list of LLNDK libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+// Make uses LLNDK_LIBRARIES to determine which libraries to install.
+// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
+// Therefore, by removing the library here, we cause it to only be installed if libc
+// depends on it.
+func llndkLibrariesTxtFactory() android.SingletonModule {
+	m := &llndkLibrariesTxtModule{}
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	return m
+}
+
+func (txt *llndkLibrariesTxtModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	filename := txt.Name()
+
+	txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
+
+	installPath := android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(installPath, filename, txt.outputFile)
+
+	ctx.SetOutputFiles(android.Paths{txt.outputFile}, "")
+}
+
+func getVndkFileName(m *Module) (string, error) {
+	if library, ok := m.linker.(*libraryDecorator); ok {
+		return library.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
+	}
+	if prebuilt, ok := m.linker.(*prebuiltLibraryLinker); ok {
+		return prebuilt.libraryDecorator.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
+	}
+	return "", fmt.Errorf("VNDK library should have libraryDecorator or prebuiltLibraryLinker as linker: %T", m.linker)
+}
+
+func (txt *llndkLibrariesTxtModule) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+	if txt.outputFile.String() == "" {
+		// Skip if target file path is empty
+		return
+	}
+
+	ctx.VisitAllModules(func(m android.Module) {
+		if c, ok := m.(*Module); ok && c.VendorProperties.IsLLNDK && !c.Header() && !c.IsVndkPrebuiltLibrary() {
+			filename, err := getVndkFileName(c)
+			if err != nil {
+				ctx.ModuleErrorf(m, "%s", err)
+			}
+
+			if !strings.HasPrefix(ctx.ModuleName(m), "libclang_rt.hwasan") {
+				txt.moduleNames = append(txt.moduleNames, ctx.ModuleName(m))
+			}
+			txt.fileNames = append(txt.fileNames, filename)
+		}
+	})
+	txt.moduleNames = android.SortedUniqueStrings(txt.moduleNames)
+	txt.fileNames = android.SortedUniqueStrings(txt.fileNames)
+
+	android.WriteFileRule(ctx, txt.outputFile, strings.Join(txt.fileNames, "\n"))
+}
+
+func (txt *llndkLibrariesTxtModule) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(txt.outputFile),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_STEM", txt.outputFile.Base())
+			},
+		},
+	}}
+}
+
+func (txt *llndkLibrariesTxtModule) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict("LLNDK_LIBRARIES", strings.Join(txt.moduleNames, " "))
+}
+
+// PrebuiltEtcModule interface
+func (txt *llndkLibrariesTxtModule) BaseDir() string {
+	return "etc"
+}
+
+// PrebuiltEtcModule interface
+func (txt *llndkLibrariesTxtModule) SubDir() string {
+	return ""
+}
+
+func llndkMutator(mctx android.BottomUpMutatorContext) {
+	m, ok := mctx.Module().(*Module)
+	if !ok {
+		return
+	}
+
+	if shouldSkipLlndkMutator(mctx, m) {
+		return
+	}
+
+	lib, isLib := m.linker.(*libraryDecorator)
+	prebuiltLib, isPrebuiltLib := m.linker.(*prebuiltLibraryLinker)
+
+	if m.InVendorOrProduct() && isLib && lib.hasLLNDKStubs() {
+		m.VendorProperties.IsLLNDK = true
+	}
+	if m.InVendorOrProduct() && isPrebuiltLib && prebuiltLib.hasLLNDKStubs() {
+		m.VendorProperties.IsLLNDK = true
+	}
+
+	if vndkprebuilt, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok {
+		if !Bool(vndkprebuilt.properties.Vndk.Enabled) {
+			m.VendorProperties.IsLLNDK = true
+		}
+	}
+}
+
+// Check for modules that mustn't be LLNDK
+func shouldSkipLlndkMutator(mctx android.BottomUpMutatorContext, m *Module) bool {
+	if !m.Enabled(mctx) {
+		return true
+	}
+	if !m.Device() {
+		return true
+	}
+	if m.Target().NativeBridge == android.NativeBridgeEnabled {
+		return true
+	}
+	return false
+}
diff --git a/cc/lto.go b/cc/lto.go
index 05fa8ee..f3af7d2 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -54,6 +54,9 @@
 
 	// Use -fwhole-program-vtables cflag.
 	Whole_program_vtables *bool
+
+	// Use --lto-O0 flag.
+	Lto_O0 *bool
 }
 
 type lto struct {
@@ -90,10 +93,6 @@
 	} else if ctx.testBinary() || ctx.testLibrary() {
 		// Do not enable LTO for tests for better debugging.
 		ltoEnabled = false
-	} else if ctx.isVndk() {
-		// FIXME: ThinLTO for VNDK produces different output.
-		// b/169217596
-		ltoEnabled = false
 	}
 
 	lto.Properties.LtoDefault = ltoDefault
@@ -110,12 +109,8 @@
 		ltoCFlags := []string{"-flto=thin", "-fsplit-lto-unit"}
 		var ltoLdFlags []string
 
-		// The module did not explicitly turn on LTO. Only leverage LTO's
-		// better dead code elimination and CFG simplification, but do
-		// not perform costly optimizations for a balance between compile
-		// time, binary size and performance.
-		// Apply the same for Eng builds as well.
-		if !lto.ThinLTO() || ctx.Config().Eng() {
+		// Do not perform costly LTO optimizations for Eng builds.
+		if Bool(lto.Properties.Lto_O0) || ctx.optimizeForSize() || ctx.Config().Eng() {
 			ltoLdFlags = append(ltoLdFlags, "-Wl,--lto-O0")
 		}
 
@@ -148,7 +143,7 @@
 
 		if !ctx.Config().IsEnvFalse("THINLTO_USE_MLGO") {
 			// Register allocation MLGO flags for ARM64.
-			if ctx.Arch().ArchType == android.Arm64 {
+			if ctx.Arch().ArchType == android.Arm64 && !ctx.optimizeForSize() {
 				ltoLdFlags = append(ltoLdFlags, "-Wl,-mllvm,-regalloc-enable-advisor=release")
 			}
 			// Flags for training MLGO model.
diff --git a/cc/makevars.go b/cc/makevars.go
index 70fdd57..cd13965 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -28,6 +28,16 @@
 	modulesWarningsAllowedKey    = android.NewOnceKey("ModulesWarningsAllowed")
 	modulesUsingWnoErrorKey      = android.NewOnceKey("ModulesUsingWnoError")
 	modulesMissingProfileFileKey = android.NewOnceKey("ModulesMissingProfileFile")
+	sanitizerVariables           = map[string]string{
+		"ADDRESS_SANITIZER_RUNTIME_LIBRARY":   config.AddressSanitizerRuntimeLibrary(),
+		"HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(),
+		"HWADDRESS_SANITIZER_STATIC_LIBRARY":  config.HWAddressSanitizerStaticLibrary(),
+		"UBSAN_RUNTIME_LIBRARY":               config.UndefinedBehaviorSanitizerRuntimeLibrary(),
+		"UBSAN_MINIMAL_RUNTIME_LIBRARY":       config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(),
+		"TSAN_RUNTIME_LIBRARY":                config.ThreadSanitizerRuntimeLibrary(),
+		"SCUDO_RUNTIME_LIBRARY":               config.ScudoRuntimeLibrary(),
+		"SCUDO_MINIMAL_RUNTIME_LIBRARY":       config.ScudoMinimalRuntimeLibrary(),
+	}
 )
 
 func init() {
@@ -97,9 +107,6 @@
 	ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
 	ctx.Strict("GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideExternalGlobalCflags}")
 
-	ctx.Strict("BOARD_VNDK_VERSION", ctx.DeviceConfig().VndkVersion())
-	ctx.Strict("RECOVERY_SNAPSHOT_VERSION", ctx.DeviceConfig().RecoverySnapshotVersion())
-
 	// Filter vendor_public_library that are exported to make
 	exportedVendorPublicLibraries := []string{}
 	ctx.VisitAllModules(func(module android.Module) {
@@ -123,15 +130,14 @@
 	ctx.Strict("SOONG_MODULES_USING_WNO_ERROR", makeStringOfKeys(ctx, modulesUsingWnoErrorKey))
 	ctx.Strict("SOONG_MODULES_MISSING_PGO_PROFILE_FILE", makeStringOfKeys(ctx, modulesMissingProfileFileKey))
 
- 	ctx.Strict("CLANG_COVERAGE_CONFIG_CFLAGS", strings.Join(clangCoverageCFlags, " "))
- 	ctx.Strict("CLANG_COVERAGE_CONFIG_COMMFLAGS", strings.Join(clangCoverageCommonFlags, " "))
- 	ctx.Strict("CLANG_COVERAGE_HOST_LDFLAGS", strings.Join(clangCoverageHostLdFlags, " "))
- 	ctx.Strict("CLANG_COVERAGE_INSTR_PROFILE", profileInstrFlag)
- 	ctx.Strict("CLANG_COVERAGE_CONTINUOUS_FLAGS", strings.Join(clangContinuousCoverageFlags, " "))
- 	ctx.Strict("CLANG_COVERAGE_HWASAN_FLAGS", strings.Join(clangCoverageHWASanFlags, " "))
+	ctx.Strict("CLANG_COVERAGE_CONFIG_CFLAGS", strings.Join(clangCoverageCFlags, " "))
+	ctx.Strict("CLANG_COVERAGE_CONFIG_COMMFLAGS", strings.Join(clangCoverageCommonFlags, " "))
+	ctx.Strict("CLANG_COVERAGE_HOST_LDFLAGS", strings.Join(clangCoverageHostLdFlags, " "))
+	ctx.Strict("CLANG_COVERAGE_INSTR_PROFILE", profileInstrFlag)
+	ctx.Strict("CLANG_COVERAGE_CONTINUOUS_FLAGS", strings.Join(clangContinuousCoverageFlags, " "))
+	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, ","))
@@ -184,6 +190,8 @@
 	if len(deviceTargets) > 1 {
 		makeVarsToolchain(ctx, "2ND_", deviceTargets[1])
 	}
+
+	makeLlndkVars(ctx)
 }
 
 func makeVarsToolchain(ctx android.MakeVarsContext, secondPrefix string,
@@ -262,43 +270,9 @@
 	}, " "))
 
 	if target.Os.Class == android.Device {
-		sanitizerVariables := map[string]string{
-			"ADDRESS_SANITIZER_RUNTIME_LIBRARY":   config.AddressSanitizerRuntimeLibrary(toolchain),
-			"HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(toolchain),
-			"HWADDRESS_SANITIZER_STATIC_LIBRARY":  config.HWAddressSanitizerStaticLibrary(toolchain),
-			"UBSAN_RUNTIME_LIBRARY":               config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain),
-			"UBSAN_MINIMAL_RUNTIME_LIBRARY":       config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain),
-			"TSAN_RUNTIME_LIBRARY":                config.ThreadSanitizerRuntimeLibrary(toolchain),
-			"SCUDO_RUNTIME_LIBRARY":               config.ScudoRuntimeLibrary(toolchain),
-			"SCUDO_MINIMAL_RUNTIME_LIBRARY":       config.ScudoMinimalRuntimeLibrary(toolchain),
-		}
-
 		for variable, value := range sanitizerVariables {
 			ctx.Strict(secondPrefix+variable, value)
 		}
-
-		sanitizerLibs := android.SortedStringValues(sanitizerVariables)
-		var sanitizerLibStems []string
-		ctx.VisitAllModules(func(m android.Module) {
-			if !m.Enabled() {
-				return
-			}
-
-			ccModule, _ := m.(*Module)
-			if ccModule == nil || ccModule.library == nil || !ccModule.library.shared() {
-				return
-			}
-
-			if android.InList(strings.TrimPrefix(ctx.ModuleName(m), "prebuilt_"), sanitizerLibs) &&
-				m.Target().Os == target.Os && m.Target().Arch.ArchType == target.Arch.ArchType {
-				outputFile := ccModule.outputFile
-				if outputFile.Valid() {
-					sanitizerLibStems = append(sanitizerLibStems, outputFile.Path().Base())
-				}
-			}
-		})
-		sanitizerLibStems = android.SortedUniqueStrings(sanitizerLibStems)
-		ctx.Strict(secondPrefix+"SANITIZER_STEMS", strings.Join(sanitizerLibStems, " "))
 	}
 
 	// This is used by external/gentoo/...
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index 86166dc..5beeab1 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -40,7 +40,7 @@
 func (n *ndkAbiDumpSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var depPaths android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled() {
+		if !module.Enabled(ctx) {
 			return
 		}
 
@@ -78,7 +78,7 @@
 func (n *ndkAbiDiffSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var depPaths android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		if m, ok := module.(android.Module); ok && !m.Enabled() {
+		if m, ok := module.(android.Module); ok && !m.Enabled(ctx) {
 			return
 		}
 
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 567cb7c..57a3b3a 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -15,7 +15,6 @@
 package cc
 
 import (
-	"fmt"
 	"path/filepath"
 
 	"android/soong/android"
@@ -45,7 +44,7 @@
 }
 
 // Returns the NDK base include path for use with sdk_version current. Usable with -I.
-func getCurrentIncludePath(ctx android.ModuleContext) android.InstallPath {
+func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath {
 	return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
 }
 
@@ -87,7 +86,7 @@
 }
 
 func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string,
-	to string) android.InstallPath {
+	to string) android.OutputPath {
 	// Output path is the sysroot base + "usr/include" + to directory + directory component
 	// of the file without the leading from directory stripped.
 	//
@@ -129,13 +128,12 @@
 	for _, header := range m.srcPaths {
 		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From),
 			String(m.properties.To))
-		installedPath := ctx.InstallFile(installDir, header.Base(), header)
 		installPath := installDir.Join(ctx, header.Base())
-		if installPath != installedPath {
-			panic(fmt.Sprintf(
-				"expected header install path (%q) not equal to actual install path %q",
-				installPath, installedPath))
-		}
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Input:  header,
+			Output: installPath,
+		})
 		m.installPaths = append(m.installPaths, installPath)
 	}
 
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 183e818..f326068 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -148,7 +148,7 @@
 }
 
 func (this *stubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
-	if !ctx.Module().Enabled() {
+	if !ctx.Module().Enabled(ctx) {
 		return nil
 	}
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
@@ -225,7 +225,7 @@
 }
 
 func init() {
-	config.ExportStringList("StubLibraryCompilerFlags", stubLibraryCompilerFlags)
+	pctx.StaticVariable("StubLibraryCompilerFlags", strings.Join(stubLibraryCompilerFlags, " "))
 }
 
 func addStubLibraryCompilerFlags(flags Flags) Flags {
@@ -325,13 +325,6 @@
 	if runtime.GOOS == "darwin" {
 		return false
 	}
-	// abidw doesn't currently handle top-byte-ignore correctly. Disable ABI
-	// dumping for those configs while we wait for a fix. We'll still have ABI
-	// checking coverage from non-hwasan builds.
-	// http://b/190554910
-	if android.InList("hwaddress", config.SanitizeDevice()) {
-		return false
-	}
 	// http://b/156513478
 	return config.ReleaseNdkAbiMonitored()
 }
@@ -525,19 +518,25 @@
 
 // Returns the install path for unversioned NDK libraries (currently only static
 // libraries).
-func getUnversionedLibraryInstallPath(ctx ModuleContext) android.InstallPath {
+func getUnversionedLibraryInstallPath(ctx ModuleContext) android.OutputPath {
 	return getNdkSysrootBase(ctx).Join(ctx, "usr/lib", config.NDKTriple(ctx.toolchain()))
 }
 
 // Returns the install path for versioned NDK libraries. These are most often
 // stubs, but the same paths are used for CRT objects.
-func getVersionedLibraryInstallPath(ctx ModuleContext, apiLevel android.ApiLevel) android.InstallPath {
+func getVersionedLibraryInstallPath(ctx ModuleContext, apiLevel android.ApiLevel) android.OutputPath {
 	return getUnversionedLibraryInstallPath(ctx).Join(ctx, apiLevel.String())
 }
 
 func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
 	installDir := getVersionedLibraryInstallPath(ctx, stub.apiLevel)
-	stub.installPath = ctx.InstallFile(installDir, path.Base(), path)
+	out := installDir.Join(ctx, path.Base())
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Input:  path,
+		Output: out,
+	})
+	stub.installPath = out
 }
 
 func newStubLibrary() *Module {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 483d23b..3c48f68 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -69,12 +69,12 @@
 	ctx.RegisterParallelSingletonType("ndk", NdkSingleton)
 }
 
-func getNdkInstallBase(ctx android.PathContext) android.InstallPath {
+func getNdkInstallBase(ctx android.PathContext) android.OutputPath {
 	return android.PathForNdkInstall(ctx)
 }
 
 // Returns the main install directory for the NDK sysroot. Usable with --sysroot.
-func getNdkSysrootBase(ctx android.PathContext) android.InstallPath {
+func getNdkSysrootBase(ctx android.PathContext) android.OutputPath {
 	return getNdkInstallBase(ctx).Join(ctx, "sysroot")
 }
 
@@ -150,7 +150,7 @@
 	var installPaths android.Paths
 	var licensePaths android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		if m, ok := module.(android.Module); ok && !m.Enabled() {
+		if m, ok := module.(android.Module); ok && !m.Enabled(ctx) {
 			return
 		}
 
diff --git a/cc/object.go b/cc/object.go
index 6c0391f..8b23295 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -220,7 +220,7 @@
 }
 
 func (object *objectLinker) strippedAllOutputFilePath() android.Path {
-	panic("Not implemented.")
+	return nil
 }
 
 func (object *objectLinker) nativeCoverage() bool {
diff --git a/cc/object_test.go b/cc/object_test.go
index c0d1331..004dfd3 100644
--- a/cc/object_test.go
+++ b/cc/object_test.go
@@ -41,7 +41,7 @@
 		{"android_arm64_armv8-a_sdk_29", "29"},
 		{"android_arm64_armv8-a_sdk_30", "30"},
 		{"android_arm64_armv8-a_sdk_current", "10000"},
-		{"android_vendor.29_arm64_armv8-a", "29"},
+		{"android_vendor_arm64_armv8-a", "10000"},
 	}
 
 	ctx := prepareForCcTest.RunTestWithBp(t, bp)
@@ -50,7 +50,7 @@
 		expected := "-target aarch64-linux-android" + v.num + " "
 		android.AssertStringDoesContain(t, "cflag", cflags, expected)
 	}
-	ctx = prepareForCcTestWithoutVndk.RunTestWithBp(t, bp)
+	ctx = prepareForCcTest.RunTestWithBp(t, bp)
 	android.AssertStringDoesContain(t, "cflag",
 		ctx.ModuleForTests("crt_foo", "android_vendor_arm64_armv8-a").Rule("cc").Args["cFlags"],
 		"-target aarch64-linux-android10000 ")
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index cbb5d58..e9f790f 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -177,7 +177,7 @@
 				implicits = append(implicits, importLibOutputFile)
 
 				ctx.Build(pctx, android.BuildParams{
-					Rule:        android.Cp,
+					Rule:        android.CpExecutable,
 					Description: "prebuilt import library",
 					Input:       importLibSrc,
 					Output:      importLibOutputFile,
@@ -188,7 +188,7 @@
 			}
 
 			ctx.Build(pctx, android.BuildParams{
-				Rule:        android.Cp,
+				Rule:        android.CpExecutable,
 				Description: "prebuilt shared library",
 				Implicits:   implicits,
 				Input:       in,
diff --git a/cc/sabi.go b/cc/sabi.go
index af26726..64eab41 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -29,8 +29,8 @@
 type lsdumpTag string
 
 const (
+	apexLsdumpTag     lsdumpTag = "APEX"
 	llndkLsdumpTag    lsdumpTag = "LLNDK"
-	ndkLsdumpTag      lsdumpTag = "NDK"
 	platformLsdumpTag lsdumpTag = "PLATFORM"
 	productLsdumpTag  lsdumpTag = "PRODUCT"
 	vendorLsdumpTag   lsdumpTag = "VENDOR"
@@ -39,12 +39,10 @@
 // Return the prebuilt ABI dump directory for a tag; an empty string for an opt-in dump.
 func (tag *lsdumpTag) dirName() string {
 	switch *tag {
-	case ndkLsdumpTag:
-		return "ndk"
+	case apexLsdumpTag:
+		return "platform"
 	case llndkLsdumpTag:
 		return "vndk"
-	case platformLsdumpTag:
-		return "platform"
 	default:
 		return ""
 	}
@@ -92,7 +90,8 @@
 
 	// Include directories that may contain ABI information exported by a library.
 	// These directories are passed to the header-abi-dumper.
-	ReexportedIncludes []string `blueprint:"mutated"`
+	ReexportedIncludes       []string `blueprint:"mutated"`
+	ReexportedSystemIncludes []string `blueprint:"mutated"`
 }
 
 type sabi struct {
@@ -133,11 +132,10 @@
 		if m.isImplementationForLLNDKPublic() {
 			result = append(result, llndkLsdumpTag)
 		}
-		// Return NDK if the library is both NDK and APEX.
-		// TODO(b/309880485): Split NDK and APEX ABI.
-		if m.IsNdk(ctx.Config()) {
-			result = append(result, ndkLsdumpTag)
-		} else if m.library.hasStubsVariants() || headerAbiChecker.enabled() {
+		if m.library.hasStubsVariants() {
+			result = append(result, apexLsdumpTag)
+		}
+		if headerAbiChecker.enabled() {
 			result = append(result, platformLsdumpTag)
 		}
 	} else if headerAbiChecker.enabled() {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 52b5be9..64a313b 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -25,7 +25,7 @@
 
 	"android/soong/android"
 	"android/soong/cc/config"
-	"android/soong/snapshot"
+	"android/soong/etc"
 )
 
 var (
@@ -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{
@@ -56,7 +55,6 @@
 		// higher number of "optimized out" stack variables.
 		// b/112437883.
 		"-instcombine-lower-dbg-declare=0",
-		"-hwasan-use-after-scope=1",
 		"-dom-tree-reachability-max-bbs-to-explore=128",
 	}
 
@@ -83,7 +81,8 @@
 		"-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", "-mllvm", "-dom-tree-reachability-max-bbs-to-explore=128"}
+	memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
+	memtagStackLlvmFlags   = []string{"-dom-tree-reachability-max-bbs-to-explore=128"}
 
 	hostOnlySanitizeFlags   = []string{"-fno-sanitize-recover=all"}
 	deviceOnlySanitizeFlags = []string{"-fsanitize-trap=all"}
@@ -177,11 +176,11 @@
 
 func (t SanitizerType) registerMutators(ctx android.RegisterMutatorsContext) {
 	switch t {
-	case cfi, Hwasan, Asan, tsan, Fuzzer, scs:
+	case cfi, Hwasan, Asan, tsan, Fuzzer, scs, Memtag_stack:
 		sanitizer := &sanitizerSplitMutator{t}
 		ctx.TopDown(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
 		ctx.Transition(t.variationName(), sanitizer)
-	case Memtag_heap, Memtag_stack, Memtag_globals, intOverflow:
+	case Memtag_heap, Memtag_globals, intOverflow:
 		// do nothing
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
@@ -383,7 +382,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"`
@@ -403,33 +414,14 @@
 
 var _ android.SkipApexAllowedDependenciesCheck = (*libraryDependencyTag)(nil)
 
-var exportedVars = android.NewExportedVariables(pctx)
-
 func init() {
-	exportedVars.ExportStringListStaticVariable("HostOnlySanitizeFlags", hostOnlySanitizeFlags)
-	exportedVars.ExportStringList("DeviceOnlySanitizeFlags", deviceOnlySanitizeFlags)
-
-	exportedVars.ExportStringList("MinimalRuntimeFlags", minimalRuntimeFlags)
-
-	// Leave out "-flto" from the slices exported to bazel, as we will use the
-	// dedicated LTO feature for this. For C Flags and Linker Flags, also leave
-	// out the cross DSO flag which will be added separately under the correct conditions.
-	exportedVars.ExportStringList("CfiCFlags", append(cfiCflags[2:], cfiEnableFlag))
-	exportedVars.ExportStringList("CfiLdFlags", cfiLdflags[2:])
-	exportedVars.ExportStringList("CfiAsFlags", cfiAsflags[1:])
-
-	exportedVars.ExportString("SanitizeIgnorelistPrefix", sanitizeIgnorelistPrefix)
-	exportedVars.ExportString("CfiCrossDsoFlag", cfiCrossDsoFlag)
-	exportedVars.ExportString("CfiBlocklistPath", cfiBlocklistPath)
-	exportedVars.ExportString("CfiBlocklistFilename", cfiBlocklistFilename)
-	exportedVars.ExportString("CfiExportsMapPath", cfiExportsMapPath)
-	exportedVars.ExportString("CfiExportsMapFilename", cfiExportsMapFilename)
-	exportedVars.ExportString("CfiAssemblySupportFlag", cfiAssemblySupportFlag)
-
-	exportedVars.ExportString("NoSanitizeLinkRuntimeFlag", noSanitizeLinkRuntimeFlag)
+	pctx.StaticVariable("HostOnlySanitizeFlags", strings.Join(hostOnlySanitizeFlags, " "))
 
 	android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
 	android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
+	android.RegisterMakeVarsProvider(pctx, memtagStackMakeVarsProvider)
+
+	RegisterSanitizerLibrariesTxtType(android.InitRegistrationContext)
 }
 
 func (sanitize *sanitize) props() []interface{} {
@@ -474,6 +466,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)
@@ -700,16 +696,14 @@
 		s.Integer_overflow = nil
 	}
 
-	// Also disable CFI for VNDK variants of components
-	if ctx.isVndk() && ctx.useVndk() {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
-	}
-
-	// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
-	// Keep libc instrumented so that ramdisk / vendor_ramdisk / recovery can run hwasan-instrumented code if necessary.
-	if (ctx.inRamdisk() || ctx.inVendorRamdisk() || ctx.inRecovery()) && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
-		s.Hwaddress = nil
+	if ctx.inRamdisk() || ctx.inVendorRamdisk() || ctx.inRecovery() {
+		// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
+		// Keep libc instrumented so that ramdisk / vendor_ramdisk / recovery can run hwasan-instrumented code if necessary.
+		if !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") {
+			s.Hwaddress = nil
+		}
+		// Memtag stack in ramdisk makes pKVM unhappy.
+		s.Memtag_stack = nil
 	}
 
 	if ctx.staticBinary() {
@@ -786,6 +780,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
 	}
@@ -798,16 +796,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() {
@@ -881,6 +880,7 @@
 
 		flags.Local.CFlags = append(flags.Local.CFlags, cfiCflags...)
 		flags.Local.AsFlags = append(flags.Local.AsFlags, cfiAsflags...)
+		flags.CFlagsDeps = append(flags.CFlagsDeps, android.PathForSource(ctx, cfiBlocklistPath+"/"+cfiBlocklistFilename))
 		if Bool(s.Properties.Sanitize.Config.Cfi_assembly_support) {
 			flags.Local.CFlags = append(flags.Local.CFlags, cfiAssemblySupportFlag)
 		}
@@ -901,6 +901,13 @@
 		flags.Local.CFlags = append(flags.Local.CFlags, memtagStackCommonFlags...)
 		flags.Local.AsFlags = append(flags.Local.AsFlags, memtagStackCommonFlags...)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, memtagStackCommonFlags...)
+
+		for _, flag := range memtagStackLlvmFlags {
+			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", flag)
+		}
+		for _, flag := range memtagStackLlvmFlags {
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,"+flag)
+		}
 	}
 
 	if (Bool(sanProps.Memtag_heap) || Bool(sanProps.Memtag_stack) || Bool(sanProps.Memtag_globals)) && ctx.binary() {
@@ -1117,7 +1124,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
 	}
 
@@ -1141,42 +1148,6 @@
 	return IsSanitizableDependencyTag
 }
 
-// Determines if the current module is a static library going to be captured
-// as vendor snapshot. Such modules must create both cfi and non-cfi variants,
-// except for ones which explicitly disable cfi.
-func needsCfiForVendorSnapshot(mctx android.BaseModuleContext) bool {
-	if inList("hwaddress", mctx.Config().SanitizeDevice()) {
-		// cfi will not be built if SANITIZE_TARGET=hwaddress is set
-		return false
-	}
-
-	if snapshot.IsVendorProprietaryModule(mctx) {
-		return false
-	}
-
-	c := mctx.Module().(PlatformSanitizeable)
-
-	if !c.InVendor() {
-		return false
-	}
-
-	if !c.StaticallyLinked() {
-		return false
-	}
-
-	if c.IsPrebuilt() {
-		return false
-	}
-
-	if !c.SanitizerSupported(cfi) {
-		return false
-	}
-
-	return c.SanitizePropDefined() &&
-		!c.SanitizeNever() &&
-		!c.IsSanitizerExplicitlyDisabled(cfi)
-}
-
 type sanitizerSplitMutator struct {
 	sanitizer SanitizerType
 }
@@ -1201,10 +1172,6 @@
 
 func (s *sanitizerSplitMutator) Split(ctx android.BaseModuleContext) []string {
 	if c, ok := ctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
-		if s.sanitizer == cfi && needsCfiForVendorSnapshot(ctx) {
-			return []string{"", s.sanitizer.variationName()}
-		}
-
 		// If the given sanitizer is not requested in the .bp file for a module, it
 		// won't automatically build the sanitized variation.
 		if !c.IsSanitizerEnabled(s.sanitizer) {
@@ -1242,17 +1209,6 @@
 		}
 	}
 
-	if c, ok := ctx.Module().(LinkableInterface); ok {
-		// Check if it's a snapshot module supporting sanitizer
-		if c.IsSnapshotSanitizer() {
-			if c.IsSnapshotSanitizerAvailable(s.sanitizer) {
-				return []string{"", s.sanitizer.variationName()}
-			} else {
-				return []string{""}
-			}
-		}
-	}
-
 	return []string{""}
 }
 
@@ -1277,12 +1233,6 @@
 
 func (s *sanitizerSplitMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
 	if d, ok := ctx.Module().(PlatformSanitizeable); ok {
-		if dm, ok := ctx.Module().(LinkableInterface); ok {
-			if dm.IsSnapshotSanitizerAvailable(s.sanitizer) {
-				return incomingVariation
-			}
-		}
-
 		if !d.SanitizePropDefined() ||
 			d.SanitizeNever() ||
 			d.IsSanitizerExplicitlyDisabled(s.sanitizer) ||
@@ -1382,6 +1332,8 @@
 					hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
 				} else if s.sanitizer == cfi {
 					cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
+				} else if s.sanitizer == Memtag_stack {
+					memtagStackStaticLibs(mctx.Config()).add(c, c.Module().Name())
 				}
 			}
 		} else if c.IsSanitizerEnabled(s.sanitizer) {
@@ -1393,61 +1345,11 @@
 		if sanitizerVariation {
 			sanitizeable.AddSanitizerDependencies(mctx, s.sanitizer.name())
 		}
-	} else if c, ok := mctx.Module().(LinkableInterface); ok {
-		if c.IsSnapshotSanitizerAvailable(s.sanitizer) {
-			if !c.IsSnapshotUnsanitizedVariant() {
-				// Snapshot sanitizer may have only one variantion.
-				// Skip exporting the module if it already has a sanitizer variation.
-				c.SetPreventInstall()
-				c.SetHideFromMake()
-				return
-			}
-			c.SetSnapshotSanitizerVariation(s.sanitizer, sanitizerVariation)
-
-			// Export the static lib name to make
-			if c.Static() && c.ExportedToMake() {
-				// use BaseModuleName which is the name for Make.
-				if s.sanitizer == cfi {
-					cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
-				} else if s.sanitizer == Hwasan {
-					hwasanStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
-				}
-			}
-		}
 	}
 }
 
-func (c *Module) IsSnapshotSanitizer() bool {
-	if _, ok := c.linker.(SnapshotSanitizer); ok {
-		return true
-	}
-	return false
-}
-
-func (c *Module) IsSnapshotSanitizerAvailable(t SanitizerType) bool {
-	if ss, ok := c.linker.(SnapshotSanitizer); ok {
-		return ss.IsSanitizerAvailable(t)
-	}
-	return false
-}
-
-func (c *Module) SetSnapshotSanitizerVariation(t SanitizerType, enabled bool) {
-	if ss, ok := c.linker.(SnapshotSanitizer); ok {
-		ss.SetSanitizerVariation(t, enabled)
-	} else {
-		panic(fmt.Errorf("Calling SetSnapshotSanitizerVariation on a non-snapshotLibraryDecorator: %s", c.Name()))
-	}
-}
-
-func (c *Module) IsSnapshotUnsanitizedVariant() bool {
-	if ss, ok := c.linker.(SnapshotSanitizer); ok {
-		return ss.IsUnsanitizedVariant()
-	}
-	return false
-}
-
 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 {
@@ -1458,6 +1360,9 @@
 func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) {
 	// 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)) {
@@ -1468,7 +1373,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.
@@ -1492,15 +1397,6 @@
 				return true
 			}
 
-			if p, ok := d.linker.(*snapshotLibraryDecorator); ok {
-				if Bool(p.properties.Sanitize_minimal_dep) {
-					c.sanitize.Properties.MinimalRuntimeDep = true
-				}
-				if Bool(p.properties.Sanitize_ubsan_dep) {
-					c.sanitize.Properties.UbsanRuntimeDep = true
-				}
-			}
-
 			return false
 		})
 	}
@@ -1509,9 +1405,13 @@
 // Add the dependency to the runtime library for each of the sanitizer variants
 func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
 	if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
-		if !c.Enabled() {
+		if !c.Enabled(mctx) {
 			return
 		}
+		if c.sanitize.Properties.ForceDisable {
+			return
+		}
+
 		var sanitizers []string
 		var diagSanitizers []string
 
@@ -1626,12 +1526,6 @@
 		}
 
 		addStaticDeps := func(dep string, hideSymbols bool) {
-			// If we're using snapshots, redirect to snapshot whenever possible
-			snapshot, _ := android.ModuleProvider(mctx, SnapshotInfoProvider)
-			if lib, ok := snapshot.StaticLibs[dep]; ok {
-				dep = lib
-			}
-
 			// static executable gets static runtime libs
 			depTag := libraryDependencyTag{Kind: staticLibraryDependency, unexportedSymbols: hideSymbols}
 			variations := append(mctx.Target().Variations(),
@@ -1652,25 +1546,25 @@
 		if Bool(sanProps.Address) {
 			if toolchain.Musl() || (c.staticBinary() && toolchain.Bionic()) {
 				// Use a static runtime for musl to match what clang does for glibc.
-				addStaticDeps(config.AddressSanitizerStaticRuntimeLibrary(toolchain), false)
-				addStaticDeps(config.AddressSanitizerCXXStaticRuntimeLibrary(toolchain), false)
+				addStaticDeps(config.AddressSanitizerStaticRuntimeLibrary(), false)
+				addStaticDeps(config.AddressSanitizerCXXStaticRuntimeLibrary(), false)
 			} else {
-				runtimeSharedLibrary = config.AddressSanitizerRuntimeLibrary(toolchain)
+				runtimeSharedLibrary = config.AddressSanitizerRuntimeLibrary()
 			}
 		} else if Bool(sanProps.Hwaddress) {
 			if c.staticBinary() {
-				addStaticDeps(config.HWAddressSanitizerStaticLibrary(toolchain), true)
+				addStaticDeps(config.HWAddressSanitizerStaticLibrary(), true)
 				addStaticDeps("libdl", false)
 			} else {
-				runtimeSharedLibrary = config.HWAddressSanitizerRuntimeLibrary(toolchain)
+				runtimeSharedLibrary = config.HWAddressSanitizerRuntimeLibrary()
 			}
 		} else if Bool(sanProps.Thread) {
-			runtimeSharedLibrary = config.ThreadSanitizerRuntimeLibrary(toolchain)
+			runtimeSharedLibrary = config.ThreadSanitizerRuntimeLibrary()
 		} else if Bool(sanProps.Scudo) {
 			if len(diagSanitizers) == 0 && !c.sanitize.Properties.UbsanRuntimeDep {
-				runtimeSharedLibrary = config.ScudoMinimalRuntimeLibrary(toolchain)
+				runtimeSharedLibrary = config.ScudoMinimalRuntimeLibrary()
 			} else {
-				runtimeSharedLibrary = config.ScudoRuntimeLibrary(toolchain)
+				runtimeSharedLibrary = config.ScudoRuntimeLibrary()
 			}
 		} else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep ||
 			Bool(sanProps.Fuzzer) ||
@@ -1683,20 +1577,20 @@
 				// Also manually add a static runtime for musl to match what clang does for glibc.
 				// Otherwise dlopening libraries that depend on libclang_rt.ubsan_standalone.so fails with:
 				// Error relocating ...: initial-exec TLS resolves to dynamic definition
-				addStaticDeps(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)+".static", true)
+				addStaticDeps(config.UndefinedBehaviorSanitizerRuntimeLibrary()+".static", true)
 			} else {
-				runtimeSharedLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
+				runtimeSharedLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary()
 			}
 		}
 
 		if enableMinimalRuntime(c.sanitize) || c.sanitize.Properties.MinimalRuntimeDep {
-			addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), true)
+			addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(), true)
 		}
 		if c.sanitize.Properties.BuiltinsDep {
-			addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain), true)
+			addStaticDeps(config.BuiltinsRuntimeLibrary(), true)
 		}
 
-		if runtimeSharedLibrary != "" && (toolchain.Bionic() || toolchain.Musl() || c.sanitize.Properties.UbsanRuntimeDep) {
+		if runtimeSharedLibrary != "" && (toolchain.Bionic() || toolchain.Musl()) {
 			// UBSan is supported on non-bionic linux host builds as well
 
 			// Adding dependency to the runtime library. We are using *FarVariation*
@@ -1713,12 +1607,6 @@
 				// the best.
 				addStaticDeps(runtimeSharedLibrary, true)
 			} else if !c.static() && !c.Header() {
-				// If we're using snapshots, redirect to snapshot whenever possible
-				snapshot, _ := android.ModuleProvider(mctx, SnapshotInfoProvider)
-				if lib, ok := snapshot.SharedLibs[runtimeSharedLibrary]; ok {
-					runtimeSharedLibrary = lib
-				}
-
 				// Skip apex dependency check for sharedLibraryDependency
 				// when sanitizer diags are enabled. Skipping the check will allow
 				// building with diag libraries without having to list the
@@ -1865,6 +1753,14 @@
 	}).(*sanitizerStaticLibsMap)
 }
 
+var memtagStackStaticLibsKey = android.NewOnceKey("memtagStackStaticLibs")
+
+func memtagStackStaticLibs(config android.Config) *sanitizerStaticLibsMap {
+	return config.Once(memtagStackStaticLibsKey, func() interface{} {
+		return newSanitizerStaticLibsMap(Memtag_stack)
+	}).(*sanitizerStaticLibsMap)
+}
+
 func enableMinimalRuntime(sanitize *sanitize) bool {
 	if sanitize.isSanitizerEnabled(Asan) {
 		return false
@@ -1911,3 +1807,122 @@
 func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
 	hwasanStaticLibs(ctx.Config()).exportToMake(ctx)
 }
+
+func memtagStackMakeVarsProvider(ctx android.MakeVarsContext) {
+	memtagStackStaticLibs(ctx.Config()).exportToMake(ctx)
+}
+
+type sanitizerLibrariesTxtModule struct {
+	android.ModuleBase
+
+	outputFile android.OutputPath
+}
+
+var _ etc.PrebuiltEtcModule = (*sanitizerLibrariesTxtModule)(nil)
+
+func RegisterSanitizerLibrariesTxtType(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("sanitizer_libraries_txt", sanitizerLibrariesTxtFactory)
+}
+
+func sanitizerLibrariesTxtFactory() android.Module {
+	m := &sanitizerLibrariesTxtModule{}
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	return m
+}
+
+type sanitizerLibraryDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+func (t sanitizerLibraryDependencyTag) AllowDisabledModuleDependency(target android.Module) bool {
+	return true
+}
+
+var _ android.AllowDisabledModuleDependency = (*sanitizerLibraryDependencyTag)(nil)
+
+func (txt *sanitizerLibrariesTxtModule) DepsMutator(actx android.BottomUpMutatorContext) {
+	targets := actx.Config().Targets[android.Android]
+	depTag := sanitizerLibraryDependencyTag{}
+
+	for _, target := range targets {
+		variation := append(target.Variations(),
+			blueprint.Variation{Mutator: "image", Variation: ""},
+			blueprint.Variation{Mutator: "sdk", Variation: ""},
+			blueprint.Variation{Mutator: "link", Variation: "shared"},
+		)
+		for _, lib := range android.SortedStringValues(sanitizerVariables) {
+			if actx.OtherModuleFarDependencyVariantExists(variation, lib) {
+				actx.AddFarVariationDependencies(variation, depTag, lib)
+			}
+
+			prebuiltLibName := "prebuilt_" + lib
+			if actx.OtherModuleFarDependencyVariantExists(variation, prebuiltLibName) {
+				actx.AddFarVariationDependencies(variation, depTag, prebuiltLibName)
+			}
+		}
+	}
+
+}
+
+func (txt *sanitizerLibrariesTxtModule) getSanitizerLibs(ctx android.ModuleContext) string {
+	var sanitizerLibStems []string
+
+	ctx.VisitDirectDepsIf(func(m android.Module) bool {
+		if !m.Enabled(ctx) {
+			return false
+		}
+
+		ccModule, _ := m.(*Module)
+		if ccModule == nil || ccModule.library == nil || !ccModule.library.shared() {
+			return false
+		}
+
+		targets := ctx.Config().Targets[android.Android]
+
+		for _, target := range targets {
+			if m.Target().Os == target.Os && m.Target().Arch.ArchType == target.Arch.ArchType {
+				return true
+			}
+		}
+
+		return false
+	}, func(m android.Module) {
+		ccModule, _ := m.(*Module)
+		outputFile := ccModule.outputFile
+		if outputFile.Valid() {
+			sanitizerLibStems = append(sanitizerLibStems, outputFile.Path().Base())
+		}
+	})
+
+	sanitizerLibStems = android.SortedUniqueStrings(sanitizerLibStems)
+	return strings.Join(sanitizerLibStems, "\n")
+}
+
+func (txt *sanitizerLibrariesTxtModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	filename := txt.Name()
+
+	txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
+	android.WriteFileRule(ctx, txt.outputFile, txt.getSanitizerLibs(ctx))
+
+	installPath := android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(installPath, filename, txt.outputFile)
+
+	ctx.SetOutputFiles(android.Paths{txt.outputFile}, "")
+}
+
+func (txt *sanitizerLibrariesTxtModule) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(txt.outputFile),
+	}}
+}
+
+// PrebuiltEtcModule interface
+func (txt *sanitizerLibrariesTxtModule) BaseDir() string {
+	return "etc"
+}
+
+// PrebuiltEtcModule interface
+func (txt *sanitizerLibrariesTxtModule) SubDir() string {
+	return ""
+}
diff --git a/cc/sdk.go b/cc/sdk.go
index 6341926..4925ce1 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -49,45 +49,19 @@
 			modules[1].(*Module).Properties.IsSdkVariant = true
 
 			if ctx.Config().UnbundledBuildApps() {
-				// For an unbundled apps build, hide the platform variant from Make.
+				// 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
-				modules[0].(*Module).Properties.PreventInstall = 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
-				modules[1].(*Module).Properties.PreventInstall = 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 && ccModule.isImportedApiLibrary() {
-			apiLibrary, _ := ccModule.linker.(*apiLibraryDecorator)
-			if apiLibrary.hasNDKStubs() && ccModule.canUseSdk() {
-				variations := []string{"sdk"}
-				if apiLibrary.hasApexStubs() {
-					variations = append(variations, "")
-				}
-				// Handle cc_api_library module with NDK stubs and variants only which can use SDK
-				modules := ctx.CreateVariations(variations...)
-				// Mark the SDK variant.
-				modules[0].(*Module).Properties.IsSdkVariant = true
-				if ctx.Config().UnbundledBuildApps() {
-					if apiLibrary.hasApexStubs() {
-						// For an unbundled apps build, hide the platform variant from Make.
-						modules[1].(*Module).Properties.HideFromMake = true
-					}
-					modules[1].(*Module).Properties.PreventInstall = true
-				} else {
-					// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
-					// exposed to Make.
-					modules[0].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
-					// SDK variant is not supposed to be installed
-					modules[0].(*Module).Properties.PreventInstall = true
-				}
-			} else {
-				ccModule.Properties.Sdk_version = nil
-				ctx.CreateVariations("")
-				ctx.AliasVariation("")
-			}
 		} else {
 			if isCcModule {
 				// Clear the sdk_version property for modules that don't have an SDK variant so
@@ -106,8 +80,6 @@
 			}
 			ctx.AliasVariation("")
 		}
-	case *snapshotModule:
-		ctx.CreateVariations("")
 	case *CcApiVariant:
 		ccApiVariant, _ := ctx.Module().(*CcApiVariant)
 		if String(ccApiVariant.properties.Variant) == "ndk" {
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index e769fe9..f4a3d88 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -18,818 +18,9 @@
 // snapshot mutators and snapshot information maps which are also defined in this file.
 
 import (
-	"fmt"
-	"strings"
-
 	"android/soong/android"
-	"android/soong/snapshot"
-
-	"github.com/google/blueprint"
 )
 
-// This interface overrides snapshot.SnapshotImage to implement cc module specific functions
-type SnapshotImage interface {
-	snapshot.SnapshotImage
-
-	// The image variant name for this snapshot image.
-	// For example, recovery snapshot image will return "recovery", and vendor snapshot image will
-	// return "vendor." + version.
-	imageVariantName(cfg android.DeviceConfig) string
-
-	// The variant suffix for snapshot modules. For example, vendor snapshot modules will have
-	// ".vendor" as their suffix.
-	moduleNameSuffix() string
-}
-
-type vendorSnapshotImage struct {
-	*snapshot.VendorSnapshotImage
-}
-
-type recoverySnapshotImage struct {
-	*snapshot.RecoverySnapshotImage
-}
-
-func (vendorSnapshotImage) imageVariantName(cfg android.DeviceConfig) string {
-	return VendorVariationPrefix + cfg.VndkVersion()
-}
-
-func (vendorSnapshotImage) moduleNameSuffix() string {
-	return VendorSuffix
-}
-
-func (recoverySnapshotImage) imageVariantName(cfg android.DeviceConfig) string {
-	return android.RecoveryVariation
-}
-
-func (recoverySnapshotImage) moduleNameSuffix() string {
-	return RecoverySuffix
-}
-
-// Override existing vendor and recovery snapshot for cc module specific extra functions
-var VendorSnapshotImageSingleton vendorSnapshotImage = vendorSnapshotImage{&snapshot.VendorSnapshotImageSingleton}
-var RecoverySnapshotImageSingleton recoverySnapshotImage = recoverySnapshotImage{&snapshot.RecoverySnapshotImageSingleton}
-
-func RegisterVendorSnapshotModules(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("vendor_snapshot", vendorSnapshotFactory)
-	ctx.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
-	ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
-	ctx.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
-	ctx.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
-	ctx.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
-}
-
-func RegisterRecoverySnapshotModules(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("recovery_snapshot", recoverySnapshotFactory)
-	ctx.RegisterModuleType("recovery_snapshot_shared", RecoverySnapshotSharedFactory)
-	ctx.RegisterModuleType("recovery_snapshot_static", RecoverySnapshotStaticFactory)
-	ctx.RegisterModuleType("recovery_snapshot_header", RecoverySnapshotHeaderFactory)
-	ctx.RegisterModuleType("recovery_snapshot_binary", RecoverySnapshotBinaryFactory)
-	ctx.RegisterModuleType("recovery_snapshot_object", RecoverySnapshotObjectFactory)
-}
-
-func init() {
-	RegisterVendorSnapshotModules(android.InitRegistrationContext)
-	RegisterRecoverySnapshotModules(android.InitRegistrationContext)
-	android.RegisterMakeVarsProvider(pctx, snapshotMakeVarsProvider)
-}
-
-const (
-	snapshotHeaderSuffix = "_header."
-	SnapshotSharedSuffix = "_shared."
-	SnapshotStaticSuffix = "_static."
-	snapshotBinarySuffix = "_binary."
-	snapshotObjectSuffix = "_object."
-	SnapshotRlibSuffix   = "_rlib."
-	SnapshotDylibSuffix  = "_dylib."
-)
-
-type SnapshotProperties struct {
-	Header_libs []string `android:"arch_variant"`
-	Static_libs []string `android:"arch_variant"`
-	Shared_libs []string `android:"arch_variant"`
-	Rlibs       []string `android:"arch_variant"`
-	Dylibs      []string `android:"arch_variant"`
-	Vndk_libs   []string `android:"arch_variant"`
-	Binaries    []string `android:"arch_variant"`
-	Objects     []string `android:"arch_variant"`
-}
-type snapshotModule struct {
-	android.ModuleBase
-
-	properties SnapshotProperties
-
-	baseSnapshot BaseSnapshotDecorator
-
-	image SnapshotImage
-}
-
-func (s *snapshotModule) ImageMutatorBegin(ctx android.BaseModuleContext) {
-	cfg := ctx.DeviceConfig()
-	if !s.image.IsUsingSnapshot(cfg) || s.image.TargetSnapshotVersion(cfg) != s.baseSnapshot.Version() {
-		s.Disable()
-	}
-}
-
-func (s *snapshotModule) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
-	return false
-}
-
-func (s *snapshotModule) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return false
-}
-
-func (s *snapshotModule) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return false
-}
-
-func (s *snapshotModule) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return false
-}
-
-func (s *snapshotModule) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
-	return false
-}
-
-func (s *snapshotModule) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	return []string{s.image.imageVariantName(ctx.DeviceConfig())}
-}
-
-func (s *snapshotModule) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
-}
-
-func (s *snapshotModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Nothing, the snapshot module is only used to forward dependency information in DepsMutator.
-}
-
-func getSnapshotNameSuffix(moduleSuffix, version, arch string) string {
-	versionSuffix := version
-	if arch != "" {
-		versionSuffix += "." + arch
-	}
-	return moduleSuffix + versionSuffix
-}
-
-func (s *snapshotModule) DepsMutator(ctx android.BottomUpMutatorContext) {
-	collectSnapshotMap := func(names []string, snapshotSuffix, moduleSuffix string) map[string]string {
-		snapshotMap := make(map[string]string)
-		for _, name := range names {
-			snapshotMap[name] = name +
-				getSnapshotNameSuffix(snapshotSuffix+moduleSuffix,
-					s.baseSnapshot.Version(),
-					ctx.DeviceConfig().Arches()[0].ArchType.String())
-		}
-		return snapshotMap
-	}
-
-	snapshotSuffix := s.image.moduleNameSuffix()
-	headers := collectSnapshotMap(s.properties.Header_libs, snapshotSuffix, snapshotHeaderSuffix)
-	binaries := collectSnapshotMap(s.properties.Binaries, snapshotSuffix, snapshotBinarySuffix)
-	objects := collectSnapshotMap(s.properties.Objects, snapshotSuffix, snapshotObjectSuffix)
-	staticLibs := collectSnapshotMap(s.properties.Static_libs, snapshotSuffix, SnapshotStaticSuffix)
-	sharedLibs := collectSnapshotMap(s.properties.Shared_libs, snapshotSuffix, SnapshotSharedSuffix)
-	rlibs := collectSnapshotMap(s.properties.Rlibs, snapshotSuffix, SnapshotRlibSuffix)
-	dylibs := collectSnapshotMap(s.properties.Dylibs, snapshotSuffix, SnapshotDylibSuffix)
-	vndkLibs := collectSnapshotMap(s.properties.Vndk_libs, "", vndkSuffix)
-	for k, v := range vndkLibs {
-		sharedLibs[k] = v
-	}
-
-	android.SetProvider(ctx, SnapshotInfoProvider, SnapshotInfo{
-		HeaderLibs: headers,
-		Binaries:   binaries,
-		Objects:    objects,
-		StaticLibs: staticLibs,
-		SharedLibs: sharedLibs,
-		Rlibs:      rlibs,
-		Dylibs:     dylibs,
-	})
-}
-
-type SnapshotInfo struct {
-	HeaderLibs, Binaries, Objects, StaticLibs, SharedLibs, Rlibs, Dylibs map[string]string
-}
-
-var SnapshotInfoProvider = blueprint.NewMutatorProvider[SnapshotInfo]("deps")
-
-var _ android.ImageInterface = (*snapshotModule)(nil)
-
-func snapshotMakeVarsProvider(ctx android.MakeVarsContext) {
-	snapshotSet := map[string]struct{}{}
-	ctx.VisitAllModules(func(m android.Module) {
-		if s, ok := m.(*snapshotModule); ok {
-			if _, ok := snapshotSet[s.Name()]; ok {
-				// arch variant generates duplicated modules
-				// skip this as we only need to know the path of the module.
-				return
-			}
-			snapshotSet[s.Name()] = struct{}{}
-			imageNameVersion := strings.Split(s.image.imageVariantName(ctx.DeviceConfig()), ".")
-			ctx.Strict(
-				strings.Join([]string{strings.ToUpper(imageNameVersion[0]), s.baseSnapshot.Version(), "SNAPSHOT_DIR"}, "_"),
-				ctx.ModuleDir(s))
-		}
-	})
-}
-
-func vendorSnapshotFactory() android.Module {
-	return snapshotFactory(VendorSnapshotImageSingleton)
-}
-
-func recoverySnapshotFactory() android.Module {
-	return snapshotFactory(RecoverySnapshotImageSingleton)
-}
-
-func snapshotFactory(image SnapshotImage) android.Module {
-	snapshotModule := &snapshotModule{}
-	snapshotModule.image = image
-	snapshotModule.AddProperties(
-		&snapshotModule.properties,
-		&snapshotModule.baseSnapshot.baseProperties)
-	android.InitAndroidArchModule(snapshotModule, android.DeviceSupported, android.MultilibBoth)
-	return snapshotModule
-}
-
-type BaseSnapshotDecoratorProperties struct {
-	// snapshot version.
-	Version string
-
-	// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
-	Target_arch string
-
-	// Suffix to be added to the module name when exporting to Android.mk, e.g. ".vendor".
-	Androidmk_suffix string `blueprint:"mutated"`
-
-	// Suffix to be added to the module name, e.g., vendor_shared,
-	// recovery_shared, etc.
-	ModuleSuffix string `blueprint:"mutated"`
-}
-
-// BaseSnapshotDecorator provides common basic functions for all snapshot modules, such as snapshot
-// version, snapshot arch, etc. It also adds a special suffix to Soong module name, so it doesn't
-// collide with source modules. e.g. the following example module,
-//
-//	vendor_snapshot_static {
-//	    name: "libbase",
-//	    arch: "arm64",
-//	    version: 30,
-//	    ...
-//	}
-//
-// will be seen as "libbase.vendor_static.30.arm64" by Soong.
-type BaseSnapshotDecorator struct {
-	baseProperties BaseSnapshotDecoratorProperties
-	Image          SnapshotImage
-}
-
-func (p *BaseSnapshotDecorator) Name(name string) string {
-	return name + p.NameSuffix()
-}
-
-func (p *BaseSnapshotDecorator) NameSuffix() string {
-	return getSnapshotNameSuffix(p.moduleSuffix(), p.Version(), p.Arch())
-}
-
-func (p *BaseSnapshotDecorator) Version() string {
-	return p.baseProperties.Version
-}
-
-func (p *BaseSnapshotDecorator) Arch() string {
-	return p.baseProperties.Target_arch
-}
-
-func (p *BaseSnapshotDecorator) moduleSuffix() string {
-	return p.baseProperties.ModuleSuffix
-}
-
-func (p *BaseSnapshotDecorator) IsSnapshotPrebuilt() bool {
-	return true
-}
-
-func (p *BaseSnapshotDecorator) SnapshotAndroidMkSuffix() string {
-	return p.baseProperties.Androidmk_suffix
-}
-
-func (p *BaseSnapshotDecorator) SetSnapshotAndroidMkSuffix(ctx android.ModuleContext, variant string) {
-	// If there are any 2 or more variations among {core, product, vendor, recovery}
-	// we have to add the androidmk suffix to avoid duplicate modules with the same
-	// name.
-	variations := append(ctx.Target().Variations(), blueprint.Variation{
-		Mutator:   "image",
-		Variation: android.CoreVariation})
-
-	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(LinkableInterface).BaseModuleName()) {
-		p.baseProperties.Androidmk_suffix = p.Image.moduleNameSuffix()
-		return
-	}
-
-	variations = append(ctx.Target().Variations(), blueprint.Variation{
-		Mutator:   "image",
-		Variation: ProductVariationPrefix + ctx.DeviceConfig().PlatformVndkVersion()})
-
-	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(LinkableInterface).BaseModuleName()) {
-		p.baseProperties.Androidmk_suffix = p.Image.moduleNameSuffix()
-		return
-	}
-
-	images := []SnapshotImage{VendorSnapshotImageSingleton, RecoverySnapshotImageSingleton}
-
-	for _, image := range images {
-		if p.Image == image {
-			continue
-		}
-		variations = append(ctx.Target().Variations(), blueprint.Variation{
-			Mutator:   "image",
-			Variation: image.imageVariantName(ctx.DeviceConfig())})
-
-		if ctx.OtherModuleFarDependencyVariantExists(variations,
-			ctx.Module().(LinkableInterface).BaseModuleName()+
-				getSnapshotNameSuffix(
-					image.moduleNameSuffix()+variant,
-					p.Version(),
-					ctx.DeviceConfig().Arches()[0].ArchType.String())) {
-			p.baseProperties.Androidmk_suffix = p.Image.moduleNameSuffix()
-			return
-		}
-	}
-
-	p.baseProperties.Androidmk_suffix = ""
-}
-
-// Call this with a module suffix after creating a snapshot module, such as
-// vendorSnapshotSharedSuffix, recoverySnapshotBinarySuffix, etc.
-func (p *BaseSnapshotDecorator) Init(m LinkableInterface, image SnapshotImage, moduleSuffix string) {
-	p.Image = image
-	p.baseProperties.ModuleSuffix = image.moduleNameSuffix() + moduleSuffix
-	m.AddProperties(&p.baseProperties)
-	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, p)
-	})
-}
-
-// vendorSnapshotLoadHook disables snapshots if it's not BOARD_VNDK_VERSION.
-// As vendor snapshot is only for vendor, such modules won't be used at all.
-func vendorSnapshotLoadHook(ctx android.LoadHookContext, p *BaseSnapshotDecorator) {
-	if p.Version() != ctx.DeviceConfig().VndkVersion() {
-		ctx.Module().Disable()
-		return
-	}
-}
-
-// Module definitions for snapshots of libraries (shared, static, header).
-//
-// Modules (vendor|recovery)_snapshot_(shared|static|header) are defined here. Shared libraries and
-// static libraries have their prebuilt library files (.so for shared, .a for static) as their src,
-// which can be installed or linked against. Also they export flags needed when linked, such as
-// include directories, c flags, sanitize dependency information, etc.
-//
-// These modules are auto-generated by development/vendor_snapshot/update.py.
-type SnapshotLibraryProperties struct {
-	// Prebuilt file for each arch.
-	Src *string `android:"arch_variant"`
-
-	// list of directories that will be added to the include path (using -I).
-	Export_include_dirs []string `android:"arch_variant"`
-
-	// list of directories that will be added to the system path (using -isystem).
-	Export_system_include_dirs []string `android:"arch_variant"`
-
-	// list of flags that will be used for any module that links against this module.
-	Export_flags []string `android:"arch_variant"`
-
-	// Whether this prebuilt needs to depend on sanitize ubsan runtime or not.
-	Sanitize_ubsan_dep *bool `android:"arch_variant"`
-
-	// Whether this prebuilt needs to depend on sanitize minimal runtime or not.
-	Sanitize_minimal_dep *bool `android:"arch_variant"`
-}
-
-type SnapshotSanitizer interface {
-	IsSanitizerAvailable(t SanitizerType) bool
-	SetSanitizerVariation(t SanitizerType, enabled bool)
-	IsSanitizerEnabled(t SanitizerType) bool
-	IsUnsanitizedVariant() bool
-}
-
-type snapshotLibraryDecorator struct {
-	BaseSnapshotDecorator
-	*libraryDecorator
-	properties          SnapshotLibraryProperties
-	sanitizerProperties struct {
-		SanitizerVariation SanitizerType `blueprint:"mutated"`
-
-		// Library flags for cfi variant.
-		Cfi SnapshotLibraryProperties `android:"arch_variant"`
-
-		// Library flags for hwasan variant.
-		Hwasan SnapshotLibraryProperties `android:"arch_variant"`
-	}
-}
-
-func (p *snapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
-	return p.libraryDecorator.linkerFlags(ctx, flags)
-}
-
-func (p *snapshotLibraryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
-	arches := config.Arches()
-	if len(arches) == 0 || arches[0].ArchType.String() != p.Arch() {
-		return false
-	}
-	if !p.header() && p.properties.Src == nil {
-		return false
-	}
-	return true
-}
-
-// cc modules' link functions are to link compiled objects into final binaries.
-// As snapshots are prebuilts, this just returns the prebuilt binary after doing things which are
-// done by normal library decorator, e.g. exporting flags.
-func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
-	var variant string
-	if p.shared() {
-		variant = SnapshotSharedSuffix
-	} else if p.static() {
-		variant = SnapshotStaticSuffix
-	} else {
-		variant = snapshotHeaderSuffix
-	}
-
-	p.SetSnapshotAndroidMkSuffix(ctx, variant)
-
-	if p.header() {
-		return p.libraryDecorator.link(ctx, flags, deps, objs)
-	}
-
-	if p.IsSanitizerEnabled(cfi) {
-		p.properties = p.sanitizerProperties.Cfi
-	} else if p.IsSanitizerEnabled(Hwasan) {
-		p.properties = p.sanitizerProperties.Hwasan
-	}
-
-	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
-		return nil
-	}
-
-	// Flags specified directly to this module.
-	p.libraryDecorator.reexportDirs(android.PathsForModuleSrc(ctx, p.properties.Export_include_dirs)...)
-	p.libraryDecorator.reexportSystemDirs(android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...)
-	p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
-
-	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
-	p.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
-	p.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
-	p.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
-	p.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
-	p.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
-
-	in := android.PathForModuleSrc(ctx, *p.properties.Src)
-	p.unstrippedOutputFile = in
-
-	if p.shared() {
-		libName := in.Base()
-
-		// Optimize out relinking against shared libraries whose interface hasn't changed by
-		// depending on a table of contents file instead of the library itself.
-		tocFile := android.PathForModuleOut(ctx, libName+".toc")
-		p.tocFile = android.OptionalPathForPath(tocFile)
-		TransformSharedObjectToToc(ctx, in, tocFile)
-
-		android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
-			SharedLibrary: in,
-			Target:        ctx.Target(),
-
-			TableOfContents: p.tocFile,
-		})
-	}
-
-	if p.static() {
-		depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(in).Build()
-		android.SetProvider(ctx, StaticLibraryInfoProvider, StaticLibraryInfo{
-			StaticLibrary: in,
-
-			TransitiveStaticLibrariesForOrdering: depSet,
-		})
-	}
-
-	p.libraryDecorator.flagExporter.setProvider(ctx)
-
-	return in
-}
-
-func (p *snapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
-	if p.MatchesWithDevice(ctx.DeviceConfig()) && p.shared() {
-		p.baseInstaller.install(ctx, file)
-	}
-}
-
-func (p *snapshotLibraryDecorator) nativeCoverage() bool {
-	return false
-}
-
-var _ SnapshotSanitizer = (*snapshotLibraryDecorator)(nil)
-
-func (p *snapshotLibraryDecorator) IsSanitizerAvailable(t SanitizerType) bool {
-	switch t {
-	case cfi:
-		return p.sanitizerProperties.Cfi.Src != nil
-	case Hwasan:
-		return p.sanitizerProperties.Hwasan.Src != nil
-	default:
-		return false
-	}
-}
-
-func (p *snapshotLibraryDecorator) SetSanitizerVariation(t SanitizerType, enabled bool) {
-	if !enabled || p.IsSanitizerEnabled(t) {
-		return
-	}
-	if !p.IsUnsanitizedVariant() {
-		panic(fmt.Errorf("snapshot Sanitizer must be one of Cfi or Hwasan but not both"))
-	}
-	p.sanitizerProperties.SanitizerVariation = t
-}
-
-func (p *snapshotLibraryDecorator) IsSanitizerEnabled(t SanitizerType) bool {
-	return p.sanitizerProperties.SanitizerVariation == t
-}
-
-func (p *snapshotLibraryDecorator) IsUnsanitizedVariant() bool {
-	return !p.IsSanitizerEnabled(Asan) &&
-		!p.IsSanitizerEnabled(Hwasan)
-}
-
-func snapshotLibraryFactory(image SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
-	module, library := NewLibrary(android.DeviceSupported)
-
-	module.stl = nil
-	module.sanitize = nil
-	library.disableStripping()
-
-	prebuilt := &snapshotLibraryDecorator{
-		libraryDecorator: library,
-	}
-
-	prebuilt.baseLinker.Properties.No_libcrt = BoolPtr(true)
-	prebuilt.baseLinker.Properties.Nocrt = BoolPtr(true)
-
-	// Prevent default system libs (libc, libm, and libdl) from being linked
-	if prebuilt.baseLinker.Properties.System_shared_libs == nil {
-		prebuilt.baseLinker.Properties.System_shared_libs = []string{}
-	}
-
-	module.compiler = nil
-	module.linker = prebuilt
-	module.installer = prebuilt
-
-	prebuilt.Init(module, image, moduleSuffix)
-	module.AddProperties(
-		&prebuilt.properties,
-		&prebuilt.sanitizerProperties,
-	)
-
-	return module, prebuilt
-}
-
-// vendor_snapshot_shared is a special prebuilt shared library which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_shared
-// overrides the vendor variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
-// is set.
-func VendorSnapshotSharedFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(VendorSnapshotImageSingleton, SnapshotSharedSuffix)
-	prebuilt.libraryDecorator.BuildOnlyShared()
-	return module.Init()
-}
-
-// recovery_snapshot_shared is a special prebuilt shared library which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_shared
-// overrides the recovery variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
-// is set.
-func RecoverySnapshotSharedFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(RecoverySnapshotImageSingleton, SnapshotSharedSuffix)
-	prebuilt.libraryDecorator.BuildOnlyShared()
-	return module.Init()
-}
-
-// vendor_snapshot_static is a special prebuilt static library which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_static
-// overrides the vendor variant of the cc static library with the same name, if BOARD_VNDK_VERSION
-// is set.
-func VendorSnapshotStaticFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(VendorSnapshotImageSingleton, SnapshotStaticSuffix)
-	prebuilt.libraryDecorator.BuildOnlyStatic()
-	return module.Init()
-}
-
-// recovery_snapshot_static is a special prebuilt static library which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_static
-// overrides the recovery variant of the cc static library with the same name, if BOARD_VNDK_VERSION
-// is set.
-func RecoverySnapshotStaticFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(RecoverySnapshotImageSingleton, SnapshotStaticSuffix)
-	prebuilt.libraryDecorator.BuildOnlyStatic()
-	return module.Init()
-}
-
-// vendor_snapshot_header is a special header library which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_header
-// overrides the vendor variant of the cc header library with the same name, if BOARD_VNDK_VERSION
-// is set.
-func VendorSnapshotHeaderFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(VendorSnapshotImageSingleton, snapshotHeaderSuffix)
-	prebuilt.libraryDecorator.HeaderOnly()
-	return module.Init()
-}
-
-// recovery_snapshot_header is a special header library which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_header
-// overrides the recovery variant of the cc header library with the same name, if BOARD_VNDK_VERSION
-// is set.
-func RecoverySnapshotHeaderFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(RecoverySnapshotImageSingleton, snapshotHeaderSuffix)
-	prebuilt.libraryDecorator.HeaderOnly()
-	return module.Init()
-}
-
-// Module definitions for snapshots of executable binaries.
-//
-// Modules (vendor|recovery)_snapshot_binary are defined here. They have their prebuilt executable
-// binaries (e.g. toybox, sh) as their src, which can be installed.
-//
-// These modules are auto-generated by development/vendor_snapshot/update.py.
-type snapshotBinaryProperties struct {
-	// Prebuilt file for each arch.
-	Src *string `android:"arch_variant"`
-}
-
-type snapshotBinaryDecorator struct {
-	BaseSnapshotDecorator
-	*binaryDecorator
-	properties snapshotBinaryProperties
-}
-
-func (p *snapshotBinaryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
-	if config.DeviceArch() != p.Arch() {
-		return false
-	}
-	if p.properties.Src == nil {
-		return false
-	}
-	return true
-}
-
-// cc modules' link functions are to link compiled objects into final binaries.
-// As snapshots are prebuilts, this just returns the prebuilt binary
-func (p *snapshotBinaryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
-	p.SetSnapshotAndroidMkSuffix(ctx, snapshotBinarySuffix)
-
-	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
-		return nil
-	}
-
-	in := android.PathForModuleSrc(ctx, *p.properties.Src)
-	p.unstrippedOutputFile = in
-	binName := in.Base()
-
-	// use cpExecutable to make it executable
-	outputFile := android.PathForModuleOut(ctx, binName)
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.CpExecutable,
-		Description: "prebuilt",
-		Output:      outputFile,
-		Input:       in,
-	})
-
-	// binary snapshots need symlinking
-	p.setSymlinkList(ctx)
-
-	return outputFile
-}
-
-func (p *snapshotBinaryDecorator) nativeCoverage() bool {
-	return false
-}
-
-// vendor_snapshot_binary is a special prebuilt executable binary which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_binary
-// overrides the vendor variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
-func VendorSnapshotBinaryFactory() android.Module {
-	return snapshotBinaryFactory(VendorSnapshotImageSingleton, snapshotBinarySuffix)
-}
-
-// recovery_snapshot_binary is a special prebuilt executable binary which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_binary
-// overrides the recovery variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
-func RecoverySnapshotBinaryFactory() android.Module {
-	return snapshotBinaryFactory(RecoverySnapshotImageSingleton, snapshotBinarySuffix)
-}
-
-func snapshotBinaryFactory(image SnapshotImage, moduleSuffix string) android.Module {
-	module, binary := NewBinary(android.DeviceSupported)
-	binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
-	binary.baseLinker.Properties.Nocrt = BoolPtr(true)
-
-	// Prevent default system libs (libc, libm, and libdl) from being linked
-	if binary.baseLinker.Properties.System_shared_libs == nil {
-		binary.baseLinker.Properties.System_shared_libs = []string{}
-	}
-
-	prebuilt := &snapshotBinaryDecorator{
-		binaryDecorator: binary,
-	}
-
-	module.compiler = nil
-	module.sanitize = nil
-	module.stl = nil
-	module.linker = prebuilt
-
-	prebuilt.Init(module, image, moduleSuffix)
-	module.AddProperties(&prebuilt.properties)
-	return module.Init()
-}
-
-// Module definitions for snapshots of object files (*.o).
-//
-// Modules (vendor|recovery)_snapshot_object are defined here. They have their prebuilt object
-// files (*.o) as their src.
-//
-// These modules are auto-generated by development/vendor_snapshot/update.py.
-type vendorSnapshotObjectProperties struct {
-	// Prebuilt file for each arch.
-	Src *string `android:"arch_variant"`
-}
-
-type snapshotObjectLinker struct {
-	BaseSnapshotDecorator
-	objectLinker
-	properties vendorSnapshotObjectProperties
-}
-
-func (p *snapshotObjectLinker) MatchesWithDevice(config android.DeviceConfig) bool {
-	if config.DeviceArch() != p.Arch() {
-		return false
-	}
-	if p.properties.Src == nil {
-		return false
-	}
-	return true
-}
-
-// cc modules' link functions are to link compiled objects into final binaries.
-// As snapshots are prebuilts, this just returns the prebuilt binary
-func (p *snapshotObjectLinker) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
-	p.SetSnapshotAndroidMkSuffix(ctx, snapshotObjectSuffix)
-
-	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
-		return nil
-	}
-
-	return android.PathForModuleSrc(ctx, *p.properties.Src)
-}
-
-func (p *snapshotObjectLinker) nativeCoverage() bool {
-	return false
-}
-
-// vendor_snapshot_object is a special prebuilt compiled object file which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_object
-// overrides the vendor variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
-func VendorSnapshotObjectFactory() android.Module {
-	module := newObject(android.DeviceSupported)
-
-	prebuilt := &snapshotObjectLinker{
-		objectLinker: objectLinker{
-			baseLinker: NewBaseLinker(nil),
-		},
-	}
-	module.linker = prebuilt
-
-	prebuilt.Init(module, VendorSnapshotImageSingleton, snapshotObjectSuffix)
-	module.AddProperties(&prebuilt.properties)
-
-	// vendor_snapshot_object module does not provide sanitizer variants
-	module.sanitize.Properties.Sanitize.Never = BoolPtr(true)
-
-	return module.Init()
-}
-
-// recovery_snapshot_object is a special prebuilt compiled object file which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_object
-// overrides the recovery variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
-func RecoverySnapshotObjectFactory() android.Module {
-	module := newObject(android.DeviceSupported)
-
-	prebuilt := &snapshotObjectLinker{
-		objectLinker: objectLinker{
-			baseLinker: NewBaseLinker(nil),
-		},
-	}
-	module.linker = prebuilt
-
-	prebuilt.Init(module, RecoverySnapshotImageSingleton, snapshotObjectSuffix)
-	module.AddProperties(&prebuilt.properties)
-	return module.Init()
-}
-
 type SnapshotInterface interface {
 	MatchesWithDevice(config android.DeviceConfig) bool
 	IsSnapshotPrebuilt() bool
@@ -838,6 +29,3 @@
 }
 
 var _ SnapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
-var _ SnapshotInterface = (*snapshotLibraryDecorator)(nil)
-var _ SnapshotInterface = (*snapshotBinaryDecorator)(nil)
-var _ SnapshotInterface = (*snapshotObjectLinker)(nil)
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
deleted file mode 100644
index 1ee120e..0000000
--- a/cc/snapshot_utils.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2020 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 cc
-
-// This file contains utility types and functions for VNDK / vendor snapshot.
-
-import (
-	"android/soong/android"
-)
-
-var (
-	HeaderExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"}
-)
-
-func (m *Module) IsSnapshotLibrary() bool {
-	if _, ok := m.linker.(snapshotLibraryInterface); ok {
-		return true
-	}
-	return false
-}
-
-func (m *Module) SnapshotHeaders() android.Paths {
-	if m.IsSnapshotLibrary() {
-		return m.linker.(snapshotLibraryInterface).snapshotHeaders()
-	}
-	return android.Paths{}
-}
-
-func (m *Module) Dylib() bool {
-	return false
-}
-
-func (m *Module) Rlib() bool {
-	return false
-}
-
-func (m *Module) SnapshotRuntimeLibs() []string {
-	return m.Properties.SnapshotRuntimeLibs
-}
-
-func (m *Module) SnapshotSharedLibs() []string {
-	return m.Properties.SnapshotSharedLibs
-}
-
-func (m *Module) SnapshotStaticLibs() []string {
-	return m.Properties.SnapshotStaticLibs
-}
-
-func (m *Module) SnapshotRlibs() []string {
-	return []string{}
-}
-
-func (m *Module) SnapshotDylibs() []string {
-	return []string{}
-}
-
-// snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots.
-type snapshotLibraryInterface interface {
-	libraryInterface
-
-	// collectHeadersForSnapshot is called in GenerateAndroidBuildActions for snapshot aware
-	// modules (See isSnapshotAware below).
-	// This function should gather all headers needed for snapshot.
-	collectHeadersForSnapshot(ctx android.ModuleContext)
-
-	// snapshotHeaders should return collected headers by collectHeadersForSnapshot.
-	// Calling snapshotHeaders before collectHeadersForSnapshot is an error.
-	snapshotHeaders() android.Paths
-}
-
-var _ snapshotLibraryInterface = (*prebuiltLibraryLinker)(nil)
-var _ snapshotLibraryInterface = (*libraryDecorator)(nil)
-
-// snapshotMap is a helper wrapper to a map from base module name to snapshot module name.
-type snapshotMap struct {
-	snapshots map[string]string
-}
-
-func newSnapshotMap() *snapshotMap {
-	return &snapshotMap{
-		snapshots: make(map[string]string),
-	}
-}
-
-func snapshotMapKey(name string, arch android.ArchType) string {
-	return name + ":" + arch.String()
-}
-
-// Adds a snapshot name for given module name and architecture.
-// e.g. add("libbase", X86, "libbase.vndk.29.x86")
-func (s *snapshotMap) add(name string, arch android.ArchType, snapshot string) {
-	s.snapshots[snapshotMapKey(name, arch)] = snapshot
-}
-
-// Returns snapshot name for given module name and architecture, if found.
-// e.g. get("libcutils", X86) => "libcutils.vndk.29.x86", true
-func (s *snapshotMap) get(name string, arch android.ArchType) (snapshot string, found bool) {
-	snapshot, found = s.snapshots[snapshotMapKey(name, arch)]
-	return snapshot, found
-}
-
-// ShouldCollectHeadersForSnapshot determines if the module is a possible candidate for snapshot.
-// If it's true, collectHeadersForSnapshot will be called in GenerateAndroidBuildActions.
-func ShouldCollectHeadersForSnapshot(ctx android.ModuleContext, m LinkableInterface, apexInfo android.ApexInfo) bool {
-	if ctx.DeviceConfig().VndkVersion() != "current" &&
-		ctx.DeviceConfig().RecoverySnapshotVersion() != "current" {
-		return false
-	}
-	if _, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo); ok {
-		return ctx.Config().VndkSnapshotBuildArtifacts()
-	}
-
-	for _, image := range []SnapshotImage{VendorSnapshotImageSingleton, RecoverySnapshotImageSingleton} {
-		if isSnapshotAware(ctx.DeviceConfig(), m, image.IsProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()), apexInfo, image) {
-			return true
-		}
-	}
-	return false
-}
diff --git a/cc/test.go b/cc/test.go
index 3a1a3af..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
@@ -359,6 +281,12 @@
 func (test *testBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = test.binaryDecorator.linkerFlags(ctx, flags)
 	flags = test.testDecorator.linkerFlags(ctx, flags)
+
+	// Add a default rpath to allow tests to dlopen libraries specified in data_libs.
+	// Host modules already get an rpath specified in linker.go.
+	if !ctx.Host() {
+		flags.Global.LdFlags = append(flags.Global.LdFlags, `-Wl,-rpath,\$$ORIGIN`)
+	}
 	return flags
 }
 
@@ -376,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 9c2900c..02f9924 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -15,14 +15,12 @@
 package cc
 
 import (
-	"encoding/json"
 	"path/filepath"
 	"testing"
 
 	"android/soong/android"
 	"android/soong/genrule"
 	"android/soong/multitree"
-	"android/soong/snapshot"
 )
 
 func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
@@ -37,6 +35,7 @@
 
 	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)
@@ -301,14 +300,12 @@
 			system_shared_libs: [],
 			stl: "none",
 			vendor_available: true,
+			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
 			host_supported: true,
 			min_sdk_version: "29",
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
+			double_loadable: true,
 			apex_available: [
 				"//apex_available:platform",
 				"//apex_available:anyapex",
@@ -558,7 +555,7 @@
 		ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
 		ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 
-		RegisterVndkLibraryTxtTypes(ctx)
+		RegisterLlndkLibraryTxtType(ctx)
 	}),
 
 	// Additional files needed in tests that disallow non-existent source files.
@@ -575,16 +572,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,
+		"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/libclang_rt.ubsan_minimal.android_arm64.a": nil,
 		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm.a":   nil,
@@ -624,19 +622,6 @@
 	android.FixtureOverrideTextFile(linuxBionicDefaultsPath, withLinuxBionic()),
 )
 
-// This adds some additional modules and singletons which might negatively impact the performance
-// of tests so they are not included in the PrepareForIntegrationTestWithCc.
-var PrepareForTestWithCcIncludeVndk = android.GroupFixturePreparers(
-	PrepareForIntegrationTestWithCc,
-	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		snapshot.VendorSnapshotImageSingleton.Init(ctx)
-		snapshot.RecoverySnapshotImageSingleton.Init(ctx)
-		RegisterVendorSnapshotModules(ctx)
-		RegisterRecoverySnapshotModules(ctx)
-		ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
-	}),
-)
-
 // PrepareForTestWithHostMusl sets the host configuration to musl libc instead of glibc.  It also disables the test
 // on mac, which doesn't support musl libc, and adds musl modules.
 var PrepareForTestWithHostMusl = android.GroupFixturePreparers(
@@ -718,12 +703,7 @@
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 
-	snapshot.VendorSnapshotImageSingleton.Init(ctx)
-	snapshot.RecoverySnapshotImageSingleton.Init(ctx)
-	RegisterVendorSnapshotModules(ctx)
-	RegisterRecoverySnapshotModules(ctx)
-	ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
-	RegisterVndkLibraryTxtTypes(ctx)
+	RegisterLlndkLibraryTxtType(ctx)
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	android.RegisterPrebuiltMutators(ctx)
@@ -776,14 +756,6 @@
 	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
 }
 
-func AssertExcludeFromVendorSnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool, variant string) {
-	t.Helper()
-	m := ctx.ModuleForTests(name, variant).Module().(LinkableInterface)
-	if m.ExcludeFromVendorSnapshot() != expected {
-		t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", m.String(), expected)
-	}
-}
-
 func GetOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
 	for _, moduleName := range moduleNames {
 		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
@@ -792,30 +764,3 @@
 	}
 	return paths
 }
-
-func AssertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool, variant string) {
-	t.Helper()
-	m := ctx.ModuleForTests(name, variant).Module().(LinkableInterface)
-	if m.ExcludeFromRecoverySnapshot() != expected {
-		t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
-	}
-}
-
-func checkOverrides(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, jsonPath string, expected []string) {
-	t.Helper()
-	out := singleton.MaybeOutput(jsonPath)
-	content := android.ContentFromFileRuleForTests(t, ctx, out)
-
-	var flags snapshotJsonFlags
-	if err := json.Unmarshal([]byte(content), &flags); err != nil {
-		t.Errorf("Error while unmarshalling json %q: %s", jsonPath, err.Error())
-		return
-	}
-
-	for _, moduleName := range expected {
-		if !android.InList(moduleName, flags.Overrides) {
-			t.Errorf("expected %q to be in %q: %q", moduleName, flags.Overrides, content)
-			return
-		}
-	}
-}
diff --git a/cc/tidy.go b/cc/tidy.go
index 76ac7d5..ec1e8a2 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -220,7 +220,7 @@
 
 	// (1) Collect all obj/tidy files into OS-specific groups.
 	ctx.VisitAllModuleVariants(module, func(variant android.Module) {
-		if ctx.Config().KatiEnabled() && android.ShouldSkipAndroidMkProcessing(variant) {
+		if ctx.Config().KatiEnabled() && android.ShouldSkipAndroidMkProcessing(ctx, variant) {
 			return
 		}
 		if m, ok := variant.(*Module); ok {
diff --git a/cc/util.go b/cc/util.go
index c93646b..8ffacae 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -19,7 +19,6 @@
 	"strings"
 
 	"android/soong/android"
-	"android/soong/snapshot"
 )
 
 // Efficiently converts a list of include directories to a single string
@@ -56,18 +55,20 @@
 		localCppFlags:        strings.Join(in.Local.CppFlags, " "),
 		localLdFlags:         strings.Join(in.Local.LdFlags, " "),
 
-		aidlFlags:     strings.Join(in.aidlFlags, " "),
-		rsFlags:       strings.Join(in.rsFlags, " "),
-		libFlags:      strings.Join(in.libFlags, " "),
-		extraLibFlags: strings.Join(in.extraLibFlags, " "),
-		tidyFlags:     strings.Join(in.TidyFlags, " "),
-		sAbiFlags:     strings.Join(in.SAbiFlags, " "),
-		toolchain:     in.Toolchain,
-		gcovCoverage:  in.GcovCoverage,
-		tidy:          in.Tidy,
-		needTidyFiles: in.NeedTidyFiles,
-		sAbiDump:      in.SAbiDump,
-		emitXrefs:     in.EmitXrefs,
+		noOverrideFlags: strings.Join(in.NoOverrideFlags, " "),
+		aidlFlags:       strings.Join(in.aidlFlags, " "),
+		rsFlags:         strings.Join(in.rsFlags, " "),
+		libFlags:        strings.Join(in.libFlags, " "),
+		extraLibFlags:   strings.Join(in.extraLibFlags, " "),
+		tidyFlags:       strings.Join(in.TidyFlags, " "),
+		sAbiFlags:       strings.Join(in.SAbiFlags, " "),
+		toolchain:       in.Toolchain,
+		gcovCoverage:    in.GcovCoverage,
+		tidy:            in.Tidy,
+		needTidyFiles:   in.NeedTidyFiles,
+		sAbiDump:        in.SAbiDump,
+		emitXrefs:       in.EmitXrefs,
+		clangVerify:     in.ClangVerify,
 
 		systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
 
@@ -100,6 +101,12 @@
 		"ln -sf " + target + " " + filepath.Join(dir, linkName)
 }
 
+func WriteStringToFileRule(ctx android.SingletonContext, content, out string) android.OutputPath {
+	outPath := android.PathForOutput(ctx, out)
+	android.WriteFileRule(ctx, outPath, content)
+	return outPath
+}
+
 // Dump a map to a list file as:
 //
 // {key1} {value1}
@@ -115,5 +122,5 @@
 		txtBuilder.WriteString(" ")
 		txtBuilder.WriteString(m[k])
 	}
-	return snapshot.WriteStringToFileRule(ctx, txtBuilder.String(), path)
+	return WriteStringToFileRule(ctx, txtBuilder.String(), path)
 }
diff --git a/cc/vendor_public_library_test.go b/cc/vendor_public_library_test.go
index 769be09..7385f2b 100644
--- a/cc/vendor_public_library_test.go
+++ b/cc/vendor_public_library_test.go
@@ -65,8 +65,8 @@
 	`)
 
 	coreVariant := "android_arm64_armv8-a_shared"
-	vendorVariant := "android_vendor.29_arm64_armv8-a_shared"
-	productVariant := "android_product.29_arm64_armv8-a_shared"
+	vendorVariant := "android_vendor_arm64_armv8-a_shared"
+	productVariant := "android_product_arm64_armv8-a_shared"
 
 	// test if header search paths are correctly added
 	// _static variant is used since _shared reuses *.o from the static variant
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
deleted file mode 100644
index a33ed5f..0000000
--- a/cc/vendor_snapshot.go
+++ /dev/null
@@ -1,459 +0,0 @@
-// Copyright 2020 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 cc
-
-import (
-	"encoding/json"
-	"path/filepath"
-	"strings"
-
-	"android/soong/android"
-	"android/soong/snapshot"
-)
-
-// This file defines how to capture cc modules into snapshot package.
-
-// Checks if the target image would contain VNDK
-func includeVndk(image snapshot.SnapshotImage) bool {
-	if image.ImageName() == snapshot.VendorSnapshotImageName {
-		return true
-	}
-
-	return false
-}
-
-// Check if the module is VNDK private
-func isPrivate(image snapshot.SnapshotImage, m LinkableInterface) bool {
-	if image.ImageName() == snapshot.VendorSnapshotImageName && m.IsVndkPrivate() {
-		return true
-	}
-
-	return false
-}
-
-// Checks if target image supports VNDK Ext
-func supportsVndkExt(image snapshot.SnapshotImage) bool {
-	if image.ImageName() == snapshot.VendorSnapshotImageName {
-		return true
-	}
-
-	return false
-}
-
-// Determines if the module is a candidate for snapshot.
-func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshot.SnapshotImage) bool {
-	if !m.Enabled() || m.HiddenFromMake() {
-		return false
-	}
-	// When android/prebuilt.go selects between source and prebuilt, it sets
-	// HideFromMake on the other one to avoid duplicate install rules in make.
-	if m.IsHideFromMake() {
-		return false
-	}
-	// skip proprietary modules, but (for the vendor snapshot only)
-	// include all VNDK (static)
-	if inProprietaryPath && (!includeVndk(image) || !m.IsVndk()) {
-		return false
-	}
-	// If the module would be included based on its path, check to see if
-	// the module is marked to be excluded. If so, skip it.
-	if image.ExcludeFromSnapshot(m) {
-		return false
-	}
-	if m.Target().Os.Class != android.Device {
-		return false
-	}
-	if m.Target().NativeBridge == android.NativeBridgeEnabled {
-		return false
-	}
-	// the module must be installed in target image
-	if !apexInfo.IsForPlatform() || m.IsSnapshotPrebuilt() || !image.InImage(m)() {
-		return false
-	}
-	// skip kernel_headers which always depend on vendor
-	if m.KernelHeadersDecorator() {
-		return false
-	}
-
-	if m.IsLlndk() {
-		return false
-	}
-
-	// Libraries
-	if sanitizable, ok := m.(PlatformSanitizeable); ok && sanitizable.IsSnapshotLibrary() {
-		if sanitizable.SanitizePropDefined() {
-			// scs exports both sanitized and unsanitized variants for static and header
-			// Always use unsanitized variant of it.
-			if !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(scs) {
-				return false
-			}
-			// cfi and hwasan cannot be enabled at the same time.
-			// Skip variants that have both cfi and hwasan enabled.
-			if sanitizable.IsSanitizerEnabled(cfi) && sanitizable.IsSanitizerEnabled(Hwasan) {
-				return false
-			}
-			// cfi and hwasan also export both variants. But for static, we capture both.
-			// This is because cfi static libraries can't be linked from non-cfi modules,
-			// and vice versa.
-			// hwasan is captured as well to support hwasan build.
-			if !sanitizable.Static() &&
-				!sanitizable.Shared() &&
-				(sanitizable.IsSanitizerEnabled(cfi) || sanitizable.IsSanitizerEnabled(Hwasan)) {
-				return false
-			}
-		}
-		if sanitizable.Static() || sanitizable.Rlib() {
-			return sanitizable.OutputFile().Valid() && !isPrivate(image, m)
-		}
-		if sanitizable.Shared() || sanitizable.Dylib() {
-			if !sanitizable.OutputFile().Valid() {
-				return false
-			}
-			if includeVndk(image) {
-				if !sanitizable.IsVndk() {
-					return true
-				}
-				return sanitizable.IsVndkExt()
-			}
-		}
-		return true
-	}
-
-	// Binaries and Objects
-	if m.Binary() || m.Object() {
-		return m.OutputFile().Valid()
-	}
-
-	return false
-}
-
-// Extend the snapshot.SnapshotJsonFlags to include cc specific fields.
-type snapshotJsonFlags struct {
-	snapshot.SnapshotJsonFlags
-	// library flags
-	ExportedDirs       []string `json:",omitempty"`
-	ExportedSystemDirs []string `json:",omitempty"`
-	ExportedFlags      []string `json:",omitempty"`
-	Sanitize           string   `json:",omitempty"`
-	SanitizeMinimalDep bool     `json:",omitempty"`
-	SanitizeUbsanDep   bool     `json:",omitempty"`
-
-	// binary flags
-	Symlinks         []string `json:",omitempty"`
-	StaticExecutable bool     `json:",omitempty"`
-	InstallInRoot    bool     `json:",omitempty"`
-
-	// dependencies
-	SharedLibs  []string `json:",omitempty"`
-	StaticLibs  []string `json:",omitempty"`
-	RuntimeLibs []string `json:",omitempty"`
-	Dylibs      []string `json:",omitempty"`
-	Rlibs       []string `json:",omitempty"`
-
-	// extra config files
-	InitRc         []string `json:",omitempty"`
-	VintfFragments []string `json:",omitempty"`
-	MinSdkVersion  string   `json:",omitempty"`
-}
-
-var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) snapshot.SnapshotPaths {
-	/*
-		Vendor snapshot zipped artifacts directory structure for cc modules:
-		{SNAPSHOT_ARCH}/
-			arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
-				shared/
-					(.so shared libraries)
-				static/
-					(.a static libraries)
-				header/
-					(header only libraries)
-				binary/
-					(executable binaries)
-				object/
-					(.o object files)
-			arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
-				shared/
-					(.so shared libraries)
-				static/
-					(.a static libraries)
-				header/
-					(header only libraries)
-				binary/
-					(executable binaries)
-				object/
-					(.o object files)
-			NOTICE_FILES/
-				(notice files, e.g. libbase.txt)
-			configs/
-				(config files, e.g. init.rc files, vintf_fragments.xml files, etc.)
-			include/
-				(header files of same directory structure with source tree)
-	*/
-
-	var snapshotOutputs android.Paths
-	var snapshotNotices android.Paths
-
-	includeDir := filepath.Join(snapshotArchDir, "include")
-	configsDir := filepath.Join(snapshotArchDir, "configs")
-
-	installedNotices := make(map[string]bool)
-	installedConfigs := make(map[string]bool)
-
-	var headers android.Paths
-
-	copyFile := func(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath {
-		if fake {
-			// All prebuilt binaries and headers are installed by copyFile function. This makes a fake
-			// snapshot just touch prebuilts and headers, rather than installing real files.
-			return snapshot.WriteStringToFileRule(ctx, "", out)
-		} else {
-			return snapshot.CopyFileRule(pctx, ctx, path, out)
-		}
-	}
-
-	// installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file.
-	// For executables, init_rc and vintf_fragments files are also copied.
-	installSnapshot := func(m LinkableInterface, fake bool) android.Paths {
-		targetArch := "arch-" + m.Target().Arch.ArchType.String()
-		if m.Target().Arch.ArchVariant != "" {
-			targetArch += "-" + m.Target().Arch.ArchVariant
-		}
-
-		var ret android.Paths
-
-		prop := snapshotJsonFlags{}
-
-		// Common properties among snapshots.
-		prop.InitBaseSnapshotPropsWithName(m, ctx.ModuleName(m))
-		if supportsVndkExt(s.Image) && m.IsVndkExt() {
-			// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
-			if m.IsVndkSp() {
-				prop.RelativeInstallPath = "vndk-sp"
-			} else {
-				prop.RelativeInstallPath = "vndk"
-			}
-		} else {
-			prop.RelativeInstallPath = m.RelativeInstallPath()
-		}
-		prop.RuntimeLibs = m.SnapshotRuntimeLibs()
-		prop.Required = m.RequiredModuleNames()
-		if o, ok := m.(overridable); ok {
-			prop.Overrides = o.overriddenModules()
-		}
-		for _, path := range m.InitRc() {
-			prop.InitRc = append(prop.InitRc, filepath.Join("configs", path.Base()))
-		}
-		for _, path := range m.VintfFragments() {
-			prop.VintfFragments = append(prop.VintfFragments, filepath.Join("configs", path.Base()))
-		}
-		if m.IsPrebuilt() {
-			prop.MinSdkVersion = "apex_inherit"
-		} else {
-			prop.MinSdkVersion = m.MinSdkVersion()
-		}
-
-		// install config files. ignores any duplicates.
-		for _, path := range append(m.InitRc(), m.VintfFragments()...) {
-			out := filepath.Join(configsDir, path.Base())
-			if !installedConfigs[out] {
-				installedConfigs[out] = true
-				ret = append(ret, copyFile(ctx, path, out, fake))
-			}
-		}
-
-		var propOut string
-
-		if m.IsSnapshotLibrary() {
-			exporterInfo, _ := android.SingletonModuleProvider(ctx, m.Module(), FlagExporterInfoProvider)
-
-			// library flags
-			prop.ExportedFlags = exporterInfo.Flags
-			for _, dir := range exporterInfo.IncludeDirs {
-				prop.ExportedDirs = append(prop.ExportedDirs, filepath.Join("include", dir.String()))
-			}
-			for _, dir := range exporterInfo.SystemIncludeDirs {
-				prop.ExportedSystemDirs = append(prop.ExportedSystemDirs, filepath.Join("include", dir.String()))
-			}
-
-			// shared libs dependencies aren't meaningful on static or header libs
-			if m.Shared() {
-				prop.SharedLibs = m.SnapshotSharedLibs()
-			}
-
-			// dylibs collect both shared and dylib dependencies.
-			if m.Dylib() {
-				prop.SharedLibs = m.SnapshotSharedLibs()
-				prop.Dylibs = m.SnapshotDylibs()
-			}
-
-			// static and rlib libs dependencies are required to collect the NOTICE files.
-			prop.StaticLibs = m.SnapshotStaticLibs()
-			prop.Rlibs = m.SnapshotRlibs()
-
-			if sanitizable, ok := m.(PlatformSanitizeable); ok {
-				if sanitizable.Static() && sanitizable.SanitizePropDefined() {
-					prop.SanitizeMinimalDep = sanitizable.MinimalRuntimeDep() || sanitizable.MinimalRuntimeNeeded()
-					prop.SanitizeUbsanDep = sanitizable.UbsanRuntimeDep() || sanitizable.UbsanRuntimeNeeded()
-				}
-			}
-
-			var libType string
-			if m.Static() {
-				libType = "static"
-			} else if m.Shared() {
-				libType = "shared"
-			} else if m.Rlib() {
-				libType = "rlib"
-			} else if m.Dylib() {
-				libType = "dylib"
-			} else {
-				libType = "header"
-			}
-
-			var stem string
-
-			// install .a, .rlib, .dylib.so, or .so
-			if libType != "header" {
-				libPath := m.OutputFile().Path()
-				stem = libPath.Base()
-				if sanitizable, ok := m.(PlatformSanitizeable); ok {
-					if (sanitizable.Static() || sanitizable.Rlib()) && sanitizable.SanitizePropDefined() {
-						if sanitizable.IsSanitizerEnabled(cfi) {
-							// both cfi and non-cfi variant for static libraries can exist.
-							// attach .cfi to distinguish between cfi and non-cfi.
-							// e.g. libbase.a -> libbase.cfi.a
-							ext := filepath.Ext(stem)
-							stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext
-							prop.Sanitize = "cfi"
-							prop.ModuleName += ".cfi"
-						} else if sanitizable.IsSanitizerEnabled(Hwasan) {
-							// Same for the hwasan
-							ext := filepath.Ext(stem)
-							stem = strings.TrimSuffix(stem, ext) + ".hwasan" + ext
-							prop.Sanitize = "hwasan"
-							prop.ModuleName += ".hwasan"
-						}
-					}
-				}
-				if m.Rlib() && m.RlibStd() {
-					// rlibs produce both rlib-std and dylib-std variants
-					ext := filepath.Ext(stem)
-					stem = strings.TrimSuffix(stem, ext) + ".rlib-std" + ext
-					prop.ModuleName += ".rlib-std"
-				}
-				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem)
-				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
-			} else {
-				stem = ctx.ModuleName(m)
-			}
-
-			propOut = filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem+".json")
-		} else if m.Binary() {
-			// binary flags
-			prop.Symlinks = m.Symlinks()
-			prop.StaticExecutable = m.StaticExecutable()
-			prop.InstallInRoot = m.InstallInRoot()
-			prop.SharedLibs = m.SnapshotSharedLibs()
-			prop.Dylibs = m.SnapshotDylibs()
-
-			// static and rlib dependencies are required to collect the NOTICE files.
-			prop.StaticLibs = m.SnapshotStaticLibs()
-			prop.Rlibs = m.SnapshotRlibs()
-
-			// install bin
-			binPath := m.OutputFile().Path()
-			snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
-			ret = append(ret, copyFile(ctx, binPath, snapshotBinOut, fake))
-			propOut = snapshotBinOut + ".json"
-		} else if m.Object() {
-			// object files aren't installed to the device, so their names can conflict.
-			// Use module name as stem.
-			objPath := m.OutputFile().Path()
-			snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object",
-				ctx.ModuleName(m)+filepath.Ext(objPath.Base()))
-			ret = append(ret, copyFile(ctx, objPath, snapshotObjOut, fake))
-			propOut = snapshotObjOut + ".json"
-		} else {
-			ctx.Errorf("unknown module %q in vendor snapshot", m.String())
-			return nil
-		}
-
-		j, err := json.Marshal(prop)
-		if err != nil {
-			ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
-			return nil
-		}
-		ret = append(ret, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
-
-		return ret
-	}
-
-	ctx.VisitAllModules(func(module android.Module) {
-		m, ok := module.(LinkableInterface)
-		if !ok {
-			return
-		}
-
-		moduleDir := ctx.ModuleDir(module)
-		inProprietaryPath := s.Image.IsProprietaryPath(moduleDir, ctx.DeviceConfig())
-		apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
-
-		if s.Image.ExcludeFromSnapshot(m) {
-			if inProprietaryPath {
-				// Error: exclude_from_vendor_snapshot applies
-				// to framework-path modules only.
-				ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir)
-				return
-			}
-		}
-
-		if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, s.Image) {
-			return
-		}
-
-		// If we are using directed snapshot and a module is not included in the
-		// list, we will still include the module as if it was a fake module.
-		// The reason is that soong needs all the dependencies to be present, even
-		// if they are not using during the build.
-		installAsFake := s.Fake
-		if s.Image.ExcludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
-			installAsFake = true
-		}
-
-		// installSnapshot installs prebuilts and json flag files
-		snapshotOutputs = append(snapshotOutputs, installSnapshot(m, installAsFake)...)
-		// just gather headers and notice files here, because they are to be deduplicated
-		if m.IsSnapshotLibrary() {
-			headers = append(headers, m.SnapshotHeaders()...)
-		}
-
-		for _, notice := range m.EffectiveLicenseFiles() {
-			if _, ok := installedNotices[notice.String()]; !ok {
-				installedNotices[notice.String()] = true
-				snapshotNotices = append(snapshotNotices, notice)
-			}
-		}
-	})
-
-	// install all headers after removing duplicates
-	for _, header := range android.FirstUniquePaths(headers) {
-		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), s.Fake))
-	}
-
-	return snapshot.SnapshotPaths{OutputFiles: snapshotOutputs, NoticeFiles: snapshotNotices}
-}
-
-func init() {
-	snapshot.RegisterSnapshotAction(ccSnapshotAction)
-}
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
deleted file mode 100644
index 0a55431..0000000
--- a/cc/vendor_snapshot_test.go
+++ /dev/null
@@ -1,1751 +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 (
-	"android/soong/android"
-	"fmt"
-	"path/filepath"
-	"reflect"
-	"strings"
-	"testing"
-)
-
-func checkJsonContents(t *testing.T, ctx *android.TestContext, snapshotSingleton android.TestingSingleton, jsonPath string, key string, value string) {
-	jsonOut := snapshotSingleton.MaybeOutput(jsonPath)
-	if jsonOut.Rule == nil {
-		t.Errorf("%q expected but not found", jsonPath)
-		return
-	}
-	content := android.ContentFromFileRuleForTests(t, ctx, jsonOut)
-	if !strings.Contains(content, fmt.Sprintf("%q:%q", key, value)) {
-		t.Errorf("%q must include %q:%q but it only has %v", jsonPath, key, value, jsonOut.Args["content"])
-	}
-}
-
-func TestVendorSnapshotCapture(t *testing.T) {
-	bp := `
-	cc_library {
-		name: "libvndk",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "libvendor",
-		vendor: true,
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "libvendor_override",
-		vendor: true,
-		nocrt: true,
-		overrides: ["libvendor"],
-	}
-
-	cc_library {
-		name: "libvendor_available",
-		vendor_available: true,
-		nocrt: true,
-		min_sdk_version: "29",
-	}
-
-	cc_library_headers {
-		name: "libvendor_headers",
-		vendor_available: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "vendor_bin",
-		vendor: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "vendor_available_bin",
-		vendor_available: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "vendor_bin_override",
-		vendor: true,
-		nocrt: true,
-		overrides: ["vendor_bin"],
-	}
-
-	cc_prebuilt_library_static {
-		name: "libb",
-		vendor_available: true,
-		srcs: ["libb.a"],
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-	}
-
-	cc_object {
-		name: "obj",
-		vendor_available: true,
-	}
-
-	cc_library {
-		name: "libllndk",
-		llndk: {
-			symbol_file: "libllndk.map.txt",
-		},
-	}
-`
-
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	ctx := testCcWithConfig(t, config)
-
-	// Check Vendor snapshot output.
-
-	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-
-	var jsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-		[]string{"arm", "armv7-a-neon"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		// For shared libraries, only non-VNDK vendor_available modules are captured
-		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(sharedDir, "libvendor.so.json"),
-			filepath.Join(sharedDir, "libvendor_available.so.json"))
-
-		// LLNDK modules are not captured
-		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
-
-		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
-		// Also cfi variants are captured, except for prebuilts like toolchain_library
-		staticVariant := fmt.Sprintf("android_vendor.29_%s_%s_static", archType, archVariant)
-		staticCfiVariant := fmt.Sprintf("android_vendor.29_%s_%s_static_cfi", archType, archVariant)
-		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
-		CheckSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(staticDir, "libb.a.json"),
-			filepath.Join(staticDir, "libvndk.a.json"),
-			filepath.Join(staticDir, "libvndk.cfi.a.json"),
-			filepath.Join(staticDir, "libvendor.a.json"),
-			filepath.Join(staticDir, "libvendor.cfi.a.json"),
-			filepath.Join(staticDir, "libvendor_available.a.json"),
-			filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
-
-		checkJsonContents(t, ctx, snapshotSingleton, filepath.Join(staticDir, "libb.a.json"), "MinSdkVersion", "apex_inherit")
-		checkJsonContents(t, ctx, snapshotSingleton, filepath.Join(staticDir, "libvendor_available.a.json"), "MinSdkVersion", "29")
-
-		// For binary executables, all vendor:true and vendor_available modules are captured.
-		if archType == "arm64" {
-			binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
-			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
-			CheckSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
-			CheckSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
-			jsonFiles = append(jsonFiles,
-				filepath.Join(binaryDir, "vendor_bin.json"),
-				filepath.Join(binaryDir, "vendor_available_bin.json"))
-
-			checkOverrides(t, ctx, snapshotSingleton, filepath.Join(binaryDir, "vendor_bin_override.json"), []string{"vendor_bin"})
-		}
-
-		// For header libraries, all vendor:true and vendor_available modules are captured.
-		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
-		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "libvendor_headers.json"))
-
-		// For object modules, all vendor:true and vendor_available modules are captured.
-		objectVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
-		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
-		CheckSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
-		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
-
-		checkOverrides(t, ctx, snapshotSingleton, filepath.Join(sharedDir, "libvendor_override.so.json"), []string{"libvendor"})
-	}
-
-	for _, jsonFile := range jsonFiles {
-		// verify all json files exist
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("%q expected but not found", jsonFile)
-		}
-	}
-
-	// fake snapshot should have all outputs in the normal snapshot.
-	fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
-	for _, output := range snapshotSingleton.AllOutputs() {
-		fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
-		if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
-			t.Errorf("%q expected but not found", fakeOutput)
-		}
-	}
-}
-
-func TestVendorSnapshotDirected(t *testing.T) {
-	bp := `
-	cc_library_shared {
-		name: "libvendor",
-		vendor: true,
-		nocrt: true,
-	}
-
-	cc_library_shared {
-		name: "libvendor_available",
-		vendor_available: true,
-		nocrt: true,
-	}
-
-	genrule {
-		name: "libfoo_gen",
-		cmd: "",
-		out: ["libfoo.so"],
-	}
-
-	cc_prebuilt_library_shared {
-		name: "libfoo",
-		vendor: true,
-		prefer: true,
-		srcs: [":libfoo_gen"],
-	}
-
-	cc_library_shared {
-		name: "libfoo",
-		vendor: true,
-		nocrt: true,
-	}
-`
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	config.TestProductVariables.DirectedVendorSnapshot = true
-	config.TestProductVariables.VendorSnapshotModules = make(map[string]bool)
-	config.TestProductVariables.VendorSnapshotModules["libvendor"] = true
-	config.TestProductVariables.VendorSnapshotModules["libfoo"] = true
-	ctx := testCcWithConfig(t, config)
-
-	// Check Vendor snapshot output.
-
-	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-
-	var includeJsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-		[]string{"arm", "armv7-a-neon"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-
-		// Included modules
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
-		// Check that snapshot captures "prefer: true" prebuilt
-		CheckSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
-
-		// Excluded modules. Modules not included in the directed vendor snapshot
-		// are still include as fake modules.
-		CheckSnapshotRule(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor_available.so.json"))
-	}
-
-	// Verify that each json file for an included module has a rule.
-	for _, jsonFile := range includeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("include json file %q not found", jsonFile)
-		}
-	}
-}
-
-func TestVendorSnapshotUse(t *testing.T) {
-	frameworkBp := `
-	cc_library {
-		name: "libvndk",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "libvendor",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-	}
-
-	cc_library {
-		name: "libvendor_available",
-		vendor_available: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-	}
-
-	cc_library {
-		name: "lib32",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-		compile_multilib: "32",
-	}
-
-	cc_library {
-		name: "lib64",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		no_crt_pad_segment: true,
-		stl: "none",
-		system_shared_libs: [],
-		compile_multilib: "64",
-	}
-
-	cc_library {
-		name: "libllndk",
-		llndk: {
-			symbol_file: "libllndk.map.txt",
-		},
-	}
-
-	cc_binary {
-		name: "bin",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-	}
-
-	cc_binary {
-		name: "bin32",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-		compile_multilib: "32",
-	}
-`
-
-	vndkBp := `
-	vndk_prebuilt_shared {
-		name: "libvndk",
-		version: "31",
-		target_arch: "arm64",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		arch: {
-			arm64: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-			arm: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-		},
-	}
-
-	// old snapshot module which has to be ignored
-	vndk_prebuilt_shared {
-		name: "libvndk",
-		version: "26",
-		target_arch: "arm64",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		arch: {
-			arm64: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-			arm: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-		},
-	}
-
-	// different arch snapshot which has to be ignored
-	vndk_prebuilt_shared {
-		name: "libvndk",
-		version: "31",
-		target_arch: "arm",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		arch: {
-			arm: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-		},
-	}
-
-	vndk_prebuilt_shared {
-		name: "libllndk",
-		version: "31",
-		target_arch: "arm64",
-		vendor_available: true,
-		product_available: true,
-		arch: {
-			arm64: {
-				srcs: ["libllndk.so"],
-			},
-			arm: {
-				srcs: ["libllndk.so"],
-			},
-		},
-	}
-`
-
-	vendorProprietaryBp := `
-	cc_library {
-		name: "libvendor_without_snapshot",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		no_crt_pad_segment: true,
-		stl: "none",
-		system_shared_libs: [],
-	}
-
-	cc_library_shared {
-		name: "libclient",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		no_crt_pad_segment: true,
-		stl: "none",
-		system_shared_libs: [],
-		shared_libs: ["libvndk", "libvendor_available", "libllndk"],
-		static_libs: ["libvendor", "libvendor_without_snapshot"],
-		arch: {
-			arm64: {
-				shared_libs: ["lib64"],
-			},
-			arm: {
-				shared_libs: ["lib32"],
-			},
-		},
-		srcs: ["client.cpp"],
-	}
-
-	cc_library_shared {
-		name: "libclient_cfi",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		no_crt_pad_segment: true,
-		stl: "none",
-		system_shared_libs: [],
-		static_libs: ["libvendor"],
-		sanitize: {
-			cfi: true,
-		},
-		srcs: ["client.cpp"],
-	}
-
-	cc_library_shared {
-		name: "libvndkext",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		no_crt_pad_segment: true,
-		stl: "none",
-		system_shared_libs: [],
-		vndk: {
-			extends: "libvndk",
-			enabled: true,
-		}
-	}
-
-	cc_binary {
-		name: "bin_without_snapshot",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "libc++_static",
-		system_shared_libs: [],
-		static_libs: ["libvndk"],
-		srcs: ["bin.cpp"],
-	}
-
-	vendor_snapshot {
-		name: "vendor_snapshot",
-		version: "31",
-		arch: {
-			arm64: {
-				vndk_libs: [
-					"libvndk",
-					"libllndk",
-				],
-				static_libs: [
-					"libc++_static",
-					"libc++demangle",
-					"libunwind",
-					"libvendor",
-					"libvendor_available",
-					"libvndk",
-					"lib64",
-				],
-				shared_libs: [
-					"libvendor",
-					"libvendor_override",
-					"libvendor_available",
-					"lib64",
-				],
-				binaries: [
-					"bin",
-					"bin_override",
-				],
-			},
-			arm: {
-				vndk_libs: [
-					"libvndk",
-					"libllndk",
-				],
-				static_libs: [
-					"libvendor",
-					"libvendor_available",
-					"libvndk",
-					"lib32",
-				],
-				shared_libs: [
-					"libvendor",
-					"libvendor_override",
-					"libvendor_available",
-					"lib32",
-				],
-				binaries: [
-					"bin32",
-				],
-			},
-		}
-	}
-
-	vendor_snapshot_static {
-		name: "libvndk",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "both",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "libvndk.a",
-			},
-			arm: {
-				src: "libvndk.a",
-			},
-		},
-		shared_libs: ["libvndk"],
-		export_shared_lib_headers: ["libvndk"],
-	}
-
-	vendor_snapshot_shared {
-		name: "libvendor",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "both",
-		vendor: true,
-		no_crt_pad_segment: true,
-		shared_libs: [
-			"libvendor_without_snapshot",
-			"libvendor_available",
-			"libvndk",
-		],
-		arch: {
-			arm64: {
-				src: "libvendor.so",
-				export_include_dirs: ["include/libvendor"],
-			},
-			arm: {
-				src: "libvendor.so",
-				export_include_dirs: ["include/libvendor"],
-			},
-		},
-	}
-
-	vendor_snapshot_shared {
-		name: "libvendor_override",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "both",
-		vendor: true,
-		no_crt_pad_segment: true,
-		overrides: ["libvendor"],
-		shared_libs: [
-			"libvendor_without_snapshot",
-			"libvendor_available",
-			"libvndk",
-		],
-		arch: {
-			arm64: {
-				src: "override/libvendor.so",
-				export_include_dirs: ["include/libvendor"],
-			},
-			arm: {
-				src: "override/libvendor.so",
-				export_include_dirs: ["include/libvendor"],
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "lib32",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "32",
-		vendor: true,
-		arch: {
-			arm: {
-				src: "lib32.a",
-			},
-		},
-	}
-
-	vendor_snapshot_shared {
-		name: "lib32",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "32",
-		vendor: true,
-		no_crt_pad_segment: true,
-		arch: {
-			arm: {
-				src: "lib32.so",
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "lib64",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "lib64.a",
-			},
-		},
-	}
-
-	vendor_snapshot_shared {
-		name: "lib64",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "64",
-		vendor: true,
-		no_crt_pad_segment: true,
-		arch: {
-			arm64: {
-				src: "lib64.so",
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "libvendor",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "both",
-		vendor: true,
-		arch: {
-			arm64: {
-				cfi: {
-					src: "libvendor.cfi.a",
-					export_include_dirs: ["include/libvendor_cfi"],
-				},
-				src: "libvendor.a",
-				export_include_dirs: ["include/libvendor"],
-			},
-			arm: {
-				cfi: {
-					src: "libvendor.cfi.a",
-					export_include_dirs: ["include/libvendor_cfi"],
-				},
-				src: "libvendor.a",
-				export_include_dirs: ["include/libvendor"],
-			},
-		},
-	}
-
-	vendor_snapshot_shared {
-		name: "libvendor_available",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "both",
-		vendor: true,
-		no_crt_pad_segment: true,
-		arch: {
-			arm64: {
-				src: "libvendor_available.so",
-				export_include_dirs: ["include/libvendor"],
-			},
-			arm: {
-				src: "libvendor_available.so",
-				export_include_dirs: ["include/libvendor"],
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "libvendor_available",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "both",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "libvendor_available.a",
-				export_include_dirs: ["include/libvendor"],
-			},
-			arm: {
-				src: "libvendor_available.so",
-				export_include_dirs: ["include/libvendor"],
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "libc++_static",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "libc++_static.a",
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "libc++demangle",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "libc++demangle.a",
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "libunwind",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "libunwind.a",
-			},
-		},
-	}
-
-	vendor_snapshot_binary {
-		name: "bin",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "bin",
-			},
-		},
-		symlinks: ["binfoo", "binbar"],
-	}
-
-	vendor_snapshot_binary {
-		name: "bin_override",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "64",
-		vendor: true,
-		overrides: ["bin"],
-		arch: {
-			arm64: {
-				src: "override/bin",
-			},
-		},
-		symlinks: ["binfoo", "binbar"],
-	}
-
-	vendor_snapshot_binary {
-		name: "bin32",
-		version: "31",
-		target_arch: "arm64",
-		compile_multilib: "32",
-		vendor: true,
-		arch: {
-			arm: {
-				src: "bin32",
-			},
-		},
-	}
-
-	// old snapshot module which has to be ignored
-	vendor_snapshot_binary {
-		name: "bin",
-		version: "26",
-		target_arch: "arm64",
-		compile_multilib: "first",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "bin",
-			},
-		},
-	}
-
-	// different arch snapshot which has to be ignored
-	vendor_snapshot_binary {
-		name: "bin",
-		version: "31",
-		target_arch: "arm",
-		compile_multilib: "first",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "bin",
-			},
-		},
-	}
-`
-	depsBp := GatherRequiredDepsForTest(android.Android)
-
-	mockFS := map[string][]byte{
-		"deps/Android.bp":                  []byte(depsBp),
-		"framework/Android.bp":             []byte(frameworkBp),
-		"framework/symbol.txt":             nil,
-		"vendor/Android.bp":                []byte(vendorProprietaryBp),
-		"vendor/bin":                       nil,
-		"vendor/override/bin":              nil,
-		"vendor/bin32":                     nil,
-		"vendor/bin.cpp":                   nil,
-		"vendor/client.cpp":                nil,
-		"vendor/include/libvndk/a.h":       nil,
-		"vendor/include/libvendor/b.h":     nil,
-		"vendor/include/libvendor_cfi/c.h": nil,
-		"vendor/libc++_static.a":           nil,
-		"vendor/libc++demangle.a":          nil,
-		"vendor/libunwind.a":               nil,
-		"vendor/libvndk.a":                 nil,
-		"vendor/libvendor.a":               nil,
-		"vendor/libvendor.cfi.a":           nil,
-		"vendor/libvendor.so":              nil,
-		"vendor/override/libvendor.so":     nil,
-		"vendor/lib32.a":                   nil,
-		"vendor/lib32.so":                  nil,
-		"vendor/lib64.a":                   nil,
-		"vendor/lib64.so":                  nil,
-		"vndk/Android.bp":                  []byte(vndkBp),
-		"vndk/include/libvndk/a.h":         nil,
-		"vndk/libvndk.so":                  nil,
-		"vndk/libllndk.so":                 nil,
-	}
-
-	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("31")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("32")
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "vendor/Android.bp", "vndk/Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	sharedVariant := "android_vendor.31_arm64_armv8-a_shared"
-	staticVariant := "android_vendor.31_arm64_armv8-a_static"
-	binaryVariant := "android_vendor.31_arm64_armv8-a"
-
-	sharedCfiVariant := "android_vendor.31_arm64_armv8-a_shared_cfi"
-	staticCfiVariant := "android_vendor.31_arm64_armv8-a_static_cfi"
-
-	shared32Variant := "android_vendor.31_arm_armv7-a-neon_shared"
-	binary32Variant := "android_vendor.31_arm_armv7-a-neon"
-
-	// libclient uses libvndk.vndk.31.arm64, libvendor.vendor_static.31.arm64, libvendor_without_snapshot
-	libclientCcFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("cc").Args["cFlags"]
-	for _, includeFlags := range []string{
-		"-Ivndk/include/libvndk",     // libvndk
-		"-Ivendor/include/libvendor", // libvendor
-	} {
-		if !strings.Contains(libclientCcFlags, includeFlags) {
-			t.Errorf("flags for libclient must contain %#v, but was %#v.",
-				includeFlags, libclientCcFlags)
-		}
-	}
-
-	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("ld").Args["libFlags"]
-	for _, input := range [][]string{
-		[]string{sharedVariant, "libvndk.vndk.31.arm64"},
-		[]string{sharedVariant, "libllndk.vndk.31.arm64"},
-		[]string{staticVariant, "libvendor.vendor_static.31.arm64"},
-		[]string{staticVariant, "libvendor_without_snapshot"},
-	} {
-		outputPaths := GetOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
-		if !strings.Contains(libclientLdFlags, outputPaths[0].String()) {
-			t.Errorf("libflags for libclient must contain %#v, but was %#v", outputPaths[0], libclientLdFlags)
-		}
-
-	}
-
-	libclientAndroidMkSharedLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkSharedLibs
-	if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "libllndk.vendor", "lib64"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted libclient AndroidMkSharedLibs %q, got %q", w, g)
-	}
-
-	libclientAndroidMkStaticLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkStaticLibs
-	if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
-	}
-
-	libclient32AndroidMkSharedLibs := ctx.ModuleForTests("libclient", shared32Variant).Module().(*Module).Properties.AndroidMkSharedLibs
-	if g, w := libclient32AndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "libllndk.vendor", "lib32"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted libclient32 AndroidMkSharedLibs %q, got %q", w, g)
-	}
-
-	// libclient_cfi uses libvendor.vendor_static.31.arm64's cfi variant
-	libclientCfiCcFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("cc").Args["cFlags"]
-	if !strings.Contains(libclientCfiCcFlags, "-Ivendor/include/libvendor_cfi") {
-		t.Errorf("flags for libclient_cfi must contain %#v, but was %#v.",
-			"-Ivendor/include/libvendor_cfi", libclientCfiCcFlags)
-	}
-
-	libclientCfiLdFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("ld").Args["libFlags"]
-	libvendorCfiOutputPaths := GetOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.31.arm64"})
-	if !strings.Contains(libclientCfiLdFlags, libvendorCfiOutputPaths[0].String()) {
-		t.Errorf("libflags for libclientCfi must contain %#v, but was %#v", libvendorCfiOutputPaths[0], libclientCfiLdFlags)
-	}
-
-	// bin_without_snapshot uses libvndk.vendor_static.31.arm64 (which reexports vndk's exported headers)
-	binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"]
-	if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivndk/include/libvndk") {
-		t.Errorf("flags for bin_without_snapshot must contain %#v, but was %#v.",
-			"-Ivendor/include/libvndk", binWithoutSnapshotCcFlags)
-	}
-
-	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("ld").Args["libFlags"]
-	libVndkStaticOutputPaths := GetOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.31.arm64"})
-	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
-		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
-			libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
-	}
-
-	// libvendor.so is installed by libvendor.vendor_shared.31.arm64
-	ctx.ModuleForTests("libvendor.vendor_shared.31.arm64", sharedVariant).Output("libvendor.so")
-
-	// lib64.so is installed by lib64.vendor_shared.31.arm64
-	ctx.ModuleForTests("lib64.vendor_shared.31.arm64", sharedVariant).Output("lib64.so")
-
-	// lib32.so is installed by lib32.vendor_shared.31.arm64
-	ctx.ModuleForTests("lib32.vendor_shared.31.arm64", shared32Variant).Output("lib32.so")
-
-	// libvendor_available.so is installed by libvendor_available.vendor_shared.31.arm64
-	ctx.ModuleForTests("libvendor_available.vendor_shared.31.arm64", sharedVariant).Output("libvendor_available.so")
-
-	// libvendor_without_snapshot.so is installed by libvendor_without_snapshot
-	ctx.ModuleForTests("libvendor_without_snapshot", sharedVariant).Output("libvendor_without_snapshot.so")
-
-	// bin is installed by bin.vendor_binary.31.arm64
-	bin64Module := ctx.ModuleForTests("bin.vendor_binary.31.arm64", binaryVariant)
-	bin64Module.Output("bin")
-
-	// also test symlinks
-	bin64MkEntries := android.AndroidMkEntriesForTest(t, ctx, bin64Module.Module())
-	bin64KatiSymlinks := bin64MkEntries[0].EntryMap["LOCAL_SOONG_INSTALL_SYMLINKS"]
-
-	// Either AndroidMk entries contain symlinks, or symlinks should be installed by Soong
-	for _, symlink := range []string{"binfoo", "binbar"} {
-		if inList(symlink, bin64KatiSymlinks) {
-			continue
-		}
-
-		bin64Module.Output(symlink)
-	}
-
-	// bin32 is installed by bin32.vendor_binary.31.arm64
-	ctx.ModuleForTests("bin32.vendor_binary.31.arm64", binary32Variant).Output("bin32")
-
-	// bin_without_snapshot is installed by bin_without_snapshot
-	ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot")
-
-	// libvendor, libvendor_available and bin don't have vendor.31 variant
-	libvendorVariants := ctx.ModuleVariantsForTests("libvendor")
-	if inList(sharedVariant, libvendorVariants) {
-		t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant)
-	}
-
-	libvendorAvailableVariants := ctx.ModuleVariantsForTests("libvendor_available")
-	if inList(sharedVariant, libvendorAvailableVariants) {
-		t.Errorf("libvendor_available must not have variant %#v, but it does", sharedVariant)
-	}
-
-	binVariants := ctx.ModuleVariantsForTests("bin")
-	if inList(binaryVariant, binVariants) {
-		t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
-	}
-
-	// test overrides property
-	binOverrideModule := ctx.ModuleForTests("bin_override.vendor_binary.31.arm64", binaryVariant)
-	binOverrideModule.Output("bin")
-	binOverrideMkEntries := android.AndroidMkEntriesForTest(t, ctx, binOverrideModule.Module())
-	binOverrideEntry := binOverrideMkEntries[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
-	if !inList("bin", binOverrideEntry) {
-		t.Errorf("bin_override must override bin but was %q\n", binOverrideEntry)
-	}
-
-	libvendorOverrideModule := ctx.ModuleForTests("libvendor_override.vendor_shared.31.arm64", sharedVariant)
-	libvendorOverrideModule.Output("libvendor.so")
-	libvendorOverrideMkEntries := android.AndroidMkEntriesForTest(t, ctx, libvendorOverrideModule.Module())
-	libvendorOverrideEntry := libvendorOverrideMkEntries[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
-	if !inList("libvendor", libvendorOverrideEntry) {
-		t.Errorf("libvendor_override must override libvendor but was %q\n", libvendorOverrideEntry)
-	}
-}
-
-func TestVendorSnapshotSanitizer(t *testing.T) {
-	bp := `
-	vendor_snapshot {
-		name: "vendor_snapshot",
-		version: "28",
-		arch: {
-			arm64: {
-				static_libs: [
-					"libsnapshot",
-					"note_memtag_heap_sync",
-				],
-				objects: [
-					"snapshot_object",
-				],
-				vndk_libs: [
-					"libclang_rt.hwasan",
-				],
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "libsnapshot",
-		vendor: true,
-		target_arch: "arm64",
-		version: "28",
-		arch: {
-			arm64: {
-				src: "libsnapshot.a",
-				cfi: {
-					src: "libsnapshot.cfi.a",
-				},
-				hwasan: {
-					src: "libsnapshot.hwasan.a",
-				},
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "note_memtag_heap_sync",
-		vendor: true,
-		target_arch: "arm64",
-		version: "28",
-		arch: {
-			arm64: {
-				src: "note_memtag_heap_sync.a",
-			},
-		},
-	}
-
-	vndk_prebuilt_shared {
-		name: "libclang_rt.hwasan",
-		version: "28",
-		target_arch: "arm64",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		arch: {
-			arm64: {
-				srcs: ["libclang_rt.hwasan.so"],
-			},
-		},
-	}
-
-	vendor_snapshot_object {
-		name: "snapshot_object",
-		vendor: true,
-		target_arch: "arm64",
-		version: "28",
-		arch: {
-			arm64: {
-				src: "snapshot_object.o",
-			},
-		},
-		stl: "none",
-	}
-
-	cc_test {
-		name: "vstest",
-		gtest: false,
-		vendor: true,
-		compile_multilib: "64",
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		static_libs: ["libsnapshot"],
-		system_shared_libs: [],
-	}
-`
-
-	mockFS := map[string][]byte{
-		"vendor/Android.bp":              []byte(bp),
-		"vendor/libc++demangle.a":        nil,
-		"vendor/libclang_rt.hwasan.so":   nil,
-		"vendor/libsnapshot.a":           nil,
-		"vendor/libsnapshot.cfi.a":       nil,
-		"vendor/libsnapshot.hwasan.a":    nil,
-		"vendor/note_memtag_heap_sync.a": nil,
-		"vendor/snapshot_object.o":       nil,
-	}
-
-	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("28")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	config.TestProductVariables.SanitizeDevice = []string{"hwaddress"}
-	ctx := testCcWithConfig(t, config)
-
-	// Check non-cfi, cfi and hwasan variant.
-	staticVariant := "android_vendor.28_arm64_armv8-a_static"
-	staticCfiVariant := "android_vendor.28_arm64_armv8-a_static_cfi"
-	staticHwasanVariant := "android_vendor.28_arm64_armv8-a_static_hwasan"
-	staticHwasanCfiVariant := "android_vendor.28_arm64_armv8-a_static_hwasan_cfi"
-
-	staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticVariant).Module().(*Module)
-	assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
-
-	staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticCfiVariant).Module().(*Module)
-	assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
-
-	staticHwasanModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticHwasanVariant).Module().(*Module)
-	assertString(t, staticHwasanModule.outputFile.Path().Base(), "libsnapshot.hwasan.a")
-
-	staticHwasanCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticHwasanCfiVariant).Module().(*Module)
-	if !staticHwasanCfiModule.HiddenFromMake() || !staticHwasanCfiModule.PreventInstall() {
-		t.Errorf("Hwasan and Cfi cannot enabled at the same time.")
-	}
-
-	snapshotObjModule := ctx.ModuleForTests("snapshot_object.vendor_object.28.arm64", "android_vendor.28_arm64_armv8-a").Module()
-	snapshotObjMkEntries := android.AndroidMkEntriesForTest(t, ctx, snapshotObjModule)
-	// snapshot object must not add ".hwasan" suffix
-	assertString(t, snapshotObjMkEntries[0].EntryMap["LOCAL_MODULE"][0], "snapshot_object")
-}
-
-func TestVendorSnapshotExclude(t *testing.T) {
-
-	// This test verifies that the exclude_from_vendor_snapshot property
-	// makes its way from the Android.bp source file into the module data
-	// structure. It also verifies that modules are correctly included or
-	// excluded in the vendor snapshot based on their path (framework or
-	// vendor) and the exclude_from_vendor_snapshot property.
-
-	frameworkBp := `
-		cc_library_shared {
-			name: "libinclude",
-			srcs: ["src/include.cpp"],
-			vendor_available: true,
-		}
-		cc_library_shared {
-			name: "libexclude",
-			srcs: ["src/exclude.cpp"],
-			vendor: true,
-			exclude_from_vendor_snapshot: true,
-		}
-		cc_library_shared {
-			name: "libavailable_exclude",
-			srcs: ["src/exclude.cpp"],
-			vendor_available: true,
-			exclude_from_vendor_snapshot: true,
-		}
-	`
-
-	vendorProprietaryBp := `
-		cc_library_shared {
-			name: "libvendor",
-			srcs: ["vendor.cpp"],
-			vendor: true,
-		}
-	`
-
-	depsBp := GatherRequiredDepsForTest(android.Android)
-
-	mockFS := map[string][]byte{
-		"deps/Android.bp":       []byte(depsBp),
-		"framework/Android.bp":  []byte(frameworkBp),
-		"framework/include.cpp": nil,
-		"framework/exclude.cpp": nil,
-		"device/Android.bp":     []byte(vendorProprietaryBp),
-		"device/vendor.cpp":     nil,
-	}
-
-	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	// Test an include and exclude framework module.
-	AssertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false, vendorVariant)
-	AssertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true, vendorVariant)
-	AssertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true, vendorVariant)
-
-	// A vendor module is excluded, but by its path, not the
-	// exclude_from_vendor_snapshot property.
-	AssertExcludeFromVendorSnapshotIs(t, ctx, "libvendor", false, vendorVariant)
-
-	// Verify the content of the vendor snapshot.
-
-	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-
-	var includeJsonFiles []string
-	var excludeJsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-		[]string{"arm", "armv7-a-neon"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-
-		// Included modules
-		CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
-
-		// Excluded modules
-		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
-		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
-		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
-	}
-
-	// Verify that each json file for an included module has a rule.
-	for _, jsonFile := range includeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("include json file %q not found", jsonFile)
-		}
-	}
-
-	// Verify that each json file for an excluded module has no rule.
-	for _, jsonFile := range excludeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
-			t.Errorf("exclude json file %q found", jsonFile)
-		}
-	}
-}
-
-func TestVendorSnapshotExcludeInVendorProprietaryPathErrors(t *testing.T) {
-
-	// This test verifies that using the exclude_from_vendor_snapshot
-	// property on a module in a vendor proprietary path generates an
-	// error. These modules are already excluded, so we prohibit using the
-	// property in this way, which could add to confusion.
-
-	vendorProprietaryBp := `
-		cc_library_shared {
-			name: "libvendor",
-			srcs: ["vendor.cpp"],
-			vendor: true,
-			exclude_from_vendor_snapshot: true,
-		}
-	`
-
-	depsBp := GatherRequiredDepsForTest(android.Android)
-
-	mockFS := map[string][]byte{
-		"deps/Android.bp":   []byte(depsBp),
-		"device/Android.bp": []byte(vendorProprietaryBp),
-		"device/vendor.cpp": nil,
-	}
-
-	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "device/Android.bp"})
-	android.FailIfErrored(t, errs)
-
-	_, errs = ctx.PrepareBuildActions(config)
-	android.CheckErrorsAgainstExpectations(t, errs, []string{
-		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
-	})
-}
-
-func TestRecoverySnapshotCapture(t *testing.T) {
-	bp := `
-	cc_library {
-		name: "libvndk",
-		vendor_available: true,
-		recovery_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "librecovery",
-		recovery: true,
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "librecovery_available",
-		recovery_available: true,
-		nocrt: true,
-	}
-
-	cc_library_headers {
-		name: "librecovery_headers",
-		recovery_available: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "recovery_bin",
-		recovery: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "recovery_available_bin",
-		recovery_available: true,
-		nocrt: true,
-	}
-
-	cc_prebuilt_library_static {
-		name: "libb",
-		recovery_available: true,
-		srcs: ["libb.a"],
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-	}
-
-	cc_object {
-		name: "obj",
-		recovery_available: true,
-	}
-`
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	ctx := testCcWithConfig(t, config)
-
-	// Check Recovery snapshot output.
-
-	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
-
-	var jsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		// For shared libraries, only recovery_available modules are captured.
-		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(sharedDir, "libvndk.so.json"),
-			filepath.Join(sharedDir, "librecovery.so.json"),
-			filepath.Join(sharedDir, "librecovery_available.so.json"))
-
-		// For static libraries, all recovery:true and recovery_available modules are captured.
-		staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
-		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
-		CheckSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(staticDir, "libb.a.json"),
-			filepath.Join(staticDir, "librecovery.a.json"),
-			filepath.Join(staticDir, "librecovery_available.a.json"))
-
-		// For binary executables, all recovery:true and recovery_available modules are captured.
-		if archType == "arm64" {
-			binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
-			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
-			CheckSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
-			CheckSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
-			jsonFiles = append(jsonFiles,
-				filepath.Join(binaryDir, "recovery_bin.json"),
-				filepath.Join(binaryDir, "recovery_available_bin.json"))
-		}
-
-		// For header libraries, all vendor:true and vendor_available modules are captured.
-		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
-		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "librecovery_headers.json"))
-
-		// For object modules, all vendor:true and vendor_available modules are captured.
-		objectVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
-		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
-		CheckSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
-		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
-	}
-
-	for _, jsonFile := range jsonFiles {
-		// verify all json files exist
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("%q expected but not found", jsonFile)
-		}
-	}
-}
-
-func TestRecoverySnapshotExclude(t *testing.T) {
-	// This test verifies that the exclude_from_recovery_snapshot property
-	// makes its way from the Android.bp source file into the module data
-	// structure. It also verifies that modules are correctly included or
-	// excluded in the recovery snapshot based on their path (framework or
-	// vendor) and the exclude_from_recovery_snapshot property.
-
-	frameworkBp := `
-		cc_library_shared {
-			name: "libinclude",
-			srcs: ["src/include.cpp"],
-			recovery_available: true,
-		}
-		cc_library_shared {
-			name: "libexclude",
-			srcs: ["src/exclude.cpp"],
-			recovery: true,
-			exclude_from_recovery_snapshot: true,
-		}
-		cc_library_shared {
-			name: "libavailable_exclude",
-			srcs: ["src/exclude.cpp"],
-			recovery_available: true,
-			exclude_from_recovery_snapshot: true,
-		}
-	`
-
-	vendorProprietaryBp := `
-		cc_library_shared {
-			name: "librecovery",
-			srcs: ["recovery.cpp"],
-			recovery: true,
-		}
-	`
-
-	depsBp := GatherRequiredDepsForTest(android.Android)
-
-	mockFS := map[string][]byte{
-		"deps/Android.bp":       []byte(depsBp),
-		"framework/Android.bp":  []byte(frameworkBp),
-		"framework/include.cpp": nil,
-		"framework/exclude.cpp": nil,
-		"device/Android.bp":     []byte(vendorProprietaryBp),
-		"device/recovery.cpp":   nil,
-	}
-
-	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
-	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	ctx := CreateTestContext(config)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	// Test an include and exclude framework module.
-	AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false, recoveryVariant)
-	AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true, recoveryVariant)
-	AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true, recoveryVariant)
-
-	// A recovery module is excluded, but by its path, not the
-	// exclude_from_recovery_snapshot property.
-	AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false, recoveryVariant)
-
-	// Verify the content of the recovery snapshot.
-
-	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
-
-	var includeJsonFiles []string
-	var excludeJsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-
-		// Included modules
-		CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
-
-		// Excluded modules
-		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
-		CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
-		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
-	}
-
-	// Verify that each json file for an included module has a rule.
-	for _, jsonFile := range includeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("include json file %q not found", jsonFile)
-		}
-	}
-
-	// Verify that each json file for an excluded module has no rule.
-	for _, jsonFile := range excludeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
-			t.Errorf("exclude json file %q found", jsonFile)
-		}
-	}
-}
-
-func TestRecoverySnapshotDirected(t *testing.T) {
-	bp := `
-	cc_library_shared {
-		name: "librecovery",
-		recovery: true,
-		nocrt: true,
-	}
-
-	cc_library_shared {
-		name: "librecovery_available",
-		recovery_available: true,
-		nocrt: true,
-	}
-
-	genrule {
-		name: "libfoo_gen",
-		cmd: "",
-		out: ["libfoo.so"],
-	}
-
-	cc_prebuilt_library_shared {
-		name: "libfoo",
-		recovery: true,
-		prefer: true,
-		srcs: [":libfoo_gen"],
-	}
-
-	cc_library_shared {
-		name: "libfoo",
-		recovery: true,
-		nocrt: true,
-	}
-`
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	config.TestProductVariables.DirectedRecoverySnapshot = true
-	config.TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
-	config.TestProductVariables.RecoverySnapshotModules["librecovery"] = true
-	config.TestProductVariables.RecoverySnapshotModules["libfoo"] = true
-	ctx := testCcWithConfig(t, config)
-
-	// Check recovery snapshot output.
-
-	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
-
-	var includeJsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-
-		// Included modules
-		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
-		// Check that snapshot captures "prefer: true" prebuilt
-		CheckSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
-
-		// Excluded modules. Modules not included in the directed recovery snapshot
-		// are still include as fake modules.
-		CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.so.json"))
-	}
-
-	// Verify that each json file for an included module has a rule.
-	for _, jsonFile := range includeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("include json file %q not found", jsonFile)
-		}
-	}
-}
-
-func TestSnapshotInRelativeInstallPath(t *testing.T) {
-	bp := `
-	cc_library {
-		name: "libvendor_available",
-		vendor_available: true,
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "libvendor_available_var",
-		vendor_available: true,
-		stem: "libvendor_available",
-		relative_install_path: "var",
-		nocrt: true,
-	}
-`
-
-	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
-	ctx := testCcWithConfig(t, config)
-
-	// Check Vendor snapshot output.
-
-	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-
-	var jsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-		[]string{"arm", "armv7-a-neon"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		// For shared libraries, only non-VNDK vendor_available modules are captured
-		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		sharedDirVar := filepath.Join(sharedDir, "var")
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
-		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available_var", "libvendor_available.so", sharedDirVar, sharedVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(sharedDir, "libvendor_available.so.json"),
-			filepath.Join(sharedDirVar, "libvendor_available.so.json"))
-	}
-
-	for _, jsonFile := range jsonFiles {
-		// verify all json files exist
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("%q expected but not found", jsonFile)
-		}
-	}
-
-	// fake snapshot should have all outputs in the normal snapshot.
-	fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
-	for _, output := range snapshotSingleton.AllOutputs() {
-		fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
-		if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
-			t.Errorf("%q expected but not found", fakeOutput)
-		}
-	}
-}
diff --git a/cc/vndk.go b/cc/vndk.go
index 2b2ea64..9d196a0 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -15,55 +15,20 @@
 package cc
 
 import (
-	"encoding/json"
-	"errors"
-	"fmt"
-	"path/filepath"
-	"sort"
 	"strings"
 
 	"android/soong/android"
-	"android/soong/cc/config"
-	"android/soong/etc"
-	"android/soong/snapshot"
-
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
 )
 
 const (
-	llndkLibrariesTxt                = "llndk.libraries.txt"
-	llndkLibrariesTxtForApex         = "llndk.libraries.txt.apex"
-	vndkCoreLibrariesTxt             = "vndkcore.libraries.txt"
-	vndkSpLibrariesTxt               = "vndksp.libraries.txt"
-	vndkPrivateLibrariesTxt          = "vndkprivate.libraries.txt"
-	vndkProductLibrariesTxt          = "vndkproduct.libraries.txt"
-	vndkUsingCoreVariantLibrariesTxt = "vndkcorevariant.libraries.txt"
+	llndkLibrariesTxt       = "llndk.libraries.txt"
+	vndkCoreLibrariesTxt    = "vndkcore.libraries.txt"
+	vndkSpLibrariesTxt      = "vndksp.libraries.txt"
+	vndkPrivateLibrariesTxt = "vndkprivate.libraries.txt"
+	vndkProductLibrariesTxt = "vndkproduct.libraries.txt"
 )
 
 func VndkLibrariesTxtModules(vndkVersion string, ctx android.BaseModuleContext) []string {
-	// Return the list of vndk txt files for the vndk apex of the vndkVersion.
-	if vndkVersion == "current" {
-		// We can assume all txt files are snapshotted if we find one of them.
-		currentVndkSnapshotted := ctx.OtherModuleExists(insertVndkVersion(llndkLibrariesTxt, ctx.DeviceConfig().PlatformVndkVersion()))
-		if currentVndkSnapshotted {
-			// If the current VNDK is already snapshotted (which can happen with
-			// the `next` config), use the prebuilt txt files in the snapshot.
-			// This is because the txt files built from source are probably be
-			// for the in-development version.
-			vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
-		} else {
-			// Use the txt files generated from the source
-			return []string{
-				llndkLibrariesTxtForApex,
-				vndkCoreLibrariesTxt,
-				vndkSpLibrariesTxt,
-				vndkPrivateLibrariesTxt,
-				vndkProductLibrariesTxt,
-			}
-		}
-	}
-
 	// Snapshot vndks have their own *.libraries.VER.txt files.
 	// Note that snapshots don't have "vndkcorevariant.libraries.VER.txt"
 	result := []string{
@@ -110,843 +75,9 @@
 	}
 }
 
-type vndkdep struct {
-	Properties VndkProperties
-}
-
-func (vndk *vndkdep) props() []interface{} {
-	return []interface{}{&vndk.Properties}
-}
-
-func (vndk *vndkdep) isVndk() bool {
-	return Bool(vndk.Properties.Vndk.Enabled)
-}
-
-func (vndk *vndkdep) isVndkSp() bool {
-	return Bool(vndk.Properties.Vndk.Support_system_process)
-}
-
-func (vndk *vndkdep) isVndkExt() bool {
-	return vndk.Properties.Vndk.Extends != nil
-}
-
-func (vndk *vndkdep) getVndkExtendsModuleName() string {
-	return String(vndk.Properties.Vndk.Extends)
-}
-
-func (vndk *vndkdep) typeName() string {
-	if !vndk.isVndk() {
-		return "native:vendor"
-	}
-	if !vndk.isVndkExt() {
-		if !vndk.isVndkSp() {
-			return "native:vendor:vndk"
-		}
-		return "native:vendor:vndksp"
-	}
-	if !vndk.isVndkSp() {
-		return "native:vendor:vndkext"
-	}
-	return "native:vendor:vndkspext"
-}
-
-// VNDK link type check from a module with UseVndk() == true.
-func (vndk *vndkdep) vndkCheckLinkType(ctx android.BaseModuleContext, to *Module, tag blueprint.DependencyTag) {
-	if to.linker == nil {
-		return
-	}
-	if !vndk.isVndk() {
-		// Non-VNDK modules those installed to /vendor, /system/vendor,
-		// /product or /system/product cannot depend on VNDK-private modules
-		// that include VNDK-core-private, VNDK-SP-private and LLNDK-private.
-		if to.IsVndkPrivate() {
-			ctx.ModuleErrorf("non-VNDK module should not link to %q which has `private: true`", to.Name())
-		}
-	}
-	if lib, ok := to.linker.(*libraryDecorator); !ok || !lib.shared() {
-		// Check only shared libraries.
-		// Other (static) libraries are allowed to link.
-		return
-	}
-
-	if to.IsLlndk() {
-		// LL-NDK libraries are allowed to link
-		return
-	}
-
-	if !to.UseVndk() {
-		ctx.ModuleErrorf("(%s) should not link to %q which is not a vendor-available library",
-			vndk.typeName(), to.Name())
-		return
-	}
-	if tag == vndkExtDepTag {
-		// Ensure `extends: "name"` property refers a vndk module that has vendor_available
-		// and has identical vndk properties.
-		if to.vndkdep == nil || !to.vndkdep.isVndk() {
-			ctx.ModuleErrorf("`extends` refers a non-vndk module %q", to.Name())
-			return
-		}
-		if vndk.isVndkSp() != to.vndkdep.isVndkSp() {
-			ctx.ModuleErrorf(
-				"`extends` refers a module %q with mismatched support_system_process",
-				to.Name())
-			return
-		}
-		if to.IsVndkPrivate() {
-			ctx.ModuleErrorf(
-				"`extends` refers module %q which has `private: true`",
-				to.Name())
-			return
-		}
-	}
-	if to.vndkdep == nil {
-		return
-	}
-
-	// Check the dependencies of VNDK shared libraries.
-	if err := vndkIsVndkDepAllowed(vndk, to.vndkdep); err != nil {
-		ctx.ModuleErrorf("(%s) should not link to %q (%s): %v",
-			vndk.typeName(), to.Name(), to.vndkdep.typeName(), err)
-		return
-	}
-}
-
-func vndkIsVndkDepAllowed(from *vndkdep, to *vndkdep) error {
-	// Check the dependencies of VNDK, VNDK-Ext, VNDK-SP, VNDK-SP-Ext and vendor modules.
-	if from.isVndkExt() {
-		if from.isVndkSp() {
-			if to.isVndk() && !to.isVndkSp() {
-				return errors.New("VNDK-SP extensions must not depend on VNDK or VNDK extensions")
-			}
-			return nil
-		}
-		// VNDK-Ext may depend on VNDK, VNDK-Ext, VNDK-SP, VNDK-SP-Ext, or vendor libs.
-		return nil
-	}
-	if from.isVndk() {
-		if to.isVndkExt() {
-			return errors.New("VNDK-core and VNDK-SP must not depend on VNDK extensions")
-		}
-		if from.isVndkSp() {
-			if !to.isVndkSp() {
-				return errors.New("VNDK-SP must only depend on VNDK-SP")
-			}
-			return nil
-		}
-		if !to.isVndk() {
-			return errors.New("VNDK-core must only depend on VNDK-core or VNDK-SP")
-		}
-		return nil
-	}
-	// Vendor modules may depend on VNDK, VNDK-Ext, VNDK-SP, VNDK-SP-Ext, or vendor libs.
-	return nil
-}
-
-type moduleListerFunc func(ctx android.SingletonContext) (moduleNames, fileNames []string)
-
-var (
-	llndkLibraries                = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsLLNDK && !m.Header() })
-	vndkSPLibraries               = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKSP })
-	vndkCoreLibraries             = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKCore })
-	vndkPrivateLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate })
-	vndkProductLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKProduct })
-	vndkUsingCoreVariantLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKUsingCoreVariant })
-)
-
-// vndkModuleLister takes a predicate that operates on a Module and returns a moduleListerFunc
-// that produces a list of module names and output file names for which the predicate returns true.
-func vndkModuleLister(predicate func(*Module) bool) moduleListerFunc {
-	return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
-		ctx.VisitAllModules(func(m android.Module) {
-			if c, ok := m.(*Module); ok && predicate(c) && !c.IsVndkPrebuiltLibrary() {
-				filename, err := getVndkFileName(c)
-				if err != nil {
-					ctx.ModuleErrorf(m, "%s", err)
-				}
-				moduleNames = append(moduleNames, ctx.ModuleName(m))
-				fileNames = append(fileNames, filename)
-			}
-		})
-		moduleNames = android.SortedUniqueStrings(moduleNames)
-		fileNames = android.SortedUniqueStrings(fileNames)
-		return
-	}
-}
-
-// vndkModuleListRemover takes a moduleListerFunc and a prefix and returns a moduleListerFunc
-// that returns the same lists as the input moduleListerFunc, but with  modules with the
-// given prefix removed.
-func vndkModuleListRemover(lister moduleListerFunc, prefix string) moduleListerFunc {
-	return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
-		moduleNames, fileNames = lister(ctx)
-		filter := func(in []string) []string {
-			out := make([]string, 0, len(in))
-			for _, lib := range in {
-				if strings.HasPrefix(lib, prefix) {
-					continue
-				}
-				out = append(out, lib)
-			}
-			return out
-		}
-		return filter(moduleNames), filter(fileNames)
-	}
-}
-
-var vndkMustUseVendorVariantListKey = android.NewOnceKey("vndkMustUseVendorVariantListKey")
-
-func vndkMustUseVendorVariantList(cfg android.Config) []string {
-	return cfg.Once(vndkMustUseVendorVariantListKey, func() interface{} {
-		return config.VndkMustUseVendorVariantList
-	}).([]string)
-}
-
-// test may call this to override global configuration(config.VndkMustUseVendorVariantList)
-// when it is called, it must be before the first call to vndkMustUseVendorVariantList()
-func setVndkMustUseVendorVariantListForTest(config android.Config, mustUseVendorVariantList []string) {
-	config.Once(vndkMustUseVendorVariantListKey, func() interface{} {
-		return mustUseVendorVariantList
-	})
-}
-
-func processVndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
-	if m.InProduct() {
-		// We may skip the steps for the product variants because they
-		// are already covered by the vendor variants.
-		return
-	}
-
-	name := m.BaseModuleName()
-
-	if lib := m.library; lib != nil && lib.hasStubsVariants() && name != "libz" {
-		// b/155456180 libz is the ONLY exception here. We don't want to make
-		// libz an LLNDK library because we in general can't guarantee that
-		// libz will behave consistently especially about the compression.
-		// i.e. the compressed output might be different across releases.
-		// As the library is an external one, it's risky to keep the compatibility
-		// promise if it becomes an LLNDK.
-		mctx.PropertyErrorf("vndk.enabled", "This library provides stubs. Shouldn't be VNDK. Consider making it as LLNDK")
-	}
-
-	if inList(name, vndkMustUseVendorVariantList(mctx.Config())) {
-		m.Properties.MustUseVendorVariant = true
-	}
-	if mctx.DeviceConfig().VndkUseCoreVariant() && !m.Properties.MustUseVendorVariant {
-		m.VendorProperties.IsVNDKUsingCoreVariant = true
-	}
-
-	if m.vndkdep.isVndkSp() {
-		m.VendorProperties.IsVNDKSP = true
-	} else {
-		m.VendorProperties.IsVNDKCore = true
-	}
-	if m.IsVndkPrivate() {
-		m.VendorProperties.IsVNDKPrivate = true
-	}
-	if Bool(m.VendorProperties.Product_available) {
-		m.VendorProperties.IsVNDKProduct = true
-	}
-}
-
-// Check for modules that mustn't be VNDK
-func shouldSkipVndkMutator(m *Module) bool {
-	if !m.Enabled() {
-		return true
-	}
-	if !m.Device() {
-		// Skip non-device modules
-		return true
-	}
-	if m.Target().NativeBridge == android.NativeBridgeEnabled {
-		// Skip native_bridge modules
-		return true
-	}
-	return false
-}
-
-func IsForVndkApex(mctx android.BottomUpMutatorContext, m *Module) bool {
-	if shouldSkipVndkMutator(m) {
-		return false
-	}
-
-	// TODO(b/142675459): Use enabled: to select target device in vndk_prebuilt_shared
-	// When b/142675459 is landed, remove following check
-	if p, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok {
-		// prebuilt vndk modules should match with device
-		if !p.MatchesWithDevice(mctx.DeviceConfig()) {
-			return false
-		}
-
-		platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
-		if platformVndkVersion != "" {
-			// ignore prebuilt vndk modules that are newer than or equal to the platform vndk version
-			platformVndkApiLevel := android.ApiLevelOrPanic(mctx, platformVndkVersion)
-			if platformVndkApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(mctx, p.Version())) {
-				return false
-			}
-		}
-	}
-
-	if lib, ok := m.linker.(libraryInterface); ok {
-		// VNDK APEX doesn't need stub variants
-		if lib.buildStubs() {
-			return false
-		}
-		useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
-			mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
-		return lib.shared() && m.InVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
-	}
-	return false
-}
-
-// gather list of vndk-core, vndk-sp, and ll-ndk libs
-func VndkMutator(mctx android.BottomUpMutatorContext) {
-	m, ok := mctx.Module().(*Module)
-	if !ok {
-		return
-	}
-
-	if shouldSkipVndkMutator(m) {
-		return
-	}
-
-	lib, isLib := m.linker.(*libraryDecorator)
-	prebuiltLib, isPrebuiltLib := m.linker.(*prebuiltLibraryLinker)
-
-	if m.InVendorOrProduct() && isLib && lib.hasLLNDKStubs() {
-		m.VendorProperties.IsLLNDK = true
-		m.VendorProperties.IsVNDKPrivate = Bool(lib.Properties.Llndk.Private)
-	}
-	if m.InVendorOrProduct() && isPrebuiltLib && prebuiltLib.hasLLNDKStubs() {
-		m.VendorProperties.IsLLNDK = true
-		m.VendorProperties.IsVNDKPrivate = Bool(prebuiltLib.Properties.Llndk.Private)
-	}
-
-	if m.IsVndkPrebuiltLibrary() && !m.IsVndk() {
-		m.VendorProperties.IsLLNDK = true
-		// TODO(b/280697209): copy "llndk.private" flag to vndk_prebuilt_shared
-	}
-
-	if (isLib && lib.buildShared()) || (isPrebuiltLib && prebuiltLib.buildShared()) {
-		if m.vndkdep != nil && m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
-			processVndkLibrary(mctx, m)
-			return
-		}
-	}
-}
-
-func init() {
-	RegisterVndkLibraryTxtTypes(android.InitRegistrationContext)
-	android.RegisterParallelSingletonType("vndk-snapshot", VndkSnapshotSingleton)
-}
-
-func RegisterVndkLibraryTxtTypes(ctx android.RegistrationContext) {
-	ctx.RegisterParallelSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
-	ctx.RegisterParallelSingletonModuleType("llndk_libraries_txt_for_apex", llndkLibrariesTxtApexOnlyFactory)
-	ctx.RegisterParallelSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
-	ctx.RegisterParallelSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
-	ctx.RegisterParallelSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
-	ctx.RegisterParallelSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
-	ctx.RegisterParallelSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
-}
-
-type vndkLibrariesTxt struct {
-	android.SingletonModuleBase
-
-	lister               moduleListerFunc
-	makeVarName          string
-	filterOutFromMakeVar string
-
-	properties VndkLibrariesTxtProperties
-
-	outputFile  android.OutputPath
-	moduleNames []string
-	fileNames   []string
-}
-
-type VndkLibrariesTxtProperties struct {
-	Insert_vndk_version *bool
-	Stem                *string
-}
-
-var _ etc.PrebuiltEtcModule = &vndkLibrariesTxt{}
-var _ android.OutputFileProducer = &vndkLibrariesTxt{}
-
-// llndk_libraries_txt is a singleton module whose content is a list of LLNDK libraries
-// generated by Soong.
-// Make uses LLNDK_LIBRARIES to determine which libraries to install.
-// HWASAN is only part of the LLNDK in builds in which libc depends on HWASAN.
-// Therefore, by removing the library here, we cause it to only be installed if libc
-// depends on it.
-func llndkLibrariesTxtFactory() android.SingletonModule {
-	return newVndkLibrariesWithMakeVarFilter(llndkLibraries, "LLNDK_LIBRARIES", "libclang_rt.hwasan")
-}
-
-// llndk_libraries_txt_for_apex is a singleton module that provide the same LLNDK libraries list
-// with the llndk_libraries_txt, but skips setting make variable LLNDK_LIBRARIES. So, it must not
-// be used without installing llndk_libraries_txt singleton.
-// We include llndk_libraries_txt by default to install the llndk.libraries.txt file to system/etc.
-// This singleton module is to install the llndk.libraries.<ver>.txt file to vndk apex.
-func llndkLibrariesTxtApexOnlyFactory() android.SingletonModule {
-	return newVndkLibrariesWithMakeVarFilter(llndkLibraries, "", "libclang_rt.hwasan")
-}
-
-// vndksp_libraries_txt is a singleton module whose content is a list of VNDKSP libraries
-// generated by Soong but can be referenced by other modules.
-// For example, apex_vndk can depend on these files as prebuilt.
-func vndkSPLibrariesTxtFactory() android.SingletonModule {
-	return newVndkLibrariesTxt(vndkSPLibraries, "VNDK_SAMEPROCESS_LIBRARIES")
-}
-
-// vndkcore_libraries_txt is a singleton module whose content is a list of VNDK core libraries
-// generated by Soong but can be referenced by other modules.
-// For example, apex_vndk can depend on these files as prebuilt.
-func vndkCoreLibrariesTxtFactory() android.SingletonModule {
-	return newVndkLibrariesTxt(vndkCoreLibraries, "VNDK_CORE_LIBRARIES")
-}
-
-// vndkprivate_libraries_txt is a singleton module whose content is a list of VNDK private libraries
-// generated by Soong but can be referenced by other modules.
-// For example, apex_vndk can depend on these files as prebuilt.
-func vndkPrivateLibrariesTxtFactory() android.SingletonModule {
-	return newVndkLibrariesTxt(vndkPrivateLibraries, "VNDK_PRIVATE_LIBRARIES")
-}
-
-// vndkproduct_libraries_txt is a singleton module whose content is a list of VNDK product libraries
-// generated by Soong but can be referenced by other modules.
-// For example, apex_vndk can depend on these files as prebuilt.
-func vndkProductLibrariesTxtFactory() android.SingletonModule {
-	return newVndkLibrariesTxt(vndkProductLibraries, "VNDK_PRODUCT_LIBRARIES")
-}
-
-// vndkcorevariant_libraries_txt is a singleton module whose content is a list of VNDK libraries
-// that are using the core variant, generated by Soong but can be referenced by other modules.
-// For example, apex_vndk can depend on these files as prebuilt.
-func vndkUsingCoreVariantLibrariesTxtFactory() android.SingletonModule {
-	return newVndkLibrariesTxt(vndkUsingCoreVariantLibraries, "VNDK_USING_CORE_VARIANT_LIBRARIES")
-}
-
-func newVndkLibrariesWithMakeVarFilter(lister moduleListerFunc, makeVarName string, filter string) android.SingletonModule {
-	m := &vndkLibrariesTxt{
-		lister:               lister,
-		makeVarName:          makeVarName,
-		filterOutFromMakeVar: filter,
-	}
-	m.AddProperties(&m.properties)
-	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
-	return m
-}
-
-func newVndkLibrariesTxt(lister moduleListerFunc, makeVarName string) android.SingletonModule {
-	return newVndkLibrariesWithMakeVarFilter(lister, makeVarName, "")
-}
-
 func insertVndkVersion(filename string, vndkVersion string) string {
 	if index := strings.LastIndex(filename, "."); index != -1 {
 		return filename[:index] + "." + vndkVersion + filename[index:]
 	}
 	return filename
 }
-
-func (txt *vndkLibrariesTxt) DepsMutator(mctx android.BottomUpMutatorContext) {
-	versionedName := insertVndkVersion(txt.Name(), mctx.DeviceConfig().PlatformVndkVersion())
-	if mctx.OtherModuleExists(versionedName) {
-		// If the prebuilt vndk libraries txt files exist, install them instead.
-		txt.HideFromMake()
-		mctx.AddDependency(txt, nil, versionedName)
-	}
-}
-
-func (txt *vndkLibrariesTxt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	filename := proptools.StringDefault(txt.properties.Stem, txt.Name())
-
-	if Bool(txt.properties.Insert_vndk_version) {
-		filename = insertVndkVersion(filename, ctx.DeviceConfig().PlatformVndkVersion())
-	}
-
-	txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
-
-	installPath := android.PathForModuleInstall(ctx, "etc")
-	ctx.InstallFile(installPath, filename, txt.outputFile)
-}
-
-func (txt *vndkLibrariesTxt) GenerateSingletonBuildActions(ctx android.SingletonContext) {
-	txt.moduleNames, txt.fileNames = txt.lister(ctx)
-	android.WriteFileRule(ctx, txt.outputFile, strings.Join(txt.fileNames, "\n"))
-}
-
-func (txt *vndkLibrariesTxt) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(txt.outputFile),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_STEM", txt.outputFile.Base())
-			},
-		},
-	}}
-}
-
-func (txt *vndkLibrariesTxt) MakeVars(ctx android.MakeVarsContext) {
-	if txt.makeVarName == "" {
-		return
-	}
-
-	filter := func(modules []string, prefix string) []string {
-		if prefix == "" {
-			return modules
-		}
-		var result []string
-		for _, module := range modules {
-			if strings.HasPrefix(module, prefix) {
-				continue
-			} else {
-				result = append(result, module)
-			}
-		}
-		return result
-	}
-	ctx.Strict(txt.makeVarName, strings.Join(filter(txt.moduleNames, txt.filterOutFromMakeVar), " "))
-}
-
-// PrebuiltEtcModule interface
-func (txt *vndkLibrariesTxt) OutputFile() android.OutputPath {
-	return txt.outputFile
-}
-
-// PrebuiltEtcModule interface
-func (txt *vndkLibrariesTxt) BaseDir() string {
-	return "etc"
-}
-
-// PrebuiltEtcModule interface
-func (txt *vndkLibrariesTxt) SubDir() string {
-	return ""
-}
-
-func (txt *vndkLibrariesTxt) OutputFiles(tag string) (android.Paths, error) {
-	return android.Paths{txt.outputFile}, nil
-}
-
-func VndkSnapshotSingleton() android.Singleton {
-	return &vndkSnapshotSingleton{}
-}
-
-type vndkSnapshotSingleton struct {
-	vndkLibrariesFile   android.OutputPath
-	vndkSnapshotZipFile android.OptionalPath
-}
-
-func isVndkSnapshotAware(config android.DeviceConfig, m LinkableInterface,
-	apexInfo android.ApexInfo) (vndkType string, isVndkSnapshotLib bool) {
-
-	if m.Target().NativeBridge == android.NativeBridgeEnabled {
-		return "", false
-	}
-	// !inVendor: There's product/vendor variants for VNDK libs. We only care about vendor variants.
-	// !installable: Snapshot only cares about "installable" modules.
-	// !m.IsLlndk: llndk stubs are required for building against snapshots.
-	// IsSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
-	// !outputFile.Valid: Snapshot requires valid output file.
-	if !m.InVendor() || (!installable(m, apexInfo) && !m.IsLlndk()) || m.IsSnapshotPrebuilt() || !m.OutputFile().Valid() {
-		return "", false
-	}
-	if !m.IsSnapshotLibrary() || !m.Shared() {
-		return "", false
-	}
-	if m.VndkVersion() == config.PlatformVndkVersion() {
-		if m.IsVndk() && !m.IsVndkExt() {
-			if m.IsVndkSp() {
-				return "vndk-sp", true
-			} else {
-				return "vndk-core", true
-			}
-		} else if m.HasLlndkStubs() && m.StubsVersion() == "" {
-			// Use default version for the snapshot.
-			return "llndk-stub", true
-		}
-	}
-
-	return "", false
-}
-
-func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	// build these files even if PlatformVndkVersion or BoardVndkVersion is not set
-	c.buildVndkLibrariesTxtFiles(ctx)
-
-	// BOARD_VNDK_VERSION must be set to 'current' in order to generate a VNDK snapshot.
-	if ctx.DeviceConfig().VndkVersion() != "current" {
-		return
-	}
-
-	if ctx.DeviceConfig().PlatformVndkVersion() == "" {
-		return
-	}
-
-	var snapshotOutputs android.Paths
-
-	/*
-		VNDK snapshot zipped artifacts directory structure:
-		{SNAPSHOT_ARCH}/
-			arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
-				shared/
-					vndk-core/
-						(VNDK-core libraries, e.g. libbinder.so)
-					vndk-sp/
-						(VNDK-SP libraries, e.g. libc++.so)
-					llndk-stub/
-						(LLNDK stub libraries)
-			arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
-				shared/
-					vndk-core/
-						(VNDK-core libraries, e.g. libbinder.so)
-					vndk-sp/
-						(VNDK-SP libraries, e.g. libc++.so)
-					llndk-stub/
-						(LLNDK stub libraries)
-			binder32/
-				(This directory is newly introduced in v28 (Android P) to hold
-				prebuilts built for 32-bit binder interface.)
-				arch-{TARGET_ARCH}-{TARGE_ARCH_VARIANT}/
-					...
-			configs/
-				(various *.txt configuration files)
-			include/
-				(header files of same directory structure with source tree)
-			NOTICE_FILES/
-				(notice files of libraries, e.g. libcutils.so.txt)
-	*/
-
-	snapshotDir := "vndk-snapshot"
-	snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
-
-	configsDir := filepath.Join(snapshotArchDir, "configs")
-	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
-	includeDir := filepath.Join(snapshotArchDir, "include")
-
-	// set of notice files copied.
-	noticeBuilt := make(map[string]bool)
-
-	// paths of VNDK modules for GPL license checking
-	modulePaths := make(map[string]string)
-
-	// actual module names of .so files
-	// e.g. moduleNames["libprotobuf-cpp-full-3.9.1.so"] = "libprotobuf-cpp-full"
-	moduleNames := make(map[string]string)
-
-	var headers android.Paths
-
-	// installVndkSnapshotLib copies built .so file from the module.
-	// Also, if the build artifacts is on, write a json file which contains all exported flags
-	// with FlagExporterInfo.
-	installVndkSnapshotLib := func(m *Module, vndkType string) (android.Paths, bool) {
-		var ret android.Paths
-
-		targetArch := "arch-" + m.Target().Arch.ArchType.String()
-		if m.Target().Arch.ArchVariant != "" {
-			targetArch += "-" + m.Target().Arch.ArchVariant
-		}
-
-		libPath := m.outputFile.Path()
-		snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "shared", vndkType, libPath.Base())
-		ret = append(ret, snapshot.CopyFileRule(pctx, ctx, libPath, snapshotLibOut))
-
-		// json struct to export snapshot information
-		prop := struct {
-			MinSdkVersion       string   `json:",omitempty"`
-			LicenseKinds        []string `json:",omitempty"`
-			LicenseTexts        []string `json:",omitempty"`
-			ExportedDirs        []string `json:",omitempty"`
-			ExportedSystemDirs  []string `json:",omitempty"`
-			ExportedFlags       []string `json:",omitempty"`
-			RelativeInstallPath string   `json:",omitempty"`
-		}{}
-
-		prop.LicenseKinds = m.EffectiveLicenseKinds()
-		prop.LicenseTexts = m.EffectiveLicenseFiles().Strings()
-		prop.MinSdkVersion = m.MinSdkVersion()
-
-		if ctx.Config().VndkSnapshotBuildArtifacts() {
-			exportedInfo, _ := android.SingletonModuleProvider(ctx, m, FlagExporterInfoProvider)
-			prop.ExportedFlags = exportedInfo.Flags
-			prop.ExportedDirs = exportedInfo.IncludeDirs.Strings()
-			prop.ExportedSystemDirs = exportedInfo.SystemIncludeDirs.Strings()
-			prop.RelativeInstallPath = m.RelativeInstallPath()
-		}
-
-		propOut := snapshotLibOut + ".json"
-
-		j, err := json.Marshal(prop)
-		if err != nil {
-			ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
-			return nil, false
-		}
-		ret = append(ret, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
-
-		return ret, true
-	}
-
-	ctx.VisitAllModules(func(module android.Module) {
-		m, ok := module.(*Module)
-		if !ok || !m.Enabled() {
-			return
-		}
-
-		apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
-
-		vndkType, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo)
-		if !ok {
-			return
-		}
-
-		// For all snapshot candidates, the followings are captured.
-		//   - .so files
-		//   - notice files
-		//
-		// The followings are also captured if VNDK_SNAPSHOT_BUILD_ARTIFACTS.
-		//   - .json files containing exported flags
-		//   - exported headers from collectHeadersForSnapshot()
-		//
-		// Headers are deduplicated after visiting all modules.
-
-		// install .so files for appropriate modules.
-		// Also install .json files if VNDK_SNAPSHOT_BUILD_ARTIFACTS
-		libs, ok := installVndkSnapshotLib(m, vndkType)
-		if !ok {
-			return
-		}
-		snapshotOutputs = append(snapshotOutputs, libs...)
-
-		// These are for generating module_names.txt and module_paths.txt
-		stem := m.outputFile.Path().Base()
-		moduleNames[stem] = ctx.ModuleName(m)
-		modulePaths[stem] = ctx.ModuleDir(m)
-
-		for _, notice := range m.EffectiveLicenseFiles() {
-			if _, ok := noticeBuilt[notice.String()]; !ok {
-				noticeBuilt[notice.String()] = true
-				snapshotOutputs = append(snapshotOutputs, snapshot.CopyFileRule(
-					pctx, ctx, notice, filepath.Join(noticeDir, notice.String())))
-			}
-		}
-
-		if ctx.Config().VndkSnapshotBuildArtifacts() {
-			headers = append(headers, m.SnapshotHeaders()...)
-		}
-	})
-
-	// install all headers after removing duplicates
-	for _, header := range android.FirstUniquePaths(headers) {
-		snapshotOutputs = append(snapshotOutputs, snapshot.CopyFileRule(
-			pctx, ctx, header, filepath.Join(includeDir, header.String())))
-	}
-
-	// install *.libraries.txt except vndkcorevariant.libraries.txt
-	ctx.VisitAllModules(func(module android.Module) {
-		m, ok := module.(*vndkLibrariesTxt)
-		if !ok || !m.Enabled() || m.Name() == vndkUsingCoreVariantLibrariesTxt {
-			return
-		}
-		snapshotOutputs = append(snapshotOutputs, snapshot.CopyFileRule(
-			pctx, ctx, m.OutputFile(), filepath.Join(configsDir, m.Name())))
-	})
-
-	/*
-		module_paths.txt contains paths on which VNDK modules are defined.
-		e.g.,
-			libbase.so system/libbase
-			libc.so bionic/libc
-			...
-	*/
-	snapshotOutputs = append(snapshotOutputs, installMapListFileRule(ctx, modulePaths, filepath.Join(configsDir, "module_paths.txt")))
-
-	/*
-		module_names.txt contains names as which VNDK modules are defined,
-		because output filename and module name can be different with stem and suffix properties.
-
-		e.g.,
-			libcutils.so libcutils
-			libprotobuf-cpp-full-3.9.2.so libprotobuf-cpp-full
-			...
-	*/
-	snapshotOutputs = append(snapshotOutputs, installMapListFileRule(ctx, moduleNames, filepath.Join(configsDir, "module_names.txt")))
-
-	// All artifacts are ready. Sort them to normalize ninja and then zip.
-	sort.Slice(snapshotOutputs, func(i, j int) bool {
-		return snapshotOutputs[i].String() < snapshotOutputs[j].String()
-	})
-
-	zipPath := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+".zip")
-	zipRule := android.NewRuleBuilder(pctx, ctx)
-
-	// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
-	snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+"_list")
-	rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp")
-	zipRule.Command().
-		Text("tr").
-		FlagWithArg("-d ", "\\'").
-		FlagWithRspFileInputList("< ", rspFile, snapshotOutputs).
-		FlagWithOutput("> ", snapshotOutputList)
-
-	zipRule.Temporary(snapshotOutputList)
-
-	zipRule.Command().
-		BuiltTool("soong_zip").
-		FlagWithOutput("-o ", zipPath).
-		FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
-		FlagWithInput("-l ", snapshotOutputList)
-
-	zipRule.Build(zipPath.String(), "vndk snapshot "+zipPath.String())
-	zipRule.DeleteTemporaryFiles()
-	c.vndkSnapshotZipFile = android.OptionalPathForPath(zipPath)
-}
-
-func getVndkFileName(m *Module) (string, error) {
-	if library, ok := m.linker.(*libraryDecorator); ok {
-		return library.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
-	}
-	if prebuilt, ok := m.linker.(*prebuiltLibraryLinker); ok {
-		return prebuilt.libraryDecorator.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
-	}
-	return "", fmt.Errorf("VNDK library should have libraryDecorator or prebuiltLibraryLinker as linker: %T", m.linker)
-}
-
-func (c *vndkSnapshotSingleton) buildVndkLibrariesTxtFiles(ctx android.SingletonContext) {
-	// Build list of vndk libs as merged & tagged & filter-out(libclang_rt):
-	// Since each target have different set of libclang_rt.* files,
-	// keep the common set of files in vndk.libraries.txt
-	_, llndk := vndkModuleListRemover(llndkLibraries, "libclang_rt.")(ctx)
-	_, vndkcore := vndkModuleListRemover(vndkCoreLibraries, "libclang_rt.")(ctx)
-	_, vndksp := vndkSPLibraries(ctx)
-	_, vndkprivate := vndkPrivateLibraries(ctx)
-	_, vndkproduct := vndkModuleListRemover(vndkProductLibraries, "libclang_rt.")(ctx)
-	var merged []string
-	merged = append(merged, addPrefix(llndk, "LLNDK: ")...)
-	merged = append(merged, addPrefix(vndksp, "VNDK-SP: ")...)
-	merged = append(merged, addPrefix(vndkcore, "VNDK-core: ")...)
-	merged = append(merged, addPrefix(vndkprivate, "VNDK-private: ")...)
-	merged = append(merged, addPrefix(vndkproduct, "VNDK-product: ")...)
-	c.vndkLibrariesFile = android.PathForOutput(ctx, "vndk", "vndk.libraries.txt")
-	android.WriteFileRule(ctx, c.vndkLibrariesFile, strings.Join(merged, "\n"))
-}
-
-func (c *vndkSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
-	// Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to avoid installing libraries on /system if
-	// they been moved to an apex.
-	movedToApexLlndkLibraries := make(map[string]bool)
-	ctx.VisitAllModules(func(module android.Module) {
-		if library := moduleLibraryInterface(module); library != nil && library.hasLLNDKStubs() {
-			// Skip bionic libs, they are handled in different manner
-			name := library.implementationModuleName(module.(*Module).BaseModuleName())
-			if module.(android.ApexModule).DirectlyInAnyApex() && !isBionic(name) {
-				movedToApexLlndkLibraries[name] = true
-			}
-		}
-	})
-
-	ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES",
-		strings.Join(android.SortedKeys(movedToApexLlndkLibraries), " "))
-
-	ctx.Strict("VNDK_LIBRARIES_FILE", c.vndkLibrariesFile.String())
-	ctx.Strict("SOONG_VNDK_SNAPSHOT_ZIP", c.vndkSnapshotZipFile.String())
-}
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 43030b8..e7dff40 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -49,6 +49,8 @@
 //	    },
 //	}
 type vndkPrebuiltProperties struct {
+	VndkProperties
+
 	// VNDK snapshot version.
 	Version *string
 
@@ -131,16 +133,6 @@
 
 func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
-	platformVndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
-	if platformVndkVersion != "" {
-		platformVndkApiLevel := android.ApiLevelOrPanic(ctx, platformVndkVersion)
-		if platformVndkApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(ctx, p.Version())) {
-			// This prebuilt VNDK module is not required for the current build
-			ctx.Module().HideFromMake()
-			return nil
-		}
-	}
-
 	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		ctx.Module().HideFromMake()
 		return nil
@@ -169,11 +161,6 @@
 
 		p.androidMkSuffix = p.NameSuffix()
 
-		vndkVersion := ctx.DeviceConfig().VndkVersion()
-		if vndkVersion == p.Version() {
-			p.androidMkSuffix = ""
-		}
-
 		android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
 			SharedLibrary: in,
 			Target:        ctx.Target(),
@@ -283,3 +270,14 @@
 func init() {
 	android.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 }
+
+func IsForVndkApex(mctx android.BottomUpMutatorContext, m *Module) bool {
+	if !m.Enabled(mctx) {
+		return true
+	}
+
+	if p, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok {
+		return p.MatchesWithDevice(mctx.DeviceConfig()) && Bool(p.properties.Vndk.Enabled)
+	}
+	return false
+}
diff --git a/cmd/release_config/build_flag/Android.bp b/cmd/release_config/build_flag/Android.bp
new file mode 100644
index 0000000..0f10c91
--- /dev/null
+++ b/cmd/release_config/build_flag/Android.bp
@@ -0,0 +1,32 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "build-flag",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-proto",
+        "soong-cmd-release_config-lib",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
+
+bootstrap_go_package {
+    name: "soong-cmd-release_config-build_flag",
+    pkgPath: "android/soong/cmd/release_config/build_flag",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-proto",
+        "soong-cmd-release_config-lib",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
diff --git a/cmd/release_config/build_flag/main.go b/cmd/release_config/build_flag/main.go
new file mode 100644
index 0000000..5d183ee
--- /dev/null
+++ b/cmd/release_config/build_flag/main.go
@@ -0,0 +1,394 @@
+package main
+
+import (
+	"cmp"
+	"flag"
+	"fmt"
+	"os"
+	"path/filepath"
+	"slices"
+	"strings"
+
+	rc_lib "android/soong/cmd/release_config/release_config_lib"
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+
+	"google.golang.org/protobuf/proto"
+)
+
+type Flags struct {
+	// The path to the top of the workspace.  Default: ".".
+	top string
+
+	// Pathlist of release config map textproto files.
+	// If not specified, then the value is (if present):
+	// - build/release/release_config_map.textproto
+	// - vendor/google_shared/build/release/release_config_map.textproto
+	// - vendor/google/release/release_config_map.textproto
+	//
+	// Additionally, any maps specified in the environment variable
+	// `PRODUCT_RELEASE_CONFIG_MAPS` are used.
+	maps rc_lib.StringList
+
+	// Output directory (relative to `top`).
+	outDir string
+
+	// Which $TARGET_RELEASE(s) should we use.  Some commands will only
+	// accept one value, others also accept `--release --all`.
+	targetReleases rc_lib.StringList
+
+	// Disable warning messages
+	quiet bool
+
+	// Show all release configs
+	allReleases bool
+
+	// Call get_build_var PRODUCT_RELEASE_CONFIG_MAPS to get the
+	// product-specific map directories.
+	useGetBuildVar bool
+
+	// Panic on errors.
+	debug bool
+
+	// Allow missing release config.
+	// If true, and we cannot find the named release config, values for
+	// `trunk_staging` will be used.
+	allowMissing bool
+}
+
+type CommandFunc func(*rc_lib.ReleaseConfigs, Flags, string, []string) error
+
+var commandMap map[string]CommandFunc = map[string]CommandFunc{
+	"get":   GetCommand,
+	"set":   SetCommand,
+	"trace": GetCommand, // Also handled by GetCommand
+}
+
+// Find the top of the release config contribution directory.
+// Returns the parent of the flag_declarations and flag_values directories.
+func GetMapDir(path string) (string, error) {
+	for p := path; p != "."; p = filepath.Dir(p) {
+		switch filepath.Base(p) {
+		case "flag_declarations":
+			return filepath.Dir(p), nil
+		case "flag_values":
+			return filepath.Dir(p), nil
+		}
+	}
+	return "", fmt.Errorf("Could not determine directory from %s", path)
+}
+
+func MarshalFlagDefaultValue(config *rc_lib.ReleaseConfig, name string) (ret string, err error) {
+	fa, ok := config.FlagArtifacts[name]
+	if !ok {
+		return "", fmt.Errorf("%s not found in %s", name, config.Name)
+	}
+	return rc_lib.MarshalValue(fa.Traces[0].Value), nil
+}
+
+func MarshalFlagValue(config *rc_lib.ReleaseConfig, name string) (ret string, err error) {
+	fa, ok := config.FlagArtifacts[name]
+	if !ok {
+		return "", fmt.Errorf("%s not found in %s", name, config.Name)
+	}
+	return rc_lib.MarshalValue(fa.Value), nil
+}
+
+// Returns a list of ReleaseConfig objects for which to process flags.
+func GetReleaseArgs(configs *rc_lib.ReleaseConfigs, commonFlags Flags) ([]*rc_lib.ReleaseConfig, error) {
+	var all bool
+	relFlags := flag.NewFlagSet("releaseFlags", flag.ExitOnError)
+	relFlags.BoolVar(&all, "all", false, "Display all releases")
+	relFlags.Parse(commonFlags.targetReleases)
+	var ret []*rc_lib.ReleaseConfig
+	if all || commonFlags.allReleases {
+		sortMap := map[string]int{
+			"trunk_staging": 0,
+			"trunk_food":    10,
+			"trunk":         20,
+			// Anything not listed above, uses this for key 1 in the sort.
+			"-default": 100,
+		}
+
+		for _, config := range configs.ReleaseConfigs {
+			ret = append(ret, config)
+		}
+		slices.SortFunc(ret, func(a, b *rc_lib.ReleaseConfig) int {
+			mapValue := func(v *rc_lib.ReleaseConfig) int {
+				if v, ok := sortMap[v.Name]; ok {
+					return v
+				}
+				return sortMap["-default"]
+			}
+			if n := cmp.Compare(mapValue(a), mapValue(b)); n != 0 {
+				return n
+			}
+			return cmp.Compare(a.Name, b.Name)
+		})
+		return ret, nil
+	}
+	for _, arg := range relFlags.Args() {
+		// Return releases in the order that they were given.
+		config, err := configs.GetReleaseConfig(arg)
+		if err != nil {
+			return nil, err
+		}
+		ret = append(ret, config)
+	}
+	return ret, nil
+}
+
+func GetCommand(configs *rc_lib.ReleaseConfigs, commonFlags Flags, cmd string, args []string) error {
+	isTrace := cmd == "trace"
+	isSet := cmd == "set"
+
+	var all bool
+	getFlags := flag.NewFlagSet("get", flag.ExitOnError)
+	getFlags.BoolVar(&all, "all", false, "Display all flags")
+	getFlags.Parse(args)
+	args = getFlags.Args()
+
+	if isSet {
+		commonFlags.allReleases = true
+	}
+	releaseConfigList, err := GetReleaseArgs(configs, commonFlags)
+	if err != nil {
+		return err
+	}
+	if isTrace && len(releaseConfigList) > 1 {
+		return fmt.Errorf("trace command only allows one --release argument.  Got: %s", strings.Join(commonFlags.targetReleases, " "))
+	}
+
+	if all {
+		args = []string{}
+		for _, fa := range configs.FlagArtifacts {
+			args = append(args, *fa.FlagDeclaration.Name)
+		}
+		slices.Sort(args)
+	}
+
+	var maxVariableNameLen, maxReleaseNameLen int
+	var releaseNameFormat, variableNameFormat string
+	valueFormat := "%s"
+	showReleaseName := len(releaseConfigList) > 1
+	showVariableName := len(args) > 1
+	if showVariableName {
+		for _, arg := range args {
+			maxVariableNameLen = max(len(arg), maxVariableNameLen)
+		}
+		variableNameFormat = fmt.Sprintf("%%-%ds ", maxVariableNameLen)
+		valueFormat = "'%s'"
+	}
+	if showReleaseName {
+		for _, config := range releaseConfigList {
+			maxReleaseNameLen = max(len(config.Name), maxReleaseNameLen)
+		}
+		releaseNameFormat = fmt.Sprintf("%%-%ds ", maxReleaseNameLen)
+		valueFormat = "'%s'"
+	}
+
+	outputOneLine := func(variable, release, value, valueFormat string) {
+		var outStr string
+		if showVariableName {
+			outStr += fmt.Sprintf(variableNameFormat, variable)
+		}
+		if showReleaseName {
+			outStr += fmt.Sprintf(releaseNameFormat, release)
+		}
+		outStr += fmt.Sprintf(valueFormat, value)
+		fmt.Println(outStr)
+	}
+
+	for _, arg := range args {
+		if _, ok := configs.FlagArtifacts[arg]; !ok {
+			return fmt.Errorf("%s is not a defined build flag", arg)
+		}
+	}
+
+	for _, arg := range args {
+		for _, config := range releaseConfigList {
+			if isSet {
+				// If this is from the set command, format the output as:
+				// <default>           ""
+				// trunk_staging       ""
+				// trunk               ""
+				//
+				// ap1a                ""
+				// ...
+				switch {
+				case config.Name == "trunk_staging":
+					defaultValue, err := MarshalFlagDefaultValue(config, arg)
+					if err != nil {
+						return err
+					}
+					outputOneLine(arg, "<default>", defaultValue, valueFormat)
+				case config.AconfigFlagsOnly:
+					continue
+				case config.Name == "trunk":
+					fmt.Println()
+				}
+			}
+			val, err := MarshalFlagValue(config, arg)
+			if err == nil {
+				outputOneLine(arg, config.Name, val, valueFormat)
+			} else {
+				outputOneLine(arg, config.Name, "REDACTED", "%s")
+			}
+			if err == nil && isTrace {
+				for _, trace := range config.FlagArtifacts[arg].Traces {
+					fmt.Printf("  => \"%s\" in %s\n", rc_lib.MarshalValue(trace.Value), *trace.Source)
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func SetCommand(configs *rc_lib.ReleaseConfigs, commonFlags Flags, cmd string, args []string) error {
+	var valueDir string
+	var redacted bool
+	var value string
+	if len(commonFlags.targetReleases) > 1 {
+		return fmt.Errorf("set command only allows one --release argument.  Got: %s", strings.Join(commonFlags.targetReleases, " "))
+	}
+	targetRelease := commonFlags.targetReleases[0]
+
+	setFlags := flag.NewFlagSet("set", flag.ExitOnError)
+	setFlags.StringVar(&valueDir, "dir", "", "Directory in which to place the value")
+	setFlags.BoolVar(&redacted, "redacted", false, "Whether the flag should be redacted")
+	setFlags.Parse(args)
+	setArgs := setFlags.Args()
+	if redacted {
+		if len(setArgs) != 1 {
+			return fmt.Errorf("set command expected '--redacted=true flag', got: --redacted=true %s", strings.Join(setArgs, " "))
+		}
+	} else if len(setArgs) != 2 {
+		return fmt.Errorf("set command expected flag and value, got: %s", strings.Join(setArgs, " "))
+	}
+	name := setArgs[0]
+	if !redacted {
+		value = setArgs[1]
+	}
+	release, err := configs.GetReleaseConfig(targetRelease)
+	targetRelease = release.Name
+	if err != nil {
+		return err
+	}
+	if release.AconfigFlagsOnly {
+		return fmt.Errorf("%s does not allow build flag overrides", targetRelease)
+	}
+	flagArtifact, ok := release.FlagArtifacts[name]
+	if !ok {
+		return fmt.Errorf("Unknown build flag %s", name)
+	}
+	if valueDir == "" {
+		mapDir, err := configs.GetFlagValueDirectory(release, flagArtifact)
+		if err != nil {
+			return err
+		}
+		valueDir = mapDir
+	}
+
+	var updatedFiles []string
+	rcPath := filepath.Join(valueDir, "release_configs", fmt.Sprintf("%s.textproto", targetRelease))
+	// Create the release config declaration only if necessary.
+	if _, err = os.Stat(rcPath); err != nil {
+		if err = os.MkdirAll(filepath.Dir(rcPath), 0775); err != nil {
+			return err
+		}
+		rcValue := &rc_proto.ReleaseConfig{
+			Name: proto.String(targetRelease),
+		}
+		err = rc_lib.WriteMessage(rcPath, rcValue)
+		if err != nil {
+			return err
+		}
+		updatedFiles = append(updatedFiles, rcPath)
+	}
+
+	flagValue := &rc_proto.FlagValue{
+		Name: proto.String(name),
+	}
+	if redacted {
+		flagValue.Redacted = proto.Bool(true)
+	} else {
+		flagValue.Value = rc_lib.UnmarshalValue(value)
+	}
+	flagPath := filepath.Join(valueDir, "flag_values", targetRelease, fmt.Sprintf("%s.textproto", name))
+	err = rc_lib.WriteMessage(flagPath, flagValue)
+	if err != nil {
+		return err
+	}
+
+	// Reload the release configs.
+	configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, commonFlags.targetReleases[0], commonFlags.useGetBuildVar, commonFlags.allowMissing)
+	if err != nil {
+		return err
+	}
+	err = GetCommand(configs, commonFlags, cmd, []string{name})
+	if err != nil {
+		return err
+	}
+	updatedFiles = append(updatedFiles, flagPath)
+	fmt.Printf("Added/Updated: %s\n", strings.Join(updatedFiles, " "))
+	return nil
+}
+
+func main() {
+	var commonFlags Flags
+	var configs *rc_lib.ReleaseConfigs
+	topDir, err := rc_lib.GetTopDir()
+
+	// Handle the common arguments
+	flag.StringVar(&commonFlags.top, "top", topDir, "path to top of workspace")
+	flag.BoolVar(&commonFlags.quiet, "quiet", false, "disable warning messages")
+	flag.Var(&commonFlags.maps, "map", "path to a release_config_map.textproto. may be repeated")
+	flag.StringVar(&commonFlags.outDir, "out-dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
+	flag.Var(&commonFlags.targetReleases, "release", "TARGET_RELEASE for this build")
+	flag.BoolVar(&commonFlags.allowMissing, "allow-missing", false, "Use trunk_staging values if release not found")
+	flag.BoolVar(&commonFlags.allReleases, "all-releases", false, "operate on all releases. (Ignored for set command)")
+	flag.BoolVar(&commonFlags.useGetBuildVar, "use-get-build-var", true, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS to get needed maps")
+	flag.BoolVar(&commonFlags.debug, "debug", false, "turn on debugging output for errors")
+	flag.Parse()
+
+	errorExit := func(err error) {
+		if commonFlags.debug {
+			panic(err)
+		}
+		fmt.Fprintf(os.Stderr, "%s\n", err)
+		os.Exit(1)
+	}
+
+	if commonFlags.quiet {
+		rc_lib.DisableWarnings()
+	}
+
+	if len(commonFlags.targetReleases) == 0 {
+		release, ok := os.LookupEnv("TARGET_RELEASE")
+		if ok {
+			commonFlags.targetReleases = rc_lib.StringList{release}
+		} else {
+			commonFlags.targetReleases = rc_lib.StringList{"trunk_staging"}
+		}
+	}
+
+	if err = os.Chdir(commonFlags.top); err != nil {
+		errorExit(err)
+	}
+
+	// Get the current state of flagging.
+	relName := commonFlags.targetReleases[0]
+	if relName == "--all" || relName == "-all" {
+		commonFlags.allReleases = true
+	}
+	configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, relName, commonFlags.useGetBuildVar, commonFlags.allowMissing)
+	if err != nil {
+		errorExit(err)
+	}
+
+	if cmd, ok := commandMap[flag.Arg(0)]; ok {
+		args := flag.Args()
+		if err = cmd(configs, commonFlags, args[0], args[1:]); err != nil {
+			errorExit(err)
+		}
+	}
+}
diff --git a/cmd/release_config/build_flag_declarations/Android.bp b/cmd/release_config/build_flag_declarations/Android.bp
new file mode 100644
index 0000000..e4f999f
--- /dev/null
+++ b/cmd/release_config/build_flag_declarations/Android.bp
@@ -0,0 +1,32 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "build-flag-declarations",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-proto",
+        "soong-cmd-release_config-lib",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
+
+bootstrap_go_package {
+    name: "soong-cmd-release_config-build_flag_declarations",
+    pkgPath: "android/soong/cmd/release_config/build_flag_declarations",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-proto",
+        "soong-cmd-release_config-lib",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
diff --git a/cmd/release_config/build_flag_declarations/main.go b/cmd/release_config/build_flag_declarations/main.go
new file mode 100644
index 0000000..cc286b6
--- /dev/null
+++ b/cmd/release_config/build_flag_declarations/main.go
@@ -0,0 +1,81 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	rc_lib "android/soong/cmd/release_config/release_config_lib"
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+)
+
+type Flags struct {
+	// The path to the top of the workspace.  Default: ".".
+	top string
+
+	// Output file.
+	output string
+
+	// Format for output file
+	format string
+
+	// List of flag_declaration files to add.
+	decls rc_lib.StringList
+
+	// List of flag_artifacts files to merge.
+	intermediates rc_lib.StringList
+
+	// Disable warning messages
+	quiet bool
+
+	// Panic on errors.
+	debug bool
+}
+
+func main() {
+	var flags Flags
+	topDir, err := rc_lib.GetTopDir()
+
+	// Handle the common arguments
+	flag.StringVar(&flags.top, "top", topDir, "path to top of workspace")
+	flag.Var(&flags.decls, "decl", "path to a flag_declaration file. May be repeated")
+	flag.Var(&flags.intermediates, "intermediate", "path to a flag_artifacts file (output from a prior run). May be repeated")
+	flag.StringVar(&flags.format, "format", "pb", "output file format")
+	flag.StringVar(&flags.output, "output", "build_flags.pb", "output file")
+	flag.BoolVar(&flags.debug, "debug", false, "turn on debugging output for errors")
+	flag.BoolVar(&flags.quiet, "quiet", false, "disable warning messages")
+	flag.Parse()
+
+	errorExit := func(err error) {
+		if flags.debug {
+			panic(err)
+		}
+		fmt.Fprintf(os.Stderr, "%s\n", err)
+		os.Exit(1)
+	}
+
+	if flags.quiet {
+		rc_lib.DisableWarnings()
+	}
+
+	if err = os.Chdir(flags.top); err != nil {
+		errorExit(err)
+	}
+
+	flagArtifacts := rc_lib.FlagArtifactsFactory("")
+	intermediates := []*rc_proto.FlagDeclarationArtifacts{}
+	for _, intermediate := range flags.intermediates {
+		fda := rc_lib.FlagDeclarationArtifactsFactory(intermediate)
+		intermediates = append(intermediates, fda)
+	}
+	for _, decl := range flags.decls {
+		fa := rc_lib.FlagArtifactFactory(decl)
+		(*flagArtifacts)[*fa.FlagDeclaration.Name] = fa
+	}
+
+	message := flagArtifacts.GenerateFlagDeclarationArtifacts(intermediates)
+	err = rc_lib.WriteFormattedMessage(flags.output, flags.format, message)
+	if err != nil {
+		errorExit(err)
+	}
+}
diff --git a/cmd/release_config/crunch_flags/Android.bp b/cmd/release_config/crunch_flags/Android.bp
new file mode 100644
index 0000000..89c9591
--- /dev/null
+++ b/cmd/release_config/crunch_flags/Android.bp
@@ -0,0 +1,32 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "crunch-flags",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-lib",
+        "soong-cmd-release_config-proto",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
+
+bootstrap_go_package {
+    name: "soong-cmd-release_config-crunch_flags",
+    pkgPath: "android/soong/cmd/release_config/crunch_flags",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-lib",
+        "soong-cmd-release_config-proto",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
diff --git a/cmd/release_config/crunch_flags/main.go b/cmd/release_config/crunch_flags/main.go
new file mode 100644
index 0000000..cd39ffd
--- /dev/null
+++ b/cmd/release_config/crunch_flags/main.go
@@ -0,0 +1,402 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+
+	rc_lib "android/soong/cmd/release_config/release_config_lib"
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/proto"
+)
+
+var (
+	// When a flag declaration has an initial value that is a string, the default workflow is PREBUILT.
+	// If the flag name starts with any of prefixes in manualFlagNamePrefixes, it is MANUAL.
+	manualFlagNamePrefixes []string = []string{
+		"RELEASE_ACONFIG_",
+		"RELEASE_PLATFORM_",
+		"RELEASE_BUILD_FLAGS_",
+	}
+
+	// Set `aconfig_flags_only: true` in these release configs.
+	aconfigFlagsOnlyConfigs map[string]bool = map[string]bool{
+		"trunk_food": true,
+	}
+
+	// Default namespace value.  This is intentionally invalid.
+	defaultFlagNamespace string = "android_UNKNOWN"
+
+	// What is the current name for "next".
+	nextName string = "ap3a"
+)
+
+func RenameNext(name string) string {
+	if name == "next" {
+		return nextName
+	}
+	return name
+}
+
+func WriteFile(path string, message proto.Message) error {
+	data, err := prototext.MarshalOptions{Multiline: true}.Marshal(message)
+	if err != nil {
+		return err
+	}
+
+	err = os.MkdirAll(filepath.Dir(path), 0775)
+	if err != nil {
+		return err
+	}
+	return os.WriteFile(path, data, 0644)
+}
+
+func WalkValueFiles(dir string, Func fs.WalkDirFunc) error {
+	valPath := filepath.Join(dir, "build_config")
+	if _, err := os.Stat(valPath); err != nil {
+		fmt.Printf("%s not found, ignoring.\n", valPath)
+		return nil
+	}
+
+	return filepath.WalkDir(valPath, func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			return err
+		}
+		if strings.HasSuffix(d.Name(), ".scl") && d.Type().IsRegular() {
+			return Func(path, d, err)
+		}
+		return nil
+	})
+}
+
+func ProcessBuildFlags(dir string, namespaceMap map[string]string) error {
+	var rootAconfigModule string
+
+	path := filepath.Join(dir, "build_flags.scl")
+	if _, err := os.Stat(path); err != nil {
+		fmt.Printf("%s not found, ignoring.\n", path)
+		return nil
+	} else {
+		fmt.Printf("Processing %s\n", path)
+	}
+	commentRegexp, err := regexp.Compile("^[[:space:]]*#(?<comment>.+)")
+	if err != nil {
+		return err
+	}
+	declRegexp, err := regexp.Compile("^[[:space:]]*flag.\"(?<name>[A-Z_0-9]+)\",[[:space:]]*(?<container>[_A-Z]*),[[:space:]]*(?<value>(\"[^\"]*\"|[^\",)]*))")
+	if err != nil {
+		return err
+	}
+	declIn, err := os.ReadFile(path)
+	if err != nil {
+		return err
+	}
+	lines := strings.Split(string(declIn), "\n")
+	var description string
+	for _, line := range lines {
+		if comment := commentRegexp.FindStringSubmatch(commentRegexp.FindString(line)); comment != nil {
+			// Description is the text from any contiguous series of lines before a `flag()` call.
+			descLine := strings.TrimSpace(comment[commentRegexp.SubexpIndex("comment")])
+			if !strings.HasPrefix(descLine, "keep-sorted") {
+				description += fmt.Sprintf(" %s", descLine)
+			}
+			continue
+		}
+		matches := declRegexp.FindStringSubmatch(declRegexp.FindString(line))
+		if matches == nil {
+			// The line is neither a comment nor a `flag()` call.
+			// Discard any description we have gathered and process the next line.
+			description = ""
+			continue
+		}
+		declName := matches[declRegexp.SubexpIndex("name")]
+		declValue := matches[declRegexp.SubexpIndex("value")]
+		description = strings.TrimSpace(description)
+		containers := []string{strings.ToLower(matches[declRegexp.SubexpIndex("container")])}
+		if containers[0] == "all" {
+			containers = []string{"product", "system", "system_ext", "vendor"}
+		}
+		var namespace string
+		var ok bool
+		if namespace, ok = namespaceMap[declName]; !ok {
+			namespace = defaultFlagNamespace
+		}
+		flagDeclaration := &rc_proto.FlagDeclaration{
+			Name:        proto.String(declName),
+			Namespace:   proto.String(namespace),
+			Description: proto.String(description),
+			Containers:  containers,
+		}
+		description = ""
+		// Most build flags are `workflow: PREBUILT`.
+		workflow := rc_proto.Workflow(rc_proto.Workflow_PREBUILT)
+		switch {
+		case declName == "RELEASE_ACONFIG_VALUE_SETS":
+			if strings.HasPrefix(declValue, "\"") {
+				rootAconfigModule = declValue[1 : len(declValue)-1]
+			}
+			continue
+		case strings.HasPrefix(declValue, "\""):
+			// String values mean that the flag workflow is (most likely) either MANUAL or PREBUILT.
+			declValue = declValue[1 : len(declValue)-1]
+			flagDeclaration.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{declValue}}
+			for _, prefix := range manualFlagNamePrefixes {
+				if strings.HasPrefix(declName, prefix) {
+					workflow = rc_proto.Workflow(rc_proto.Workflow_MANUAL)
+					break
+				}
+			}
+		case declValue == "False" || declValue == "True":
+			// Boolean values are LAUNCH flags.
+			flagDeclaration.Value = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{declValue == "True"}}
+			workflow = rc_proto.Workflow(rc_proto.Workflow_LAUNCH)
+		case declValue == "None":
+			// Use PREBUILT workflow with no initial value.
+		default:
+			fmt.Printf("%s: Unexpected value %s=%s\n", path, declName, declValue)
+		}
+		flagDeclaration.Workflow = &workflow
+		if flagDeclaration != nil {
+			declPath := filepath.Join(dir, "flag_declarations", fmt.Sprintf("%s.textproto", declName))
+			err := WriteFile(declPath, flagDeclaration)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	if rootAconfigModule != "" {
+		rootProto := &rc_proto.ReleaseConfig{
+			Name:             proto.String("root"),
+			AconfigValueSets: []string{rootAconfigModule},
+		}
+		return WriteFile(filepath.Join(dir, "release_configs", "root.textproto"), rootProto)
+	}
+	return nil
+}
+
+func ProcessBuildConfigs(dir, name string, paths []string, releaseProto *rc_proto.ReleaseConfig) error {
+	valRegexp, err := regexp.Compile("[[:space:]]+value.\"(?<name>[A-Z_0-9]+)\",[[:space:]]*(?<value>(\"[^\"]*\"|[^\",)]*))")
+	if err != nil {
+		return err
+	}
+	for _, path := range paths {
+		fmt.Printf("Processing %s\n", path)
+		valIn, err := os.ReadFile(path)
+		if err != nil {
+			fmt.Printf("%s: error: %v\n", path, err)
+			return err
+		}
+		vals := valRegexp.FindAllString(string(valIn), -1)
+		for _, val := range vals {
+			matches := valRegexp.FindStringSubmatch(val)
+			valValue := matches[valRegexp.SubexpIndex("value")]
+			valName := matches[valRegexp.SubexpIndex("name")]
+			flagValue := &rc_proto.FlagValue{
+				Name: proto.String(valName),
+			}
+			switch {
+			case valName == "RELEASE_ACONFIG_VALUE_SETS":
+				flagValue = nil
+				if releaseProto.AconfigValueSets == nil {
+					releaseProto.AconfigValueSets = []string{}
+				}
+				releaseProto.AconfigValueSets = append(releaseProto.AconfigValueSets, valValue[1:len(valValue)-1])
+			case strings.HasPrefix(valValue, "\""):
+				valValue = valValue[1 : len(valValue)-1]
+				flagValue.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{valValue}}
+			case valValue == "None":
+				// nothing to do here.
+			case valValue == "True":
+				flagValue.Value = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{true}}
+			case valValue == "False":
+				flagValue.Value = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{false}}
+			default:
+				fmt.Printf("%s: Unexpected value %s=%s\n", path, valName, valValue)
+			}
+			if flagValue != nil {
+				if releaseProto.GetAconfigFlagsOnly() {
+					return fmt.Errorf("%s does not allow build flag overrides", RenameNext(name))
+				}
+				valPath := filepath.Join(dir, "flag_values", RenameNext(name), fmt.Sprintf("%s.textproto", valName))
+				err := WriteFile(valPath, flagValue)
+				if err != nil {
+					return err
+				}
+			}
+		}
+	}
+	return err
+}
+
+var (
+	allContainers = func() []string {
+		return []string{"product", "system", "system_ext", "vendor"}
+	}()
+)
+
+func ProcessReleaseConfigMap(dir string, descriptionMap map[string]string) error {
+	path := filepath.Join(dir, "release_config_map.mk")
+	if _, err := os.Stat(path); err != nil {
+		fmt.Printf("%s not found, ignoring.\n", path)
+		return nil
+	} else {
+		fmt.Printf("Processing %s\n", path)
+	}
+	configRegexp, err := regexp.Compile("^..call[[:space:]]+declare-release-config,[[:space:]]+(?<name>[_a-z0-9A-Z]+),[[:space:]]+(?<files>[^,]*)(,[[:space:]]*(?<inherits>.*)|[[:space:]]*)[)]$")
+	if err != nil {
+		return err
+	}
+	aliasRegexp, err := regexp.Compile("^..call[[:space:]]+alias-release-config,[[:space:]]+(?<name>[_a-z0-9A-Z]+),[[:space:]]+(?<target>[_a-z0-9A-Z]+)")
+	if err != nil {
+		return err
+	}
+
+	mapIn, err := os.ReadFile(path)
+	if err != nil {
+		return err
+	}
+	cleanDir := strings.TrimLeft(dir, "../")
+	var defaultContainers []string
+	switch {
+	case strings.HasPrefix(cleanDir, "build/") || cleanDir == "vendor/google_shared/build":
+		defaultContainers = allContainers
+	case cleanDir == "vendor/google/release":
+		defaultContainers = allContainers
+	default:
+		defaultContainers = []string{"vendor"}
+	}
+	releaseConfigMap := &rc_proto.ReleaseConfigMap{DefaultContainers: defaultContainers}
+	// If we find a description for the directory, include it.
+	if description, ok := descriptionMap[cleanDir]; ok {
+		releaseConfigMap.Description = proto.String(description)
+	}
+	lines := strings.Split(string(mapIn), "\n")
+	for _, line := range lines {
+		alias := aliasRegexp.FindStringSubmatch(aliasRegexp.FindString(line))
+		if alias != nil {
+			fmt.Printf("processing alias %s\n", line)
+			name := alias[aliasRegexp.SubexpIndex("name")]
+			target := alias[aliasRegexp.SubexpIndex("target")]
+			if target == "next" {
+				if RenameNext(target) != name {
+					return fmt.Errorf("Unexpected name for next (%s)", RenameNext(target))
+				}
+				target, name = name, target
+			}
+			releaseConfigMap.Aliases = append(releaseConfigMap.Aliases,
+				&rc_proto.ReleaseAlias{
+					Name:   proto.String(name),
+					Target: proto.String(target),
+				})
+		}
+		config := configRegexp.FindStringSubmatch(configRegexp.FindString(line))
+		if config == nil {
+			continue
+		}
+		name := config[configRegexp.SubexpIndex("name")]
+		releaseConfig := &rc_proto.ReleaseConfig{
+			Name: proto.String(RenameNext(name)),
+		}
+		if aconfigFlagsOnlyConfigs[name] {
+			releaseConfig.AconfigFlagsOnly = proto.Bool(true)
+		}
+		configFiles := config[configRegexp.SubexpIndex("files")]
+		files := strings.Split(strings.ReplaceAll(configFiles, "$(local_dir)", dir+"/"), " ")
+		configInherits := config[configRegexp.SubexpIndex("inherits")]
+		if len(configInherits) > 0 {
+			releaseConfig.Inherits = strings.Split(configInherits, " ")
+		}
+		err := ProcessBuildConfigs(dir, name, files, releaseConfig)
+		if err != nil {
+			return err
+		}
+
+		releasePath := filepath.Join(dir, "release_configs", fmt.Sprintf("%s.textproto", RenameNext(name)))
+		err = WriteFile(releasePath, releaseConfig)
+		if err != nil {
+			return err
+		}
+	}
+	return WriteFile(filepath.Join(dir, "release_config_map.textproto"), releaseConfigMap)
+}
+
+func main() {
+	var err error
+	var top string
+	var dirs rc_lib.StringList
+	var namespacesFile string
+	var descriptionsFile string
+	var debug bool
+	defaultTopDir, err := rc_lib.GetTopDir()
+
+	flag.StringVar(&top, "top", defaultTopDir, "path to top of workspace")
+	flag.Var(&dirs, "dir", "directory to process, relative to the top of the workspace")
+	flag.StringVar(&namespacesFile, "namespaces", "", "location of file with 'flag_name namespace' information")
+	flag.StringVar(&descriptionsFile, "descriptions", "", "location of file with 'directory description' information")
+	flag.BoolVar(&debug, "debug", false, "turn on debugging output for errors")
+	flag.Parse()
+
+	errorExit := func(err error) {
+		if debug {
+			panic(err)
+		}
+		fmt.Fprintf(os.Stderr, "%s\n", err)
+		os.Exit(1)
+	}
+
+	if err = os.Chdir(top); err != nil {
+		errorExit(err)
+	}
+	if len(dirs) == 0 {
+		dirs = rc_lib.StringList{"build/release", "vendor/google_shared/build/release", "vendor/google/release"}
+	}
+
+	namespaceMap := make(map[string]string)
+	if namespacesFile != "" {
+		data, err := os.ReadFile(namespacesFile)
+		if err != nil {
+			errorExit(err)
+		}
+		for idx, line := range strings.Split(string(data), "\n") {
+			fields := strings.Split(line, " ")
+			if len(fields) > 2 {
+				errorExit(fmt.Errorf("line %d: too many fields: %s", idx, line))
+			}
+			namespaceMap[fields[0]] = fields[1]
+		}
+
+	}
+
+	descriptionMap := make(map[string]string)
+	descriptionMap["build/release"] = "Published open-source flags and declarations"
+	if descriptionsFile != "" {
+		data, err := os.ReadFile(descriptionsFile)
+		if err != nil {
+			errorExit(err)
+		}
+		for _, line := range strings.Split(string(data), "\n") {
+			if strings.TrimSpace(line) != "" {
+				fields := strings.SplitN(line, " ", 2)
+				descriptionMap[fields[0]] = fields[1]
+			}
+		}
+
+	}
+
+	for _, dir := range dirs {
+		err = ProcessBuildFlags(dir, namespaceMap)
+		if err != nil {
+			errorExit(err)
+		}
+
+		err = ProcessReleaseConfigMap(dir, descriptionMap)
+		if err != nil {
+			errorExit(err)
+		}
+	}
+}
diff --git a/cmd/release_config/release_config/Android.bp b/cmd/release_config/release_config/Android.bp
new file mode 100644
index 0000000..3c73826
--- /dev/null
+++ b/cmd/release_config/release_config/Android.bp
@@ -0,0 +1,18 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-cmd-release_config-release_config",
+    pkgPath: "android/soong/cmd/release_config/release_config",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-proto",
+        "soong-cmd-release_config-lib",
+    ],
+    srcs: [
+        "main.go",
+    ],
+}
diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go
new file mode 100644
index 0000000..d06b2b7
--- /dev/null
+++ b/cmd/release_config/release_config/main.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 main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+	"path/filepath"
+
+	rc_lib "android/soong/cmd/release_config/release_config_lib"
+)
+
+func main() {
+	var top string
+	var quiet bool
+	var releaseConfigMapPaths rc_lib.StringList
+	var targetRelease string
+	var outputDir string
+	var err error
+	var configs *rc_lib.ReleaseConfigs
+	var json, pb, textproto, inheritance bool
+	var product string
+	var allMake bool
+	var useBuildVar, allowMissing bool
+	var guard bool
+
+	defaultRelease := os.Getenv("TARGET_RELEASE")
+	if defaultRelease == "" {
+		defaultRelease = "trunk_staging"
+	}
+
+	flag.StringVar(&top, "top", ".", "path to top of workspace")
+	flag.StringVar(&product, "product", os.Getenv("TARGET_PRODUCT"), "TARGET_PRODUCT for the build")
+	flag.BoolVar(&quiet, "quiet", false, "disable warning messages")
+	flag.Var(&releaseConfigMapPaths, "map", "path to a release_config_map.textproto. may be repeated")
+	flag.StringVar(&targetRelease, "release", defaultRelease, "TARGET_RELEASE for this build")
+	flag.BoolVar(&allowMissing, "allow-missing", false, "Use trunk_staging values if release not found")
+	flag.StringVar(&outputDir, "out_dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
+	flag.BoolVar(&textproto, "textproto", true, "write artifacts as text protobuf")
+	flag.BoolVar(&json, "json", true, "write artifacts as json")
+	flag.BoolVar(&pb, "pb", true, "write artifacts as binary protobuf")
+	flag.BoolVar(&allMake, "all_make", false, "write makefiles for all release configs")
+	flag.BoolVar(&inheritance, "inheritance", true, "write inheritance graph")
+	flag.BoolVar(&useBuildVar, "use_get_build_var", false, "use get_build_var PRODUCT_RELEASE_CONFIG_MAPS")
+	flag.BoolVar(&guard, "guard", true, "whether to guard with RELEASE_BUILD_FLAGS_IN_PROTOBUF")
+
+	flag.Parse()
+
+	if quiet {
+		rc_lib.DisableWarnings()
+	}
+
+	if err = os.Chdir(top); err != nil {
+		panic(err)
+	}
+	configs, err = rc_lib.ReadReleaseConfigMaps(releaseConfigMapPaths, targetRelease, useBuildVar, allowMissing)
+	if err != nil {
+		panic(err)
+	}
+	config, err := configs.GetReleaseConfig(targetRelease)
+	if err != nil {
+		panic(err)
+	}
+	err = os.MkdirAll(outputDir, 0775)
+	if err != nil {
+		panic(err)
+	}
+
+	makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, targetRelease))
+	useProto, ok := config.FlagArtifacts["RELEASE_BUILD_FLAGS_IN_PROTOBUF"]
+	if guard && (!ok || rc_lib.MarshalValue(useProto.Value) == "") {
+		// We were told to guard operation and either we have no build flag, or it is False.
+		// Write an empty file so that release_config.mk will use the old process.
+		os.WriteFile(makefilePath, []byte{}, 0644)
+		return
+	}
+	// Write the makefile where release_config.mk is going to look for it.
+	err = config.WriteMakefile(makefilePath, targetRelease, configs)
+	if err != nil {
+		panic(err)
+	}
+	if allMake {
+		// Write one makefile per release config, using the canonical release name.
+		for _, c := range configs.GetSortedReleaseConfigs() {
+			if c.Name != targetRelease {
+				makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, c.Name))
+				err = config.WriteMakefile(makefilePath, c.Name, configs)
+				if err != nil {
+					panic(err)
+				}
+			}
+		}
+	}
+	if inheritance {
+		inheritPath := filepath.Join(outputDir, fmt.Sprintf("inheritance_graph-%s.dot", product))
+		err = configs.WriteInheritanceGraph(inheritPath)
+		if err != nil {
+			panic(err)
+		}
+	}
+	if json {
+		err = configs.WriteArtifact(outputDir, product, "json")
+		if err != nil {
+			panic(err)
+		}
+	}
+	if pb {
+		err = configs.WriteArtifact(outputDir, product, "pb")
+		if err != nil {
+			panic(err)
+		}
+	}
+	if textproto {
+		err = configs.WriteArtifact(outputDir, product, "textproto")
+		if err != nil {
+			panic(err)
+		}
+	}
+	if err = config.WritePartitionBuildFlags(outputDir); err != nil {
+		panic(err)
+	}
+
+}
diff --git a/cmd/release_config/release_config_lib/Android.bp b/cmd/release_config/release_config_lib/Android.bp
new file mode 100644
index 0000000..17251bd
--- /dev/null
+++ b/cmd/release_config/release_config_lib/Android.bp
@@ -0,0 +1,37 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-cmd-release_config-lib",
+    pkgPath: "android/soong/cmd/release_config/release_config_lib",
+    deps: [
+        "golang-protobuf-encoding-prototext",
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+        "soong-cmd-release_config-proto",
+        "blueprint-pathtools",
+    ],
+    srcs: [
+        "flag_artifact.go",
+        "flag_declaration.go",
+        "flag_value.go",
+        "release_config.go",
+        "release_configs.go",
+        "util.go",
+    ],
+}
diff --git a/cmd/release_config/release_config_lib/flag_artifact.go b/cmd/release_config/release_config_lib/flag_artifact.go
new file mode 100644
index 0000000..93c50cd
--- /dev/null
+++ b/cmd/release_config/release_config_lib/flag_artifact.go
@@ -0,0 +1,234 @@
+// 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 release_config_lib
+
+import (
+	"cmp"
+	"fmt"
+	"slices"
+
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+
+	"google.golang.org/protobuf/proto"
+)
+
+// A flag artifact, with its final value and declaration/override history.
+type FlagArtifact struct {
+	// The flag_declaration message.
+	FlagDeclaration *rc_proto.FlagDeclaration
+
+	// The index of the config directory where this flag was declared.
+	// Flag values cannot be set in a location with a lower index.
+	DeclarationIndex int
+
+	// A history of value assignments and overrides.
+	Traces []*rc_proto.Tracepoint
+
+	// The value of the flag.
+	Value *rc_proto.Value
+
+	// This flag is redacted.  Set by UpdateValue when the FlagValue proto
+	// says to redact it.
+	Redacted bool
+}
+
+// Key is flag name.
+type FlagArtifacts map[string]*FlagArtifact
+
+func FlagArtifactFactory(declPath string) *FlagArtifact {
+	fd := &rc_proto.FlagDeclaration{}
+	fa := &FlagArtifact{
+		FlagDeclaration:  fd,
+		DeclarationIndex: -1,
+		Traces:           []*rc_proto.Tracepoint{},
+	}
+	if declPath != "" {
+		LoadMessage(declPath, fd)
+		fa.Value = fd.GetValue()
+		fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(declPath), Value: fa.Value})
+	}
+	return fa
+}
+
+func FlagArtifactsFactory(artifactsPath string) *FlagArtifacts {
+	ret := make(FlagArtifacts)
+	if artifactsPath != "" {
+		fas := &rc_proto.FlagArtifacts{}
+		LoadMessage(artifactsPath, fas)
+		for _, fa_pb := range fas.Flags {
+			fa := &FlagArtifact{}
+			fa.FlagDeclaration = fa_pb.GetFlagDeclaration()
+			if val := fa_pb.GetValue(); val != nil {
+				fa.Value = val
+			}
+			if traces := fa_pb.GetTraces(); traces != nil {
+				fa.Traces = traces
+			}
+			ret[*fa.FlagDeclaration.Name] = fa
+		}
+	}
+	return &ret
+}
+
+func (fas *FlagArtifacts) SortedFlagNames() []string {
+	var names []string
+	for k, _ := range *fas {
+		names = append(names, k)
+	}
+	slices.Sort(names)
+	return names
+}
+
+func (fa *FlagArtifact) GenerateFlagDeclarationArtifact() *rc_proto.FlagDeclarationArtifact {
+	ret := &rc_proto.FlagDeclarationArtifact{
+		Name:            fa.FlagDeclaration.Name,
+		DeclarationPath: fa.Traces[0].Source,
+	}
+	if namespace := fa.FlagDeclaration.GetNamespace(); namespace != "" {
+		ret.Namespace = proto.String(namespace)
+	}
+	if description := fa.FlagDeclaration.GetDescription(); description != "" {
+		ret.Description = proto.String(description)
+	}
+	if workflow := fa.FlagDeclaration.GetWorkflow(); workflow != rc_proto.Workflow_Workflow_Unspecified {
+		ret.Workflow = &workflow
+	}
+	if containers := fa.FlagDeclaration.GetContainers(); containers != nil {
+		ret.Containers = containers
+	}
+	return ret
+}
+
+func FlagDeclarationArtifactsFactory(path string) *rc_proto.FlagDeclarationArtifacts {
+	ret := &rc_proto.FlagDeclarationArtifacts{}
+	if path != "" {
+		LoadMessage(path, ret)
+	} else {
+		ret.FlagDeclarationArtifactList = []*rc_proto.FlagDeclarationArtifact{}
+	}
+	return ret
+}
+
+func (fas *FlagArtifacts) GenerateFlagDeclarationArtifacts(intermediates []*rc_proto.FlagDeclarationArtifacts) *rc_proto.FlagDeclarationArtifacts {
+	ret := &rc_proto.FlagDeclarationArtifacts{FlagDeclarationArtifactList: []*rc_proto.FlagDeclarationArtifact{}}
+	for _, fa := range *fas {
+		ret.FlagDeclarationArtifactList = append(ret.FlagDeclarationArtifactList, fa.GenerateFlagDeclarationArtifact())
+	}
+	for _, fda := range intermediates {
+		ret.FlagDeclarationArtifactList = append(ret.FlagDeclarationArtifactList, fda.FlagDeclarationArtifactList...)
+	}
+	slices.SortFunc(ret.FlagDeclarationArtifactList, func(a, b *rc_proto.FlagDeclarationArtifact) int {
+		return cmp.Compare(*a.Name, *b.Name)
+	})
+	return ret
+}
+
+// Create a clone of the flag artifact.
+//
+// Returns:
+//
+//	*FlagArtifact: the copy of the artifact.
+func (src *FlagArtifact) Clone() *FlagArtifact {
+	value := &rc_proto.Value{}
+	proto.Merge(value, src.Value)
+	return &FlagArtifact{
+		FlagDeclaration:  src.FlagDeclaration,
+		Traces:           src.Traces,
+		Value:            value,
+		DeclarationIndex: src.DeclarationIndex,
+		Redacted:         src.Redacted,
+	}
+}
+
+// Clone FlagArtifacts.
+//
+// Returns:
+//
+//	FlagArtifacts: a copy of the source FlagArtifacts.
+func (src FlagArtifacts) Clone() (dst FlagArtifacts) {
+	if dst == nil {
+		dst = make(FlagArtifacts)
+	}
+	for k, v := range src {
+		dst[k] = v.Clone()
+	}
+	return
+}
+
+// Update the value of a flag.
+//
+// This appends to flagArtifact.Traces, and updates flagArtifact.Value.
+//
+// Args:
+//
+//	flagValue FlagValue: the value to assign
+//
+// Returns:
+//
+//	error: any error encountered
+func (fa *FlagArtifact) UpdateValue(flagValue FlagValue) error {
+	name := *flagValue.proto.Name
+	fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value})
+	if flagValue.proto.GetRedacted() {
+		fa.Redacted = true
+		fmt.Printf("Redacting flag %s in %s\n", name, flagValue.path)
+		return nil
+	}
+	if fa.Value.GetObsolete() {
+		return fmt.Errorf("Attempting to set obsolete flag %s. Trace=%v", name, fa.Traces)
+	}
+	var newValue *rc_proto.Value
+	switch val := flagValue.proto.Value.Val.(type) {
+	case *rc_proto.Value_StringValue:
+		newValue = &rc_proto.Value{Val: &rc_proto.Value_StringValue{val.StringValue}}
+	case *rc_proto.Value_BoolValue:
+		newValue = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{val.BoolValue}}
+	case *rc_proto.Value_Obsolete:
+		if !val.Obsolete {
+			return fmt.Errorf("%s: Cannot set obsolete=false.  Trace=%v", name, fa.Traces)
+		}
+		newValue = &rc_proto.Value{Val: &rc_proto.Value_Obsolete{true}}
+	default:
+		return fmt.Errorf("Invalid type for flag_value: %T.  Trace=%v", val, fa.Traces)
+	}
+	if proto.Equal(newValue, fa.Value) {
+		warnf("%s: redundant override (set in %s)\n", flagValue.path, *fa.Traces[len(fa.Traces)-2].Source)
+	}
+	fa.Value = newValue
+	return nil
+}
+
+// Marshal the FlagArtifact into a flag_artifact message.
+func (fa *FlagArtifact) Marshal() (*rc_proto.FlagArtifact, error) {
+	if fa.Redacted {
+		return nil, nil
+	}
+	return &rc_proto.FlagArtifact{
+		FlagDeclaration: fa.FlagDeclaration,
+		Value:           fa.Value,
+		Traces:          fa.Traces,
+	}, nil
+}
+
+// Marshal the FlagArtifact without Traces.
+func (fa *FlagArtifact) MarshalWithoutTraces() (*rc_proto.FlagArtifact, error) {
+	if fa.Redacted {
+		return nil, nil
+	}
+	return &rc_proto.FlagArtifact{
+		FlagDeclaration: fa.FlagDeclaration,
+		Value:           fa.Value,
+	}, nil
+}
diff --git a/cmd/release_config/release_config_lib/flag_declaration.go b/cmd/release_config/release_config_lib/flag_declaration.go
new file mode 100644
index 0000000..97d4d4c
--- /dev/null
+++ b/cmd/release_config/release_config_lib/flag_declaration.go
@@ -0,0 +1,27 @@
+// 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 release_config_lib
+
+import (
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+)
+
+func FlagDeclarationFactory(protoPath string) (fd *rc_proto.FlagDeclaration) {
+	fd = &rc_proto.FlagDeclaration{}
+	if protoPath != "" {
+		LoadMessage(protoPath, fd)
+	}
+	return fd
+}
diff --git a/cmd/release_config/release_config_lib/flag_value.go b/cmd/release_config/release_config_lib/flag_value.go
new file mode 100644
index 0000000..76363ce
--- /dev/null
+++ b/cmd/release_config/release_config_lib/flag_value.go
@@ -0,0 +1,95 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package release_config_lib
+
+import (
+	"strings"
+
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+)
+
+type FlagValue struct {
+	// The path providing this value.
+	path string
+
+	// Protobuf
+	proto rc_proto.FlagValue
+}
+
+func FlagValueFactory(protoPath string) (fv *FlagValue) {
+	fv = &FlagValue{path: protoPath}
+	if protoPath != "" {
+		LoadMessage(protoPath, &fv.proto)
+	}
+	return fv
+}
+
+func UnmarshalValue(str string) *rc_proto.Value {
+	ret := &rc_proto.Value{}
+	switch v := strings.ToLower(str); v {
+	case "true":
+		ret = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{true}}
+	case "false":
+		ret = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{false}}
+	case "##obsolete":
+		ret = &rc_proto.Value{Val: &rc_proto.Value_Obsolete{true}}
+	default:
+		ret = &rc_proto.Value{Val: &rc_proto.Value_StringValue{str}}
+	}
+	return ret
+}
+
+func MarshalValue(value *rc_proto.Value) string {
+	if value == nil {
+		return ""
+	}
+	switch val := value.Val.(type) {
+	case *rc_proto.Value_UnspecifiedValue:
+		// Value was never set.
+		return ""
+	case *rc_proto.Value_StringValue:
+		return val.StringValue
+	case *rc_proto.Value_BoolValue:
+		if val.BoolValue {
+			return "true"
+		}
+		// False ==> empty string
+		return ""
+	case *rc_proto.Value_Obsolete:
+		return " #OBSOLETE"
+	default:
+		// Flagged as error elsewhere, so return empty string here.
+		return ""
+	}
+}
+
+// Returns a string representation of the type of the value for make
+func ValueType(value *rc_proto.Value) string {
+	if value == nil || value.Val == nil {
+		return "unspecified"
+	}
+	switch value.Val.(type) {
+	case *rc_proto.Value_UnspecifiedValue:
+		return "unspecified"
+	case *rc_proto.Value_StringValue:
+		return "string"
+	case *rc_proto.Value_BoolValue:
+		return "bool"
+	case *rc_proto.Value_Obsolete:
+		return "obsolete"
+	default:
+		panic("Unhandled type")
+	}
+}
diff --git a/cmd/release_config/release_config_lib/flag_value_test.go b/cmd/release_config/release_config_lib/flag_value_test.go
new file mode 100644
index 0000000..8a98baf
--- /dev/null
+++ b/cmd/release_config/release_config_lib/flag_value_test.go
@@ -0,0 +1,114 @@
+// 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 release_config_lib
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+
+	"google.golang.org/protobuf/proto"
+)
+
+type testCaseFlagValueFactory struct {
+	protoPath string
+	name      string
+	data      []byte
+	expected  rc_proto.FlagValue
+	err       error
+}
+
+func (tc testCaseFlagValueFactory) assertProtoEqual(t *testing.T, expected, actual proto.Message) {
+	if !proto.Equal(expected, actual) {
+		t.Errorf("Expected %q found %q", expected, actual)
+	}
+}
+
+func TestFlagValueFactory(t *testing.T) {
+	testCases := []testCaseFlagValueFactory{
+		{
+			name:      "stringVal",
+			protoPath: "build/release/flag_values/test/RELEASE_FOO.textproto",
+			data:      []byte(`name: "RELEASE_FOO" value {string_value: "BAR"}`),
+			expected: rc_proto.FlagValue{
+				Name:  proto.String("RELEASE_FOO"),
+				Value: &rc_proto.Value{Val: &rc_proto.Value_StringValue{"BAR"}},
+			},
+			err: nil,
+		},
+	}
+	for _, tc := range testCases {
+		var err error
+		tempdir := t.TempDir()
+		path := filepath.Join(tempdir, tc.protoPath)
+		if err = os.MkdirAll(filepath.Dir(path), 0755); err != nil {
+			t.Fatal(err)
+		}
+		if err = os.WriteFile(path, tc.data, 0644); err != nil {
+			t.Fatal(err)
+		}
+		actual := FlagValueFactory(path)
+		tc.assertProtoEqual(t, &tc.expected, &actual.proto)
+	}
+}
+
+type testCaseMarshalValue struct {
+	name     string
+	value    *rc_proto.Value
+	expected string
+}
+
+func TestMarshalValue(t *testing.T) {
+	testCases := []testCaseMarshalValue{
+		{
+			name:     "nil",
+			value:    nil,
+			expected: "",
+		},
+		{
+			name:     "unspecified",
+			value:    &rc_proto.Value{},
+			expected: "",
+		},
+		{
+			name:     "false",
+			value:    &rc_proto.Value{Val: &rc_proto.Value_BoolValue{false}},
+			expected: "",
+		},
+		{
+			name:     "true",
+			value:    &rc_proto.Value{Val: &rc_proto.Value_BoolValue{true}},
+			expected: "true",
+		},
+		{
+			name:     "string",
+			value:    &rc_proto.Value{Val: &rc_proto.Value_StringValue{"BAR"}},
+			expected: "BAR",
+		},
+		{
+			name:     "obsolete",
+			value:    &rc_proto.Value{Val: &rc_proto.Value_Obsolete{true}},
+			expected: " #OBSOLETE",
+		},
+	}
+	for _, tc := range testCases {
+		actual := MarshalValue(tc.value)
+		if actual != tc.expected {
+			t.Errorf("Expected %q found %q", tc.expected, actual)
+		}
+	}
+}
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
new file mode 100644
index 0000000..adf0e62
--- /dev/null
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -0,0 +1,435 @@
+// 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 release_config_lib
+
+import (
+	"cmp"
+	"fmt"
+	"os"
+	"path/filepath"
+	"regexp"
+	"slices"
+	"sort"
+	"strings"
+
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+
+	"google.golang.org/protobuf/proto"
+)
+
+// One directory's contribution to the a release config.
+type ReleaseConfigContribution struct {
+	// Path of the file providing this config contribution.
+	path string
+
+	// The index of the config directory where this release config
+	// contribution was declared.
+	// Flag values cannot be set in a location with a lower index.
+	DeclarationIndex int
+
+	// Protobufs relevant to the config.
+	proto rc_proto.ReleaseConfig
+
+	FlagValues []*FlagValue
+}
+
+// A generated release config.
+type ReleaseConfig struct {
+	// the Name of the release config
+	Name string
+
+	// The index of the config directory where this release config was
+	// first declared.
+	// Flag values cannot be set in a location with a lower index.
+	DeclarationIndex int
+
+	// What contributes to this config.
+	Contributions []*ReleaseConfigContribution
+
+	// Aliases for this release
+	OtherNames []string
+
+	// The names of release configs that we inherit
+	InheritNames []string
+
+	// True if this release config only allows inheritance and aconfig flag
+	// overrides. Build flag value overrides are an error.
+	AconfigFlagsOnly bool
+
+	// Unmarshalled flag artifacts
+	FlagArtifacts FlagArtifacts
+
+	// The files used by this release config
+	FilesUsedMap map[string]bool
+
+	// Generated release config
+	ReleaseConfigArtifact *rc_proto.ReleaseConfigArtifact
+
+	// We have begun compiling this release config.
+	compileInProgress bool
+
+	// Partitioned artifacts for {partition}/etc/build_flags.json
+	PartitionBuildFlags map[string]*rc_proto.FlagArtifacts
+
+	// Prior stage(s) for flag advancement (during development).
+	// Once a flag has met criteria in a prior stage, it can advance to this one.
+	PriorStagesMap map[string]bool
+}
+
+func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) {
+	return &ReleaseConfig{
+		Name:             name,
+		DeclarationIndex: index,
+		FilesUsedMap:     make(map[string]bool),
+		PriorStagesMap:   make(map[string]bool),
+	}
+}
+
+func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error {
+	for f := range iConfig.FilesUsedMap {
+		config.FilesUsedMap[f] = true
+	}
+	for _, fa := range iConfig.FlagArtifacts {
+		name := *fa.FlagDeclaration.Name
+		myFa, ok := config.FlagArtifacts[name]
+		if !ok {
+			return fmt.Errorf("Could not inherit flag %s from %s", name, iConfig.Name)
+		}
+		if name == "RELEASE_ACONFIG_VALUE_SETS" {
+			// If there is a value assigned, add the trace.
+			if len(fa.Value.GetStringValue()) > 0 {
+				myFa.Traces = append(myFa.Traces, fa.Traces...)
+				myFa.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{
+					myFa.Value.GetStringValue() + " " + fa.Value.GetStringValue()}}
+			}
+		} else if len(fa.Traces) > 1 {
+			// A value was assigned. Set our value.
+			myFa.Traces = append(myFa.Traces, fa.Traces[1:]...)
+			myFa.Value = fa.Value
+		}
+	}
+	return nil
+}
+
+func (config *ReleaseConfig) GetSortedFileList() []string {
+	return SortedMapKeys(config.FilesUsedMap)
+}
+
+func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error {
+	if config.ReleaseConfigArtifact != nil {
+		return nil
+	}
+	if config.compileInProgress {
+		return fmt.Errorf("Loop detected for release config %s", config.Name)
+	}
+	config.compileInProgress = true
+	isRoot := config.Name == "root"
+
+	// Is this a build-prefix release config, such as 'ap3a'?
+	isBuildPrefix, err := regexp.MatchString("^[a-z][a-z][0-9][0-9a-z]$", config.Name)
+	if err != nil {
+		return err
+	}
+	// Start with only the flag declarations.
+	config.FlagArtifacts = configs.FlagArtifacts.Clone()
+	releaseAconfigValueSets := config.FlagArtifacts["RELEASE_ACONFIG_VALUE_SETS"]
+	releasePlatformVersion := config.FlagArtifacts["RELEASE_PLATFORM_VERSION"]
+
+	// Generate any configs we need to inherit.  This will detect loops in
+	// the config.
+	contributionsToApply := []*ReleaseConfigContribution{}
+	myInherits := []string{}
+	myInheritsSet := make(map[string]bool)
+	// If there is a "root" release config, it is the start of every inheritance chain.
+	_, err = configs.GetReleaseConfig("root")
+	if err == nil && !isRoot {
+		config.InheritNames = append([]string{"root"}, config.InheritNames...)
+	}
+	for _, inherit := range config.InheritNames {
+		if _, ok := myInheritsSet[inherit]; ok {
+			continue
+		}
+		if isBuildPrefix && configs.Aliases[inherit] != nil {
+			return fmt.Errorf("%s cannot inherit from alias %s", config.Name, inherit)
+		}
+		myInherits = append(myInherits, inherit)
+		myInheritsSet[inherit] = true
+		iConfig, err := configs.GetReleaseConfig(inherit)
+		if err != nil {
+			return err
+		}
+		err = iConfig.GenerateReleaseConfig(configs)
+		if err != nil {
+			return err
+		}
+		err = config.InheritConfig(iConfig)
+		if err != nil {
+			return err
+		}
+	}
+
+	// If we inherited nothing, then we need to mark the global files as used for this
+	// config.  If we inherited, then we already marked them as part of inheritance.
+	if len(config.InheritNames) == 0 {
+		for f := range configs.FilesUsedMap {
+			config.FilesUsedMap[f] = true
+		}
+	}
+
+	contributionsToApply = append(contributionsToApply, config.Contributions...)
+
+	workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL)
+	myDirsMap := make(map[int]bool)
+	myValueDirsMap := make(map[int]bool)
+	if isBuildPrefix && releasePlatformVersion != nil {
+		if MarshalValue(releasePlatformVersion.Value) != strings.ToUpper(config.Name) {
+			value := FlagValue{
+				path: config.Contributions[0].path,
+				proto: rc_proto.FlagValue{
+					Name:  releasePlatformVersion.FlagDeclaration.Name,
+					Value: UnmarshalValue(strings.ToUpper(config.Name)),
+				},
+			}
+			if err := releasePlatformVersion.UpdateValue(value); err != nil {
+				return err
+			}
+		}
+	}
+	for _, contrib := range contributionsToApply {
+		contribAconfigValueSets := []string{}
+		// Gather the aconfig_value_sets from this contribution, allowing duplicates for simplicity.
+		for _, v := range contrib.proto.AconfigValueSets {
+			contribAconfigValueSets = append(contribAconfigValueSets, v)
+		}
+		contribAconfigValueSetsString := strings.Join(contribAconfigValueSets, " ")
+		releaseAconfigValueSets.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{
+			releaseAconfigValueSets.Value.GetStringValue() + " " + contribAconfigValueSetsString}}
+		releaseAconfigValueSets.Traces = append(
+			releaseAconfigValueSets.Traces,
+			&rc_proto.Tracepoint{
+				Source: proto.String(contrib.path),
+				Value:  &rc_proto.Value{Val: &rc_proto.Value_StringValue{contribAconfigValueSetsString}},
+			})
+
+		for _, priorStage := range contrib.proto.PriorStages {
+			config.PriorStagesMap[priorStage] = true
+		}
+		myDirsMap[contrib.DeclarationIndex] = true
+		// 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
+			fa, ok := config.FlagArtifacts[name]
+			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 (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.
+				return fmt.Errorf("Setting value for non-MANUAL flag %s is not allowed in %s", name, value.path)
+			}
+			if err := fa.UpdateValue(*value); err != nil {
+				return err
+			}
+			if fa.Redacted {
+				delete(config.FlagArtifacts, name)
+			}
+		}
+	}
+	// Now remove any duplicates from the actual value of RELEASE_ACONFIG_VALUE_SETS
+	myAconfigValueSets := []string{}
+	myAconfigValueSetsMap := map[string]bool{}
+	for _, v := range strings.Split(releaseAconfigValueSets.Value.GetStringValue(), " ") {
+		if v == "" || myAconfigValueSetsMap[v] {
+			continue
+		}
+		myAconfigValueSetsMap[v] = true
+		myAconfigValueSets = append(myAconfigValueSets, v)
+	}
+	releaseAconfigValueSets.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{strings.TrimSpace(strings.Join(myAconfigValueSets, " "))}}
+
+	directories := []string{}
+	valueDirectories := []string{}
+	for idx, confDir := range configs.configDirs {
+		if _, ok := myDirsMap[idx]; ok {
+			directories = append(directories, confDir)
+		}
+		if _, ok := myValueDirsMap[idx]; ok {
+			valueDirectories = append(valueDirectories, confDir)
+		}
+	}
+
+	// Now build the per-partition artifacts
+	config.PartitionBuildFlags = make(map[string]*rc_proto.FlagArtifacts)
+	for _, v := range config.FlagArtifacts {
+		artifact, err := v.MarshalWithoutTraces()
+		if err != nil {
+			return err
+		}
+		for _, container := range v.FlagDeclaration.Containers {
+			if _, ok := config.PartitionBuildFlags[container]; !ok {
+				config.PartitionBuildFlags[container] = &rc_proto.FlagArtifacts{}
+			}
+			config.PartitionBuildFlags[container].Flags = append(config.PartitionBuildFlags[container].Flags, artifact)
+		}
+	}
+	config.ReleaseConfigArtifact = &rc_proto.ReleaseConfigArtifact{
+		Name:       proto.String(config.Name),
+		OtherNames: config.OtherNames,
+		Flags: func() []*rc_proto.FlagArtifact {
+			ret := []*rc_proto.FlagArtifact{}
+			flagNames := []string{}
+			for k := range config.FlagArtifacts {
+				flagNames = append(flagNames, k)
+			}
+			sort.Strings(flagNames)
+			for _, flagName := range flagNames {
+				flag := config.FlagArtifacts[flagName]
+				ret = append(ret, &rc_proto.FlagArtifact{
+					FlagDeclaration: flag.FlagDeclaration,
+					Traces:          flag.Traces,
+					Value:           flag.Value,
+				})
+			}
+			return ret
+		}(),
+		AconfigValueSets: myAconfigValueSets,
+		Inherits:         myInherits,
+		Directories:      directories,
+		ValueDirectories: valueDirectories,
+		PriorStages:      SortedMapKeys(config.PriorStagesMap),
+	}
+
+	config.compileInProgress = false
+	return nil
+}
+
+// Write the makefile for this targetRelease.
+func (config *ReleaseConfig) WriteMakefile(outFile, targetRelease string, configs *ReleaseConfigs) error {
+	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)
+
+	vNames := []string{}
+	addVar := func(name, suffix, value string) {
+		fullName := fmt.Sprintf("_ALL_RELEASE_FLAGS.%s.%s", name, suffix)
+		vNames = append(vNames, fullName)
+		makeVars[fullName] = value
+	}
+
+	for _, name := range names {
+		flag := myFlagArtifacts[name]
+		decl := flag.FlagDeclaration
+
+		for _, container := range decl.Containers {
+			partitions[container] = append(partitions[container], name)
+		}
+		value := MarshalValue(flag.Value)
+		makeVars[name] = value
+		addVar(name, "TYPE", ValueType(flag.Value))
+		addVar(name, "PARTITIONS", strings.Join(decl.Containers, " "))
+		addVar(name, "DEFAULT", MarshalValue(decl.Value))
+		addVar(name, "VALUE", value)
+		addVar(name, "DECLARED_IN", *flag.Traces[0].Source)
+		addVar(name, "SET_IN", *flag.Traces[len(flag.Traces)-1].Source)
+		addVar(name, "NAMESPACE", *decl.Namespace)
+	}
+	pNames := []string{}
+	for k := range partitions {
+		pNames = append(pNames, k)
+	}
+	slices.Sort(pNames)
+
+	// Now sort the make variables, and output them.
+	slices.Sort(vNames)
+
+	// Write the flags as:
+	//   _ALL_RELELASE_FLAGS
+	//   _ALL_RELEASE_FLAGS.PARTITIONS.*
+	//   all _ALL_RELEASE_FLAGS.*, sorted by name
+	//   Final flag values, sorted by name.
+	data := fmt.Sprintf("# TARGET_RELEASE=%s\n", config.Name)
+	if targetRelease != config.Name {
+		data += fmt.Sprintf("# User specified TARGET_RELEASE=%s\n", targetRelease)
+	}
+	// As it stands this list is not per-product, but conceptually it is, and will be.
+	data += fmt.Sprintf("ALL_RELEASE_CONFIGS_FOR_PRODUCT :=$= %s\n", strings.Join(configs.GetAllReleaseNames(), " "))
+	data += fmt.Sprintf("_used_files := %s\n", strings.Join(config.GetSortedFileList(), " "))
+	data += fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " "))
+	for _, pName := range pNames {
+		data += fmt.Sprintf("_ALL_RELEASE_FLAGS.PARTITIONS.%s :=$= %s\n", pName, strings.Join(partitions[pName], " "))
+	}
+	for _, vName := range vNames {
+		data += fmt.Sprintf("%s :=$= %s\n", vName, makeVars[vName])
+	}
+	data += "\n\n# Values for all build flags\n"
+	for _, name := range names {
+		data += fmt.Sprintf("%s :=$= %s\n", name, makeVars[name])
+	}
+	return os.WriteFile(outFile, []byte(data), 0644)
+}
+
+func (config *ReleaseConfig) WritePartitionBuildFlags(outDir string) error {
+	var err error
+	for partition, flags := range config.PartitionBuildFlags {
+		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
+		// build_flags_json module
+		if err = WriteMessage(filepath.Join(outDir, fmt.Sprintf("build_flags_%s.json", partition)), flags); err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
new file mode 100644
index 0000000..a4c884c
--- /dev/null
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -0,0 +1,533 @@
+// 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 release_config_lib
+
+import (
+	"cmp"
+	"fmt"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"slices"
+	"strings"
+
+	rc_proto "android/soong/cmd/release_config/release_config_proto"
+
+	"google.golang.org/protobuf/proto"
+)
+
+// A single release_config_map.textproto and its associated data.
+// Used primarily for debugging.
+type ReleaseConfigMap struct {
+	// The path to this release_config_map file.
+	path string
+
+	// Data received
+	proto rc_proto.ReleaseConfigMap
+
+	// Map of name:contribution for release config contributions.
+	ReleaseConfigContributions map[string]*ReleaseConfigContribution
+
+	// 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
+
+// The generated release configs.
+type ReleaseConfigs struct {
+	// Ordered list of release config maps processed.
+	ReleaseConfigMaps []*ReleaseConfigMap
+
+	// Aliases
+	Aliases map[string]*string
+
+	// Dictionary of flag_name:FlagDeclaration, with no overrides applied.
+	FlagArtifacts FlagArtifacts
+
+	// Generated release configs artifact
+	Artifact rc_proto.ReleaseConfigsArtifact
+
+	// Dictionary of name:ReleaseConfig
+	// Use `GetReleaseConfigs(name)` to get a release config.
+	ReleaseConfigs map[string]*ReleaseConfig
+
+	// Map of directory to *ReleaseConfigMap
+	releaseConfigMapsMap map[string]*ReleaseConfigMap
+
+	// The files used by all release configs
+	FilesUsedMap map[string]bool
+
+	// The list of config directories used.
+	configDirs []string
+
+	// A map from the config directory to its order in the list of config
+	// directories.
+	configDirIndexes ReleaseConfigDirMap
+
+	// True if we should allow a missing primary release config.  In this
+	// case, we will substitute `trunk_staging` values, but the release
+	// config will not be in ALL_RELEASE_CONFIGS_FOR_PRODUCT.
+	allowMissing bool
+}
+
+func (configs *ReleaseConfigs) WriteInheritanceGraph(outFile string) error {
+	data := []string{}
+	usedAliases := make(map[string]bool)
+	priorStages := make(map[string][]string)
+	for _, config := range configs.ReleaseConfigs {
+		if config.Name == "root" {
+			continue
+		}
+		var fillColor string
+		inherits := []string{}
+		for _, inherit := range config.InheritNames {
+			if inherit == "root" {
+				continue
+			}
+			data = append(data, fmt.Sprintf(`"%s" -> "%s"`, config.Name, inherit))
+			inherits = append(inherits, inherit)
+			// If inheriting an alias, add a link from the alias to that release config.
+			if name, found := configs.Aliases[inherit]; found {
+				if !usedAliases[inherit] {
+					usedAliases[inherit] = true
+					data = append(data, fmt.Sprintf(`"%s" -> "%s"`, inherit, *name))
+					data = append(data,
+						fmt.Sprintf(`"%s" [ label="%s\ncurrently: %s" shape=oval ]`,
+							inherit, inherit, *name))
+				}
+			}
+		}
+		// Add links for all of the advancement progressions.
+		for priorStage := range config.PriorStagesMap {
+			data = append(data, fmt.Sprintf(`"%s" -> "%s" [ style=dashed color="#81c995" ]`,
+				priorStage, config.Name))
+			priorStages[config.Name] = append(priorStages[config.Name], priorStage)
+		}
+		label := config.Name
+		if len(inherits) > 0 {
+			label += "\\ninherits: " + strings.Join(inherits, " ")
+		}
+		if len(config.OtherNames) > 0 {
+			label += "\\nother names: " + strings.Join(config.OtherNames, " ")
+		}
+		switch config.Name {
+		case *configs.Artifact.ReleaseConfig.Name:
+			// The active release config has a light blue fill.
+			fillColor = `fillcolor="#d2e3fc" `
+		case "trunk", "trunk_staging":
+			// Certain workflow stages have a light green fill.
+			fillColor = `fillcolor="#ceead6" `
+		default:
+			// Look for "next" and "*_next", make them light green as well.
+			for _, n := range config.OtherNames {
+				if n == "next" || strings.HasSuffix(n, "_next") {
+					fillColor = `fillcolor="#ceead6" `
+				}
+			}
+		}
+		data = append(data,
+			fmt.Sprintf(`"%s" [ label="%s" %s]`, config.Name, label, fillColor))
+	}
+	slices.Sort(data)
+	data = append([]string{
+		"digraph {",
+		"graph [ ratio=.5 ]",
+		"node [ shape=box style=filled fillcolor=white colorscheme=svg fontcolor=black ]",
+	}, data...)
+	data = append(data, "}")
+	return os.WriteFile(outFile, []byte(strings.Join(data, "\n")), 0644)
+}
+
+// Write the "all_release_configs" artifact.
+//
+// The file will be in "{outDir}/all_release_configs-{product}.{format}"
+//
+// Args:
+//
+//	outDir string: directory path. Will be created if not present.
+//	product string: TARGET_PRODUCT for the release_configs.
+//	format string: one of "json", "pb", or "textproto"
+//
+// Returns:
+//
+//	error: Any error encountered.
+func (configs *ReleaseConfigs) WriteArtifact(outDir, product, format string) error {
+	return WriteMessage(
+		filepath.Join(outDir, fmt.Sprintf("all_release_configs-%s.%s", product, format)),
+		&configs.Artifact)
+}
+
+func ReleaseConfigsFactory() (c *ReleaseConfigs) {
+	configs := ReleaseConfigs{
+		Aliases:              make(map[string]*string),
+		FlagArtifacts:        make(map[string]*FlagArtifact),
+		ReleaseConfigs:       make(map[string]*ReleaseConfig),
+		releaseConfigMapsMap: make(map[string]*ReleaseConfigMap),
+		configDirs:           []string{},
+		configDirIndexes:     make(ReleaseConfigDirMap),
+		FilesUsedMap:         make(map[string]bool),
+	}
+	workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL)
+	releaseAconfigValueSets := FlagArtifact{
+		FlagDeclaration: &rc_proto.FlagDeclaration{
+			Name:        proto.String("RELEASE_ACONFIG_VALUE_SETS"),
+			Namespace:   proto.String("android_UNKNOWN"),
+			Description: proto.String("Aconfig value sets assembled by release-config"),
+			Workflow:    &workflowManual,
+			Containers:  []string{"system", "system_ext", "product", "vendor"},
+			Value:       &rc_proto.Value{Val: &rc_proto.Value_UnspecifiedValue{false}},
+		},
+		DeclarationIndex: -1,
+		Traces:           []*rc_proto.Tracepoint{},
+	}
+	configs.FlagArtifacts["RELEASE_ACONFIG_VALUE_SETS"] = &releaseAconfigValueSets
+	return &configs
+}
+
+func (configs *ReleaseConfigs) GetSortedReleaseConfigs() (ret []*ReleaseConfig) {
+	for _, config := range configs.ReleaseConfigs {
+		ret = append(ret, config)
+	}
+	slices.SortFunc(ret, func(a, b *ReleaseConfig) int {
+		return cmp.Compare(a.Name, b.Name)
+	})
+	return ret
+}
+
+func ReleaseConfigMapFactory(protoPath string) (m *ReleaseConfigMap) {
+	m = &ReleaseConfigMap{
+		path:                       protoPath,
+		ReleaseConfigContributions: make(map[string]*ReleaseConfigContribution),
+	}
+	if protoPath != "" {
+		LoadMessage(protoPath, &m.proto)
+	}
+	return m
+}
+
+// Find the top of the release config contribution directory.
+// Returns the parent of the flag_declarations and flag_values directories.
+func (configs *ReleaseConfigs) GetDirIndex(path string) (int, error) {
+	for p := path; p != "."; p = filepath.Dir(p) {
+		if idx, ok := configs.configDirIndexes[p]; ok {
+			return idx, nil
+		}
+	}
+	return -1, fmt.Errorf("Could not determine release config directory from %s", path)
+}
+
+// Determine the default directory for writing a flag value.
+//
+// Returns the path of the highest-Indexed one of:
+//   - Where the flag is declared
+//   - Where the release config is first declared
+//   - The last place the value is being written.
+func (configs *ReleaseConfigs) GetFlagValueDirectory(config *ReleaseConfig, flag *FlagArtifact) (string, error) {
+	current, err := configs.GetDirIndex(*flag.Traces[len(flag.Traces)-1].Source)
+	if err != nil {
+		return "", err
+	}
+	index := max(flag.DeclarationIndex, config.DeclarationIndex, current)
+	return configs.configDirs[index], nil
+}
+
+func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex int) error {
+	if _, err := os.Stat(path); err != nil {
+		return fmt.Errorf("%s does not exist\n", path)
+	}
+	m := ReleaseConfigMapFactory(path)
+	if m.proto.DefaultContainers == nil {
+		return fmt.Errorf("Release config map %s lacks default_containers", path)
+	}
+	for _, container := range m.proto.DefaultContainers {
+		if !validContainer(container) {
+			return fmt.Errorf("Release config map %s has invalid container %s", path, container)
+		}
+	}
+	configs.FilesUsedMap[path] = true
+	dir := filepath.Dir(path)
+	// Record any aliases, checking for duplicates.
+	for _, alias := range m.proto.Aliases {
+		name := *alias.Name
+		oldTarget, ok := configs.Aliases[name]
+		if ok {
+			if *oldTarget != *alias.Target {
+				return fmt.Errorf("Conflicting alias declarations: %s vs %s",
+					*oldTarget, *alias.Target)
+			}
+		}
+		configs.Aliases[name] = alias.Target
+	}
+	var err error
+	err = WalkTextprotoFiles(dir, "flag_declarations", func(path string, d fs.DirEntry, err error) error {
+		flagDeclaration := FlagDeclarationFactory(path)
+		// Container must be specified.
+		if flagDeclaration.Containers == nil {
+			flagDeclaration.Containers = m.proto.DefaultContainers
+		} else {
+			for _, container := range flagDeclaration.Containers {
+				if !validContainer(container) {
+					return fmt.Errorf("Flag declaration %s has invalid container %s", path, container)
+				}
+			}
+		}
+
+		// 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" {
+			return fmt.Errorf("%s: %s is a reserved build flag", path, name)
+		}
+		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)
+		}
+		// Set the initial value in the flag artifact.
+		configs.FilesUsedMap[path] = true
+		configs.FlagArtifacts[name].UpdateValue(
+			FlagValue{path: path, proto: rc_proto.FlagValue{
+				Name: proto.String(name), Value: flagDeclaration.Value}})
+		if configs.FlagArtifacts[name].Redacted {
+			return fmt.Errorf("%s may not be redacted by default.", name)
+		}
+		return nil
+	})
+	if err != nil {
+		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)
+		name := *releaseConfigContribution.proto.Name
+		if fmt.Sprintf("%s.textproto", name) != filepath.Base(path) {
+			return fmt.Errorf("%s incorrectly declares release config %s", path, name)
+		}
+		if _, ok := configs.ReleaseConfigs[name]; !ok {
+			configs.ReleaseConfigs[name] = ReleaseConfigFactory(name, ConfigDirIndex)
+		}
+		config := configs.ReleaseConfigs[name]
+		config.FilesUsedMap[path] = true
+		inheritNames := make(map[string]bool)
+		for _, inh := range config.InheritNames {
+			inheritNames[inh] = true
+		}
+		// If this contribution says to inherit something we already inherited, we do not want the duplicate.
+		for _, cInh := range releaseConfigContribution.proto.Inherits {
+			if !inheritNames[cInh] {
+				config.InheritNames = append(config.InheritNames, cInh)
+				inheritNames[cInh] = true
+			}
+		}
+
+		// Only walk flag_values/{RELEASE} for defined releases.
+		err2 := WalkTextprotoFiles(dir, filepath.Join("flag_values", name), func(path string, d fs.DirEntry, err error) error {
+			flagValue := FlagValueFactory(path)
+			if fmt.Sprintf("%s.textproto", *flagValue.proto.Name) != filepath.Base(path) {
+				return fmt.Errorf("%s incorrectly sets value for flag %s", path, *flagValue.proto.Name)
+			}
+			if *flagValue.proto.Name == "RELEASE_ACONFIG_VALUE_SETS" {
+				return fmt.Errorf("%s: %s is a reserved build flag", path, *flagValue.proto.Name)
+			}
+			config.FilesUsedMap[path] = true
+			releaseConfigContribution.FlagValues = append(releaseConfigContribution.FlagValues, flagValue)
+			return nil
+		})
+		if err2 != nil {
+			return err2
+		}
+		if releaseConfigContribution.proto.GetAconfigFlagsOnly() {
+			config.AconfigFlagsOnly = true
+		}
+		m.ReleaseConfigContributions[name] = releaseConfigContribution
+		config.Contributions = append(config.Contributions, releaseConfigContribution)
+		return nil
+	})
+	if err != nil {
+		return err
+	}
+	configs.ReleaseConfigMaps = append(configs.ReleaseConfigMaps, m)
+	configs.releaseConfigMapsMap[dir] = m
+	return nil
+}
+
+func (configs *ReleaseConfigs) GetReleaseConfig(name string) (*ReleaseConfig, error) {
+	trace := []string{name}
+	for target, ok := configs.Aliases[name]; ok; target, ok = configs.Aliases[name] {
+		name = *target
+		trace = append(trace, name)
+	}
+	if config, ok := configs.ReleaseConfigs[name]; ok {
+		return config, nil
+	}
+	if configs.allowMissing {
+		if config, ok := configs.ReleaseConfigs["trunk_staging"]; ok {
+			return config, nil
+		}
+	}
+	return nil, fmt.Errorf("Missing config %s.  Trace=%v", name, trace)
+}
+
+func (configs *ReleaseConfigs) GetAllReleaseNames() []string {
+	var allReleaseNames []string
+	for _, v := range configs.ReleaseConfigs {
+		allReleaseNames = append(allReleaseNames, v.Name)
+		allReleaseNames = append(allReleaseNames, v.OtherNames...)
+	}
+	slices.Sort(allReleaseNames)
+	return allReleaseNames
+}
+
+func (configs *ReleaseConfigs) GenerateReleaseConfigs(targetRelease string) error {
+	otherNames := make(map[string][]string)
+	for aliasName, aliasTarget := range configs.Aliases {
+		if _, ok := configs.ReleaseConfigs[aliasName]; ok {
+			return fmt.Errorf("Alias %s is a declared release config", aliasName)
+		}
+		if _, ok := configs.ReleaseConfigs[*aliasTarget]; !ok {
+			if _, ok2 := configs.Aliases[*aliasTarget]; !ok2 {
+				return fmt.Errorf("Alias %s points to non-existing config %s", aliasName, *aliasTarget)
+			}
+		}
+		otherNames[*aliasTarget] = append(otherNames[*aliasTarget], aliasName)
+	}
+	for name, aliases := range otherNames {
+		configs.ReleaseConfigs[name].OtherNames = aliases
+	}
+
+	sortedReleaseConfigs := configs.GetSortedReleaseConfigs()
+	for _, c := range sortedReleaseConfigs {
+		err := c.GenerateReleaseConfig(configs)
+		if err != nil {
+			return err
+		}
+	}
+
+	// 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
+	}
+	orc := []*rc_proto.ReleaseConfigArtifact{}
+	for _, c := range sortedReleaseConfigs {
+		if c.Name != releaseConfig.Name {
+			orc = append(orc, c.ReleaseConfigArtifact)
+		}
+	}
+
+	configs.Artifact = rc_proto.ReleaseConfigsArtifact{
+		ReleaseConfig:       releaseConfig.ReleaseConfigArtifact,
+		OtherReleaseConfigs: orc,
+		ReleaseConfigMapsMap: func() map[string]*rc_proto.ReleaseConfigMap {
+			ret := make(map[string]*rc_proto.ReleaseConfigMap)
+			for k, v := range configs.releaseConfigMapsMap {
+				ret[k] = &v.proto
+			}
+			return ret
+		}(),
+	}
+	return nil
+}
+
+func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease string, useBuildVar, allowMissing bool) (*ReleaseConfigs, error) {
+	var err error
+
+	if len(releaseConfigMapPaths) == 0 {
+		releaseConfigMapPaths, err = GetDefaultMapPaths(useBuildVar)
+		if err != nil {
+			return nil, err
+		}
+		if len(releaseConfigMapPaths) == 0 {
+			return nil, fmt.Errorf("No maps found")
+		}
+		if !useBuildVar {
+			warnf("No --map argument provided.  Using: --map %s\n", strings.Join(releaseConfigMapPaths, " --map "))
+		}
+	}
+
+	configs := ReleaseConfigsFactory()
+	configs.allowMissing = allowMissing
+	mapsRead := make(map[string]bool)
+	var idx int
+	for _, releaseConfigMapPath := range releaseConfigMapPaths {
+		// Maintain an ordered list of release config directories.
+		configDir := filepath.Dir(releaseConfigMapPath)
+		if mapsRead[configDir] {
+			continue
+		}
+		mapsRead[configDir] = true
+		configs.configDirIndexes[configDir] = idx
+		configs.configDirs = append(configs.configDirs, configDir)
+		// Force the path to be the textproto path, so that both the scl and textproto formats can coexist.
+		releaseConfigMapPath = filepath.Join(configDir, "release_config_map.textproto")
+		err = configs.LoadReleaseConfigMap(releaseConfigMapPath, idx)
+		if err != nil {
+			return nil, err
+		}
+		idx += 1
+	}
+
+	// Now that we have all of the release config maps, can meld them and generate the artifacts.
+	err = configs.GenerateReleaseConfigs(targetRelease)
+	return configs, err
+}
diff --git a/cmd/release_config/release_config_lib/util.go b/cmd/release_config/release_config_lib/util.go
new file mode 100644
index 0000000..b149293
--- /dev/null
+++ b/cmd/release_config/release_config_lib/util.go
@@ -0,0 +1,257 @@
+// 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 release_config_lib
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/fs"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"regexp"
+	"slices"
+	"strings"
+
+	"github.com/google/blueprint/pathtools"
+	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/proto"
+)
+
+var (
+	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
+
+func (l *StringList) Set(v string) error {
+	*l = append(*l, v)
+	return nil
+}
+
+func (l *StringList) String() string {
+	return fmt.Sprintf("%v", *l)
+}
+
+// Write a marshalled message to a file.
+//
+// Marshal the message based on the extension of the path we are writing it to.
+//
+// Args:
+//
+//	path string: the path of the file to write to.  Directories are not created.
+//	  Supported extensions are: ".json", ".pb", and ".textproto".
+//	message proto.Message: the message to write.
+//
+// Returns:
+//
+//	error: any error encountered.
+func WriteMessage(path string, message proto.Message) (err error) {
+	format := filepath.Ext(path)
+	if len(format) > 1 {
+		// Strip any leading dot.
+		format = format[1:]
+	}
+	return WriteFormattedMessage(path, format, message)
+}
+
+// Write a marshalled message to a file.
+//
+// Marshal the message using the given format.
+//
+// Args:
+//
+//	path string: the path of the file to write to.  Directories are not created.
+//	  Supported extensions are: ".json", ".pb", and ".textproto".
+//	format string: one of "json", "pb", or "textproto".
+//	message proto.Message: the message to write.
+//
+// Returns:
+//
+//	error: any error encountered.
+func WriteFormattedMessage(path, format string, message proto.Message) (err error) {
+	var data []byte
+	if _, err := os.Stat(filepath.Dir(path)); err != nil {
+		if err = os.MkdirAll(filepath.Dir(path), 0775); err != nil {
+			return err
+		}
+	}
+	switch format {
+	case "json":
+		data, err = json.MarshalIndent(message, "", "  ")
+	case "pb", "binaryproto", "protobuf":
+		data, err = proto.Marshal(message)
+	case "textproto":
+		data, err = prototext.MarshalOptions{Multiline: true}.Marshal(message)
+	default:
+		return fmt.Errorf("Unknown message format for %s", path)
+	}
+	if err != nil {
+		return err
+	}
+	return pathtools.WriteFileIfChanged(path, data, 0644)
+}
+
+// Read a message from a file.
+//
+// The message is unmarshalled based on the extension of the file read.
+//
+// Args:
+//
+//	path string: the path of the file to read.
+//	message proto.Message: the message to unmarshal the message into.
+//
+// Returns:
+//
+//	error: any error encountered.
+func LoadMessage(path string, message proto.Message) error {
+	data, err := os.ReadFile(path)
+	if err != nil {
+		return err
+	}
+	switch filepath.Ext(path) {
+	case ".json":
+		return json.Unmarshal(data, message)
+	case ".pb", ".protobuf", ".binaryproto":
+		return proto.Unmarshal(data, message)
+	case ".textproto":
+		return prototext.Unmarshal(data, message)
+	}
+	return fmt.Errorf("Unknown message format for %s", path)
+}
+
+// Call Func for any textproto files found in {root}/{subdir}.
+func WalkTextprotoFiles(root string, subdir string, Func fs.WalkDirFunc) error {
+	path := filepath.Join(root, subdir)
+	if _, err := os.Stat(path); err != nil {
+		// Missing subdirs are not an error.
+		return nil
+	}
+	return filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			return err
+		}
+		if strings.HasSuffix(d.Name(), ".textproto") && d.Type().IsRegular() {
+			return Func(path, d, err)
+		}
+		return nil
+	})
+}
+
+// Turn off all warning output
+func DisableWarnings() {
+	disableWarnings = true
+}
+
+// warnf will log to stdout if warnings are enabled. In make code,
+// stdout is redirected to a file, so the warnings will not be shown
+// in the terminal.
+func warnf(format string, args ...any) (n int, err error) {
+	if !disableWarnings {
+		return fmt.Printf(format, args...)
+	}
+	return 0, nil
+}
+
+func SortedMapKeys(inputMap map[string]bool) []string {
+	ret := []string{}
+	for k := range inputMap {
+		ret = append(ret, k)
+	}
+	slices.Sort(ret)
+	return ret
+}
+
+func validContainer(container string) bool {
+	return containerRegexp.MatchString(container)
+}
+
+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")
+	if outEnv == "" {
+		outEnv = "out"
+	}
+	return filepath.Join(outEnv, "soong", "release-config")
+}
+
+// Find the top of the workspace.
+//
+// This mirrors the logic in build/envsetup.sh's gettop().
+func GetTopDir() (topDir string, err error) {
+	workingDir, err := os.Getwd()
+	if err != nil {
+		return
+	}
+	topFile := "build/make/core/envsetup.mk"
+	for topDir = workingDir; topDir != "/"; topDir = filepath.Dir(topDir) {
+		if _, err = os.Stat(filepath.Join(topDir, topFile)); err == nil {
+			return filepath.Rel(workingDir, topDir)
+		}
+	}
+	return "", fmt.Errorf("Unable to locate top of workspace")
+}
+
+// Return the default list of map files to use.
+func GetDefaultMapPaths(queryMaps bool) (defaultMapPaths StringList, err error) {
+	var defaultLocations StringList
+	workingDir, err := os.Getwd()
+	if err != nil {
+		return
+	}
+	defer func() {
+		os.Chdir(workingDir)
+	}()
+	topDir, err := GetTopDir()
+	os.Chdir(topDir)
+
+	defaultLocations = StringList{
+		"build/release/release_config_map.textproto",
+		"vendor/google_shared/build/release/release_config_map.textproto",
+		"vendor/google/release/release_config_map.textproto",
+	}
+	for _, path := range defaultLocations {
+		if _, err = os.Stat(path); err == nil {
+			defaultMapPaths = append(defaultMapPaths, path)
+		}
+	}
+
+	var prodMaps string
+	if queryMaps {
+		getBuildVar := exec.Command("build/soong/soong_ui.bash", "--dumpvar-mode", "PRODUCT_RELEASE_CONFIG_MAPS")
+		var stdout strings.Builder
+		getBuildVar.Stdin = strings.NewReader("")
+		getBuildVar.Stdout = &stdout
+		getBuildVar.Stderr = os.Stderr
+		err = getBuildVar.Run()
+		if err != nil {
+			return
+		}
+		prodMaps = stdout.String()
+	} else {
+		prodMaps = os.Getenv("PRODUCT_RELEASE_CONFIG_MAPS")
+	}
+	prodMaps = strings.TrimSpace(prodMaps)
+	if len(prodMaps) > 0 {
+		defaultMapPaths = append(defaultMapPaths, strings.Split(prodMaps, " ")...)
+	}
+	return
+}
diff --git a/cmd/release_config/release_config_proto/Android.bp b/cmd/release_config/release_config_proto/Android.bp
new file mode 100644
index 0000000..c34d203
--- /dev/null
+++ b/cmd/release_config/release_config_proto/Android.bp
@@ -0,0 +1,32 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-cmd-release_config-proto",
+    pkgPath: "android/soong/cmd/release_config/release_config_proto",
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
+    srcs: [
+        "build_flags_common.pb.go",
+        "build_flags_declarations.pb.go",
+        "build_flags_src.pb.go",
+        "build_flags_out.pb.go",
+    ],
+}
diff --git a/cmd/release_config/release_config_proto/build_flags_common.pb.go b/cmd/release_config/release_config_proto/build_flags_common.pb.go
new file mode 100644
index 0000000..82fbcfa
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_common.pb.go
@@ -0,0 +1,169 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        v3.21.12
+// source: build_flags_common.proto
+
+package release_config_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Workflow int32
+
+const (
+	Workflow_Workflow_Unspecified Workflow = 0
+	// Boolean value flags that progress from false to true.
+	Workflow_LAUNCH Workflow = 1
+	// String value flags that get updated with new version strings to control
+	// prebuilt inclusion.
+	Workflow_PREBUILT Workflow = 2
+	// Manually managed outside flags.  These are likely to be found in a
+	// different directory than flags with other workflows.
+	Workflow_MANUAL Workflow = 3
+)
+
+// Enum value maps for Workflow.
+var (
+	Workflow_name = map[int32]string{
+		0: "Workflow_Unspecified",
+		1: "LAUNCH",
+		2: "PREBUILT",
+		3: "MANUAL",
+	}
+	Workflow_value = map[string]int32{
+		"Workflow_Unspecified": 0,
+		"LAUNCH":               1,
+		"PREBUILT":             2,
+		"MANUAL":               3,
+	}
+)
+
+func (x Workflow) Enum() *Workflow {
+	p := new(Workflow)
+	*p = x
+	return p
+}
+
+func (x Workflow) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Workflow) Descriptor() protoreflect.EnumDescriptor {
+	return file_build_flags_common_proto_enumTypes[0].Descriptor()
+}
+
+func (Workflow) Type() protoreflect.EnumType {
+	return &file_build_flags_common_proto_enumTypes[0]
+}
+
+func (x Workflow) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *Workflow) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
+	if err != nil {
+		return err
+	}
+	*x = Workflow(num)
+	return nil
+}
+
+// Deprecated: Use Workflow.Descriptor instead.
+func (Workflow) EnumDescriptor() ([]byte, []int) {
+	return file_build_flags_common_proto_rawDescGZIP(), []int{0}
+}
+
+var File_build_flags_common_proto protoreflect.FileDescriptor
+
+var file_build_flags_common_proto_rawDesc = []byte{
+	0x0a, 0x18, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x63, 0x6f,
+	0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0x4a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b,
+	0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
+	0x5f, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x10, 0x00, 0x12, 0x0a,
+	0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52,
+	0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x41, 0x4e, 0x55,
+	0x41, 0x4c, 0x10, 0x03, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
+	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_build_flags_common_proto_rawDescOnce sync.Once
+	file_build_flags_common_proto_rawDescData = file_build_flags_common_proto_rawDesc
+)
+
+func file_build_flags_common_proto_rawDescGZIP() []byte {
+	file_build_flags_common_proto_rawDescOnce.Do(func() {
+		file_build_flags_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_build_flags_common_proto_rawDescData)
+	})
+	return file_build_flags_common_proto_rawDescData
+}
+
+var file_build_flags_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_build_flags_common_proto_goTypes = []interface{}{
+	(Workflow)(0), // 0: android.release_config_proto.workflow
+}
+var file_build_flags_common_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_build_flags_common_proto_init() }
+func file_build_flags_common_proto_init() {
+	if File_build_flags_common_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_build_flags_common_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   0,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_build_flags_common_proto_goTypes,
+		DependencyIndexes: file_build_flags_common_proto_depIdxs,
+		EnumInfos:         file_build_flags_common_proto_enumTypes,
+	}.Build()
+	File_build_flags_common_proto = out.File
+	file_build_flags_common_proto_rawDesc = nil
+	file_build_flags_common_proto_goTypes = nil
+	file_build_flags_common_proto_depIdxs = nil
+}
diff --git a/cmd/release_config/release_config_proto/build_flags_common.proto b/cmd/release_config/release_config_proto/build_flags_common.proto
new file mode 100644
index 0000000..d5d6101
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_common.proto
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package android.release_config_proto;
+option go_package = "android/soong/release_config/release_config_proto";
+
+// This protobuf file defines common messages used in the rest of the build flag
+// protos.
+
+enum workflow {
+  Workflow_Unspecified = 0;
+
+  // Boolean value flags that progress from false to true.
+  LAUNCH = 1;
+
+  // String value flags that get updated with new version strings to control
+  // prebuilt inclusion.
+  PREBUILT = 2;
+
+  // Manually managed outside flags.  These are likely to be found in a
+  // different directory than flags with other workflows.
+  MANUAL = 3;
+}
diff --git a/cmd/release_config/release_config_proto/build_flags_declarations.pb.go b/cmd/release_config/release_config_proto/build_flags_declarations.pb.go
new file mode 100644
index 0000000..d2de89a
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_declarations.pb.go
@@ -0,0 +1,301 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        v3.21.12
+// source: build_flags_declarations.proto
+
+package release_config_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type FlagDeclarationArtifact struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the flag.
+	// See # name for format detail
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// Namespace the flag belongs to (required)
+	// See # namespace for format detail
+	Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"`
+	// Text description of the flag's purpose.
+	Description *string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
+	// Where the flag was declared.
+	DeclarationPath *string `protobuf:"bytes,5,opt,name=declaration_path,json=declarationPath" json:"declaration_path,omitempty"`
+	// Workflow for this flag.
+	Workflow *Workflow `protobuf:"varint,205,opt,name=workflow,enum=android.release_config_proto.Workflow" json:"workflow,omitempty"`
+	// The container for this flag.  This overrides any default container given
+	// in the release_config_map message.
+	Containers []string `protobuf:"bytes,206,rep,name=containers" json:"containers,omitempty"`
+}
+
+func (x *FlagDeclarationArtifact) Reset() {
+	*x = FlagDeclarationArtifact{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_declarations_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FlagDeclarationArtifact) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FlagDeclarationArtifact) ProtoMessage() {}
+
+func (x *FlagDeclarationArtifact) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_declarations_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use FlagDeclarationArtifact.ProtoReflect.Descriptor instead.
+func (*FlagDeclarationArtifact) Descriptor() ([]byte, []int) {
+	return file_build_flags_declarations_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *FlagDeclarationArtifact) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *FlagDeclarationArtifact) GetNamespace() string {
+	if x != nil && x.Namespace != nil {
+		return *x.Namespace
+	}
+	return ""
+}
+
+func (x *FlagDeclarationArtifact) GetDescription() string {
+	if x != nil && x.Description != nil {
+		return *x.Description
+	}
+	return ""
+}
+
+func (x *FlagDeclarationArtifact) GetDeclarationPath() string {
+	if x != nil && x.DeclarationPath != nil {
+		return *x.DeclarationPath
+	}
+	return ""
+}
+
+func (x *FlagDeclarationArtifact) GetWorkflow() Workflow {
+	if x != nil && x.Workflow != nil {
+		return *x.Workflow
+	}
+	return Workflow_Workflow_Unspecified
+}
+
+func (x *FlagDeclarationArtifact) GetContainers() []string {
+	if x != nil {
+		return x.Containers
+	}
+	return nil
+}
+
+type FlagDeclarationArtifacts struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The artifacts
+	FlagDeclarationArtifactList []*FlagDeclarationArtifact `protobuf:"bytes,1,rep,name=flag_declaration_artifact_list,json=flagDeclarationArtifactList" json:"flag_declaration_artifact_list,omitempty"`
+}
+
+func (x *FlagDeclarationArtifacts) Reset() {
+	*x = FlagDeclarationArtifacts{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_declarations_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FlagDeclarationArtifacts) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FlagDeclarationArtifacts) ProtoMessage() {}
+
+func (x *FlagDeclarationArtifacts) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_declarations_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use FlagDeclarationArtifacts.ProtoReflect.Descriptor instead.
+func (*FlagDeclarationArtifacts) Descriptor() ([]byte, []int) {
+	return file_build_flags_declarations_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *FlagDeclarationArtifacts) GetFlagDeclarationArtifactList() []*FlagDeclarationArtifact {
+	if x != nil {
+		return x.FlagDeclarationArtifactList
+	}
+	return nil
+}
+
+var File_build_flags_declarations_proto protoreflect.FileDescriptor
+
+var file_build_flags_declarations_proto_rawDesc = []byte{
+	0x0a, 0x1e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x64, 0x65,
+	0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x12, 0x1c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x6d,
+	0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8e, 0x02, 0x0a, 0x19, 0x66, 0x6c, 0x61,
+	0x67, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72,
+	0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61,
+	0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e,
+	0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
+	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65,
+	0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f,
+	0x6e, 0x50, 0x61, 0x74, 0x68, 0x12, 0x43, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
+	0x77, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
+	0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1f, 0x0a, 0x0a, 0x63, 0x6f,
+	0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0xce, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x4a, 0x04, 0x08, 0x04, 0x10,
+	0x05, 0x4a, 0x06, 0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01, 0x22, 0x9a, 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, 0x7c, 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, 0x37, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
+	0x66, 0x6c, 0x61, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 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 (
+	file_build_flags_declarations_proto_rawDescOnce sync.Once
+	file_build_flags_declarations_proto_rawDescData = file_build_flags_declarations_proto_rawDesc
+)
+
+func file_build_flags_declarations_proto_rawDescGZIP() []byte {
+	file_build_flags_declarations_proto_rawDescOnce.Do(func() {
+		file_build_flags_declarations_proto_rawDescData = protoimpl.X.CompressGZIP(file_build_flags_declarations_proto_rawDescData)
+	})
+	return file_build_flags_declarations_proto_rawDescData
+}
+
+var file_build_flags_declarations_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_build_flags_declarations_proto_goTypes = []interface{}{
+	(*FlagDeclarationArtifact)(nil),  // 0: android.release_config_proto.flag_declaration_artifact
+	(*FlagDeclarationArtifacts)(nil), // 1: android.release_config_proto.flag_declaration_artifacts
+	(Workflow)(0),                    // 2: android.release_config_proto.workflow
+}
+var file_build_flags_declarations_proto_depIdxs = []int32{
+	2, // 0: android.release_config_proto.flag_declaration_artifact.workflow:type_name -> android.release_config_proto.workflow
+	0, // 1: android.release_config_proto.flag_declaration_artifacts.flag_declaration_artifact_list:type_name -> android.release_config_proto.flag_declaration_artifact
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	2, // [2:2] is the sub-list for extension type_name
+	2, // [2:2] is the sub-list for extension extendee
+	0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_build_flags_declarations_proto_init() }
+func file_build_flags_declarations_proto_init() {
+	if File_build_flags_declarations_proto != nil {
+		return
+	}
+	file_build_flags_common_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_build_flags_declarations_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FlagDeclarationArtifact); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_declarations_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FlagDeclarationArtifacts); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_build_flags_declarations_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_build_flags_declarations_proto_goTypes,
+		DependencyIndexes: file_build_flags_declarations_proto_depIdxs,
+		MessageInfos:      file_build_flags_declarations_proto_msgTypes,
+	}.Build()
+	File_build_flags_declarations_proto = out.File
+	file_build_flags_declarations_proto_rawDesc = nil
+	file_build_flags_declarations_proto_goTypes = nil
+	file_build_flags_declarations_proto_depIdxs = nil
+}
diff --git a/cmd/release_config/release_config_proto/build_flags_declarations.proto b/cmd/release_config/release_config_proto/build_flags_declarations.proto
new file mode 100644
index 0000000..233158e
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_declarations.proto
@@ -0,0 +1,75 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package android.release_config_proto;
+option go_package = "android/soong/release_config/release_config_proto";
+
+import "build_flags_common.proto";
+
+// This protobuf file defines messages used to represent the
+// all_build_flag_declarations artifact for use in automated systems, such as
+// Gantry.
+//
+// The following format requirements apply across various message fields:
+//
+// # name: name of the flag
+//
+//    format: an uppercase string in SNAKE_CASE format starting with RELEASE_,
+//      no consecutive underscores, and no leading digit. For example
+//      RELEASE_MY_PACKAGE_FLAG is a valid name, while MY_PACKAGE_FLAG, and
+//      RELEASE_MY_PACKAGE__FLAG are invalid.
+//
+// # package: package to which the flag belongs
+//
+//    format: lowercase strings in snake_case format, delimited by dots, no
+//      consecutive underscores and no leading digit in each string. For example
+//      com.android.mypackage is a valid name while com.android.myPackage,
+//      com.android.1mypackage are invalid
+
+message flag_declaration_artifact {
+  // The name of the flag.
+  // See # name for format detail
+  optional string name = 1;
+
+  // Namespace the flag belongs to (required)
+  // See # namespace for format detail
+  optional string namespace = 2;
+
+  // Text description of the flag's purpose.
+  optional string description = 3;
+
+  // reserve this for bug, if needed.
+  reserved 4;
+
+  // Where the flag was declared.
+  optional string declaration_path = 5;
+
+  // Workflow for this flag.
+  optional workflow workflow = 205;
+
+  // The container for this flag.  This overrides any default container given
+  // in the release_config_map message.
+  repeated string containers = 206;
+
+  // The package associated with this flag.
+  // (when Gantry is ready for it) optional string package = 207;
+  reserved 207;
+}
+
+message flag_declaration_artifacts {
+  // The artifacts
+  repeated flag_declaration_artifact flag_declaration_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
new file mode 100644
index 0000000..c63ea26
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_out.pb.go
@@ -0,0 +1,615 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        v3.21.12
+// source: build_flags_out.proto
+
+package release_config_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Tracepoint struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Path to declaration or value file relative to $TOP
+	Source *string `protobuf:"bytes,1,opt,name=source" json:"source,omitempty"`
+	Value  *Value  `protobuf:"bytes,201,opt,name=value" json:"value,omitempty"`
+}
+
+func (x *Tracepoint) Reset() {
+	*x = Tracepoint{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_out_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Tracepoint) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Tracepoint) ProtoMessage() {}
+
+func (x *Tracepoint) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_out_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Tracepoint.ProtoReflect.Descriptor instead.
+func (*Tracepoint) Descriptor() ([]byte, []int) {
+	return file_build_flags_out_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Tracepoint) GetSource() string {
+	if x != nil && x.Source != nil {
+		return *x.Source
+	}
+	return ""
+}
+
+func (x *Tracepoint) GetValue() *Value {
+	if x != nil {
+		return x.Value
+	}
+	return nil
+}
+
+type FlagArtifact struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The original declaration
+	FlagDeclaration *FlagDeclaration `protobuf:"bytes,1,opt,name=flag_declaration,json=flagDeclaration" json:"flag_declaration,omitempty"`
+	// Value for the flag
+	Value *Value `protobuf:"bytes,201,opt,name=value" json:"value,omitempty"`
+	// Trace of where the flag value was assigned.
+	Traces []*Tracepoint `protobuf:"bytes,8,rep,name=traces" json:"traces,omitempty"`
+}
+
+func (x *FlagArtifact) Reset() {
+	*x = FlagArtifact{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_out_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FlagArtifact) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FlagArtifact) ProtoMessage() {}
+
+func (x *FlagArtifact) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_out_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use FlagArtifact.ProtoReflect.Descriptor instead.
+func (*FlagArtifact) Descriptor() ([]byte, []int) {
+	return file_build_flags_out_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *FlagArtifact) GetFlagDeclaration() *FlagDeclaration {
+	if x != nil {
+		return x.FlagDeclaration
+	}
+	return nil
+}
+
+func (x *FlagArtifact) GetValue() *Value {
+	if x != nil {
+		return x.Value
+	}
+	return nil
+}
+
+func (x *FlagArtifact) GetTraces() []*Tracepoint {
+	if x != nil {
+		return x.Traces
+	}
+	return nil
+}
+
+type FlagArtifacts struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The artifacts
+	Flags []*FlagArtifact `protobuf:"bytes,1,rep,name=flags" json:"flags,omitempty"`
+}
+
+func (x *FlagArtifacts) Reset() {
+	*x = FlagArtifacts{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_out_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FlagArtifacts) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FlagArtifacts) ProtoMessage() {}
+
+func (x *FlagArtifacts) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_out_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 FlagArtifacts.ProtoReflect.Descriptor instead.
+func (*FlagArtifacts) Descriptor() ([]byte, []int) {
+	return file_build_flags_out_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *FlagArtifacts) GetFlags() []*FlagArtifact {
+	if x != nil {
+		return x.Flags
+	}
+	return nil
+}
+
+type ReleaseConfigArtifact struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the release config.
+	// See # name for format detail
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// Other names by which this release is known (for example, `next`)
+	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.
+	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.  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() {
+	*x = ReleaseConfigArtifact{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_out_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ReleaseConfigArtifact) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReleaseConfigArtifact) ProtoMessage() {}
+
+func (x *ReleaseConfigArtifact) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_out_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ReleaseConfigArtifact.ProtoReflect.Descriptor instead.
+func (*ReleaseConfigArtifact) Descriptor() ([]byte, []int) {
+	return file_build_flags_out_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *ReleaseConfigArtifact) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *ReleaseConfigArtifact) GetOtherNames() []string {
+	if x != nil {
+		return x.OtherNames
+	}
+	return nil
+}
+
+func (x *ReleaseConfigArtifact) GetFlags() []*FlagArtifact {
+	if x != nil {
+		return x.Flags
+	}
+	return nil
+}
+
+func (x *ReleaseConfigArtifact) GetAconfigValueSets() []string {
+	if x != nil {
+		return x.AconfigValueSets
+	}
+	return nil
+}
+
+func (x *ReleaseConfigArtifact) GetInherits() []string {
+	if x != nil {
+		return x.Inherits
+	}
+	return nil
+}
+
+func (x *ReleaseConfigArtifact) GetDirectories() []string {
+	if x != nil {
+		return x.Directories
+	}
+	return nil
+}
+
+func (x *ReleaseConfigArtifact) GetPriorStages() []string {
+	if x != nil {
+		return x.PriorStages
+	}
+	return nil
+}
+
+func (x *ReleaseConfigArtifact) GetValueDirectories() []string {
+	if x != nil {
+		return x.ValueDirectories
+	}
+	return nil
+}
+
+type ReleaseConfigsArtifact struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The active release config for this build.
+	ReleaseConfig *ReleaseConfigArtifact `protobuf:"bytes,1,opt,name=release_config,json=releaseConfig" json:"release_config,omitempty"`
+	// All other release configs defined for this TARGET_PRODUCT.
+	OtherReleaseConfigs []*ReleaseConfigArtifact `protobuf:"bytes,2,rep,name=other_release_configs,json=otherReleaseConfigs" json:"other_release_configs,omitempty"`
+	// Map of release_config_artifact.directories to release_config_map message.
+	ReleaseConfigMapsMap map[string]*ReleaseConfigMap `protobuf:"bytes,3,rep,name=release_config_maps_map,json=releaseConfigMapsMap" json:"release_config_maps_map,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+}
+
+func (x *ReleaseConfigsArtifact) Reset() {
+	*x = ReleaseConfigsArtifact{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_out_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ReleaseConfigsArtifact) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReleaseConfigsArtifact) ProtoMessage() {}
+
+func (x *ReleaseConfigsArtifact) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_out_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ReleaseConfigsArtifact.ProtoReflect.Descriptor instead.
+func (*ReleaseConfigsArtifact) Descriptor() ([]byte, []int) {
+	return file_build_flags_out_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *ReleaseConfigsArtifact) GetReleaseConfig() *ReleaseConfigArtifact {
+	if x != nil {
+		return x.ReleaseConfig
+	}
+	return nil
+}
+
+func (x *ReleaseConfigsArtifact) GetOtherReleaseConfigs() []*ReleaseConfigArtifact {
+	if x != nil {
+		return x.OtherReleaseConfigs
+	}
+	return nil
+}
+
+func (x *ReleaseConfigsArtifact) GetReleaseConfigMapsMap() map[string]*ReleaseConfigMap {
+	if x != nil {
+		return x.ReleaseConfigMapsMap
+	}
+	return nil
+}
+
+var File_build_flags_out_proto protoreflect.FileDescriptor
+
+var file_build_flags_out_proto_rawDesc = []byte{
+	0x0a, 0x15, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x6f, 0x75,
+	0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 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,
+	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,
+	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, 0x63, 0x0a, 0x0e, 0x66, 0x6c, 0x61,
+	0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x05, 0x66,
+	0x6c, 0x61, 0x67, 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, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x0e,
+	0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22, 0xdd,
+	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,
+	0x41, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x03, 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, 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, 0xe8,
+	0x03, 0x0a, 0x18, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x5c, 0x0a, 0x0e, 0x72,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65,
+	0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x0d, 0x72, 0x65, 0x6c, 0x65,
+	0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x69, 0x0a, 0x15, 0x6f, 0x74, 0x68,
+	0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52,
+	0x13, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x17, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
+	0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x5f, 0x6d, 0x61, 0x70,
+	0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x50, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x2e, 0x52,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73,
+	0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x1a, 0x79,
+	0x0a, 0x19, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d,
+	0x61, 0x70, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
+	0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x46, 0x0a,
+	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65,
+	0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70, 0x52, 0x05,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64,
+	0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_build_flags_out_proto_rawDescOnce sync.Once
+	file_build_flags_out_proto_rawDescData = file_build_flags_out_proto_rawDesc
+)
+
+func file_build_flags_out_proto_rawDescGZIP() []byte {
+	file_build_flags_out_proto_rawDescOnce.Do(func() {
+		file_build_flags_out_proto_rawDescData = protoimpl.X.CompressGZIP(file_build_flags_out_proto_rawDescData)
+	})
+	return file_build_flags_out_proto_rawDescData
+}
+
+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
+}
+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.flags:type_name -> android.release_config_proto.flag_artifact
+	1,  // 5: android.release_config_proto.release_config_artifact.flags: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
+	10, // [10:10] is the sub-list for method output_type
+	10, // [10:10] is the sub-list for method input_type
+	10, // [10:10] is the sub-list for extension type_name
+	10, // [10:10] is the sub-list for extension extendee
+	0,  // [0:10] is the sub-list for field type_name
+}
+
+func init() { file_build_flags_out_proto_init() }
+func file_build_flags_out_proto_init() {
+	if File_build_flags_out_proto != nil {
+		return
+	}
+	file_build_flags_src_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_build_flags_out_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Tracepoint); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_out_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FlagArtifact); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_out_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FlagArtifacts); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_out_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ReleaseConfigArtifact); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_out_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ReleaseConfigsArtifact); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_build_flags_out_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   6,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_build_flags_out_proto_goTypes,
+		DependencyIndexes: file_build_flags_out_proto_depIdxs,
+		MessageInfos:      file_build_flags_out_proto_msgTypes,
+	}.Build()
+	File_build_flags_out_proto = out.File
+	file_build_flags_out_proto_rawDesc = nil
+	file_build_flags_out_proto_goTypes = nil
+	file_build_flags_out_proto_depIdxs = nil
+}
diff --git a/cmd/release_config/release_config_proto/build_flags_out.proto b/cmd/release_config/release_config_proto/build_flags_out.proto
new file mode 100644
index 0000000..4dc84e9
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_out.proto
@@ -0,0 +1,111 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package android.release_config_proto;
+option go_package = "android/soong/release_config/release_config_proto";
+
+import "build_flags_src.proto";
+
+// This protobuf file defines messages used to represent the release config for
+// the android build system, delivered as a build artifact for use by tools such
+// as Gantry.
+//
+// The following format requirements apply across various message fields:
+//
+// # name: name of the flag
+//
+//    format: an uppercase string in SNAKE_CASE format starting with RELEASE_,
+//      no consecutive underscores, and no leading digit. For example
+//      RELEASE_MY_PACKAGE_FLAG is a valid name, while MY_PACKAGE_FLAG, and
+//      RELEASE_MY_PACKAGE__FLAG are invalid.
+//
+// # package: package to which the flag belongs
+//
+//    format: lowercase strings in snake_case format, delimited by dots, no
+//      consecutive underscores and no leading digit in each string. For example
+//      com.android.mypackage is a valid name while com.android.myPackage,
+//      com.android.1mypackage are invalid
+
+message tracepoint {
+  // Path to declaration or value file relative to $TOP
+  optional string source = 1;
+  optional value value = 201;
+}
+
+message flag_artifact {
+  // The original declaration
+  optional flag_declaration flag_declaration = 1;
+
+  // Value for the flag
+  optional value value = 201;
+
+  // Trace of where the flag value was assigned.
+  repeated tracepoint traces = 8;
+}
+
+message flag_artifacts {
+  // The artifacts
+  repeated flag_artifact flags = 1;
+  reserved "flag_artifacts";
+}
+
+message release_config_artifact {
+  // The name of the release config.
+  // See # name for format detail
+  optional string name = 1;
+
+  // Other names by which this release is known (for example, `next`)
+  repeated string other_names = 2;
+
+  // The complete set of build flags in this release config, after all
+  // inheritance and other processing is complete.
+  repeated flag_artifact flags = 3;
+  reserved "flag_artifacts";
+
+  // The (complete) list of aconfig_value_sets Soong modules to use.
+  repeated string aconfig_value_sets = 4;
+
+  // The names of the release_config_artifacts from which we inherited.
+  // Included for reference only.
+  repeated string inherits = 5;
+
+  // 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 {
+  // The active release config for this build.
+  optional release_config_artifact release_config = 1;
+
+  // All other release configs defined for this TARGET_PRODUCT.
+  repeated release_config_artifact 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;
+}
+
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
new file mode 100644
index 0000000..bc5f5c0
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_src.pb.go
@@ -0,0 +1,740 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.33.0
+// 	protoc        v3.21.12
+// source: build_flags_src.proto
+
+package release_config_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Value struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to Val:
+	//
+	//	*Value_UnspecifiedValue
+	//	*Value_StringValue
+	//	*Value_BoolValue
+	//	*Value_Obsolete
+	Val isValue_Val `protobuf_oneof:"val"`
+}
+
+func (x *Value) Reset() {
+	*x = Value{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_src_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Value) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Value) ProtoMessage() {}
+
+func (x *Value) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_src_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Value.ProtoReflect.Descriptor instead.
+func (*Value) Descriptor() ([]byte, []int) {
+	return file_build_flags_src_proto_rawDescGZIP(), []int{0}
+}
+
+func (m *Value) GetVal() isValue_Val {
+	if m != nil {
+		return m.Val
+	}
+	return nil
+}
+
+func (x *Value) GetUnspecifiedValue() bool {
+	if x, ok := x.GetVal().(*Value_UnspecifiedValue); ok {
+		return x.UnspecifiedValue
+	}
+	return false
+}
+
+func (x *Value) GetStringValue() string {
+	if x, ok := x.GetVal().(*Value_StringValue); ok {
+		return x.StringValue
+	}
+	return ""
+}
+
+func (x *Value) GetBoolValue() bool {
+	if x, ok := x.GetVal().(*Value_BoolValue); ok {
+		return x.BoolValue
+	}
+	return false
+}
+
+func (x *Value) GetObsolete() bool {
+	if x, ok := x.GetVal().(*Value_Obsolete); ok {
+		return x.Obsolete
+	}
+	return false
+}
+
+type isValue_Val interface {
+	isValue_Val()
+}
+
+type Value_UnspecifiedValue struct {
+	UnspecifiedValue bool `protobuf:"varint,200,opt,name=unspecified_value,json=unspecifiedValue,oneof"`
+}
+
+type Value_StringValue struct {
+	StringValue string `protobuf:"bytes,201,opt,name=string_value,json=stringValue,oneof"`
+}
+
+type Value_BoolValue struct {
+	BoolValue bool `protobuf:"varint,202,opt,name=bool_value,json=boolValue,oneof"`
+}
+
+type Value_Obsolete struct {
+	// If true, the flag is obsolete.  Assigning it further will be flagged.
+	Obsolete bool `protobuf:"varint,203,opt,name=obsolete,oneof"`
+}
+
+func (*Value_UnspecifiedValue) isValue_Val() {}
+
+func (*Value_StringValue) isValue_Val() {}
+
+func (*Value_BoolValue) isValue_Val() {}
+
+func (*Value_Obsolete) isValue_Val() {}
+
+// The proto used in the source tree.
+type FlagDeclaration struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the flag.
+	// See # name for format detail
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// Namespace the flag belongs to (required)
+	// See # namespace for format detail
+	Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"`
+	// Text description of the flag's purpose.
+	Description *string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
+	// Value for the flag
+	Value *Value `protobuf:"bytes,201,opt,name=value" json:"value,omitempty"`
+	// Workflow for this flag.
+	Workflow *Workflow `protobuf:"varint,205,opt,name=workflow,enum=android.release_config_proto.Workflow" json:"workflow,omitempty"`
+	// The container for this flag.  This overrides any default container given
+	// in the release_config_map message.
+	Containers []string `protobuf:"bytes,206,rep,name=containers" json:"containers,omitempty"`
+}
+
+func (x *FlagDeclaration) Reset() {
+	*x = FlagDeclaration{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_src_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FlagDeclaration) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FlagDeclaration) ProtoMessage() {}
+
+func (x *FlagDeclaration) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_src_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use FlagDeclaration.ProtoReflect.Descriptor instead.
+func (*FlagDeclaration) Descriptor() ([]byte, []int) {
+	return file_build_flags_src_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *FlagDeclaration) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *FlagDeclaration) GetNamespace() string {
+	if x != nil && x.Namespace != nil {
+		return *x.Namespace
+	}
+	return ""
+}
+
+func (x *FlagDeclaration) GetDescription() string {
+	if x != nil && x.Description != nil {
+		return *x.Description
+	}
+	return ""
+}
+
+func (x *FlagDeclaration) GetValue() *Value {
+	if x != nil {
+		return x.Value
+	}
+	return nil
+}
+
+func (x *FlagDeclaration) GetWorkflow() Workflow {
+	if x != nil && x.Workflow != nil {
+		return *x.Workflow
+	}
+	return Workflow_Workflow_Unspecified
+}
+
+func (x *FlagDeclaration) GetContainers() []string {
+	if x != nil {
+		return x.Containers
+	}
+	return nil
+}
+
+type FlagValue struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Name of the flag.
+	// See # name for format detail
+	Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
+	// Value for the flag
+	Value *Value `protobuf:"bytes,201,opt,name=value" json:"value,omitempty"`
+	// If true, the flag is completely removed from the release config as if
+	// never declared.
+	Redacted *bool `protobuf:"varint,202,opt,name=redacted" json:"redacted,omitempty"`
+}
+
+func (x *FlagValue) Reset() {
+	*x = FlagValue{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_src_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FlagValue) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FlagValue) ProtoMessage() {}
+
+func (x *FlagValue) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_src_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 FlagValue.ProtoReflect.Descriptor instead.
+func (*FlagValue) Descriptor() ([]byte, []int) {
+	return file_build_flags_src_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *FlagValue) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *FlagValue) GetValue() *Value {
+	if x != nil {
+		return x.Value
+	}
+	return nil
+}
+
+func (x *FlagValue) GetRedacted() bool {
+	if x != nil && x.Redacted != nil {
+		return *x.Redacted
+	}
+	return false
+}
+
+// This replaces $(call declare-release-config).
+type ReleaseConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the release config.
+	// See # name for format detail
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// From which other release configs does this one inherit?
+	Inherits []string `protobuf:"bytes,2,rep,name=inherits" json:"inherits,omitempty"`
+	// List of names of the aconfig_value_set soong module(s) for this
+	// contribution.
+	AconfigValueSets []string `protobuf:"bytes,3,rep,name=aconfig_value_sets,json=aconfigValueSets" json:"aconfig_value_sets,omitempty"`
+	// Only aconfig flags are allowed in this release config.
+	AconfigFlagsOnly *bool `protobuf:"varint,4,opt,name=aconfig_flags_only,json=aconfigFlagsOnly" json:"aconfig_flags_only,omitempty"`
+	// Prior stage(s) for flag advancement (during development).
+	// Once a flag has met criteria in a prior stage, it can advance to this one.
+	PriorStages []string `protobuf:"bytes,5,rep,name=prior_stages,json=priorStages" json:"prior_stages,omitempty"`
+}
+
+func (x *ReleaseConfig) Reset() {
+	*x = ReleaseConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_src_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ReleaseConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReleaseConfig) ProtoMessage() {}
+
+func (x *ReleaseConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_src_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ReleaseConfig.ProtoReflect.Descriptor instead.
+func (*ReleaseConfig) Descriptor() ([]byte, []int) {
+	return file_build_flags_src_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *ReleaseConfig) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *ReleaseConfig) GetInherits() []string {
+	if x != nil {
+		return x.Inherits
+	}
+	return nil
+}
+
+func (x *ReleaseConfig) GetAconfigValueSets() []string {
+	if x != nil {
+		return x.AconfigValueSets
+	}
+	return nil
+}
+
+func (x *ReleaseConfig) GetAconfigFlagsOnly() bool {
+	if x != nil && x.AconfigFlagsOnly != nil {
+		return *x.AconfigFlagsOnly
+	}
+	return false
+}
+
+func (x *ReleaseConfig) GetPriorStages() []string {
+	if x != nil {
+		return x.PriorStages
+	}
+	return nil
+}
+
+// Any aliases.  These are used for continuous integration builder config.
+type ReleaseAlias struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the alias.
+	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// The release that `name` is an alias for.
+	Target *string `protobuf:"bytes,2,opt,name=target" json:"target,omitempty"`
+}
+
+func (x *ReleaseAlias) Reset() {
+	*x = ReleaseAlias{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_src_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ReleaseAlias) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReleaseAlias) ProtoMessage() {}
+
+func (x *ReleaseAlias) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_src_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ReleaseAlias.ProtoReflect.Descriptor instead.
+func (*ReleaseAlias) Descriptor() ([]byte, []int) {
+	return file_build_flags_src_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *ReleaseAlias) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
+	}
+	return ""
+}
+
+func (x *ReleaseAlias) GetTarget() string {
+	if x != nil && x.Target != nil {
+		return *x.Target
+	}
+	return ""
+}
+
+// This provides the data from release_config_map.mk
+type ReleaseConfigMap struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Any aliases.
+	Aliases []*ReleaseAlias `protobuf:"bytes,1,rep,name=aliases" json:"aliases,omitempty"`
+	// Description of this map and its intended use.
+	Description *string `protobuf:"bytes,2,opt,name=description" json:"description,omitempty"`
+	// The default container for flags declared here.
+	DefaultContainers []string `protobuf:"bytes,3,rep,name=default_containers,json=defaultContainers" json:"default_containers,omitempty"`
+}
+
+func (x *ReleaseConfigMap) Reset() {
+	*x = ReleaseConfigMap{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_flags_src_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ReleaseConfigMap) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReleaseConfigMap) ProtoMessage() {}
+
+func (x *ReleaseConfigMap) ProtoReflect() protoreflect.Message {
+	mi := &file_build_flags_src_proto_msgTypes[5]
+	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 ReleaseConfigMap.ProtoReflect.Descriptor instead.
+func (*ReleaseConfigMap) Descriptor() ([]byte, []int) {
+	return file_build_flags_src_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *ReleaseConfigMap) GetAliases() []*ReleaseAlias {
+	if x != nil {
+		return x.Aliases
+	}
+	return nil
+}
+
+func (x *ReleaseConfigMap) GetDescription() string {
+	if x != nil && x.Description != nil {
+		return *x.Description
+	}
+	return ""
+}
+
+func (x *ReleaseConfigMap) GetDefaultContainers() []string {
+	if x != nil {
+		return x.DefaultContainers
+	}
+	return nil
+}
+
+var File_build_flags_src_proto protoreflect.FileDescriptor
+
+var file_build_flags_src_proto_rawDesc = []byte{
+	0x0a, 0x15, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x73, 0x72,
+	0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x61,
+	0x67, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+	0xa5, 0x01, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2e, 0x0a, 0x11, 0x75, 0x6e, 0x73,
+	0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc8,
+	0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x10, 0x75, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69,
+	0x66, 0x69, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x24, 0x0a, 0x0c, 0x73, 0x74, 0x72,
+	0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
+	0x20, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xca, 0x01,
+	0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75,
+	0x65, 0x12, 0x1d, 0x0a, 0x08, 0x6f, 0x62, 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x18, 0xcb, 0x01,
+	0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x08, 0x6f, 0x62, 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65,
+	0x42, 0x05, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x96, 0x02, 0x0a, 0x10, 0x66, 0x6c, 0x61, 0x67,
+	0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x20,
+	0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x43, 0x0a, 0x08,
+	0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
+	0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x77,
+	0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
+	0x77, 0x12, 0x1f, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18,
+	0xce, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
+	0x72, 0x73, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x06, 0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01,
+	0x22, 0x79, 0x0a, 0x0a, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b,
+	0x0a, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28,
+	0x08, 0x52, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64, 0x22, 0xbf, 0x01, 0x0a, 0x0e,
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x02,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x2c,
+	0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f,
+	0x73, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12,
+	0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5f, 0x6f, 0x6e,
+	0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72,
+	0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x22, 0x3b, 0x0a,
+	0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x12, 0x72,
+	0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61,
+	0x70, 0x12, 0x45, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c,
+	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x52,
+	0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
+	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65,
+	0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73,
+	0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43,
+	0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64,
+	0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61,
+	0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_build_flags_src_proto_rawDescOnce sync.Once
+	file_build_flags_src_proto_rawDescData = file_build_flags_src_proto_rawDesc
+)
+
+func file_build_flags_src_proto_rawDescGZIP() []byte {
+	file_build_flags_src_proto_rawDescOnce.Do(func() {
+		file_build_flags_src_proto_rawDescData = protoimpl.X.CompressGZIP(file_build_flags_src_proto_rawDescData)
+	})
+	return file_build_flags_src_proto_rawDescData
+}
+
+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
+}
+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
+	4, // [4:4] is the sub-list for method output_type
+	4, // [4:4] is the sub-list for method input_type
+	4, // [4:4] is the sub-list for extension type_name
+	4, // [4:4] is the sub-list for extension extendee
+	0, // [0:4] is the sub-list for field type_name
+}
+
+func init() { file_build_flags_src_proto_init() }
+func file_build_flags_src_proto_init() {
+	if File_build_flags_src_proto != nil {
+		return
+	}
+	file_build_flags_common_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_build_flags_src_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Value); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_src_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FlagDeclaration); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_src_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FlagValue); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_src_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ReleaseConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_src_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ReleaseAlias); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_flags_src_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ReleaseConfigMap); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_build_flags_src_proto_msgTypes[0].OneofWrappers = []interface{}{
+		(*Value_UnspecifiedValue)(nil),
+		(*Value_StringValue)(nil),
+		(*Value_BoolValue)(nil),
+		(*Value_Obsolete)(nil),
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_build_flags_src_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   6,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_build_flags_src_proto_goTypes,
+		DependencyIndexes: file_build_flags_src_proto_depIdxs,
+		MessageInfos:      file_build_flags_src_proto_msgTypes,
+	}.Build()
+	File_build_flags_src_proto = out.File
+	file_build_flags_src_proto_rawDesc = nil
+	file_build_flags_src_proto_goTypes = nil
+	file_build_flags_src_proto_depIdxs = nil
+}
diff --git a/cmd/release_config/release_config_proto/build_flags_src.proto b/cmd/release_config/release_config_proto/build_flags_src.proto
new file mode 100644
index 0000000..4fad478
--- /dev/null
+++ b/cmd/release_config/release_config_proto/build_flags_src.proto
@@ -0,0 +1,146 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package android.release_config_proto;
+option go_package = "android/soong/release_config/release_config_proto";
+
+import "build_flags_common.proto";
+
+// This protobuf file defines messages used to represent the build flags used by
+// a release in a more human-editable form.  It is used for on-disk files in the
+// source tree.
+//
+// The following format requirements apply across various message fields:
+//
+// # name: name of the flag
+//
+//    format: an uppercase string in SNAKE_CASE format starting with RELEASE_,
+//      no consecutive underscores, and no leading digit. For example
+//      RELEASE_MY_PACKAGE_FLAG is a valid name, while MY_PACKAGE_FLAG, and
+//      RELEASE_MY_PACKAGE__FLAG are invalid.
+//
+// # namespace: namespace the flag belongs to
+//
+//    format: a lowercase string in snake_case format, no consecutive underscores, and no leading
+//      digit. For example android_bar_system
+//
+// # package: package to which the flag belongs
+//
+//    format: lowercase strings in snake_case format, delimited by dots, no
+//      consecutive underscores and no leading digit in each string. For example
+//      com.android.mypackage is a valid name while com.android.myPackage,
+//      com.android.1mypackage are invalid
+
+message value {
+  oneof val {
+    bool unspecified_value = 200;
+    string string_value = 201;
+    bool bool_value = 202;
+    // If true, the flag is obsolete.  Assigning it further will be flagged.
+    bool obsolete = 203;
+  }
+}
+
+// The proto used in the source tree.
+message flag_declaration {
+  // The name of the flag.
+  // See # name for format detail
+  optional string name = 1;
+
+  // Namespace the flag belongs to (required)
+  // See # namespace for format detail
+  optional string namespace = 2;
+
+  // Text description of the flag's purpose.
+  optional string description = 3;
+
+  // reserve this for bug, if needed.
+  reserved 4;
+
+  // Value for the flag
+  optional value value = 201;
+
+  // Workflow for this flag.
+  optional workflow workflow = 205;
+
+  // The container for this flag.  This overrides any default container given
+  // in the release_config_map message.
+  repeated string containers = 206;
+
+  // The package associated with this flag.
+  // (when Gantry is ready for it) optional string package = 207;
+  reserved 207;
+}
+
+message flag_value {
+  // Name of the flag.
+  // See # name for format detail
+  optional string name = 2;
+
+  // Value for the flag
+  optional value value = 201;
+
+  // If true, the flag is completely removed from the release config as if
+  // never declared.
+  optional bool redacted = 202;
+}
+
+// This replaces $(call declare-release-config).
+message release_config {
+  // The name of the release config.
+  // See # name for format detail
+  optional string name = 1;
+
+  // From which other release configs does this one inherit?
+  repeated string inherits = 2;
+
+  // List of names of the aconfig_value_set soong module(s) for this
+  // contribution.
+  repeated string aconfig_value_sets = 3;
+
+  // Only aconfig flags are allowed in this release config.
+  optional bool aconfig_flags_only = 4;
+
+  // Prior stage(s) for flag advancement (during development).
+  // Once a flag has met criteria in a prior stage, it can advance to this one.
+  repeated string prior_stages = 5;
+}
+
+// Any aliases.  These are used for continuous integration builder config.
+message release_alias {
+  // The name of the alias.
+  optional string name = 1;
+
+  // The release that `name` is an alias for.
+  optional string target = 2;
+}
+
+// This provides the data from release_config_map.mk
+message release_config_map {
+  // Any aliases.
+  repeated release_alias aliases = 1;
+
+  // Description of this map and its intended use.
+  optional string description = 2;
+
+  // The default container for flags declared here.
+  repeated string default_containers = 3;
+
+  // If needed, we can add these fields instead of hardcoding the location.
+  // Flag declarations: `flag_declarations/*.textproto`
+  // Release config contributions: `release_configs/*.textproto`
+  // Flag values: `flag_values/{RELEASE_NAME}/*.textproto`
+}
diff --git a/cmd/release_config/release_config_proto/regen.sh b/cmd/release_config/release_config_proto/regen.sh
new file mode 100644
index 0000000..23e3115
--- /dev/null
+++ b/cmd/release_config/release_config_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. build_flags_src.proto build_flags_out.proto build_flags_common.proto build_flags_declarations.proto
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index d64010e..a8be7ec 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -16,6 +16,7 @@
 
 import (
 	"bytes"
+	"encoding/json"
 	"errors"
 	"flag"
 	"fmt"
@@ -28,11 +29,11 @@
 	"android/soong/android/allowlists"
 	"android/soong/bp2build"
 	"android/soong/shared"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/deptools"
 	"github.com/google/blueprint/metrics"
+	"github.com/google/blueprint/proptools"
 	androidProtobuf "google.golang.org/protobuf/android"
 )
 
@@ -49,6 +50,14 @@
 	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")
@@ -82,6 +91,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
@@ -98,7 +108,6 @@
 	ctx := android.NewContext(configuration)
 	ctx.SetNameInterface(newNameResolver(configuration))
 	ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
-	ctx.AddIncludeTags(configuration.IncludeTags()...)
 	ctx.AddSourceRootDirs(configuration.SourceRootDirs()...)
 	return ctx
 }
@@ -108,7 +117,7 @@
 	case "always":
 		return true
 	case "depend":
-		if _, err := os.Stat(filepath.Join(ctx.Config().OutDir(), ".ninja_log")); errors.Is(err, os.ErrNotExist) {
+		if _, err := os.Stat(filepath.Join(topDir, ctx.Config().OutDir(), ".ninja_log")); errors.Is(err, os.ErrNotExist) {
 			return true
 		}
 	}
@@ -219,6 +228,60 @@
 	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.
+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 {
 	ctx.EventHandler.Begin("soong_build")
@@ -320,8 +383,26 @@
 	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)
+
+	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)
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index fe3f8f7..2d3156a 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -155,7 +155,6 @@
 
 	// Create a new trace file writer, making it log events to the log instance.
 	trace := tracer.New(log)
-	defer trace.Close()
 
 	// Create a new Status instance, which manages action counts and event output channels.
 	stat := &status.Status{}
@@ -194,14 +193,29 @@
 	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
 	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
 	soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
+	buildTraceFile := filepath.Join(logsDir, c.logsPrefix+"build.trace.gz")
 
 	metricsFiles := []string{
 		buildErrorFile,        // build error strings
 		rbeMetricsFile,        // high level metrics related to remote build execution.
 		soongMetricsFile,      // high level metrics related to this build system.
 		soongBuildMetricsFile, // high level metrics related to soong build
+		buildTraceFile,
 	}
 
+	defer func() {
+		stat.Finish()
+		criticalPath.WriteToMetrics(met)
+		met.Dump(soongMetricsFile)
+		if !config.SkipMetricsUpload() {
+			build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
+		}
+	}()
+
+	// This has to come after the metrics uploading function, so that
+	// build.trace.gz is closed and ready for upload.
+	defer trace.Close()
+
 	os.MkdirAll(logsDir, 0777)
 
 	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
@@ -222,16 +236,7 @@
 		config = freshConfig()
 	}
 
-	defer func() {
-		stat.Finish()
-		criticalPath.WriteToMetrics(met)
-		met.Dump(soongMetricsFile)
-		if !config.SkipMetricsUpload() {
-			build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
-		}
-	}()
 	c.run(buildCtx, config, args)
-
 }
 
 // This function must not modify config, since product config may cause us to recreate the config,
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 57c7ae8..af1d33d 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -323,6 +323,7 @@
 		} else if clc.Host == hostPath && clc.Device == devicePath {
 			// Ok, the same library with the same paths. Don't re-add it, but don't raise an error
 			// either, as the same library may be reachable via different transitional dependencies.
+			clc.Optional = clc.Optional && optional
 			return nil
 		} else {
 			// Fail, as someone is trying to add the same library with different paths. This likely
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 04bc61d..201515f 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -52,7 +52,7 @@
 // GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
 // ModuleConfig.  The produced files and their install locations will be available through rule.Installs().
 func GenerateDexpreoptRule(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
-	global *GlobalConfig, module *ModuleConfig, productPackages android.Path) (
+	global *GlobalConfig, module *ModuleConfig, productPackages android.Path, copyApexSystemServerJarDex bool) (
 	rule *android.RuleBuilder, err error) {
 
 	defer func() {
@@ -94,7 +94,7 @@
 
 			for archIdx, _ := range module.Archs {
 				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage,
-					generateDM, productPackages)
+					generateDM, productPackages, copyApexSystemServerJarDex)
 			}
 		}
 	}
@@ -231,7 +231,7 @@
 
 func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
 	global *GlobalConfig, module *ModuleConfig, rule *android.RuleBuilder, archIdx int,
-	profile android.WritablePath, appImage bool, generateDM bool, productPackages android.Path) {
+	profile android.WritablePath, appImage bool, generateDM bool, productPackages android.Path, copyApexSystemServerJarDex bool) {
 
 	arch := module.Archs[archIdx]
 
@@ -277,7 +277,7 @@
 			clcTarget = append(clcTarget, GetSystemServerDexLocation(ctx, global, lib))
 		}
 
-		if DexpreoptRunningInSoong {
+		if DexpreoptRunningInSoong && copyApexSystemServerJarDex {
 			// Copy the system server jar to a predefined location where dex2oat will find it.
 			dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
 			rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
@@ -527,12 +527,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_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index 8033b48..7512005 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -205,8 +205,9 @@
 			panic(err)
 		}
 	}
+	cpApexSscpServerJar := false // dexpreopt_gen operates on make modules, and since sscp libraries are in soong, this should be a noop
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
-		ctx, globalSoong, global, module, android.PathForTesting(productPackagesPath))
+		ctx, globalSoong, global, module, android.PathForTesting(productPackagesPath), cpApexSscpServerJar)
 	if err != nil {
 		panic(err)
 	}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 7071f3e..6f7d3bb 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -101,7 +101,7 @@
 	module := testSystemModuleConfig(ctx, "test")
 	productPackages := android.PathForTesting("product_packages.txt")
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -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"},
 			},
 		},
 	}
@@ -161,7 +161,7 @@
 	for _, test := range tests {
 		global.PatternsOnSystemOther = test.patterns
 		for _, mt := range test.moduleTests {
-			rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module, productPackages)
+			rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module, productPackages, true)
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -181,6 +181,11 @@
 }
 
 func TestDexPreoptApexSystemServerJars(t *testing.T) {
+	// modify the global variable for test
+	var oldDexpreoptRunningInSoong = DexpreoptRunningInSoong
+	DexpreoptRunningInSoong = true
+
+	// test begin
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
 	globalSoong := globalSoongConfigForTests(ctx)
@@ -191,7 +196,7 @@
 	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -202,6 +207,18 @@
 	}
 
 	android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
+
+	android.AssertStringListContains(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
+
+	// rule with apex sscp cp as false
+	rule, err = GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	android.AssertStringListDoesNotContain(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
+
+	// cleanup the global variable for test
+	DexpreoptRunningInSoong = oldDexpreoptRunningInSoong
 }
 
 func TestDexPreoptStandaloneSystemServerJars(t *testing.T) {
@@ -215,7 +232,7 @@
 	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"platform:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -239,7 +256,7 @@
 	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"system_ext:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -263,7 +280,7 @@
 	global.ApexStandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -286,7 +303,7 @@
 
 	module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
 	if err != nil {
 		t.Fatal(err)
 	}
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/etc/Android.bp b/etc/Android.bp
index cefd717..f02c12a 100644
--- a/etc/Android.bp
+++ b/etc/Android.bp
@@ -9,16 +9,14 @@
         "blueprint",
         "soong",
         "soong-android",
-        "soong-snapshot",
     ],
     srcs: [
-        "prebuilt_etc.go",
-        "snapshot_etc.go",
         "install_symlink.go",
+        "otacerts_zip.go",
+        "prebuilt_etc.go",
     ],
     testSrcs: [
         "prebuilt_etc_test.go",
-        "snapshot_etc_test.go",
         "install_symlink_test.go",
     ],
     pluginFor: ["soong_build"],
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 7642378..fc6d1f7 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -28,7 +28,6 @@
 // various `prebuilt_*` mutators.
 
 import (
-	"encoding/json"
 	"fmt"
 	"path/filepath"
 	"strings"
@@ -36,7 +35,6 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
-	"android/soong/snapshot"
 )
 
 var pctx = android.NewPackageContext("android/soong/etc")
@@ -46,18 +44,23 @@
 func init() {
 	pctx.Import("android/soong/android")
 	RegisterPrebuiltEtcBuildComponents(android.InitRegistrationContext)
-	snapshot.RegisterSnapshotAction(generatePrebuiltSnapshot)
 }
 
 func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) {
 	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)
 	ctx.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
+	ctx.RegisterModuleType("prebuilt_usr_hyphendata", PrebuiltUserHyphenDataFactory)
+	ctx.RegisterModuleType("prebuilt_usr_keylayout", PrebuiltUserKeyLayoutFactory)
+	ctx.RegisterModuleType("prebuilt_usr_keychars", PrebuiltUserKeyCharsFactory)
+	ctx.RegisterModuleType("prebuilt_usr_idc", PrebuiltUserIdcFactory)
 	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
+	ctx.RegisterModuleType("prebuilt_overlay", PrebuiltOverlayFactory)
 	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
 	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
 	ctx.RegisterModuleType("prebuilt_rfsa", PrebuiltRFSAFactory)
@@ -71,10 +74,15 @@
 
 type prebuiltEtcProperties struct {
 	// Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
-	Src *string `android:"path,arch_variant"`
+	// Mutually exclusive with srcs.
+	Src proptools.Configurable[string] `android:"path,arch_variant,replace_instead_of_append"`
+
+	// Source files of this prebuilt. Can reference a genrule type module with the ":module" syntax.
+	// Mutually exclusive with src. When used, filename_from_src is set to true.
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	// Optional name for the installed file. If unspecified, name of the module is used as the file
-	// name.
+	// name. Only available when using a single source (src).
 	Filename *string `android:"arch_variant"`
 
 	// When set to true, and filename property is not set, the name for the installed file
@@ -118,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
 
@@ -126,24 +143,22 @@
 
 	// Returns the sub install directory relative to BaseDir().
 	SubDir() string
-
-	// Returns an android.OutputPath to the intermeidate file, which is the renamed prebuilt source
-	// file.
-	OutputFile() android.OutputPath
 }
 
 type PrebuiltEtc struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
 
-	snapshot.VendorSnapshotModuleInterface
-	snapshot.RecoverySnapshotModuleInterface
+	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
 
-	sourceFilePath android.Path
-	outputFilePath android.OutputPath
+	sourceFilePaths android.Paths
+	outputFilePaths android.OutputPaths
 	// The base install location, e.g. "etc" for prebuilt_etc, "usr/share" for prebuilt_usr_share.
 	installDirBase               string
 	installDirBase64             string
@@ -154,10 +169,9 @@
 	installDirPath         android.InstallPath
 	additionalDependencies *android.Paths
 
-	makeClass string
+	usedSrcsProperty bool
 
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
+	makeClass string
 }
 
 type Defaults struct {
@@ -217,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()
@@ -234,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()
 }
@@ -242,11 +268,14 @@
 	return nil
 }
 
-func (p *PrebuiltEtc) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+func (p *PrebuiltEtc) SetImageVariation(ctx android.BaseModuleContext, variation string) {
 }
 
 func (p *PrebuiltEtc) SourceFilePath(ctx android.ModuleContext) android.Path {
-	return android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
+	if len(p.properties.Srcs.GetOrDefault(ctx, nil)) > 0 {
+		panic(fmt.Errorf("SourceFilePath not available on multi-source prebuilt %q", p.Name()))
+	}
+	return android.PathForModuleSrc(ctx, p.properties.Src.GetOrDefault(ctx, ""))
 }
 
 func (p *PrebuiltEtc) InstallDirPath() android.InstallPath {
@@ -260,18 +289,10 @@
 }
 
 func (p *PrebuiltEtc) OutputFile() android.OutputPath {
-	return p.outputFilePath
-}
-
-var _ android.OutputFileProducer = (*PrebuiltEtc)(nil)
-
-func (p *PrebuiltEtc) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{p.outputFilePath}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	if p.usedSrcsProperty {
+		panic(fmt.Errorf("OutputFile not available on multi-source prebuilt %q", p.Name()))
 	}
+	return p.outputFilePaths[0]
 }
 
 func (p *PrebuiltEtc) SubDir() string {
@@ -293,58 +314,7 @@
 	return p.ModuleBase.InstallInVendor()
 }
 
-func (p *PrebuiltEtc) ExcludeFromVendorSnapshot() bool {
-	return false
-}
-
-func (p *PrebuiltEtc) ExcludeFromRecoverySnapshot() bool {
-	return false
-}
-
-func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	filename := proptools.String(p.properties.Filename)
-	filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
-	if p.properties.Src != nil {
-		p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
-
-		// Determine the output file basename.
-		// If Filename is set, use the name specified by the property.
-		// If Filename_from_src is set, use the source file name.
-		// Otherwise use the module name.
-		if filename != "" {
-			if filenameFromSrc {
-				ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
-				return
-			}
-		} else if filenameFromSrc {
-			filename = p.sourceFilePath.Base()
-		} else {
-			filename = ctx.ModuleName()
-		}
-	} else if ctx.Config().AllowMissingDependencies() {
-		// If no srcs was set and AllowMissingDependencies is enabled then
-		// mark the module as missing dependencies and set a fake source path
-		// and file name.
-		ctx.AddMissingDependencies([]string{"MISSING_PREBUILT_SRC_FILE"})
-		p.sourceFilePath = android.PathForModuleSrc(ctx)
-		if filename == "" {
-			filename = ctx.ModuleName()
-		}
-	} else {
-		ctx.PropertyErrorf("src", "missing prebuilt source file")
-		return
-	}
-
-	if strings.Contains(filename, "/") {
-		ctx.PropertyErrorf("filename", "filename cannot contain separator '/'")
-		return
-	}
-
-	// Check that `sub_dir` and `relative_install_path` are not set at the same time.
-	if p.subdirProperties.Sub_dir != nil && p.subdirProperties.Relative_install_path != nil {
-		ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
-	}
-
+func (p *PrebuiltEtc) installBaseDir(ctx android.ModuleContext) string {
 	// If soc install dir was specified and SOC specific is set, set the installDirPath to the
 	// specified socInstallDirBase.
 	installBaseDir := p.installDirBase
@@ -357,47 +327,142 @@
 	if p.installAvoidMultilibConflict && !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
 		installBaseDir = filepath.Join(installBaseDir, ctx.Arch().ArchType.String())
 	}
+	return installBaseDir
+}
 
-	p.installDirPath = android.PathForModuleInstall(ctx, installBaseDir, p.SubDir())
+func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var installs []installProperties
 
-	// Call InstallFile even when uninstallable to make the module included in the package
-	ip := installProperties{
-		installable:    p.Installable(),
-		filename:       filename,
-		sourceFilePath: p.sourceFilePath,
-		symlinks:       p.properties.Symlinks,
+	srcProperty := p.properties.Src.Get(ctx)
+	srcsProperty := p.properties.Srcs.GetOrDefault(ctx, nil)
+	if srcProperty.IsPresent() && len(srcsProperty) > 0 {
+		ctx.PropertyErrorf("src", "src is set. Cannot set srcs")
 	}
-	p.addInstallRules(ctx, ip)
-	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
+
+	// Check that `sub_dir` and `relative_install_path` are not set at the same time.
+	if p.subdirProperties.Sub_dir != nil && p.subdirProperties.Relative_install_path != nil {
+		ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
+	}
+	p.installDirPath = android.PathForModuleInstall(ctx, p.installBaseDir(ctx), p.SubDir())
+
+	filename := proptools.String(p.properties.Filename)
+	filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
+	if srcProperty.IsPresent() {
+		p.sourceFilePaths = android.PathsForModuleSrc(ctx, []string{srcProperty.Get()})
+		// If the source was not found, set a fake source path to
+		// support AllowMissingDependencies executions.
+		if len(p.sourceFilePaths) == 0 {
+			p.sourceFilePaths = android.Paths{android.PathForModuleSrc(ctx)}
+		}
+
+		// Determine the output file basename.
+		// If Filename is set, use the name specified by the property.
+		// If Filename_from_src is set, use the source file name.
+		// Otherwise use the module name.
+		if filename != "" {
+			if filenameFromSrc {
+				ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+				return
+			}
+		} else if filenameFromSrc {
+			filename = p.sourceFilePaths[0].Base()
+		} else {
+			filename = ctx.ModuleName()
+		}
+		if strings.Contains(filename, "/") {
+			ctx.PropertyErrorf("filename", "filename cannot contain separator '/'")
+			return
+		}
+		p.outputFilePaths = android.OutputPaths{android.PathForModuleOut(ctx, filename).OutputPath}
+
+		ip := installProperties{
+			filename:       filename,
+			sourceFilePath: p.sourceFilePaths[0],
+			outputFilePath: p.outputFilePaths[0],
+			installDirPath: p.installDirPath,
+			symlinks:       p.properties.Symlinks,
+		}
+		installs = append(installs, ip)
+	} else if len(srcsProperty) > 0 {
+		p.usedSrcsProperty = true
+		if filename != "" {
+			ctx.PropertyErrorf("filename", "filename cannot be set when using srcs")
+		}
+		if len(p.properties.Symlinks) > 0 {
+			ctx.PropertyErrorf("symlinks", "symlinks cannot be set when using srcs")
+		}
+		if p.properties.Filename_from_src != nil {
+			ctx.PropertyErrorf("filename_from_src", "filename_from_src is implicitly set to true when using srcs")
+		}
+		p.sourceFilePaths = android.PathsForModuleSrc(ctx, srcsProperty)
+		for _, src := range p.sourceFilePaths {
+			filename := src.Base()
+			output := android.PathForModuleOut(ctx, filename).OutputPath
+			ip := installProperties{
+				filename:       filename,
+				sourceFilePath: src,
+				outputFilePath: output,
+				installDirPath: p.installDirPath,
+			}
+			p.outputFilePaths = append(p.outputFilePaths, output)
+			installs = append(installs, ip)
+		}
+	} else if ctx.Config().AllowMissingDependencies() {
+		// If no srcs was set and AllowMissingDependencies is enabled then
+		// mark the module as missing dependencies and set a fake source path
+		// and file name.
+		ctx.AddMissingDependencies([]string{"MISSING_PREBUILT_SRC_FILE"})
+		p.sourceFilePaths = android.Paths{android.PathForModuleSrc(ctx)}
+		if filename == "" {
+			filename = ctx.ModuleName()
+		}
+		p.outputFilePaths = android.OutputPaths{android.PathForModuleOut(ctx, filename).OutputPath}
+		ip := installProperties{
+			filename:       filename,
+			sourceFilePath: p.sourceFilePaths[0],
+			outputFilePath: p.outputFilePaths[0],
+			installDirPath: p.installDirPath,
+		}
+		installs = append(installs, ip)
+	} else {
+		ctx.PropertyErrorf("src", "missing prebuilt source file")
+		return
+	}
+
+	// Call InstallFile even when uninstallable to make the module included in the package.
+	if !p.Installable() {
+		p.SkipInstall()
+	}
+	for _, ip := range installs {
+		ip.addInstallRules(ctx)
+	}
+
+	ctx.SetOutputFiles(p.outputFilePaths.Paths(), "")
 }
 
 type installProperties struct {
-	installable    bool
 	filename       string
 	sourceFilePath android.Path
+	outputFilePath android.OutputPath
+	installDirPath android.InstallPath
 	symlinks       []string
 }
 
 // utility function to add install rules to the build graph.
 // Reduces code duplication between Soong and Mixed build analysis
-func (p *PrebuiltEtc) addInstallRules(ctx android.ModuleContext, ip installProperties) {
-	if !ip.installable {
-		p.SkipInstall()
-	}
-
+func (ip *installProperties) addInstallRules(ctx android.ModuleContext) {
 	// Copy the file from src to a location in out/ with the correct `filename`
 	// This ensures that outputFilePath has the correct name for others to
 	// use, as the source file may have a different name.
-	p.outputFilePath = android.PathForModuleOut(ctx, ip.filename).OutputPath
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   android.Cp,
-		Output: p.outputFilePath,
+		Output: ip.outputFilePath,
 		Input:  ip.sourceFilePath,
 	})
 
-	installPath := ctx.InstallFile(p.installDirPath, ip.filename, p.outputFilePath)
+	installPath := ctx.InstallFile(ip.installDirPath, ip.filename, ip.outputFilePath)
 	for _, sl := range ip.symlinks {
-		ctx.InstallSymlink(p.installDirPath, sl, installPath)
+		ctx.InstallSymlink(ip.installDirPath, sl, installPath)
 	}
 }
 
@@ -421,15 +486,15 @@
 		class = "ETC"
 	}
 
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
+	return []android.AndroidMkEntries{{
 		Class:      class,
 		SubName:    nameSuffix,
-		OutputFile: android.OptionalPathForPath(p.outputFilePath),
+		OutputFile: android.OptionalPathForPath(p.outputFilePaths[0]),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
 				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePaths[0].Base())
 				if len(p.properties.Symlinks) > 0 {
 					entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
 				}
@@ -437,7 +502,6 @@
 				if p.additionalDependencies != nil {
 					entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", p.additionalDependencies.Strings()...)
 				}
-				android.SetAconfigFileMkEntries(p.AndroidModuleBase(), entries, p.mergedAconfigFiles)
 			},
 		},
 	}}
@@ -456,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
@@ -508,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 {
@@ -552,6 +637,50 @@
 	return module
 }
 
+// prebuilt_usr_hyphendata is for a prebuilt artifact that is installed in
+// <partition>/usr/hyphen-data/<sub_dir> directory.
+func PrebuiltUserHyphenDataFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "usr/hyphen-data")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_usr_keylayout is for a prebuilt artifact that is installed in
+// <partition>/usr/keylayout/<sub_dir> directory.
+func PrebuiltUserKeyLayoutFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "usr/keylayout")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_usr_keychars is for a prebuilt artifact that is installed in
+// <partition>/usr/keychars/<sub_dir> directory.
+func PrebuiltUserKeyCharsFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "usr/keychars")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+	return module
+}
+
+// prebuilt_usr_idc is for a prebuilt artifact that is installed in
+// <partition>/usr/idc/<sub_dir> directory.
+func PrebuiltUserIdcFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "usr/idc")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitDefaultableModule(module)
+	return module
+}
+
 // prebuilt_font installs a font in <partition>/fonts directory.
 func PrebuiltFontFactory() android.Module {
 	module := &PrebuiltEtc{}
@@ -562,6 +691,15 @@
 	return module
 }
 
+// prebuilt_overlay is for a prebuilt artifact in <partition>/overlay directory.
+func PrebuiltOverlayFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "overlay")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
 // prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system
 // image.
 // If soc_specific property is set to true, the firmware file is installed to the
@@ -615,117 +753,3 @@
 	android.InitDefaultableModule(module)
 	return module
 }
-
-// Copy file into the snapshot
-func copyFile(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath {
-	if fake {
-		// Create empty file instead for the fake snapshot
-		return snapshot.WriteStringToFileRule(ctx, "", out)
-	} else {
-		return snapshot.CopyFileRule(pctx, ctx, path, out)
-	}
-}
-
-// Check if the module is target of the snapshot
-func isSnapshotAware(ctx android.SingletonContext, m *PrebuiltEtc, image snapshot.SnapshotImage) bool {
-	if !m.Enabled() {
-		return false
-	}
-
-	// Skip if the module is not included in the image
-	if !image.InImage(m)() {
-		return false
-	}
-
-	// When android/prebuilt.go selects between source and prebuilt, it sets
-	// HideFromMake on the other one to avoid duplicate install rules in make.
-	if m.IsHideFromMake() {
-		return false
-	}
-
-	// There are some prebuilt_etc module with multiple definition of same name.
-	// Check if the target would be included from the build
-	if !m.ExportedToMake() {
-		return false
-	}
-
-	// Skip if the module is in the predefined path list to skip
-	if image.IsProprietaryPath(ctx.ModuleDir(m), ctx.DeviceConfig()) {
-		return false
-	}
-
-	// Skip if the module should be excluded
-	if image.ExcludeFromSnapshot(m) || image.ExcludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
-		return false
-	}
-
-	// Skip from other exceptional cases
-	if m.Target().Os.Class != android.Device {
-		return false
-	}
-	if m.Target().NativeBridge == android.NativeBridgeEnabled {
-		return false
-	}
-
-	return true
-}
-
-func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) snapshot.SnapshotPaths {
-	/*
-		Snapshot zipped artifacts directory structure for etc modules:
-		{SNAPSHOT_ARCH}/
-			arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
-				etc/
-					(prebuilt etc files)
-			arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
-				etc/
-					(prebuilt etc files)
-			NOTICE_FILES/
-				(notice files)
-	*/
-	var snapshotOutputs android.Paths
-	var snapshotNotices android.Paths
-	installedNotices := make(map[string]bool)
-
-	ctx.VisitAllModules(func(module android.Module) {
-		m, ok := module.(*PrebuiltEtc)
-		if !ok {
-			return
-		}
-
-		if !isSnapshotAware(ctx, m, s.Image) {
-			return
-		}
-
-		targetArch := "arch-" + m.Target().Arch.ArchType.String()
-
-		snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "etc", m.BaseModuleName())
-		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, m.OutputFile(), snapshotLibOut, s.Fake))
-
-		prop := snapshot.SnapshotJsonFlags{}
-		propOut := snapshotLibOut + ".json"
-		prop.InitBaseSnapshotProps(m)
-		prop.RelativeInstallPath = m.SubDir()
-
-		if m.properties.Filename != nil {
-			prop.Filename = *m.properties.Filename
-		}
-
-		j, err := json.Marshal(prop)
-		if err != nil {
-			ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
-			return
-		}
-		snapshotOutputs = append(snapshotOutputs, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
-
-		for _, notice := range m.EffectiveLicenseFiles() {
-			if _, ok := installedNotices[notice.String()]; !ok {
-				installedNotices[notice.String()] = true
-				snapshotNotices = append(snapshotNotices, notice)
-			}
-		}
-
-	})
-
-	return snapshot.SnapshotPaths{OutputFiles: snapshotOutputs, NoticeFiles: snapshotNotices}
-}
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index df11709..e739afe 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -15,7 +15,6 @@
 package etc
 
 import (
-	"fmt"
 	"os"
 	"path/filepath"
 	"testing"
@@ -23,7 +22,6 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
-	"android/soong/snapshot"
 )
 
 func TestMain(m *testing.M) {
@@ -40,18 +38,6 @@
 	}),
 )
 
-var prepareForPrebuiltEtcSnapshotTest = android.GroupFixturePreparers(
-	prepareForPrebuiltEtcTest,
-	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		snapshot.VendorSnapshotImageSingleton.Init(ctx)
-		snapshot.RecoverySnapshotImageSingleton.Init(ctx)
-	}),
-	android.FixtureModifyConfig(func(config android.Config) {
-		config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
-		config.TestProductVariables.RecoverySnapshotVersion = proptools.StringPtr("current")
-	}),
-)
-
 func TestPrebuiltEtcVariants(t *testing.T) {
 	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc {
@@ -96,7 +82,7 @@
 	`)
 
 	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
-	android.AssertStringEquals(t, "output file path", "foo.installed.conf", p.outputFilePath.Base())
+	android.AssertStringEquals(t, "output file path", "foo.installed.conf", p.outputFilePaths[0].Base())
 }
 
 func TestPrebuiltEtcGlob(t *testing.T) {
@@ -113,10 +99,24 @@
 	`)
 
 	p := result.Module("my_foo", "android_arm64_armv8-a").(*PrebuiltEtc)
-	android.AssertStringEquals(t, "my_foo output file path", "my_foo", p.outputFilePath.Base())
+	android.AssertStringEquals(t, "my_foo output file path", "my_foo", p.outputFilePaths[0].Base())
 
 	p = result.Module("my_bar", "android_arm64_armv8-a").(*PrebuiltEtc)
-	android.AssertStringEquals(t, "my_bar output file path", "bar.conf", p.outputFilePath.Base())
+	android.AssertStringEquals(t, "my_bar output file path", "bar.conf", p.outputFilePaths[0].Base())
+}
+
+func TestPrebuiltEtcMultipleSrcs(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_etc {
+			name: "foo",
+			srcs: ["*.conf"],
+		}
+	`)
+
+	p := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc)
+	android.AssertStringEquals(t, "output file path", "bar.conf", p.outputFilePaths[0].Base())
+	android.AssertStringEquals(t, "output file path", "baz.conf", p.outputFilePaths[1].Base())
+	android.AssertStringEquals(t, "output file path", "foo.conf", p.outputFilePaths[2].Base())
 }
 
 func TestPrebuiltEtcAndroidMk(t *testing.T) {
@@ -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 {
@@ -273,6 +298,62 @@
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
 }
 
+func TestPrebuiltPrebuiltUserHyphenDataInstallDirPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+	prebuilt_usr_hyphendata {
+			name: "foo.conf",
+			src: "foo.conf",
+			sub_dir: "bar",
+		}
+	`)
+
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+	expected := "out/soong/target/product/test_device/system/usr/hyphen-data/bar"
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+}
+
+func TestPrebuiltPrebuiltUserKeyLayoutInstallDirPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+	prebuilt_usr_keylayout {
+			name: "foo.conf",
+			src: "foo.conf",
+			sub_dir: "bar",
+		}
+	`)
+
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+	expected := "out/soong/target/product/test_device/system/usr/keylayout/bar"
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+}
+
+func TestPrebuiltPrebuiltUserKeyCharsInstallDirPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+	prebuilt_usr_keychars {
+			name: "foo.conf",
+			src: "foo.conf",
+			sub_dir: "bar",
+		}
+	`)
+
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+	expected := "out/soong/target/product/test_device/system/usr/keychars/bar"
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+}
+
+func TestPrebuiltPrebuiltUserIdcInstallDirPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+	prebuilt_usr_idc {
+			name: "foo.conf",
+			src: "foo.conf",
+			sub_dir: "bar",
+		}
+	`)
+
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+	expected := "out/soong/target/product/test_device/system/usr/idc/bar"
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+}
+
 func TestPrebuiltFontInstallDirPath(t *testing.T) {
 	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_font {
@@ -286,6 +367,19 @@
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
 }
 
+func TestPrebuiltOverlayInstallDirPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_overlay {
+			name: "foo.conf",
+			src: "foo.conf",
+		}
+	`)
+
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+	expected := "out/soong/target/product/test_device/system/overlay"
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+}
+
 func TestPrebuiltFirmwareDirPath(t *testing.T) {
 	targetPath := "out/soong/target/product/test_device"
 	tests := []struct {
@@ -387,110 +481,3 @@
 		})
 	}
 }
-
-func checkIfSnapshotTaken(t *testing.T, result *android.TestResult, image string, moduleName string) {
-	checkIfSnapshotExistAsExpected(t, result, image, moduleName, true)
-}
-
-func checkIfSnapshotNotTaken(t *testing.T, result *android.TestResult, image string, moduleName string) {
-	checkIfSnapshotExistAsExpected(t, result, image, moduleName, false)
-}
-
-func checkIfSnapshotExistAsExpected(t *testing.T, result *android.TestResult, image string, moduleName string, expectToExist bool) {
-	snapshotSingleton := result.SingletonForTests(image + "-snapshot")
-	archType := "arm64"
-	archVariant := "armv8-a"
-	archDir := fmt.Sprintf("arch-%s", archType)
-
-	snapshotDir := fmt.Sprintf("%s-snapshot", image)
-	snapshotVariantPath := filepath.Join(snapshotDir, archType)
-	outputDir := filepath.Join(snapshotVariantPath, archDir, "etc")
-	imageVariant := ""
-	if image == "recovery" {
-		imageVariant = "recovery_"
-	}
-	mod := result.ModuleForTests(moduleName, fmt.Sprintf("android_%s%s_%s", imageVariant, archType, archVariant))
-	outputFiles := mod.OutputFiles(t, "")
-	if len(outputFiles) != 1 {
-		t.Errorf("%q must have single output\n", moduleName)
-		return
-	}
-	snapshotPath := filepath.Join(outputDir, moduleName)
-
-	if expectToExist {
-		out := snapshotSingleton.Output(snapshotPath)
-
-		if out.Input.String() != outputFiles[0].String() {
-			t.Errorf("The input of snapshot %q must be %q, but %q", "prebuilt_vendor", out.Input.String(), outputFiles[0])
-		}
-
-		snapshotJsonPath := snapshotPath + ".json"
-
-		if snapshotSingleton.MaybeOutput(snapshotJsonPath).Rule == nil {
-			t.Errorf("%q expected but not found", snapshotJsonPath)
-		}
-	} else {
-		out := snapshotSingleton.MaybeOutput(snapshotPath)
-		if out.Rule != nil {
-			t.Errorf("There must be no rule for module %q output file %q", moduleName, outputFiles[0])
-		}
-	}
-}
-
-func TestPrebuiltTakeSnapshot(t *testing.T) {
-	var testBp = `
-	prebuilt_etc {
-		name: "prebuilt_vendor",
-		src: "foo.conf",
-		vendor: true,
-	}
-
-	prebuilt_etc {
-		name: "prebuilt_vendor_indirect",
-		src: "foo.conf",
-		vendor: true,
-	}
-
-	prebuilt_etc {
-		name: "prebuilt_recovery",
-		src: "bar.conf",
-		recovery: true,
-	}
-
-	prebuilt_etc {
-		name: "prebuilt_recovery_indirect",
-		src: "bar.conf",
-		recovery: true,
-	}
-	`
-
-	t.Run("prebuilt: vendor and recovery snapshot", func(t *testing.T) {
-		result := prepareForPrebuiltEtcSnapshotTest.RunTestWithBp(t, testBp)
-
-		checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor")
-		checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor_indirect")
-		checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery")
-		checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery_indirect")
-	})
-
-	t.Run("prebuilt: directed snapshot", func(t *testing.T) {
-		prepareForPrebuiltEtcDirectedSnapshotTest := android.GroupFixturePreparers(
-			prepareForPrebuiltEtcSnapshotTest,
-			android.FixtureModifyConfig(func(config android.Config) {
-				config.TestProductVariables.DirectedVendorSnapshot = true
-				config.TestProductVariables.VendorSnapshotModules = make(map[string]bool)
-				config.TestProductVariables.VendorSnapshotModules["prebuilt_vendor"] = true
-				config.TestProductVariables.DirectedRecoverySnapshot = true
-				config.TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
-				config.TestProductVariables.RecoverySnapshotModules["prebuilt_recovery"] = true
-			}),
-		)
-
-		result := prepareForPrebuiltEtcDirectedSnapshotTest.RunTestWithBp(t, testBp)
-
-		checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor")
-		checkIfSnapshotNotTaken(t, result, "vendor", "prebuilt_vendor_indirect")
-		checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery")
-		checkIfSnapshotNotTaken(t, result, "recovery", "prebuilt_recovery_indirect")
-	})
-}
diff --git a/etc/snapshot_etc.go b/etc/snapshot_etc.go
deleted file mode 100644
index 0d65ab6..0000000
--- a/etc/snapshot_etc.go
+++ /dev/null
@@ -1,186 +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 etc
-
-// This file implements snapshot module of 'prebuilt_etc' type
-// 'snapshot_etc' module defines android.PrebuiltInterface so it can be handled
-// as prebuilt of 'prebuilt_etc' type.
-// Properties of 'snapshot_etc' follows properties from snapshotJsonFlags type
-
-import (
-	"android/soong/android"
-	"fmt"
-	"strings"
-
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
-)
-
-func RegisterSnapshotEtcModule(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("snapshot_etc", SnapshotEtcFactory)
-}
-
-func init() {
-	RegisterSnapshotEtcModule(android.InitRegistrationContext)
-}
-
-// snapshot_etc is a prebuilt module type to be installed under etc which is auto-generated by
-// development/vendor_snapshot/update.py. This module will override prebuilt_etc module with same
-// name when 'prefer' property is true.
-func SnapshotEtcFactory() android.Module {
-	module := &SnapshotEtc{}
-	module.AddProperties(&module.properties)
-
-	var srcsSupplier = func(_ android.BaseModuleContext, prebuilt android.Module) []string {
-		s, ok := prebuilt.(*SnapshotEtc)
-		if !ok || s.properties.Src == nil {
-			return []string{}
-		}
-
-		return []string{*s.properties.Src}
-	}
-
-	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
-	android.InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "src")
-	return module
-}
-
-type snapshotEtcProperties struct {
-	Src                   *string `android:"path,arch_variant"` // Source of snapshot_etc file
-	Filename              *string `android:"arch_variant"`      // Target file name when it differs from module name
-	Relative_install_path *string `android:"arch_variant"`      // Relative install path when it should be installed subdirectory of etc
-}
-
-type SnapshotEtc struct {
-	android.ModuleBase
-	prebuilt   android.Prebuilt
-	properties snapshotEtcProperties
-
-	outputFilePath android.OutputPath
-	installDirPath android.InstallPath
-}
-
-func (s *SnapshotEtc) Prebuilt() *android.Prebuilt {
-	return &s.prebuilt
-}
-
-func (s *SnapshotEtc) Name() string {
-	return s.prebuilt.Name(s.BaseModuleName())
-}
-
-func (s *SnapshotEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if s.properties.Src == nil {
-		ctx.PropertyErrorf("src", "missing prebuilt source file")
-		return
-	}
-
-	sourceFilePath := s.prebuilt.SingleSourcePath(ctx)
-
-	// Determine the output file basename.
-	// If Filename is set, use the name specified by the property.
-	// Otherwise use the module name.
-	filename := proptools.String(s.properties.Filename)
-	if filename == "" {
-		filename = ctx.ModuleName()
-	}
-
-	s.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
-
-	if strings.Contains(filename, "/") {
-		ctx.PropertyErrorf("filename", "filename cannot contain separator '/'")
-		return
-	}
-
-	subDir := ""
-	if s.properties.Relative_install_path != nil {
-		subDir = *s.properties.Relative_install_path
-	}
-
-	s.installDirPath = android.PathForModuleInstall(ctx, "etc", subDir)
-
-	// 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,
-		Input:       sourceFilePath,
-		Output:      s.outputFilePath,
-		Description: "Install snapshot etc module " + s.BaseModuleName(),
-	})
-
-	ctx.InstallFile(s.installDirPath, s.outputFilePath.Base(), sourceFilePath)
-}
-
-func (p *SnapshotEtc) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(p.outputFilePath),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_TAGS", "optional")
-				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
-			},
-		},
-	}}
-}
-
-type snapshotEtcDependencyTag struct {
-	blueprint.DependencyTag
-}
-
-var tag = snapshotEtcDependencyTag{}
-
-func (s *SnapshotEtc) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
-	return !s.ModuleBase.InstallInRecovery() && !s.ModuleBase.InstallInRamdisk() &&
-		!s.ModuleBase.InstallInVendorRamdisk() && !s.ModuleBase.InstallInDebugRamdisk()
-}
-
-func (p *SnapshotEtc) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return p.ModuleBase.InstallInRamdisk()
-}
-
-func (p *SnapshotEtc) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return p.ModuleBase.InstallInVendorRamdisk()
-}
-
-func (p *SnapshotEtc) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return p.ModuleBase.InstallInDebugRamdisk()
-}
-
-func (p *SnapshotEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
-	return p.ModuleBase.InstallInRecovery()
-}
-
-func (p *SnapshotEtc) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	return nil
-}
-
-func (p *SnapshotEtc) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
-}
-
-func (p *SnapshotEtc) ImageMutatorBegin(ctx android.BaseModuleContext) {}
-
-func (p *SnapshotEtc) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{p.outputFilePath}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-
-}
-
-var _ android.PrebuiltInterface = (*SnapshotEtc)(nil)
-var _ android.ImageInterface = (*SnapshotEtc)(nil)
-var _ android.OutputFileProducer = (*SnapshotEtc)(nil)
diff --git a/etc/snapshot_etc_test.go b/etc/snapshot_etc_test.go
deleted file mode 100644
index b9d5504..0000000
--- a/etc/snapshot_etc_test.go
+++ /dev/null
@@ -1,185 +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 etc
-
-import (
-	"android/soong/android"
-	"testing"
-
-	"github.com/google/blueprint"
-)
-
-var registerSourceModule = func(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("source", newSourceModule)
-}
-
-type sourceModuleProperties struct {
-	Deps []string `android:"path,arch_variant"`
-}
-
-type sourceModule struct {
-	android.ModuleBase
-	android.OverridableModuleBase
-
-	properties                                     sourceModuleProperties
-	dependsOnSourceModule, dependsOnPrebuiltModule bool
-	deps                                           android.Paths
-	src                                            android.Path
-}
-
-func newSourceModule() android.Module {
-	m := &sourceModule{}
-	m.AddProperties(&m.properties)
-	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibFirst)
-	android.InitOverridableModule(m, nil)
-	return m
-}
-
-func (s *sourceModule) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
-	// s.properties.Deps are annotated with android:path, so they are
-	// automatically added to the dependency by pathDeps mutator
-}
-
-func (s *sourceModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	s.deps = android.PathsForModuleSrc(ctx, s.properties.Deps)
-	s.src = android.PathForModuleSrc(ctx, "source_file")
-}
-
-func (s *sourceModule) Srcs() android.Paths {
-	return android.Paths{s.src}
-}
-
-var prepareForSnapshotEtcTest = android.GroupFixturePreparers(
-	android.PrepareForTestWithArchMutator,
-	android.PrepareForTestWithPrebuilts,
-	PrepareForTestWithPrebuiltEtc,
-	android.FixtureRegisterWithContext(RegisterSnapshotEtcModule),
-	android.FixtureRegisterWithContext(registerSourceModule),
-	android.FixtureMergeMockFs(android.MockFS{
-		"foo.conf": nil,
-		"bar.conf": nil,
-	}),
-)
-
-func TestSnapshotWithFilename(t *testing.T) {
-	var androidBp = `
-	snapshot_etc {
-		name: "etc_module",
-		src: "foo.conf",
-		filename: "bar.conf",
-	}
-	`
-
-	result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp)
-	for _, variant := range result.ModuleVariantsForTests("etc_module") {
-		module := result.ModuleForTests("etc_module", variant)
-		s, ok := module.Module().(*SnapshotEtc)
-		if !ok {
-			t.Errorf("Expected snapshot_etc module type")
-		}
-		if s.outputFilePath.Base() != "bar.conf" {
-			t.Errorf("Output file path does not match with specified filename")
-		}
-	}
-}
-
-func TestSnapshotEtcWithOrigin(t *testing.T) {
-	var androidBp = `
-	prebuilt_etc {
-		name: "etc_module",
-		src: "foo.conf",
-	}
-
-	snapshot_etc {
-		name: "etc_module",
-		src: "bar.conf",
-	}
-
-	source {
-		name: "source",
-		deps: [":etc_module"],
-	}
-	`
-
-	result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp)
-
-	for _, variant := range result.ModuleVariantsForTests("source") {
-		source := result.ModuleForTests("source", variant)
-
-		result.VisitDirectDeps(source.Module(), func(m blueprint.Module) {
-			if _, ok := m.(*PrebuiltEtc); !ok {
-				t.Errorf("Original prebuilt_etc module expected.")
-			}
-		})
-	}
-}
-
-func TestSnapshotEtcWithOriginAndPrefer(t *testing.T) {
-	var androidBp = `
-	prebuilt_etc {
-		name: "etc_module",
-		src: "foo.conf",
-	}
-
-	snapshot_etc {
-		name: "etc_module",
-		src: "bar.conf",
-		prefer: true,
-	}
-
-	source {
-		name: "source",
-		deps: [":etc_module"],
-	}
-	`
-
-	result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp)
-
-	for _, variant := range result.ModuleVariantsForTests("source") {
-		source := result.ModuleForTests("source", variant)
-
-		result.VisitDirectDeps(source.Module(), func(m blueprint.Module) {
-			if _, ok := m.(*SnapshotEtc); !ok {
-				t.Errorf("Preferred snapshot_etc module expected.")
-			}
-		})
-	}
-}
-
-func TestSnapshotEtcWithoutOrigin(t *testing.T) {
-	var androidBp = `
-	snapshot_etc {
-		name: "etc_module",
-		src: "bar.conf",
-	}
-
-	source {
-		name: "source",
-		deps: [":etc_module"],
-	}
-	`
-
-	result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp)
-
-	for _, variant := range result.ModuleVariantsForTests("source") {
-		source := result.ModuleForTests("source", variant)
-
-		result.VisitDirectDeps(source.Module(), func(m blueprint.Module) {
-			if _, ok := m.(*SnapshotEtc); !ok {
-				t.Errorf("Only source snapshot_etc module expected.")
-			}
-		})
-	}
-}
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index cdf6682..a08f7cf 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -10,13 +10,17 @@
         "soong",
         "soong-android",
         "soong-bpf", // for testing
+        "soong-java", // for testing
         "soong-linkerconfig",
+        "soong-phony", // for testing
     ],
     srcs: [
+        "aconfig_files.go",
         "avb_add_hash_footer.go",
         "avb_gen_vbmeta_image.go",
         "bootimg.go",
         "filesystem.go",
+        "fsverity_metadata.go",
         "logical_partition.go",
         "raw_binary.go",
         "system_image.go",
diff --git a/filesystem/aconfig_files.go b/filesystem/aconfig_files.go
new file mode 100644
index 0000000..5c047bc
--- /dev/null
+++ b/filesystem/aconfig_files.go
@@ -0,0 +1,84 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package filesystem
+
+import (
+	"android/soong/android"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+)
+
+func (f *filesystem) buildAconfigFlagsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, dir android.OutputPath) {
+	if !proptools.Bool(f.properties.Gen_aconfig_flags_pb) {
+		return
+	}
+
+	aconfigFlagsBuilderPath := android.PathForModuleOut(ctx, "aconfig_flags_builder.sh")
+	aconfigToolPath := ctx.Config().HostToolPath(ctx, "aconfig")
+	cmd := builder.Command().Tool(aconfigFlagsBuilderPath).Implicit(aconfigToolPath)
+
+	var caches []string
+	for _, ps := range specs {
+		cmd.Implicits(ps.GetAconfigPaths())
+		caches = append(caches, ps.GetAconfigPaths().Strings()...)
+	}
+	caches = android.SortedUniqueStrings(caches)
+
+	var sbCaches strings.Builder
+	for _, cache := range caches {
+		sbCaches.WriteString("  --cache ")
+		sbCaches.WriteString(cache)
+		sbCaches.WriteString(" \\\n")
+	}
+	sbCaches.WriteRune('\n')
+
+	var sb strings.Builder
+	sb.WriteString("set -e\n")
+
+	installAconfigFlagsPath := dir.Join(ctx, "etc", "aconfig_flags.pb")
+	sb.WriteString(aconfigToolPath.String())
+	sb.WriteString(" dump-cache --dedup --format protobuf --out ")
+	sb.WriteString(installAconfigFlagsPath.String())
+	sb.WriteString(" \\\n")
+	sb.WriteString(sbCaches.String())
+	cmd.ImplicitOutput(installAconfigFlagsPath)
+	f.appendToEntry(ctx, installAconfigFlagsPath)
+
+	installAconfigStorageDir := dir.Join(ctx, "etc", "aconfig")
+	sb.WriteString("mkdir -p ")
+	sb.WriteString(installAconfigStorageDir.String())
+	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(outputPath.String())
+		sb.WriteString(" \\\n")
+		sb.WriteString(sbCaches.String())
+		cmd.ImplicitOutput(outputPath)
+		f.appendToEntry(ctx, outputPath)
+	}
+	generatePartitionAconfigStorageFile("package_map", "package.map")
+	generatePartitionAconfigStorageFile("flag_map", "flag.map")
+	generatePartitionAconfigStorageFile("flag_val", "flag.val")
+
+	android.WriteExecutableFileRuleVerbatim(ctx, aconfigFlagsBuilderPath, sb.String())
+}
diff --git a/filesystem/avb_gen_vbmeta_image.go b/filesystem/avb_gen_vbmeta_image.go
index 985f0ea..a7fd782 100644
--- a/filesystem/avb_gen_vbmeta_image.go
+++ b/filesystem/avb_gen_vbmeta_image.go
@@ -81,6 +81,8 @@
 	a.output = android.PathForModuleOut(ctx, a.installFileName()).OutputPath
 	cmd.FlagWithOutput("--output_vbmeta_image ", a.output)
 	builder.Build("avbGenVbmetaImage", fmt.Sprintf("avbGenVbmetaImage %s", ctx.ModuleName()))
+
+	ctx.SetOutputFiles([]android.Path{a.output}, "")
 }
 
 var _ android.AndroidMkEntriesProvider = (*avbGenVbmetaImage)(nil)
@@ -99,16 +101,6 @@
 	}}
 }
 
-var _ android.OutputFileProducer = (*avbGenVbmetaImage)(nil)
-
-// Implements android.OutputFileProducer
-func (a *avbGenVbmetaImage) OutputFiles(tag string) (android.Paths, error) {
-	if tag == "" {
-		return []android.Path{a.output}, nil
-	}
-	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
-
 type avbGenVbmetaImageDefaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 352b451..e796ab9 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -123,6 +123,8 @@
 
 	b.installDir = android.PathForModuleInstall(ctx, "etc")
 	ctx.InstallFile(b.installDir, b.installFileName(), b.output)
+
+	ctx.SetOutputFiles([]android.Path{b.output}, "")
 }
 
 func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android.OutputPath {
@@ -292,13 +294,3 @@
 	}
 	return nil
 }
-
-var _ android.OutputFileProducer = (*bootimg)(nil)
-
-// Implements android.OutputFileProducer
-func (b *bootimg) OutputFiles(tag string) (android.Paths, error) {
-	if tag == "" {
-		return []android.Path{b.output}, nil
-	}
-	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 64a2e23..5c7ef43 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -20,6 +20,7 @@
 	"io"
 	"path/filepath"
 	"slices"
+	"strconv"
 	"strings"
 
 	"android/soong/android"
@@ -35,6 +36,7 @@
 
 func registerBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_filesystem", filesystemFactory)
+	ctx.RegisterModuleType("android_filesystem_defaults", filesystemDefaultsFactory)
 	ctx.RegisterModuleType("android_system_image", systemImageFactory)
 	ctx.RegisterModuleType("avb_add_hash_footer", avbAddHashFooterFactory)
 	ctx.RegisterModuleType("avb_add_hash_footer_defaults", avbAddHashFooterDefaultsFactory)
@@ -45,6 +47,7 @@
 type filesystem struct {
 	android.ModuleBase
 	android.PackagingBase
+	android.DefaultableModuleBase
 
 	properties filesystemProperties
 
@@ -57,7 +60,9 @@
 	output     android.OutputPath
 	installDir android.InstallPath
 
-	// For testing. Keeps the result of CopyDepsToZip()
+	fileListFile android.OutputPath
+
+	// Keeps the entries installed from this filesystem
 	entries []string
 }
 
@@ -81,6 +86,9 @@
 	// avbtool. Default used by avbtool is sha1.
 	Avb_hash_algorithm *string
 
+	// The index used to prevent rollback of the image. Only used if use_avb is true.
+	Rollback_index *int64
+
 	// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
 	Partition_name *string
 
@@ -88,6 +96,10 @@
 	// is ext4.
 	Type *string
 
+	// Identifies which partition this is for //visibility:any_system_image (and others) visibility
+	// checks, and will be used in the future for API surface checks.
+	Partition_type *string
+
 	// file_contexts file to make image. Currently, only ext4 is supported.
 	File_contexts *string `android:"path"`
 
@@ -96,7 +108,7 @@
 	Base_dir *string
 
 	// Directories to be created under root. e.g. /dev, /proc, etc.
-	Dirs []string
+	Dirs proptools.Configurable[[]string]
 
 	// Symbolic links to be created under root with "ln -sf <target> <name>".
 	Symlinks []symlinkDefinition
@@ -116,6 +128,15 @@
 	// modules would be installed to the same location as a make module, they will overwrite
 	// the make version.
 	Include_make_built_files string
+
+	// When set, builds etc/event-log-tags file by merging logtags from all dependencies.
+	// Default is false
+	Build_logtags *bool
+
+	// Install aconfig_flags.pb file for the modules installed in this partition.
+	Gen_aconfig_flags_pb *bool
+
+	Fsverity fsverityProperties
 }
 
 // android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -125,6 +146,7 @@
 // partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
 func filesystemFactory() android.Module {
 	module := &filesystem{}
+	module.filterPackagingSpec = module.filterInstallablePackagingSpec
 	initFilesystemModule(module)
 	return module
 }
@@ -132,7 +154,9 @@
 func initFilesystemModule(module *filesystem) {
 	module.AddProperties(&module.properties)
 	android.InitPackageModule(module)
+	module.PackagingBase.DepsCollectFirstTargetOnly = true
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
 }
 
 var dependencyTag = struct {
@@ -172,9 +196,20 @@
 	return f.BaseModuleName() + ".img"
 }
 
+func (f *filesystem) partitionName() string {
+	return proptools.StringDefault(f.properties.Partition_name, f.Name())
+}
+
+func (f *filesystem) filterInstallablePackagingSpec(ps android.PackagingSpec) bool {
+	// Filesystem module respects the installation semantic. A PackagingSpec from a module with
+	// IsSkipInstall() is skipped.
+	return !ps.SkipInstall()
+}
+
 var pctx = android.NewPackageContext("android/soong/filesystem")
 
 func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	validatePartitionType(ctx, f)
 	switch f.fsType(ctx) {
 	case ext4Type:
 		f.output = f.buildImageUsingBuildImage(ctx)
@@ -188,13 +223,49 @@
 
 	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) {
+	if !android.InList(p.PartitionType(), validPartitions) {
+		ctx.PropertyErrorf("partition_type", "partition_type must be one of %s, found: %s", validPartitions, p.PartitionType())
+	}
+
+	ctx.VisitDirectDepsWithTag(android.DefaultsDepTag, func(m android.Module) {
+		if fdm, ok := m.(*filesystemDefaults); ok {
+			if p.PartitionType() != fdm.PartitionType() {
+				ctx.PropertyErrorf("partition_type",
+					"%s doesn't match with the partition type %s of the filesystem default module %s",
+					p.PartitionType(), fdm.PartitionType(), m.Name())
+			}
+		}
+	})
 }
 
 // Copy extra files/dirs that are not from the `deps` property to `rootDir`, checking for conflicts with files
 // already in `rootDir`.
 func (f *filesystem) buildNonDepsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.OutputPath) {
 	// create dirs and symlinks
-	for _, dir := range f.properties.Dirs {
+	for _, dir := range f.properties.Dirs.GetOrDefault(ctx, nil) {
 		// OutputPath.Join verifies dir
 		builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, dir).String())
 	}
@@ -218,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").
@@ -239,6 +312,25 @@
 	}
 }
 
+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) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath {
 	rootDir := android.PathForModuleOut(ctx, "root").OutputPath
 	rebasedDir := rootDir
@@ -248,10 +340,14 @@
 	builder := android.NewRuleBuilder(pctx, ctx)
 	// Wipe the root dir to get rid of leftover files from prior builds
 	builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
-	f.entries = f.CopySpecsToDir(ctx, builder, f.gatherFilteredPackagingSpecs(ctx), rebasedDir)
+	specs := f.gatherFilteredPackagingSpecs(ctx)
+	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)
 
 	// run host_init_verifier
 	// Ideally we should have a concept of pluggable linters that verify the generated image.
@@ -331,13 +427,19 @@
 		addStr("avb_algorithm", algorithm)
 		key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key))
 		addPath("avb_key_path", key)
-		partitionName := proptools.StringDefault(f.properties.Partition_name, f.Name())
-		addStr("partition_name", partitionName)
+		addStr("partition_name", f.partitionName())
 		avb_add_hashtree_footer_args := "--do_not_generate_fec"
 		if hashAlgorithm := proptools.String(f.properties.Avb_hash_algorithm); hashAlgorithm != "" {
 			avb_add_hashtree_footer_args += " --hash_algorithm " + hashAlgorithm
 		}
-		securityPatchKey := "com.android.build." + partitionName + ".security_patch"
+		if f.properties.Rollback_index != nil {
+			rollbackIndex := proptools.Int(f.properties.Rollback_index)
+			if rollbackIndex < 0 {
+				ctx.PropertyErrorf("rollback_index", "Rollback index must be non-negative")
+			}
+			avb_add_hashtree_footer_args += " --rollback_index " + strconv.Itoa(rollbackIndex)
+		}
+		securityPatchKey := "com.android.build." + f.partitionName() + ".security_patch"
 		securityPatchValue := ctx.Config().PlatformSecurityPatch()
 		avb_add_hashtree_footer_args += " --prop " + securityPatchKey + ":" + securityPatchValue
 		addStr("avb_add_hashtree_footer_args", avb_add_hashtree_footer_args)
@@ -381,9 +483,13 @@
 	builder := android.NewRuleBuilder(pctx, ctx)
 	// Wipe the root dir to get rid of leftover files from prior builds
 	builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
-	f.entries = f.CopySpecsToDir(ctx, builder, f.gatherFilteredPackagingSpecs(ctx), rebasedDir)
+	specs := f.gatherFilteredPackagingSpecs(ctx)
+	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)
 
 	output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
 	cmd := builder.Command().
@@ -441,6 +547,49 @@
 		Text(android.PathForArbitraryOutput(ctx, stagingDir).String())
 }
 
+func (f *filesystem) buildEventLogtagsFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
+	if !proptools.Bool(f.properties.Build_logtags) {
+		return
+	}
+
+	logtagsFilePaths := make(map[string]bool)
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		if logtagsInfo, ok := android.OtherModuleProvider(ctx, child, android.LogtagsProviderKey); ok {
+			for _, path := range logtagsInfo.Logtags {
+				logtagsFilePaths[path.String()] = true
+			}
+		}
+		return true
+	})
+
+	if len(logtagsFilePaths) == 0 {
+		return
+	}
+
+	etcPath := rebasedDir.Join(ctx, "etc")
+	eventLogtagsPath := etcPath.Join(ctx, "event-log-tags")
+	builder.Command().Text("mkdir").Flag("-p").Text(etcPath.String())
+	cmd := builder.Command().BuiltTool("merge-event-log-tags").
+		FlagWithArg("-o ", eventLogtagsPath.String()).
+		FlagWithInput("-m ", android.MergedLogtagsPath(ctx))
+
+	for _, path := range android.SortedKeys(logtagsFilePaths) {
+		cmd.Text(path)
+	}
+
+	f.appendToEntry(ctx, eventLogtagsPath)
+}
+
+type partition interface {
+	PartitionType() string
+}
+
+func (f *filesystem) PartitionType() string {
+	return proptools.StringDefault(f.properties.Partition_type, "system")
+}
+
+var _ partition = (*filesystem)(nil)
+
 var _ android.AndroidMkEntriesProvider = (*filesystem)(nil)
 
 // Implements android.AndroidMkEntriesProvider
@@ -452,21 +601,12 @@
 			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())
 			},
 		},
 	}}
 }
 
-var _ android.OutputFileProducer = (*filesystem)(nil)
-
-// Implements android.OutputFileProducer
-func (f *filesystem) OutputFiles(tag string) (android.Paths, error) {
-	if tag == "" {
-		return []android.Path{f.output}, nil
-	}
-	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
-
 // Filesystem is the public interface for the filesystem struct. Currently, it's only for the apex
 // package to have access to the output file.
 type Filesystem interface {
@@ -511,6 +651,40 @@
 
 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()
 }
+
+// android_filesystem_defaults
+
+type filesystemDefaults struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+
+	properties filesystemDefaultsProperties
+}
+
+type filesystemDefaultsProperties struct {
+	// Identifies which partition this is for //visibility:any_system_image (and others) visibility
+	// checks, and will be used in the future for API surface checks.
+	Partition_type *string
+}
+
+// android_filesystem_defaults is a default module for android_filesystem and android_system_image
+func filesystemDefaultsFactory() android.Module {
+	module := &filesystemDefaults{}
+	module.AddProperties(&module.properties)
+	module.AddProperties(&android.PackagingProperties{})
+	android.InitDefaultsModule(module)
+	return module
+}
+
+func (f *filesystemDefaults) PartitionType() string {
+	return proptools.StringDefault(f.properties.Partition_type, "system")
+}
+
+var _ partition = (*filesystemDefaults)(nil)
+
+func (f *filesystemDefaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	validatePartitionType(ctx, f)
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 6d62746..2dc8c21 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -23,6 +23,8 @@
 	"android/soong/bpf"
 	"android/soong/cc"
 	"android/soong/etc"
+	"android/soong/java"
+	"android/soong/phony"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -33,9 +35,13 @@
 
 var fixture = android.GroupFixturePreparers(
 	android.PrepareForIntegrationTestWithAndroid,
+	android.PrepareForTestWithAndroidBuildComponents,
 	bpf.PrepareForTestWithBpf,
-	etc.PrepareForTestWithPrebuiltEtc,
 	cc.PrepareForIntegrationTestWithCc,
+	etc.PrepareForTestWithPrebuiltEtc,
+	java.PrepareForTestWithJavaBuildComponents,
+	java.PrepareForTestWithJavaDefaultModules,
+	phony.PrepareForTestWithPhony,
 	PrepareForTestWithFilesystemBuildComponents,
 )
 
@@ -47,6 +53,7 @@
 				common: {
 					deps: [
 						"bpf.o",
+						"phony",
 					],
 				},
 				lib32: {
@@ -76,6 +83,38 @@
 
 		cc_library {
 			name: "libbar",
+			required: ["libbaz"],
+			target: {
+				platform: {
+					required: ["lib_platform_only"],
+				},
+			},
+		}
+
+		cc_library {
+			name: "libbaz",
+		}
+
+		cc_library {
+			name: "lib_platform_only",
+		}
+
+		phony {
+			name: "phony",
+			required: [
+				"libquz",
+				"myapp",
+			],
+		}
+
+		cc_library {
+			name: "libquz",
+		}
+
+		android_app {
+			name: "myapp",
+			platform_apis: true,
+			installable: true,
 		}
 	`)
 
@@ -84,9 +123,13 @@
 
 	fs := result.ModuleForTests("myfilesystem", "android_common").Module().(*filesystem)
 	expected := []string{
+		"app/myapp/myapp.apk",
 		"bin/foo",
 		"lib/libbar.so",
 		"lib64/libbar.so",
+		"lib64/libbaz.so",
+		"lib64/libquz.so",
+		"lib64/lib_platform_only.so",
 		"etc/bpf/bpf.o",
 	}
 	for _, e := range expected {
@@ -256,43 +299,6 @@
 		cmd, "--include_descriptors_from_image ")
 }
 
-func TestFileSystemShouldInstallCoreVariantIfTargetBuildAppsIsSet(t *testing.T) {
-	context := android.GroupFixturePreparers(
-		fixture,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.Unbundled_build_apps = []string{"bar"}
-		}),
-	)
-	result := context.RunTestWithBp(t, `
-		android_system_image {
-			name: "myfilesystem",
-			deps: [
-				"libfoo",
-			],
-			linker_config_src: "linker.config.json",
-		}
-
-		cc_library {
-			name: "libfoo",
-			shared_libs: [
-				"libbar",
-			],
-			stl: "none",
-		}
-
-		cc_library {
-			name: "libbar",
-			sdk_version: "9",
-			stl: "none",
-		}
-	`)
-
-	inputs := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img").Implicits
-	android.AssertStringListContains(t, "filesystem should have libbar even for unbundled build",
-		inputs.Strings(),
-		"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
-}
-
 func TestFileSystemWithCoverageVariants(t *testing.T) {
 	context := android.GroupFixturePreparers(
 		fixture,
@@ -345,3 +351,212 @@
 		t.Error("prebuilt should use cov variant of filesystem")
 	}
 }
+
+func TestSystemImageDefaults(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem_defaults {
+			name: "defaults",
+			multilib: {
+				common: {
+					deps: [
+						"phony",
+					],
+				},
+				lib64: {
+					deps: [
+						"libbar",
+					],
+				},
+			},
+			compile_multilib: "both",
+		}
+
+		android_system_image {
+			name: "system",
+			defaults: ["defaults"],
+			multilib: {
+				lib32: {
+					deps: [
+						"foo",
+						"libbar",
+					],
+				},
+			},
+		}
+
+		cc_binary {
+			name: "foo",
+			compile_multilib: "prefer32",
+		}
+
+		cc_library {
+			name: "libbar",
+			required: ["libbaz"],
+		}
+
+		cc_library {
+			name: "libbaz",
+		}
+
+		phony {
+			name: "phony",
+			required: ["libquz"],
+		}
+
+		cc_library {
+			name: "libquz",
+		}
+	`)
+
+	fs := result.ModuleForTests("system", "android_common").Module().(*systemImage)
+	expected := []string{
+		"bin/foo",
+		"lib/libbar.so",
+		"lib64/libbar.so",
+		"lib64/libbaz.so",
+		"lib64/libquz.so",
+	}
+	for _, e := range expected {
+		android.AssertStringListContains(t, "missing entry", fs.entries, e)
+	}
+}
+
+func TestInconsistentPartitionTypesInDefaults(t *testing.T) {
+	fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		"doesn't match with the partition type")).
+		RunTestWithBp(t, `
+		android_filesystem_defaults {
+			name: "system_ext_def",
+			partition_type: "system_ext",
+		}
+
+		android_filesystem_defaults {
+			name: "system_def",
+			partition_type: "system",
+			defaults: ["system_ext_def"],
+		}
+
+		android_system_image {
+			name: "system",
+			defaults: ["system_def"],
+		}
+	`)
+}
+
+func TestPreventDuplicatedEntries(t *testing.T) {
+	fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+		"packaging conflict at")).
+		RunTestWithBp(t, `
+		android_filesystem {
+			name: "fs",
+			deps: [
+				"foo",
+				"foo_dup",
+			],
+		}
+
+		cc_binary {
+			name: "foo",
+		}
+
+		cc_binary {
+			name: "foo_dup",
+			stem: "foo",
+		}
+	`)
+}
+
+func TestTrackPhonyAsRequiredDep(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem {
+			name: "fs",
+			deps: ["foo"],
+		}
+
+		cc_binary {
+			name: "foo",
+			required: ["phony"],
+		}
+
+		phony {
+			name: "phony",
+			required: ["libbar"],
+		}
+
+		cc_library {
+			name: "libbar",
+		}
+	`)
+
+	fs := result.ModuleForTests("fs", "android_common").Module().(*filesystem)
+	expected := []string{
+		"bin/foo",
+		"lib64/libbar.so",
+	}
+	for _, e := range expected {
+		android.AssertStringListContains(t, "missing entry", fs.entries, e)
+	}
+}
+
+func TestFilterOutUnsupportedArches(t *testing.T) {
+	result := fixture.RunTestWithBp(t, `
+		android_filesystem {
+			name: "fs_64_only",
+			deps: ["foo"],
+		}
+
+		android_filesystem {
+			name: "fs_64_32",
+			compile_multilib: "both",
+			deps: ["foo"],
+		}
+
+		cc_binary {
+			name: "foo",
+			required: ["phony"],
+		}
+
+		phony {
+			name: "phony",
+			required: [
+				"libbar",
+				"app",
+			],
+		}
+
+		cc_library {
+			name: "libbar",
+		}
+
+		android_app {
+			name: "app",
+			srcs: ["a.java"],
+			platform_apis: true,
+		}
+	`)
+	testcases := []struct {
+		fsName     string
+		expected   []string
+		unexpected []string
+	}{
+		{
+			fsName:     "fs_64_only",
+			expected:   []string{"app/app/app.apk", "bin/foo", "lib64/libbar.so"},
+			unexpected: []string{"lib/libbar.so"},
+		},
+		{
+			fsName:     "fs_64_32",
+			expected:   []string{"app/app/app.apk", "bin/foo", "lib64/libbar.so", "lib/libbar.so"},
+			unexpected: []string{},
+		},
+	}
+	for _, c := range testcases {
+		fs := result.ModuleForTests(c.fsName, "android_common").Module().(*filesystem)
+		for _, e := range c.expected {
+			android.AssertStringListContains(t, "missing entry", fs.entries, e)
+		}
+		for _, e := range c.unexpected {
+			android.AssertStringListDoesNotContain(t, "unexpected entry", fs.entries, e)
+		}
+	}
+}
diff --git a/filesystem/fsverity_metadata.go b/filesystem/fsverity_metadata.go
new file mode 100644
index 0000000..d7bb654
--- /dev/null
+++ b/filesystem/fsverity_metadata.go
@@ -0,0 +1,179 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package filesystem
+
+import (
+	"path/filepath"
+	"strings"
+
+	"android/soong/android"
+)
+
+type fsverityProperties struct {
+	// Patterns of files for fsverity metadata generation.  For each matched file, a .fsv_meta file
+	// will be generated and included to the filesystem image.
+	// etc/security/fsverity/BuildManifest.apk will also be generated which contains information
+	// about generated .fsv_meta files.
+	Inputs []string
+
+	// APK libraries to link against, for etc/security/fsverity/BuildManifest.apk
+	Libs []string `android:"path"`
+}
+
+func (f *filesystem) writeManifestGeneratorListFile(ctx android.ModuleContext, outputPath android.OutputPath, matchedSpecs []android.PackagingSpec, rebasedDir android.OutputPath) {
+	var buf strings.Builder
+	for _, spec := range matchedSpecs {
+		buf.WriteString(rebasedDir.Join(ctx, spec.RelPathInPackage()).String())
+		buf.WriteRune('\n')
+	}
+	android.WriteFileRuleVerbatim(ctx, outputPath, buf.String())
+}
+
+func (f *filesystem) buildFsverityMetadataFiles(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir android.OutputPath, rebasedDir android.OutputPath) {
+	match := func(path string) bool {
+		for _, pattern := range f.properties.Fsverity.Inputs {
+			if matched, err := filepath.Match(pattern, path); matched {
+				return true
+			} else if err != nil {
+				ctx.PropertyErrorf("fsverity.inputs", "bad pattern %q", pattern)
+				return false
+			}
+		}
+		return false
+	}
+
+	var matchedSpecs []android.PackagingSpec
+	for _, relPath := range android.SortedKeys(specs) {
+		if match(relPath) {
+			matchedSpecs = append(matchedSpecs, specs[relPath])
+		}
+	}
+
+	if len(matchedSpecs) == 0 {
+		return
+	}
+
+	fsverityBuilderPath := android.PathForModuleOut(ctx, "fsverity_builder.sh")
+	metadataGeneratorPath := ctx.Config().HostToolPath(ctx, "fsverity_metadata_generator")
+	fsverityPath := ctx.Config().HostToolPath(ctx, "fsverity")
+
+	cmd := builder.Command().Tool(fsverityBuilderPath)
+
+	// STEP 1: generate .fsv_meta
+	var sb strings.Builder
+	sb.WriteString("set -e\n")
+	cmd.Implicit(metadataGeneratorPath).Implicit(fsverityPath)
+	for _, spec := range matchedSpecs {
+		// srcPath is copied by CopySpecsToDir()
+		srcPath := rebasedDir.Join(ctx, spec.RelPathInPackage())
+		destPath := rebasedDir.Join(ctx, spec.RelPathInPackage()+".fsv_meta")
+		sb.WriteString(metadataGeneratorPath.String())
+		sb.WriteString(" --fsverity-path ")
+		sb.WriteString(fsverityPath.String())
+		sb.WriteString(" --signature none --hash-alg sha256 --output ")
+		sb.WriteString(destPath.String())
+		sb.WriteRune(' ')
+		sb.WriteString(srcPath.String())
+		sb.WriteRune('\n')
+		f.appendToEntry(ctx, destPath)
+	}
+
+	// STEP 2: generate signed BuildManifest.apk
+	// STEP 2-1: generate build_manifest.pb
+	assetsPath := android.PathForModuleOut(ctx, "fsverity_manifest/assets")
+	manifestPbPath := assetsPath.Join(ctx, "build_manifest.pb")
+	manifestGeneratorPath := ctx.Config().HostToolPath(ctx, "fsverity_manifest_generator")
+	cmd.Implicit(manifestGeneratorPath)
+	sb.WriteString("rm -rf ")
+	sb.WriteString(assetsPath.String())
+	sb.WriteString(" && mkdir -p ")
+	sb.WriteString(assetsPath.String())
+	sb.WriteRune('\n')
+	sb.WriteString(manifestGeneratorPath.String())
+	sb.WriteString(" --fsverity-path ")
+	sb.WriteString(fsverityPath.String())
+	sb.WriteString(" --base-dir ")
+	sb.WriteString(rootDir.String())
+	sb.WriteString(" --output ")
+	sb.WriteString(manifestPbPath.String())
+	sb.WriteRune(' ')
+	f.appendToEntry(ctx, manifestPbPath)
+
+	manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity_manifest.list")
+	f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath.OutputPath, matchedSpecs, rebasedDir)
+	sb.WriteRune('@')
+	sb.WriteString(manifestGeneratorListPath.String())
+	sb.WriteRune('\n')
+	cmd.Implicit(manifestGeneratorListPath)
+	f.appendToEntry(ctx, manifestGeneratorListPath.OutputPath)
+
+	// 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 ")
+	sb.WriteString(apkPath.String())
+	sb.WriteString(" -A ")
+	sb.WriteString(assetsPath.String())
+	for _, lib := range libs {
+		sb.WriteString(" -I ")
+		sb.WriteString(lib.String())
+	}
+	minSdkVersion := ctx.Config().PlatformSdkCodename()
+	if minSdkVersion == "REL" {
+		minSdkVersion = ctx.Config().PlatformSdkVersion().String()
+	}
+	sb.WriteString(" --min-sdk-version ")
+	sb.WriteString(minSdkVersion)
+	sb.WriteString(" --version-code ")
+	sb.WriteString(ctx.Config().PlatformSdkVersion().String())
+	sb.WriteString(" --version-name ")
+	sb.WriteString(ctx.Config().AppsDefaultVersionName())
+	sb.WriteString(" --manifest ")
+	sb.WriteString(manifestTemplatePath.String())
+	sb.WriteString(" --rename-manifest-package com.android.security.fsverity_metadata.")
+	sb.WriteString(f.partitionName())
+	sb.WriteRune('\n')
+
+	f.appendToEntry(ctx, apkPath)
+
+	// STEP 2-3: sign BuildManifest.apk
+	apksignerPath := ctx.Config().HostToolPath(ctx, "apksigner")
+	pemPath, keyPath := ctx.Config().DefaultAppCertificate(ctx)
+	cmd.Implicit(apksignerPath)
+	cmd.Implicit(pemPath)
+	cmd.Implicit(keyPath)
+	cmd.ImplicitOutput(idsigPath)
+	sb.WriteString(apksignerPath.String())
+	sb.WriteString(" sign --in ")
+	sb.WriteString(apkPath.String())
+	sb.WriteString(" --cert ")
+	sb.WriteString(pemPath.String())
+	sb.WriteString(" --key ")
+	sb.WriteString(keyPath.String())
+	sb.WriteRune('\n')
+
+	f.appendToEntry(ctx, idsigPath)
+
+	android.WriteExecutableFileRuleVerbatim(ctx, fsverityBuilderPath, sb.String())
+}
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index e2f7d7b..e483fe4 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -185,6 +185,8 @@
 
 	l.installDir = android.PathForModuleInstall(ctx, "etc")
 	ctx.InstallFile(l.installDir, l.installFileName(), l.output)
+
+	ctx.SetOutputFiles([]android.Path{l.output}, "")
 }
 
 // Add a rule that converts the filesystem for the given partition to the given rule builder. The
@@ -231,13 +233,3 @@
 func (l *logicalPartition) SignedOutputPath() android.Path {
 	return nil // logical partition is not signed by itself
 }
-
-var _ android.OutputFileProducer = (*logicalPartition)(nil)
-
-// Implements android.OutputFileProducer
-func (l *logicalPartition) OutputFiles(tag string) (android.Paths, error) {
-	if tag == "" {
-		return []android.Path{l.output}, nil
-	}
-	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/filesystem/raw_binary.go b/filesystem/raw_binary.go
index 1544ea7..ad36c29 100644
--- a/filesystem/raw_binary.go
+++ b/filesystem/raw_binary.go
@@ -15,8 +15,6 @@
 package filesystem
 
 import (
-	"fmt"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -88,6 +86,8 @@
 	r.output = outputFile
 	r.installDir = android.PathForModuleInstall(ctx, "etc")
 	ctx.InstallFile(r.installDir, r.installFileName(), r.output)
+
+	ctx.SetOutputFiles([]android.Path{r.output}, "")
 }
 
 var _ android.AndroidMkEntriesProvider = (*rawBinary)(nil)
@@ -109,13 +109,3 @@
 func (r *rawBinary) SignedOutputPath() android.Path {
 	return nil
 }
-
-var _ android.OutputFileProducer = (*rawBinary)(nil)
-
-// Implements android.OutputFileProducer
-func (r *rawBinary) OutputFiles(tag string) (android.Paths, error) {
-	if tag == "" {
-		return []android.Path{r.output}, nil
-	}
-	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 34f4ffb..69d922d 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -43,6 +43,9 @@
 }
 
 func (s *systemImage) buildExtraFiles(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths {
+	if s.filesystem.properties.Partition_type != nil {
+		ctx.PropertyErrorf("partition_type", "partition_type must be unset on an android_system_image module. It is assumed to be 'system'.")
+	}
 	lc := s.buildLinkerConfigFile(ctx, root)
 	// Add more files if needed
 	return []android.OutputPath{lc}
@@ -53,26 +56,48 @@
 	output := root.Join(ctx, "system", "etc", "linker.config.pb")
 
 	// we need "Module"s for packaging items
-	var otherModules []android.Module
+	modulesInPackageByModule := make(map[android.Module]bool)
+	modulesInPackageByName := make(map[string]bool)
+
 	deps := s.gatherFilteredPackagingSpecs(ctx)
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		for _, ps := range child.PackagingSpecs() {
 			if _, ok := deps[ps.RelPathInPackage()]; ok {
-				otherModules = append(otherModules, child)
+				modulesInPackageByModule[child] = true
+				modulesInPackageByName[child.Name()] = true
+				return true
 			}
 		}
 		return true
 	})
 
+	provideModules := make([]android.Module, 0, len(modulesInPackageByModule))
+	for mod := range modulesInPackageByModule {
+		provideModules = append(provideModules, mod)
+	}
+
+	var requireModules []android.Module
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		_, parentInPackage := modulesInPackageByModule[parent]
+		_, childInPackageName := modulesInPackageByName[child.Name()]
+
+		// When parent is in the package, and child (or its variant) is not, this can be from an interface.
+		if parentInPackage && !childInPackageName {
+			requireModules = append(requireModules, child)
+		}
+		return true
+	})
+
 	builder := android.NewRuleBuilder(pctx, ctx)
-	linkerconfig.BuildLinkerConfig(ctx, builder, input, otherModules, output)
+	linkerconfig.BuildLinkerConfig(ctx, builder, input, provideModules, requireModules, output)
 	builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String())
 	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 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 43a2f37..0c6e7f4 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -211,6 +211,8 @@
 
 	v.installDir = android.PathForModuleInstall(ctx, "etc")
 	ctx.InstallFile(v.installDir, v.installFileName(), v.output)
+
+	ctx.SetOutputFiles([]android.Path{v.output}, "")
 }
 
 // Returns the embedded shell command that prints the rollback index
@@ -288,13 +290,3 @@
 func (v *vbmeta) SignedOutputPath() android.Path {
 	return v.OutputPath() // vbmeta is always signed
 }
-
-var _ android.OutputFileProducer = (*vbmeta)(nil)
-
-// Implements android.OutputFileProducer
-func (v *vbmeta) OutputFiles(tag string) (android.Paths, error) {
-	if tag == "" {
-		return []android.Path{v.output}, nil
-	}
-	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 47fd8f4..306d65e 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -449,10 +449,10 @@
 	}
 }
 
-func IsValid(fuzzModule FuzzModule) bool {
+func IsValid(ctx android.ConfigAndErrorContext, fuzzModule FuzzModule) bool {
 	// Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
 	// fuzz targets we're going to package anyway.
-	if !fuzzModule.Enabled() || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() {
+	if !fuzzModule.Enabled(ctx) || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() {
 		return false
 	}
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index cf2a966..5b40768 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -126,7 +126,7 @@
 	//  $(out): a single output file.
 	//  $(genDir): the sandbox directory for this tool; contains $(out).
 	//  $$: a literal $
-	Cmd *string
+	Cmd proptools.Configurable[string] `android:"replace_instead_of_append"`
 
 	// name of the modules (if any) that produces the host executable.   Leave empty for
 	// prebuilts or scripts that do not need a module to build them.
@@ -180,9 +180,6 @@
 
 	subName string
 	subDir  string
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
 }
 
 type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
@@ -216,21 +213,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 {
@@ -299,7 +282,7 @@
 				case android.HostToolProvider:
 					// A HostToolProvider provides the path to a tool, which will be copied
 					// into the sandbox.
-					if !t.(android.Module).Enabled() {
+					if !t.(android.Module).Enabled(ctx) {
 						if ctx.Config().AllowMissingDependencies() {
 							ctx.AddMissingDependencies([]string{tool})
 						} else {
@@ -317,7 +300,17 @@
 						// required relative locations of the tool and its dependencies, use those
 						// instead.  They will be copied to those relative locations in the sbox
 						// sandbox.
-						packagedTools = append(packagedTools, specs...)
+						// Care must be taken since TransitivePackagingSpec may return device-side
+						// paths via the required property. Filter them out.
+						for i, ps := range specs {
+							if ps.Partition() != "" {
+								if i == 0 {
+									panic("first PackagingSpec is assumed to be the host-side tool")
+								}
+								continue
+							}
+							packagedTools = append(packagedTools, ps)
+						}
 						// Assume that the first PackagingSpec of the module is the tool.
 						addLocationLabel(tag.label, packagedToolLocation{specs[0]})
 					} else {
@@ -396,7 +389,7 @@
 	var outputFiles android.WritablePaths
 	var zipArgs strings.Builder
 
-	cmd := String(g.properties.Cmd)
+	cmd := g.properties.Cmd.GetOrDefault(ctx, "")
 	if g.CmdModifier != nil {
 		cmd = g.CmdModifier(ctx, cmd)
 	}
@@ -578,24 +571,19 @@
 		})
 		g.outputDeps = android.Paths{phonyFile}
 	}
-	android.CollectDependencyAconfigFiles(ctx, &g.mergedAconfigFiles)
+
+	g.setOutputFiles(ctx)
 }
 
-func (g *Module) AndroidMkEntries() []android.AndroidMkEntries {
-	ret := android.AndroidMkEntries{
-		OutputFile: android.OptionalPathForPath(g.outputFiles[0]),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				android.SetAconfigFileMkEntries(g.AndroidModuleBase(), entries, g.mergedAconfigFiles)
-			},
-		},
+func (g *Module) setOutputFiles(ctx android.ModuleContext) {
+	if len(g.outputFiles) == 0 {
+		return
 	}
-
-	return []android.AndroidMkEntries{ret}
-}
-
-func (g *Module) AndroidModuleBase() *android.ModuleBase {
-	return &g.ModuleBase
+	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.
@@ -655,13 +643,15 @@
 type noopImageInterface struct{}
 
 func (x noopImageInterface) ImageMutatorBegin(android.BaseModuleContext)                 {}
+func (x noopImageInterface) VendorVariantNeeded(android.BaseModuleContext) bool          { return false }
+func (x noopImageInterface) ProductVariantNeeded(android.BaseModuleContext) bool         { return false }
 func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool            { return false }
 func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool         { return false }
 func (x noopImageInterface) VendorRamdiskVariantNeeded(android.BaseModuleContext) bool   { return false }
 func (x noopImageInterface) DebugRamdiskVariantNeeded(android.BaseModuleContext) bool    { return false }
 func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool        { return false }
 func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
-func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string) {
 }
 
 func NewGenSrcs() *Module {
@@ -704,13 +694,13 @@
 			rule := getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil))
 
 			for _, in := range shard {
-				outFile := android.GenPathWithExt(ctx, finalSubDir, in, String(properties.Output_extension))
+				outFile := android.GenPathWithExtAndTrimExt(ctx, finalSubDir, in, String(properties.Output_extension), String(properties.Trim_extension))
 
 				// If sharding is enabled, then outFile is the path to the output file in
 				// the shard directory, and copyTo is the path to the output file in the
 				// final directory.
 				if len(shards) > 1 {
-					shardFile := android.GenPathWithExt(ctx, genSubDir, in, String(properties.Output_extension))
+					shardFile := android.GenPathWithExtAndTrimExt(ctx, genSubDir, in, String(properties.Output_extension), String(properties.Trim_extension))
 					copyTo = append(copyTo, outFile)
 					outFile = shardFile
 				}
@@ -764,6 +754,7 @@
 func GenSrcsFactory() android.Module {
 	m := NewGenSrcs()
 	android.InitAndroidModule(m)
+	android.InitDefaultableModule(m)
 	return m
 }
 
@@ -776,6 +767,9 @@
 
 	// Additional files needed for build that are not tooling related.
 	Data []string `android:"path"`
+
+	// Trim the matched extension for each input file, and it should start with ".".
+	Trim_extension *string
 }
 
 const defaultShardSize = 50
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 2dc6a79..fba9aec 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -894,6 +894,155 @@
 	)
 }
 
+func TestGenSrcsWithTrimExtAndOutpuExtension(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForGenRuleTest,
+		android.FixtureMergeMockFs(android.MockFS{
+			"external-protos/path/Android.bp": []byte(`
+				filegroup {
+					name: "external-protos",
+					srcs: [
+					    "baz.a.b.c.proto/baz.a.b.c.proto",
+					    "bar.a.b.c.proto",
+					    "qux.ext.a.b.c.proto",
+					],
+				}
+			`),
+			"package-dir/Android.bp": []byte(`
+				gensrcs {
+					name: "module-name",
+					cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
+					srcs: [
+						"src/foo.a.b.c.proto",
+						":external-protos",
+					],
+
+					trim_extension: ".a.b.c.proto",
+					output_extension: "proto.h",
+				}
+			`),
+		}),
+	).RunTest(t)
+
+	exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
+	gen := result.Module("module-name", "").(*Module)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"include path",
+		[]string{exportedIncludeDir},
+		gen.exportedIncludeDirs,
+	)
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"files",
+		[]string{
+			exportedIncludeDir + "/package-dir/src/foo.proto.h",
+			exportedIncludeDir + "/external-protos/path/baz.a.b.c.proto/baz.proto.h",
+			exportedIncludeDir + "/external-protos/path/bar.proto.h",
+			exportedIncludeDir + "/external-protos/path/qux.ext.proto.h",
+		},
+		gen.outputFiles,
+	)
+}
+
+func TestGenSrcsWithTrimExtButNoOutpuExtension(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForGenRuleTest,
+		android.FixtureMergeMockFs(android.MockFS{
+			"external-protos/path/Android.bp": []byte(`
+				filegroup {
+					name: "external-protos",
+					srcs: [
+					    "baz.a.b.c.proto/baz.a.b.c.proto",
+					    "bar.a.b.c.proto",
+					    "qux.ext.a.b.c.proto",
+					],
+				}
+			`),
+			"package-dir/Android.bp": []byte(`
+				gensrcs {
+					name: "module-name",
+					cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
+					srcs: [
+						"src/foo.a.b.c.proto",
+						":external-protos",
+					],
+
+					trim_extension: ".a.b.c.proto",
+				}
+			`),
+		}),
+	).RunTest(t)
+
+	exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
+	gen := result.Module("module-name", "").(*Module)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"include path",
+		[]string{exportedIncludeDir},
+		gen.exportedIncludeDirs,
+	)
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"files",
+		[]string{
+			exportedIncludeDir + "/package-dir/src/foo",
+			exportedIncludeDir + "/external-protos/path/baz.a.b.c.proto/baz",
+			exportedIncludeDir + "/external-protos/path/bar",
+			exportedIncludeDir + "/external-protos/path/qux.ext",
+		},
+		gen.outputFiles,
+	)
+}
+
+func TestGenSrcsWithOutpuExtension(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForGenRuleTest,
+		android.FixtureMergeMockFs(android.MockFS{
+			"external-protos/path/Android.bp": []byte(`
+				filegroup {
+					name: "external-protos",
+					srcs: ["baz/baz.a.b.c.proto", "bar.a.b.c.proto"],
+				}
+			`),
+			"package-dir/Android.bp": []byte(`
+				gensrcs {
+					name: "module-name",
+					cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
+					srcs: [
+						"src/foo.a.b.c.proto",
+						":external-protos",
+					],
+
+					output_extension: "proto.h",
+				}
+			`),
+		}),
+	).RunTest(t)
+
+	exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
+	gen := result.Module("module-name", "").(*Module)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"include path",
+		[]string{exportedIncludeDir},
+		gen.exportedIncludeDirs,
+	)
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"files",
+		[]string{
+			exportedIncludeDir + "/package-dir/src/foo.a.b.c.proto.h",
+			exportedIncludeDir + "/external-protos/path/baz/baz.a.b.c.proto.h",
+			exportedIncludeDir + "/external-protos/path/bar.a.b.c.proto.h",
+		},
+		gen.outputFiles,
+	)
+}
+
 func TestPrebuiltTool(t *testing.T) {
 	testcases := []struct {
 		name             string
@@ -1105,12 +1254,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 1174958..aa43066 100644
--- a/go.mod
+++ b/go.mod
@@ -1,10 +1,9 @@
 module android/soong
 
-go 1.21
+go 1.22
 
 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 7c6022b..46a135b 100644
--- a/go.work
+++ b/go.work
@@ -1,12 +1,10 @@
-go 1.21
+go 1.22
 
 use (
 	.
 	../../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/java/Android.bp b/java/Android.bp
index 54b36ab..a941754 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",
diff --git a/java/aar.go b/java/aar.go
index 2835792..2f49a95 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -164,7 +164,9 @@
 }
 
 func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool {
-	return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault())
+	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")
 }
 
 func (a *aapt) filterProduct() string {
@@ -351,14 +353,16 @@
 	classLoaderContexts            dexpreopt.ClassLoaderContextMap
 	excludedLibs                   []string
 	enforceDefaultTargetSdkVersion bool
+	forceNonFinalResourceIDs       bool
 	extraLinkFlags                 []string
 	aconfigTextFiles               android.Paths
+	usesLibrary                    *usesLibrary
 }
 
 func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptions) {
 
 	staticResourcesNodesDepSet, sharedResourcesNodesDepSet, staticRRODirsDepSet, staticManifestsDepSet, sharedExportPackages, libFlags :=
-		aaptLibs(ctx, opts.sdkContext, opts.classLoaderContexts)
+		aaptLibs(ctx, opts.sdkContext, opts.classLoaderContexts, opts.usesLibrary)
 
 	// Exclude any libraries from the supplied list.
 	opts.classLoaderContexts = opts.classLoaderContexts.ExcludeLibs(opts.excludedLibs)
@@ -386,11 +390,6 @@
 	// Add additional manifest files to transitive manifests.
 	additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests)
 	transitiveManifestPaths := append(android.Paths{manifestPath}, additionalManifests...)
-	// TODO(b/288358614): Soong has historically not merged manifests from dependencies of android_library_import
-	// modules.  Merging manifests from dependencies could remove the need for pom2bp to generate the "-nodeps" copies
-	// of androidx libraries, but doing so triggers errors due to errors introduced by existing dependencies of
-	// android_library_import modules.  If this is fixed, staticManifestsDepSet can be dropped completely in favor of
-	// staticResourcesNodesDepSet.manifests()
 	transitiveManifestPaths = append(transitiveManifestPaths, staticManifestsDepSet.ToList()...)
 
 	if len(transitiveManifestPaths) > 1 && !Bool(a.aaptProperties.Dont_merge_manifests) {
@@ -419,6 +418,9 @@
 	if a.isLibrary {
 		linkFlags = append(linkFlags, "--static-lib")
 	}
+	if opts.forceNonFinalResourceIDs {
+		linkFlags = append(linkFlags, "--non-final-ids")
+	}
 
 	linkFlags = append(linkFlags, "--no-static-lib-packages")
 	if a.isLibrary && a.useResourceProcessorBusyBox(ctx) {
@@ -544,7 +546,8 @@
 
 	if a.useResourceProcessorBusyBox(ctx) {
 		rJar := android.PathForModuleOut(ctx, "busybox/R.jar")
-		resourceProcessorBusyBoxGenerateBinaryR(ctx, rTxt, a.mergedManifestFile, rJar, staticDeps, a.isLibrary, a.aaptProperties.Aaptflags)
+		resourceProcessorBusyBoxGenerateBinaryR(ctx, rTxt, a.mergedManifestFile, rJar, staticDeps, a.isLibrary, a.aaptProperties.Aaptflags,
+			opts.forceNonFinalResourceIDs)
 		aapt2ExtractExtraPackages(ctx, extraPackages, rJar)
 		transitiveRJars = append(transitiveRJars, rJar)
 		a.rJar = rJar
@@ -608,7 +611,8 @@
 // using Bazel's ResourceProcessorBusyBox tool, which is faster than compiling the R.java files and
 // supports producing classes for static dependencies that only include resources from that dependency.
 func resourceProcessorBusyBoxGenerateBinaryR(ctx android.ModuleContext, rTxt, manifest android.Path,
-	rJar android.WritablePath, transitiveDeps transitiveAarDeps, isLibrary bool, aaptFlags []string) {
+	rJar android.WritablePath, transitiveDeps transitiveAarDeps, isLibrary bool, aaptFlags []string,
+	forceNonFinalIds bool) {
 
 	var args []string
 	var deps android.Paths
@@ -618,6 +622,9 @@
 		// to ResourceProcessorBusyBox so that it can regenerate R.class files with the final resource IDs for each
 		// package.
 		args, deps = transitiveDeps.resourceProcessorDeps()
+		if forceNonFinalIds {
+			args = append(args, "--finalFields=false")
+		}
 	} else {
 		// When compiling a library don't pass any dependencies as it only needs to generate an R.class file for this
 		// library.  Pass --finalFields=false so that the R.class file contains non-final fields so they don't get
@@ -700,7 +707,8 @@
 }
 
 // aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
-func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap) (
+func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext,
+	classLoaderContexts dexpreopt.ClassLoaderContextMap, usesLibrary *usesLibrary) (
 	staticResourcesNodes, sharedResourcesNodes *android.DepSet[*resourcesNode], staticRRODirs *android.DepSet[rroDir],
 	staticManifests *android.DepSet[android.Path], sharedLibs android.Paths, flags []string) {
 
@@ -750,6 +758,9 @@
 		}
 
 		addCLCFromDep(ctx, module, classLoaderContexts)
+		if usesLibrary != nil {
+			addMissingOptionalUsesLibsFromDep(ctx, module, usesLibrary)
+		}
 	})
 
 	// AAPT2 overlays are in lowest to highest priority order, the topological order will be reversed later.
@@ -757,7 +768,8 @@
 	// reverse later.
 	// NOTE: this is legacy and probably incorrect behavior, for most other cases (e.g. conflicting classes in
 	// dependencies) the highest priority dependency is listed first, but for resources the highest priority
-	// dependency has to be listed last.
+	// dependency has to be listed last.  This is also inconsistent with the way manifests from the same
+	// transitive dependencies are merged.
 	staticResourcesNodes = android.NewDepSet(android.TOPOLOGICAL, nil,
 		android.ReverseSliceInPlace(staticResourcesNodeDepSets))
 	sharedResourcesNodes = android.NewDepSet(android.TOPOLOGICAL, nil,
@@ -786,27 +798,15 @@
 	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) {
+	a.usesLibrary.deps(ctx, false)
 	a.Module.deps(ctx)
 	sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
 	if sdkDep.hasFrameworkLibs() {
 		a.aapt.deps(ctx, sdkDep)
 	}
-	a.usesLibrary.deps(ctx, false)
 
 	for _, aconfig_declaration := range a.aaptProperties.Flags_packages {
 		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration)
@@ -819,12 +819,14 @@
 	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,
 		},
 	)
 
@@ -893,6 +895,17 @@
 			JniPackages: prebuiltJniPackages,
 		})
 	}
+
+	android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{
+		AconfigTextFiles: aconfigTextFilePaths,
+	})
+
+	a.setOutputFiles(ctx)
+}
+
+func (a *AndroidLibrary) setOutputFiles(ctx android.ModuleContext) {
+	ctx.SetOutputFiles([]android.Path{a.aarFile}, ".aar")
+	setOutputFiles(ctx, a.Library.Module)
 }
 
 func (a *AndroidLibrary) IDEInfo(dpInfo *android.IdeInfo) {
@@ -918,7 +931,8 @@
 	module.Module.addHostAndDeviceProperties()
 	module.AddProperties(
 		&module.aaptProperties,
-		&module.androidLibraryProperties)
+		&module.androidLibraryProperties,
+		&module.sourceProperties)
 
 	module.androidLibraryProperties.BuildAAR = true
 	module.Module.linter.library = true
@@ -957,6 +971,9 @@
 	// will be passed transitively through android_libraries to an android_app.
 	//TODO(b/241138093) evaluate whether we can have this flag default to true for Bazel conversion
 	Extract_jni *bool
+
+	// If set, overrides the manifest extracted from the AAR with the provided path.
+	Manifest *string `android:"path"`
 }
 
 type AARImport struct {
@@ -972,12 +989,14 @@
 
 	properties AARImportProperties
 
-	classpathFile                      android.WritablePath
+	headerJarFile                      android.WritablePath
+	implementationJarFile              android.WritablePath
+	implementationAndResourcesJarFile  android.WritablePath
 	proguardFlags                      android.WritablePath
 	exportPackage                      android.WritablePath
 	transitiveAaptResourcePackagesFile android.Path
 	extraAaptPackagesFile              android.WritablePath
-	manifest                           android.WritablePath
+	manifest                           android.Path
 	assetsPackage                      android.WritablePath
 	rTxt                               android.WritablePath
 	rJar                               android.WritablePath
@@ -993,22 +1012,8 @@
 	sdkVersion    android.SdkSpec
 	minSdkVersion android.ApiLevel
 
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
-}
-
-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.classpathFile}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
+	usesLibrary
+	classLoaderContexts dexpreopt.ClassLoaderContextMap
 }
 
 func (a *AARImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
@@ -1088,6 +1093,8 @@
 
 	ctx.AddVariationDependencies(nil, libTag, a.properties.Libs...)
 	ctx.AddVariationDependencies(nil, staticLibTag, a.properties.Static_libs...)
+
+	a.usesLibrary.deps(ctx, false)
 }
 
 type JniPackageInfo struct {
@@ -1145,28 +1152,43 @@
 		TransformJetifier(ctx, a.aarPath.(android.WritablePath), inputFile)
 	}
 
+	jarName := ctx.ModuleName() + ".jar"
 	extractedAARDir := android.PathForModuleOut(ctx, "aar")
-	a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar")
-	a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
+	classpathFile := extractedAARDir.Join(ctx, jarName)
+
+	extractedManifest := extractedAARDir.Join(ctx, "AndroidManifest.xml")
+	providedManifest := android.OptionalPathForModuleSrc(ctx, a.properties.Manifest)
+	if providedManifest.Valid() {
+		a.manifest = providedManifest.Path()
+	} else {
+		a.manifest = extractedManifest
+	}
+
 	a.rTxt = extractedAARDir.Join(ctx, "R.txt")
 	a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip")
 	a.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},
+			transitiveProguardFlags,
+		),
+		UnconditionallyExportedProguardFlags: android.NewDepSet[android.Path](
+			android.POSTORDER,
 			nil,
+			transitiveUnconditionalExportedFlags,
 		),
 	})
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        unzipAAR,
 		Input:       a.aarPath,
-		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage, a.rTxt},
+		Outputs:     android.WritablePaths{classpathFile, a.proguardFlags, extractedManifest, a.assetsPackage, a.rTxt},
 		Description: "unzip AAR",
 		Args: map[string]string{
 			"outDir":             extractedAARDir.String(),
-			"combinedClassesJar": a.classpathFile.String(),
+			"combinedClassesJar": classpathFile.String(),
 			"assetsPackage":      a.assetsPackage.String(),
 		},
 	})
@@ -1196,7 +1218,7 @@
 	linkDeps = append(linkDeps, a.manifest)
 
 	staticResourcesNodesDepSet, sharedResourcesNodesDepSet, staticRRODirsDepSet, staticManifestsDepSet, sharedLibs, libFlags :=
-		aaptLibs(ctx, android.SdkContext(a), nil)
+		aaptLibs(ctx, android.SdkContext(a), nil, nil)
 
 	_ = sharedResourcesNodesDepSet
 	_ = staticRRODirsDepSet
@@ -1221,7 +1243,7 @@
 		linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil, nil)
 
 	a.rJar = android.PathForModuleOut(ctx, "busybox/R.jar")
-	resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true, nil)
+	resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true, nil, false)
 
 	aapt2ExtractExtraPackages(ctx, a.extraAaptPackagesFile, a.rJar)
 
@@ -1239,13 +1261,7 @@
 	a.resourcesNodesDepSet = resourcesNodesDepSetBuilder.Build()
 
 	manifestDepSetBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(a.manifest)
-	// TODO(b/288358614): Soong has historically not merged manifests from dependencies of android_library_import
-	// modules.  Merging manifests from dependencies could remove the need for pom2bp to generate the "-nodeps" copies
-	// of androidx libraries, but doing so triggers errors due to errors introduced by existing dependencies of
-	// android_library_import modules.  If this is fixed, AndroidLibraryDependency.ManifestsDepSet can be dropped
-	// completely in favor of AndroidLibraryDependency.ResourceNodesDepSet.manifest
-	//manifestDepSetBuilder.Transitive(transitiveStaticDeps.manifests)
-	_ = staticManifestsDepSet
+	manifestDepSetBuilder.Transitive(staticManifestsDepSet)
 	a.manifestsDepSet = manifestDepSetBuilder.Build()
 
 	transitiveAaptResourcePackages := staticDeps.resPackages().Strings()
@@ -1257,12 +1273,74 @@
 	a.transitiveAaptResourcePackagesFile = transitiveAaptResourcePackagesFile
 
 	a.collectTransitiveHeaderJars(ctx)
+
+	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+
+	var staticJars android.Paths
+	var staticHeaderJars android.Paths
+	var staticResourceJars android.Paths
+	ctx.VisitDirectDeps(func(module android.Module) {
+		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			tag := ctx.OtherModuleDependencyTag(module)
+			switch tag {
+			case staticLibTag:
+				staticJars = append(staticJars, dep.ImplementationJars...)
+				staticHeaderJars = append(staticHeaderJars, dep.HeaderJars...)
+				staticResourceJars = append(staticResourceJars, dep.ResourceJars...)
+			}
+		}
+		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)
+	} else {
+		implementationJarFile = classpathFile
+	}
+
+	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]
+	}
+
+	// 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{},
+			false, nil, nil)
+		implementationAndResourcesJar = combinedJar
+	}
+
+	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)
+	} else {
+		a.headerJarFile = classpathFile
+	}
+
 	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
-		HeaderJars:                     android.PathsIfNonNil(a.classpathFile),
+		HeaderJars:                     android.PathsIfNonNil(a.headerJarFile),
+		ResourceJars:                   android.PathsIfNonNil(resourceJarFile),
 		TransitiveLibsHeaderJars:       a.transitiveLibsHeaderJars,
 		TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars,
-		ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
-		ImplementationJars:             android.PathsIfNonNil(a.classpathFile),
+		ImplementationAndResourcesJars: android.PathsIfNonNil(a.implementationAndResourcesJarFile),
+		ImplementationJars:             android.PathsIfNonNil(a.implementationJarFile),
 		StubsLinkType:                  Implementation,
 		// TransitiveAconfigFiles: // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
 	})
@@ -1291,19 +1369,21 @@
 	android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{
 		JniPackages: a.jniPackages,
 	})
-	android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles)
+
+	ctx.SetOutputFiles([]android.Path{a.implementationAndResourcesJarFile}, "")
+	ctx.SetOutputFiles([]android.Path{a.aarPath}, ".aar")
 }
 
 func (a *AARImport) HeaderJars() android.Paths {
-	return android.Paths{a.classpathFile}
+	return android.Paths{a.headerJarFile}
 }
 
 func (a *AARImport) ImplementationAndResourcesJars() android.Paths {
-	return android.Paths{a.classpathFile}
+	return android.Paths{a.implementationAndResourcesJarFile}
 }
 
-func (a *AARImport) DexJarBuildPath(ctx android.ModuleErrorfContext) android.Path {
-	return nil
+func (a *AARImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
+	return OptionalDexJarPath{}
 }
 
 func (a *AARImport) DexJarInstallPath() android.Path {
@@ -1311,9 +1391,11 @@
 }
 
 func (a *AARImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
-	return nil
+	return a.classLoaderContexts
 }
 
+var _ UsesLibraryDependency = (*AARImport)(nil)
+
 var _ android.ApexModule = (*AARImport)(nil)
 
 // Implements android.ApexModule
@@ -1322,13 +1404,19 @@
 }
 
 // Implements android.ApexModule
-func (g *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+func (a *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	return nil
 }
 
 var _ android.PrebuiltInterface = (*AARImport)(nil)
 
+func (a *AARImport) UsesLibrary() *usesLibrary {
+	return &a.usesLibrary
+}
+
+var _ ModuleWithUsesLibrary = (*AARImport)(nil)
+
 // android_library_import imports an `.aar` file into the build graph as if it was built with android_library.
 //
 // This module is not suitable for installing on a device, but can be used as a `static_libs` dependency of
@@ -1336,7 +1424,10 @@
 func AARImportFactory() android.Module {
 	module := &AARImport{}
 
-	module.AddProperties(&module.properties)
+	module.AddProperties(
+		&module.properties,
+		&module.usesLibrary.usesLibraryProperties,
+	)
 
 	android.InitPrebuiltModule(module, &module.properties.Aars)
 	android.InitApexModule(module)
diff --git a/java/aar_test.go b/java/aar_test.go
index 6bd53f2..ebad310 100644
--- a/java/aar_test.go
+++ b/java/aar_test.go
@@ -15,8 +15,9 @@
 package java
 
 import (
-	"android/soong/android"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestAarImportProducesJniPackages(t *testing.T) {
@@ -98,6 +99,7 @@
 		aconfig_declarations {
 			name: "bar",
 			package: "com.example.package.bar",
+			container: "com.android.foo",
 			srcs: [
 				"bar.aconfig",
 			],
@@ -105,6 +107,7 @@
 		aconfig_declarations {
 			name: "baz",
 			package: "com.example.package.baz",
+			container: "com.android.foo",
 			srcs: [
 				"baz.aconfig",
 			],
@@ -128,3 +131,49 @@
 		"--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt",
 	)
 }
+
+func TestAndroidLibraryOutputFilesRel(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		android_library {
+			name: "foo",
+			srcs: ["a.java"],
+			java_resources: ["foo.txt"],
+		}
+
+		android_library_import {
+			name: "bar",
+			aars: ["bar_prebuilt.aar"],
+
+		}
+
+		android_library_import {
+			name: "baz",
+			aars: ["baz_prebuilt.aar"],
+			static_libs: ["foo", "bar"],
+		}
+	`)
+
+	foo := result.ModuleForTests("foo", "android_common")
+	bar := result.ModuleForTests("bar", "android_common")
+	baz := result.ModuleForTests("baz", "android_common")
+
+	fooOutputPaths := foo.OutputFiles(t, "")
+	barOutputPaths := bar.OutputFiles(t, "")
+	bazOutputPaths := baz.OutputFiles(t, "")
+
+	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", fooOutputPaths[0].Rel())
+	android.AssertStringEquals(t, "bar relative output path",
+		"bar.jar", barOutputPaths[0].Rel())
+	android.AssertStringEquals(t, "baz relative output path",
+		"baz.jar", bazOutputPaths[0].Rel())
+}
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 8599003..0c77968 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_manifest_test.go b/java/android_manifest_test.go
index 5909b1e..7c91884 100644
--- a/java/android_manifest_test.go
+++ b/java/android_manifest_test.go
@@ -92,10 +92,9 @@
 			"out/soong/.intermediates/transitive/android_common/manifest_fixer/AndroidManifest.xml",
 			"transitive/AndroidManifest2.xml",
 			"out/soong/.intermediates/transitive_import/android_common/aar/AndroidManifest.xml",
+			"out/soong/.intermediates/transitive_import_dep/android_common/aar/AndroidManifest.xml",
 			"out/soong/.intermediates/direct_import/android_common/aar/AndroidManifest.xml",
-			// TODO(b/288358614): Soong has historically not merged manifests from dependencies of
-			// android_library_import modules.
-
+			"out/soong/.intermediates/direct_import_dep/android_common/aar/AndroidManifest.xml",
 		},
 		manifestMergerRule.Implicits)
 }
diff --git a/java/androidmk.go b/java/androidmk.go
index 498962f..a1bc904 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"io"
-	"strings"
 
 	"android/soong/android"
 
@@ -92,11 +91,7 @@
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					if len(library.logtagsSrcs) > 0 {
-						var logtags []string
-						for _, l := range library.logtagsSrcs {
-							logtags = append(logtags, l.Rel())
-						}
-						entries.AddStrings("LOCAL_LOGTAGS_FILES", logtags...)
+						entries.AddStrings("LOCAL_SOONG_LOGTAGS_FILES", library.logtagsSrcs.Strings()...)
 					}
 
 					if library.installFile == nil {
@@ -128,7 +123,6 @@
 					if library.dexpreopter.configPath != nil {
 						entries.SetPath("LOCAL_SOONG_DEXPREOPT_CONFIG", library.dexpreopter.configPath)
 					}
-					android.SetAconfigFileMkEntries(&library.ModuleBase, entries, library.mergedAconfigFiles)
 				},
 			},
 		})
@@ -211,7 +205,7 @@
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:        "JAVA_LIBRARIES",
 		OverrideName: prebuilt.BaseModuleName(),
-		OutputFile:   android.OptionalPathForPath(prebuilt.combinedClasspathFile),
+		OutputFile:   android.OptionalPathForPath(prebuilt.combinedImplementationFile),
 		Include:      "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
@@ -219,8 +213,8 @@
 				if prebuilt.dexJarFile.IsSet() {
 					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile.Path())
 				}
-				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
-				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
+				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedHeaderFile)
+				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedImplementationFile)
 				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String())
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 				// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
@@ -262,13 +256,13 @@
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
-		OutputFile: android.OptionalPathForPath(prebuilt.classpathFile),
+		OutputFile: android.OptionalPathForPath(prebuilt.implementationJarFile),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.classpathFile)
-				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.classpathFile)
+				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.headerJarFile)
+				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.implementationJarFile)
 				entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", prebuilt.exportPackage)
 				entries.SetPath("LOCAL_SOONG_TRANSITIVE_RES_PACKAGES", prebuilt.transitiveAaptResourcePackagesFile)
 				entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", prebuilt.proguardFlags)
@@ -302,7 +296,6 @@
 					if len(binary.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled)
 					}
-					android.SetAconfigFileMkEntries(&binary.ModuleBase, entries, binary.mergedAconfigFiles)
 				},
 			},
 			ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -420,22 +413,11 @@
 					jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String())
 					entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String())
 				} else {
+					var names []string
 					for _, jniLib := range app.jniLibs {
-						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
-						var partitionTag string
-
-						// Mimic the creation of partition_tag in build/make,
-						// which defaults to an empty string when the partition is system.
-						// Otherwise, capitalize with a leading _
-						if jniLib.partition == "system" {
-							partitionTag = ""
-						} else {
-							split := strings.Split(jniLib.partition, "/")
-							partitionTag = "_" + strings.ToUpper(split[len(split)-1])
-						}
-						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(),
-							jniLib.name+":"+partitionTag)
+						names = append(names, jniLib.name)
 					}
+					entries.AddStrings("LOCAL_REQUIRED_MODULES", names...)
 				}
 
 				if len(app.jniCoverageOutputs) > 0 {
@@ -454,9 +436,7 @@
 
 				entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports)
 
-				if app.Name() != "framework-res" {
-					android.SetAconfigFileMkEntries(&app.ModuleBase, entries, app.mergedAconfigFiles)
-				}
+				entries.AddStrings("LOCAL_SOONG_LOGTAGS_FILES", app.logtagsSrcs.Strings()...)
 			},
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -533,7 +513,6 @@
 		entries.SetPath("LOCAL_FULL_MANIFEST_FILE", a.mergedManifestFile)
 		entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.combinedExportedProguardFlagsFile)
 		entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
-		android.SetAconfigFileMkEntries(&a.ModuleBase, entries, a.mergedAconfigFiles)
 	})
 
 	return entriesList
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 1232cd1..243a279 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -20,8 +20,6 @@
 
 	"android/soong/android"
 	"android/soong/cc"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func TestRequired(t *testing.T) {
@@ -161,8 +159,8 @@
 		moduleName string
 		expected   []string
 	}{
-		{"foo-shared_library", []string{"foo-shared_library.xml"}},
-		{"foo-no_shared_library", nil},
+		{"foo-shared_library", []string{"foo-shared_library.impl", "foo-shared_library.xml"}},
+		{"foo-no_shared_library", []string{"foo-no_shared_library.impl"}},
 	}
 	for _, tc := range testCases {
 		mod := result.ModuleForTests(tc.moduleName, "android_common").Module()
@@ -256,148 +254,50 @@
 	}
 }
 
-func TestJniPartition(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libjni_system",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-		}
-
-		cc_library {
-			name: "libjni_system_ext",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			system_ext_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_odm",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			device_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_product",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			product_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_vendor",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			soc_specific: true,
-		}
-
-		android_app {
-			name: "test_app_system_jni_system",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system"],
-		}
-
-		android_app {
-			name: "test_app_system_jni_system_ext",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system_ext"],
-		}
-
-		android_app {
-			name: "test_app_system_ext_jni_system",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system"],
-			system_ext_specific: true
-		}
-
-		android_app {
-			name: "test_app_system_ext_jni_system_ext",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_system_ext"],
-			system_ext_specific: true
-		}
-
-		android_app {
-			name: "test_app_product_jni_product",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_product"],
-			product_specific: true
-		}
-
-		android_app {
-			name: "test_app_vendor_jni_odm",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_odm"],
-			soc_specific: true
-		}
-
-		android_app {
-			name: "test_app_odm_jni_vendor",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_vendor"],
-			device_specific: true
-		}
-		android_app {
-			name: "test_app_system_jni_multiple",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system", "libjni_system_ext"],
-		}
-		android_app {
-			name: "test_app_vendor_jni_multiple",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_odm", "libjni_vendor"],
-			soc_specific: true
-		}
-		`
-	arch := "arm64"
+func TestJniAsRequiredDeps(t *testing.T) {
 	ctx := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		cc.PrepareForTestWithCcDefaultModules,
 		android.PrepareForTestWithAndroidMk,
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.TestProductVariables.DeviceArch = proptools.StringPtr(arch)
-		}),
-	).
-		RunTestWithBp(t, bp)
-	testCases := []struct {
-		name           string
-		partitionNames []string
-		partitionTags  []string
+	).RunTestWithBp(t, `
+		android_app {
+			name: "app",
+			jni_libs: ["libjni"],
+			platform_apis: true,
+		}
+
+		android_app {
+			name: "app_embedded",
+			jni_libs: ["libjni"],
+			platform_apis: true,
+			use_embedded_native_libs: true,
+		}
+
+		cc_library {
+			name: "libjni",
+			system_shared_libs: [],
+			stl: "none",
+		}
+		`)
+
+	testcases := []struct {
+		name     string
+		expected []string
 	}{
-		{"test_app_system_jni_system", []string{"libjni_system"}, []string{""}},
-		{"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
-		{"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}},
-		{"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
-		{"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}},
-		{"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}},
-		{"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}},
-		{"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}},
-		{"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}},
+		{
+			name:     "app",
+			expected: []string{"libjni"},
+		},
+		{
+			name:     "app_embedded",
+			expected: nil,
+		},
 	}
 
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			mod := ctx.ModuleForTests(test.name, "android_common").Module()
-			entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
-			for i := range test.partitionNames {
-				actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i]
-				expected := test.partitionNames[i] + ":" + test.partitionTags[i]
-				android.AssertStringEquals(t, "Expected and actual differ", expected, actual)
-			}
-		})
+	for _, tc := range testcases {
+		mod := ctx.ModuleForTests(tc.name, "android_common").Module()
+		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
+		required := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
+		android.AssertDeepEquals(t, "unexpected required deps", tc.expected, required)
 	}
 }
diff --git a/java/app.go b/java/app.go
old mode 100755
new mode 100644
index 4656888..19dc8d5
--- 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)
@@ -249,13 +256,13 @@
 }
 
 func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
-	a.Module.deps(ctx)
-
 	if String(a.appProperties.Stl) == "c++_shared" && !a.SdkVersion(ctx).Specified() {
 		ctx.PropertyErrorf("stl", "sdk_version must be set in order to use c++_shared")
 	}
 
 	sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
+	a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
+	a.Module.deps(ctx)
 	if sdkDep.hasFrameworkLibs() {
 		a.aapt.deps(ctx, sdkDep)
 	}
@@ -274,20 +281,38 @@
 		variation := append(jniTarget.Variations(),
 			blueprint.Variation{Mutator: "link", Variation: "shared"})
 
-		// If the app builds against an Android SDK use the SDK variant of JNI dependencies
-		// unless jni_uses_platform_apis is set.
-		// Don't require the SDK variant for apps that are shipped on vendor, etc., as they already
-		// have stable APIs through the VNDK.
-		if (usesSDK && !a.RequiresStableAPIs(ctx) &&
-			!Bool(a.appProperties.Jni_uses_platform_apis)) ||
-			Bool(a.appProperties.Jni_uses_sdk_apis) {
+		// Test whether to use the SDK variant or the non-SDK variant of JNI dependencies.
+		// Many factors are considered here.
+		// 1. Basically, the selection follows whether the app has sdk_version set or not.
+		jniUsesSdkVariant := usesSDK
+		// 2. However, jni_uses_platform_apis and jni_uses_sdk_apis can override it
+		if Bool(a.appProperties.Jni_uses_sdk_apis) {
+			jniUsesSdkVariant = true
+		}
+		if Bool(a.appProperties.Jni_uses_platform_apis) {
+			jniUsesSdkVariant = false
+		}
+		// 3. Then the use of SDK variant is again prohibited for the following cases:
+		// 3.1. the app is shipped on unbundled partitions like vendor. Since the entire
+		// partition (not only the app) is considered unbudled, there's no need to use the
+		// SDK variant.
+		// 3.2. the app doesn't support embedding the JNI libs
+		if a.RequiresStableAPIs(ctx) || !a.shouldEmbedJnis(ctx) {
+			jniUsesSdkVariant = false
+		}
+		if jniUsesSdkVariant {
 			variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
 		}
-		ctx.AddFarVariationDependencies(variation, jniLibTag, a.appProperties.Jni_libs...)
+
+		// Use the installable dep tag when the JNIs are not embedded
+		var tag dependencyTag
+		if a.shouldEmbedJnis(ctx) {
+			tag = jniLibTag
+		} else {
+			tag = jniInstallTag
+		}
+		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
 	}
-
-	a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
-
 	for _, aconfig_declaration := range a.aaptProperties.Flags_packages {
 		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration)
 	}
@@ -320,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 {
@@ -329,31 +382,25 @@
 		a.aapt.manifestValues.applicationId = *applicationId
 	}
 	a.generateAndroidBuildActions(ctx)
+	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+		TestOnly: true,
+	})
+
 }
 
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.checkAppSdkVersions(ctx)
+	a.checkEmbedJnis(ctx)
 	a.generateAndroidBuildActions(ctx)
 	a.generateJavaUsedByApex(ctx)
 }
 
-func (a *AndroidApp) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
-	defaultMinSdkVersion := a.Module.MinSdkVersion(ctx)
-	if proptools.Bool(a.appProperties.Updatable) {
-		overrideApiLevel := android.MinSdkVersionFromValue(ctx, ctx.DeviceConfig().ApexGlobalMinSdkVersionOverride())
-		if !overrideApiLevel.IsNone() && overrideApiLevel.CompareTo(defaultMinSdkVersion) > 0 {
-			return overrideApiLevel
-		}
-	}
-	return defaultMinSdkVersion
-}
-
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
 	if a.Updatable() {
 		if !a.SdkVersion(ctx).Stable() {
 			ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.SdkVersion(ctx))
 		}
-		if String(a.deviceProperties.Min_sdk_version) == "" {
+		if String(a.overridableProperties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.")
 		}
 
@@ -377,6 +424,17 @@
 	a.checkSdkVersions(ctx)
 }
 
+// Ensures that use_embedded_native_libs are set for apk-in-apex
+func (a *AndroidApp) checkEmbedJnis(ctx android.BaseModuleContext) {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	apkInApex := !apexInfo.IsForPlatform()
+	hasJnis := len(a.appProperties.Jni_libs) > 0
+
+	if apkInApex && hasJnis && !Bool(a.appProperties.Use_embedded_native_libs) {
+		ctx.ModuleErrorf("APK in APEX should have use_embedded_native_libs: true")
+	}
+}
+
 // If an updatable APK sets min_sdk_version, min_sdk_vesion of JNI libs should match with it.
 // This check is enforced for "updatable" APKs (including APK-in-APEX).
 func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) {
@@ -432,9 +490,9 @@
 }
 
 func (a *AndroidApp) shouldEmbedJnis(ctx android.BaseModuleContext) bool {
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	return ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) ||
-		!apexInfo.IsForPlatform() || a.appProperties.AlwaysPackageNativeLibs
+		Bool(a.appProperties.Updatable) ||
+		a.appProperties.AlwaysPackageNativeLibs
 }
 
 func generateAaptRenamePackageFlags(packageName string, renameResourcesPackage bool) []string {
@@ -455,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) {
@@ -516,22 +583,37 @@
 	a.aapt.splitNames = a.appProperties.Package_splits
 	a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent)
 	if a.Updatable() {
-		a.aapt.defaultManifestVersion = android.DefaultUpdatableModuleVersion
+		if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" {
+			a.aapt.defaultManifestVersion = override
+		} else {
+			a.aapt.defaultManifestVersion = android.DefaultUpdatableModuleVersion
+		}
 	}
 
+	// 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),
 			classLoaderContexts:            a.classLoaderContexts,
 			excludedLibs:                   a.usesLibraryProperties.Exclude_uses_libs,
 			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) {
@@ -547,7 +629,13 @@
 	staticLibProguardFlagFiles = android.FirstUniquePaths(staticLibProguardFlagFiles)
 
 	a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, staticLibProguardFlagFiles...)
-	a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, a.proguardOptionsFile)
+	if !(a.dexProperties.optimizedResourceShrinkingEnabled(ctx)) {
+		// When using the optimized shrinking the R8 enqueuer will traverse the xml files that become
+		// live for code references and (transitively) mark these as live.
+		// In this case we explicitly don't wan't the aapt2 generated keep files (which would keep the now
+		// dead code alive)
+		a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, a.proguardOptionsFile)
+	}
 }
 
 func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath {
@@ -580,7 +668,7 @@
 	var packageResources = a.exportPackage
 
 	if ctx.ModuleName() != "framework-res" {
-		if Bool(a.dexProperties.Optimize.Shrink_resources) {
+		if a.dexProperties.resourceShrinkingEnabled(ctx) {
 			protoFile := android.PathForModuleOut(ctx, packageResources.Base()+".proto.apk")
 			aapt2Convert(ctx, protoFile, packageResources, "proto")
 			a.dexer.resourcesInput = android.OptionalPathForPath(protoFile)
@@ -603,7 +691,7 @@
 		}
 
 		a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars)
-		if Bool(a.dexProperties.Optimize.Shrink_resources) {
+		if a.dexProperties.resourceShrinkingEnabled(ctx) {
 			binaryResources := android.PathForModuleOut(ctx, packageResources.Base()+".binary.out.apk")
 			aapt2Convert(ctx, binaryResources, a.dexer.resourcesOutput.Path(), "binary")
 			packageResources = binaryResources
@@ -802,18 +890,10 @@
 	// The decision to enforce <uses-library> checks is made before adding implicit SDK libraries.
 	a.usesLibrary.freezeEnforceUsesLibraries()
 
-	// Add implicit SDK libraries to <uses-library> list.
-	requiredUsesLibs, optionalUsesLibs := a.classLoaderContexts.UsesLibs()
-	for _, usesLib := range requiredUsesLibs {
-		a.usesLibrary.addLib(usesLib, false)
-	}
-	for _, usesLib := range optionalUsesLibs {
-		a.usesLibrary.addLib(usesLib, true)
-	}
-
 	// Check that the <uses-library> list is coherent with the manifest.
 	if a.usesLibrary.enforceUsesLibraries() {
-		manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(ctx, a.mergedManifestFile)
+		manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(
+			ctx, a.mergedManifestFile, &a.classLoaderContexts)
 		apkDeps = append(apkDeps, manifestCheckFile)
 	}
 
@@ -826,7 +906,9 @@
 
 	dexJarFile, packageResources := a.dexBuildActions(ctx)
 
-	jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
+	// No need to check the SDK version of the JNI deps unless we embed them
+	checkNativeSdkVersion := a.shouldEmbedJnis(ctx) && !Bool(a.appProperties.Jni_uses_platform_apis)
+	jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), checkNativeSdkVersion)
 	jniJarFile := a.jniBuildActions(jniLibs, prebuiltJniPackages, ctx)
 
 	if ctx.Failed() {
@@ -908,6 +990,22 @@
 			installed := ctx.InstallFile(a.installDir, extra.Base(), extra)
 			extraInstalledPaths = append(extraInstalledPaths, installed)
 		}
+		// If we don't embed jni libs, make sure that those are installed along with the
+		// app, and also place symlinks to the installed paths under the lib/<arch>
+		// directory of the app installation directory. ex:
+		// /system/app/MyApp/lib/arm64/libfoo.so -> /system/lib64/libfoo.so
+		if !a.embeddedJniLibs {
+			for _, jniLib := range jniLibs {
+				archStr := jniLib.target.Arch.ArchType.String()
+				symlinkDir := a.installDir.Join(ctx, "lib", archStr)
+				for _, installedLib := range jniLib.installPaths {
+					// install the symlink itself
+					symlinkName := installedLib.Base()
+					symlinkTarget := android.InstallPathToOnDevicePath(ctx, installedLib)
+					ctx.InstallAbsoluteSymlink(symlinkDir, symlinkName, symlinkTarget)
+				}
+			}
+		}
 		ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...)
 	}
 
@@ -919,6 +1017,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 {
@@ -995,6 +1109,7 @@
 						coverageFile:   dep.CoverageOutputFile(),
 						unstrippedFile: dep.UnstrippedOutputFile(),
 						partition:      dep.Partition(),
+						installPaths:   dep.FilesToInstall(),
 					})
 				} else if ctx.Config().AllowMissingDependencies() {
 					ctx.AddMissingDependencies([]string{otherName})
@@ -1108,36 +1223,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()
 }
 
@@ -1182,7 +1272,8 @@
 	module.AddProperties(
 		&module.aaptProperties,
 		&module.appProperties,
-		&module.overridableAppProperties)
+		&module.overridableAppProperties,
+		&module.Library.sourceProperties)
 
 	module.usesLibrary.enforce = true
 
@@ -1239,6 +1330,11 @@
 			Manifest:       proptools.StringPtr(":" + rroManifestName),
 			Resource_dirs:  a.aaptProperties.Resource_dirs,
 		}
+		if !Bool(a.aaptProperties.Aapt_include_all_resources) {
+			for _, aaptConfig := range ctx.Config().ProductAAPTConfig() {
+				rroProperties.Aaptflags = append(rroProperties.Aaptflags, "-c", aaptConfig)
+			}
+		}
 		ctx.CreateModule(RuntimeResourceOverlayFactory, &rroProperties)
 	})
 
@@ -1293,6 +1389,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,
@@ -1328,7 +1425,16 @@
 		OutputFile:              a.OutputFile(),
 		TestConfig:              a.testConfig,
 		HostRequiredModuleNames: a.HostRequiredModuleNames(),
+		TestSuites:              a.testProperties.Test_suites,
+		IsHost:                  false,
+		LocalCertificate:        a.certificate.AndroidMkString(),
+		IsUnitTest:              Bool(a.testProperties.Test_options.Unit_test),
 	})
+	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+		TestOnly:       true,
+		TopLevelTarget: true,
+	})
+
 }
 
 func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig android.Path) android.Path {
@@ -1521,9 +1627,13 @@
 	android.OverrideModuleBase
 }
 
-func (i *OverrideAndroidTest) GenerateAndroidBuildActions(_ android.ModuleContext) {
+func (i *OverrideAndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// All the overrides happen in the base module.
 	// TODO(jungjw): Check the base module type.
+	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+		TestOnly:       true,
+		TopLevelTarget: true,
+	})
 }
 
 // override_android_test is used to create an android_app module based on another android_test by overriding
@@ -1571,6 +1681,9 @@
 	// provide the android.test.base statically and use jarjar to rename them so they do not collide
 	// with the classes provided by the android.test.base library.
 	Exclude_uses_libs []string
+
+	// The module names of optional uses-library libraries that are missing from the source tree.
+	Missing_optional_uses_libs []string `blueprint:"mutated"`
 }
 
 // usesLibrary provides properties and helper functions for AndroidApp and AndroidAppImport to verify that the
@@ -1587,20 +1700,11 @@
 	shouldDisableDexpreopt bool
 }
 
-func (u *usesLibrary) addLib(lib string, optional bool) {
-	if !android.InList(lib, u.usesLibraryProperties.Uses_libs) && !android.InList(lib, u.usesLibraryProperties.Optional_uses_libs) {
-		if optional {
-			u.usesLibraryProperties.Optional_uses_libs = append(u.usesLibraryProperties.Optional_uses_libs, lib)
-		} else {
-			u.usesLibraryProperties.Uses_libs = append(u.usesLibraryProperties.Uses_libs, lib)
-		}
-	}
-}
-
 func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, addCompatDeps bool) {
 	if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() {
 		ctx.AddVariationDependencies(nil, usesLibReqTag, u.usesLibraryProperties.Uses_libs...)
-		ctx.AddVariationDependencies(nil, usesLibOptTag, u.presentOptionalUsesLibs(ctx)...)
+		presentOptionalUsesLibs := u.presentOptionalUsesLibs(ctx)
+		ctx.AddVariationDependencies(nil, usesLibOptTag, presentOptionalUsesLibs...)
 		// Only add these extra dependencies if the module is an app that depends on framework
 		// libs. This avoids creating a cyclic dependency:
 		//     e.g. framework-res -> org.apache.http.legacy -> ... -> framework-res.
@@ -1611,6 +1715,8 @@
 			ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
 			ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
 		}
+		_, diff, _ := android.ListSetDifference(u.usesLibraryProperties.Optional_uses_libs, presentOptionalUsesLibs)
+		u.usesLibraryProperties.Missing_optional_uses_libs = diff
 	} else {
 		ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.usesLibraryProperties.Uses_libs...)
 		ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.presentOptionalUsesLibs(ctx)...)
@@ -1629,15 +1735,6 @@
 	return optionalUsesLibs
 }
 
-// Helper function to replace string in a list.
-func replaceInList(list []string, oldstr, newstr string) {
-	for i, str := range list {
-		if str == oldstr {
-			list[i] = newstr
-		}
-	}
-}
-
 // Returns a map of module names of shared library dependencies to the paths to their dex jars on
 // host and on device.
 func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap {
@@ -1679,11 +1776,6 @@
 			libName := dep
 			if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
 				libName = *ulib.ProvidesUsesLib()
-				// Replace module name with library name in `uses_libs`/`optional_uses_libs` in
-				// order to pass verify_uses_libraries check (which compares these properties
-				// against library names written in the manifest).
-				replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName)
-				replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
 			}
 			clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional,
 				lib.DexJarBuildPath(ctx).PathOrNil(), lib.DexJarInstallPath(),
@@ -1717,7 +1809,7 @@
 // an APK with the manifest embedded in it (manifest_check will know which one it is by the file
 // extension: APKs are supposed to end with '.apk').
 func (u *usesLibrary) verifyUsesLibraries(ctx android.ModuleContext, inputFile android.Path,
-	outputFile android.WritablePath) android.Path {
+	outputFile android.WritablePath, classLoaderContexts *dexpreopt.ClassLoaderContextMap) android.Path {
 
 	statusFile := dexpreopt.UsesLibrariesStatusFile(ctx)
 
@@ -1745,27 +1837,37 @@
 		cmd.Flag("--enforce-uses-libraries-relax")
 	}
 
-	for _, lib := range u.usesLibraryProperties.Uses_libs {
+	requiredUsesLibs, optionalUsesLibs := classLoaderContexts.UsesLibs()
+	for _, lib := range requiredUsesLibs {
 		cmd.FlagWithArg("--uses-library ", lib)
 	}
-
-	for _, lib := range u.usesLibraryProperties.Optional_uses_libs {
+	for _, lib := range optionalUsesLibs {
 		cmd.FlagWithArg("--optional-uses-library ", lib)
 	}
 
+	// Also add missing optional uses libs, as the manifest check expects them.
+	// Note that what we add here are the module names of those missing libs, not library names, while
+	// the manifest check actually expects library names. However, the case where a library is missing
+	// and the module name != the library name is too rare for us to handle.
+	for _, lib := range u.usesLibraryProperties.Missing_optional_uses_libs {
+		cmd.FlagWithArg("--missing-optional-uses-library ", lib)
+	}
+
 	rule.Build("verify_uses_libraries", "verify <uses-library>")
 	return outputFile
 }
 
 // verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against
 // the build system and returns the path to a copy of the manifest.
-func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
+func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path,
+	classLoaderContexts *dexpreopt.ClassLoaderContextMap) android.Path {
 	outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
-	return u.verifyUsesLibraries(ctx, manifest, outputFile)
+	return u.verifyUsesLibraries(ctx, manifest, outputFile, classLoaderContexts)
 }
 
 // verifyUsesLibrariesAPK checks the <uses-library> tags in the manifest of an APK against the build
 // system and returns the path to a copy of the APK.
-func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) {
-	u.verifyUsesLibraries(ctx, apk, nil) // for APKs manifest_check does not write output file
+func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path,
+	classLoaderContexts *dexpreopt.ClassLoaderContextMap) {
+	u.verifyUsesLibraries(ctx, apk, nil, classLoaderContexts) // for APKs manifest_check does not write output file
 }
diff --git a/java/app_import.go b/java/app_import.go
index 7387e16..fa87997 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"
 
@@ -87,9 +86,6 @@
 	hideApexVariantFromMake bool
 
 	provenanceMetaDataFile android.OutputPath
-
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
 }
 
 type AndroidAppImportProperties struct {
@@ -355,7 +351,7 @@
 	}
 
 	if a.usesLibrary.enforceUsesLibraries() {
-		a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
+		a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk, &a.dexpreopter.classLoaderContexts)
 	}
 
 	a.dexpreopter.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), jnisUncompressed)
@@ -416,7 +412,6 @@
 		artifactPath := android.PathForModuleSrc(ctx, *a.properties.Apk)
 		a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath)
 	}
-	android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles)
 
 	providePrebuiltInfo(ctx,
 		prebuiltInfoProps{
@@ -426,6 +421,8 @@
 		},
 	)
 
+	ctx.SetOutputFiles([]android.Path{a.outputFile}, "")
+
 	// TODO: androidmk converter jni libs
 }
 
@@ -465,15 +462,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
 }
@@ -611,6 +599,12 @@
 	return return_struct
 }
 
+func (a *AndroidAppImport) UsesLibrary() *usesLibrary {
+	return &a.usesLibrary
+}
+
+var _ ModuleWithUsesLibrary = (*AndroidAppImport)(nil)
+
 // android_app_import imports a prebuilt apk with additional processing specified in the module.
 // DPI-specific apk source files can be specified using dpi_variants. Example:
 //
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 5de50e7..496fc13 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -509,7 +509,7 @@
 
 			variant := ctx.ModuleForTests("foo", "android_common")
 			if test.expected == "" {
-				if variant.Module().Enabled() {
+				if variant.Module().Enabled(android.PanickingConfigAndErrorContext(ctx)) {
 					t.Error("module should have been disabled, but wasn't")
 				}
 				rule := variant.MaybeRule("genProvenanceMetaData")
@@ -586,7 +586,7 @@
 
 			variant := ctx.ModuleForTests("foo", "android_common")
 			if test.expected == "" {
-				if variant.Module().Enabled() {
+				if variant.Module().Enabled(android.PanickingConfigAndErrorContext(ctx)) {
 					t.Error("module should have been disabled, but wasn't")
 				}
 				rule := variant.MaybeRule("genProvenanceMetaData")
@@ -629,7 +629,7 @@
 	if !a.prebuilt.UsePrebuilt() {
 		t.Errorf("prebuilt foo module is not active")
 	}
-	if !a.Enabled() {
+	if !a.Enabled(android.PanickingConfigAndErrorContext(ctx)) {
 		t.Errorf("prebuilt foo module is disabled")
 	}
 }
diff --git a/java/app_test.go b/java/app_test.go
index b75cb16..e878ccf 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -119,10 +119,7 @@
 		foo.Output(expectedOutput)
 	}
 
-	outputFiles, err := foo.Module().(*AndroidApp).OutputFiles("")
-	if err != nil {
-		t.Fatal(err)
-	}
+	outputFiles := foo.OutputFiles(t, "")
 	android.AssertPathsRelativeToTopEquals(t, `OutputFiles("")`, expectedOutputs, outputFiles)
 }
 
@@ -519,6 +516,49 @@
 	testJavaError(t, `"libjni" .*: links "libbar" built against newer API version "current"`, bp)
 }
 
+func TestUpdatableApps_ApplyDefaultUpdatableModuleVersion(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		android_app {
+			name: "com.android.foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			min_sdk_version: "31",
+			updatable: true,
+		}
+	`)
+	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	android.AssertStringDoesContain(t,
+		"com.android.foo: expected manifest fixer to set override-placeholder-version to android.DefaultUpdatableModuleVersion",
+		foo.BuildParams.Args["args"],
+		fmt.Sprintf("--override-placeholder-version %s", android.DefaultUpdatableModuleVersion),
+	)
+}
+
+func TestUpdatableApps_ApplyOverrideApexManifestDefaultVersion(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.FixtureMergeEnv(map[string]string{
+			"OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234",
+		}),
+	).RunTestWithBp(t, `
+		android_app {
+			name: "com.android.foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			min_sdk_version: "31",
+			updatable: true,
+		}
+	`)
+	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	android.AssertStringDoesContain(t,
+		"com.android.foo: expected manifest fixer to set override-placeholder-version to 1234",
+		foo.BuildParams.Args["args"],
+		"--override-placeholder-version 1234",
+	)
+}
+
 func TestResourceDirs(t *testing.T) {
 	testCases := []struct {
 		name      string
@@ -829,12 +869,12 @@
 				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
 				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
 				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
 			},
 
 			directResources: nil,
@@ -849,12 +889,12 @@
 			directClasspath: []string{
 				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
 				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
@@ -928,13 +968,13 @@
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
 				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
 				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
 				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
 			},
 
 			directResources: nil,
@@ -953,12 +993,12 @@
 				"out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
@@ -1034,13 +1074,13 @@
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
 				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
 				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
 				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
 			},
 
 			dontVerifyDirect:           true,
@@ -1075,12 +1115,12 @@
 				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
 				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
 				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
 			},
 
 			directResources: nil,
@@ -1098,12 +1138,12 @@
 				"out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
 			},
 
 			dontVerifyTransitive:       true,
@@ -1137,12 +1177,12 @@
 				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
 				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
 				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
 			},
 
 			directResources: nil,
@@ -1157,12 +1197,12 @@
 			directClasspath: []string{
 				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
 				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar",
+				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
@@ -3244,7 +3284,10 @@
 			name: "static-y",
 			srcs: ["a.java"],
 			uses_libs: ["runtime-required-y"],
-			optional_uses_libs: ["runtime-optional-y"],
+			optional_uses_libs: [
+				"runtime-optional-y",
+				"missing-lib-a",
+			],
 			sdk_version: "current",
 		}
 
@@ -3280,7 +3323,7 @@
 			sdk_version: "current",
 			optional_uses_libs: [
 				"bar",
-				"baz",
+				"missing-lib-b",
 			],
 		}
 
@@ -3295,7 +3338,7 @@
 			],
 			optional_uses_libs: [
 				"bar",
-				"baz",
+				"missing-lib-b",
 			],
 		}
 	`
@@ -3317,10 +3360,10 @@
 	// propagated from dependencies.
 	actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
 	expectManifestFixerArgs := `--extract-native-libs=true ` +
-		`--uses-library qux ` +
-		`--uses-library quuz ` +
 		`--uses-library foo ` +
 		`--uses-library com.non.sdk.lib ` +
+		`--uses-library qux ` +
+		`--uses-library quuz ` +
 		`--uses-library runtime-library ` +
 		`--uses-library runtime-required-x ` +
 		`--uses-library runtime-required-y ` +
@@ -3339,9 +3382,10 @@
 		`--uses-library runtime-required-x ` +
 		`--uses-library runtime-required-y ` +
 		`--optional-uses-library bar ` +
-		`--optional-uses-library baz ` +
 		`--optional-uses-library runtime-optional-x ` +
-		`--optional-uses-library runtime-optional-y `
+		`--optional-uses-library runtime-optional-y ` +
+		`--missing-optional-uses-library missing-lib-b ` +
+		`--missing-optional-uses-library missing-lib-a`
 	android.AssertStringDoesContain(t, "verify cmd args", verifyCmd, verifyArgs)
 
 	// Test that all libraries are verified for an APK (library order matters).
@@ -3350,7 +3394,7 @@
 		`--uses-library com.non.sdk.lib ` +
 		`--uses-library android.test.runner ` +
 		`--optional-uses-library bar ` +
-		`--optional-uses-library baz `
+		`--missing-optional-uses-library missing-lib-b `
 	android.AssertStringDoesContain(t, "verify apk cmd args", verifyApkCmd, verifyApkArgs)
 
 	// Test that necessary args are passed for constructing CLC in Ninja phase.
@@ -3625,7 +3669,10 @@
 		android_app {
 			name: "foo",
 			sdk_version: "current",
-			static_libs: ["lib1"],
+			static_libs: [
+				"lib1",
+				"lib3",
+			],
 		}
 
 		android_library {
@@ -3633,22 +3680,49 @@
 			sdk_version: "current",
 			optimize: {
 				proguard_flags_files: ["lib1proguard.cfg"],
+			},
+			static_libs: ["lib2"],
+		}
+
+		android_library {
+			name: "lib2",
+			sdk_version: "current",
+			optimize: {
+				proguard_flags_files: ["lib2proguard.cfg"],
 			}
 		}
+
+		android_library_import {
+			name: "lib3",
+			sdk_version: "current",
+			aars: ["lib3.aar"],
+			static_libs: ["lib4"],
+		}
+
+		android_library {
+			name: "lib4",
+			sdk_version: "current",
+			optimize: {
+				proguard_flags_files: ["lib4proguard.cfg"],
+			}
+		}
+
+
 	`)
 
 	m := ctx.ModuleForTests("foo", "android_common")
-	hasLib1Proguard := false
-	for _, s := range m.Rule("java.r8").Implicits.Strings() {
-		if s == "lib1proguard.cfg" {
-			hasLib1Proguard = true
-			break
-		}
-	}
+	r8 := m.Rule("java.r8")
+	implicits := r8.Implicits.RelativeToTop().Strings()
+	android.AssertStringListContains(t, "r8 implicits", implicits, "lib1proguard.cfg")
+	android.AssertStringListContains(t, "r8 implicits", implicits, "lib2proguard.cfg")
+	android.AssertStringListContains(t, "r8 implicits", implicits, "lib4proguard.cfg")
+	android.AssertStringListContains(t, "r8 implicits", implicits, "out/soong/.intermediates/lib3/android_common/aar/proguard.txt")
 
-	if !hasLib1Proguard {
-		t.Errorf("App does not use library proguard config")
-	}
+	flags := r8.Args["r8Flags"]
+	android.AssertStringDoesContain(t, "r8 flags", flags, "-include lib1proguard.cfg")
+	android.AssertStringDoesContain(t, "r8 flags", flags, "-include lib2proguard.cfg")
+	android.AssertStringDoesContain(t, "r8 flags", flags, "-include lib4proguard.cfg")
+	android.AssertStringDoesContain(t, "r8 flags", flags, "-include out/soong/.intermediates/lib3/android_common/aar/proguard.txt")
 }
 
 func TestTargetSdkVersionManifestFixer(t *testing.T) {
@@ -4112,6 +4186,7 @@
 	bpTemplate := `
 	%v {
 		name: "mytest",
+		min_sdk_version: "34",
 		target_sdk_version: "%v",
 		test_suites: ["othersuite", "%v"],
 	}
@@ -4288,52 +4363,6 @@
 	)
 }
 
-func TestApexGlobalMinSdkVersionOverride(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		PrepareForTestWithJavaDefaultModules,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.ApexGlobalMinSdkVersionOverride = proptools.StringPtr("Tiramisu")
-		}),
-	).RunTestWithBp(t, `
-		android_app {
-			name: "com.android.bar",
-			srcs: ["a.java"],
-			sdk_version: "current",
-		}
-		android_app {
-			name: "com.android.foo",
-			srcs: ["a.java"],
-			sdk_version: "current",
-			min_sdk_version: "S",
-			updatable: true,
-		}
-		override_android_app {
-			name: "com.android.go.foo",
-			base: "com.android.foo",
-		}
-	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
-	fooOverride := result.ModuleForTests("com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer")
-	bar := result.ModuleForTests("com.android.bar", "android_common").Rule("manifestFixer")
-
-	android.AssertStringDoesContain(t,
-		"expected manifest fixer to set com.android.bar minSdkVersion to S",
-		bar.BuildParams.Args["args"],
-		"--minSdkVersion  S",
-	)
-	android.AssertStringDoesContain(t,
-		"com.android.foo: expected manifest fixer to set minSdkVersion to T",
-		foo.BuildParams.Args["args"],
-		"--minSdkVersion  T",
-	)
-	android.AssertStringDoesContain(t,
-		"com.android.go.foo: expected manifest fixer to set minSdkVersion to T",
-		fooOverride.BuildParams.Args["args"],
-		"--minSdkVersion  T",
-	)
-
-}
-
 func TestAppFlagsPackages(t *testing.T) {
 	ctx := testApp(t, `
 		android_app {
@@ -4348,6 +4377,7 @@
 		aconfig_declarations {
 			name: "bar",
 			package: "com.example.package.bar",
+			container: "com.android.foo",
 			srcs: [
 				"bar.aconfig",
 			],
@@ -4355,6 +4385,7 @@
 		aconfig_declarations {
 			name: "baz",
 			package: "com.example.package.baz",
+			container: "com.android.foo",
 			srcs: [
 				"baz.aconfig",
 			],
@@ -4379,6 +4410,82 @@
 	)
 }
 
+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.
 func TestNoDexpreoptOptionalUsesLibDoesNotHaveImpl(t *testing.T) {
 	bp := `
@@ -4402,6 +4509,44 @@
 	android.AssertBoolEquals(t, "dexpreopt should be disabled if optional_uses_libs does not have an implementation", true, dexpreopt == nil)
 }
 
+func TestTestOnlyApp(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+                // These should be test-only
+                android_test {
+                        name: "android-test",
+                }
+                android_test_helper_app {
+                        name: "helper-app",
+                }
+                override_android_test {
+                        name: "override-test",
+                        base: "android-app",
+                }
+                // And these should not be
+		android_app {
+			name: "android-app",
+                        srcs: ["b.java"],
+			sdk_version: "current",
+		}
+	`)
+
+	expectedTestOnly := []string{
+		"android-test",
+		"helper-app",
+		"override-test",
+	}
+
+	expectedTopLevel := []string{
+		"android-test",
+		"override-test",
+	}
+
+	assertTestOnlyAndTopLevel(t, ctx, expectedTestOnly, expectedTopLevel)
+}
+
 func TestAppStem(t *testing.T) {
 	ctx := testApp(t, `
 				android_app {
@@ -4418,3 +4563,77 @@
 		t.Errorf("Module output does not contain expected apk %s", "foo-new.apk")
 	}
 }
+
+func TestAppMinSdkVersionOverride(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		android_app {
+			name: "com.android.foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			min_sdk_version: "31",
+			updatable: true,
+		}
+		override_android_app {
+			name: "com.android.go.foo",
+			base: "com.android.foo",
+			min_sdk_version: "33",
+		}
+	`)
+	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	fooOverride := result.ModuleForTests("com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer")
+
+	android.AssertStringDoesContain(t,
+		"com.android.foo: expected manifest fixer to set minSdkVersion to T",
+		foo.BuildParams.Args["args"],
+		"--minSdkVersion  31",
+	)
+	android.AssertStringDoesContain(t,
+		"com.android.go.foo: expected manifest fixer to set minSdkVersion to T",
+		fooOverride.BuildParams.Args["args"],
+		"--minSdkVersion  33",
+	)
+
+}
+
+func TestNotApplyDefaultUpdatableModuleVersion(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		android_app {
+			name: "com.android.foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			min_sdk_version: "31",
+		}
+	`)
+	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	android.AssertStringDoesNotContain(t,
+		"com.android.foo: expected manifest fixer to not set override-placeholder-version",
+		foo.BuildParams.Args["args"],
+		"--override-placeholder-version",
+	)
+}
+
+func TestNotApplyOverrideApexManifestDefaultVersion(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.FixtureMergeEnv(map[string]string{
+			"OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234",
+		}),
+	).RunTestWithBp(t, `
+		android_app {
+			name: "com.android.foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			min_sdk_version: "31",
+		}
+	`)
+	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	android.AssertStringDoesNotContain(t,
+		"com.android.foo: expected manifest fixer to not set override-placeholder-version",
+		foo.BuildParams.Args["args"],
+		"--override-placeholder-version",
+	)
+}
diff --git a/java/base.go b/java/base.go
index be286fe..02dc3e3 100644
--- a/java/base.go
+++ b/java/base.go
@@ -200,6 +200,9 @@
 	// Additional srcJars tacked in by GeneratedJavaLibraryModule
 	Generated_srcjars []android.Path `android:"mutated"`
 
+	// intermediate aconfig cache file tacked in by GeneratedJavaLibraryModule
+	Aconfig_Cache_files []android.Path `android:"mutated"`
+
 	// If true, then only the headers are built and not the implementation jar.
 	Headers_only *bool
 
@@ -229,10 +232,6 @@
 	// If the SDK kind is empty, it will be set to public.
 	Sdk_version *string
 
-	// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
-	// Defaults to sdk_version if not set. See sdk_version for possible values.
-	Min_sdk_version *string
-
 	// if not blank, set the maximum version of the sdk that the compiled artifacts will run against.
 	// Defaults to empty string "". See sdk_version for possible values.
 	Max_sdk_version *string
@@ -312,6 +311,10 @@
 	// Otherwise, both the overridden and the overriding modules will have the same output name, which
 	// can cause the duplicate output error.
 	Stem *string
+
+	// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+	// Defaults to sdk_version if not set. See sdk_version for possible values.
+	Min_sdk_version *string
 }
 
 // Functionality common to Module and Import
@@ -435,6 +438,7 @@
 	deviceProperties DeviceProperties
 
 	overridableProperties OverridableProperties
+	sourceProperties      android.SourceProperties
 
 	// jar file containing header classes including static library dependencies, suitable for
 	// inserting into the bootclasspath/classpath of another compile
@@ -536,14 +540,28 @@
 	// or the module should override Stem().
 	stem string
 
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
-
 	// Values that will be set in the JarJarProvider data for jarjar repackaging,
 	// and merged with our dependencies' rules.
 	jarjarRenameRules map[string]string
 
 	stubsLinkType StubsLinkType
+
+	// Paths to the aconfig intermediate cache files that are provided by the
+	// 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
+}
+
+var _ android.InstallableModule = (*Module)(nil)
+
+// To satisfy the InstallableModule interface
+func (j *Module) EnforceApiContainerChecks() bool {
+	return true
+}
+
+// Overrides android.ModuleBase.InstallInProduct()
+func (j *Module) InstallInProduct() bool {
+	return j.ProductSpecific()
 }
 
 func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -646,35 +664,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)
 }
@@ -712,6 +716,7 @@
 	// doesn't make sense) or framework libraries (e.g. libraries found in the InstrumentFrameworkModules list) unless EMMA_INSTRUMENT_FRAMEWORK is true.
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	isJacocoAgent := ctx.ModuleName() == "jacocoagent"
+
 	if j.DirectlyInAnyApex() && !isJacocoAgent && !apexInfo.IsForPlatform() {
 		if !inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
 			return true
@@ -735,8 +740,8 @@
 }
 
 func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
-	if j.deviceProperties.Min_sdk_version != nil {
-		return android.ApiLevelFrom(ctx, *j.deviceProperties.Min_sdk_version)
+	if j.overridableProperties.Min_sdk_version != nil {
+		return android.ApiLevelFrom(ctx, *j.overridableProperties.Min_sdk_version)
 	}
 	return j.SdkVersion(ctx).ApiLevel
 }
@@ -804,7 +809,7 @@
 	// Add dependency on libraries that provide additional hidden api annotations.
 	ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...)
 
-	if ctx.DeviceConfig().VndkVersion() != "" && ctx.Config().EnforceInterPartitionJavaSdkLibrary() {
+	if ctx.Config().EnforceInterPartitionJavaSdkLibrary() {
 		// Require java_sdk_library at inter-partition java dependency to ensure stable
 		// interface between partitions. If inter-partition java_library dependency is detected,
 		// raise build error because java_library doesn't have a stable interface.
@@ -837,9 +842,11 @@
 		if dep != nil {
 			if component, ok := dep.(SdkLibraryComponentDependency); ok {
 				if lib := component.OptionalSdkLibraryImplementation(); lib != nil {
-					// Add library as optional if it's one of the optional compatibility libs.
+					// Add library as optional if it's one of the optional compatibility libs or it's
+					// explicitly listed in the optional_uses_libs property.
 					tag := usesLibReqTag
-					if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) {
+					if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) ||
+						android.InList(*lib, j.usesLibrary.usesLibraryProperties.Optional_uses_libs) {
 						tag = usesLibOptTag
 					}
 					ctx.AddVariationDependencies(nil, tag, *lib)
@@ -1202,6 +1209,8 @@
 	// final R classes from the app.
 	flags.classpath = append(android.CopyOf(extraClasspathJars), flags.classpath...)
 
+	j.aconfigCacheFiles = append(deps.aconfigProtoFiles, j.properties.Aconfig_Cache_files...)
+
 	// If compiling headers then compile them and skip the rest
 	if proptools.Bool(j.properties.Headers_only) {
 		if srcFiles.HasExt(".kt") {
@@ -1294,10 +1303,12 @@
 			return
 		}
 
+		kotlinJarPath := j.repackageFlagsIfNecessary(ctx, kotlinJar.OutputPath, jarName, "kotlinc")
+
 		// Make javac rule depend on the kotlinc rule
 		flags.classpath = append(classpath{kotlinHeaderJar}, flags.classpath...)
 
-		kotlinJars = append(kotlinJars, kotlinJar)
+		kotlinJars = append(kotlinJars, kotlinJarPath)
 		kotlinHeaderJars = append(kotlinHeaderJars, kotlinHeaderJar)
 
 		// Jar kotlin classes into the final jar after javac
@@ -1377,6 +1388,7 @@
 				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)
 				}
 			}
@@ -1389,11 +1401,13 @@
 				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)
 				}
 			}
 		} else {
 			classes := j.compileJavaClasses(ctx, jarName, -1, uniqueJavaFiles, srcJars, flags, extraJarDeps)
+			classes = j.repackageFlagsIfNecessary(ctx, classes, jarName, "javac")
 			jars = append(jars, classes)
 		}
 		if ctx.Failed() {
@@ -1552,16 +1566,6 @@
 		}
 	}
 
-	// Automatic jarjar rules propagation
-	if j.repackageJarjarRules != nil {
-		repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", jarName).OutputPath
-		TransformJarJar(ctx, repackagedJarjarFile, outputFile, j.repackageJarjarRules)
-		outputFile = repackagedJarjarFile
-		if ctx.Failed() {
-			return
-		}
-	}
-
 	// Check package restrictions if necessary.
 	if len(j.properties.Permitted_packages) > 0 {
 		// Time stamp file created by the package check rule.
@@ -1649,11 +1653,28 @@
 				classesJar:    implementationAndResourcesJar,
 				jarName:       jarName,
 			}
-			dexOutputFile = j.dexer.compileDex(ctx, params)
+			if j.GetProfileGuided() && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting() {
+				ctx.PropertyErrorf("enable_profile_rewriting",
+					"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on. The attached profile should be sourced from an unoptimized/unobfuscated APK.",
+				)
+			}
+			if j.EnableProfileRewriting() {
+				profile := j.GetProfile()
+				if profile == "" || !j.GetProfileGuided() {
+					ctx.PropertyErrorf("enable_profile_rewriting", "Profile and Profile_guided must be set when enable_profile_rewriting is true")
+				}
+				params.artProfileInput = &profile
+			}
+			dexOutputFile, dexArtProfileOutput := j.dexer.compileDex(ctx, params)
 			if ctx.Failed() {
 				return
 			}
 
+			// If r8/d8 provides a profile that matches the optimized dex, use that for dexpreopt.
+			if dexArtProfileOutput != nil {
+				j.dexpreopter.SetRewrittenProfile(*dexArtProfileOutput)
+			}
+
 			// merge dex jar with resources if necessary
 			if j.resourceJar != nil {
 				jars := android.Paths{dexOutputFile, j.resourceJar}
@@ -1679,7 +1700,11 @@
 			j.dexJarFile = makeDexJarPathFromPath(dexOutputFile)
 
 			// Dexpreopting
-			j.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), dexOutputFile)
+			libName := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
+			if j.SdkLibraryName() != nil && strings.HasSuffix(ctx.ModuleName(), ".impl") {
+				libName = strings.TrimSuffix(libName, ".impl")
+			}
+			j.dexpreopt(ctx, libName, dexOutputFile)
 
 			outputFile = dexOutputFile
 		} else {
@@ -1727,8 +1752,6 @@
 
 	ctx.CheckbuildFile(outputFile)
 
-	android.CollectDependencyAconfigFiles(ctx, &j.mergedAconfigFiles)
-
 	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 		HeaderJars:                          android.PathsIfNonNil(j.headerJarFile),
 		RepackagedHeaderJars:                android.PathsIfNonNil(j.repackagedHeaderJarFile),
@@ -1746,7 +1769,7 @@
 		ExportedPluginDisableTurbine:        j.exportedDisableTurbine,
 		JacocoReportClassesFile:             j.jacocoReportClassesFile,
 		StubsLinkType:                       j.stubsLinkType,
-		AconfigIntermediateCacheOutputPaths: deps.aconfigProtoFiles,
+		AconfigIntermediateCacheOutputPaths: j.aconfigCacheFiles,
 	})
 
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
@@ -1757,10 +1780,7 @@
 	return android.InList("androidx.compose.runtime_runtime", j.properties.Static_libs)
 }
 
-func (j *Module) collectProguardSpecInfo(ctx android.ModuleContext) ProguardSpecInfo {
-	transitiveUnconditionalExportedFlags := []*android.DepSet[android.Path]{}
-	transitiveProguardFlags := []*android.DepSet[android.Path]{}
-
+func collectDepProguardSpecInfo(ctx android.ModuleContext) (transitiveProguardFlags, transitiveUnconditionalExportedFlags []*android.DepSet[android.Path]) {
 	ctx.VisitDirectDeps(func(m android.Module) {
 		depProguardInfo, _ := android.OtherModuleProvider(ctx, m, ProguardSpecInfoProvider)
 		depTag := ctx.OtherModuleDependencyTag(m)
@@ -1775,6 +1795,12 @@
 		}
 	})
 
+	return transitiveProguardFlags, transitiveUnconditionalExportedFlags
+}
+
+func (j *Module) collectProguardSpecInfo(ctx android.ModuleContext) ProguardSpecInfo {
+	transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx)
+
 	directUnconditionalExportedFlags := android.Paths{}
 	proguardFlagsForThisModule := android.PathsForModuleSrc(ctx, j.dexProperties.Optimize.Proguard_flags_files)
 	exportUnconditionally := proptools.Bool(j.dexProperties.Optimize.Export_proguard_flags_files)
@@ -1829,7 +1855,7 @@
 	classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath
 	TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, annoSrcJar, flags, extraJarDeps)
 
-	if ctx.Config().EmitXrefRules() {
+	if ctx.Config().EmitXrefRules() && ctx.Module() == ctx.PrimaryModule() {
 		extractionFile := android.PathForModuleOut(ctx, kzipName)
 		emitXrefRule(ctx, extractionFile, idx, srcFiles, srcJars, flags, extraJarDeps)
 		j.kytheFiles = append(j.kytheFiles, extractionFile)
@@ -2357,7 +2383,10 @@
 				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...)
 			}
 		} else if dep, ok := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider); ok {
-			deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...)
+			switch tag {
+			case staticLibTag:
+				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...)
+			}
 		} else {
 			switch tag {
 			case bootClasspathTag:
@@ -2380,6 +2409,7 @@
 		}
 
 		addCLCFromDep(ctx, module, j.classLoaderContexts)
+		addMissingOptionalUsesLibsFromDep(ctx, module, &j.usesLibrary)
 	})
 
 	return deps
@@ -2517,7 +2547,7 @@
 				case Implementation:
 					return RenameUseInclude, "info"
 				default:
-					//fmt.Printf("LJ: %v -> %v StubsLinkType unknown\n", module, m)
+					//fmt.Printf("collectDirectDepsProviders: %v -> %v StubsLinkType unknown\n", module, m)
 					// Fall through to the heuristic logic.
 				}
 				switch reflect.TypeOf(m).String() {
@@ -2683,6 +2713,16 @@
 	return result
 }
 
+// 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 {
+	if j.repackageJarjarRules == nil {
+		return infile
+	}
+	repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", info+jarName)
+	TransformJarJar(ctx, repackagedJarjarFile, infile, j.repackageJarjarRules)
+	return repackagedJarjarFile
+}
+
 func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) {
 	deps.processorPath = append(deps.processorPath, pluginJars...)
 	deps.processorClasses = append(deps.processorClasses, pluginClasses...)
@@ -2703,3 +2743,13 @@
 }
 
 var _ ModuleWithStem = (*Module)(nil)
+
+type ModuleWithUsesLibrary interface {
+	UsesLibrary() *usesLibrary
+}
+
+func (j *Module) UsesLibrary() *usesLibrary {
+	return &j.usesLibrary
+}
+
+var _ ModuleWithUsesLibrary = (*Module)(nil)
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 5d40ec3..6223ded 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -21,8 +21,8 @@
 // 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(module android.Module) bool {
-	if !module.Enabled() {
+func isActiveModule(ctx android.ConfigAndErrorContext, module android.Module) bool {
+	if !module.Enabled(ctx) {
 		return false
 	}
 	return android.IsModulePreferred(module)
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index c7dc3af..77ddf5c 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -127,7 +127,10 @@
 // added by addDependencyOntoApexModulePair.
 func gatherApexModulePairDepsWithTag(ctx android.BaseModuleContext, tag blueprint.DependencyTag) []android.Module {
 	var modules []android.Module
-	ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
+	isActiveModulePred := func(module android.Module) bool {
+		return isActiveModule(ctx, module)
+	}
+	ctx.VisitDirectDepsIf(isActiveModulePred, func(module android.Module) {
 		t := ctx.OtherModuleDependencyTag(module)
 		if t == tag {
 			modules = append(modules, module)
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 7c45d30..16209b7 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -77,7 +77,7 @@
 		return javaSdkLibrarySdkMemberType
 	}
 
-	return javaBootLibsSdkMemberType
+	return JavaBootLibsSdkMemberType
 }
 
 func (b bootclasspathFragmentContentDependencyTag) ExportMember() bool {
@@ -474,7 +474,7 @@
 	// Only perform a consistency check if this module is the active module. That will prevent an
 	// unused prebuilt that was created without instrumentation from breaking an instrumentation
 	// build.
-	if isActiveModule(ctx.Module()) {
+	if isActiveModule(ctx, ctx.Module()) {
 		b.bootclasspathFragmentPropertyCheck(ctx)
 	}
 
@@ -519,15 +519,21 @@
 // empty string if this module should not provide a boot image profile.
 func (b *BootclasspathFragmentModule) getProfileProviderApex(ctx android.BaseModuleContext) string {
 	// Only use the profile from the module that is preferred.
-	if !isActiveModule(ctx.Module()) {
+	if !isActiveModule(ctx, ctx.Module()) {
 		return ""
 	}
 
 	// Bootclasspath fragment modules that are for the platform do not produce boot related files.
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
-	for _, apex := range apexInfo.InApexVariants {
-		if isProfileProviderApex(ctx, apex) {
-			return apex
+	apexInfos, _ := android.ModuleProvider(ctx, android.AllApexInfoProvider)
+	if apexInfos == nil {
+		return ""
+	}
+
+	for _, apexInfo := range apexInfos.ApexInfos {
+		for _, apex := range apexInfo.InApexVariants {
+			if isProfileProviderApex(ctx, apex) {
+				return apex
+			}
 		}
 	}
 
@@ -590,13 +596,36 @@
 		// So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS.
 		// TODO(b/202896428): Add better way to handle this.
 		_, unknown = android.RemoveFromList("android.car-module", unknown)
-		if isActiveModule(ctx.Module()) && len(unknown) > 0 {
-			ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
+		if isApexVariant(ctx) && len(unknown) > 0 {
+			if android.IsModulePrebuilt(ctx.Module()) {
+				// prebuilt bcpf. the validation of this will be done at the top-level apex
+				providerClasspathFragmentValidationInfoProvider(ctx, unknown)
+			} else if !disableSourceApexVariant(ctx) {
+				// source bcpf, and prebuilt apex are not selected.
+				ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
+			}
 		}
 	}
 	return jars
 }
 
+var ClasspathFragmentValidationInfoProvider = blueprint.NewProvider[ClasspathFragmentValidationInfo]()
+
+type ClasspathFragmentValidationInfo struct {
+	ClasspathFragmentModuleName string
+	UnknownJars                 []string
+}
+
+// Set a provider with the list of jars that have not been added to PRODUCT_APEX_BOOT_JARS
+// The validation will be done in the ctx of the top-level _selected_ apex
+func providerClasspathFragmentValidationInfoProvider(ctx android.ModuleContext, unknown []string) {
+	info := ClasspathFragmentValidationInfo{
+		ClasspathFragmentModuleName: ctx.ModuleName(),
+		UnknownJars:                 unknown,
+	}
+	android.SetProvider(ctx, ClasspathFragmentValidationInfoProvider, info)
+}
+
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
 func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput {
 
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index 0ebab4d..18a5dae 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -128,19 +128,21 @@
 				return m.Name() == configuredJars.Jar(i)
 			}, func(m android.Module) {
 				if s, ok := m.(*SdkLibrary); ok {
+					minSdkVersion := s.MinSdkVersion(ctx)
+					maxSdkVersion := s.MaxSdkVersion(ctx)
 					// TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current"
-					if s.minSdkVersion.Specified() {
-						if s.minSdkVersion.IsCurrent() {
+					if minSdkVersion.Specified() {
+						if minSdkVersion.IsCurrent() {
 							jar.minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
 						} else {
-							jar.minSdkVersion = s.minSdkVersion.String()
+							jar.minSdkVersion = minSdkVersion.String()
 						}
 					}
-					if s.maxSdkVersion.Specified() {
-						if s.maxSdkVersion.IsCurrent() {
+					if maxSdkVersion.Specified() {
+						if maxSdkVersion.IsCurrent() {
 							jar.maxSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
 						} else {
-							jar.maxSdkVersion = s.maxSdkVersion.String()
+							jar.maxSdkVersion = maxSdkVersion.String()
 						}
 					}
 				}
@@ -151,10 +153,14 @@
 	return jars
 }
 
+func (c *ClasspathFragmentBase) outputFilename() string {
+	return strings.ToLower(c.classpathType.String()) + ".pb"
+}
+
 func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, jars []classpathJar) {
 	generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true)
 	if generateProto {
-		outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
+		outputFilename := c.outputFilename()
 		c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
 		c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
 
@@ -181,6 +187,10 @@
 	android.SetProvider(ctx, ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo)
 }
 
+func (c *ClasspathFragmentBase) installClasspathProto(ctx android.ModuleContext) android.InstallPath {
+	return ctx.InstallFile(c.installDirPath, c.outputFilename(), c.outputFilepath)
+}
+
 func writeClasspathsTextproto(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) {
 	var content strings.Builder
 
diff --git a/java/code_metadata_test.go b/java/code_metadata_test.go
index 0ef348a..99b1f52 100644
--- a/java/code_metadata_test.go
+++ b/java/code_metadata_test.go
@@ -7,6 +7,7 @@
 	"android/soong/android"
 	soongTesting "android/soong/testing"
 	"android/soong/testing/code_metadata_internal_proto"
+
 	"google.golang.org/protobuf/proto"
 )
 
diff --git a/java/config/config.go b/java/config/config.go
index d720046..2bb50f6 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -26,8 +26,7 @@
 )
 
 var (
-	pctx         = android.NewPackageContext("android/soong/java/config")
-	exportedVars = android.NewExportedVariables(pctx)
+	pctx = android.NewPackageContext("android/soong/java/config")
 
 	LegacyCorePlatformBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"}
 	LegacyCorePlatformSystemModules          = "legacy-core-platform-api-stubs-system-modules"
@@ -79,30 +78,30 @@
 func init() {
 	pctx.Import("github.com/google/blueprint/bootstrap")
 
-	exportedVars.ExportStringStaticVariable("JavacHeapSize", "4096M")
-	exportedVars.ExportStringStaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
+	pctx.StaticVariable("JavacHeapSize", "4096M")
+	pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
 
 	// ErrorProne can use significantly more memory than javac alone, give it a higher heap
 	// size (b/221480398).
-	exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "8192M")
-	exportedVars.ExportStringStaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
+	pctx.StaticVariable("ErrorProneHeapSize", "8192M")
+	pctx.StaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
 
 	// D8 invocations are shorter lived, so we restrict their JIT tiering relative to R8.
 	// Note that the `-JXX` prefix syntax is specific to the R8/D8 invocation wrappers.
-	exportedVars.ExportStringListStaticVariable("D8Flags", append([]string{
+	pctx.StaticVariable("D8Flags", strings.Join(append([]string{
 		"-JXmx4096M",
 		"-JXX:+TieredCompilation",
 		"-JXX:TieredStopAtLevel=1",
 		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
-	}, dexerJavaVmFlagsList...))
-	exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
+	}, dexerJavaVmFlagsList...), " "))
+	pctx.StaticVariable("R8Flags", strings.Join(append([]string{
 		"-JXmx4096M",
 		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
-	}, dexerJavaVmFlagsList...))
+	}, dexerJavaVmFlagsList...), " "))
 
-	exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
+	pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
 		`-Xmaxerrs 9999999`,
 		`-encoding UTF-8`,
 		`-sourcepath ""`,
@@ -116,10 +115,10 @@
 
 		// b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9
 		`-XDstringConcat=inline`,
-	})
+	}, " "))
 
-	exportedVars.ExportStringListStaticVariable("JavaVmFlags", javaVmFlagsList)
-	exportedVars.ExportStringListStaticVariable("JavacVmFlags", javacVmFlagsList)
+	pctx.StaticVariable("JavaVmFlags", strings.Join(javaVmFlagsList, " "))
+	pctx.StaticVariable("JavacVmFlags", strings.Join(javacVmFlagsList, " "))
 
 	pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
 
diff --git a/java/config/droidstubs.go b/java/config/droidstubs.go
index 39eec44..04a3f96 100644
--- a/java/config/droidstubs.go
+++ b/java/config/droidstubs.go
@@ -58,11 +58,7 @@
 )
 
 func init() {
-	exportedVars.ExportStringList("MetalavaFlags", metalavaFlags)
+	pctx.StaticVariable("MetalavaAnnotationsFlags", strings.Join(metalavaAnnotationsFlags, " "))
 
-	exportedVars.ExportString("MetalavaAddOpens", MetalavaAddOpens)
-
-	exportedVars.ExportStringListStaticVariable("MetalavaAnnotationsFlags", metalavaAnnotationsFlags)
-
-	exportedVars.ExportStringListStaticVariable("MetalavaAnnotationWarningsFlags", metalavaAnnotationsWarningsFlags)
+	pctx.StaticVariable("MetalavaAnnotationWarningsFlags", strings.Join(metalavaAnnotationsWarningsFlags, " "))
 }
diff --git a/java/config/error_prone.go b/java/config/error_prone.go
index 5f853c8..767164f 100644
--- a/java/config/error_prone.go
+++ b/java/config/error_prone.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"android/soong/android"
 	"strings"
 )
 
@@ -29,23 +30,23 @@
 )
 
 // Wrapper that grabs value of val late so it can be initialized by a later module's init function
-func errorProneVar(val *[]string, sep string) func() string {
-	return func() string {
+func errorProneVar(val *[]string, sep string) func(android.PackageVarContext) string {
+	return func(android.PackageVarContext) string {
 		return strings.Join(*val, sep)
 	}
 }
 
 func init() {
-	exportedVars.ExportVariableFuncVariable("ErrorProneClasspath", errorProneVar(&ErrorProneClasspath, ":"))
-	exportedVars.ExportVariableFuncVariable("ErrorProneChecksError", errorProneVar(&ErrorProneChecksError, " "))
-	exportedVars.ExportVariableFuncVariable("ErrorProneChecksWarning", errorProneVar(&ErrorProneChecksWarning, " "))
-	exportedVars.ExportVariableFuncVariable("ErrorProneChecksDefaultDisabled", errorProneVar(&ErrorProneChecksDefaultDisabled, " "))
-	exportedVars.ExportVariableFuncVariable("ErrorProneChecksOff", errorProneVar(&ErrorProneChecksOff, " "))
-	exportedVars.ExportVariableFuncVariable("ErrorProneFlags", errorProneVar(&ErrorProneFlags, " "))
-	exportedVars.ExportStringListStaticVariable("ErrorProneChecks", []string{
+	pctx.VariableFunc("ErrorProneClasspath", errorProneVar(&ErrorProneClasspath, ":"))
+	pctx.VariableFunc("ErrorProneChecksError", errorProneVar(&ErrorProneChecksError, " "))
+	pctx.VariableFunc("ErrorProneChecksWarning", errorProneVar(&ErrorProneChecksWarning, " "))
+	pctx.VariableFunc("ErrorProneChecksDefaultDisabled", errorProneVar(&ErrorProneChecksDefaultDisabled, " "))
+	pctx.VariableFunc("ErrorProneChecksOff", errorProneVar(&ErrorProneChecksOff, " "))
+	pctx.VariableFunc("ErrorProneFlags", errorProneVar(&ErrorProneFlags, " "))
+	pctx.StaticVariable("ErrorProneChecks", strings.Join([]string{
 		"${ErrorProneChecksOff}",
 		"${ErrorProneChecksError}",
 		"${ErrorProneChecksWarning}",
 		"${ErrorProneChecksDefaultDisabled}",
-	})
+	}, " "))
 }
diff --git a/java/container_test.go b/java/container_test.go
new file mode 100644
index 0000000..3441855
--- /dev/null
+++ b/java/container_test.go
@@ -0,0 +1,129 @@
+// 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",
+			],
+		}
+	`)
+
+	testcases := []struct {
+		moduleName         string
+		isSystemContainer  bool
+		isVendorContainer  bool
+		isProductContainer bool
+		isCts              bool
+	}{
+		{
+			moduleName:         "foo",
+			isSystemContainer:  true,
+			isVendorContainer:  false,
+			isProductContainer: false,
+			isCts:              false,
+		},
+		{
+			moduleName:         "foo_vendor",
+			isSystemContainer:  false,
+			isVendorContainer:  true,
+			isProductContainer: false,
+			isCts:              false,
+		},
+		{
+			moduleName:         "foo_soc_specific",
+			isSystemContainer:  false,
+			isVendorContainer:  true,
+			isProductContainer: false,
+			isCts:              false,
+		},
+		{
+			moduleName:         "foo_product_specific",
+			isSystemContainer:  false,
+			isVendorContainer:  false,
+			isProductContainer: true,
+			isCts:              false,
+		},
+		{
+			moduleName:         "foo_cts_test",
+			isSystemContainer:  false,
+			isVendorContainer:  false,
+			isProductContainer: false,
+			isCts:              true,
+		},
+		{
+			moduleName:         "foo_non_cts_test",
+			isSystemContainer:  false,
+			isVendorContainer:  false,
+			isProductContainer: false,
+			isCts:              false,
+		},
+	}
+
+	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))
+	}
+}
diff --git a/java/device_host_converter_test.go b/java/device_host_converter_test.go
index 3413da0..6ccc5c1 100644
--- a/java/device_host_converter_test.go
+++ b/java/device_host_converter_test.go
@@ -16,7 +16,7 @@
 
 import (
 	"android/soong/android"
-	"reflect"
+	"slices"
 	"strings"
 	"testing"
 )
@@ -84,7 +84,7 @@
 		deviceImportCombined.Output,
 	}
 
-	if !reflect.DeepEqual(combined.Inputs, expectedInputs) {
+	if !slices.Equal(combined.Inputs.Strings(), expectedInputs.Strings()) {
 		t.Errorf("expected host_module combined inputs:\n%q\ngot:\n%q",
 			expectedInputs, combined.Inputs)
 	}
@@ -95,7 +95,7 @@
 		deviceRes.Output,
 	}
 
-	if !reflect.DeepEqual(resCombined.Inputs, expectedInputs) {
+	if !slices.Equal(resCombined.Inputs.Strings(), expectedInputs.Strings()) {
 		t.Errorf("expected host_module res combined inputs:\n%q\ngot:\n%q",
 			expectedInputs, resCombined.Inputs)
 	}
@@ -165,7 +165,7 @@
 		hostImportCombined.Output,
 	}
 
-	if !reflect.DeepEqual(combined.Inputs, expectedInputs) {
+	if !slices.Equal(combined.Inputs.Strings(), expectedInputs.Strings()) {
 		t.Errorf("expected device_module combined inputs:\n%q\ngot:\n%q",
 			expectedInputs, combined.Inputs)
 	}
@@ -176,7 +176,7 @@
 		hostRes.Output,
 	}
 
-	if !reflect.DeepEqual(resCombined.Inputs, expectedInputs) {
+	if !slices.Equal(resCombined.Inputs.Strings(), expectedInputs.Strings()) {
 		t.Errorf("expected device_module res combined inputs:\n%q\ngot:\n%q",
 			expectedInputs, resCombined.Inputs)
 	}
diff --git a/java/dex.go b/java/dex.go
index 4474c63..7bb6925 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -66,6 +66,12 @@
 		// If true, optimize for size by removing unused resources. Defaults to false.
 		Shrink_resources *bool
 
+		// If true, use optimized resource shrinking in R8, overriding the
+		// Shrink_resources setting. Defaults to false.
+		// Optimized shrinking means that R8 will trace and treeshake resources together with code
+		// and apply additional optimizations. This implies non final fields in the R classes.
+		Optimized_shrink_resources *bool
+
 		// Flags to pass to proguard.
 		Proguard_flags []string
 
@@ -85,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 {
@@ -105,6 +115,18 @@
 	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
 }
 
+func (d *DexProperties) resourceShrinkingEnabled(ctx android.ModuleContext) bool {
+	return !ctx.Config().Eng() && BoolDefault(d.Optimize.Optimized_shrink_resources, Bool(d.Optimize.Shrink_resources))
+}
+
+func (d *DexProperties) optimizedResourceShrinkingEnabled(ctx android.ModuleContext) bool {
+	return d.resourceShrinkingEnabled(ctx) && Bool(d.Optimize.Optimized_shrink_resources)
+}
+
+func (d *dexer) optimizeOrObfuscateEnabled() bool {
+	return d.effectiveOptimizeEnabled() && (proptools.Bool(d.dexProperties.Optimize.Optimize) || proptools.Bool(d.dexProperties.Optimize.Obfuscate))
+}
+
 var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -162,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}"},
@@ -182,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) {
@@ -239,17 +261,25 @@
 	return flags, deps
 }
 
-func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) {
+func (d *dexer) d8Flags(ctx android.ModuleContext, dexParams *compileDexParams) (d8Flags []string, d8Deps android.Paths, artProfileOutput *android.OutputPath) {
+	flags := dexParams.flags
 	d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
 	d8Flags = append(d8Flags, flags.dexClasspath.FormRepeatedClassPath("--lib ")...)
 
 	d8Deps = append(d8Deps, flags.bootClasspath...)
 	d8Deps = append(d8Deps, flags.dexClasspath...)
 
-	return d8Flags, d8Deps
+	if flags, deps, profileOutput := d.addArtProfile(ctx, dexParams); profileOutput != nil {
+		d8Flags = append(d8Flags, flags...)
+		d8Deps = append(d8Deps, deps...)
+		artProfileOutput = profileOutput
+	}
+
+	return d8Flags, d8Deps, artProfileOutput
 }
 
-func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
+func (d *dexer) r8Flags(ctx android.ModuleContext, dexParams *compileDexParams) (r8Flags []string, r8Deps android.Paths, artProfileOutput *android.OutputPath) {
+	flags := dexParams.flags
 	opt := d.dexProperties.Optimize
 
 	// When an app contains references to APIs that are not in the SDK specified by
@@ -351,24 +381,54 @@
 		r8Flags = append(r8Flags, "-ignorewarnings")
 	}
 
+	// resourcesInput is empty when we don't use resource shrinking, if on, pass these to R8
 	if d.resourcesInput.Valid() {
 		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) {
+			r8Flags = append(r8Flags, "--optimized-resource-shrinking")
+		}
 	}
 
-	return r8Flags, r8Deps
+	if flags, deps, profileOutput := d.addArtProfile(ctx, dexParams); profileOutput != nil {
+		r8Flags = append(r8Flags, flags...)
+		r8Deps = append(r8Deps, deps...)
+		artProfileOutput = profileOutput
+	}
+
+	return r8Flags, r8Deps, artProfileOutput
 }
 
 type compileDexParams struct {
-	flags         javaBuilderFlags
-	sdkVersion    android.SdkSpec
-	minSdkVersion android.ApiLevel
-	classesJar    android.Path
-	jarName       string
+	flags           javaBuilderFlags
+	sdkVersion      android.SdkSpec
+	minSdkVersion   android.ApiLevel
+	classesJar      android.Path
+	jarName         string
+	artProfileInput *string
 }
 
-func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) android.OutputPath {
+// Adds --art-profile to r8/d8 command.
+// r8/d8 will output a generated profile file to match the optimized dex code.
+func (d *dexer) addArtProfile(ctx android.ModuleContext, dexParams *compileDexParams) (flags []string, deps android.Paths, artProfileOutputPath *android.OutputPath) {
+	if dexParams.artProfileInput != nil {
+		artProfileInputPath := android.PathForModuleSrc(ctx, *dexParams.artProfileInput)
+		artProfileOutputPathValue := android.PathForModuleOut(ctx, "profile.prof.txt").OutputPath
+		artProfileOutputPath = &artProfileOutputPathValue
+		flags = []string{
+			"--art-profile",
+			artProfileInputPath.String(),
+			artProfileOutputPath.String(),
+		}
+		deps = append(deps, artProfileInputPath)
+	}
+	return flags, deps, artProfileOutputPath
+
+}
+
+// Return the compiled dex jar and (optional) profile _after_ r8 optimization
+func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.OutputPath, *android.OutputPath) {
 
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
@@ -388,6 +448,7 @@
 	}
 
 	useR8 := d.effectiveOptimizeEnabled()
+	var artProfileOutputPath *android.OutputPath
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
@@ -400,8 +461,12 @@
 		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
 		resourcesOutput := android.PathForModuleOut(ctx, "package-res-shrunken.apk")
 		d.resourcesOutput = android.OptionalPathForPath(resourcesOutput)
-		r8Flags, r8Deps := d.r8Flags(ctx, dexParams.flags)
-		r8Deps = append(r8Deps, commonDeps...)
+		implicitOutputs := android.WritablePaths{
+			proguardDictionary,
+			proguardUsageZip,
+			proguardConfiguration,
+		}
+		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams)
 		rule := r8
 		args := map[string]string{
 			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
@@ -414,14 +479,21 @@
 			"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(), ",")
 		}
-		implicitOutputs := android.WritablePaths{
-			proguardDictionary,
-			proguardUsageZip,
-			proguardConfiguration}
 		if d.resourcesInput.Valid() {
 			implicitOutputs = append(implicitOutputs, resourcesOutput)
 			args["resourcesOutput"] = resourcesOutput.String()
@@ -436,18 +508,27 @@
 			Args:            args,
 		})
 	} else {
-		d8Flags, d8Deps := d8Flags(dexParams.flags)
+		implicitOutputs := android.WritablePaths{}
+		d8Flags, d8Deps, d8ArtProfileOutputPath := d.d8Flags(ctx, dexParams)
+		if d8ArtProfileOutputPath != nil {
+			artProfileOutputPath = d8ArtProfileOutputPath
+			implicitOutputs = append(
+				implicitOutputs,
+				artProfileOutputPath,
+			)
+		}
 		d8Deps = append(d8Deps, commonDeps...)
 		rule := d8
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
 			rule = d8RE
 		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:        rule,
-			Description: "d8",
-			Output:      javalibJar,
-			Input:       dexParams.classesJar,
-			Implicits:   d8Deps,
+			Rule:            rule,
+			Description:     "d8",
+			Output:          javalibJar,
+			Input:           dexParams.classesJar,
+			ImplicitOutputs: implicitOutputs,
+			Implicits:       d8Deps,
 			Args: map[string]string{
 				"d8Flags":        strings.Join(append(commonFlags, d8Flags...), " "),
 				"zipFlags":       zipFlags,
@@ -462,5 +543,5 @@
 		javalibJar = alignedJavalibJar
 	}
 
-	return javalibJar
+	return javalibJar, artProfileOutputPath
 }
diff --git a/java/dex_test.go b/java/dex_test.go
index 1ecdae0..4862d06 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -662,3 +662,54 @@
 	android.AssertStringDoesContain(t, "expected aarimports's proguard flags",
 		appR8.Args["r8Flags"], "proguard.txt")
 }
+
+func TestR8FlagsArtProfile(t *testing.T) {
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
+		android_app {
+			name: "app",
+			srcs: ["foo.java"],
+			platform_apis: true,
+			dex_preopt: {
+				profile_guided: true,
+				profile: "profile.txt.prof",
+				enable_profile_rewriting: true,
+			},
+		}
+	`)
+
+	app := result.ModuleForTests("app", "android_common")
+	appR8 := app.Rule("r8")
+	android.AssertStringDoesContain(t, "expected --art-profile in app r8 flags",
+		appR8.Args["r8Flags"], "--art-profile")
+
+	appDexpreopt := app.Rule("dexpreopt")
+	android.AssertStringDoesContain(t,
+		"expected --art-profile output to be used to create .prof binary",
+		appDexpreopt.RuleParams.Command,
+		"--create-profile-from=out/soong/.intermediates/app/android_common/profile.prof.txt --output-profile-type=app",
+	)
+}
+
+// This test checks that users explicitly set `enable_profile_rewriting` to true when the following are true
+// 1. optimize or obfuscate is enabled AND
+// 2. dex_preopt.profile_guided is enabled
+//
+// The rewritten profile should be used since the dex signatures in the checked-in profile will not match the optimized binary.
+func TestEnableProfileRewritingIsRequiredForOptimizedApps(t *testing.T) {
+	testJavaError(t,
+		"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on",
+		`
+android_app {
+	name: "app",
+	srcs: ["foo.java"],
+	platform_apis: true,
+	dex_preopt: {
+		profile_guided: true,
+		profile: "profile.txt.prof",
+		// enable_profile_rewriting is not set, this is an error
+	},
+	optimize: {
+		optimize: true,
+	}
+}`)
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 38ed856..7949244 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -19,6 +19,8 @@
 	"sort"
 	"strings"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 )
@@ -94,6 +96,10 @@
 	}
 }
 
+func (install dexpreopterInstall) PackageFile(ctx android.ModuleContext) android.PackagingSpec {
+	return ctx.PackageFile(install.installDirOnDevice, install.installFileOnDevice, install.outputPathOnHost)
+}
+
 type Dexpreopter struct {
 	dexpreopter
 }
@@ -139,6 +145,10 @@
 	// The path to the profile that dexpreopter accepts. It must be in the binary format. If this is
 	// set, it overrides the profile settings in `dexpreoptProperties`.
 	inputProfilePathOnHost android.Path
+
+	// The path to the profile that matches the dex optimized by r8/d8. It is in text format. If this is
+	// set, it will be converted to a binary profile which will be subsequently used for dexpreopt.
+	rewrittenProfile android.Path
 }
 
 type DexpreoptProperties struct {
@@ -158,6 +168,11 @@
 		// defaults to searching for a file that matches the name of this module in the default
 		// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
 		Profile *string `android:"path"`
+
+		// If set to true, r8/d8 will use `profile` as input to generate a new profile that matches
+		// the optimized dex.
+		// The new profile will be subsequently used as the profile to dexpreopt the dex file.
+		Enable_profile_rewriting *bool
 	}
 
 	Dex_preopt_result struct {
@@ -196,8 +211,10 @@
 	}
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	psi := android.PrebuiltSelectionInfoMap{}
-	ctx.VisitDirectDepsWithTag(android.PrebuiltDepTag, func(am android.Module) {
-		psi, _ = android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider)
+	ctx.VisitDirectDeps(func(am android.Module) {
+		if prebuiltSelectionInfo, ok := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); ok {
+			psi = prebuiltSelectionInfo
+		}
 	})
 	// Find the apex variant for this module
 	_, apexVariantsWithoutTestApexes, _ := android.ListSetDifference(apexInfo.InApexVariants, apexInfo.TestApexes)
@@ -243,10 +260,6 @@
 		return true
 	}
 
-	if disableSourceApexVariant(ctx) {
-		return true
-	}
-
 	if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex {
 		// dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes
 		return false
@@ -264,6 +277,20 @@
 		if !isApexSystemServerJar {
 			return true
 		}
+		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+		allApexInfos := []android.ApexInfo{}
+		if allApexInfosProvider, ok := android.ModuleProvider(ctx, android.AllApexInfoProvider); ok {
+			allApexInfos = allApexInfosProvider.ApexInfos
+		}
+		if len(allApexInfos) > 0 && !ai.MinSdkVersion.EqualTo(allApexInfos[0].MinSdkVersion) {
+			// Apex system server jars are dexpreopted and installed on to the system image.
+			// Since we can have BigAndroid and Go variants of system server jar providing apexes,
+			// and these two variants can have different min_sdk_versions, hide one of the apex variants
+			// from make to prevent collisions.
+			//
+			// Unlike cc, min_sdk_version does not have an effect on the build actions of java libraries.
+			ctx.Module().MakeUninstallable()
+		}
 	} else {
 		// Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
 		if isApexSystemServerJar {
@@ -289,10 +316,6 @@
 	dexpreopt.RegisterToolDeps(ctx)
 }
 
-func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, libName string, installPath android.InstallPath) bool {
-	return dexpreopt.OdexOnSystemOtherByName(libName, android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
-}
-
 // Returns the install path of the dex jar of a module.
 //
 // Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather
@@ -409,13 +432,17 @@
 	if d.inputProfilePathOnHost != nil {
 		profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost)
 	} else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) {
-		// If dex_preopt.profile_guided is not set, default it based on the existence of the
-		// dexprepot.profile option or the profile class listing.
-		if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
+		// If enable_profile_rewriting is set, use the rewritten profile instead of the checked-in profile
+		if d.EnableProfileRewriting() {
+			profileClassListing = android.OptionalPathForPath(d.GetRewrittenProfile())
+			profileIsTextListing = true
+		} else if profile := d.GetProfile(); profile != "" {
+			// If dex_preopt.profile_guided is not set, default it based on the existence of the
+			// dexprepot.profile option or the profile class listing.
 			profileClassListing = android.OptionalPathForPath(
-				android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile)))
+				android.PathForModuleSrc(ctx, profile))
 			profileBootListing = android.ExistentPathForSource(ctx,
-				ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot")
+				ctx.ModuleDir(), profile+"-boot")
 			profileIsTextListing = true
 		} else if global.ProfileDir != "" {
 			profileClassListing = android.ExistentPathForSource(ctx,
@@ -501,8 +528,12 @@
 		Output(appProductPackages)
 	productPackagesRule.Restat().Build("product_packages."+dexJarStem, "dexpreopt product_packages")
 
+	// Prebuilts are active, do not copy the dexpreopt'd source javalib to out/soong/system_server_dexjars
+	// The javalib from the deapexed prebuilt will be copied to this location.
+	// TODO (b/331665856): Implement a principled solution for this.
+	copyApexSystemServerJarDex := !disableSourceApexVariant(ctx) && !ctx.Module().IsHideFromMake()
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
-		ctx, globalSoong, global, dexpreoptConfig, appProductPackages)
+		ctx, globalSoong, global, dexpreoptConfig, appProductPackages, copyApexSystemServerJarDex)
 	if err != nil {
 		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
 		return
@@ -514,12 +545,29 @@
 	// Use the path of the dex file to determine the library name
 	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(dexJarStem)
 
+	dexpreoptPartition := d.installPath.Partition()
+	// dexpreoptPartition is set to empty for dexpreopts of system APEX and system_other.
+	// In case of system APEX, however, we can set it to "system" manually.
+	// TODO(b/346662300): Let dexpreopter generate the installPath for dexpreopt files instead of
+	// using the dex location to generate the installPath.
+	if isApexSystemServerJar {
+		dexpreoptPartition = "system"
+	}
 	for _, install := range dexpreoptRule.Installs() {
 		// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
 		installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
+		partition := dexpreoptPartition
+		if strings.HasPrefix(installDir, partition+"/") {
+			installDir = strings.TrimPrefix(installDir, partition+"/")
+		} else {
+			// If the partition for the installDir is different from the install partition, set the
+			// partition empty to install the dexpreopt files to the desired partition.
+			// TODO(b/346439786): Define and use the dexpreopt module type to avoid this mismatch.
+			partition = ""
+		}
 		installBase := filepath.Base(install.To)
 		arch := filepath.Base(installDir)
-		installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+		installPath := android.PathForModuleInPartitionInstall(ctx, partition, installDir)
 		isProfile := strings.HasSuffix(installBase, ".prof")
 
 		if isProfile {
@@ -553,6 +601,37 @@
 	}
 }
 
+func getModuleInstallPathInfo(ctx android.ModuleContext, fullInstallPath string) (android.InstallPath, string, string) {
+	installPath := android.PathForModuleInstall(ctx)
+	installDir, installBase := filepath.Split(strings.TrimPrefix(fullInstallPath, "/"))
+
+	if !strings.HasPrefix(installDir, installPath.Partition()+"/") {
+		// Return empty filename if the install partition is not for the target image.
+		return installPath, "", ""
+	}
+	relDir, err := filepath.Rel(installPath.Partition(), installDir)
+	if err != nil {
+		panic(err)
+	}
+	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) {
+	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.
+	// The skipped "apex" files are for testing only, for example,
+	// "/apex/art_boot_images/javalib/x86/boot.vdex".
+	// 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)
+	}
+}
+
 func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
 	return d.builtInstalledForApex
 }
@@ -572,3 +651,23 @@
 func (d *dexpreopter) disableDexpreopt() {
 	d.shouldDisableDexpreopt = true
 }
+
+func (d *dexpreopter) EnableProfileRewriting() bool {
+	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting)
+}
+
+func (d *dexpreopter) GetProfile() string {
+	return proptools.String(d.dexpreoptProperties.Dex_preopt.Profile)
+}
+
+func (d *dexpreopter) GetProfileGuided() bool {
+	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Profile_guided)
+}
+
+func (d *dexpreopter) GetRewrittenProfile() android.Path {
+	return d.rewrittenProfile
+}
+
+func (d *dexpreopter) SetRewrittenProfile(p android.Path) {
+	d.rewrittenProfile = p
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f7e3cb9..defa82c 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -562,7 +562,7 @@
 	return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} {
 		fragments := make(map[string]android.Module)
 		ctx.WalkDeps(func(child, parent android.Module) bool {
-			if !isActiveModule(child) {
+			if !isActiveModule(ctx, child) {
 				return false
 			}
 			tag := ctx.OtherModuleDependencyTag(child)
@@ -612,6 +612,9 @@
 			profileInstalls:            profileInstalls,
 			profileLicenseMetadataFile: android.OptionalPathForPath(ctx.LicenseMetadataFile()),
 		})
+		for _, install := range profileInstalls {
+			packageFile(ctx, install)
+		}
 	}
 }
 
@@ -929,6 +932,35 @@
 	return apexNameToApexExportsInfoMap
 }
 
+func packageFileForTargetImage(ctx android.ModuleContext, image *bootImageVariant) {
+	if image.target.Os != ctx.Os() {
+		// This is not for the target device.
+		return
+	}
+
+	for _, install := range image.installs {
+		packageFile(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)
+		}
+	}
+}
+
 // Generate boot image build rules for a specific target.
 func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs {
 
@@ -1123,9 +1155,10 @@
 	image.installs = rule.Installs()
 	image.vdexInstalls = vdexInstalls
 	image.unstrippedInstalls = unstrippedInstalls
+	packageFileForTargetImage(ctx, image)
 
 	// Only set the licenseMetadataFile from the active module.
-	if isActiveModule(ctx.Module()) {
+	if isActiveModule(ctx, ctx.Module()) {
 		image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
 	}
 
diff --git a/java/droiddoc.go b/java/droiddoc.go
index aec40b3..730f236 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -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))
 }
@@ -391,12 +378,14 @@
 			} else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
+				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...)
 			} else if dep, ok := module.(android.SourceFileProducer); ok {
 				checkProducesJars(ctx, dep)
 				deps.classpath = append(deps.classpath, dep.Srcs()...)
 			} else {
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
+
 		case java9LibTag:
 			if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 				deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...)
@@ -429,6 +418,19 @@
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
 	j.implicits = append(j.implicits, srcFiles...)
 
+	// Module can depend on a java_aconfig_library module using the ":module_name{.tag}" syntax.
+	// Find the corresponding aconfig_declarations module name for such case.
+	for _, src := range j.properties.Srcs {
+		if moduleName, tag := android.SrcIsModuleWithTag(src); moduleName != "" {
+			otherModule := android.GetModuleFromPathDep(ctx, moduleName, tag)
+			if otherModule != nil {
+				if dep, ok := android.OtherModuleProvider(ctx, otherModule, android.CodegenInfoProvider); ok {
+					deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...)
+				}
+			}
+		}
+	}
+
 	filterByPackage := func(srcs []android.Path, filterPackages []string) []android.Path {
 		if filterPackages == nil {
 			return srcs
@@ -570,6 +572,9 @@
 	zipSyncCleanupCmd(rule, srcJarDir)
 
 	rule.Build("javadoc", "javadoc")
+
+	ctx.SetOutputFiles(android.Paths{j.stubsSrcJar}, "")
+	ctx.SetOutputFiles(android.Paths{j.docZip}, ".docs.zip")
 }
 
 // Droiddoc
@@ -601,15 +606,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)
 
@@ -861,6 +857,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..e584640 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(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 9556e95..a8e0a22 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -106,9 +106,6 @@
 	everythingArtifacts stubsArtifacts
 	exportableArtifacts stubsArtifacts
 
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
-
 	exportableApiFile        android.WritablePath
 	exportableRemovedApiFile android.WritablePath
 }
@@ -286,66 +283,6 @@
 	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:
@@ -532,8 +469,8 @@
 		cmd.Flag(config.MetalavaAnnotationsFlags)
 
 		if params.migratingNullability {
-			previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
-			cmd.FlagWithInput("--migrate-nullness ", previousApi)
+			previousApiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Previous_api)})
+			cmd.FlagForEachInput("--migrate-nullness ", previousApiFiles)
 		}
 
 		if s := String(d.properties.Validate_nullability_from_list); s != "" {
@@ -604,6 +541,11 @@
 	}
 }
 
+// AndroidPlusUpdatableJar is the name of some extra jars added into `module-lib` and
+// `system-server` directories that contain all the APIs provided by the platform and updatable
+// modules because the `android.jar` files do not. See b/337836752.
+const AndroidPlusUpdatableJar = "android-plus-updatable.jar"
+
 func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) {
 	if len(d.properties.Api_levels_annotations_dirs) == 0 {
 		ctx.PropertyErrorf("api_levels_annotations_dirs",
@@ -614,25 +556,72 @@
 
 	filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
 
+	// TODO: Avoid the duplication of API surfaces, reuse apiScope.
+	// Add all relevant --android-jar-pattern patterns for Metalava.
+	// When parsing a stub jar for a specific version, Metalava picks the first pattern that defines
+	// an actual file present on disk (in the order the patterns were passed). For system APIs for
+	// privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs
+	// for older releases. Similarly, module-lib falls back to system API.
+	var sdkDirs []string
+	apiLevelsSdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public")
+	switch apiLevelsSdkType {
+	case "system-server":
+		sdkDirs = []string{"system-server", "module-lib", "system", "public"}
+	case "module-lib":
+		sdkDirs = []string{"module-lib", "system", "public"}
+	case "system":
+		sdkDirs = []string{"system", "public"}
+	case "public":
+		sdkDirs = []string{"public"}
+	default:
+		ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes)
+		return
+	}
+
+	// Construct a pattern to match the appropriate extensions that should be included in the
+	// generated api-versions.xml file.
+	//
+	// Use the first item in the sdkDirs array as that is the sdk type for the target API levels
+	// being generated but has the advantage over `Api_levels_sdk_type` as it has been validated.
+	// The exception is for system-server which needs to include module-lib and system-server. That
+	// is because while system-server extends module-lib the system-server extension directory only
+	// contains service-* modules which provide system-server APIs it does not list the modules which
+	// only provide a module-lib, so they have to be included separately.
+	extensionSurfacesPattern := sdkDirs[0]
+	if apiLevelsSdkType == "system-server" {
+		// Take the first two items in sdkDirs, which are system-server and module-lib, and construct
+		// a pattern that will match either.
+		extensionSurfacesPattern = strings.Join(sdkDirs[0:2], "|")
+	}
+	extensionsPattern := fmt.Sprintf(`/extensions/[0-9]+/(%s)/.*\.jar`, extensionSurfacesPattern)
+
 	var dirs []string
 	var extensions_dir string
 	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
 		if t, ok := m.(*ExportedDroiddocDir); ok {
-			extRegex := regexp.MustCompile(t.dir.String() + `/extensions/[0-9]+/public/.*\.jar`)
+			extRegex := regexp.MustCompile(t.dir.String() + extensionsPattern)
 
 			// Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps;
 			// ideally this should be read from prebuiltApis.properties.Extensions_*
 			for _, dep := range t.deps {
+				// Check to see if it matches an extension first.
+				depBase := dep.Base()
 				if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil {
 					if extensions_dir == "" {
 						extensions_dir = t.dir.String() + "/extensions"
 					}
 					cmd.Implicit(dep)
-				}
-				if dep.Base() == filename {
+				} else if depBase == filename {
+					// Check to see if it matches a dessert release for an SDK, e.g. Android, Car, Wear, etc..
 					cmd.Implicit(dep)
-				}
-				if filename != "android.jar" && dep.Base() == "android.jar" {
+				} else if depBase == AndroidPlusUpdatableJar && d.properties.Extensions_info_file != nil {
+					// The output api-versions.xml has been requested to include information on SDK
+					// extensions. That means it also needs to include
+					// so
+					// The module-lib and system-server directories should use `android-plus-updatable.jar`
+					// instead of `android.jar`. See AndroidPlusUpdatableJar for more information.
+					cmd.Implicit(dep)
+				} else if filename != "android.jar" && depBase == "android.jar" {
 					// Metalava implicitly searches these patterns:
 					//  prebuilts/tools/common/api-versions/android-%/android.jar
 					//  prebuilts/sdk/%/public/android.jar
@@ -650,29 +639,25 @@
 		}
 	})
 
-	// Add all relevant --android-jar-pattern patterns for Metalava.
-	// When parsing a stub jar for a specific version, Metalava picks the first pattern that defines
-	// an actual file present on disk (in the order the patterns were passed). For system APIs for
-	// privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs
-	// for older releases. Similarly, module-lib falls back to system API.
-	var sdkDirs []string
-	switch proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") {
-	case "system-server":
-		sdkDirs = []string{"system-server", "module-lib", "system", "public"}
-	case "module-lib":
-		sdkDirs = []string{"module-lib", "system", "public"}
-	case "system":
-		sdkDirs = []string{"system", "public"}
-	case "public":
-		sdkDirs = []string{"public"}
-	default:
-		ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes)
-		return
-	}
-
+	// Generate the list of --android-jar-pattern options. The order matters so the first one which
+	// matches will be the one that is used for a specific api level..
 	for _, sdkDir := range sdkDirs {
 		for _, dir := range dirs {
-			cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename))
+			addPattern := func(jarFilename string) {
+				cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, jarFilename))
+			}
+
+			if sdkDir == "module-lib" || sdkDir == "system-server" {
+				// The module-lib and system-server android.jars do not include the updatable modules (as
+				// doing so in the source would introduce dependency cycles and the prebuilts have to
+				// match the sources). So, instead an additional `android-plus-updatable.jar` will be used
+				// that does include the updatable modules and this pattern will match that. This pattern
+				// is added in addition to the following pattern to decouple this change from the change
+				// to add the `android-plus-updatable.jar`.
+				addPattern(AndroidPlusUpdatableJar)
+			}
+
+			addPattern(filename)
 		}
 	}
 
@@ -687,12 +672,29 @@
 	}
 }
 
+func (d *Droidstubs) apiCompatibilityFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType) {
+	if len(d.Javadoc.properties.Out) > 0 {
+		ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+	}
+
+	apiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Check_api.Last_released.Api_file)})
+	removedApiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Check_api.Last_released.Removed_api_file)})
+
+	cmd.FlagForEachInput("--check-compatibility:api:released ", apiFiles)
+	cmd.FlagForEachInput("--check-compatibility:removed:released ", removedApiFiles)
+
+	baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
+	if baselineFile.Valid() {
+		cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
+	}
+}
+
 func metalavaUseRbe(ctx android.ModuleContext) bool {
 	return ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA")
 }
 
-func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
-	srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand {
+func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
+	srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams) *android.RuleBuilderCommand {
 	rule.Command().Text("rm -rf").Flag(homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(homeDir.String())
 
@@ -722,14 +724,14 @@
 	cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")).
 		Flag(config.JavacVmFlags).
 		Flag(config.MetalavaAddOpens).
-		FlagWithArg("--java-source ", javaVersion.String()).
-		FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs).
+		FlagWithArg("--java-source ", params.javaVersion.String()).
+		FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, fmt.Sprintf("%s.metalava.rsp", params.stubsType.String())), srcs).
 		FlagWithInput("@", srcJarList)
 
 	// Metalava does not differentiate between bootclasspath and classpath and has not done so for
 	// years, so it is unlikely to change any time soon.
-	combinedPaths := append(([]android.Path)(nil), bootclasspath.Paths()...)
-	combinedPaths = append(combinedPaths, classpath.Paths()...)
+	combinedPaths := append(([]android.Path)(nil), params.deps.bootClasspath.Paths()...)
+	combinedPaths = append(combinedPaths, params.deps.classpath.Paths()...)
 	if len(combinedPaths) > 0 {
 		cmd.FlagWithInputList("--classpath ", combinedPaths, ":")
 	}
@@ -810,8 +812,7 @@
 	srcJarList := zipSyncCmd(ctx, rule, params.srcJarDir, d.Javadoc.srcJars)
 
 	homeDir := android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "home")
-	cmd := metalavaCmd(ctx, rule, params.stubConfig.javaVersion, d.Javadoc.srcFiles, srcJarList,
-		params.stubConfig.deps.bootClasspath, params.stubConfig.deps.classpath, homeDir)
+	cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig)
 	cmd.Implicits(d.Javadoc.implicits)
 
 	d.stubsFlags(ctx, cmd, params.stubsDir, params.stubConfig.stubsType, params.stubConfig.checkApi)
@@ -831,6 +832,10 @@
 	d.inclusionAnnotationsFlags(ctx, cmd)
 	d.apiLevelsAnnotationsFlags(ctx, cmd, params.stubConfig.stubsType, params.apiVersionsXml)
 
+	if params.stubConfig.doCheckReleased {
+		d.apiCompatibilityFlags(ctx, cmd, params.stubConfig.stubsType)
+	}
+
 	d.expandArgs(ctx, cmd)
 
 	for _, o := range d.Javadoc.properties.Out {
@@ -928,20 +933,21 @@
 func (d *Droidstubs) everythingOptionalCmd(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, doApiLint bool, doCheckReleased bool) {
 
 	// Add API lint options.
+	treatDocumentationIssuesAsErrors := false
 	if doApiLint {
-		newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
-		if newSince.Valid() {
-			cmd.FlagWithInput("--api-lint ", newSince.Path())
-		} else {
-			cmd.Flag("--api-lint")
+		var newSince android.Paths
+		if d.properties.Check_api.Api_lint.New_since != nil {
+			newSince = android.PathsForModuleSrc(ctx, []string{proptools.String(d.properties.Check_api.Api_lint.New_since)})
 		}
+		cmd.Flag("--api-lint")
+		cmd.FlagForEachInput("--api-lint-previous-api ", newSince)
 		d.apiLintReport = android.PathForModuleOut(ctx, Everything.String(), "api_lint_report.txt")
 		cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO:  Change to ":api-lint"
 
 		// TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
 		if d.Name() != "android.car-system-stubs-docs" &&
 			d.Name() != "android.car-stubs-docs" {
-			cmd.Flag("--lints-as-errors")
+			treatDocumentationIssuesAsErrors = true
 			cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
 		}
 
@@ -987,27 +993,18 @@
 		cmd.FlagWithArg("--error-message:api-lint ", msg)
 	}
 
+	if !treatDocumentationIssuesAsErrors {
+		treatDocumentationIssuesAsWarningErrorWhenNew(cmd)
+	}
+
 	// Add "check released" options. (Detect incompatible API changes from the last public release)
 	if doCheckReleased {
-		if len(d.Javadoc.properties.Out) > 0 {
-			ctx.PropertyErrorf("out", "out property may not be combined with check_api")
-		}
-
-		apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
-		removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
 		baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
-		updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "last_released_baseline.txt")
-
 		d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_last_released_api.timestamp")
-
-		cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
-		cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
-
 		if baselineFile.Valid() {
-			cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
+			updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "last_released_baseline.txt")
 			cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
 		}
-
 		// Note this string includes quote ($' ... '), which decodes the "\n"s.
 		msg := `$'\n******************************\n` +
 			`You have tried to change the API from what has been previously released in\n` +
@@ -1025,6 +1022,22 @@
 	}
 }
 
+// HIDDEN_DOCUMENTATION_ISSUES is the set of documentation related issues that should always be
+// hidden as they are very noisy and provide little value.
+var HIDDEN_DOCUMENTATION_ISSUES = []string{
+	"Deprecated",
+	"IntDef",
+	"Nullable",
+}
+
+func treatDocumentationIssuesAsWarningErrorWhenNew(cmd *android.RuleBuilderCommand) {
+	// Treat documentation issues as warnings, but error when new.
+	cmd.Flag("--error-when-new-category").Flag("Documentation")
+
+	// Hide some documentation issues that generated a lot of noise for little benefit.
+	cmd.FlagForEachArg("--hide ", HIDDEN_DOCUMENTATION_ISSUES)
+}
+
 // Sandbox rule for generating exportable stubs and other artifacts
 func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) {
 	optionalCmdParams := stubsCommandParams{
@@ -1095,6 +1108,9 @@
 		}
 	}
 
+	// Treat documentation issues as warnings, but error when new.
+	treatDocumentationIssuesAsWarningErrorWhenNew(cmd)
+
 	if params.stubConfig.generateStubs {
 		rule.Command().
 			BuiltTool("soong_zip").
@@ -1287,7 +1303,46 @@
 
 		rule.Build("nullabilityWarningsCheck", "nullability warnings check")
 	}
-	android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles)
+
+	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) {
@@ -1315,7 +1370,7 @@
 // use a strict naming convention
 var (
 	droidstubsModuleNamingToSdkKind = map[string]android.SdkKind{
-		//public is commented out since the core libraries use public in their java_sdk_library names
+		// public is commented out since the core libraries use public in their java_sdk_library names
 		"intracore":     android.SdkIntraCore,
 		"intra.core":    android.SdkIntraCore,
 		"system_server": android.SdkSystemServer,
@@ -1378,17 +1433,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
 }
@@ -1427,6 +1471,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 8da695f..6a14f36 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -379,6 +379,7 @@
 	aconfig_declarations {
 		name: "bar",
 		package: "com.example.package",
+		container: "com.android.foo",
 		srcs: [
 			"bar.aconfig",
 		],
@@ -434,6 +435,7 @@
 	aconfig_declarations {
 		name: "bar",
 		package: "com.example.package",
+		container: "com.android.foo",
 		srcs: [
 			"bar.aconfig",
 		],
diff --git a/java/fuzz.go b/java/fuzz.go
index dc4c6be..d37c558 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -64,6 +64,8 @@
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
 	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Top_level_test_target = true
 
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
 		disableLinuxBionic := struct {
@@ -177,7 +179,7 @@
 			javaFuzzModule.ApexModuleBase,
 		}
 
-		if ok := fuzz.IsValid(fuzzModuleValidator); !ok {
+		if ok := fuzz.IsValid(ctx, fuzzModuleValidator); !ok {
 			return
 		}
 
diff --git a/java/gen.go b/java/gen.go
index 68a9b53..1b4f4c7 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -27,7 +27,6 @@
 
 func init() {
 	pctx.SourcePathVariable("logtagsCmd", "build/make/tools/java-event-log-tags.py")
-	pctx.SourcePathVariable("mergeLogtagsCmd", "build/make/tools/merge-event-log-tags.py")
 	pctx.SourcePathVariable("logtagsLib", "build/make/tools/event_log_tags.py")
 }
 
@@ -37,12 +36,6 @@
 			Command:     "$logtagsCmd -o $out $in",
 			CommandDeps: []string{"$logtagsCmd", "$logtagsLib"},
 		})
-
-	mergeLogtags = pctx.AndroidStaticRule("mergeLogtags",
-		blueprint.RuleParams{
-			Command:     "$mergeLogtagsCmd -o $out $in",
-			CommandDeps: []string{"$mergeLogtagsCmd", "$logtagsLib"},
-		})
 )
 
 func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlGlobalFlags string, aidlIndividualFlags map[string]string, deps android.Paths) android.Paths {
@@ -178,37 +171,9 @@
 		outSrcFiles = append(outSrcFiles, srcJarFiles...)
 	}
 
+	android.SetProvider(ctx, android.LogtagsProviderKey, &android.LogtagsInfo{
+		Logtags: j.logtagsSrcs,
+	})
+
 	return outSrcFiles
 }
-
-func LogtagsSingleton() android.Singleton {
-	return &logtagsSingleton{}
-}
-
-type logtagsProducer interface {
-	logtags() android.Paths
-}
-
-func (j *Module) logtags() android.Paths {
-	return j.logtagsSrcs
-}
-
-var _ logtagsProducer = (*Module)(nil)
-
-type logtagsSingleton struct{}
-
-func (l *logtagsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	var allLogtags android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
-		if logtags, ok := module.(logtagsProducer); ok {
-			allLogtags = append(allLogtags, logtags.logtags()...)
-		}
-	})
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        mergeLogtags,
-		Description: "merge logtags",
-		Output:      android.PathForIntermediates(ctx, "all-event-log-tags.txt"),
-		Inputs:      allLogtags,
-	})
-}
diff --git a/java/generated_java_library.go b/java/generated_java_library.go
index e8316cc..d5e6d8f 100644
--- a/java/generated_java_library.go
+++ b/java/generated_java_library.go
@@ -34,7 +34,7 @@
 
 	// Called from inside GenerateAndroidBuildActions. Add the build rules to
 	// make the srcjar, and return the path to it.
-	GenerateSourceJarBuildActions(module *GeneratedJavaLibraryModule, ctx android.ModuleContext) android.Path
+	GenerateSourceJarBuildActions(module *GeneratedJavaLibraryModule, ctx android.ModuleContext) (android.Path, android.Path)
 }
 
 // GeneratedJavaLibraryModuleFactory provides a utility for modules that are generated
@@ -103,8 +103,10 @@
 	checkPropertyEmpty(ctx, module, "plugins", module.Library.properties.Plugins)
 	checkPropertyEmpty(ctx, module, "exported_plugins", module.Library.properties.Exported_plugins)
 
-	srcJarPath := module.callbacks.GenerateSourceJarBuildActions(module, ctx)
+	srcJarPath, cacheOutputPath := module.callbacks.GenerateSourceJarBuildActions(module, ctx)
+
 	module.Library.properties.Generated_srcjars = append(module.Library.properties.Generated_srcjars, srcJarPath)
+	module.Library.properties.Aconfig_Cache_files = append(module.Library.properties.Aconfig_Cache_files, cacheOutputPath)
 	module.Library.GenerateAndroidBuildActions(ctx)
 }
 
diff --git a/java/generated_java_library_test.go b/java/generated_java_library_test.go
index be816cd..a5c4be1 100644
--- a/java/generated_java_library_test.go
+++ b/java/generated_java_library_test.go
@@ -36,8 +36,8 @@
 func (callbacks *JavaGenLibTestCallbacks) DepsMutator(module *GeneratedJavaLibraryModule, ctx android.BottomUpMutatorContext) {
 }
 
-func (callbacks *JavaGenLibTestCallbacks) GenerateSourceJarBuildActions(module *GeneratedJavaLibraryModule, ctx android.ModuleContext) android.Path {
-	return android.PathForOutput(ctx, "blah.srcjar")
+func (callbacks *JavaGenLibTestCallbacks) GenerateSourceJarBuildActions(module *GeneratedJavaLibraryModule, ctx android.ModuleContext) (android.Path, android.Path) {
+	return android.PathForOutput(ctx, "blah.srcjar"), android.PathForOutput(ctx, "blah.pb")
 }
 
 func testGenLib(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult {
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index e4beb5e..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)
 }
@@ -1428,7 +1429,7 @@
 		// should not contribute to anything. So, rather than have a missing dex jar cause a Soong
 		// failure defer the error reporting to Ninja. Unless the prebuilt build target is explicitly
 		// built Ninja should never use the dex jar file.
-		if !isActiveModule(module) {
+		if !isActiveModule(ctx, module) {
 			return true
 		}
 
@@ -1478,13 +1479,3 @@
 	}
 	return bootDexJar.Path()
 }
-
-// extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules.
-func extractEncodedDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
-	encodedDexJarsByModuleName := bootDexJarByModule{}
-	for _, module := range contents {
-		path := retrieveEncodedBootDexJarFromModule(ctx, module)
-		encodedDexJarsByModuleName.addPath(module, path)
-	}
-	return encodedDexJarsByModuleName
-}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 8cb78cd..7d21b7a 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -150,6 +150,10 @@
 	// Strip a prebuilt_ prefix so that this can match a prebuilt module that has not been renamed.
 	name = android.RemoveOptionalPrebuiltPrefix(name)
 
+	// Strip the ".impl" suffix, so that the implementation library of the java_sdk_library is
+	// treated identical to the top level java_sdk_library.
+	name = strings.TrimSuffix(name, ".impl")
+
 	// Ignore any module that is not listed in the boot image configuration.
 	index := configuredBootJars.IndexOfJar(name)
 	if index == -1 {
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index c1fee21..6229797 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -198,13 +198,22 @@
 				hiddenApiFixtureFactory,
 				tc.preparer,
 				prepareForTestWithDefaultPlatformBootclasspath,
+				// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
+				// file creation.
+				FixtureConfigureBootJars("platform:foo"),
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
 					variables.BuildFlags = map[string]string{
 						"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
 					}
 				}),
-			).RunTest(t)
+			).RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+		}
+		`)
 
 			hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
 			hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
@@ -303,7 +312,7 @@
 	`)
 
 	checkDexEncoded := func(t *testing.T, name, unencodedDexJar, encodedDexJar string) {
-		moduleForTests := result.ModuleForTests(name, "android_common")
+		moduleForTests := result.ModuleForTests(name+".impl", "android_common")
 
 		encodeDexRule := moduleForTests.Rule("hiddenAPIEncodeDex")
 		actualUnencodedDexJar := encodeDexRule.Input
@@ -319,18 +328,8 @@
 
 	// The java_library embedded with the java_sdk_library must be dex encoded.
 	t.Run("foo", func(t *testing.T) {
-		expectedUnencodedDexJar := "out/soong/.intermediates/foo/android_common/aligned/foo.jar"
-		expectedEncodedDexJar := "out/soong/.intermediates/foo/android_common/hiddenapi/foo.jar"
+		expectedUnencodedDexJar := "out/soong/.intermediates/foo.impl/android_common/aligned/foo.jar"
+		expectedEncodedDexJar := "out/soong/.intermediates/foo.impl/android_common/hiddenapi/foo.jar"
 		checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar)
 	})
-
-	// The dex jar of the child implementation java_library of the java_sdk_library is not currently
-	// dex encoded.
-	t.Run("foo.impl", func(t *testing.T) {
-		fooImpl := result.ModuleForTests("foo.impl", "android_common")
-		encodeDexRule := fooImpl.MaybeRule("hiddenAPIEncodeDex")
-		if encodeDexRule.Rule != nil {
-			t.Errorf("foo.impl is not expected to be encoded")
-		}
-	})
 }
diff --git a/java/jacoco.go b/java/jacoco.go
index a820b38..696a0cc 100644
--- a/java/jacoco.go
+++ b/java/jacoco.go
@@ -53,7 +53,7 @@
 	}
 
 	j, ok := ctx.Module().(instrumentable)
-	if !ctx.Module().Enabled() || !ok {
+	if !ctx.Module().Enabled(ctx) || !ok {
 		return
 	}
 
diff --git a/java/java.go b/java/java.go
index ad10cbd..88b31b5 100644
--- a/java/java.go
+++ b/java/java.go
@@ -21,6 +21,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"slices"
 	"sort"
 	"strings"
 
@@ -74,7 +75,6 @@
 		ctx.BottomUp("jacoco_deps", jacocoDepsMutator).Parallel()
 	})
 
-	ctx.RegisterParallelSingletonType("logtags", LogtagsSingleton)
 	ctx.RegisterParallelSingletonType("kythe_java_extract", kytheExtractJavaFactory)
 }
 
@@ -82,8 +82,8 @@
 	// Register sdk member types.
 	android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType)
 	android.RegisterSdkMemberType(javaLibsSdkMemberType)
-	android.RegisterSdkMemberType(javaBootLibsSdkMemberType)
-	android.RegisterSdkMemberType(javaSystemserverLibsSdkMemberType)
+	android.RegisterSdkMemberType(JavaBootLibsSdkMemberType)
+	android.RegisterSdkMemberType(JavaSystemserverLibsSdkMemberType)
 	android.RegisterSdkMemberType(javaTestSdkMemberType)
 }
 
@@ -154,7 +154,7 @@
 	// either java_libs, or java_header_libs would end up exporting more information than was strictly
 	// necessary. The java_boot_libs property to allow those modules to be exported as part of the
 	// sdk/module_exports without exposing any unnecessary information.
-	javaBootLibsSdkMemberType = &librarySdkMemberType{
+	JavaBootLibsSdkMemberType = &librarySdkMemberType{
 		android.SdkMemberTypeBase{
 			PropertyName: "java_boot_libs",
 			SupportsSdk:  true,
@@ -193,7 +193,7 @@
 	// either java_libs, or java_header_libs would end up exporting more information than was strictly
 	// necessary. The java_systemserver_libs property to allow those modules to be exported as part of
 	// the sdk/module_exports without exposing any unnecessary information.
-	javaSystemserverLibsSdkMemberType = &librarySdkMemberType{
+	JavaSystemserverLibsSdkMemberType = &librarySdkMemberType{
 		android.SdkMemberTypeBase{
 			PropertyName: "java_systemserver_libs",
 			SupportsSdk:  true,
@@ -366,14 +366,14 @@
 	toolchain bool
 
 	static bool
+
+	installable bool
 }
 
-// installDependencyTag is a dependency tag that is annotated to cause the installed files of the
-// dependency to be installed when the parent module is installed.
-type installDependencyTag struct {
-	blueprint.BaseDependencyTag
-	android.InstallAlwaysNeededDependencyTag
-	name string
+var _ android.InstallNeededDependencyTag = (*dependencyTag)(nil)
+
+func (d dependencyTag) InstallDepNeeded() bool {
+	return d.installable
 }
 
 func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
@@ -405,7 +405,7 @@
 }
 
 func IsJniDepTag(depTag blueprint.DependencyTag) bool {
-	return depTag == jniLibTag
+	return depTag == jniLibTag || depTag == jniInstallTag
 }
 
 var (
@@ -434,8 +434,8 @@
 	javaApiContributionTag  = dependencyTag{name: "java-api-contribution"}
 	depApiSrcsTag           = dependencyTag{name: "dep-api-srcs"}
 	aconfigDeclarationTag   = dependencyTag{name: "aconfig-declaration"}
-	jniInstallTag           = installDependencyTag{name: "jni install"}
-	binaryInstallTag        = installDependencyTag{name: "binary install"}
+	jniInstallTag           = dependencyTag{name: "jni install", runtimeLinked: true, installable: true}
+	binaryInstallTag        = dependencyTag{name: "binary install", runtimeLinked: true, installable: true}
 	usesLibReqTag           = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false)
 	usesLibOptTag           = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true)
 	usesLibCompat28OptTag   = makeUsesLibraryDependencyTag(28, true)
@@ -491,6 +491,7 @@
 	coverageFile   android.OptionalPath
 	unstrippedFile android.Path
 	partition      string
+	installPaths   android.InstallPaths
 }
 
 func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) {
@@ -566,6 +567,12 @@
 		return normalizeJavaVersion(ctx, javaVersion)
 	} else if ctx.Device() {
 		return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
+	} else if ctx.Config().TargetsJava21() {
+		// Temporary experimental flag to be able to try and build with
+		// java version 21 options.  The flag, if used, just sets Java
+		// 21 as the default version, leaving any components that
+		// target an older version intact.
+		return JAVA_VERSION_21
 	} else {
 		return JAVA_VERSION_17
 	}
@@ -586,6 +593,7 @@
 	JAVA_VERSION_9           = 9
 	JAVA_VERSION_11          = 11
 	JAVA_VERSION_17          = 17
+	JAVA_VERSION_21          = 21
 )
 
 func (v javaVersion) String() string {
@@ -604,6 +612,8 @@
 		return "11"
 	case JAVA_VERSION_17:
 		return "17"
+	case JAVA_VERSION_21:
+		return "21"
 	default:
 		return "unsupported"
 	}
@@ -646,6 +656,8 @@
 		return JAVA_VERSION_11
 	case "17":
 		return JAVA_VERSION_17
+	case "21":
+		return JAVA_VERSION_21
 	case "10", "12", "13", "14", "15", "16":
 		ctx.PropertyErrorf("java_version", "Java language level %s is not supported", javaVersion)
 		return JAVA_VERSION_UNSUPPORTED
@@ -669,6 +681,10 @@
 
 var _ android.ApexModule = (*Library)(nil)
 
+func (j *Library) CheckDepsMinSdkVersion(ctx android.ModuleContext) {
+	CheckMinSdkVersion(ctx, j)
+}
+
 // Provides access to the list of permitted packages from apex boot jars.
 type PermittedPackagesForUpdatableBootJars interface {
 	PermittedPackagesForUpdatableBootJars() []string
@@ -799,6 +815,7 @@
 		"android.hardware.security.keymint-V2-java":         true,
 		"android.hardware.security.keymint-V3-java":         true,
 		"android.hardware.security.keymint-V4-java":         true,
+		"android.hardware.security.secretkeeper-V1-java":    true,
 		"android.hardware.security.secureclock-V1-java":     true,
 		"android.hardware.security.secureclock-V2-java":     true,
 		"android.hardware.thermal-V1-java":                  true,
@@ -884,12 +901,24 @@
 }
 
 func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if disableSourceApexVariant(ctx) {
+		// Prebuilts are active, do not create the installation rules for the source javalib.
+		// Even though the source javalib is not used, we need to hide it to prevent duplicate installation rules.
+		// TODO (b/331665856): Implement a principled solution for this.
+		j.HideFromMake()
+	}
 	j.provideHiddenAPIPropertyInfo(ctx)
 
 	j.sdkVersion = j.SdkVersion(ctx)
 	j.minSdkVersion = j.MinSdkVersion(ctx)
 	j.maxSdkVersion = j.MaxSdkVersion(ctx)
 
+	// Check min_sdk_version of the transitive dependencies if this module is created from
+	// java_sdk_library.
+	if j.overridableProperties.Min_sdk_version != nil && j.SdkLibraryName() != nil {
+		j.CheckDepsMinSdkVersion(ctx)
+	}
+
 	// SdkLibrary.GenerateAndroidBuildActions(ctx) sets the stubsLinkType to Unknown.
 	// If the stubsLinkType has already been set to Unknown, the stubsLinkType should
 	// not be overridden.
@@ -920,8 +949,12 @@
 	j.checkSdkVersions(ctx)
 	j.checkHeadersOnly(ctx)
 	if ctx.Device() {
+		libName := j.Name()
+		if j.SdkLibraryName() != nil && strings.HasSuffix(libName, ".impl") {
+			libName = proptools.String(j.SdkLibraryName())
+		}
 		j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
-			ctx, j.Name(), android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
+			ctx, libName, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
 		j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
 		setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
 		j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
@@ -932,8 +965,26 @@
 	}
 	j.compile(ctx, nil, nil, nil)
 
-	exclusivelyForApex := !apexInfo.IsForPlatform()
-	if (Bool(j.properties.Installable) || ctx.Host()) && !exclusivelyForApex {
+	// If this module is an impl library created from java_sdk_library,
+	// install the files under the java_sdk_library module outdir instead of this module outdir.
+	if j.SdkLibraryName() != nil && strings.HasSuffix(j.Name(), ".impl") {
+		j.setInstallRules(ctx, proptools.String(j.SdkLibraryName()))
+	} else {
+		j.setInstallRules(ctx, ctx.ModuleName())
+	}
+
+	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+		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) {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+
+	if (Bool(j.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() {
 		var extraInstallDeps android.InstallPaths
 		if j.InstallMixin != nil {
 			extraInstallDeps = j.InstallMixin(ctx, j.outputFile)
@@ -950,7 +1001,7 @@
 			if !ctx.Host() {
 				archDir = ctx.DeviceConfig().DeviceArch()
 			}
-			installDir = android.PathForModuleInstall(ctx, ctx.ModuleName(), archDir)
+			installDir = android.PathForModuleInstall(ctx, installModuleName, archDir)
 		} else {
 			installDir = android.PathForModuleInstall(ctx, "framework")
 		}
@@ -959,8 +1010,18 @@
 }
 
 func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
-	j.deps(ctx)
 	j.usesLibrary.deps(ctx, false)
+	j.deps(ctx)
+
+	if j.SdkLibraryName() != nil && strings.HasSuffix(j.Name(), ".impl") {
+		if dexpreopt.IsDex2oatNeeded(ctx) {
+			dexpreopt.RegisterToolDeps(ctx)
+		}
+		prebuiltSdkLibExists := ctx.OtherModuleExists(android.PrebuiltNameFromSource(proptools.String(j.SdkLibraryName())))
+		if prebuiltSdkLibExists && ctx.OtherModuleExists("all_apex_contributions") {
+			ctx.AddDependency(ctx.Module(), android.AcDepTag, "all_apex_contributions")
+		}
+	}
 }
 
 const (
@@ -1044,7 +1105,7 @@
 
 	// If the min_sdk_version was set then add the canonical representation of the API level to the
 	// snapshot.
-	if j.deviceProperties.Min_sdk_version != nil {
+	if j.overridableProperties.Min_sdk_version != nil {
 		canonical, err := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.String())
 		if err != nil {
 			ctx.ModuleErrorf("%s", err)
@@ -1121,6 +1182,7 @@
 	module := &Library{}
 
 	module.addHostAndDeviceProperties()
+	module.AddProperties(&module.sourceProperties)
 
 	module.initModuleAndImport(module)
 
@@ -1296,7 +1358,7 @@
 	return true
 }
 
-func (j *TestHost) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (j *TestHost) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
@@ -1437,9 +1499,20 @@
 
 	j.Test.generateAndroidBuildActionsWithConfig(ctx, configs)
 	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+	android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
+		InstalledFiles:      j.data,
+		OutputFile:          j.outputFile,
+		TestConfig:          j.testConfig,
+		RequiredModuleNames: j.RequiredModuleNames(ctx),
+		TestSuites:          j.testProperties.Test_suites,
+		IsHost:              true,
+		LocalSdkVersion:     j.sdkVersion.String(),
+		IsUnitTest:          Bool(j.testProperties.Test_options.Unit_test),
+	})
 }
 
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	checkMinSdkVersionMts(ctx, j.MinSdkVersion(ctx))
 	j.generateAndroidBuildActionsWithConfig(ctx, nil)
 	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
@@ -1594,6 +1667,8 @@
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
 	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Top_level_test_target = true
 
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	return module
@@ -1609,6 +1684,7 @@
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
 	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
 
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	return module
@@ -1664,6 +1740,8 @@
 	th.properties.Installable = installable
 	th.testProperties.Auto_gen_config = autoGenConfig
 	th.testProperties.Test_suites = testSuites
+	th.sourceProperties.Test_only = proptools.BoolPtr(true)
+	th.sourceProperties.Top_level_test_target = true
 }
 
 //
@@ -1760,6 +1838,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)
 	}
 }
 
@@ -1789,7 +1869,7 @@
 	module := &Binary{}
 
 	module.addHostAndDeviceProperties()
-	module.AddProperties(&module.binaryProperties)
+	module.AddProperties(&module.binaryProperties, &module.sourceProperties)
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
@@ -2105,7 +2185,7 @@
 
 // Map where key is the api scope name and value is the int value
 // representing the order of the api scope, narrowest to the widest
-var scopeOrderMap = allApiScopes.MapToIndex(
+var scopeOrderMap = AllApiScopes.MapToIndex(
 	func(s *apiScope) string { return s.name })
 
 func (al *ApiLibrary) sortApiFilesByApiScope(ctx android.ModuleContext, srcFilesInfo []JavaApiImportInfo) []JavaApiImportInfo {
@@ -2210,10 +2290,10 @@
 
 	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)
@@ -2266,7 +2346,7 @@
 		classesJar:    al.stubsJar,
 		jarName:       ctx.ModuleName() + ".jar",
 	}
-	dexOutputFile := al.dexer.compileDex(ctx, dexParams)
+	dexOutputFile, _ := al.dexer.compileDex(ctx, dexParams)
 	uncompressed := true
 	al.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), al.stubsJar, &uncompressed)
 	dexOutputFile = al.hiddenAPIEncodeDex(ctx, dexOutputFile)
@@ -2337,6 +2417,9 @@
 	// List of shared java libs that this module has dependencies to
 	Libs []string
 
+	// List of static java libs that this module has dependencies to
+	Static_libs []string
+
 	// List of files to remove from the jar file(s)
 	Exclude_files []string
 
@@ -2389,9 +2472,10 @@
 	dexJarFileErr     error
 	dexJarInstallFile android.Path
 
-	combinedClasspathFile android.Path
-	classLoaderContexts   dexpreopt.ClassLoaderContextMap
-	exportAidlIncludeDirs android.Paths
+	combinedImplementationFile android.Path
+	combinedHeaderFile         android.Path
+	classLoaderContexts        dexpreopt.ClassLoaderContextMap
+	exportAidlIncludeDirs      android.Paths
 
 	hideApexVariantFromMake bool
 
@@ -2475,6 +2559,7 @@
 
 func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
+	ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...)
 
 	if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
 		sdkDeps(ctx, android.SdkContext(j), j.dexer)
@@ -2504,23 +2589,13 @@
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	j.commonBuildActions(ctx)
 
-	jars := android.PathsForModuleSrc(ctx, j.properties.Jars)
-
-	jarName := j.Stem() + ".jar"
-	outputFile := android.PathForModuleOut(ctx, "combined", jarName)
-	TransformJarsToJar(ctx, outputFile, "for prebuilts", jars, android.OptionalPath{},
-		false, j.properties.Exclude_files, j.properties.Exclude_dirs)
-	if Bool(j.properties.Jetifier) {
-		inputFile := outputFile
-		outputFile = android.PathForModuleOut(ctx, "jetifier", jarName)
-		TransformJetifier(ctx, outputFile, inputFile)
-	}
-	j.combinedClasspathFile = outputFile
 	j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
 
 	var flags javaBuilderFlags
 
 	j.collectTransitiveHeaderJars(ctx)
+	var staticJars android.Paths
+	var staticHeaderJars android.Paths
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
@@ -2530,6 +2605,8 @@
 				flags.dexClasspath = append(flags.dexClasspath, dep.HeaderJars...)
 			case staticLibTag:
 				flags.classpath = append(flags.classpath, dep.HeaderJars...)
+				staticJars = append(staticJars, dep.ImplementationAndResourcesJars...)
+				staticHeaderJars = append(staticHeaderJars, dep.HeaderJars...)
 			case bootClasspathTag:
 				flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars...)
 			}
@@ -2543,6 +2620,50 @@
 		addCLCFromDep(ctx, module, j.classLoaderContexts)
 	})
 
+	jars := android.PathsForModuleSrc(ctx, j.properties.Jars)
+	jarName := j.Stem() + ".jar"
+
+	// 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{},
+		false, j.properties.Exclude_files, j.properties.Exclude_dirs)
+
+	// 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
+	if reuseImplementationJarAsHeaderJar {
+		headerOutputFile = outputFile
+	} else {
+		headerJars := append(slices.Clone(jars), 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)
+	}
+
+	if Bool(j.properties.Jetifier) {
+		inputFile := outputFile
+		outputFile = android.PathForModuleOut(ctx, "jetifier", jarName)
+		TransformJetifier(ctx, outputFile, inputFile)
+
+		if !reuseImplementationJarAsHeaderJar {
+			headerInputFile := headerOutputFile
+			headerOutputFile = android.PathForModuleOut(ctx, "jetifier-headers", jarName)
+			TransformJetifier(ctx, headerOutputFile, headerInputFile)
+		} else {
+			headerOutputFile = outputFile
+		}
+	}
+
+	// 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.combinedImplementationFile = outputFile.WithoutRel()
+
 	j.maybeInstall(ctx, jarName, outputFile)
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
@@ -2609,7 +2730,7 @@
 				jarName:       jarName,
 			}
 
-			dexOutputFile = j.dexer.compileDex(ctx, dexParams)
+			dexOutputFile, _ = j.dexer.compileDex(ctx, dexParams)
 			if ctx.Failed() {
 				return
 			}
@@ -2626,15 +2747,18 @@
 	}
 
 	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
-		HeaderJars:                     android.PathsIfNonNil(j.combinedClasspathFile),
+		HeaderJars:                     android.PathsIfNonNil(j.combinedHeaderFile),
 		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
 		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
-		ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedClasspathFile),
-		ImplementationJars:             android.PathsIfNonNil(j.combinedClasspathFile),
+		ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedImplementationFile),
+		ImplementationJars:             android.PathsIfNonNil(j.combinedImplementationFile),
 		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) {
@@ -2655,29 +2779,12 @@
 	ctx.InstallFile(installDir, jarName, outputFile)
 }
 
-func (j *Import) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "", ".jar":
-		return android.Paths{j.combinedClasspathFile}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
-var _ android.OutputFileProducer = (*Import)(nil)
-
 func (j *Import) HeaderJars() android.Paths {
-	if j.combinedClasspathFile == nil {
-		return nil
-	}
-	return android.Paths{j.combinedClasspathFile}
+	return android.PathsIfNonNil(j.combinedHeaderFile)
 }
 
 func (j *Import) ImplementationAndResourcesJars() android.Paths {
-	if j.combinedClasspathFile == nil {
-		return nil
-	}
-	return android.Paths{j.combinedClasspathFile}
+	return android.PathsIfNonNil(j.combinedImplementationFile)
 }
 
 func (j *Import) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
@@ -3106,13 +3213,35 @@
 	// <uses_library> and should not be added to CLC, but the transitive <uses-library> dependencies
 	// from its CLC should be added to the current CLC.
 	if sdkLib != nil {
-		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false,
+		optional := false
+		if module, ok := ctx.Module().(ModuleWithUsesLibrary); ok {
+			if android.InList(*sdkLib, module.UsesLibrary().usesLibraryProperties.Optional_uses_libs) {
+				optional = true
+			}
+		}
+		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, optional,
 			dep.DexJarBuildPath(ctx).PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
 	} else {
 		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
 	}
 }
 
+func addMissingOptionalUsesLibsFromDep(ctx android.ModuleContext, depModule android.Module,
+	usesLibrary *usesLibrary) {
+
+	dep, ok := depModule.(ModuleWithUsesLibrary)
+	if !ok {
+		return
+	}
+
+	for _, lib := range dep.UsesLibrary().usesLibraryProperties.Missing_optional_uses_libs {
+		if !android.InList(lib, usesLibrary.usesLibraryProperties.Missing_optional_uses_libs) {
+			usesLibrary.usesLibraryProperties.Missing_optional_uses_libs =
+				append(usesLibrary.usesLibraryProperties.Missing_optional_uses_libs, lib)
+		}
+	}
+}
+
 type JavaApiContributionImport struct {
 	JavaApiContribution
 
diff --git a/java/java_test.go b/java/java_test.go
index 194f9d9..33079f3 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -588,10 +588,11 @@
 	javac := fooModule.Rule("javac")
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
 	barModule := ctx.ModuleForTests("bar", "android_common")
-	barJar := barModule.Rule("combineJar").Output
+	barJar := barModule.Output("combined/bar.jar").Output
 	bazModule := ctx.ModuleForTests("baz", "android_common")
 	bazJar := bazModule.Rule("combineJar").Output
-	sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").Rule("combineJar").Output
+	sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").
+		Output("combined/sdklib.stubs.jar").Output
 
 	fooLibrary := fooModule.Module().(*Library)
 	assertDeepEquals(t, "foo unique sources incorrect",
@@ -1035,7 +1036,7 @@
 	}
 }
 
-func TestJavaLibrary(t *testing.T) {
+func TestJavaLibraryOutputFiles(t *testing.T) {
 	testJavaWithFS(t, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_library {
@@ -1052,7 +1053,7 @@
 	})
 }
 
-func TestJavaImport(t *testing.T) {
+func TestJavaImportOutputFiles(t *testing.T) {
 	testJavaWithFS(t, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_import {
@@ -1068,6 +1069,85 @@
 	})
 }
 
+func TestJavaImport(t *testing.T) {
+	bp := `
+		java_library {
+			name: "source_library",
+			srcs: ["source.java"],
+		}
+
+		java_import {
+			name: "import_with_no_deps",
+			jars: ["no_deps.jar"],
+		}
+
+		java_import {
+			name: "import_with_source_deps",
+			jars: ["source_deps.jar"],
+			static_libs: ["source_library"],
+		}
+
+		java_import {
+			name: "import_with_import_deps",
+			jars: ["import_deps.jar"],
+			static_libs: ["import_with_no_deps"],
+		}
+	`
+	ctx := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, bp)
+
+	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)
+
+	// The source library produces separate implementation and header jars
+	android.AssertPathsRelativeToTopEquals(t, "source library implementation jar",
+		[]string{sourceJar.Output.String()}, sourceJavaInfo.ImplementationAndResourcesJars)
+	android.AssertPathsRelativeToTopEquals(t, "source library header jar",
+		[]string{sourceHeaderJar.Output.String()}, sourceJavaInfo.HeaderJars)
+
+	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)
+
+	// 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",
+		[]string{importWithNoDepsJar.Output.String()}, importWithNoDepsJavaInfo.ImplementationAndResourcesJars)
+	android.AssertPathsRelativeToTopEquals(t, "import with no deps header jar",
+		[]string{importWithNoDepsJar.Output.String()}, importWithNoDepsJavaInfo.HeaderJars)
+	android.AssertPathsRelativeToTopEquals(t, "import with no deps combined inputs",
+		[]string{"no_deps.jar"}, importWithNoDepsJar.Inputs)
+
+	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)
+
+	// An import with source deps produces separate header and implementation jars.
+	android.AssertPathsRelativeToTopEquals(t, "import with source deps implementation jar",
+		[]string{importWithSourceDepsJar.Output.String()}, importWithSourceDepsJavaInfo.ImplementationAndResourcesJars)
+	android.AssertPathsRelativeToTopEquals(t, "import with source deps header jar",
+		[]string{importWithSourceDepsHeaderJar.Output.String()}, importWithSourceDepsJavaInfo.HeaderJars)
+	android.AssertPathsRelativeToTopEquals(t, "import with source deps combined implementation jar inputs",
+		[]string{"source_deps.jar", sourceJar.Output.String()}, importWithSourceDepsJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with source deps combined header jar inputs",
+		[]string{"source_deps.jar", sourceHeaderJar.Output.String()}, importWithSourceDepsHeaderJar.Inputs)
+
+	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)
+
+	// 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",
+		[]string{importWithImportDepsJar.Output.String()}, importWithImportDepsJavaInfo.ImplementationAndResourcesJars)
+	android.AssertPathsRelativeToTopEquals(t, "import with import deps header jar",
+		[]string{importWithImportDepsJar.Output.String()}, importWithImportDepsJavaInfo.HeaderJars)
+	android.AssertPathsRelativeToTopEquals(t, "import with import deps combined implementation jar inputs",
+		[]string{"import_deps.jar", importWithNoDepsJar.Output.String()}, importWithImportDepsJar.Inputs)
+}
+
 var compilerFlagsTestCases = []struct {
 	in  string
 	out bool
@@ -2721,6 +2801,7 @@
 	aconfig_declarations {
 		name: "bar",
 		package: "com.example.package",
+		container: "com.android.foo",
 		srcs: [
 			"bar.aconfig",
 		],
@@ -2758,6 +2839,99 @@
 	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt")
 }
 
+func TestTestOnly(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+                // These should be test-only
+		java_library {
+			name: "lib1-test-only",
+                        srcs: ["a.java"],
+                        test_only: true,
+		}
+                java_test {
+                        name: "java-test",
+                }
+                java_test_host {
+                        name: "java-test-host",
+                }
+                java_test_helper_library {
+                        name: "helper-library",
+                }
+                java_binary {
+                        name: "java-data-binary",
+			srcs: ["foo.java"],
+			main_class: "foo.bar.jb",
+                        test_only: true,
+                }
+
+                // These are NOT
+		java_library {
+			name: "lib2-app",
+                        srcs: ["b.java"],
+		}
+		java_import {
+			name: "bar",
+			jars: ["bar.jar"],
+		}
+                java_binary {
+                        name: "java-binary",
+			srcs: ["foo.java"],
+			main_class: "foo.bar.jb",
+                }
+	`)
+
+	expectedTestOnlyModules := []string{
+		"lib1-test-only",
+		"java-test",
+		"java-test-host",
+		"helper-library",
+		"java-data-binary",
+	}
+	expectedTopLevelTests := []string{
+		"java-test",
+		"java-test-host",
+	}
+	assertTestOnlyAndTopLevel(t, ctx, expectedTestOnlyModules, expectedTopLevelTests)
+}
+
+// Don't allow setting test-only on things that are always tests or never tests.
+func TestInvalidTestOnlyTargets(t *testing.T) {
+	testCases := []string{
+		` java_test {  name: "java-test", test_only: true, srcs: ["foo.java"],  } `,
+		` java_test_host {  name: "java-test-host", test_only: true, srcs: ["foo.java"],  } `,
+		` java_test_import {  name: "java-test-import", test_only: true, } `,
+		` java_api_library {  name: "java-api-library", test_only: true, } `,
+		` java_test_helper_library { name: "test-help-lib", test_only: true, } `,
+		` java_defaults { name: "java-defaults", test_only: true, } `,
+	}
+
+	for i, bp := range testCases {
+		android.GroupFixturePreparers(prepareForJavaTest).
+			ExtendWithErrorHandler(
+				expectOneError("unrecognized property \"test_only\"",
+					fmt.Sprintf("testcase: %d", i))).
+			RunTestWithBp(t, bp)
+	}
+}
+
+// Expect exactly one that matches 'expected'.
+// Append 'msg' to the Errorf that printed.
+func expectOneError(expected string, msg string) android.FixtureErrorHandler {
+	return android.FixtureCustomErrorHandler(func(t *testing.T, result *android.TestResult) {
+		t.Helper()
+		if len(result.Errs) != 1 {
+			t.Errorf("Expected exactly one error, but found: %d when  setting test_only on: %s", len(result.Errs), msg)
+			return
+		}
+		actualErrMsg := result.Errs[0].Error()
+		if !strings.Contains(actualErrMsg, expected) {
+			t.Errorf("Different error than expected.  Received: [%v] on %s expected: %s", actualErrMsg, msg, expected)
+		}
+	})
+}
+
 func TestJavaLibHostWithStem(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_library_host {
@@ -2792,3 +2966,79 @@
 		t.Errorf("Module output does not contain expected jar %s", "foo-new.jar")
 	}
 }
+
+func TestJavaLibraryOutputFilesRel(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+
+		java_import {
+			name: "bar",
+			jars: ["bar.aar"],
+
+		}
+
+		java_import {
+			name: "baz",
+			jars: ["baz.aar"],
+			static_libs: ["bar"],
+		}
+	`)
+
+	foo := result.ModuleForTests("foo", "android_common")
+	bar := result.ModuleForTests("bar", "android_common")
+	baz := result.ModuleForTests("baz", "android_common")
+
+	fooOutputPaths := foo.OutputFiles(t, "")
+	barOutputPaths := bar.OutputFiles(t, "")
+	bazOutputPaths := baz.OutputFiles(t, "")
+
+	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", fooOutputPaths[0].Rel())
+	android.AssertStringEquals(t, "bar relative output path",
+		"bar.jar", barOutputPaths[0].Rel())
+	android.AssertStringEquals(t, "baz relative output path",
+		"baz.jar", bazOutputPaths[0].Rel())
+}
+
+func assertTestOnlyAndTopLevel(t *testing.T, ctx *android.TestResult, expectedTestOnly []string, expectedTopLevel []string) {
+	t.Helper()
+	actualTrueModules := []string{}
+	actualTopLevelTests := []string{}
+	addActuals := func(m blueprint.Module, key blueprint.ProviderKey[android.TestModuleInformation]) {
+		if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, key); ok {
+			if provider.TestOnly {
+				actualTrueModules = append(actualTrueModules, m.Name())
+			}
+			if provider.TopLevelTarget {
+				actualTopLevelTests = append(actualTopLevelTests, m.Name())
+			}
+		}
+	}
+
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		addActuals(m, android.TestOnlyProviderKey)
+
+	})
+
+	notEqual, left, right := android.ListSetDifference(expectedTestOnly, actualTrueModules)
+	if notEqual {
+		t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
+	}
+
+	notEqual, left, right = android.ListSetDifference(expectedTopLevel, actualTopLevelTests)
+	if notEqual {
+		t.Errorf("top-level: Expected but not found: %v, Found but not expected: %v", left, right)
+	}
+}
diff --git a/java/jdeps.go b/java/jdeps.go
index 91f7ce7..3400263 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -48,7 +48,7 @@
 	moduleInfos := make(map[string]android.IdeInfo)
 
 	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled() {
+		if !module.Enabled(ctx) {
 			return
 		}
 
diff --git a/java/lint.go b/java/lint.go
index 31e7f35..2eea07d 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -319,25 +319,19 @@
 	cmd.FlagWithInput("@",
 		android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
 
-	if l.compileSdkKind == android.SdkPublic {
-		cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
-	} else {
-		// TODO(b/268261262): Remove this branch. We're demoting NewApi to a warning due to pre-existing issues that need to be fixed.
-		cmd.FlagForEachArg("--warning_check ", l.extraMainlineLintErrors)
-	}
+	cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
 	cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
 	cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
 	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
 	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
 
-	// TODO(b/193460475): Re-enable strict updatability linting
-	//if l.GetStrictUpdatabilityLinting() {
-	//	// Verify the module does not baseline issues that endanger safe updatability.
-	//	if baselinePath := l.getBaselineFilepath(ctx); baselinePath.Valid() {
-	//		cmd.FlagWithInput("--baseline ", baselinePath.Path())
-	//		cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
-	//	}
-	//}
+	if l.GetStrictUpdatabilityLinting() {
+		// Verify the module does not baseline issues that endanger safe updatability.
+		if l.properties.Lint.Baseline_filename != nil {
+			cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename))
+			cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
+		}
+	}
 
 	return lintPaths{
 		projectXML: projectXMLPath,
@@ -612,7 +606,7 @@
 		apiVersionsDb := findModuleOrErr(ctx, files.apiVersionsModule)
 		if apiVersionsDb == nil {
 			if !ctx.Config().AllowMissingDependencies() {
-				ctx.Errorf("lint: missing module api_versions_public")
+				ctx.Errorf("lint: missing module %s", files.apiVersionsModule)
 			}
 			return
 		}
@@ -620,7 +614,7 @@
 		sdkAnnotations := findModuleOrErr(ctx, files.annotationsModule)
 		if sdkAnnotations == nil {
 			if !ctx.Config().AllowMissingDependencies() {
-				ctx.Errorf("lint: missing module sdk-annotations.zip")
+				ctx.Errorf("lint: missing module %s", files.annotationsModule)
 			}
 			return
 		}
diff --git a/java/lint_test.go b/java/lint_test.go
index 751b139..b51753f 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -91,9 +91,8 @@
 		t.Error("did not use the correct file for baseline")
 	}
 
-	if !strings.Contains(*sboxProto.Commands[0].Command, "--warning_check NewApi") {
-		// TODO(b/268261262): Change this to check for --error_check
-		t.Error("should check NewApi warnings")
+	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check NewApi") {
+		t.Error("should check NewApi errors")
 	}
 
 	if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check SomeCheck") {
@@ -153,52 +152,55 @@
 	}
 }
 
-// TODO(b/193460475): Re-enable this test
-//func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
-//	bp := `
-//		java_library {
-//			name: "foo",
-//			srcs: [
-//				"a.java",
-//			],
-//			static_libs: ["bar"],
-//			min_sdk_version: "29",
-//			sdk_version: "current",
-//			lint: {
-//				strict_updatability_linting: true,
-//			},
-//		}
-//
-//		java_library {
-//			name: "bar",
-//			srcs: [
-//				"a.java",
-//			],
-//			min_sdk_version: "29",
-//			sdk_version: "current",
-//		}
-//	`
-//	fs := android.MockFS{
-//		"lint-baseline.xml": nil,
-//	}
-//
-//	result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
-//		RunTestWithBp(t, bp)
-//
-//	foo := result.ModuleForTests("foo", "android_common")
-//	sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto"))
-//	if !strings.Contains(*sboxProto.Commands[0].Command,
-//		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
-//		t.Error("did not restrict baselining NewApi")
-//	}
-//
-//	bar := result.ModuleForTests("bar", "android_common")
-//	sboxProto = android.RuleBuilderSboxProtoForTests(t, bar.Output("lint.sbox.textproto"))
-//	if !strings.Contains(*sboxProto.Commands[0].Command,
-//		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
-//		t.Error("did not restrict baselining NewApi")
-//	}
-//}
+func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
+	bp := `
+		java_library {
+			name: "foo",
+			srcs: [
+				"a.java",
+			],
+			static_libs: ["bar"],
+			min_sdk_version: "29",
+			sdk_version: "current",
+			lint: {
+				strict_updatability_linting: true,
+				baseline_filename: "lint-baseline.xml",
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: [
+				"a.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "current",
+			lint: {
+				baseline_filename: "lint-baseline.xml",
+			}
+		}
+	`
+	fs := android.MockFS{
+		"lint-baseline.xml": nil,
+	}
+
+	result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
+		RunTestWithBp(t, bp)
+
+	foo := result.ModuleForTests("foo", "android_common")
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, foo.Output("lint.sbox.textproto"))
+	if !strings.Contains(*sboxProto.Commands[0].Command,
+		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
+		t.Error("did not restrict baselining NewApi")
+	}
+
+	bar := result.ModuleForTests("bar", "android_common")
+	sboxProto = android.RuleBuilderSboxProtoForTests(t, result.TestContext, bar.Output("lint.sbox.textproto"))
+	if !strings.Contains(*sboxProto.Commands[0].Command,
+		"--baseline lint-baseline.xml --disallowed_issues NewApi") {
+		t.Error("did not restrict baselining NewApi")
+	}
+}
 
 func TestJavaLintDatabaseSelectionFull(t *testing.T) {
 	testCases := []struct {
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 4db426e..38553a6 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")
@@ -198,8 +175,8 @@
 	}
 	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 +190,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
@@ -221,6 +203,7 @@
 	// ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH
 	classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
 	b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
+	b.classpathFragmentBase().installClasspathProto(ctx)
 }
 
 func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
@@ -293,6 +276,15 @@
 
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
 func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule {
+	createEmptyHiddenApiFiles := func() {
+		paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
+		for _, path := range paths {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Touch,
+				Output: path,
+			})
+		}
+	}
 
 	// Save the paths to the monolithic files for retrieval via OutputFiles().
 	b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags
@@ -305,13 +297,7 @@
 	// optimization that can be used to reduce the incremental build time but as its name suggests it
 	// can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath.
 	if ctx.Config().DisableHiddenApiChecks() {
-		paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
-		for _, path := range paths {
-			ctx.Build(pctx, android.BuildParams{
-				Rule:   android.Touch,
-				Output: path,
-			})
-		}
+		createEmptyHiddenApiFiles()
 		return bootDexJarByModule
 	}
 
@@ -324,6 +310,13 @@
 	// the fragments will have already provided the flags that are needed.
 	classesJars := monolithicInfo.ClassesJars
 
+	if len(classesJars) == 0 {
+		// This product does not include any monolithic jars. Monolithic hiddenapi flag generation is not required.
+		// However, generate an empty file so that the dist tags in f/b/boot/Android.bp can be resolved, and `m dist` works.
+		createEmptyHiddenApiFiles()
+		return bootDexJarByModule
+	}
+
 	// Create the input to pass to buildRuleToGenerateHiddenAPIStubFlagsFile
 	input := newHiddenAPIFlagInput()
 
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 37ff639..0d2acae 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -353,7 +353,7 @@
 
 	// All the intermediate rules use the same inputs.
 	expectedIntermediateInputs := `
-		out/soong/.intermediates/bar/android_common/javac/bar.jar
+		out/soong/.intermediates/bar.impl/android_common/javac/bar.jar
 		out/soong/.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
 		out/soong/.intermediates/foo/android_common/javac/foo.jar
 	`
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 2fc6c02..67ed84e 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -15,10 +15,10 @@
 package java
 
 import (
-	"fmt"
 	"path/filepath"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -60,6 +60,8 @@
 	installDirPath android.InstallPath
 	configFile     android.OutputPath
 	metadataFile   android.OutputPath
+
+	installConfigFile android.InstallPath
 }
 
 func (p *platformCompatConfig) compatConfigMetadata() android.Path {
@@ -105,21 +107,15 @@
 		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)
 }
 
 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())
-			},
-		},
 	}}
 }
 
@@ -233,7 +229,7 @@
 	var compatConfigMetadata android.Paths
 
 	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled() {
+		if !module.Enabled(ctx) {
 			return
 		}
 		if c, ok := module.(platformCompatConfigMetadataProvider); ok {
@@ -265,7 +261,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)
 	}
 }
 
@@ -283,32 +279,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 94e9c6c..00613ee 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -158,6 +158,21 @@
 	mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
 }
 
+func createCombinedApiFilegroupModule(mctx android.LoadHookContext, name string, srcs []string) {
+	filegroupProps := struct {
+		Name *string
+		Srcs []string
+	}{}
+	filegroupProps.Name = proptools.StringPtr(name)
+
+	var transformedSrcs []string
+	for _, src := range srcs {
+		transformedSrcs = append(transformedSrcs, ":"+src)
+	}
+	filegroupProps.Srcs = transformedSrcs
+	mctx.CreateModule(android.FileGroupFactory, &filegroupProps)
+}
+
 func createLatestApiModuleExtensionVersionFile(mctx android.LoadHookContext, name string, version string) {
 	genruleProps := struct {
 		Name *string
@@ -252,6 +267,10 @@
 	return module + ".api." + scope + "." + version
 }
 
+func PrebuiltApiCombinedModuleName(module, scope, version string) string {
+	return module + ".api.combined." + scope + "." + version
+}
+
 func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
 	// <apiver>/<scope>/api/<module>.txt
 	apiLevelFiles := globApiDirs(mctx, p, "api/*.txt")
@@ -307,7 +326,9 @@
 	}
 
 	// Sort the keys in order to make build.ninja stable
-	for _, k := range android.SortedKeys(latest) {
+	sortedLatestKeys := android.SortedKeys(latest)
+
+	for _, k := range sortedLatestKeys {
 		info := latest[k]
 		name := PrebuiltApiModuleName(info.module, info.scope, "latest")
 		latestExtensionVersionModuleName := PrebuiltApiModuleName(info.module, info.scope, "latest.extension_version")
@@ -333,11 +354,38 @@
 		}
 	}
 	// Create empty incompatibilities files for remaining modules
-	for _, k := range android.SortedKeys(latest) {
+	// If the incompatibility module has been created, create a corresponding combined module
+	for _, k := range sortedLatestKeys {
 		if _, ok := incompatibilities[k]; !ok {
 			createEmptyFile(mctx, PrebuiltApiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest"))
 		}
 	}
+
+	// Create combined latest api and removed api files modules.
+	// The combined modules list all api files of the api scope and its subset api scopes.
+	for _, k := range sortedLatestKeys {
+		info := latest[k]
+		name := PrebuiltApiCombinedModuleName(info.module, info.scope, "latest")
+
+		// Iterate until the currentApiScope does not extend any other api scopes
+		// i.e. is not a superset of any other api scopes
+		// the relationship between the api scopes is defined in java/sdk_library.go
+		var srcs []string
+		currentApiScope := scopeByName[info.scope]
+		for currentApiScope != nil {
+			if _, ok := latest[fmt.Sprintf("%s.%s", info.module, currentApiScope.name)]; ok {
+				srcs = append(srcs, PrebuiltApiModuleName(info.module, currentApiScope.name, "latest"))
+			}
+			currentApiScope = currentApiScope.extends
+		}
+
+		// srcs is currently listed in the order from the widest api scope to the narrowest api scopes
+		// e.g. module lib -> system -> public
+		// In order to pass the files in metalava from the narrowest api scope to the widest api scope,
+		// the list has to be reversed.
+		android.ReverseSliceInPlace(srcs)
+		createCombinedApiFilegroupModule(mctx, name, srcs)
+	}
 }
 
 func createPrebuiltApiModules(mctx android.LoadHookContext) {
diff --git a/java/robolectric.go b/java/robolectric.go
index 9e8850c..4cad5b1 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -29,8 +29,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{
@@ -46,6 +50,7 @@
 var (
 	roboCoverageLibsTag = dependencyTag{name: "roboCoverageLibs"}
 	roboRuntimesTag     = dependencyTag{name: "roboRuntimes"}
+	roboRuntimeOnlyTag  = dependencyTag{name: "roboRuntimeOnlyTag"}
 )
 
 type robolectricProperties struct {
@@ -70,6 +75,11 @@
 	// Use /external/robolectric rather than /external/robolectric-shadows as the version of robolectric
 	// to use.  /external/robolectric closely tracks github's master, and will fully replace /external/robolectric-shadows
 	Upstream *bool
+
+	// Use strict mode to limit access of Robolectric API directly. See go/roboStrictMode
+	Strict_mode *bool
+
+	Jni_libs []string
 }
 
 type robolectricTest struct {
@@ -112,7 +122,7 @@
 
 	if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
 		ctx.AddVariationDependencies(nil, libTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v))
-	} else {
+	} else if !proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
 		if proptools.Bool(r.robolectricProperties.Upstream) {
 			ctx.AddVariationDependencies(nil, libTag, robolectricCurrentLib+"_upstream")
 		} else {
@@ -120,12 +130,23 @@
 		}
 	}
 
+	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, libTag, "robolectric_non_strict_mode_permission")
+	}
+
 	ctx.AddVariationDependencies(nil, libTag, 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) {
@@ -192,19 +213,25 @@
 		combinedJarJars = append(combinedJarJars, instrumentedApp.implementationAndResourcesJar)
 	}
 
-	handleLibDeps := func(dep android.Module) {
+	handleLibDeps := func(dep android.Module, runtimeOnly bool) {
 		m, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
-		r.libs = append(r.libs, ctx.OtherModuleName(dep))
+		if !runtimeOnly {
+			r.libs = append(r.libs, ctx.OtherModuleName(dep))
+		}
 		if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) {
 			combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars...)
 		}
 	}
 
 	for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
-		handleLibDeps(dep)
+		handleLibDeps(dep, false)
 	}
 	for _, dep := range ctx.GetDirectDepsWithTag(sdkLibTag) {
-		handleLibDeps(dep)
+		handleLibDeps(dep, false)
+	}
+	// handle the runtimeOnly tag for strict_mode
+	for _, dep := range ctx.GetDirectDepsWithTag(roboRuntimeOnlyTag) {
+		handleLibDeps(dep, true)
 	}
 
 	r.combinedJar = android.PathForModuleOut(ctx, "robolectric_combined", r.outputFile.Base())
@@ -253,6 +280,12 @@
 		installDeps = append(installDeps, installedData)
 	}
 
+	soInstallPath := installPath.Join(ctx, getLibPath(r.forceArchType))
+	for _, jniLib := range collectTransitiveJniDeps(ctx) {
+		installJni := ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
+		installDeps = append(installDeps, installJni)
+	}
+
 	r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...)
 	android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 }
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 3e0f8e9..0fc6e1c 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -122,6 +122,10 @@
 
 	ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...)
 	ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...)
+
+	for _, aconfig_declaration := range r.aaptProperties.Flags_packages {
+		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration)
+	}
 }
 
 func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -146,11 +150,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:               aconfigTextFilePaths,
 		},
 	)
 
@@ -171,6 +177,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/rro_test.go b/java/rro_test.go
index c4a4d04..742c839 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -405,3 +405,51 @@
 		android.AssertPathRelativeToTopEquals(t, "Install dir is not correct for "+testCase.name, testCase.expectedPath, mod.installDir)
 	}
 }
+
+func TestRuntimeResourceOverlayFlagsPackages(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+		runtime_resource_overlay {
+			name: "foo",
+			sdk_version: "current",
+			flags_packages: [
+				"bar",
+				"baz",
+			],
+		}
+		aconfig_declarations {
+			name: "bar",
+			package: "com.example.package.bar",
+			container: "com.android.foo",
+			srcs: [
+				"bar.aconfig",
+			],
+		}
+		aconfig_declarations {
+			name: "baz",
+			package: "com.example.package.baz",
+			container: "com.android.foo",
+			srcs: [
+				"baz.aconfig",
+			],
+		}
+	`)
+
+	foo := result.ModuleForTests("foo", "android_common")
+
+	// runtime_resource_overlay module depends on aconfig_declarations listed in flags_packages
+	android.AssertBoolEquals(t, "foo expected to depend on bar", true,
+		CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"))
+
+	android.AssertBoolEquals(t, "foo expected to depend on baz", true,
+		CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "baz"))
+
+	aapt2LinkRule := foo.Rule("android/soong/java.aapt2Link")
+	linkInFlags := aapt2LinkRule.Args["inFlags"]
+	android.AssertStringDoesContain(t,
+		"aapt2 link command expected to pass feature flags arguments",
+		linkInFlags,
+		"--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt",
+	)
+}
diff --git a/java/sdk.go b/java/sdk.go
index 3591ccd..4ef4ee2 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -31,7 +31,6 @@
 
 var sdkFrameworkAidlPathKey = android.NewOnceKey("sdkFrameworkAidlPathKey")
 var nonUpdatableFrameworkAidlPathKey = android.NewOnceKey("nonUpdatableFrameworkAidlPathKey")
-var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
 
 func UseApiFingerprint(ctx android.BaseModuleContext) (useApiFingerprint bool, fingerprintSdkVersion string, fingerprintDeps android.OutputPath) {
 	if ctx.Config().UnbundledBuild() && !ctx.Config().AlwaysUsePrebuiltSdks() {
@@ -45,8 +44,8 @@
 
 		useApiFingerprint = apiFingerprintTrue || dessertShaIsSet
 		if apiFingerprintTrue {
-			fingerprintSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
-			fingerprintDeps = ApiFingerprintPath(ctx)
+			fingerprintSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", android.ApiFingerprintPath(ctx).String())
+			fingerprintDeps = android.ApiFingerprintPath(ctx)
 		}
 		if dessertShaIsSet {
 			fingerprintSdkVersion = ctx.Config().Getenv("UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA")
@@ -66,6 +65,12 @@
 		return JAVA_VERSION_9
 	} else if sdk.FinalOrFutureInt() <= 33 {
 		return JAVA_VERSION_11
+	} else if ctx.Config().TargetsJava21() {
+		// Temporary experimental flag to be able to try and build with
+		// java version 21 options.  The flag, if used, just sets Java
+		// 21 as the default version, leaving any components that
+		// target an older version intact.
+		return JAVA_VERSION_21
 	} else {
 		return JAVA_VERSION_17
 	}
@@ -337,7 +342,7 @@
 
 // Create api_fingerprint.txt
 func createAPIFingerprint(ctx android.SingletonContext) {
-	out := ApiFingerprintPath(ctx)
+	out := android.ApiFingerprintPath(ctx)
 
 	rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -378,17 +383,11 @@
 	rule.Build("api_fingerprint", "generate api_fingerprint.txt")
 }
 
-func ApiFingerprintPath(ctx android.PathContext) android.OutputPath {
-	return ctx.Config().Once(apiFingerprintPathKey, func() interface{} {
-		return android.PathForOutput(ctx, "api_fingerprint.txt")
-	}).(android.OutputPath)
-}
-
 func sdkMakeVars(ctx android.MakeVarsContext) {
 	if ctx.Config().AlwaysUsePrebuiltSdks() {
 		return
 	}
 
 	ctx.Strict("FRAMEWORK_AIDL", sdkFrameworkAidlPath(ctx).String())
-	ctx.Strict("API_FINGERPRINT", ApiFingerprintPath(ctx).String())
+	ctx.Strict("API_FINGERPRINT", android.ApiFingerprintPath(ctx).String())
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index cdd0448..1eb7ab8 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -30,6 +30,7 @@
 
 	"android/soong/android"
 	"android/soong/dexpreopt"
+	"android/soong/etc"
 )
 
 const (
@@ -117,9 +118,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
 
@@ -194,11 +192,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,
@@ -323,6 +316,16 @@
 	return ret
 }
 
+func (scopes apiScopes) ConvertStubsLibraryExportableToEverything(name string) string {
+	for _, scope := range scopes {
+		if strings.HasSuffix(name, scope.exportableStubsLibraryModuleNameSuffix()) {
+			return strings.TrimSuffix(name, scope.exportableStubsLibraryModuleNameSuffix()) +
+				scope.stubsLibraryModuleNameSuffix()
+		}
+	}
+	return name
+}
+
 var (
 	scopeByName    = make(map[string]*apiScope)
 	allScopeNames  []string
@@ -417,7 +420,7 @@
 		},
 		kind: android.SdkSystemServer,
 	})
-	allApiScopes = apiScopes{
+	AllApiScopes = apiScopes{
 		apiScopePublic,
 		apiScopeSystem,
 		apiScopeTest,
@@ -535,9 +538,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
@@ -704,10 +704,10 @@
 	annotationsZip android.OptionalPath
 
 	// The path to the latest API file.
-	latestApiPath android.OptionalPath
+	latestApiPaths android.Paths
 
 	// The path to the latest removed API file.
-	latestRemovedApiPath android.OptionalPath
+	latestRemovedApiPaths android.Paths
 }
 
 func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error {
@@ -793,12 +793,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 {
@@ -808,48 +802,46 @@
 }
 
 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)
 	})
 }
 
-func extractSingleOptionalOutputPath(dep android.Module) (android.OptionalPath, error) {
+func extractOutputPaths(dep android.Module) (android.Paths, error) {
 	var paths android.Paths
 	if sourceFileProducer, ok := dep.(android.SourceFileProducer); ok {
 		paths = sourceFileProducer.Srcs()
+		return paths, nil
 	} else {
-		return android.OptionalPath{}, fmt.Errorf("module %q does not produce source files", dep)
+		return nil, fmt.Errorf("module %q does not produce source files", dep)
 	}
-	if len(paths) != 1 {
-		return android.OptionalPath{}, fmt.Errorf("expected one path from %q, got %q", dep, paths)
-	}
-	return android.OptionalPathForPath(paths[0]), nil
 }
 
 func (paths *scopePaths) extractLatestApiPath(ctx android.ModuleContext, dep android.Module) error {
-	outputPath, err := extractSingleOptionalOutputPath(dep)
-	paths.latestApiPath = outputPath
+	outputPaths, err := extractOutputPaths(dep)
+	paths.latestApiPaths = outputPaths
 	return err
 }
 
 func (paths *scopePaths) extractLatestRemovedApiPath(ctx android.ModuleContext, dep android.Module) error {
-	outputPath, err := extractSingleOptionalOutputPath(dep)
-	paths.latestRemovedApiPath = outputPath
+	outputPaths, err := extractOutputPaths(dep)
+	paths.latestRemovedApiPaths = outputPaths
 	return err
 }
 
@@ -948,6 +940,14 @@
 
 	// Functionality related to this being used as a component of a java_sdk_library.
 	EmbeddableSdkLibraryComponent
+
+	// Path to the header jars of the implementation library
+	// This is non-empty only when api_only is false.
+	implLibraryHeaderJars android.Paths
+
+	// The reference to the implementation library created by the source module.
+	// Is nil if the source module does not exist.
+	implLibraryModule *Library
 }
 
 func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImportModule) {
@@ -994,6 +994,10 @@
 	c.doctagPaths = android.PathsForModuleSrc(ctx, c.commonSdkLibraryProperties.Doctag_files)
 }
 
+func (c *commonToSdkLibraryAndImport) getImplLibraryModule() *Library {
+	return c.implLibraryModule
+}
+
 // Module name of the runtime implementation library
 func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
 	return c.module.RootLibraryName() + ".impl"
@@ -1079,57 +1083,26 @@
 	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)
+func (module *commonToSdkLibraryAndImport) setOutputFiles(ctx android.ModuleContext) {
+	if module.doctagPaths != nil {
+		ctx.SetOutputFiles(module.doctagPaths, ".doctags")
+	}
+	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
 	}
 }
 
@@ -1194,7 +1167,7 @@
 	paths := c.findClosestScopePath(apiScope)
 	if paths == nil {
 		var scopes []string
-		for _, s := range allApiScopes {
+		for _, s := range AllApiScopes {
 			if c.findScopePaths(s) != nil {
 				scopes = append(scopes, s.name)
 			}
@@ -1355,13 +1328,6 @@
 	// class changes but it does not contain and implementation or JavaDoc.
 	SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths
 
-	// Get the implementation jars appropriate for the supplied sdk version.
-	//
-	// These are either the implementation jar for the whole sdk library or the implementation
-	// jars for the stubs. The latter should only be needed when generating JavaDoc as otherwise
-	// they are identical to the corresponding header jars.
-	SdkImplementationJars(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.
@@ -1377,6 +1343,8 @@
 
 	// sharedLibrary returns true if this can be used as a shared library.
 	sharedLibrary() bool
+
+	getImplLibraryModule() *Library
 }
 
 type SdkLibrary struct {
@@ -1388,6 +1356,8 @@
 	scopeToProperties map[*apiScope]*ApiScopeProperties
 
 	commonToSdkLibraryAndImport
+
+	builtInstalledForApex []dexpreopterInstall
 }
 
 var _ SdkLibraryDependency = (*SdkLibrary)(nil)
@@ -1396,11 +1366,25 @@
 	return module.sdkLibraryProperties.Generate_system_and_test_apis
 }
 
+func (module *SdkLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
+	if module.implLibraryModule != nil {
+		return module.implLibraryModule.DexJarBuildPath(ctx)
+	}
+	return makeUnsetDexJarPath()
+}
+
+func (module *SdkLibrary) DexJarInstallPath() android.Path {
+	if module.implLibraryModule != nil {
+		return module.implLibraryModule.DexJarInstallPath()
+	}
+	return nil
+}
+
 func (module *SdkLibrary) getGeneratedApiScopes(ctx android.EarlyModuleContext) apiScopes {
 	// Check to see if any scopes have been explicitly enabled. If any have then all
 	// must be.
 	anyScopesExplicitlyEnabled := false
-	for _, scope := range allApiScopes {
+	for _, scope := range AllApiScopes {
 		scopeProperties := module.scopeToProperties[scope]
 		if scopeProperties.Enabled != nil {
 			anyScopesExplicitlyEnabled = true
@@ -1410,7 +1394,7 @@
 
 	var generatedScopes apiScopes
 	enabledScopes := make(map[*apiScope]struct{})
-	for _, scope := range allApiScopes {
+	for _, scope := range AllApiScopes {
 		scopeProperties := module.scopeToProperties[scope]
 		// If any scopes are explicitly enabled then ignore the legacy enabled status.
 		// This is to ensure that any new usages of this module type do not rely on legacy
@@ -1430,7 +1414,7 @@
 
 	// Now check to make sure that any scope that is extended by an enabled scope is also
 	// enabled.
-	for _, scope := range allApiScopes {
+	for _, scope := range AllApiScopes {
 		if _, ok := enabledScopes[scope]; ok {
 			extends := scope.extends
 			if extends != nil {
@@ -1447,6 +1431,10 @@
 var _ android.ModuleWithMinSdkVersionCheck = (*SdkLibrary)(nil)
 
 func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) {
+	CheckMinSdkVersion(ctx, &module.Library)
+}
+
+func CheckMinSdkVersion(ctx android.ModuleContext, module *Library) {
 	android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.ModuleContext, do android.PayloadDepsCallback) {
 		ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
 			isExternal := !module.depIsInSameApex(ctx, child)
@@ -1479,6 +1467,18 @@
 
 var implLibraryTag = sdkLibraryComponentTag{name: "impl-library"}
 
+var _ android.InstallNeededDependencyTag = sdkLibraryComponentTag{}
+
+// To satisfy the CopyDirectlyInAnyApexTag interface. Implementation library of the sdk library
+// in an apex is considered to be directly in the apex, as if it was listed in java_libs.
+func (t sdkLibraryComponentTag) CopyDirectlyInAnyApex() {}
+
+var _ android.CopyDirectlyInAnyApexTag = implLibraryTag
+
+func (t sdkLibraryComponentTag) InstallDepNeeded() bool {
+	return t.name == "xml-permissions-file" || t.name == "impl-library"
+}
+
 // Add the dependencies on the child modules in the component deps mutator.
 func (module *SdkLibrary) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
 	for _, apiScope := range module.getGeneratedApiScopes(ctx) {
@@ -1543,39 +1543,21 @@
 		m += "Please see the documentation of the prebuilt_apis module type (and a usage example in prebuilts/sdk) for a convenient way to generate these."
 		ctx.ModuleErrorf(m)
 	}
-	if module.requiresRuntimeImplementationLibrary() {
-		// Only add the deps for the library if it is actually going to be built.
-		module.Library.deps(ctx)
-	}
-}
-
-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.Library.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 proptools.String(module.deviceProperties.Min_sdk_version) != "" {
-		module.CheckMinSdkVersion(ctx)
+	if disableSourceApexVariant(ctx) {
+		// Prebuilts are active, do not create the installation rules for the source javalib.
+		// Even though the source javalib is not used, we need to hide it to prevent duplicate installation rules.
+		// TODO (b/331665856): Implement a principled solution for this.
+		module.HideFromMake()
 	}
 
 	module.generateCommonBuildActions(ctx)
 
-	// Only build an implementation library if required.
-	if module.requiresRuntimeImplementationLibrary() {
-		// stubsLinkType must be set before calling Library.GenerateAndroidBuildActions
-		module.Library.stubsLinkType = Unknown
-		module.Library.GenerateAndroidBuildActions(ctx)
-	}
+	module.stem = proptools.StringDefault(module.overridableProperties.Stem, ctx.ModuleName())
+
+	module.provideHiddenAPIPropertyInfo(ctx)
 
 	// Collate the components exported by this module. All scope specific modules are exported but
 	// the impl and xml component modules are not.
@@ -1598,8 +1580,53 @@
 
 			exportedComponents[ctx.OtherModuleName(to)] = struct{}{}
 		}
+
+		if tag == implLibraryTag {
+			if dep, ok := android.OtherModuleProvider(ctx, to, JavaInfoProvider); ok {
+				module.implLibraryHeaderJars = append(module.implLibraryHeaderJars, dep.HeaderJars...)
+				module.implLibraryModule = to.(*Library)
+				android.SetProvider(ctx, JavaInfoProvider, dep)
+			}
+		}
 	})
 
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	if !apexInfo.IsForPlatform() {
+		module.hideApexVariantFromMake = true
+	}
+
+	if module.implLibraryModule != nil {
+		if ctx.Device() {
+			module.classesJarPaths = android.Paths{module.implLibraryModule.implementationJarFile}
+			module.bootDexJarPath = module.implLibraryModule.bootDexJarPath
+			module.uncompressDexState = module.implLibraryModule.uncompressDexState
+			module.active = module.implLibraryModule.active
+		}
+
+		module.outputFile = module.implLibraryModule.outputFile
+		module.dexJarFile = makeDexJarPathFromPath(module.implLibraryModule.dexJarFile.Path())
+		module.headerJarFile = module.implLibraryModule.headerJarFile
+		module.implementationAndResourcesJar = module.implLibraryModule.implementationAndResourcesJar
+		module.builtInstalledForApex = module.implLibraryModule.builtInstalledForApex
+		module.dexpreopter.configPath = module.implLibraryModule.dexpreopter.configPath
+		module.dexpreopter.outputProfilePathOnHost = module.implLibraryModule.dexpreopter.outputProfilePathOnHost
+
+		// Properties required for Library.AndroidMkEntries
+		module.logtagsSrcs = module.implLibraryModule.logtagsSrcs
+		module.dexpreopter.builtInstalled = module.implLibraryModule.dexpreopter.builtInstalled
+		module.jacocoReportClassesFile = module.implLibraryModule.jacocoReportClassesFile
+		module.dexer.proguardDictionary = module.implLibraryModule.dexer.proguardDictionary
+		module.dexer.proguardUsageZip = module.implLibraryModule.dexer.proguardUsageZip
+		module.linter.reports = module.implLibraryModule.linter.reports
+		module.linter.outputs.depSets = module.implLibraryModule.LintDepSets()
+
+		if !module.Host() {
+			module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile
+		}
+
+		android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: module.implLibraryModule.uniqueSrcFiles.Strings()})
+	}
+
 	// Make the set of components exported by this module available for use elsewhere.
 	exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedKeys(exportedComponents)}
 	android.SetProvider(ctx, android.ExportedComponentsInfoProvider, exportedComponentInfo)
@@ -1615,14 +1642,26 @@
 		scopes[scope.name] = scopeInfo
 		scopeInfo["current_api"] = scope.snapshotRelativeCurrentApiTxtPath(baseModuleName)
 		scopeInfo["removed_api"] = scope.snapshotRelativeRemovedApiTxtPath(baseModuleName)
-		if p := scopePaths.latestApiPath; p.Valid() {
-			scopeInfo["latest_api"] = p.Path().String()
+		if p := scopePaths.latestApiPaths; len(p) > 0 {
+			// The last path in the list is the one that applies to this scope, the
+			// preceding ones, if any, are for the scope(s) that it extends.
+			scopeInfo["latest_api"] = p[len(p)-1].String()
 		}
-		if p := scopePaths.latestRemovedApiPath; p.Valid() {
-			scopeInfo["latest_removed_api"] = p.Path().String()
+		if p := scopePaths.latestRemovedApiPaths; len(p) > 0 {
+			// The last path in the list is the one that applies to this scope, the
+			// preceding ones, if any, are for the scope(s) that it extends.
+			scopeInfo["latest_removed_api"] = p[len(p)-1].String()
 		}
 	}
 	android.SetProvider(ctx, android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo})
+	module.setOutputFiles(ctx)
+	if module.requiresRuntimeImplementationLibrary() && module.implLibraryModule != nil {
+		setOutputFiles(ctx, module.implLibraryModule.Module)
+	}
+}
+
+func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall {
+	return module.builtInstalledForApex
 }
 
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
@@ -1630,8 +1669,9 @@
 		return nil
 	}
 	entriesList := module.Library.AndroidMkEntries()
+	entries := &entriesList[0]
+	entries.Required = append(entries.Required, module.implLibraryModuleName())
 	if module.sharedLibrary() {
-		entries := &entriesList[0]
 		entries.Required = append(entries.Required, module.xmlPermissionsModuleName())
 	}
 	return entriesList
@@ -1672,12 +1712,16 @@
 	return PrebuiltApiModuleName(name, apiScope.name, "latest")
 }
 
+func latestPrebuiltApiCombinedModuleName(name string, apiScope *apiScope) string {
+	return PrebuiltApiCombinedModuleName(name, apiScope.name, "latest")
+}
+
 func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string {
 	return ":" + module.latestApiModuleName(apiScope)
 }
 
 func (module *SdkLibrary) latestApiModuleName(apiScope *apiScope) string {
-	return latestPrebuiltApiModuleName(module.distStem(), apiScope)
+	return latestPrebuiltApiCombinedModuleName(module.distStem(), apiScope)
 }
 
 func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) string {
@@ -1685,7 +1729,7 @@
 }
 
 func (module *SdkLibrary) latestRemovedApiModuleName(apiScope *apiScope) string {
-	return latestPrebuiltApiModuleName(module.distStem()+"-removed", apiScope)
+	return latestPrebuiltApiCombinedModuleName(module.distStem()+"-removed", apiScope)
 }
 
 func (module *SdkLibrary) latestIncompatibilitiesFilegroupName(apiScope *apiScope) string {
@@ -1744,24 +1788,22 @@
 	props := struct {
 		Name           *string
 		Visibility     []string
-		Instrument     bool
 		Libs           []string
 		Static_libs    []string
 		Apex_available []string
+		Stem           *string
 	}{
 		Name:       proptools.StringPtr(module.implLibraryModuleName()),
 		Visibility: visibility,
-		// Set the instrument property to ensure it is instrumented when instrumentation is required.
-		Instrument: true,
-		// Set the impl_only libs. Note that the module's "Libs" get appended as well, via the
-		// addition of &module.properties below.
-		Libs: module.sdkLibraryProperties.Impl_only_libs,
-		// Set the impl_only static libs. Note that the module's "static_libs" get appended as well, via the
-		// addition of &module.properties below.
-		Static_libs: module.sdkLibraryProperties.Impl_only_static_libs,
+
+		Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...),
+
+		Static_libs: append(module.properties.Static_libs, module.sdkLibraryProperties.Impl_only_static_libs...),
 		// Pass the apex_available settings down so that the impl library can be statically
 		// embedded within a library that is added to an APEX. Needed for updatable-media.
 		Apex_available: module.ApexAvailable(),
+
+		Stem: proptools.StringPtr(module.Name()),
 	}
 
 	properties := []interface{}{
@@ -1771,6 +1813,7 @@
 		&module.dexProperties,
 		&module.dexpreoptProperties,
 		&module.linter.properties,
+		&module.overridableProperties,
 		&props,
 		module.sdkComponentPropertiesForChildLibrary(),
 	}
@@ -1920,10 +1963,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) {
@@ -1993,20 +2032,25 @@
 	if !Bool(module.sdkLibraryProperties.No_dist) {
 		// Dist the api txt and removed api txt artifacts for sdk builds.
 		distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
+		stubsTypeTagPrefix := ""
+		if mctx.Config().ReleaseHiddenApiExportableStubs() {
+			stubsTypeTagPrefix = ".exportable"
+		}
 		for _, p := range []struct {
 			tag     string
 			pattern string
 		}{
 			// "exportable" api files are copied to the dist directory instead of the
-			// "everything" api files.
-			{tag: ".exportable.api.txt", pattern: "%s.txt"},
-			{tag: ".exportable.removed-api.txt", pattern: "%s-removed.txt"},
+			// "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag
+			// is set. Otherwise, the "everything" api files are copied to the dist directory.
+			{tag: "%s.api.txt", pattern: "%s.txt"},
+			{tag: "%s.removed-api.txt", pattern: "%s-removed.txt"},
 		} {
 			props.Dists = append(props.Dists, android.Dist{
 				Targets: []string{"sdk", "win_sdk"},
 				Dir:     distDir,
 				Dest:    proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())),
-				Tag:     proptools.StringPtr(p.tag),
+				Tag:     proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)),
 			})
 		}
 	}
@@ -2079,7 +2123,7 @@
 	mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
 }
 
-func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties {
+func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties {
 	props := libraryProperties{}
 
 	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
@@ -2095,13 +2139,22 @@
 	}
 	props.Compile_dex = compileDex
 
+	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")
+	}
+
 	return props
 }
 
 func (module *SdkLibrary) createTopLevelStubsLibrary(
 	mctx android.DefaultableHookContext, apiScope *apiScope, contributesToApiSurface bool) {
 
-	props := module.topLevelStubsLibraryProps(mctx, apiScope)
+	// Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false
+	doDist := !mctx.Config().ReleaseHiddenApiExportableStubs()
+	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
 	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
 
 	// Add the stub compiling java_library/java_api_library as static lib based on build config
@@ -2117,18 +2170,11 @@
 func (module *SdkLibrary) createTopLevelExportableStubsLibrary(
 	mctx android.DefaultableHookContext, apiScope *apiScope) {
 
-	props := module.topLevelStubsLibraryProps(mctx, apiScope)
+	// Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true
+	doDist := mctx.Config().ReleaseHiddenApiExportableStubs()
+	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
 	props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope))
 
-	// Dist the class jar artifact for sdk builds.
-	// "exportable" stubs are copied to dist for sdk builds instead of the "everything" stubs.
-	if !Bool(module.sdkLibraryProperties.No_dist) {
-		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")
-	}
-
 	staticLib := module.exportableSourceStubsLibraryModuleName(apiScope)
 	props.Static_libs = append(props.Static_libs, staticLib)
 
@@ -2145,6 +2191,9 @@
 	if depTag == xmlPermissionsFileTag {
 		return true
 	}
+	if dep.Name() == module.implLibraryModuleName() {
+		return true
+	}
 	return module.Library.DepIsInSameApex(mctx, dep)
 }
 
@@ -2226,7 +2275,7 @@
 	return len(otherApexInfo.InApexVariants) > 0 && reflect.DeepEqual(apexInfo.InApexVariants, otherApexInfo.InApexVariants)
 }
 
-func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths {
+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
@@ -2243,11 +2292,7 @@
 		// * 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) {
-			if headerJars {
-				return module.HeaderJars()
-			} else {
-				return module.ImplementationJars()
-			}
+			return module.implLibraryHeaderJars
 		}
 	}
 
@@ -2256,12 +2301,7 @@
 
 // to satisfy SdkLibraryDependency interface
 func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-	return module.sdkJars(ctx, sdkVersion, true /*headerJars*/)
-}
-
-// to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-	return module.sdkJars(ctx, sdkVersion, false /*headerJars*/)
+	return module.sdkJars(ctx, sdkVersion)
 }
 
 var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries")
@@ -2281,7 +2321,7 @@
 // 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() {
+	if !module.Enabled(mctx) {
 		return
 	}
 
@@ -2490,7 +2530,7 @@
 
 	// Initialize the map from scope to scope specific properties.
 	scopeToProperties := make(map[*apiScope]*ApiScopeProperties)
-	for _, scope := range allApiScopes {
+	for _, scope := range AllApiScopes {
 		scopeToProperties[scope] = scope.scopeSpecificProperties(module)
 	}
 	module.scopeToProperties = scopeToProperties
@@ -2579,10 +2619,6 @@
 
 	commonToSdkLibraryAndImport
 
-	// The reference to the implementation library created by the source module.
-	// Is nil if the source module does not exist.
-	implLibraryModule *Library
-
 	// The reference to the xml permissions module created by the source module.
 	// Is nil if the source module does not exist.
 	xmlPermissionsFileModule *sdkLibraryXml
@@ -2611,7 +2647,7 @@
 // Dynamically create a structure type for each apiscope in allApiScopes.
 func createAllScopePropertiesStructType() reflect.Type {
 	var fields []reflect.StructField
-	for _, apiScope := range allApiScopes {
+	for _, apiScope := range AllApiScopes {
 		field := reflect.StructField{
 			Name: apiScope.fieldName,
 			Type: reflect.TypeOf(sdkLibraryScopeProperties{}),
@@ -2629,7 +2665,7 @@
 	allScopePropertiesStruct := allScopePropertiesPtr.Elem()
 	scopeProperties := make(map[*apiScope]*sdkLibraryScopeProperties)
 
-	for _, apiScope := range allApiScopes {
+	for _, apiScope := range AllApiScopes {
 		field := allScopePropertiesStruct.FieldByName(apiScope.fieldName)
 		scopeProperties[apiScope] = field.Addr().Interface().(*sdkLibraryScopeProperties)
 	}
@@ -2853,18 +2889,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)
 
@@ -2947,6 +2971,11 @@
 			}
 		}
 	}
+
+	module.setOutputFiles(ctx)
+	if module.implLibraryModule != nil {
+		setOutputFiles(ctx, module.implLibraryModule.Module)
+	}
 }
 
 func (module *SdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths {
@@ -2971,12 +3000,6 @@
 	return module.sdkJars(ctx, sdkVersion, true)
 }
 
-// to satisfy SdkLibraryDependency interface
-func (module *SdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths {
-	// This module is just a wrapper for the stubs.
-	return module.sdkJars(ctx, sdkVersion, false)
-}
-
 // to satisfy UsesLibraryDependency interface
 func (module *SdkLibraryImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
 	// The dex implementation jar extracted from the .apex file should be used in preference to the
@@ -3162,10 +3185,7 @@
 	return "permissions"
 }
 
-// from android.PrebuiltEtcModule
-func (module *sdkLibraryXml) OutputFile() android.OutputPath {
-	return module.outputFilePath
-}
+var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil)
 
 // from android.ApexModule
 func (module *sdkLibraryXml) AvailableFor(what string) bool {
@@ -3192,7 +3212,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() {
@@ -3236,14 +3256,14 @@
 	if value == nil {
 		return ""
 	}
-	return fmt.Sprintf(`        %s=\"%s\"\n`, attrName, *value)
+	return fmt.Sprintf("        %s=\"%s\"\n", attrName, *value)
 }
 
 func formattedDependenciesAttribute(dependencies []string) string {
 	if dependencies == nil {
 		return ""
 	}
-	return fmt.Sprintf(`        dependency=\"%s\"\n`, strings.Join(dependencies, ":"))
+	return fmt.Sprintf("        dependency=\"%s\"\n", strings.Join(dependencies, ":"))
 }
 
 func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string {
@@ -3260,28 +3280,28 @@
 	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T
 	var libraryTag string
 	if module.properties.Min_device_sdk != nil {
-		libraryTag = `    <apex-library\n`
+		libraryTag = "    <apex-library\n"
 	} else {
-		libraryTag = `    <library\n`
+		libraryTag = "    <library\n"
 	}
 
 	return strings.Join([]string{
-		`<?xml version=\"1.0\" encoding=\"utf-8\"?>\n`,
-		`<!-- Copyright (C) 2018 The Android Open Source Project\n`,
-		`\n`,
-		`    Licensed under the Apache License, Version 2.0 (the \"License\");\n`,
-		`    you may not use this file except in compliance with the License.\n`,
-		`    You may obtain a copy of the License at\n`,
-		`\n`,
-		`        http://www.apache.org/licenses/LICENSE-2.0\n`,
-		`\n`,
-		`    Unless required by applicable law or agreed to in writing, software\n`,
-		`    distributed under the License is distributed on an \"AS IS\" BASIS,\n`,
-		`    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n`,
-		`    See the License for the specific language governing permissions and\n`,
-		`    limitations under the License.\n`,
-		`-->\n`,
-		`<permissions>\n`,
+		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
+		"<!-- Copyright (C) 2018 The Android Open Source Project\n",
+		"\n",
+		"    Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+		"    you may not use this file except in compliance with the License.\n",
+		"    You may obtain a copy of the License at\n",
+		"\n",
+		"        http://www.apache.org/licenses/LICENSE-2.0\n",
+		"\n",
+		"    Unless required by applicable law or agreed to in writing, software\n",
+		"    distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+		"    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+		"    See the License for the specific language governing permissions and\n",
+		"    limitations under the License.\n",
+		"-->\n",
+		"<permissions>\n",
 		libraryTag,
 		libNameAttr,
 		filePathAttr,
@@ -3290,8 +3310,9 @@
 		minSdkAttr,
 		maxSdkAttr,
 		dependenciesAttr,
-		`    />\n`,
-		`</permissions>\n`}, "")
+		"    />\n",
+		"</permissions>\n",
+	}, "")
 }
 
 func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -3303,14 +3324,12 @@
 	xmlContent := module.permissionsContents(ctx)
 
 	module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
-		Text("/bin/bash -c \"echo -e '" + xmlContent + "'\" > ").
-		Output(module.outputFilePath)
-
-	rule.Build("java_sdk_xml", "Permission XML")
+	android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent)
 
 	module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
+	ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
+
+	ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "")
 }
 
 func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
@@ -3518,7 +3537,7 @@
 	s.Stem = sdk.distStem()
 
 	s.Scopes = make(map[*apiScope]*scopeProperties)
-	for _, apiScope := range allApiScopes {
+	for _, apiScope := range AllApiScopes {
 		paths := sdk.findScopePaths(apiScope)
 		if paths == nil {
 			continue
@@ -3554,7 +3573,8 @@
 	s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk
 	s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk
 
-	if sdk.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
+	implLibrary := sdk.getImplLibraryModule()
+	if implLibrary != nil && implLibrary.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
 		s.DexPreoptProfileGuided = proptools.BoolPtr(true)
 	}
 }
@@ -3579,7 +3599,7 @@
 
 	stem := s.Stem
 
-	for _, apiScope := range allApiScopes {
+	for _, apiScope := range AllApiScopes {
 		if properties, ok := s.Scopes[apiScope]; ok {
 			scopeSet := propertySet.AddPropertySet(apiScope.propertyName)
 
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 93ef408..a8a1494 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -139,10 +139,10 @@
 
 	exportedComponentsInfo, _ := android.SingletonModuleProvider(result, foo.Module(), android.ExportedComponentsInfoProvider)
 	expectedFooExportedComponents := []string{
-		"foo-removed.api.public.latest",
-		"foo-removed.api.system.latest",
-		"foo.api.public.latest",
-		"foo.api.system.latest",
+		"foo-removed.api.combined.public.latest",
+		"foo-removed.api.combined.system.latest",
+		"foo.api.combined.public.latest",
+		"foo.api.combined.system.latest",
 		"foo.stubs",
 		"foo.stubs.exportable",
 		"foo.stubs.exportable.system",
@@ -186,13 +186,13 @@
 	// test if quuz have created the api_contribution module
 	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("quuz")+".api.contribution", "")
 
-	fooDexJar := result.ModuleForTests("foo", "android_common").Rule("d8")
-	// tests if kotlinc generated files are NOT excluded from output of foo.
-	android.AssertStringDoesNotContain(t, "foo dex", fooDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
+	fooImplDexJar := result.ModuleForTests("foo.impl", "android_common").Rule("d8")
+	// tests if kotlinc generated files are NOT excluded from output of foo.impl.
+	android.AssertStringDoesNotContain(t, "foo.impl dex", fooImplDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
 
-	barDexJar := result.ModuleForTests("bar", "android_common").Rule("d8")
-	// tests if kotlinc generated files are excluded from output of bar.
-	android.AssertStringDoesContain(t, "bar dex", barDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
+	barImplDexJar := result.ModuleForTests("bar.impl", "android_common").Rule("d8")
+	// tests if kotlinc generated files are excluded from output of bar.impl.
+	android.AssertStringDoesContain(t, "bar.impl dex", barImplDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) {
@@ -227,19 +227,21 @@
 `)
 
 	// test that updatability attributes are passed on correctly
-	fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Rule("java_sdk_xml")
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"U\"`)
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"V\"`)
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"W\"`)
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"X\"`)
+	fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Output("fooUpdatable.xml")
+	fooUpdatableContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooUpdatable)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `on-bootclasspath-since="U"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `on-bootclasspath-before="V"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `min-device-sdk="W"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `max-device-sdk="X"`)
 
 	// double check that updatability attributes are not written if they don't exist in the bp file
 	// the permissions file for the foo library defined above
-	fooPermissions := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml")
-	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-since`)
-	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-before`)
-	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `min-device-sdk`)
-	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `max-device-sdk`)
+	fooPermissions := result.ModuleForTests("foo.xml", "android_common").Output("foo.xml")
+	fooPermissionsContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooPermissions)
+	android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `on-bootclasspath-since`)
+	android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `on-bootclasspath-before`)
+	android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `min-device-sdk`)
+	android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `max-device-sdk`)
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_Validation_ValidVersion(t *testing.T) {
@@ -370,9 +372,10 @@
 		}
 `)
 	// test that updatability attributes are passed on correctly
-	fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml")
-	android.AssertStringDoesContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<apex-library`)
-	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<library`)
+	fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Output("foo.xml")
+	fooUpdatableContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooUpdatable)
+	android.AssertStringDoesContain(t, "foo.xml contents", fooUpdatableContents, `<apex-library`)
+	android.AssertStringDoesNotContain(t, "foo.xml contents", fooUpdatableContents, `<library`)
 }
 
 func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
@@ -489,7 +492,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": path dependency ":foo{.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",
@@ -514,7 +517,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",
@@ -556,8 +559,8 @@
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
 		`dex2oatd`,
-		`sdklib-removed.api.public.latest`,
-		`sdklib.api.public.latest`,
+		`sdklib-removed.api.combined.public.latest`,
+		`sdklib.api.combined.public.latest`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
 		`sdklib.stubs.exportable`,
@@ -603,7 +606,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",
@@ -618,7 +621,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",
@@ -632,7 +635,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",
@@ -960,8 +963,8 @@
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
 		`dex2oatd`,
 		`prebuilt_sdklib`,
-		`sdklib-removed.api.public.latest`,
-		`sdklib.api.public.latest`,
+		`sdklib-removed.api.combined.public.latest`,
+		`sdklib.api.combined.public.latest`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
 		`sdklib.stubs.exportable`,
@@ -1039,8 +1042,8 @@
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
 		`prebuilt_sdklib`,
-		`sdklib-removed.api.public.latest`,
-		`sdklib.api.public.latest`,
+		`sdklib-removed.api.combined.public.latest`,
+		`sdklib.api.combined.public.latest`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
 		`sdklib.stubs.exportable`,
@@ -1082,18 +1085,6 @@
 	t.Run("prefer", func(t *testing.T) {
 		testJavaSdkLibraryImport_Preferred(t, "prefer: true,", android.NullFixturePreparer)
 	})
-
-	t.Run("use_source_config_var", func(t *testing.T) {
-		testJavaSdkLibraryImport_Preferred(t,
-			"use_source_config_var: {config_namespace: \"acme\", var_name: \"use_source\"},",
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.VendorVars = map[string]map[string]string{
-					"acme": {
-						"use_source": "false",
-					},
-				}
-			}))
-	})
 }
 
 // If a module is listed in `mainline_module_contributions, it should be used
@@ -1239,7 +1230,6 @@
 		libraryType                string
 		fromPartition              string
 		toPartition                string
-		enforceVendorInterface     bool
 		enforceProductInterface    bool
 		enforceJavaSdkLibraryCheck bool
 		allowList                  []string
@@ -1274,9 +1264,6 @@
 			android.FixtureWithRootAndroidBp(bpFile),
 			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 				variables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface)
-				if info.enforceVendorInterface {
-					variables.DeviceVndkVersion = proptools.StringPtr("current")
-				}
 				variables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck)
 				variables.InterPartitionJavaLibraryAllowList = info.allowList
 			}),
@@ -1304,7 +1291,6 @@
 		libraryType:                "java_library",
 		fromPartition:              "product",
 		toPartition:                "system",
-		enforceVendorInterface:     true,
 		enforceProductInterface:    true,
 		enforceJavaSdkLibraryCheck: false,
 	}, "")
@@ -1313,7 +1299,6 @@
 		libraryType:                "java_library",
 		fromPartition:              "product",
 		toPartition:                "system",
-		enforceVendorInterface:     true,
 		enforceProductInterface:    false,
 		enforceJavaSdkLibraryCheck: true,
 	}, "")
@@ -1322,7 +1307,6 @@
 		libraryType:                "java_library",
 		fromPartition:              "product",
 		toPartition:                "system",
-		enforceVendorInterface:     true,
 		enforceProductInterface:    true,
 		enforceJavaSdkLibraryCheck: true,
 	}, errorMessage)
@@ -1331,7 +1315,6 @@
 		libraryType:                "java_library",
 		fromPartition:              "vendor",
 		toPartition:                "system",
-		enforceVendorInterface:     true,
 		enforceProductInterface:    true,
 		enforceJavaSdkLibraryCheck: true,
 	}, errorMessage)
@@ -1340,7 +1323,6 @@
 		libraryType:                "java_library",
 		fromPartition:              "vendor",
 		toPartition:                "system",
-		enforceVendorInterface:     true,
 		enforceProductInterface:    true,
 		enforceJavaSdkLibraryCheck: true,
 		allowList:                  []string{"bar"},
@@ -1350,7 +1332,6 @@
 		libraryType:                "java_library",
 		fromPartition:              "vendor",
 		toPartition:                "product",
-		enforceVendorInterface:     true,
 		enforceProductInterface:    true,
 		enforceJavaSdkLibraryCheck: true,
 	}, errorMessage)
@@ -1359,7 +1340,6 @@
 		libraryType:                "java_sdk_library",
 		fromPartition:              "product",
 		toPartition:                "system",
-		enforceVendorInterface:     true,
 		enforceProductInterface:    true,
 		enforceJavaSdkLibraryCheck: true,
 	}, "")
@@ -1368,7 +1348,6 @@
 		libraryType:                "java_sdk_library",
 		fromPartition:              "vendor",
 		toPartition:                "system",
-		enforceVendorInterface:     true,
 		enforceProductInterface:    true,
 		enforceJavaSdkLibraryCheck: true,
 	}, "")
@@ -1377,7 +1356,6 @@
 		libraryType:                "java_sdk_library",
 		fromPartition:              "vendor",
 		toPartition:                "product",
-		enforceVendorInterface:     true,
 		enforceProductInterface:    true,
 		enforceJavaSdkLibraryCheck: true,
 	}, "")
@@ -1393,6 +1371,11 @@
 			"sdklib_group_foo",
 			"sdklib_owner_foo",
 			"foo"),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildFlags = map[string]string{
+				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+			}
+		}),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib_no_group",
@@ -1474,11 +1457,11 @@
 	preparer.RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
-            srcs: ["a.java"],
-            static_libs: ["util"],
-            min_sdk_version: "30",
+			srcs: ["a.java"],
+			static_libs: ["util"],
+			min_sdk_version: "30",
 			unsafe_ignore_missing_latest_api: true,
-        }
+		}
 
 		java_library {
 			name: "util",
@@ -1715,9 +1698,9 @@
 		}
 `)
 
-	barPermissions := result.ModuleForTests("bar.xml", "android_common").Rule("java_sdk_xml")
-
-	android.AssertStringDoesContain(t, "bar.xml java_sdk_xml command", barPermissions.RuleParams.Command, `dependency=\"foo\"`)
+	barPermissions := result.ModuleForTests("bar.xml", "android_common").Output("bar.xml")
+	barContents := android.ContentFromFileRuleForTests(t, result.TestContext, barPermissions)
+	android.AssertStringDoesContain(t, "bar.xml java_sdk_xml command", barContents, `dependency="foo"`)
 }
 
 func TestSdkLibraryExportableStubsLibrary(t *testing.T) {
@@ -1732,6 +1715,7 @@
 		aconfig_declarations {
 			name: "bar",
 			package: "com.example.package",
+			container: "com.android.foo",
 			srcs: [
 				"bar.aconfig",
 			],
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 59c5466..bad2cf1 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -69,6 +69,7 @@
 	configuredJars = configuredJars.AppendList(&standaloneConfiguredJars)
 	classpathJars = append(classpathJars, standaloneClasspathJars...)
 	p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
+	p.classpathFragmentBase().installClasspathProto(ctx)
 }
 
 func (p *platformSystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
@@ -189,7 +190,7 @@
 		return javaSdkLibrarySdkMemberType
 	}
 
-	return javaSystemserverLibsSdkMemberType
+	return JavaSystemserverLibsSdkMemberType
 }
 
 func (b systemServerClasspathFragmentContentDependencyTag) ExportMember() bool {
diff --git a/java/testing.go b/java/testing.go
index 631d516..5ae326d 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -711,7 +711,7 @@
 
 func registerFakeApexMutator(ctx android.RegistrationContext) {
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("apex", fakeApexMutator).Parallel()
+		ctx.Transition("apex", &fakeApexMutator{})
 	})
 }
 
@@ -726,16 +726,30 @@
 // `apex_available`. It helps us avoid a dependency on the real mutator defined in "soong-apex",
 // which will cause a cyclic dependency, and it provides an easy way to create an APEX variant for
 // testing without dealing with all the complexities in the real mutator.
-func fakeApexMutator(mctx android.BottomUpMutatorContext) {
-	switch mctx.Module().(type) {
+type fakeApexMutator struct{}
+
+func (f *fakeApexMutator) Split(ctx android.BaseModuleContext) []string {
+	switch ctx.Module().(type) {
 	case *Library, *SdkLibrary:
-		if len(mctx.Module().(apexModuleBase).ApexAvailable()) > 0 {
-			modules := mctx.CreateVariations("", "apex1000")
-			apexInfo := android.ApexInfo{
-				ApexVariationName: "apex1000",
-			}
-			mctx.SetVariationProvider(modules[1], android.ApexInfoProvider, apexInfo)
+		return []string{"", "apex1000"}
+	}
+	return []string{""}
+}
+
+func (f *fakeApexMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (f *fakeApexMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+	return incomingVariation
+}
+
+func (f *fakeApexMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if variation != "" {
+		apexInfo := android.ApexInfo{
+			ApexVariationName: "apex1000",
 		}
+		android.SetProvider(ctx, android.ApexInfoProvider, apexInfo)
 	}
 }
 
diff --git a/licenses/Android.bp b/licenses/Android.bp
index d045725..e4e5da7 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -1126,6 +1126,12 @@
 }
 
 license_kind {
+    name: "SPDX-license-identifier-Unicode-3.0",
+    conditions: ["notice"],
+    url: "https://spdx.org/licenses/Unicode-3.0.html",
+}
+
+license_kind {
     name: "SPDX-license-identifier-Unicode-DFS-2015",
     conditions: ["notice"],
     url: "https://spdx.org/licenses/Unicode-DFS-2015.html",
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 78b62d9..87c0814 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -15,7 +15,6 @@
 package linkerconfig
 
 import (
-	"fmt"
 	"sort"
 	"strings"
 
@@ -73,23 +72,12 @@
 	return l.outputFilePath
 }
 
-var _ android.OutputFileProducer = (*linkerConfig)(nil)
-
-func (l *linkerConfig) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{l.outputFilePath}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (l *linkerConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	input := android.PathForModuleSrc(ctx, android.String(l.properties.Src))
 	output := android.PathForModuleOut(ctx, "linker.config.pb").OutputPath
 
 	builder := android.NewRuleBuilder(pctx, ctx)
-	BuildLinkerConfig(ctx, builder, input, nil, output)
+	BuildLinkerConfig(ctx, builder, input, nil, nil, output)
 	builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String())
 
 	l.outputFilePath = output
@@ -98,10 +86,12 @@
 		l.SkipInstall()
 	}
 	ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
+
+	ctx.SetOutputFiles(android.Paths{l.outputFilePath}, "")
 }
 
 func BuildLinkerConfig(ctx android.ModuleContext, builder *android.RuleBuilder,
-	input android.Path, otherModules []android.Module, output android.OutputPath) {
+	input android.Path, provideModules []android.Module, requireModules []android.Module, output android.OutputPath) {
 
 	// First, convert the input json to protobuf format
 	interimOutput := android.PathForModuleOut(ctx, "temp.pb")
@@ -111,10 +101,10 @@
 		FlagWithInput("-s ", input).
 		FlagWithOutput("-o ", interimOutput)
 
-	// Secondly, if there's provideLibs gathered from otherModules, append them
+	// Secondly, if there's provideLibs gathered from provideModules, append them
 	var provideLibs []string
-	for _, m := range otherModules {
-		if c, ok := m.(*cc.Module); ok && cc.IsStubTarget(c) {
+	for _, m := range provideModules {
+		if c, ok := m.(*cc.Module); ok && (cc.IsStubTarget(c) || c.HasLlndkStubs()) {
 			for _, ps := range c.PackagingSpecs() {
 				provideLibs = append(provideLibs, ps.FileName())
 			}
@@ -122,18 +112,45 @@
 	}
 	provideLibs = android.FirstUniqueStrings(provideLibs)
 	sort.Strings(provideLibs)
+
+	var requireLibs []string
+	for _, m := range requireModules {
+		if c, ok := m.(*cc.Module); ok && c.HasStubsVariants() && !c.Host() {
+			requireLibs = append(requireLibs, c.ImplementationModuleName(ctx)+".so")
+		}
+	}
+
+	requireLibs = android.FirstUniqueStrings(requireLibs)
+	sort.Strings(requireLibs)
+
 	if len(provideLibs) > 0 {
+		prevOutput := interimOutput
+		interimOutput = android.PathForModuleOut(ctx, "temp_provideLibs.pb")
 		builder.Command().
 			BuiltTool("conv_linker_config").
 			Flag("append").
-			FlagWithInput("-s ", interimOutput).
-			FlagWithOutput("-o ", output).
+			FlagWithInput("-s ", prevOutput).
+			FlagWithOutput("-o ", interimOutput).
 			FlagWithArg("--key ", "provideLibs").
 			FlagWithArg("--value ", proptools.ShellEscapeIncludingSpaces(strings.Join(provideLibs, " ")))
-	} else {
-		// If nothing to add, just cp to the final output
-		builder.Command().Text("cp").Input(interimOutput).Output(output)
+		builder.Temporary(prevOutput)
 	}
+	if len(requireLibs) > 0 {
+		prevOutput := interimOutput
+		interimOutput = android.PathForModuleOut(ctx, "temp_requireLibs.pb")
+		builder.Command().
+			BuiltTool("conv_linker_config").
+			Flag("append").
+			FlagWithInput("-s ", prevOutput).
+			FlagWithOutput("-o ", interimOutput).
+			FlagWithArg("--key ", "requireLibs").
+			FlagWithArg("--value ", proptools.ShellEscapeIncludingSpaces(strings.Join(requireLibs, " ")))
+		builder.Temporary(prevOutput)
+	}
+
+	// cp to the final output
+	builder.Command().Text("cp").Input(interimOutput).Output(output)
+
 	builder.Temporary(interimOutput)
 	builder.DeleteTemporaryFiles()
 }
diff --git a/multitree/api_surface.go b/multitree/api_surface.go
index f739a24..0f605d8 100644
--- a/multitree/api_surface.go
+++ b/multitree/api_surface.go
@@ -16,8 +16,6 @@
 
 import (
 	"android/soong/android"
-	"fmt"
-
 	"github.com/google/blueprint"
 )
 
@@ -40,7 +38,6 @@
 	ExportableModuleBase
 	properties apiSurfaceProperties
 
-	allOutputs    android.Paths
 	taggedOutputs map[string]android.Paths
 }
 
@@ -86,15 +83,9 @@
 		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
+	ctx.SetOutputFiles(allOutputs, "")
 }
 
 func (surface *ApiSurface) TaggedOutputs() map[string]android.Paths {
@@ -105,7 +96,6 @@
 	return true
 }
 
-var _ android.OutputFileProducer = (*ApiSurface)(nil)
 var _ Exportable = (*ApiSurface)(nil)
 
 type ApiContribution interface {
diff --git a/multitree/export.go b/multitree/export.go
index aecade5..8be8f70 100644
--- a/multitree/export.go
+++ b/multitree/export.go
@@ -50,7 +50,6 @@
 
 type ExportableModule interface {
 	android.Module
-	android.OutputFileProducer
 	Exportable
 }
 
diff --git a/partner/androidmk/androidmk_test.go b/partner/androidmk/androidmk_test.go
index 6bae836..3ace750 100644
--- a/partner/androidmk/androidmk_test.go
+++ b/partner/androidmk/androidmk_test.go
@@ -54,6 +54,9 @@
 }
 
 func TestEndToEnd(t *testing.T) {
+	// Skip checking Android.mk path with cleaning "ANDROID_BUILD_TOP"
+	t.Setenv("ANDROID_BUILD_TOP", "")
+
 	for i, test := range testCases {
 		expected, err := bpfix.Reformat(test.expected)
 		if err != nil {
diff --git a/phony/phony.go b/phony/phony.go
index b8dbd00..b421176 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -23,10 +23,17 @@
 )
 
 func init() {
-	android.RegisterModuleType("phony", PhonyFactory)
-	android.RegisterModuleType("phony_rule", PhonyRuleFactory)
+	registerPhonyModuleTypes(android.InitRegistrationContext)
 }
 
+func registerPhonyModuleTypes(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("phony", PhonyFactory)
+	ctx.RegisterModuleType("phony_rule", PhonyRuleFactory)
+	ctx.RegisterModuleType("phony_rule_defaults", PhonyRuleDefaultsFactory)
+}
+
+var PrepareForTestWithPhony = android.FixtureRegisterWithContext(registerPhonyModuleTypes)
+
 type phony struct {
 	android.ModuleBase
 	requiredModuleNames       []string
@@ -42,7 +49,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()
 }
@@ -79,6 +86,7 @@
 
 type PhonyRule struct {
 	android.ModuleBase
+	android.DefaultableModuleBase
 
 	properties PhonyProperties
 }
@@ -96,6 +104,7 @@
 	module := &PhonyRule{}
 	android.InitAndroidModule(module)
 	module.AddProperties(&module.properties)
+	android.InitDefaultableModule(module)
 	return module
 }
 
@@ -113,3 +122,45 @@
 		},
 	}
 }
+
+// PhonyRuleDefaults
+type PhonyRuleDefaults struct {
+	android.ModuleBase
+	android.DefaultsModuleBase
+}
+
+// phony_rule_defaults provides a set of properties that can be inherited by other phony_rules.
+//
+// A module can use the properties from a phony_rule_defaults module using `defaults: ["defaults_module_name"]`.  Each
+// property in the defaults module that exists in the depending module will be prepended to the depending module's
+// value for that property.
+//
+// Example:
+//
+//	phony_rule_defaults {
+//	    name: "add_module1_defaults",
+//	    phony_deps: [
+//	        "module1",
+//	    ],
+//	}
+//
+//	phony_rule {
+//	    name: "example",
+//	    defaults: ["add_module1_defaults"],
+//	}
+//
+// is functionally identical to:
+//
+//	phony_rule {
+//	    name: "example",
+//	    phony_deps: [
+//	        "module1",
+//	    ],
+//	}
+func PhonyRuleDefaultsFactory() android.Module {
+	module := &PhonyRuleDefaults{}
+	module.AddProperties(&PhonyProperties{})
+	android.InitDefaultsModule(module)
+
+	return module
+}
diff --git a/provenance/provenance_singleton.go b/provenance/provenance_singleton.go
index 97345af..679632c 100644
--- a/provenance/provenance_singleton.go
+++ b/provenance/provenance_singleton.go
@@ -18,6 +18,7 @@
 
 import (
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
@@ -68,6 +69,15 @@
 
 func (p *provenanceInfoSingleton) GenerateBuildActions(context android.SingletonContext) {
 	allMetaDataFiles := make([]android.Path, 0)
+	moduleFilter := func(module android.Module) bool {
+		if !module.Enabled(context) || module.IsSkipInstall() {
+			return false
+		}
+		if p, ok := module.(ProvenanceMetadata); ok {
+			return p.ProvenanceMetaDataFile().String() != ""
+		}
+		return false
+	}
 	context.VisitAllModulesIf(moduleFilter, func(module android.Module) {
 		if p, ok := module.(ProvenanceMetadata); ok {
 			allMetaDataFiles = append(allMetaDataFiles, p.ProvenanceMetaDataFile())
@@ -91,16 +101,6 @@
 	context.Phony("droidcore", android.PathForPhony(context, "provenance_metadata"))
 }
 
-func moduleFilter(module android.Module) bool {
-	if !module.Enabled() || module.IsSkipInstall() {
-		return false
-	}
-	if p, ok := module.(ProvenanceMetadata); ok {
-		return p.ProvenanceMetaDataFile().String() != ""
-	}
-	return false
-}
-
 func GenerateArtifactProvenanceMetaData(ctx android.ModuleContext, artifactPath android.Path, installedFile android.InstallPath) android.OutputPath {
 	onDevicePathOfInstalledFile := android.InstallPathToOnDevicePath(ctx, installedFile)
 	artifactMetaDataFile := android.PathForIntermediates(ctx, "provenance_metadata", ctx.ModuleDir(), ctx.ModuleName(), "provenance_metadata.textproto")
diff --git a/provenance/tools/Android.bp b/provenance/tools/Android.bp
index 0eddd76..b42e543 100644
--- a/provenance/tools/Android.bp
+++ b/provenance/tools/Android.bp
@@ -23,11 +23,6 @@
     srcs: [
         "gen_provenance_metadata.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     libs: [
         "provenance_metadata_proto",
         "libprotobuf-python",
diff --git a/python/binary.go b/python/binary.go
index d6750c6..5f60761 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -71,9 +71,6 @@
 	installedDest android.Path
 
 	androidMkSharedLibs []string
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
 }
 
 var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil)
@@ -106,7 +103,7 @@
 	p.buildBinary(ctx)
 	p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
 		p.installSource.Base(), p.installSource)
-	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
+	ctx.SetOutputFiles(android.Paths{p.installSource}, "")
 }
 
 func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
@@ -170,7 +167,6 @@
 			entries.SetString("LOCAL_MODULE_STEM", stem)
 			entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...)
 			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
-			android.SetAconfigFileMkEntries(&p.ModuleBase, entries, p.mergedAconfigFiles)
 		})
 
 	return []android.AndroidMkEntries{entries}
@@ -192,18 +188,8 @@
 	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 Bool(p.properties.Embedded_launcher)
+	return BoolDefault(p.properties.Embedded_launcher, true)
 }
 
 func (b *PythonBinaryModule) autorun() bool {
diff --git a/python/python.go b/python/python.go
index d3cbd76..1ee533f 100644
--- a/python/python.go
+++ b/python/python.go
@@ -59,7 +59,7 @@
 	// list of the Python libraries used only for this Python version.
 	Libs []string `android:"arch_variant"`
 
-	// whether the binary is required to be built with embedded launcher for this version, defaults to false.
+	// whether the binary is required to be built with embedded launcher for this version, defaults to true.
 	Embedded_launcher *bool // TODO(b/174041232): Remove this property
 }
 
@@ -151,6 +151,8 @@
 	// The zip file containing the current module's source/data files, with the
 	// source files precompiled.
 	precompiledSrcsZip android.Path
+
+	sourceProperties android.SourceProperties
 }
 
 // newModule generates new Python base module
@@ -203,7 +205,7 @@
 var _ pythonDependency = (*PythonLibraryModule)(nil)
 
 func (p *PythonLibraryModule) init() android.Module {
-	p.AddProperties(&p.properties, &p.protoProperties)
+	p.AddProperties(&p.properties, &p.protoProperties, &p.sourceProperties)
 	android.InitAndroidArchModule(p, p.hod, p.multilib)
 	android.InitDefaultableModule(p)
 	return p
@@ -421,6 +423,11 @@
 func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: expandedSrcs.Strings()})
+	// Keep before any early returns.
+	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+		TestOnly:       Bool(p.sourceProperties.Test_only),
+		TopLevelTarget: p.sourceProperties.Top_level_test_target,
+	})
 
 	// expand data files from "data" property.
 	expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
@@ -499,8 +506,8 @@
 	}
 
 	for _, d := range expandedData {
-		if d.Ext() == pyExt || d.Ext() == protoExt {
-			ctx.PropertyErrorf("data", "found (.py|.proto) file: %q!", d.String())
+		if d.Ext() == pyExt {
+			ctx.PropertyErrorf("data", "found (.py) file: %q!", d.String())
 			continue
 		}
 		runfilesPath := filepath.Join(pkgPath, d.Rel())
@@ -516,19 +523,19 @@
 	relativeRootMap := make(map[string]android.Paths)
 	var protoSrcs android.Paths
 	addPathMapping := func(path pathMapping) {
-		// handle proto sources separately
-		if path.src.Ext() == protoExt {
-			protoSrcs = append(protoSrcs, path.src)
-		} else {
-			relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
-			relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
-		}
+		relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
+		relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
 	}
 
 	// "srcs" or "data" properties may contain filegroups so it might happen that
 	// the root directory for each source path is different.
 	for _, path := range p.srcsPathMappings {
-		addPathMapping(path)
+		// handle proto sources separately
+		if path.src.Ext() == protoExt {
+			protoSrcs = append(protoSrcs, path.src)
+		} else {
+			addPathMapping(path)
+		}
 	}
 	for _, path := range p.dataPathMappings {
 		addPathMapping(path)
@@ -545,7 +552,6 @@
 			var stagedProtoSrcs android.Paths
 			for _, srcFile := range protoSrcs {
 				stagedProtoSrc := pkgPathStagingDir.Join(ctx, pkgPath, srcFile.Rel())
-				rule.Command().Text("mkdir -p").Flag(filepath.Base(stagedProtoSrc.String()))
 				rule.Command().Text("cp -f").Input(srcFile).Output(stagedProtoSrc)
 				stagedProtoSrcs = append(stagedProtoSrcs, stagedProtoSrc)
 			}
diff --git a/python/python_test.go b/python/python_test.go
index 75a6a89..6a6bd1d 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -18,10 +18,13 @@
 	"fmt"
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	"android/soong/android"
 	"android/soong/cc"
+
+	"github.com/google/blueprint"
 )
 
 type pyModule struct {
@@ -47,7 +50,7 @@
 		" Second file: in module %s at path %q."
 	noSrcFileErr      = moduleVariantErrTemplate + "doesn't have any source files!"
 	badSrcFileExtErr  = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!"
-	badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py|.proto) file: %q!"
+	badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py) file: %q!"
 	bpFile            = "Android.bp"
 
 	data = []struct {
@@ -360,6 +363,76 @@
 	}
 }
 
+func TestTestOnlyProvider(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		PrepareForTestWithPythonBuildComponents,
+		android.PrepareForTestWithAllowMissingDependencies,
+	).RunTestWithBp(t, `
+                // These should be test-only
+                python_library { name: "py-lib-test", test_only: true }
+                python_library { name: "py-lib-test-host", test_only: true, host_supported: true }
+                python_test {    name: "py-test", srcs: ["py-test.py"] }
+                python_test_host { name: "py-test-host", srcs: ["py-test-host.py"] }
+                python_binary_host { name: "py-bin-test", srcs: ["py-bin-test.py"] }
+
+                // These should not be.
+                python_library { name: "py-lib" }
+                python_binary_host { name: "py-bin", srcs: ["py-bin.py"] }
+	`)
+
+	// Visit all modules and ensure only the ones that should
+	// marked as test-only are marked as test-only.
+
+	actualTestOnly := []string{}
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok {
+			if provider.TestOnly {
+				actualTestOnly = append(actualTestOnly, m.Name())
+			}
+		}
+	})
+	expectedTestOnlyModules := []string{
+		"py-lib-test",
+		"py-lib-test-host",
+		"py-test",
+		"py-test-host",
+	}
+
+	notEqual, left, right := android.ListSetDifference(expectedTestOnlyModules, actualTestOnly)
+	if notEqual {
+		t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
+	}
+}
+
+// Don't allow setting test-only on things that are always tests or never tests.
+func TestInvalidTestOnlyTargets(t *testing.T) {
+	testCases := []string{
+		` python_test { name: "py-test", test_only: true, srcs: ["py-test.py"] } `,
+		` python_test_host { name: "py-test-host", test_only: true, srcs: ["py-test-host.py"] } `,
+		` python_defaults { name: "py-defaults", test_only: true, srcs: ["foo.py"] } `,
+	}
+
+	for i, bp := range testCases {
+		ctx := android.GroupFixturePreparers(
+			PrepareForTestWithPythonBuildComponents,
+			android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+
+				ctx.RegisterModuleType("python_defaults", DefaultsFactory)
+			}),
+			android.PrepareForTestWithAllowMissingDependencies).
+			ExtendWithErrorHandler(android.FixtureIgnoreErrors).
+			RunTestWithBp(t, bp)
+		if len(ctx.Errs) != 1 {
+			t.Errorf("Expected err setting test_only in testcase #%d: %d errs", i, len(ctx.Errs))
+			continue
+		}
+		if !strings.Contains(ctx.Errs[0].Error(), "unrecognized property \"test_only\"") {
+			t.Errorf("ERR: %s bad bp: %s", ctx.Errs[0], bp)
+		}
+	}
+}
+
 func expectModule(t *testing.T, ctx *android.TestContext, name, variant, expectedSrcsZip string, expectedPyRunfiles []string) {
 	module := ctx.ModuleForTests(name, variant)
 
diff --git a/python/scripts/precompile_python.py b/python/scripts/precompile_python.py
index aa1a5df..b3cf950 100644
--- a/python/scripts/precompile_python.py
+++ b/python/scripts/precompile_python.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import print_function
 import argparse
 import py_compile
 import os
@@ -29,6 +30,7 @@
     # Date was chosen to be the same as
     # https://cs.android.com/android/platform/superproject/main/+/main:build/soong/jar/jar.go;l=36;drc=2863e4535eb65e15f955dc8ed48fa99b1d2a1db5
     info = zipfile.ZipInfo(filename=name, date_time=(2008, 1, 1, 0, 0, 0))
+    info.compress_type = zipfile.ZIP_DEFLATED
 
     if not info.filename.endswith('.py'):
         outzip.writestr(info, infile.read())
@@ -63,11 +65,23 @@
     parser.add_argument('dst_zip')
     args = parser.parse_args()
 
+    errors = []
     with open(args.dst_zip, 'wb') as outf, open(args.src_zip, 'rb') as inf:
         with zipfile.ZipFile(outf, mode='w') as outzip, zipfile.ZipFile(inf, mode='r') as inzip:
             for name in inzip.namelist():
                 with inzip.open(name, mode='r') as inzipf:
-                    process_one_file(name, inzipf, outzip)
+                    try:
+                        process_one_file(name, inzipf, outzip)
+                    except py_compile.PyCompileError as e:
+                        errors.append(e)
+
+    if errors:
+        for i, error in enumerate(errors):
+            # Print an empty line in between each error
+            if i > 0:
+                print(file=sys.stderr)
+            print(str(error).strip(), file=sys.stderr)
+        sys.exit(1)
 
 
 if __name__ == "__main__":
diff --git a/python/test.go b/python/test.go
index 826f353..85decf9 100644
--- a/python/test.go
+++ b/python/test.go
@@ -18,6 +18,7 @@
 	"fmt"
 
 	"android/soong/testing"
+
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -36,7 +37,9 @@
 }
 
 func NewTest(hod android.HostOrDeviceSupported) *PythonTestModule {
-	return &PythonTestModule{PythonBinaryModule: *NewBinary(hod)}
+	p := &PythonTestModule{PythonBinaryModule: *NewBinary(hod)}
+	p.sourceProperties = android.SourceProperties{Test_only: proptools.BoolPtr(true), Top_level_test_target: true}
+	return p
 }
 
 func PythonTestHostFactory() android.Module {
@@ -149,7 +152,6 @@
 	// just use buildBinary() so that the binary is not installed into the location
 	// it would be for regular binaries.
 	p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
-	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
 	p.buildBinary(ctx)
 
 	var configs []tradefed.Option
@@ -225,7 +227,6 @@
 			}
 
 			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
-			android.SetAconfigFileMkEntries(&p.ModuleBase, entries, p.mergedAconfigFiles)
 
 			p.testProperties.Test_options.SetAndroidMkEntries(entries)
 		})
diff --git a/python/tests/Android.bp b/python/tests/Android.bp
index e5569ba..056f7ed 100644
--- a/python/tests/Android.bp
+++ b/python/tests/Android.bp
@@ -27,9 +27,4 @@
     test_options: {
         unit_test: false,
     },
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
 }
diff --git a/python/tests/dont_import_folder_of_entrypoint/Android.bp b/python/tests/dont_import_folder_of_entrypoint/Android.bp
index e54e9b2..ab2e314 100644
--- a/python/tests/dont_import_folder_of_entrypoint/Android.bp
+++ b/python/tests/dont_import_folder_of_entrypoint/Android.bp
@@ -10,17 +10,3 @@
         "mypkg/mymodule.py",
     ],
 }
-
-python_test_host {
-    name: "py_dont_import_folder_of_entrypoint_test_embedded_launcher",
-    main: "mypkg/main.py",
-    srcs: [
-        "mypkg/main.py",
-        "mypkg/mymodule.py",
-    ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
-}
diff --git a/rust/Android.bp b/rust/Android.bp
index 637042d..53c9462 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -12,7 +12,6 @@
         "soong-bloaty",
         "soong-cc",
         "soong-rust-config",
-        "soong-snapshot",
         "soong-testing",
     ],
     srcs: [
@@ -36,8 +35,6 @@
         "rust.go",
         "sanitize.go",
         "source_provider.go",
-        "snapshot_prebuilt.go",
-        "snapshot_utils.go",
         "strip.go",
         "test.go",
         "testing.go",
@@ -62,7 +59,6 @@
         "sanitize_test.go",
         "source_provider_test.go",
         "test_test.go",
-        "vendor_snapshot_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/rust/afdo.go b/rust/afdo.go
index 6116c5e..6bd4bae 100644
--- a/rust/afdo.go
+++ b/rust/afdo.go
@@ -39,7 +39,7 @@
 		return
 	}
 
-	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() {
+	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled(ctx) {
 		fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName())
 		if err != nil {
 			ctx.ModuleErrorf("%s", err.Error())
diff --git a/rust/androidmk.go b/rust/androidmk.go
index e0cb3ce..8de6b60 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -62,13 +62,13 @@
 				entries.AddStrings("LOCAL_PROC_MACRO_LIBRARIES", mod.Properties.AndroidMkProcMacroLibs...)
 				entries.AddStrings("LOCAL_SHARED_LIBRARIES", mod.transitiveAndroidMkSharedLibs.ToList()...)
 				entries.AddStrings("LOCAL_STATIC_LIBRARIES", mod.Properties.AndroidMkStaticLibs...)
+				entries.AddStrings("LOCAL_HEADER_LIBRARIES", mod.Properties.AndroidMkHeaderLibs...)
 				entries.AddStrings("LOCAL_SOONG_LINK_TYPE", mod.makeLinkType)
 				if mod.InVendor() {
 					entries.SetBool("LOCAL_IN_VENDOR", true)
 				} else if mod.InProduct() {
 					entries.SetBool("LOCAL_IN_PRODUCT", true)
 				}
-				android.SetAconfigFileMkEntries(mod.AndroidModuleBase(), entries, mod.mergedAconfigFiles)
 			},
 		},
 	}
@@ -154,11 +154,6 @@
 		})
 }
 
-func (library *snapshotLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
-	ctx.SubAndroidMk(ret, library.libraryDecorator)
-	ret.SubName = library.SnapshotAndroidMkSuffix()
-}
-
 func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
 	ctx.SubAndroidMk(ret, procMacro.baseCompiler)
 
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 454dd87..dbc3697 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-r522817"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -101,6 +101,18 @@
 	//
 	// "my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]"
 	Custom_bindgen string
+
+	// flag to indicate if bindgen should handle `static inline` functions (default is false).
+	// If true, Static_inline_library must be set.
+	Handle_static_inline *bool
+
+	// module name of the corresponding cc_library_static which includes the static_inline wrapper
+	// generated functions from bindgen. Must be used together with handle_static_inline.
+	//
+	// If there are no static inline functions provided through the header file,
+	// then bindgen (as of 0.69.2) will silently fail to output a .c file, and
+	// the cc_library_static depending on this module will fail compilation.
+	Static_inline_library *string
 }
 
 type bindgenDecorator struct {
@@ -156,6 +168,18 @@
 
 	var cflags []string
 	var implicits android.Paths
+	var implicitOutputs android.WritablePaths
+	var validations android.Paths
+
+	if Bool(b.Properties.Handle_static_inline) && b.Properties.Static_inline_library == nil {
+		ctx.PropertyErrorf("handle_static_inline",
+			"requires declaring static_inline_library to the corresponding cc_library module that includes the generated C source from bindgen.")
+	}
+
+	if b.Properties.Static_inline_library != nil && !Bool(b.Properties.Handle_static_inline) {
+		ctx.PropertyErrorf("static_inline_library",
+			"requires declaring handle_static_inline.")
+	}
 
 	implicits = append(implicits, deps.depGeneratedHeaders...)
 
@@ -212,7 +236,8 @@
 	esc := proptools.NinjaAndShellEscapeList
 
 	// Filter out invalid cflags
-	for _, flag := range b.ClangProperties.Cflags {
+	cflagsProp := b.ClangProperties.Cflags.GetOrDefault(ctx, nil)
+	for _, flag := range cflagsProp {
 		if flag == "-x c++" || flag == "-xc++" {
 			ctx.PropertyErrorf("cflags",
 				"-x c++ should not be specified in cflags; setting cpp_std specifies this is a C++ header, or change the file extension to '.hpp' or '.hh'")
@@ -224,7 +249,7 @@
 	}
 
 	// Module defined clang flags and include paths
-	cflags = append(cflags, esc(b.ClangProperties.Cflags)...)
+	cflags = append(cflags, esc(cflagsProp)...)
 	for _, include := range b.ClangProperties.Local_include_dirs {
 		cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String())
 		implicits = append(implicits, android.PathForModuleSrc(ctx, include))
@@ -232,6 +257,12 @@
 
 	bindgenFlags := defaultBindgenFlags
 	bindgenFlags = append(bindgenFlags, esc(b.Properties.Bindgen_flags)...)
+	if Bool(b.Properties.Handle_static_inline) {
+		outputStaticFnsFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".c")
+		implicitOutputs = append(implicitOutputs, outputStaticFnsFile)
+		validations = append(validations, outputStaticFnsFile)
+		bindgenFlags = append(bindgenFlags, []string{"--experimental", "--wrap-static-fns", "--wrap-static-fns-path=" + outputStaticFnsFile.String()}...)
+	}
 
 	// cat reads from stdin if its command line is empty,
 	// so we pass in /dev/null if there are no other flag files
@@ -279,11 +310,13 @@
 	}
 
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        bindgen,
-		Description: strings.Join([]string{cmdDesc, wrapperFile.Path().Rel()}, " "),
-		Output:      outputFile,
-		Input:       wrapperFile.Path(),
-		Implicits:   implicits,
+		Rule:            bindgen,
+		Description:     strings.Join([]string{cmdDesc, wrapperFile.Path().Rel()}, " "),
+		Output:          outputFile,
+		Input:           wrapperFile.Path(),
+		Implicits:       implicits,
+		ImplicitOutputs: implicitOutputs,
+		Validations:     validations,
 		Args: map[string]string{
 			"cmd":       cmd,
 			"flags":     strings.Join(bindgenFlags, " "),
@@ -293,6 +326,14 @@
 	})
 
 	b.BaseSourceProvider.OutputFiles = android.Paths{outputFile}
+
+	// Append any additional implicit outputs after the entry point source.
+	// We append any generated .c file here so it can picked up by cc_library_static modules.
+	// Those CC modules need to be sure not to pass any included .rs files to Clang.
+	// We don't have to worry about the additional .c files for Rust modules as only the entry point
+	// is passed to rustc.
+	b.BaseSourceProvider.OutputFiles = append(b.BaseSourceProvider.OutputFiles, implicitOutputs.Paths()...)
+
 	return outputFile
 }
 
@@ -344,8 +385,16 @@
 		deps = muslDeps(ctx, deps, false)
 	}
 
+	if !ctx.RustModule().Source() && b.Properties.Static_inline_library != nil {
+		// This is not the source variant, so add the static inline library as a dependency.
+		//
+		// This is necessary to avoid a circular dependency between the source variant and the
+		// dependent cc module.
+		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.StaticLibs, b.ClangProperties.Header_libs...)
+	deps.HeaderLibs = append(deps.HeaderLibs, b.ClangProperties.Header_libs...)
 	return deps
 }
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index 0ba0ff8..2b7362f 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -17,6 +17,8 @@
 import (
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestRustBindgen(t *testing.T) {
@@ -31,7 +33,21 @@
 			bindgen_flags: ["--bindgen-flag.*"],
 			cflags: ["--clang-flag()"],
 			shared_libs: ["libfoo_shared"],
+		}
+		rust_bindgen {
+			name: "libbindgen_staticlib",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen_staticlib",
+			stem: "libbindgen_staticlib",
+			source_stem: "bindings",
 			static_libs: ["libfoo_static"],
+		}
+		rust_bindgen {
+			name: "libbindgen_headerlib",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen_headerlib",
+			stem: "libbindgen_headerlib",
+			source_stem: "bindings",
 			header_libs: ["libfoo_header"],
 		}
 		cc_library_shared {
@@ -52,6 +68,9 @@
 		}
 	`)
 	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgenStatic := ctx.ModuleForTests("libbindgen_staticlib", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgenHeader := ctx.ModuleForTests("libbindgen_headerlib", "android_arm64_armv8-a_source").Output("bindings.rs")
+	libbindgenHeaderModule := ctx.ModuleForTests("libbindgen_headerlib", "android_arm64_armv8-a_source").Module().(*Module)
 	// Ensure that the flags are present and escaped
 	if !strings.Contains(libbindgen.Args["flags"], "'--bindgen-flag.*'") {
 		t.Errorf("missing bindgen flags in rust_bindgen rule: flags %#v", libbindgen.Args["flags"])
@@ -62,12 +81,17 @@
 	if !strings.Contains(libbindgen.Args["cflags"], "-Ishared_include") {
 		t.Errorf("missing shared_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
 	}
-	if !strings.Contains(libbindgen.Args["cflags"], "-Istatic_include") {
-		t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
+	if !strings.Contains(libbindgenStatic.Args["cflags"], "-Istatic_include") {
+		t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgenStatic.Args["cflags"])
 	}
-	if !strings.Contains(libbindgen.Args["cflags"], "-Iheader_include") {
-		t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
+	if !strings.Contains(libbindgenHeader.Args["cflags"], "-Iheader_include") {
+		t.Errorf("missing header_libs exported includes in rust_bindgen rule: cflags %#v", libbindgenHeader.Args["cflags"])
 	}
+
+	if android.InList("libfoo_static", libbindgenHeaderModule.Properties.AndroidMkHeaderLibs) {
+		t.Errorf("Static library dependency should not be in HeaderLibs list")
+	}
+
 	if !strings.Contains(libbindgen.Args["cflags"], "--default-flag") {
 		t.Errorf("rust_bindgen missing cflags defined in cc_defaults: cflags %#v", libbindgen.Args["cflags"])
 	}
@@ -203,3 +227,63 @@
 	// TODO: The best we can do right now is check $flagfiles. Once bindgen.go switches to RuleBuilder,
 	// we may be able to check libbinder.RuleParams.Command to see if it contains $(cat /dev/null flag_file.txt)
 }
+
+func TestBindgenHandleStaticInlining(t *testing.T) {
+	ctx := testRust(t, `
+		rust_bindgen {
+			name: "libbindgen",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			handle_static_inline: true,
+			static_inline_library: "libbindgen_staticfns"
+		}
+
+		cc_library_static {
+			name: "libbindgen_staticfns",
+			srcs: [":libbindgen"],
+			include_dirs: ["src/"],
+		}
+	`)
+	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
+	// Make sure the flag to support `static inline` functions is present
+	if !strings.Contains(libbindgen.Args["flags"], "--wrap-static-fns") {
+		t.Errorf("missing flag to handle static inlining in rust_bindgen rule: flags %#v", libbindgen.Args["flags"])
+	}
+
+	if !strings.Contains(libbindgen.Args["flags"], "--wrap-static-fns-path") {
+		t.Errorf("missing flag to define path for static inlining C source from bindgen (--wrap-static-fns-path): flags %#v", libbindgen.Args["flags"])
+	}
+
+}
+
+func TestBindgenStaticInlineProperties(t *testing.T) {
+	// Make sure handle_static_inline without static_inline_library generates an error
+	testRustError(t, "requires declaring static_inline_library to the corresponding cc_library module that includes the generated C source from bindgen", `
+		rust_bindgen {
+			name: "libbindgen",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			handle_static_inline: true
+		}
+	`)
+	testRustError(t, "requires declaring handle_static_inline", `
+		rust_bindgen {
+			name: "libbindgen",
+			wrapper_src: "src/any.h",
+			crate_name: "bindgen",
+			stem: "libbindgen",
+			source_stem: "bindings",
+			static_inline_library: "libbindgen_staticfns"
+		}
+
+		cc_library_static {
+			name: "libbindgen_staticfns",
+			srcs: [":libbindgen"],
+			include_dirs: ["src/"],
+		}
+	`)
+}
diff --git a/rust/builder.go b/rust/builder.go
index c855cfb..f469f56 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -21,6 +21,7 @@
 	"github.com/google/blueprint"
 
 	"android/soong/android"
+	"android/soong/cc"
 	"android/soong/rust/config"
 )
 
@@ -32,7 +33,7 @@
 				"-C linker=${config.RustLinker} " +
 				"-C link-args=\"${crtBegin} ${earlyLinkFlags} ${linkFlags} ${crtEnd}\" " +
 				"--emit link -o $out --emit dep-info=$out.d.raw $in ${libFlags} $rustcFlags" +
-				" && grep \"^$out:\" $out.d.raw > $out.d",
+				" && grep ^$out: $out.d.raw > $out.d",
 			CommandDeps: []string{"$rustcCmd"},
 			// Rustc deps-info writes out make compatible dep files: https://github.com/rust-lang/rust/issues/7633
 			// Rustc emits unneeded dependency lines for the .d and input .rs files.
@@ -61,7 +62,7 @@
 				// Use the metadata output as it has the smallest footprint.
 				"--emit metadata -o $out --emit dep-info=$out.d.raw $in ${libFlags} " +
 				"$rustcFlags $clippyFlags" +
-				" && grep \"^$out:\" $out.d.raw > $out.d",
+				" && grep ^$out: $out.d.raw > $out.d",
 			CommandDeps: []string{"$clippyCmd"},
 			Deps:        blueprint.DepsGCC,
 			Depfile:     "$out.d",
@@ -118,42 +119,129 @@
 
 func init() {
 	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
+	cc.TransformRlibstoStaticlib = TransformRlibstoStaticlib
+}
+
+type transformProperties struct {
+	crateName       string
+	targetTriple    string
+	is64Bit         bool
+	bootstrap       bool
+	inRecovery      bool
+	inRamdisk       bool
+	inVendorRamdisk bool
+	cargoOutDir     android.OptionalPath
+	synthetic       bool
+	crateType       string
+}
+
+// Populates a standard transformProperties struct for Rust modules
+func getTransformProperties(ctx ModuleContext, crateType string) transformProperties {
+	module := ctx.RustModule()
+	return transformProperties{
+		crateName:       module.CrateName(),
+		is64Bit:         ctx.toolchain().Is64Bit(),
+		targetTriple:    ctx.toolchain().RustTriple(),
+		bootstrap:       module.Bootstrap(),
+		inRecovery:      module.InRecovery(),
+		inRamdisk:       module.InRamdisk(),
+		inVendorRamdisk: module.InVendorRamdisk(),
+		cargoOutDir:     module.compiler.cargoOutDir(),
+
+		// crateType indicates what type of crate to build
+		crateType: crateType,
+
+		// synthetic indicates whether this is an actual Rust module or not
+		synthetic: false,
+	}
 }
 
 func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	if ctx.RustModule().compiler.Thinlto() {
+		flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	}
 
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin")
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "bin"))
 }
 
 func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib")
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib"))
+}
+
+func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep,
+	outputFile android.WritablePath) android.Path {
+
+	var rustPathDeps PathDeps
+	var rustFlags Flags
+
+	for _, rlibDep := range deps {
+		rustPathDeps.RLibs = append(rustPathDeps.RLibs, RustLibrary{Path: rlibDep.LibPath, CrateName: rlibDep.CrateName})
+		rustPathDeps.linkDirs = append(rustPathDeps.linkDirs, rlibDep.LinkDirs...)
+	}
+
+	ccModule := ctx.(cc.ModuleContext).Module().(*cc.Module)
+	toolchain := config.FindToolchain(ctx.Os(), ctx.Arch())
+	t := transformProperties{
+		// Crate name can be a predefined value as this is a staticlib and
+		// it does not need to be unique. The crate name is used for name
+		// mangling, but it is mixed with the metadata for that purpose, which we
+		// already set to the module name.
+		crateName:       "generated_rust_staticlib",
+		is64Bit:         toolchain.Is64Bit(),
+		targetTriple:    toolchain.RustTriple(),
+		bootstrap:       ccModule.Bootstrap(),
+		inRecovery:      ccModule.InRecovery(),
+		inRamdisk:       ccModule.InRamdisk(),
+		inVendorRamdisk: ccModule.InVendorRamdisk(),
+
+		// crateType indicates what type of crate to build
+		crateType: "staticlib",
+
+		// synthetic indicates whether this is an actual Rust module or not
+		synthetic: true,
+	}
+
+	rustFlags = CommonDefaultFlags(ctx, toolchain, rustFlags)
+	rustFlags = CommonLibraryCompilerFlags(ctx, rustFlags)
+	rustFlags.GlobalRustFlags = append(rustFlags.GlobalRustFlags, "-C lto=thin")
+
+	rustFlags.EmitXrefs = false
+
+	return transformSrctoCrate(ctx, mainSrc, rustPathDeps, rustFlags, outputFile, t).outputFile
 }
 
 func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	if ctx.RustModule().compiler.Thinlto() {
+		flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	}
 
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib")
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "dylib"))
 }
 
 func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib")
+	if ctx.RustModule().compiler.Thinlto() {
+		flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	}
+
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "staticlib"))
 }
 
 func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib")
+	if ctx.RustModule().compiler.Thinlto() {
+		flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
+	}
+
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "cdylib"))
 }
 
 func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps,
 	flags Flags, outputFile android.WritablePath) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro")
+	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "proc-macro"))
 }
 
 func rustLibsToPaths(libs RustLibraries) android.Paths {
@@ -185,18 +273,18 @@
 	return libFlags
 }
 
-func rustEnvVars(ctx ModuleContext, deps PathDeps) []string {
+func rustEnvVars(ctx android.ModuleContext, deps PathDeps, crateName string, cargoOutDir android.OptionalPath) []string {
 	var envVars []string
 
 	// libstd requires a specific environment variable to be set. This is
 	// not officially documented and may be removed in the future. See
 	// https://github.com/rust-lang/rust/blob/master/library/std/src/env.rs#L866.
-	if ctx.RustModule().CrateName() == "std" {
-		envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.RustModule().Arch().ArchType])
+	if crateName == "std" {
+		envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.Arch().ArchType])
 	}
 
-	if len(deps.SrcDeps) > 0 {
-		moduleGenDir := ctx.RustModule().compiler.cargoOutDir()
+	if len(deps.SrcDeps) > 0 && cargoOutDir.Valid() {
+		moduleGenDir := cargoOutDir
 		// We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this)
 		// assumes that paths are relative to the source file.
 		var outDirPrefix string
@@ -215,13 +303,15 @@
 
 	envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx))
 
-	if ctx.RustModule().compiler.cargoEnvCompat() {
-		if bin, ok := ctx.RustModule().compiler.(*binaryDecorator); ok {
+	if rustMod, ok := ctx.Module().(*Module); ok && rustMod.compiler.cargoEnvCompat() {
+		// We only emulate cargo environment variables for 3p code, which is only ever built
+		// by defining a Rust module, so we only need to set these for true Rust modules.
+		if bin, ok := rustMod.compiler.(*binaryDecorator); ok {
 			envVars = append(envVars, "CARGO_BIN_NAME="+bin.getStem(ctx))
 		}
-		envVars = append(envVars, "CARGO_CRATE_NAME="+ctx.RustModule().CrateName())
-		envVars = append(envVars, "CARGO_PKG_NAME="+ctx.RustModule().CrateName())
-		pkgVersion := ctx.RustModule().compiler.cargoPkgVersion()
+		envVars = append(envVars, "CARGO_CRATE_NAME="+crateName)
+		envVars = append(envVars, "CARGO_PKG_NAME="+crateName)
+		pkgVersion := rustMod.compiler.cargoPkgVersion()
 		if pkgVersion != "" {
 			envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion)
 
@@ -245,8 +335,8 @@
 	return envVars
 }
 
-func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags,
-	outputFile android.WritablePath, crateType string) buildOutput {
+func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags,
+	outputFile android.WritablePath, t transformProperties) buildOutput {
 
 	var inputs android.Paths
 	var implicits android.Paths
@@ -256,23 +346,21 @@
 	var earlyLinkFlags string
 
 	output.outputFile = outputFile
-	crateName := ctx.RustModule().CrateName()
-	targetTriple := ctx.toolchain().RustTriple()
 
-	envVars := rustEnvVars(ctx, deps)
+	envVars := rustEnvVars(ctx, deps, t.crateName, t.cargoOutDir)
 
 	inputs = append(inputs, main)
 
 	// Collect rustc flags
 	rustcFlags = append(rustcFlags, flags.GlobalRustFlags...)
 	rustcFlags = append(rustcFlags, flags.RustFlags...)
-	rustcFlags = append(rustcFlags, "--crate-type="+crateType)
-	if crateName != "" {
-		rustcFlags = append(rustcFlags, "--crate-name="+crateName)
+	rustcFlags = append(rustcFlags, "--crate-type="+t.crateType)
+	if t.crateName != "" {
+		rustcFlags = append(rustcFlags, "--crate-name="+t.crateName)
 	}
-	if targetTriple != "" {
-		rustcFlags = append(rustcFlags, "--target="+targetTriple)
-		linkFlags = append(linkFlags, "-target "+targetTriple)
+	if t.targetTriple != "" {
+		rustcFlags = append(rustcFlags, "--target="+t.targetTriple)
+		linkFlags = append(linkFlags, "-target "+t.targetTriple)
 	}
 
 	// Suppress an implicit sysroot
@@ -302,9 +390,9 @@
 	linkFlags = append(linkFlags, flags.LinkFlags...)
 
 	// Check if this module needs to use the bootstrap linker
-	if ctx.RustModule().Bootstrap() && !ctx.RustModule().InRecovery() && !ctx.RustModule().InRamdisk() && !ctx.RustModule().InVendorRamdisk() {
+	if t.bootstrap && !t.inRecovery && !t.inRamdisk && !t.inVendorRamdisk {
 		dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker"
-		if ctx.toolchain().Is64Bit() {
+		if t.is64Bit {
 			dynamicLinker += "64"
 		}
 		linkFlags = append(linkFlags, dynamicLinker)
@@ -326,49 +414,56 @@
 
 	orderOnly = append(orderOnly, deps.SharedLibs...)
 
-	if len(deps.SrcDeps) > 0 {
-		moduleGenDir := ctx.RustModule().compiler.cargoOutDir()
-		var outputs android.WritablePaths
+	if !t.synthetic {
+		// Only worry about OUT_DIR for actual Rust modules.
+		// Libraries built from cc use generated source, and do not utilize OUT_DIR.
+		if len(deps.SrcDeps) > 0 {
+			var outputs android.WritablePaths
 
-		for _, genSrc := range deps.SrcDeps {
-			if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) {
-				ctx.PropertyErrorf("srcs",
-					"multiple source providers generate the same filename output: "+genSrc.Base())
+			for _, genSrc := range deps.SrcDeps {
+				if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) {
+					ctx.PropertyErrorf("srcs",
+						"multiple source providers generate the same filename output: "+genSrc.Base())
+				}
+				outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base()))
 			}
-			outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base()))
-		}
 
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        cp,
-			Description: "cp " + moduleGenDir.Path().Rel(),
-			Outputs:     outputs,
-			Inputs:      deps.SrcDeps,
-			Args: map[string]string{
-				"outDir": moduleGenDir.String(),
-			},
-		})
-		implicits = append(implicits, outputs.Paths()...)
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        cp,
+				Description: "cp " + t.cargoOutDir.Path().Rel(),
+				Outputs:     outputs,
+				Inputs:      deps.SrcDeps,
+				Args: map[string]string{
+					"outDir": t.cargoOutDir.String(),
+				},
+			})
+			implicits = append(implicits, outputs.Paths()...)
+		}
 	}
 
-	if flags.Clippy {
-		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:            clippyDriver,
-			Description:     "clippy " + main.Rel(),
-			Output:          clippyFile,
-			ImplicitOutputs: nil,
-			Inputs:          inputs,
-			Implicits:       implicits,
-			OrderOnly:       orderOnly,
-			Args: map[string]string{
-				"rustcFlags":  strings.Join(rustcFlags, " "),
-				"libFlags":    strings.Join(libFlags, " "),
-				"clippyFlags": strings.Join(flags.ClippyFlags, " "),
-				"envVars":     strings.Join(envVars, " "),
-			},
-		})
-		// Declare the clippy build as an implicit dependency of the original crate.
-		implicits = append(implicits, clippyFile)
+	if !t.synthetic {
+		// Only worry about clippy for actual Rust modules.
+		// Libraries built from cc use generated source, and don't need to run clippy.
+		if flags.Clippy {
+			clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:            clippyDriver,
+				Description:     "clippy " + main.Rel(),
+				Output:          clippyFile,
+				ImplicitOutputs: nil,
+				Inputs:          inputs,
+				Implicits:       implicits,
+				OrderOnly:       orderOnly,
+				Args: map[string]string{
+					"rustcFlags":  strings.Join(rustcFlags, " "),
+					"libFlags":    strings.Join(libFlags, " "),
+					"clippyFlags": strings.Join(flags.ClippyFlags, " "),
+					"envVars":     strings.Join(envVars, " "),
+				},
+			})
+			// Declare the clippy build as an implicit dependency of the original crate.
+			implicits = append(implicits, clippyFile)
+		}
 	}
 
 	ctx.Build(pctx, android.BuildParams{
@@ -389,25 +484,28 @@
 		},
 	})
 
-	if flags.EmitXrefs {
-		kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        kytheExtract,
-			Description: "Xref Rust extractor " + main.Rel(),
-			Output:      kytheFile,
-			Inputs:      inputs,
-			Implicits:   implicits,
-			OrderOnly:   orderOnly,
-			Args: map[string]string{
-				"rustcFlags": strings.Join(rustcFlags, " "),
-				"linkFlags":  strings.Join(linkFlags, " "),
-				"libFlags":   strings.Join(libFlags, " "),
-				"crtBegin":   strings.Join(deps.CrtBegin.Strings(), " "),
-				"crtEnd":     strings.Join(deps.CrtEnd.Strings(), " "),
-				"envVars":    strings.Join(envVars, " "),
-			},
-		})
-		output.kytheFile = kytheFile
+	if !t.synthetic {
+		// Only emit xrefs for true Rust modules.
+		if flags.EmitXrefs {
+			kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        kytheExtract,
+				Description: "Xref Rust extractor " + main.Rel(),
+				Output:      kytheFile,
+				Inputs:      inputs,
+				Implicits:   implicits,
+				OrderOnly:   orderOnly,
+				Args: map[string]string{
+					"rustcFlags": strings.Join(rustcFlags, " "),
+					"linkFlags":  strings.Join(linkFlags, " "),
+					"libFlags":   strings.Join(libFlags, " "),
+					"crtBegin":   strings.Join(deps.CrtBegin.Strings(), " "),
+					"crtEnd":     strings.Join(deps.CrtEnd.Strings(), " "),
+					"envVars":    strings.Join(envVars, " "),
+				},
+			})
+			output.kytheFile = kytheFile
+		}
 	}
 	return output
 }
@@ -422,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
@@ -457,7 +558,7 @@
 		Args: map[string]string{
 			"rustdocFlags": strings.Join(rustdocFlags, " "),
 			"outDir":       docDir.String(),
-			"envVars":      strings.Join(rustEnvVars(ctx, deps), " "),
+			"envVars":      strings.Join(rustEnvVars(ctx, deps, crateName, ctx.RustModule().compiler.cargoOutDir()), " "),
 		},
 	})
 
diff --git a/rust/builder_test.go b/rust/builder_test.go
index 639f6d4..ae5ccde 100644
--- a/rust/builder_test.go
+++ b/rust/builder_test.go
@@ -46,6 +46,9 @@
 }
 
 func TestCompilationOutputFiles(t *testing.T) {
+
+	// Note: Rustdoc output is produced for the PrimaryModule, so if the variant
+	// order changes, then it may be produced for a different variant.
 	ctx := testRust(t, `
 		rust_library {
 			name: "libfizz_buzz",
@@ -62,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
@@ -115,14 +123,24 @@
 			},
 		},
 		{
-			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",
+			},
+		},
+		{
+			testName:   "rust_ffi rlib",
+			moduleName: "librust_ffi",
+			variant:    "android_arm64_armv8-a_rlib_rlib-std",
+			expectedFiles: []string{
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/librust_ffi.rlib",
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/librust_ffi.rlib.clippy",
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/meta_lic",
 			},
 		},
 		{
@@ -135,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/compiler.go b/rust/compiler.go
index c1bdbeb..a2546a1 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -47,6 +47,7 @@
 	edition() string
 	features() []string
 	rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath
+	Thinlto() bool
 
 	// Output directory in which source-generated code from dependencies is
 	// copied. This is equivalent to Cargo's OUT_DIR variable.
@@ -75,6 +76,8 @@
 	strippedOutputFilePath() android.OptionalPath
 
 	checkedCrateRootPath() (android.Path, error)
+
+	Aliases() map[string]string
 }
 
 func (compiler *baseCompiler) edition() string {
@@ -140,6 +143,12 @@
 	// flags to pass to the linker
 	Ld_flags []string `android:"arch_variant"`
 
+	// Rust crate dependencies to rename. Each entry should be a string of the form "dependencyname:alias".
+	//
+	// "dependencyname" here should be the name of the crate, not the Android module. This is
+	// equivalent to writing `alias = { package = "dependencyname" }` in a `Cargo.toml`.
+	Aliases []string
+
 	// list of rust rlib crate dependencies
 	Rlibs []string `android:"arch_variant"`
 
@@ -188,7 +197,7 @@
 	Features []string `android:"arch_variant"`
 
 	// list of configuration options to enable for this crate. To enable features, use the "features" property.
-	Cfgs []string `android:"arch_variant"`
+	Cfgs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// specific rust edition that should be used if the default version is not desired
 	Edition *string `android:"arch_variant"`
@@ -223,6 +232,15 @@
 
 	// If cargo_env_compat is true, sets the CARGO_PKG_VERSION env var to this value.
 	Cargo_pkg_version *string
+
+	// Control whether LTO is used for the final (Rust) linkage. This does not impact
+	// cross-language LTO.
+	Lto struct {
+		// Whether thin LTO should be enabled. By default this is true.
+		// LTO provides such a large code size benefit for Rust, this should always
+		// be enabled for production builds unless there's a clear need to disable it.
+		Thin *bool `android:"arch_variant"`
+	} `android:"arch_variant"`
 }
 
 type baseCompiler struct {
@@ -265,6 +283,11 @@
 	return false
 }
 
+// Thin LTO is enabled by default.
+func (compiler *baseCompiler) Thinlto() bool {
+	return BoolDefault(compiler.Properties.Lto.Thin, true)
+}
+
 func (compiler *baseCompiler) SetDisabled() {
 	panic("baseCompiler does not implement SetDisabled()")
 }
@@ -281,6 +304,18 @@
 	return Bool(compiler.Properties.Prefer_rlib)
 }
 
+func (compiler *baseCompiler) Aliases() map[string]string {
+	aliases := map[string]string{}
+	for _, entry := range compiler.Properties.Aliases {
+		dep, alias, found := strings.Cut(entry, ":")
+		if !found {
+			panic(fmt.Errorf("invalid aliases entry %q missing ':'", entry))
+		}
+		aliases[dep] = alias
+	}
+	return aliases
+}
+
 func (compiler *baseCompiler) stdLinkage(ctx *depsContext) RustLinkage {
 	// For devices, we always link stdlibs in as dylibs by default.
 	if compiler.preferRlib() {
@@ -302,9 +337,9 @@
 	return []interface{}{&compiler.Properties}
 }
 
-func (compiler *baseCompiler) cfgsToFlags() []string {
-	flags := []string{}
-	for _, cfg := range compiler.Properties.Cfgs {
+func cfgsToFlags(cfgs []string) []string {
+	flags := make([]string, 0, len(cfgs))
+	for _, cfg := range cfgs {
 		flags = append(flags, "--cfg '"+cfg+"'")
 	}
 
@@ -331,23 +366,62 @@
 	return flags
 }
 
-func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags {
-	if ctx.RustModule().InVendorOrProduct() {
-		compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vndk")
-		if ctx.RustModule().InVendor() {
-			compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vendor")
-		} else if ctx.RustModule().InProduct() {
-			compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_product")
+func CommonDefaultCfgFlags(flags Flags, vendor bool, product bool) Flags {
+	var cfgs []string
+	if vendor || product {
+		cfgs = append(cfgs, "android_vndk")
+		if vendor {
+			cfgs = append(cfgs, "android_vendor")
+		} else if product {
+			cfgs = append(cfgs, "android_product")
 		}
 	}
 
-	flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...)
-	flags.RustdocFlags = append(flags.RustdocFlags, compiler.cfgsToFlags()...)
+	flags.RustFlags = append(flags.RustFlags, cfgsToFlags(cfgs)...)
+	flags.RustdocFlags = append(flags.RustdocFlags, cfgsToFlags(cfgs)...)
+	return flags
+}
+
+func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = CommonDefaultCfgFlags(flags, ctx.RustModule().InVendor(), ctx.RustModule().InProduct())
+
+	cfgFlags := cfgsToFlags(compiler.Properties.Cfgs.GetOrDefault(ctx, nil))
+	flags.RustFlags = append(flags.RustFlags, cfgFlags...)
+	flags.RustdocFlags = append(flags.RustdocFlags, cfgFlags...)
+
+	return flags
+}
+
+func CommonDefaultFlags(ctx android.ModuleContext, toolchain config.Toolchain, flags Flags) Flags {
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, toolchain.ToolchainRustFlags())
+	flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, toolchain.ToolchainLinkFlags())
+	flags.EmitXrefs = ctx.Config().EmitXrefRules()
+
+	if ctx.Host() && !ctx.Windows() {
+		flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...)
+	}
+
+	if ctx.Os() == android.Linux {
+		// Add -lc, -lrt, -ldl, -lpthread, -lm and -lgcc_s to glibc builds to match
+		// the default behavior of device builds.
+		flags.LinkFlags = append(flags.LinkFlags, config.LinuxHostGlobalLinkFlags...)
+	} else if ctx.Os() == android.Darwin {
+		// Add -lc, -ldl, -lpthread and -lm to glibc darwin builds to match the default
+		// behavior of device builds.
+		flags.LinkFlags = append(flags.LinkFlags,
+			"-lc",
+			"-ldl",
+			"-lpthread",
+			"-lm",
+		)
+	}
 	return flags
 }
 
 func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 
+	flags = CommonDefaultFlags(ctx, ctx.toolchain(), flags)
 	lintFlags, err := config.RustcLintsForDir(ctx.ModuleDir(), compiler.Properties.Lints)
 	if err != nil {
 		ctx.PropertyErrorf("lints", err.Error())
@@ -376,29 +450,7 @@
 	flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition())
 	flags.RustdocFlags = append(flags.RustdocFlags, "--edition="+compiler.edition())
 	flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...)
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags())
-	flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, ctx.toolchain().ToolchainLinkFlags())
-	flags.EmitXrefs = ctx.Config().EmitXrefRules()
 
-	if ctx.Host() && !ctx.Windows() {
-		flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...)
-	}
-
-	if ctx.Os() == android.Linux {
-		// Add -lc, -lrt, -ldl, -lpthread, -lm and -lgcc_s to glibc builds to match
-		// the default behavior of device builds.
-		flags.LinkFlags = append(flags.LinkFlags, config.LinuxHostGlobalLinkFlags...)
-	} else if ctx.Os() == android.Darwin {
-		// Add -lc, -ldl, -lpthread and -lm to glibc darwin builds to match the default
-		// behavior of device builds.
-		flags.LinkFlags = append(flags.LinkFlags,
-			"-lc",
-			"-ldl",
-			"-lpthread",
-			"-lm",
-		)
-	}
 	return flags
 }
 
@@ -548,11 +600,11 @@
 	compiler.installDeps = append(compiler.installDeps, installedData...)
 }
 
-func (compiler *baseCompiler) getStem(ctx ModuleContext) string {
+func (compiler *baseCompiler) getStem(ctx android.ModuleContext) string {
 	return compiler.getStemWithoutSuffix(ctx) + String(compiler.Properties.Suffix)
 }
 
-func (compiler *baseCompiler) getStemWithoutSuffix(ctx BaseModuleContext) string {
+func (compiler *baseCompiler) getStemWithoutSuffix(ctx android.BaseModuleContext) string {
 	stem := ctx.ModuleName()
 	if String(compiler.Properties.Stem) != "" {
 		stem = String(compiler.Properties.Stem)
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 89f4d1a..4caa12b 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -63,6 +63,35 @@
 	}
 }
 
+func TestLtoFlag(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_host {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			lto: {
+				thin: false,
+			}
+		}
+
+		rust_library_host {
+			name: "libfoo_lto",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}
+		`)
+
+	libfoo := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+	libfooLto := ctx.ModuleForTests("libfoo_lto", "linux_glibc_x86_64_dylib").Rule("rustc")
+
+	if strings.Contains(libfoo.Args["rustcFlags"], "-C lto=thin") {
+		t.Fatalf("libfoo expected to disable lto -- rustcFlags: %#v", libfoo.Args["rustcFlags"])
+	}
+	if !strings.Contains(libfooLto.Args["rustcFlags"], "-C lto=thin") {
+		t.Fatalf("libfoo expected to enable lto by default -- rustcFlags: %#v", libfooLto.Args["rustcFlags"])
+	}
+}
+
 // Test that we reject multiple source files.
 func TestEnforceSingleSourceFile(t *testing.T) {
 
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 6c021c7..9850570 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -54,7 +54,7 @@
 			strings.Join(rustFlags, " "))
 	}
 
-	ExportedVars.ExportStringListStaticVariable("DEVICE_ARM64_RUSTC_FLAGS", Arm64RustFlags)
+	pctx.StaticVariable("DEVICE_ARM64_RUSTC_FLAGS", strings.Join(Arm64RustFlags, " "))
 }
 
 type toolchainArm64 struct {
diff --git a/rust/config/arm_device.go b/rust/config/arm_device.go
index a5f4afb..5394e8a 100644
--- a/rust/config/arm_device.go
+++ b/rust/config/arm_device.go
@@ -44,7 +44,7 @@
 			strings.Join(rustFlags, " "))
 	}
 
-	ExportedVars.ExportStringListStaticVariable("DEVICE_ARM_RUSTC_FLAGS", ArmRustFlags)
+	pctx.StaticVariable("DEVICE_ARM_RUSTC_FLAGS", strings.Join(ArmRustFlags, " "))
 }
 
 type toolchainArm struct {
diff --git a/rust/config/darwin_host.go b/rust/config/darwin_host.go
index 03bea82..a4bc187 100644
--- a/rust/config/darwin_host.go
+++ b/rust/config/darwin_host.go
@@ -21,7 +21,7 @@
 )
 
 var (
-	DarwinRustFlags     = []string{}
+	DarwinRustFlags     = []string{"-C split-debuginfo=off"}
 	DarwinRustLinkFlags = []string{
 		"-B${cc_config.MacToolPath}",
 	}
diff --git a/rust/config/global.go b/rust/config/global.go
index e28dbaa..6943467 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -22,10 +22,9 @@
 )
 
 var (
-	pctx         = android.NewPackageContext("android/soong/rust/config")
-	ExportedVars = android.NewExportedVariables(pctx)
+	pctx = android.NewPackageContext("android/soong/rust/config")
 
-	RustDefaultVersion = "1.76.0"
+	RustDefaultVersion = "1.78.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
@@ -54,6 +53,9 @@
 		"--color=always",
 		"-Z dylib-lto",
 		"-Z link-native-libraries=no",
+
+		// cfg flag to indicate that we are building in AOSP with Soong
+		"--cfg soong",
 	}
 
 	LinuxHostGlobalLinkFlags = []string{
@@ -112,17 +114,17 @@
 
 	pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
 
-	ExportedVars.ExportStringStaticVariable("RUST_DEFAULT_VERSION", RustDefaultVersion)
-	ExportedVars.ExportStringListStaticVariable("GLOBAL_RUSTC_FLAGS", GlobalRustFlags)
-	ExportedVars.ExportStringListStaticVariable("LINUX_HOST_GLOBAL_LINK_FLAGS", LinuxHostGlobalLinkFlags)
+	pctx.StaticVariable("RUST_DEFAULT_VERSION", RustDefaultVersion)
+	pctx.StaticVariable("GLOBAL_RUSTC_FLAGS", strings.Join(GlobalRustFlags, " "))
+	pctx.StaticVariable("LINUX_HOST_GLOBAL_LINK_FLAGS", strings.Join(LinuxHostGlobalLinkFlags, " "))
 
-	ExportedVars.ExportStringListStaticVariable("DEVICE_GLOBAL_RUSTC_FLAGS", deviceGlobalRustFlags)
-	ExportedVars.ExportStringListStaticVariable("DEVICE_GLOBAL_LINK_FLAGS",
-		android.RemoveListFromList(deviceGlobalLinkFlags, []string{
+	pctx.StaticVariable("DEVICE_GLOBAL_RUSTC_FLAGS", strings.Join(deviceGlobalRustFlags, " "))
+	pctx.StaticVariable("DEVICE_GLOBAL_LINK_FLAGS",
+		strings.Join(android.RemoveListFromList(deviceGlobalLinkFlags, []string{
 			// The cc_config flags are retrieved from cc_toolchain by rust rules.
 			"${cc_config.DeviceGlobalLldflags}",
 			"-B${cc_config.ClangBin}",
-		}))
+		}), " "))
 }
 
 func HostPrebuiltTag(config android.Config) string {
diff --git a/rust/config/x86_64_device.go b/rust/config/x86_64_device.go
index 49f7c77..fee1923 100644
--- a/rust/config/x86_64_device.go
+++ b/rust/config/x86_64_device.go
@@ -54,7 +54,7 @@
 		pctx.StaticVariable("X86_64"+variant+"VariantRustFlags",
 			strings.Join(rustFlags, " "))
 	}
-	ExportedVars.ExportStringListStaticVariable("DEVICE_X86_64_RUSTC_FLAGS", x86_64RustFlags)
+	pctx.StaticVariable("DEVICE_X86_64_RUSTC_FLAGS", strings.Join(x86_64RustFlags, " "))
 }
 
 type toolchainX86_64 struct {
diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go
index 2a57e73..5d9d88a 100644
--- a/rust/config/x86_device.go
+++ b/rust/config/x86_device.go
@@ -56,7 +56,7 @@
 			strings.Join(rustFlags, " "))
 	}
 
-	ExportedVars.ExportStringListStaticVariable("DEVICE_X86_RUSTC_FLAGS", x86RustFlags)
+	pctx.StaticVariable("DEVICE_X86_RUSTC_FLAGS", strings.Join(x86RustFlags, " "))
 }
 
 type toolchainX86 struct {
diff --git a/rust/coverage.go b/rust/coverage.go
index bc6504d..e0e919c 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -39,13 +39,15 @@
 
 func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
 	if cov.Properties.NeedCoverageVariant {
-		ctx.AddVariationDependencies([]blueprint.Variation{
-			{Mutator: "link", Variation: "static"},
-		}, cc.CoverageDepTag, CovLibraryName)
+		if ctx.Device() {
+			ctx.AddVariationDependencies([]blueprint.Variation{
+				{Mutator: "link", Variation: "static"},
+			}, cc.CoverageDepTag, CovLibraryName)
+		}
 
 		// no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
 		if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
-			ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}, rlibDepTag, ProfilerBuiltins)
+			ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}, rlibDepTag, ProfilerBuiltins)
 		}
 	}
 
@@ -60,12 +62,14 @@
 
 	if cov.Properties.CoverageEnabled {
 		flags.Coverage = true
-		coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
 		flags.RustFlags = append(flags.RustFlags,
 			"-C instrument-coverage", "-g")
-		flags.LinkFlags = append(flags.LinkFlags,
-			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
-		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+		if ctx.Device() {
+			coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
+			flags.LinkFlags = append(flags.LinkFlags,
+				profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
+			deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+		}
 
 		// 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() {
diff --git a/rust/doc.go b/rust/doc.go
index 6970d79..fe20523 100644
--- a/rust/doc.go
+++ b/rust/doc.go
@@ -38,7 +38,7 @@
 		FlagWithArg("-D ", docDir.String())
 
 	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled() {
+		if !module.Enabled(ctx) {
 			return
 		}
 
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index ee28c6d..6cb8b93 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -114,6 +114,12 @@
 				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"],
@@ -122,11 +128,15 @@
 				name: "fuzz_static_libtest",
 				static_libs: ["libtest_fuzzing"],
 			}
-
+			cc_fuzz {
+				name: "fuzz_staticffi_libtest",
+				static_libs: ["libtest_fuzzing_static"],
+			}
 	`)
 
 	fuzz_shared_libtest := ctx.ModuleForTests("fuzz_shared_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
 	fuzz_static_libtest := ctx.ModuleForTests("fuzz_static_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
+	fuzz_staticffi_libtest := ctx.ModuleForTests("fuzz_staticffi_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface)
 
 	if !strings.Contains(fuzz_shared_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
 		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_shared ('libcc_transitive_dep'): %#v", fuzz_shared_libtest.FuzzSharedLibraries().String())
@@ -134,4 +144,7 @@
 	if !strings.Contains(fuzz_static_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
 		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_static ('libcc_transitive_dep'): %#v", fuzz_static_libtest.FuzzSharedLibraries().String())
 	}
+	if !strings.Contains(fuzz_staticffi_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") {
+		t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_rlib ('libcc_transitive_dep'): %#v", fuzz_staticffi_libtest.FuzzSharedLibraries().String())
+	}
 }
diff --git a/rust/image.go b/rust/image.go
index 530c56e..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.
@@ -197,29 +213,20 @@
 	return mod.InVendor() || mod.InProduct()
 }
 
-func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
-	m := module.(*Module)
+func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant string) {
 	if variant == android.VendorRamdiskVariation {
-		m.MakeAsPlatform()
+		mod.MakeAsPlatform()
 	} else if variant == android.RecoveryVariation {
-		m.MakeAsPlatform()
-	} else if strings.HasPrefix(variant, cc.VendorVariation) {
-		m.Properties.ImageVariation = cc.VendorVariation
+		mod.MakeAsPlatform()
+	} else if strings.HasPrefix(variant, android.VendorVariation) {
+		mod.Properties.ImageVariation = android.VendorVariation
 		if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
-			m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
+			mod.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
 		}
-
-		// Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
-		// Hide other vendor variants to avoid collision.
-		vndkVersion := ctx.DeviceConfig().VndkVersion()
-		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
-			m.Properties.HideFromMake = true
-			m.HideFromMake()
-		}
-	} else if strings.HasPrefix(variant, cc.ProductVariation) {
-		m.Properties.ImageVariation = cc.ProductVariation
+	} else if strings.HasPrefix(variant, android.ProductVariation) {
+		mod.Properties.ImageVariation = android.ProductVariation
 		if strings.HasPrefix(variant, cc.ProductVariationPrefix) {
-			m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.ProductVariationPrefix)
+			mod.Properties.VndkVersion = strings.TrimPrefix(variant, cc.ProductVariationPrefix)
 		}
 	}
 }
diff --git a/rust/image_test.go b/rust/image_test.go
index fb4d9c1..d84eb10 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -22,33 +22,47 @@
 	"android/soong/cc"
 )
 
-// Test that cc modules can link against vendor_available 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 := testRustVndk(t, `
+	ctx := testRust(t, `
 			cc_binary {
-				name: "fizz_vendor",
+				name: "fizz_vendor_available",
+				static_libs: [
+					"libfoo_vendor",
+					"libfoo_vendor_static"
+				],
+				vendor_available: true,
+			}
+			cc_binary {
+				name: "fizz_soc_specific",
 				static_libs: ["libfoo_vendor"],
 				soc_specific: true,
 			}
-			rust_ffi_static {
+			rust_ffi_rlib {
 				name: "libfoo_vendor",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
 				vendor_available: true,
 			}
+			rust_ffi_static {
+				name: "libfoo_vendor_static",
+				crate_name: "foo",
+				srcs: ["foo.rs"],
+				vendor_available: true,
+			}
 		`)
 
-	vendorBinary := ctx.ModuleForTests("fizz_vendor", "android_vendor.29_arm64_armv8-a").Module().(*cc.Module)
+	vendorBinary := ctx.ModuleForTests("fizz_vendor_available", "android_vendor_arm64_armv8-a").Module().(*cc.Module)
 
-	if !android.InList("libfoo_vendor.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("vendorBinary should have a dependency on libfoo_vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
+	if android.InList("libfoo_vendor_static.vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("vendorBinary should not have a staticlib dependency on libfoo_vendor_static.vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs)
 	}
 }
 
 // Test that variants which use the vndk emit the appropriate cfg flag.
-func TestImageVndkCfgFlag(t *testing.T) {
-	ctx := testRustVndk(t, `
-			rust_ffi_static {
+func TestImageCfgFlag(t *testing.T) {
+	ctx := testRust(t, `
+			rust_ffi_shared {
 				name: "libfoo",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
@@ -57,7 +71,7 @@
 			}
 		`)
 
-	vendor := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_static").Rule("rustc")
+	vendor := ctx.ModuleForTests("libfoo", "android_vendor_arm64_armv8-a_shared").Rule("rustc")
 
 	if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
@@ -69,7 +83,7 @@
 		t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
 	}
 
-	product := ctx.ModuleForTests("libfoo", "android_product.29_arm64_armv8-a_static").Rule("rustc")
+	product := ctx.ModuleForTests("libfoo", "android_product_arm64_armv8-a_shared").Rule("rustc")
 	if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
 	}
@@ -80,7 +94,7 @@
 		t.Errorf("missing \"--cfg 'android_product'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
 	}
 
-	system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Rule("rustc")
+	system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("rustc")
 	if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vndk'") {
 		t.Errorf("unexpected \"--cfg 'android_vndk'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"])
 	}
@@ -93,33 +107,42 @@
 
 }
 
-// Test that cc modules can link against vendor_ramdisk_available rust_ffi_static libraries.
+// Test that cc modules can link against vendor_ramdisk_available rust_ffi_rlib and rust_ffi_static libraries.
 func TestVendorRamdiskLinkage(t *testing.T) {
-	ctx := testRustVndk(t, `
-			cc_library_static {
+	ctx := testRust(t, `
+			cc_library_shared {
 				name: "libcc_vendor_ramdisk",
-				static_libs: ["libfoo_vendor_ramdisk"],
+				static_libs: [
+					"libfoo_vendor_ramdisk",
+					"libfoo_static_vendor_ramdisk"
+				],
 				system_shared_libs: [],
 				vendor_ramdisk_available: true,
 			}
-			rust_ffi_static {
+			rust_ffi_rlib {
 				name: "libfoo_vendor_ramdisk",
 				crate_name: "foo",
 				srcs: ["foo.rs"],
 				vendor_ramdisk_available: true,
 			}
+			rust_ffi_static {
+				name: "libfoo_static_vendor_ramdisk",
+				crate_name: "foo",
+				srcs: ["foo.rs"],
+				vendor_ramdisk_available: true,
+			}
 		`)
 
-	vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_static").Module().(*cc.Module)
+	vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_shared").Module().(*cc.Module)
 
-	if !android.InList("libfoo_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
-		t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_vendor_ramdisk")
+	if android.InList("libfoo_static_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) {
+		t.Errorf("libcc_vendor_ramdisk should not have a dependency on the libfoo_static_vendor_ramdisk static library")
 	}
 }
 
 // Test that prebuilt libraries cannot be made vendor available.
 func TestForbiddenVendorLinkage(t *testing.T) {
-	testRustVndkError(t, "Rust prebuilt modules not supported for non-system images.", `
+	testRustError(t, "Rust prebuilt modules not supported for non-system images.", `
 		rust_prebuilt_library {
 			name: "librust_prebuilt",
 			crate_name: "rust_prebuilt",
diff --git a/rust/library.go b/rust/library.go
index 7f004fc..ba73f27 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -37,10 +37,15 @@
 	android.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
 	android.RegisterModuleType("rust_ffi", RustFFIFactory)
 	android.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
-	android.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)
+	android.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory)
 	android.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
 	android.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
-	android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
+	android.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory)
+
+	// TODO: Remove when all instances of rust_ffi_static have been switched to rust_ffi_rlib
+	// Alias rust_ffi_static to the rust_ffi_rlib factory
+	android.RegisterModuleType("rust_ffi_static", RustFFIRlibFactory)
+	android.RegisterModuleType("rust_ffi_host_static", RustFFIRlibHostFactory)
 }
 
 type VariantLibraryProperties struct {
@@ -54,9 +59,13 @@
 	Shared VariantLibraryProperties `android:"arch_variant"`
 	Static VariantLibraryProperties `android:"arch_variant"`
 
-	// path to include directories to pass to cc_* modules, only relevant for static/shared variants.
+	// TODO: Remove this when all instances of Include_dirs have been removed from rust_ffi modules.
+	// path to include directories to pass to cc_* modules, only relevant for static/shared variants (deprecated, use export_include_dirs instead).
 	Include_dirs []string `android:"path,arch_variant"`
 
+	// path to include directories to export to cc_* modules, only relevant for static/shared variants.
+	Export_include_dirs []string `android:"path,arch_variant"`
+
 	// Whether this library is part of the Rust toolchain sysroot.
 	Sysroot *bool
 }
@@ -77,8 +86,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"`
 
@@ -100,7 +107,7 @@
 	includeDirs       android.Paths
 	sourceProvider    SourceProvider
 
-	collectedSnapshotHeaders android.Paths
+	isFFI bool
 
 	// table-of-contents file for cdylib crates to optimize out relinking when possible
 	tocFile android.OptionalPath
@@ -141,6 +148,8 @@
 	BuildOnlyShared()
 
 	toc() android.OptionalPath
+
+	isFFILibrary() bool
 }
 
 func (library *libraryDecorator) nativeCoverage() bool {
@@ -168,7 +177,7 @@
 }
 
 func (library *libraryDecorator) static() bool {
-	return library.MutatedProperties.VariantIsStatic
+	return false
 }
 
 func (library *libraryDecorator) source() bool {
@@ -194,14 +203,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
 }
 
@@ -218,17 +225,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() {
@@ -248,7 +251,7 @@
 }
 
 func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
-	if library.static() || library.MutatedProperties.VariantIsStaticStd {
+	if library.static() || library.MutatedProperties.VariantIsStaticStd || (library.rlib() && library.isFFILibrary()) {
 		return RlibLinkage
 	} else if library.baseCompiler.preferRlib() {
 		return RlibLinkage
@@ -268,8 +271,8 @@
 	return module.Init()
 }
 
-// rust_ffi produces all FFI variants (rust_ffi_shared and
-// rust_ffi_static).
+// rust_ffi produces all FFI variants (rust_ffi_shared, rust_ffi_static, and
+// rust_ffi_rlib).
 func RustFFIFactory() android.Module {
 	module, library := NewRustLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyFFI()
@@ -298,14 +301,6 @@
 	return module.Init()
 }
 
-// rust_ffi_static produces a static library (Rust crate type
-// "staticlib").
-func RustFFIStaticFactory() android.Module {
-	module, library := NewRustLibrary(android.HostAndDeviceSupported)
-	library.BuildOnlyStatic()
-	return module.Init()
-}
-
 // rust_library_host produces all Rust variants for the host
 // (rust_library_dylib_host and rust_library_rlib_host).
 func RustLibraryHostFactory() android.Module {
@@ -315,7 +310,7 @@
 }
 
 // rust_ffi_host produces all FFI variants for the host
-// (rust_ffi_static_host and rust_ffi_shared_host).
+// (rust_ffi_rlib_host, rust_ffi_static_host, and rust_ffi_shared_host).
 func RustFFIHostFactory() android.Module {
 	module, library := NewRustLibrary(android.HostSupported)
 	library.BuildOnlyFFI()
@@ -338,14 +333,6 @@
 	return module.Init()
 }
 
-// rust_ffi_static_host produces a static library for the host (Rust
-// crate type "staticlib").
-func RustFFIStaticHostFactory() android.Module {
-	module, library := NewRustLibrary(android.HostSupported)
-	library.BuildOnlyStatic()
-	return module.Init()
-}
-
 // rust_ffi_shared_host produces an shared library for the host (Rust
 // crate type "cdylib").
 func RustFFISharedHostFactory() android.Module {
@@ -354,11 +341,33 @@
 	return module.Init()
 }
 
+// rust_ffi_rlib_host produces an rlib for the host (Rust crate
+// type "rlib").
+func RustFFIRlibHostFactory() android.Module {
+	module, library := NewRustLibrary(android.HostSupported)
+	library.BuildOnlyRlib()
+
+	library.isFFI = true
+	return module.Init()
+}
+
+// rust_ffi_rlib produces an rlib (Rust crate type "rlib").
+func RustFFIRlibFactory() android.Module {
+	module, library := NewRustLibrary(android.HostAndDeviceSupported)
+	library.BuildOnlyRlib()
+
+	library.isFFI = true
+	return module.Init()
+}
+
 func (library *libraryDecorator) BuildOnlyFFI() {
 	library.MutatedProperties.BuildDylib = false
-	library.MutatedProperties.BuildRlib = false
+	// we build rlibs for later static ffi linkage.
+	library.MutatedProperties.BuildRlib = true
 	library.MutatedProperties.BuildShared = true
-	library.MutatedProperties.BuildStatic = true
+	library.MutatedProperties.BuildStatic = false
+
+	library.isFFI = true
 }
 
 func (library *libraryDecorator) BuildOnlyRust() {
@@ -387,6 +396,8 @@
 	library.MutatedProperties.BuildDylib = false
 	library.MutatedProperties.BuildShared = false
 	library.MutatedProperties.BuildStatic = true
+
+	library.isFFI = true
 }
 
 func (library *libraryDecorator) BuildOnlyShared() {
@@ -394,6 +405,12 @@
 	library.MutatedProperties.BuildDylib = false
 	library.MutatedProperties.BuildStatic = false
 	library.MutatedProperties.BuildShared = true
+
+	library.isFFI = true
+}
+
+func (library *libraryDecorator) isFFILibrary() bool {
+	return library.isFFI
 }
 
 func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
@@ -444,17 +461,35 @@
 	return library.getStem(ctx) + ctx.toolchain().SharedLibSuffix()
 }
 
+// Library cfg flags common to all variants
+func CommonLibraryCfgFlags(ctx android.ModuleContext, flags Flags) Flags {
+	return flags
+}
+
 func (library *libraryDecorator) cfgFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = library.baseCompiler.cfgFlags(ctx, flags)
+	flags = CommonLibraryCfgFlags(ctx, flags)
+
+	cfgs := library.baseCompiler.Properties.Cfgs.GetOrDefault(ctx, nil)
+
 	if library.dylib() {
 		// We need to add a dependency on std in order to link crates as dylibs.
 		// The hack to add this dependency is guarded by the following cfg so
 		// that we don't force a dependency when it isn't needed.
-		library.baseCompiler.Properties.Cfgs = append(library.baseCompiler.Properties.Cfgs, "android_dylib")
+		cfgs = append(cfgs, "android_dylib")
 	}
 
-	flags.RustFlags = append(flags.RustFlags, library.baseCompiler.cfgsToFlags()...)
-	flags.RustdocFlags = append(flags.RustdocFlags, library.baseCompiler.cfgsToFlags()...)
+	cfgFlags := cfgsToFlags(cfgs)
+
+	flags.RustFlags = append(flags.RustFlags, cfgFlags...)
+	flags.RustdocFlags = append(flags.RustdocFlags, cfgFlags...)
+
+	return flags
+}
+
+// Common flags applied to all libraries irrespective of properties or variant should be included here
+func CommonLibraryCompilerFlags(ctx android.ModuleContext, flags Flags) Flags {
+	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName())
 
 	return flags
 }
@@ -462,10 +497,13 @@
 func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = library.baseCompiler.compilerFlags(ctx, flags)
 
-	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName())
-	if library.shared() || library.static() {
+	flags = CommonLibraryCompilerFlags(ctx, flags)
+
+	if library.isFFI {
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...)
+		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Export_include_dirs)...)
 	}
+
 	if library.shared() {
 		if ctx.Darwin() {
 			flags.LinkFlags = append(
@@ -491,6 +529,9 @@
 		deps.srcProviderFiles = append(deps.srcProviderFiles, library.sourceProvider.Srcs()...)
 	}
 
+	// Ensure link dirs are not duplicated
+	deps.linkDirs = android.FirstUniqueStrings(deps.linkDirs)
+
 	// Calculate output filename
 	if library.rlib() {
 		fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix()
@@ -546,9 +587,10 @@
 		library.flagExporter.exportLinkObjects(deps.linkObjects...)
 	}
 
-	if library.static() || library.shared() {
+	// Since we have FFI rlibs, we need to collect their includes as well
+	if library.static() || library.shared() || library.rlib() {
 		android.SetProvider(ctx, cc.FlagExporterInfoProvider, cc.FlagExporterInfo{
-			IncludeDirs: library.includeDirs,
+			IncludeDirs: android.FirstUniquePaths(library.includeDirs),
 		})
 	}
 
@@ -663,6 +705,11 @@
 		return
 	}
 
+	// Don't produce rlib/dylib/source variants for shared or static variants
+	if library.shared() || library.static() {
+		return
+	}
+
 	var variants []string
 	// The source variant is used for SourceProvider modules. The other variants (i.e. rlib and dylib)
 	// depend on this variant. It must be the first variant to be declared.
@@ -685,10 +732,13 @@
 
 	// The order of the variations (modules) matches the variant names provided. Iterate
 	// through the new variation modules and set their mutated properties.
+	var emptyVariant = false
+	var rlibVariant = false
 	for i, v := range modules {
 		switch variants[i] {
 		case rlibVariation:
 			v.(*Module).compiler.(libraryInterface).setRlib()
+			rlibVariant = true
 		case dylibVariation:
 			v.(*Module).compiler.(libraryInterface).setDylib()
 			if v.(*Module).ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
@@ -702,15 +752,26 @@
 			// The source variant does not produce any library.
 			// Disable the compilation steps.
 			v.(*Module).compiler.SetDisabled()
+		case "":
+			emptyVariant = true
 		}
 	}
 
+	if rlibVariant && library.isFFILibrary() {
+		// If an rlib variant is set and this is an FFI library, make it the
+		// default variant so CC can link against it appropriately.
+		mctx.AliasVariation(rlibVariation)
+	} else if emptyVariant {
+		// If there's an empty variant, alias it so it is the default variant
+		mctx.AliasVariation("")
+	}
+
 	// 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() {
+			if !v.Enabled(mctx) {
 				continue
 			}
 			mctx.AddInterVariantDependency(sourceDepTag, v, sv)
@@ -726,73 +787,31 @@
 		case libraryInterface:
 			// Only create a variant if a library is actually being built.
 			if library.rlib() && !library.sysroot() {
-				variants := []string{"rlib-std", "dylib-std"}
-				modules := mctx.CreateLocalVariations(variants...)
+				// If this is a rust_ffi variant it only needs rlib-std
+				if library.isFFILibrary() {
+					variants := []string{"rlib-std"}
+					modules := mctx.CreateLocalVariations(variants...)
+					rlib := modules[0].(*Module)
+					rlib.compiler.(libraryInterface).setRlibStd()
+					rlib.Properties.RustSubName += RlibStdlibSuffix
+					mctx.AliasVariation("rlib-std")
+				} else {
+					variants := []string{"rlib-std", "dylib-std"}
+					modules := mctx.CreateLocalVariations(variants...)
 
-				rlib := modules[0].(*Module)
-				dylib := modules[1].(*Module)
-				rlib.compiler.(libraryInterface).setRlibStd()
-				dylib.compiler.(libraryInterface).setDylibStd()
-				if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
-					// TODO(b/165791368)
-					// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
-					// variants are properly supported.
-					dylib.Disable()
-				}
-				rlib.Properties.RustSubName += RlibStdlibSuffix
-			}
-		}
-	}
-}
-
-func (l *libraryDecorator) snapshotHeaders() android.Paths {
-	if l.collectedSnapshotHeaders == nil {
-		panic("snapshotHeaders() must be called after collectHeadersForSnapshot()")
-	}
-	return l.collectedSnapshotHeaders
-}
-
-// collectHeadersForSnapshot collects all exported headers from library.
-// It globs header files in the source tree for exported include directories,
-// and tracks generated header files separately.
-//
-// This is to be called from GenerateAndroidBuildActions, and then collected
-// header files can be retrieved by snapshotHeaders().
-func (l *libraryDecorator) collectHeadersForSnapshot(ctx android.ModuleContext, deps PathDeps) {
-	ret := android.Paths{}
-
-	// Glob together the headers from the modules include_dirs property
-	for _, path := range android.CopyOfPaths(l.includeDirs) {
-		dir := path.String()
-		globDir := dir + "/**/*"
-		glob, err := ctx.GlobWithDeps(globDir, nil)
-		if err != nil {
-			ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
-			return
-		}
-
-		for _, header := range glob {
-			// Filter out only the files with extensions that are headers.
-			found := false
-			for _, ext := range cc.HeaderExts {
-				if strings.HasSuffix(header, ext) {
-					found = true
-					break
+					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
 				}
 			}
-			if !found {
-				continue
-			}
-			ret = append(ret, android.PathForSource(ctx, header))
 		}
 	}
-
-	// Glob together the headers from C dependencies as well, starting with non-generated headers.
-	ret = append(ret, cc.GlobHeadersForSnapshot(ctx, append(android.CopyOfPaths(deps.depIncludePaths), deps.depSystemIncludePaths...))...)
-
-	// Collect generated headers from C dependencies.
-	ret = append(ret, cc.GlobGeneratedHeadersForSnapshot(ctx, deps.depGeneratedHeaders)...)
-
-	// TODO(185577950): If support for generated headers is added, they need to be collected here as well.
-	l.collectedSnapshotHeaders = ret
 }
diff --git a/rust/library_test.go b/rust/library_test.go
index e03074d..35a420c 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -34,18 +34,22 @@
 			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.
 	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
-	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc")
+	libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Rule("rustc")
 
 	rlibCrateType := "rlib"
 	dylibCrateType := "dylib"
 	sharedCrateType := "cdylib"
-	staticCrateType := "staticlib"
 
 	// Test crate type for rlib is correct.
 	if !strings.Contains(libfooRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) {
@@ -57,9 +61,9 @@
 		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"])
 	}
 
 	// Test crate type for C shared libraries is correct.
@@ -188,19 +192,19 @@
 			crate_name: "foo",
 		}`)
 
-	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std")
 
 	if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) {
-		t.Errorf("Static libstd rlib expected to be a dependency of Rust static libraries. Rlib deps are: %#v",
+		t.Errorf("Static libstd rlib expected to be a dependency of Rust rlib libraries. Rlib deps are: %#v",
 			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
 	}
 }
 
 func TestNativeDependencyOfRlib(t *testing.T) {
 	ctx := testRust(t, `
-		rust_ffi_static {
-			name: "libffi_static",
-			crate_name: "ffi_static",
+		rust_ffi_rlib {
+			name: "libffi_rlib",
+			crate_name: "ffi_rlib",
 			rlibs: ["librust_rlib"],
 			srcs: ["foo.rs"],
 		}
@@ -208,27 +212,27 @@
 			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,
-		ffiStatic,
+		ffiRlib,
 	}
 
 	// librust_rlib specifies -L flag to cc deps output directory on rustc command
@@ -239,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"],
 			)
 		}
 	}
@@ -286,31 +290,40 @@
 				"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")
-	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static")
+	libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std")
 	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared")
 
-	for _, static := range []android.TestingModule{libfooRlib, libfooStatic} {
+	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")
+			t.Errorf("libbar not present as rlib dependency in static lib: %s", static.Module().Name())
 		}
 		if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) {
-			t.Errorf("libbar present as dynamic dependency in static lib")
+			t.Errorf("libbar present as dynamic dependency in static lib: %s", static.Module().Name())
 		}
 	}
 
 	for _, dyn := range []android.TestingModule{libfooDylib, libfooShared} {
 		if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) {
-			t.Errorf("libbar not present as dynamic dependency in dynamic lib")
+			t.Errorf("libbar not present as dynamic dependency in dynamic lib: %s", dyn.Module().Name())
 		}
 		if android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("libbar present as rlib dependency in dynamic lib")
+			t.Errorf("libbar present as rlib dependency in dynamic lib: %s", dyn.Module().Name())
 		}
 		if !android.InList("librlib_only", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
-			t.Errorf("librlib_only should be selected by rustlibs as an rlib.")
+			t.Errorf("librlib_only should be selected by rustlibs as an rlib: %s.", dyn.Module().Name())
 		}
 	}
 }
@@ -361,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"],
@@ -374,7 +393,7 @@
 	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.
 	libbarRlibStd := ctx.ModuleForTests("libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module)
@@ -392,14 +411,33 @@
 	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("libstd", libbarFFIRlib.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_ffi_rlib 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("libfoo.rlib-std", libbarFFIRlib.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_ffi_rlib does not link dependent rustlib rlib-std variant")
 	}
 	if !android.InList("libstd", libbarRlibStd.Properties.AndroidMkRlibs) {
 		t.Errorf("rust_ffi with prefer_rlib does not link libstd as an rlib")
 	}
 
 }
+
+func TestRustFFIExportedIncludes(t *testing.T) {
+	ctx := testRust(t, `
+		rust_ffi {
+			name: "libbar",
+			srcs: ["foo.rs"],
+			crate_name: "bar",
+			export_include_dirs: ["rust_includes"],
+			host_supported: true,
+		}
+		cc_library_static {
+			name: "libfoo",
+			srcs: ["foo.cpp"],
+			shared_libs: ["libbar"],
+			host_supported: true,
+		}`)
+	libfooStatic := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_static").Rule("cc")
+	android.AssertStringDoesContain(t, "cFlags for lib module", libfooStatic.Args["cFlags"], " -Irust_includes ")
+}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index b491449..1ff6637 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -76,6 +76,7 @@
 	srcPath := crateRootPath(ctx, procMacro)
 	ret := TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile)
 	procMacro.baseCompiler.unstrippedOutputFile = outputFile
+
 	return ret
 }
 
diff --git a/rust/project_json.go b/rust/project_json.go
index ad9b690..24dcc89 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -96,7 +96,7 @@
 		var childId int
 		cInfo, known := singleton.knownCrates[rChild.Name()]
 		if !known {
-			childId, ok = singleton.addCrate(ctx, rChild, make(map[string]int))
+			childId, ok = singleton.addCrate(ctx, rChild)
 			if !ok {
 				return
 			}
@@ -119,7 +119,7 @@
 	if !ok {
 		return nil, false
 	}
-	if !rModule.Enabled() {
+	if !rModule.Enabled(ctx) {
 		return nil, false
 	}
 	return rModule, true
@@ -128,7 +128,8 @@
 // addCrate adds a crate to singleton.project.Crates ensuring that required
 // dependencies are also added. It returns the index of the new crate in
 // singleton.project.Crates
-func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContext, rModule *Module, deps map[string]int) (int, bool) {
+func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContext, rModule *Module) (int, bool) {
+	deps := make(map[string]int)
 	rootModule, err := rModule.compiler.checkedCrateRootPath()
 	if err != nil {
 		return 0, false
@@ -180,7 +181,7 @@
 	if cInfo, ok := singleton.knownCrates[module.Name()]; ok {
 		// If we have a new device variant, override the old one
 		if !cInfo.Device && rModule.Device() {
-			singleton.addCrate(ctx, rModule, cInfo.Deps)
+			singleton.addCrate(ctx, rModule)
 			return
 		}
 		crate := singleton.project.Crates[cInfo.Idx]
@@ -188,7 +189,7 @@
 		singleton.project.Crates[cInfo.Idx] = crate
 		return
 	}
-	singleton.addCrate(ctx, rModule, make(map[string]int))
+	singleton.addCrate(ctx, rModule)
 }
 
 func (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
diff --git a/rust/protobuf.go b/rust/protobuf.go
index 0b26b80..fab5259 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"strconv"
 	"strings"
 
 	"android/soong/android"
@@ -122,41 +123,65 @@
 	// stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
 	var outputs android.WritablePaths
 
-	rule := android.NewRuleBuilder(pctx, ctx)
+	for i, shard := range android.ShardPaths(protoFiles, 50) {
+		rule := android.NewRuleBuilder(pctx, ctx)
 
-	for _, protoFile := range protoFiles {
-		// Since we're iterating over the protoFiles already, make sure they're not redeclared in grpcFiles
-		if android.InList(protoFile.String(), grpcFiles.Strings()) {
-			ctx.PropertyErrorf("protos",
-				"A proto can only be added once to either grpc_protos or protos. %q is declared in both properties",
-				protoFile.String())
+		for _, protoFile := range shard {
+			// Since we're iterating over the protoFiles already, make sure they're not redeclared in grpcFiles
+			if android.InList(protoFile.String(), grpcFiles.Strings()) {
+				ctx.PropertyErrorf("protos",
+					"A proto can only be added once to either grpc_protos or protos. %q is declared in both properties",
+					protoFile.String())
+			}
+
+			protoName := strings.TrimSuffix(protoFile.Base(), ".proto")
+			proto.protoNames = append(proto.protoNames, protoName)
+
+			protoOut := android.PathForModuleOut(ctx, protoName+".rs")
+			depFile := android.PathForModuleOut(ctx, protoName+".d")
+
+			ruleOutputs := android.WritablePaths{protoOut, depFile}
+
+			android.ProtoRule(rule, protoFile, protoFlags, protoFlags.Deps, outDir, depFile, ruleOutputs)
+			outputs = append(outputs, ruleOutputs...)
 		}
 
-		protoName := strings.TrimSuffix(protoFile.Base(), ".proto")
-		proto.protoNames = append(proto.protoNames, protoName)
+		ruleName := "protoc"
+		ruleDesc := "protoc"
+		if i > 0 {
+			ruleName += "_" + strconv.Itoa(i+1)
+			ruleDesc += " " + strconv.Itoa(i+1)
+		}
 
-		protoOut := android.PathForModuleOut(ctx, protoName+".rs")
-		depFile := android.PathForModuleOut(ctx, protoName+".d")
-
-		ruleOutputs := android.WritablePaths{protoOut, depFile}
-
-		android.ProtoRule(rule, protoFile, protoFlags, protoFlags.Deps, outDir, depFile, ruleOutputs)
-		outputs = append(outputs, ruleOutputs...)
+		rule.Build(ruleName, ruleDesc)
 	}
 
-	for _, grpcFile := range grpcFiles {
-		grpcName := strings.TrimSuffix(grpcFile.Base(), ".proto")
-		proto.grpcNames = append(proto.grpcNames, grpcName)
+	for i, shard := range android.ShardPaths(grpcFiles, 50) {
+		rule := android.NewRuleBuilder(pctx, ctx)
 
-		// GRPC protos produce two files, a proto.rs and a proto_grpc.rs
-		protoOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+".rs"))
-		grpcOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+grpcSuffix+".rs"))
-		depFile := android.PathForModuleOut(ctx, grpcName+".d")
+		for _, grpcFile := range shard {
+			grpcName := strings.TrimSuffix(grpcFile.Base(), ".proto")
+			proto.grpcNames = append(proto.grpcNames, grpcName)
 
-		ruleOutputs := android.WritablePaths{protoOut, grpcOut, depFile}
+			// GRPC protos produce two files, a proto.rs and a proto_grpc.rs
+			protoOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+".rs"))
+			grpcOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+grpcSuffix+".rs"))
+			depFile := android.PathForModuleOut(ctx, grpcName+".d")
 
-		android.ProtoRule(rule, grpcFile, grpcProtoFlags, grpcProtoFlags.Deps, outDir, depFile, ruleOutputs)
-		outputs = append(outputs, ruleOutputs...)
+			ruleOutputs := android.WritablePaths{protoOut, grpcOut, depFile}
+
+			android.ProtoRule(rule, grpcFile, grpcProtoFlags, grpcProtoFlags.Deps, outDir, depFile, ruleOutputs)
+			outputs = append(outputs, ruleOutputs...)
+		}
+
+		ruleName := "protoc_grpc"
+		ruleDesc := "protoc grpc"
+		if i > 0 {
+			ruleName += "_" + strconv.Itoa(i+1)
+			ruleDesc += " " + strconv.Itoa(i+1)
+		}
+
+		rule.Build(ruleName, ruleDesc)
 	}
 
 	// Check that all proto base filenames are unique as outputs are written to the same directory.
@@ -168,8 +193,6 @@
 
 	android.WriteFileRule(ctx, stemFile, proto.genModFileContents())
 
-	rule.Build("protoc_"+ctx.ModuleName(), "protoc "+ctx.ModuleName())
-
 	// stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
 	proto.BaseSourceProvider.OutputFiles = append(android.Paths{stemFile}, outputs.Paths()...)
 
diff --git a/rust/rust.go b/rust/rust.go
index 668dd8f..9dae75e 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"strconv"
 	"strings"
 
 	"android/soong/bloaty"
@@ -30,7 +31,6 @@
 	"android/soong/fuzz"
 	"android/soong/multitree"
 	"android/soong/rust/config"
-	"android/soong/snapshot"
 )
 
 var pctx = android.NewPackageContext("android/soong/rust")
@@ -69,6 +69,7 @@
 	AndroidMkDylibs        []string `blueprint:"mutated"`
 	AndroidMkProcMacroLibs []string `blueprint:"mutated"`
 	AndroidMkStaticLibs    []string `blueprint:"mutated"`
+	AndroidMkHeaderLibs    []string `blueprint:"mutated"`
 
 	ImageVariation string `blueprint:"mutated"`
 	VndkVersion    string `blueprint:"mutated"`
@@ -80,6 +81,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"`
@@ -158,6 +161,8 @@
 	sourceProvider   SourceProvider
 	subAndroidMkOnce map[SubAndroidMkProvider]bool
 
+	exportedLinkDirs []string
+
 	// Output file to be installed, may be stripped or unstripped.
 	outputFile android.OptionalPath
 
@@ -172,9 +177,6 @@
 	apexSdkVersion android.ApiLevel
 
 	transitiveAndroidMkSharedLibs *android.DepSet[string]
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
 }
 
 func (mod *Module) Header() bool {
@@ -207,35 +209,14 @@
 	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 ""
 }
 
 func (mod *Module) NonCcVariants() bool {
 	if mod.compiler != nil {
-		if _, ok := mod.compiler.(libraryInterface); ok {
-			return false
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.buildRlib() || library.buildDylib()
 		}
 	}
 	panic(fmt.Errorf("NonCcVariants called on non-library module: %q", mod.BaseModuleName()))
@@ -342,27 +323,10 @@
 	return Bool(mod.Properties.Bootstrap)
 }
 
-func (mod *Module) MustUseVendorVariant() bool {
-	return true
-}
-
 func (mod *Module) SubName() string {
 	return mod.Properties.SubName
 }
 
-func (mod *Module) IsVndk() bool {
-	// TODO(b/165791368)
-	return false
-}
-
-func (mod *Module) IsVndkExt() bool {
-	return false
-}
-
-func (mod *Module) IsVndkSp() bool {
-	return false
-}
-
 func (mod *Module) IsVndkPrebuiltLibrary() bool {
 	// Rust modules do not provide VNDK prebuilts
 	return false
@@ -385,10 +349,6 @@
 	return false
 }
 
-func (c *Module) IsLlndkPublic() bool {
-	return false
-}
-
 func (mod *Module) KernelHeadersDecorator() bool {
 	return false
 }
@@ -460,11 +420,16 @@
 	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
 
+	// exportedLinkDirs are exported linkDirs for direct rlib dependencies to
+	// cc_library_static dependants of rlibs.
+	// Track them separately from linkDirs so superfluous -L flags don't get emitted.
+	exportedLinkDirs []string
+
 	// Used by bindgen modules which call clang
 	depClangFlags         []string
 	depIncludePaths       android.Paths
@@ -497,6 +462,7 @@
 
 type flagExporter struct {
 	linkDirs    []string
+	ccLinkDirs  []string
 	linkObjects []string
 }
 
@@ -535,7 +501,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
 }
 
@@ -543,6 +509,10 @@
 	return mod.Properties.VndkVersion
 }
 
+func (mod *Module) ExportedCrateLinkDirs() []string {
+	return mod.exportedLinkDirs
+}
+
 func (mod *Module) PreventInstall() bool {
 	return mod.Properties.PreventInstall
 }
@@ -657,15 +627,6 @@
 	return nil
 }
 
-func (mod *Module) IncludeDirs() android.Paths {
-	if mod.compiler != nil {
-		if library, ok := mod.compiler.(*libraryDecorator); ok {
-			return library.includeDirs
-		}
-	}
-	panic(fmt.Errorf("IncludeDirs called on non-library module: %q", mod.BaseModuleName()))
-}
-
 func (mod *Module) SetStatic() {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
@@ -695,6 +656,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 {
@@ -914,6 +893,10 @@
 	}
 
 	deps := mod.depsToPaths(ctx)
+	// Export linkDirs for CC rust generatedlibs
+	mod.exportedLinkDirs = append(mod.exportedLinkDirs, deps.exportedLinkDirs...)
+	mod.exportedLinkDirs = append(mod.exportedLinkDirs, deps.linkDirs...)
+
 	flags := Flags{
 		Toolchain: toolchain,
 	}
@@ -971,14 +954,6 @@
 			ctx.CheckbuildFile(mod.docTimestampFile.Path())
 		}
 
-		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
-		// RECOVERY_SNAPSHOT_VERSION is current.
-		if lib, ok := mod.compiler.(snapshotLibraryInterface); ok {
-			if cc.ShouldCollectHeadersForSnapshot(ctx, mod, apexInfo) {
-				lib.collectHeadersForSnapshot(ctx, deps)
-			}
-		}
-
 		apexInfo, _ := android.ModuleProvider(actx, android.ApexInfoProvider)
 		if !proptools.BoolDefault(mod.Installable(), mod.EverInstallable()) && !mod.ProcMacro() {
 			// If the module has been specifically configure to not be installed then
@@ -999,6 +974,9 @@
 			if ctx.Failed() {
 				return
 			}
+			// Export your own directory as a linkDir
+			mod.exportedLinkDirs = append(mod.exportedLinkDirs, linkPathFromFilePath(mod.OutputFile().Path()))
+
 		}
 
 		ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
@@ -1007,7 +985,60 @@
 		android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 	}
 
-	android.CollectDependencyAconfigFiles(ctx, &mod.mergedAconfigFiles)
+	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 {
@@ -1076,7 +1107,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"}
@@ -1124,6 +1154,11 @@
 	return nil
 }
 
+func (mod *Module) Symlinks() []string {
+	// TODO update this to return the list of symlinks when Rust supports defining symlinks
+	return nil
+}
+
 func rustMakeLibName(ctx android.ModuleContext, c cc.LinkableInterface, dep cc.LinkableInterface, depName string) string {
 	if rustDep, ok := dep.(*Module); ok {
 		// Use base module name for snapshots when exporting to Makefile.
@@ -1142,6 +1177,7 @@
 		}
 	}
 }
+
 func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
 
@@ -1216,7 +1252,6 @@
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
-
 		if _, exists := skipModuleList[depName]; exists {
 			return
 		}
@@ -1225,12 +1260,12 @@
 			return
 		}
 
-		if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() {
+		if rustDep, ok := dep.(*Module); ok && !rustDep.Static() && !rustDep.Shared() {
 			//Handle Rust Modules
 			makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
 
-			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)
@@ -1240,8 +1275,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)
@@ -1251,14 +1285,30 @@
 				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName)
 				mod.Properties.SnapshotRlibs = append(mod.Properties.SnapshotRlibs, cc.BaseLibName(depName))
 
-			case procMacroDepTag:
+				// rust_ffi rlibs may export include dirs, so collect those here.
+				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
+				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
+				depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
+
+			case 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)
@@ -1283,12 +1333,12 @@
 				directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep)
 			}
 
+			exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
 			//Append the dependencies exportedDirs, except for proc-macros which target a different arch/OS
 			if depTag != procMacroDepTag {
-				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider)
-				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
 				depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...)
 				depPaths.linkObjects = append(depPaths.linkObjects, exportedInfo.LinkObjects...)
+				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
@@ -1298,6 +1348,7 @@
 					lib.exportLinkDirs(linkDir)
 				}
 			}
+
 			if depTag == sourceDepTag {
 				if _, ok := mod.sourceProvider.(*protobufDecorator); ok && mod.Source() {
 					if _, ok := rustDep.sourceProvider.(*protobufDecorator); ok {
@@ -1402,6 +1453,7 @@
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
+				mod.Properties.AndroidMkHeaderLibs = append(mod.Properties.AndroidMkHeaderLibs, makeLibName)
 			case depTag == cc.CrtBeginDepTag:
 				depPaths.CrtBegin = append(depPaths.CrtBegin, linkObject.Path())
 			case depTag == cc.CrtEndDepTag:
@@ -1433,16 +1485,29 @@
 	mod.transitiveAndroidMkSharedLibs = android.NewDepSet[string](android.PREORDER, directAndroidMkSharedLibs, transitiveAndroidMkSharedLibs)
 
 	var rlibDepFiles RustLibraries
+	aliases := mod.compiler.Aliases()
 	for _, dep := range directRlibDeps {
-		rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
+		crateName := dep.CrateName()
+		if alias, aliased := aliases[crateName]; aliased {
+			crateName = alias
+		}
+		rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName})
 	}
 	var dylibDepFiles RustLibraries
 	for _, dep := range directDylibDeps {
-		dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
+		crateName := dep.CrateName()
+		if alias, aliased := aliases[crateName]; aliased {
+			crateName = alias
+		}
+		dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName})
 	}
 	var procMacroDepFiles RustLibraries
 	for _, dep := range directProcMacroDeps {
-		procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
+		crateName := dep.CrateName()
+		if alias, aliased := aliases[crateName]; aliased {
+			crateName = alias
+		}
+		procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName})
 	}
 
 	var staticLibDepFiles android.Paths
@@ -1463,7 +1528,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 {
@@ -1530,7 +1595,6 @@
 
 	deps := mod.deps(ctx)
 	var commonDepVariations []blueprint.Variation
-	var snapshotInfo *cc.SnapshotInfo
 
 	apiImportInfo := cc.GetApiImports(mod, actx)
 	if mod.usePublicApi() || mod.useVendorApi() {
@@ -1540,7 +1604,7 @@
 	}
 
 	if ctx.Os() == android.Android {
-		deps.SharedLibs, _ = cc.RewriteLibs(mod, &snapshotInfo, actx, ctx.Config(), deps.SharedLibs)
+		deps.SharedLibs, _ = cc.FilterNdkLibs(mod, ctx.Config(), deps.SharedLibs)
 	}
 
 	stdLinkage := "dylib-std"
@@ -1549,6 +1613,7 @@
 	}
 
 	rlibDepVariations := commonDepVariations
+	rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 
 	if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() {
 		rlibDepVariations = append(rlibDepVariations,
@@ -1559,15 +1624,15 @@
 	rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: rlibVariation})
 	for _, lib := range deps.Rlibs {
 		depTag := rlibDepTag
-		lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
-
 		actx.AddVariationDependencies(rlibDepVariations, depTag, lib)
 	}
 
 	// dylibs
 	dylibDepVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: dylibVariation})
+	dylibDepVariations = append(dylibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""})
+
 	for _, lib := range deps.Dylibs {
-		addDylibDependency(actx, lib, mod, &snapshotInfo, dylibDepVariations, dylibDepTag)
+		actx.AddVariationDependencies(dylibDepVariations, dylibDepTag, lib)
 	}
 
 	// rustlibs
@@ -1577,31 +1642,35 @@
 				autoDep := mod.compiler.(autoDeppable).autoDep(ctx)
 				if autoDep.depTag == rlibDepTag {
 					// Handle the rlib deptag case
-					addRlibDependency(actx, lib, mod, &snapshotInfo, rlibDepVariations)
+					actx.AddVariationDependencies(rlibDepVariations, rlibDepTag, lib)
+
 				} else {
 					// autoDep.depTag is a dylib depTag. Not all rustlibs may be available as a dylib however.
 					// Check for the existence of the dylib deptag variant. Select it if available,
 					// 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)
 
-					replacementLib := cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Dylibs)
-
-					if actx.OtherModuleDependencyVariantExists(autoDepVariations, replacementLib) {
-						addDylibDependency(actx, lib, mod, &snapshotInfo, autoDepVariations, autoDep.depTag)
 					} else {
 						// If there's no dylib dependency available, try to add the rlib dependency instead.
-						addRlibDependency(actx, lib, mod, &snapshotInfo, rlibDepVariations)
+						actx.AddVariationDependencies(rlibDepVariations, rlibDepTag, lib)
+
 					}
 				}
 			}
 		} else if _, ok := mod.sourceProvider.(*protobufDecorator); ok {
 			for _, lib := range deps.Rustlibs {
-				replacementLib := cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Dylibs)
 				srcProviderVariations := append(commonDepVariations,
 					blueprint.Variation{Mutator: "rust_libraries", Variation: "source"})
+				srcProviderVariations = append(srcProviderVariations, blueprint.Variation{Mutator: "link", Variation: ""})
 
-				if actx.OtherModuleDependencyVariantExists(srcProviderVariations, replacementLib) {
+				// Only add rustlib dependencies if they're source providers themselves.
+				// This is used to track which crate names need to be added to the source generated
+				// in the rust_protobuf mod.rs.
+				if actx.OtherModuleDependencyVariantExists(srcProviderVariations, lib) {
 					actx.AddVariationDependencies(srcProviderVariations, sourceDepTag, lib)
 				}
 			}
@@ -1612,13 +1681,13 @@
 	if deps.Stdlibs != nil {
 		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
 			for _, lib := range deps.Stdlibs {
-				lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
-				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
+				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}...),
 					rlibDepTag, lib)
 			}
 		} else {
 			for _, lib := range deps.Stdlibs {
-				addDylibDependency(actx, lib, mod, &snapshotInfo, dylibDepVariations, dylibDepTag)
+				actx.AddVariationDependencies(dylibDepVariations, dylibDepTag, lib)
+
 			}
 		}
 	}
@@ -1643,7 +1712,6 @@
 
 	for _, lib := range deps.WholeStaticLibs {
 		depTag := cc.StaticDepTag(true)
-		lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -1652,7 +1720,6 @@
 
 	for _, lib := range deps.StaticLibs {
 		depTag := cc.StaticDepTag(false)
-		lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -1663,12 +1730,10 @@
 
 	crtVariations := cc.GetCrtVariations(ctx, mod)
 	for _, crt := range deps.CrtBegin {
-		actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag,
-			cc.GetReplaceModuleName(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
+		actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag, crt)
 	}
 	for _, crt := range deps.CrtEnd {
-		actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag,
-			cc.GetReplaceModuleName(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
+		actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag, crt)
 	}
 
 	if mod.sourceProvider != nil {
@@ -1691,19 +1756,8 @@
 	mod.afdo.addDep(ctx, actx)
 }
 
-// addRlibDependency will add an rlib dependency, rewriting to the snapshot library if available.
-func addRlibDependency(actx android.BottomUpMutatorContext, lib string, mod *Module, snapshotInfo **cc.SnapshotInfo, variations []blueprint.Variation) {
-	lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, snapshotInfo, actx).Rlibs)
-	actx.AddVariationDependencies(variations, rlibDepTag, lib)
-}
-
-func addDylibDependency(actx android.BottomUpMutatorContext, lib string, mod *Module, snapshotInfo **cc.SnapshotInfo, variations []blueprint.Variation, depTag dependencyTag) {
-	lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, snapshotInfo, actx).Dylibs)
-	actx.AddVariationDependencies(variations, depTag, lib)
-}
-
 func BeginMutator(ctx android.BottomUpMutatorContext) {
-	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() {
+	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled(ctx) {
 		mod.beginMutator(ctx)
 	}
 }
@@ -1733,7 +1787,6 @@
 }
 
 var _ android.HostToolProvider = (*Module)(nil)
-var _ snapshot.RelativeInstallPath = (*Module)(nil)
 
 func (mod *Module) HostToolPath() android.OptionalPath {
 	if !mod.Host() {
@@ -1862,5 +1915,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 d609c2f..0d005d0 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -37,11 +37,7 @@
 
 	genrule.PrepareForTestWithGenRuleBuildComponents,
 
-	PrepareForTestWithRustIncludeVndk,
-	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.DeviceVndkVersion = StringPtr("current")
-		variables.Platform_vndk_version = StringPtr("29")
-	}),
+	PrepareForIntegrationTestWithRust,
 )
 
 var rustMockedFiles = android.MockFS{
@@ -73,60 +69,21 @@
 	return result.TestContext
 }
 
-func testRustVndk(t *testing.T, bp string) *android.TestContext {
-	return testRustVndkFs(t, bp, rustMockedFiles)
-}
-
 const (
-	sharedVendorVariant        = "android_vendor.29_arm64_armv8-a_shared"
-	rlibVendorVariant          = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
-	rlibDylibStdVendorVariant  = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
-	dylibVendorVariant         = "android_vendor.29_arm64_armv8-a_dylib"
+	sharedVendorVariant        = "android_vendor_arm64_armv8-a_shared"
+	rlibVendorVariant          = "android_vendor_arm64_armv8-a_rlib_rlib-std"
+	rlibDylibStdVendorVariant  = "android_vendor_arm64_armv8-a_rlib_rlib-std"
+	dylibVendorVariant         = "android_vendor_arm64_armv8-a_dylib"
 	sharedRecoveryVariant      = "android_recovery_arm64_armv8-a_shared"
 	rlibRecoveryVariant        = "android_recovery_arm64_armv8-a_rlib_dylib-std"
 	rlibRlibStdRecoveryVariant = "android_recovery_arm64_armv8-a_rlib_rlib-std"
 	dylibRecoveryVariant       = "android_recovery_arm64_armv8-a_dylib"
 	binaryCoreVariant          = "android_arm64_armv8-a"
-	binaryVendorVariant        = "android_vendor.29_arm64_armv8-a"
-	binaryProductVariant       = "android_product.29_arm64_armv8-a"
+	binaryVendorVariant        = "android_vendor_arm64_armv8-a"
+	binaryProductVariant       = "android_product_arm64_armv8-a"
 	binaryRecoveryVariant      = "android_recovery_arm64_armv8-a"
 )
 
-func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
-	return testRustVndkFsVersions(t, bp, fs, "current", "current", "29")
-}
-
-func testRustVndkFsVersions(t *testing.T, bp string, fs android.MockFS, device_version, product_version, vndk_version string) *android.TestContext {
-	skipTestIfOsNotSupported(t)
-	result := android.GroupFixturePreparers(
-		prepareForRustTest,
-		fs.AddToFixture(),
-		android.FixtureModifyProductVariables(
-			func(variables android.FixtureProductVariables) {
-				variables.DeviceVndkVersion = StringPtr(device_version)
-				variables.Platform_vndk_version = StringPtr(vndk_version)
-			},
-		),
-	).RunTestWithBp(t, bp)
-	return result.TestContext
-}
-
-func testRustRecoveryFsVersions(t *testing.T, bp string, fs android.MockFS, device_version, vndk_version, recovery_version string) *android.TestContext {
-	skipTestIfOsNotSupported(t)
-	result := android.GroupFixturePreparers(
-		prepareForRustTest,
-		fs.AddToFixture(),
-		android.FixtureModifyProductVariables(
-			func(variables android.FixtureProductVariables) {
-				variables.DeviceVndkVersion = StringPtr(device_version)
-				variables.RecoverySnapshotVersion = StringPtr(recovery_version)
-				variables.Platform_vndk_version = StringPtr(vndk_version)
-			},
-		),
-	).RunTestWithBp(t, bp)
-	return result.TestContext
-}
-
 // testRustCov returns a TestContext in which a basic environment has been
 // setup. This environment explicitly enables coverage.
 func testRustCov(t *testing.T, bp string) *android.TestContext {
@@ -158,27 +115,6 @@
 		RunTestWithBp(t, bp)
 }
 
-// testRustVndkError is similar to testRustError, but can be used to test VNDK-related errors.
-func testRustVndkError(t *testing.T, pattern string, bp string) {
-	testRustVndkFsError(t, pattern, bp, rustMockedFiles)
-}
-
-func testRustVndkFsError(t *testing.T, pattern string, bp string, fs android.MockFS) {
-	skipTestIfOsNotSupported(t)
-	android.GroupFixturePreparers(
-		prepareForRustTest,
-		fs.AddToFixture(),
-		android.FixtureModifyProductVariables(
-			func(variables android.FixtureProductVariables) {
-				variables.DeviceVndkVersion = StringPtr("current")
-				variables.Platform_vndk_version = StringPtr("VER")
-			},
-		),
-	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
-		RunTestWithBp(t, bp)
-}
-
 // testRustCtx is used to build a particular test environment. Unless your
 // tests requires a specific setup, prefer the wrapping functions: testRust,
 // testRustCov or testRustError.
@@ -214,15 +150,11 @@
 			host_supported: true,
 			name: "cc_stubs_dep",
 		}
-		rust_ffi_host_static {
+		cc_library_host_static {
 			name: "libstatic",
-			srcs: ["foo.rs"],
-			crate_name: "static",
 		}
-		rust_ffi_host_static {
+		cc_library_host_static {
 			name: "libwholestatic",
-			srcs: ["foo.rs"],
-			crate_name: "wholestatic",
 		}
 		rust_ffi_host_shared {
 			name: "libshared",
@@ -470,6 +402,144 @@
 	m.Output("libwaldo.dylib.so.bloaty.csv")
 }
 
+// Test that aliases are respected.
+func TestRustAliases(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library {
+			name: "libbar",
+			crate_name: "bar",
+			srcs: ["src/lib.rs"],
+		}
+		rust_library {
+			name: "libbaz",
+			crate_name: "baz",
+			srcs: ["src/lib.rs"],
+		}
+		rust_binary {
+			name: "foo",
+			srcs: ["src/main.rs"],
+			rustlibs: ["libbar", "libbaz"],
+			aliases: ["bar:bar_renamed"],
+		}`)
+
+	fooRustc := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
+	if !strings.Contains(fooRustc.Args["libFlags"], "--extern bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so") {
+		t.Errorf("--extern bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
+	}
+	if !strings.Contains(fooRustc.Args["libFlags"], "--extern baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so") {
+		t.Errorf("--extern baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
+	}
+}
+
+func TestRustRlibs(t *testing.T) {
+	ctx := testRust(t, `
+		rust_ffi_rlib {
+			name: "libbar",
+			crate_name: "bar",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["bar_includes"]
+		}
+
+		rust_ffi_rlib {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["foo_includes"]
+		}
+
+		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_libs: ["libbar"],
+		}
+
+		cc_library_static {
+			name: "libcc_static",
+			srcs:["foo.c"],
+			static_libs: ["libbuzz"],
+			whole_static_libs: ["libfoo"],
+		}
+
+		cc_binary {
+			name: "ccBin",
+			srcs:["foo.c"],
+			static_libs: ["libcc_static", "libbar"],
+		}
+		`)
+
+	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc")
+	libcc_shared_rustc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc")
+	libcc_shared_ld := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("ld")
+	libcc_shared_cc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("cc")
+	ccbin_rustc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("rustc")
+	ccbin_ld := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("ld")
+	ccbin_cc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("cc")
+
+	if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"])
+	}
+
+	// Make sure there's a rustc command, and it's producing a staticlib
+	if !strings.Contains(libcc_shared_rustc.Args["rustcFlags"], "crate-type=staticlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v",
+			"staticlib", libcc_shared_rustc.Args["rustcFlags"])
+	}
+
+	// Make sure the static lib is included in the ld command
+	if !strings.Contains(libcc_shared_ld.Args["libFlags"], "generated_rust_staticlib/liblibcc_shared_rust_staticlib.a") {
+		t.Errorf("missing generated static library in linker step libFlags %#v, libFlags: %#v",
+			"libcc_shared.generated_rust_staticlib.a", libcc_shared_ld.Args["libFlags"])
+	}
+
+	// Make sure the static lib includes are in the cc command
+	if !strings.Contains(libcc_shared_cc.Args["cFlags"], "-Ibar_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ibar_includes", libcc_shared_cc.Args["cFlags"])
+	}
+
+	// Make sure there's a rustc command, and it's producing a staticlib
+	if !strings.Contains(ccbin_rustc.Args["rustcFlags"], "crate-type=staticlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "staticlib", ccbin_rustc.Args["rustcFlags"])
+	}
+
+	// Make sure the static lib is included in the cc command
+	if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/libccBin_rust_staticlib.a") {
+		t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v",
+			"ccBin.generated_rust_staticlib.a", ccbin_ld.Args["libFlags"])
+	}
+
+	// Make sure the static lib includes are in the ld command
+	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ibar_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ibar_includes", ccbin_cc.Args)
+	}
+
+	// Make sure that direct dependencies and indirect whole static dependencies are
+	// propagating correctly to the generated rlib.
+	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern foo=") {
+		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"])
+	}
+
+	// Test indirect includes propagation
+	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ifoo_includes", ccbin_cc.Args)
+	}
+}
+
 func assertString(t *testing.T, got, expected string) {
 	t.Helper()
 	if got != expected {
diff --git a/rust/sanitize.go b/rust/sanitize.go
index 3c08cd8..c086880 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -258,7 +258,7 @@
 
 func rustSanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
 	if mod, ok := mctx.Module().(*Module); ok && mod.sanitize != nil {
-		if !mod.Enabled() {
+		if !mod.Enabled(mctx) {
 			return
 		}
 
@@ -267,12 +267,6 @@
 			if Bool(mod.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
 				noteDep = "note_memtag_heap_sync"
 			}
-			// If we're using snapshots, redirect to snapshot whenever possible
-			// TODO(b/178470649): clean manual snapshot redirections
-			snapshot, _ := android.ModuleProvider(mctx, cc.SnapshotInfoProvider)
-			if lib, ok := snapshot.StaticLibs[noteDep]; ok {
-				noteDep = lib
-			}
 			depTag := cc.StaticDepTag(true)
 			variations := append(mctx.Target().Variations(),
 				blueprint.Variation{Mutator: "link", Variation: "static"})
diff --git a/rust/snapshot_prebuilt.go b/rust/snapshot_prebuilt.go
deleted file mode 100644
index 42e3cef..0000000
--- a/rust/snapshot_prebuilt.go
+++ /dev/null
@@ -1,208 +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 rust
-
-import (
-	"fmt"
-
-	"android/soong/android"
-	"android/soong/cc"
-
-	"github.com/google/blueprint/proptools"
-)
-
-type snapshotLibraryDecorator struct {
-	cc.BaseSnapshotDecorator
-	*libraryDecorator
-	properties          cc.SnapshotLibraryProperties
-	sanitizerProperties struct {
-		SanitizerVariation cc.SanitizerType `blueprint:"mutated"`
-
-		//TODO: Library flags for cfi variant when CFI is supported.
-		//Cfi cc.SnapshotLibraryProperties `android:"arch_variant"`
-
-		// Library flags for hwasan variant.
-		Hwasan cc.SnapshotLibraryProperties `android:"arch_variant"`
-	}
-}
-
-var _ cc.SnapshotSanitizer = (*snapshotLibraryDecorator)(nil)
-
-func (library *snapshotLibraryDecorator) IsSanitizerAvailable(t cc.SanitizerType) bool {
-	switch t {
-	//TODO: When CFI is supported, add a check here as well
-	case cc.Hwasan:
-		return library.sanitizerProperties.Hwasan.Src != nil
-	default:
-		return false
-	}
-}
-
-func (library *snapshotLibraryDecorator) SetSanitizerVariation(t cc.SanitizerType, enabled bool) {
-	if !enabled || library.IsSanitizerEnabled(t) {
-		return
-	}
-	if !library.IsUnsanitizedVariant() {
-		panic(fmt.Errorf("snapshot Sanitizer must be one of Cfi or Hwasan but not both"))
-	}
-	library.sanitizerProperties.SanitizerVariation = t
-}
-
-func (library *snapshotLibraryDecorator) IsSanitizerEnabled(t cc.SanitizerType) bool {
-	return library.sanitizerProperties.SanitizerVariation == t
-}
-
-func (library *snapshotLibraryDecorator) IsUnsanitizedVariant() bool {
-	//TODO: When CFI is supported, add a check here as well
-	return !library.IsSanitizerEnabled(cc.Hwasan)
-}
-
-func init() {
-	registerRustSnapshotModules(android.InitRegistrationContext)
-}
-
-func (mod *Module) IsSnapshotSanitizerAvailable(t cc.SanitizerType) bool {
-	if ss, ok := mod.compiler.(cc.SnapshotSanitizer); ok {
-		return ss.IsSanitizerAvailable(t)
-	}
-	return false
-}
-
-func (mod *Module) SetSnapshotSanitizerVariation(t cc.SanitizerType, enabled bool) {
-	if ss, ok := mod.compiler.(cc.SnapshotSanitizer); ok {
-		ss.SetSanitizerVariation(t, enabled)
-	} else {
-		panic(fmt.Errorf("Calling SetSnapshotSanitizerVariation on a non-snapshotLibraryDecorator: %s", mod.Name()))
-	}
-}
-
-func (mod *Module) IsSnapshotUnsanitizedVariant() bool {
-	if ss, ok := mod.compiler.(cc.SnapshotSanitizer); ok {
-		return ss.IsUnsanitizedVariant()
-	}
-	return false
-}
-
-func (mod *Module) IsSnapshotSanitizer() bool {
-	if _, ok := mod.compiler.(cc.SnapshotSanitizer); ok {
-		return true
-	}
-	return false
-}
-
-func registerRustSnapshotModules(ctx android.RegistrationContext) {
-	cc.VendorSnapshotImageSingleton.RegisterAdditionalModule(ctx,
-		"vendor_snapshot_rlib", VendorSnapshotRlibFactory)
-	cc.VendorSnapshotImageSingleton.RegisterAdditionalModule(ctx,
-		"vendor_snapshot_dylib", VendorSnapshotDylibFactory)
-	cc.RecoverySnapshotImageSingleton.RegisterAdditionalModule(ctx,
-		"recovery_snapshot_rlib", RecoverySnapshotRlibFactory)
-}
-
-func snapshotLibraryFactory(image cc.SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
-	module, library := NewRustLibrary(android.DeviceSupported)
-
-	module.sanitize = nil
-	library.stripper.StripProperties.Strip.None = proptools.BoolPtr(true)
-
-	prebuilt := &snapshotLibraryDecorator{
-		libraryDecorator: library,
-	}
-
-	module.compiler = prebuilt
-
-	prebuilt.Init(module, image, moduleSuffix)
-	module.AddProperties(
-		&prebuilt.properties,
-		&prebuilt.sanitizerProperties,
-	)
-
-	return module, prebuilt
-}
-
-func (library *snapshotLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
-	var variant string
-	if library.static() {
-		variant = cc.SnapshotStaticSuffix
-	} else if library.shared() {
-		variant = cc.SnapshotSharedSuffix
-	} else if library.rlib() {
-		variant = cc.SnapshotRlibSuffix
-	} else if library.dylib() {
-		variant = cc.SnapshotDylibSuffix
-	}
-
-	library.SetSnapshotAndroidMkSuffix(ctx, variant)
-
-	if library.IsSanitizerEnabled(cc.Hwasan) {
-		library.properties = library.sanitizerProperties.Hwasan
-	}
-	if !library.MatchesWithDevice(ctx.DeviceConfig()) {
-		return buildOutput{}
-	}
-	outputFile := android.PathForModuleSrc(ctx, *library.properties.Src)
-	library.unstrippedOutputFile = outputFile
-	return buildOutput{outputFile: outputFile}
-}
-
-func (library *snapshotLibraryDecorator) rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath {
-	return android.OptionalPath{}
-}
-
-// vendor_snapshot_rlib is a special prebuilt rlib library which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_rlib
-// overrides the vendor variant of the rust rlib library with the same name, if BOARD_VNDK_VERSION
-// is set.
-func VendorSnapshotRlibFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(cc.VendorSnapshotImageSingleton, cc.SnapshotRlibSuffix)
-	prebuilt.libraryDecorator.BuildOnlyRlib()
-	prebuilt.libraryDecorator.setNoStdlibs()
-	return module.Init()
-}
-
-// vendor_snapshot_dylib is a special prebuilt dylib library which is auto-generated by
-// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_dylib
-// overrides the vendor variant of the rust dylib library with the same name, if BOARD_VNDK_VERSION
-// is set.
-func VendorSnapshotDylibFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(cc.VendorSnapshotImageSingleton, cc.SnapshotDylibSuffix)
-	prebuilt.libraryDecorator.BuildOnlyDylib()
-	prebuilt.libraryDecorator.setNoStdlibs()
-	return module.Init()
-}
-
-func RecoverySnapshotRlibFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(cc.RecoverySnapshotImageSingleton, cc.SnapshotRlibSuffix)
-	prebuilt.libraryDecorator.BuildOnlyRlib()
-	prebuilt.libraryDecorator.setNoStdlibs()
-	return module.Init()
-}
-
-func (library *snapshotLibraryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
-	arches := config.Arches()
-	if len(arches) == 0 || arches[0].ArchType.String() != library.Arch() {
-		return false
-	}
-	if library.properties.Src == nil {
-		return false
-	}
-	return true
-}
-
-func (library *snapshotLibraryDecorator) IsSnapshotPrebuilt() bool {
-	return true
-}
-
-var _ cc.SnapshotInterface = (*snapshotLibraryDecorator)(nil)
diff --git a/rust/snapshot_utils.go b/rust/snapshot_utils.go
deleted file mode 100644
index 55c85e6..0000000
--- a/rust/snapshot_utils.go
+++ /dev/null
@@ -1,81 +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 rust
-
-import (
-	"android/soong/android"
-)
-
-// snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots.
-type snapshotLibraryInterface interface {
-	libraryInterface
-
-	// collectHeadersForSnapshot is called in GenerateAndroidBuildActions for snapshot aware
-	// modules (See isSnapshotAware below).
-	// This function should gather all headers needed for snapshot.
-	collectHeadersForSnapshot(ctx android.ModuleContext, deps PathDeps)
-
-	// snapshotHeaders should return collected headers by collectHeadersForSnapshot.
-	// Calling snapshotHeaders before collectHeadersForSnapshot is an error.
-	snapshotHeaders() android.Paths
-}
-
-func (mod *Module) ExcludeFromVendorSnapshot() bool {
-	return Bool(mod.Properties.Exclude_from_vendor_snapshot)
-}
-
-func (mod *Module) ExcludeFromRecoverySnapshot() bool {
-	return Bool(mod.Properties.Exclude_from_recovery_snapshot)
-}
-
-func (mod *Module) IsSnapshotLibrary() bool {
-	if lib, ok := mod.compiler.(libraryInterface); ok {
-		return lib.shared() || lib.static() || lib.rlib() || lib.dylib()
-	}
-	return false
-}
-
-func (mod *Module) SnapshotRuntimeLibs() []string {
-	// TODO Rust does not yet support a runtime libs notion similar to CC
-	return []string{}
-}
-
-func (mod *Module) SnapshotSharedLibs() []string {
-	return mod.Properties.SnapshotSharedLibs
-}
-
-func (mod *Module) SnapshotStaticLibs() []string {
-	return mod.Properties.SnapshotStaticLibs
-}
-
-func (mod *Module) SnapshotRlibs() []string {
-	return mod.Properties.SnapshotRlibs
-}
-
-func (mod *Module) SnapshotDylibs() []string {
-	return mod.Properties.SnapshotDylibs
-}
-
-func (mod *Module) Symlinks() []string {
-	// TODO update this to return the list of symlinks when Rust supports defining symlinks
-	return nil
-}
-
-func (m *Module) SnapshotHeaders() android.Paths {
-	if l, ok := m.compiler.(snapshotLibraryInterface); ok {
-		return l.snapshotHeaders()
-	}
-	return android.Paths{}
-}
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..dc796c8 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(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(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 d9cacdc..6ee49a9 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -43,26 +43,34 @@
 // Preparer that will allow use of all rust modules fully.
 var PrepareForIntegrationTestWithRust = android.GroupFixturePreparers(
 	PrepareForTestWithRustDefaultModules,
-)
-
-var PrepareForTestWithRustIncludeVndk = android.GroupFixturePreparers(
-	PrepareForIntegrationTestWithRust,
-	cc.PrepareForTestWithCcIncludeVndk,
+	cc.PrepareForIntegrationTestWithCc,
 )
 
 func GatherRequiredDepsForTest() string {
 	bp := `
 		rust_prebuilt_library {
-				name: "libstd",
-				crate_name: "std",
-				rlib: {
-					srcs: ["libstd.rlib"],
-				},
-				dylib: {
-					srcs: ["libstd.so"],
-				},
-				host_supported: true,
-				sysroot: true,
+			name: "libstd",
+			crate_name: "std",
+			rlib: {
+				srcs: ["libstd/libstd.rlib"],
+			},
+			dylib: {
+				srcs: ["libstd/libstd.so"],
+			},
+			host_supported: true,
+			sysroot: true,
+		}
+		rust_prebuilt_library {
+			name: "libcore.sysroot",
+			crate_name: "core",
+			rlib: {
+				srcs: ["libcore/libcore.rlib"],
+			},
+			dylib: {
+				srcs: ["libcore/libcore.so"],
+			},
+			host_supported: true,
+			sysroot: true,
 		}
 		//////////////////////////////
 		// Device module requirements
@@ -180,10 +188,12 @@
 	ctx.RegisterModuleType("rust_fuzz_host", RustFuzzHostFactory)
 	ctx.RegisterModuleType("rust_ffi", RustFFIFactory)
 	ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory)
-	ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory)
+	ctx.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory)
+	ctx.RegisterModuleType("rust_ffi_static", RustFFIRlibFactory)
 	ctx.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
 	ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
-	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
+	ctx.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory)
+	ctx.RegisterModuleType("rust_ffi_host_static", RustFFIRlibHostFactory)
 	ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
 	ctx.RegisterModuleType("rust_protobuf", RustProtobufFactory)
 	ctx.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
@@ -201,5 +211,4 @@
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
 	})
-	registerRustSnapshotModules(ctx)
 }
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
deleted file mode 100644
index 7ebe66b..0000000
--- a/rust/vendor_snapshot_test.go
+++ /dev/null
@@ -1,1573 +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 rust
-
-import (
-	"fmt"
-	"path/filepath"
-	"reflect"
-	"strings"
-	"testing"
-
-	"android/soong/android"
-	"android/soong/cc"
-)
-
-func TestVendorSnapshotCapture(t *testing.T) {
-	bp := `
-	rust_ffi {
-		name: "libffivendor_available",
-		crate_name: "ffivendor_available",
-		srcs: ["lib.rs"],
-		vendor_available: true,
-		include_dirs: ["rust_headers/"],
-	}
-
-	rust_ffi {
-		name: "libffivendor",
-		crate_name: "ffivendor",
-		srcs: ["lib.rs"],
-		vendor: true,
-		include_dirs: ["rust_headers/"],
-	}
-
-	rust_library {
-		name: "librustvendor_available",
-		crate_name: "rustvendor_available",
-		srcs: ["lib.rs"],
-		vendor_available: true,
-	}
-
-	rust_library {
-		name: "librustvendor",
-		crate_name: "rustvendor",
-		srcs: ["lib.rs"],
-		vendor: true,
-	}
-
-	rust_binary {
-		name: "vendor_available_bin",
-		vendor_available: true,
-		srcs: ["srcs/lib.rs"],
-	}
-
-	rust_binary {
-		name: "vendor_bin",
-		vendor: true,
-		srcs: ["srcs/lib.rs"],
-	}
-    `
-	skipTestIfOsNotSupported(t)
-	result := android.GroupFixturePreparers(
-		prepareForRustTest,
-		rustMockedFiles.AddToFixture(),
-		android.FixtureModifyProductVariables(
-			func(variables android.FixtureProductVariables) {
-				variables.DeviceVndkVersion = StringPtr("current")
-				variables.Platform_vndk_version = StringPtr("29")
-			},
-		),
-	).RunTestWithBp(t, bp)
-	ctx := result.TestContext
-
-	// Check Vendor snapshot output.
-
-	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-	var jsonFiles []string
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-		[]string{"arm", "armv7-a-neon"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		// For shared libraries, only non-VNDK vendor_available modules are captured
-		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor_available", "libffivendor_available.so", sharedDir, sharedVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(sharedDir, "libffivendor_available.so.json"))
-
-		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
-		staticVariant := fmt.Sprintf("android_vendor.29_%s_%s_static", archType, archVariant)
-		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor_available", "libffivendor_available.a", staticDir, staticVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor", "libffivendor.a", staticDir, staticVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(staticDir, "libffivendor_available.a.json"))
-		jsonFiles = append(jsonFiles,
-			filepath.Join(staticDir, "libffivendor.a.json"))
-
-		// For rlib libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
-		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_dylib-std", archType, archVariant)
-		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib", rlibDir, rlibVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor", "librustvendor.rlib", rlibDir, rlibVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(rlibDir, "librustvendor_available.rlib.json"))
-		jsonFiles = append(jsonFiles,
-			filepath.Join(rlibDir, "librustvendor.rlib.json"))
-
-		// For rlib libraries, all rlib-std variants vendor:true and vendor_available modules (including VNDK) are captured.
-		rlibStdVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib-std.rlib", rlibDir, rlibStdVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor", "librustvendor.rlib-std.rlib", rlibDir, rlibStdVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(rlibDir, "librustvendor_available.rlib.json"))
-		jsonFiles = append(jsonFiles,
-			filepath.Join(rlibDir, "librustvendor.rlib.json"))
-
-		// For dylib libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
-		dylibVariant := fmt.Sprintf("android_vendor.29_%s_%s_dylib", archType, archVariant)
-		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.dylib.so", dylibDir, dylibVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor", "librustvendor.dylib.so", dylibDir, dylibVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(dylibDir, "librustvendor_available.dylib.so.json"))
-		jsonFiles = append(jsonFiles,
-			filepath.Join(dylibDir, "librustvendor.dylib.so.json"))
-
-		// For binary executables, all vendor:true and vendor_available modules are captured.
-		if archType == "arm64" {
-			binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
-			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
-			cc.CheckSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
-			cc.CheckSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
-			jsonFiles = append(jsonFiles,
-				filepath.Join(binaryDir, "vendor_available_bin.json"))
-			jsonFiles = append(jsonFiles,
-				filepath.Join(binaryDir, "vendor_bin.json"))
-		}
-	}
-
-	for _, jsonFile := range jsonFiles {
-		// verify all json files exist
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("%q expected but not found; #%v", jsonFile, jsonFiles)
-		}
-	}
-
-	// fake snapshot should have all outputs in the normal snapshot.
-	fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
-
-	for _, output := range snapshotSingleton.AllOutputs() {
-		fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
-		if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
-			t.Errorf("%q expected but not found", fakeOutput)
-		}
-	}
-}
-
-func TestVendorSnapshotDirected(t *testing.T) {
-	bp := `
-	rust_ffi_shared {
-		name: "libffivendor_available",
-		crate_name: "ffivendor_available",
-		srcs: ["lib.rs"],
-		vendor_available: true,
-	}
-
-	rust_library {
-		name: "librustvendor_available",
-		crate_name: "rustvendor_available",
-		srcs: ["lib.rs"],
-		vendor_available: true,
-	}
-
-	rust_ffi_shared {
-		name: "libffivendor_exclude",
-		crate_name: "ffivendor_exclude",
-		srcs: ["lib.rs"],
-		vendor_available: true,
-	}
-
-	rust_library {
-		name: "librustvendor_exclude",
-		crate_name: "rustvendor_exclude",
-		srcs: ["lib.rs"],
-		vendor_available: true,
-	}
-`
-	ctx := testRustVndk(t, bp)
-	ctx.Config().TestProductVariables.VendorSnapshotModules = make(map[string]bool)
-	ctx.Config().TestProductVariables.VendorSnapshotModules["librustvendor_available"] = true
-	ctx.Config().TestProductVariables.VendorSnapshotModules["libffivendor_available"] = true
-	ctx.Config().TestProductVariables.DirectedVendorSnapshot = true
-
-	// Check Vendor snapshot output.
-
-	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-
-	var includeJsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-		[]string{"arm", "armv7-a-neon"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
-		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_dylib-std", archType, archVariant)
-		rlibRlibStdVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
-		dylibVariant := fmt.Sprintf("android_vendor.29_%s_%s_dylib", archType, archVariant)
-		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
-
-		// Included modules
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib", rlibDir, rlibVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.dylib.so", dylibDir, dylibVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor_available", "libffivendor_available.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_available.rlib.json"))
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_available.rlib-std.rlib.json"))
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(dylibDir, "librustvendor_available.dylib.so.json"))
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libffivendor_available.so.json"))
-
-		// Excluded modules. Modules not included in the directed vendor snapshot
-		// are still include as fake modules.
-		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librustvendor_exclude", "librustvendor_exclude.rlib", rlibDir, rlibVariant)
-		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librustvendor_exclude", "librustvendor_exclude.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librustvendor_exclude", "librustvendor_exclude.dylib.so", dylibDir, dylibVariant)
-		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "libffivendor_exclude", "libffivendor_exclude.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_exclude.rlib.json"))
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_exclude.rlib-std.rlib.json"))
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(dylibDir, "librustvendor_exclude.dylib.so.json"))
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libffivendor_exclude.so.json"))
-	}
-
-	// Verify that each json file for an included module has a rule.
-	for _, jsonFile := range includeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("include json file %q not found", jsonFile)
-		}
-	}
-}
-
-func TestVendorSnapshotExclude(t *testing.T) {
-
-	// This test verifies that the exclude_from_vendor_snapshot property
-	// makes its way from the Android.bp source file into the module data
-	// structure. It also verifies that modules are correctly included or
-	// excluded in the vendor snapshot based on their path (framework or
-	// vendor) and the exclude_from_vendor_snapshot property.
-
-	frameworkBp := `
-		rust_ffi_shared {
-			name: "libinclude",
-			crate_name: "include",
-			srcs: ["include.rs"],
-			vendor_available: true,
-		}
-
-		rust_ffi_shared {
-			name: "libexclude",
-			crate_name: "exclude",
-			srcs: ["exclude.rs"],
-			vendor: true,
-			exclude_from_vendor_snapshot: true,
-		}
-
-		rust_ffi_shared {
-			name: "libavailable_exclude",
-			crate_name: "available_exclude",
-			srcs: ["lib.rs"],
-			vendor_available: true,
-			exclude_from_vendor_snapshot: true,
-		}
-
-		rust_library {
-			name: "librust_include",
-			crate_name: "rust_include",
-			srcs: ["include.rs"],
-			vendor_available: true,
-		}
-
-		rust_library {
-			name: "librust_exclude",
-			crate_name: "rust_exclude",
-			srcs: ["exclude.rs"],
-			vendor: true,
-			exclude_from_vendor_snapshot: true,
-		}
-
-		rust_library {
-			name: "librust_available_exclude",
-			crate_name: "rust_available_exclude",
-			srcs: ["lib.rs"],
-			vendor_available: true,
-			exclude_from_vendor_snapshot: true,
-		}
-	`
-
-	mockFS := map[string][]byte{
-		"framework/Android.bp": []byte(frameworkBp),
-		"framework/include.rs": nil,
-		"framework/exclude.rs": nil,
-	}
-
-	ctx := testRustVndkFs(t, "", mockFS)
-
-	// Test an include and exclude framework module.
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false, sharedVendorVariant)
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true, sharedVendorVariant)
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true, sharedVendorVariant)
-
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_include", false, rlibVendorVariant)
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_exclude", true, rlibVendorVariant)
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_available_exclude", true, rlibVendorVariant)
-
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_include", false, rlibDylibStdVendorVariant)
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_exclude", true, rlibDylibStdVendorVariant)
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_available_exclude", true, rlibDylibStdVendorVariant)
-
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_include", false, dylibVendorVariant)
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_exclude", true, dylibVendorVariant)
-	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_available_exclude", true, dylibVendorVariant)
-
-	// Verify the content of the vendor snapshot.
-
-	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-
-	var includeJsonFiles []string
-	var excludeJsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-		[]string{"arm", "armv7-a-neon"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-
-		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_dylib-std", archType, archVariant)
-		rlibRlibStdVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
-		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
-		dylibVariant := fmt.Sprintf("android_vendor.29_%s_%s_dylib", archType, archVariant)
-		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
-
-		// Included modules
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librust_include", "librust_include.rlib", rlibDir, rlibVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librust_include.rlib.json"))
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librust_include", "librust_include.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librust_include.rlib-std.rlib.json"))
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librust_include", "librust_include.dylib.so", dylibDir, dylibVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(dylibDir, "librust_include.dylib.so.json"))
-
-		// Excluded modules
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_exclude", "librust_exclude.rlib", rlibDir, rlibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librust_exclude.rlib.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_available_exclude", "librust_available_exclude.rlib", rlibDir, rlibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librust_available_exclude.rlib.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_available_exclude", "librust_available_exclude.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librust_available_exclude.rlib.rlib-std.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_exclude", "librust_exclude.dylib.so", dylibDir, dylibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(dylibDir, "librust_exclude.dylib.so.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_available_exclude", "librust_available_exclude.dylib.so", dylibDir, dylibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(dylibDir, "librust_available_exclude.dylib.so.json"))
-	}
-
-	// Verify that each json file for an included module has a rule.
-	for _, jsonFile := range includeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("include json file %q not found", jsonFile)
-		}
-	}
-
-	// Verify that each json file for an excluded module has no rule.
-	for _, jsonFile := range excludeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
-			t.Errorf("exclude json file %q found", jsonFile)
-		}
-	}
-}
-
-func TestVendorSnapshotUse(t *testing.T) {
-	frameworkBp := `
-	cc_library {
-		name: "libvndk",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "libvendor",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-	}
-
-	cc_library {
-		name: "libvendor_available",
-		vendor_available: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-	}
-
-	cc_library {
-		name: "lib32",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-		compile_multilib: "32",
-	}
-
-	cc_library {
-		name: "lib64",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		stl: "none",
-		system_shared_libs: [],
-		compile_multilib: "64",
-	}
-
-	rust_binary {
-		name: "bin",
-		vendor: true,
-		srcs: ["bin.rs"],
-	}
-
-	rust_binary {
-		name: "bin32",
-		vendor: true,
-		compile_multilib: "32",
-		srcs: ["bin.rs"],
-	}
-
-	rust_library {
-		name: "librust_vendor_available",
-		crate_name: "rust_vendor",
-		vendor_available: true,
-		srcs: ["client.rs"],
-	}
-
-`
-
-	vndkBp := `
-	vndk_prebuilt_shared {
-		name: "libvndk",
-		version: "30",
-		target_arch: "arm64",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		arch: {
-			arm64: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-			arm: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-		},
-	}
-
-	// old snapshot module which has to be ignored
-	vndk_prebuilt_shared {
-		name: "libvndk",
-		version: "26",
-		target_arch: "arm64",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		arch: {
-			arm64: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-			arm: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-		},
-	}
-
-	// different arch snapshot which has to be ignored
-	vndk_prebuilt_shared {
-		name: "libvndk",
-		version: "30",
-		target_arch: "arm",
-		vendor_available: true,
-		product_available: true,
-		vndk: {
-			enabled: true,
-		},
-		arch: {
-			arm: {
-				srcs: ["libvndk.so"],
-				export_include_dirs: ["include/libvndk"],
-			},
-		},
-	}
-`
-
-	vendorProprietaryBp := `
-	cc_library {
-		name: "libvendor_without_snapshot",
-		vendor: true,
-		nocrt: true,
-		no_libcrt: true,
-		no_crt_pad_segment: true,
-		stl: "none",
-		system_shared_libs: [],
-	}
-
-	rust_ffi_shared {
-		name: "libclient",
-		crate_name: "client",
-		vendor: true,
-		shared_libs: ["libvndk", "libvendor_available"],
-		static_libs: ["libvendor", "libvendor_without_snapshot"],
-		rustlibs: ["librust_vendor_available"],
-		arch: {
-			arm64: {
-				shared_libs: ["lib64"],
-			},
-			arm: {
-				shared_libs: ["lib32"],
-			},
-		},
-		srcs: ["client.rs"],
-	}
-
-	rust_library {
-		name: "libclient_rust",
-		crate_name: "client_rust",
-		vendor: true,
-		shared_libs: ["libvndk", "libvendor_available"],
-		static_libs: ["libvendor", "libvendor_without_snapshot"],
-		rustlibs: ["librust_vendor_available"],
-		arch: {
-			arm64: {
-				shared_libs: ["lib64"],
-			},
-			arm: {
-				shared_libs: ["lib32"],
-			},
-		},
-		srcs: ["client.rs"],
-	}
-
-	rust_binary {
-		name: "bin_without_snapshot",
-		vendor: true,
-		static_libs: ["libvndk"],
-		srcs: ["bin.rs"],
-		rustlibs: ["librust_vendor_available"],
-	}
-
-	vendor_snapshot {
-		name: "vendor_snapshot",
-		version: "30",
-		arch: {
-			arm64: {
-				vndk_libs: [
-					"libvndk",
-				],
-				static_libs: [
-					"libvendor",
-					"libvndk",
-					"libclang_rt.builtins",
-					"note_memtag_heap_sync",
-				],
-				shared_libs: [
-					"libvendor_available",
-					"lib64",
-				],
-				rlibs: [
-					"libstd",
-					"librust_vendor_available",
-					"librust_vendor_available.rlib-std"
-				],
-				dylibs: [
-					"libstd",
-					"librust_vendor_available",
-				],
-				binaries: [
-					"bin",
-				],
-                objects: [
-				    "crtend_so",
-					"crtbegin_so",
-					"crtbegin_dynamic",
-					"crtend_android"
-				],
-			},
-			arm: {
-				vndk_libs: [
-					"libvndk",
-				],
-				static_libs: [
-					"libvendor",
-					"libvndk",
-					"libclang_rt.builtins",
-				],
-				shared_libs: [
-					"libvendor_available",
-					"lib32",
-				],
-				rlibs: [
-					"libstd",
-					"librust_vendor_available",
-				],
-				dylibs: [
-					"libstd",
-					"librust_vendor_available",
-				],
-				binaries: [
-					"bin32",
-				],
-                objects: [
-				    "crtend_so",
-					"crtbegin_so",
-					"crtbegin_dynamic",
-					"crtend_android"
-				],
-
-			},
-		}
-	}
-
-	vendor_snapshot_object {
-		name: "crtend_so",
-		version: "30",
-		target_arch: "arm64",
-		vendor: true,
-		stl: "none",
-		crt: true,
-		arch: {
-			arm64: {
-				src: "crtend_so.o",
-			},
-			arm: {
-				src: "crtend_so.o",
-			},
-		},
-	}
-
-	vendor_snapshot_object {
-		name: "crtbegin_so",
-		version: "30",
-		target_arch: "arm64",
-		vendor: true,
-		stl: "none",
-		crt: true,
-		arch: {
-			arm64: {
-				src: "crtbegin_so.o",
-			},
-			arm: {
-				src: "crtbegin_so.o",
-			},
-		},
-	}
-
-	vendor_snapshot_rlib {
-		name: "libstd",
-		version: "30",
-		target_arch: "arm64",
-		vendor: true,
-		sysroot: true,
-		arch: {
-			arm64: {
-				src: "libstd.rlib",
-			},
-			arm: {
-				src: "libstd.rlib",
-			},
-		},
-	}
-
-	vendor_snapshot_rlib {
-		name: "librust_vendor_available",
-		version: "30",
-		target_arch: "arm64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "librust_vendor_available.rlib",
-			},
-			arm: {
-				src: "librust_vendor_available.rlib",
-			},
-		},
-	}
-
-	vendor_snapshot_rlib {
-		name: "librust_vendor_available.rlib-std",
-		version: "30",
-		target_arch: "arm64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "librust_vendor_available.rlib-std.rlib",
-			},
-			arm: {
-				src: "librust_vendor_available.rlib-std.rlib",
-			},
-		},
-	}
-
-	vendor_snapshot_dylib {
-		name: "libstd",
-		version: "30",
-		target_arch: "arm64",
-		vendor: true,
-		sysroot: true,
-		arch: {
-			arm64: {
-				src: "libstd.dylib.so",
-			},
-			arm: {
-				src: "libstd.dylib.so",
-			},
-		},
-	}
-
-	vendor_snapshot_dylib {
-		name: "librust_vendor_available",
-		version: "30",
-		target_arch: "arm64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "librust_vendor_available.dylib.so",
-			},
-			arm: {
-				src: "librust_vendor_available.dylib.so",
-			},
-		},
-	}
-
-	vendor_snapshot_object {
-		name: "crtend_android",
-		version: "30",
-		target_arch: "arm64",
-		vendor: true,
-		stl: "none",
-		crt: true,
-		arch: {
-			arm64: {
-				src: "crtend_so.o",
-			},
-			arm: {
-				src: "crtend_so.o",
-			},
-		},
-	}
-
-	vendor_snapshot_object {
-		name: "crtbegin_dynamic",
-		version: "30",
-		target_arch: "arm64",
-		vendor: true,
-		stl: "none",
-		crt: true,
-		arch: {
-			arm64: {
-				src: "crtbegin_so.o",
-			},
-			arm: {
-				src: "crtbegin_so.o",
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "libvndk",
-		version: "30",
-		target_arch: "arm64",
-		compile_multilib: "both",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "libvndk.a",
-			},
-			arm: {
-				src: "libvndk.a",
-			},
-		},
-		shared_libs: ["libvndk"],
-		export_shared_lib_headers: ["libvndk"],
-	}
-
-	vendor_snapshot_static {
-		name: "libclang_rt.builtins",
-		version: "30",
-		target_arch: "arm64",
-		vendor: true,
-		arch: {
-			arm: {
-				src: "libclang_rt.builtins-arm-android.a",
-			},
-			arm64: {
-				src: "libclang_rt.builtins-aarch64-android.a",
-			},
-		},
-    }
-
-	vendor_snapshot_shared {
-		name: "lib32",
-		version: "30",
-		target_arch: "arm64",
-		compile_multilib: "32",
-		vendor: true,
-		no_crt_pad_segment: true,
-		arch: {
-			arm: {
-				src: "lib32.so",
-			},
-		},
-	}
-
-	vendor_snapshot_shared {
-		name: "lib64",
-		version: "30",
-		target_arch: "arm64",
-		compile_multilib: "64",
-		vendor: true,
-		no_crt_pad_segment: true,
-		arch: {
-			arm64: {
-				src: "lib64.so",
-			},
-		},
-	}
-	vendor_snapshot_shared {
-		name: "liblog",
-		version: "30",
-		target_arch: "arm64",
-		compile_multilib: "64",
-		vendor: true,
-		no_crt_pad_segment: true,
-		arch: {
-			arm64: {
-				src: "liblog.so",
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "libvendor",
-		version: "30",
-		target_arch: "arm64",
-		compile_multilib: "both",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "libvendor.a",
-				export_include_dirs: ["include/libvendor"],
-			},
-			arm: {
-				src: "libvendor.a",
-				export_include_dirs: ["include/libvendor"],
-			},
-		},
-	}
-
-	vendor_snapshot_shared {
-		name: "libvendor_available",
-		version: "30",
-		target_arch: "arm64",
-		compile_multilib: "both",
-		vendor: true,
-		no_crt_pad_segment: true,
-		arch: {
-			arm64: {
-				src: "libvendor_available.so",
-				export_include_dirs: ["include/libvendor"],
-			},
-			arm: {
-				src: "libvendor_available.so",
-				export_include_dirs: ["include/libvendor"],
-			},
-		},
-	}
-
-	vendor_snapshot_binary {
-		name: "bin",
-		version: "30",
-		target_arch: "arm64",
-		compile_multilib: "64",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "bin",
-			},
-		},
-	}
-
-	vendor_snapshot_binary {
-		name: "bin32",
-		version: "30",
-		target_arch: "arm64",
-		compile_multilib: "32",
-		vendor: true,
-		arch: {
-			arm: {
-				src: "bin32",
-			},
-		},
-	}
-
-	// Test sanitizers use the snapshot libraries
-	rust_binary {
-		name: "memtag_binary",
-		srcs: ["vendor/bin.rs"],
-		vendor: true,
-		compile_multilib: "64",
-		sanitize: {
-			memtag_heap: true,
-			diag: {
-				memtag_heap: true,
-			}
-		},
-	}
-
-	// old snapshot module which has to be ignored
-	vendor_snapshot_binary {
-		name: "bin",
-		version: "26",
-		target_arch: "arm64",
-		compile_multilib: "first",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "bin",
-			},
-		},
-	}
-
-	// different arch snapshot which has to be ignored
-	vendor_snapshot_binary {
-		name: "bin",
-		version: "30",
-		target_arch: "arm",
-		compile_multilib: "first",
-		vendor: true,
-		arch: {
-			arm64: {
-				src: "bin",
-			},
-		},
-	}
-
-	vendor_snapshot_static {
-		name: "note_memtag_heap_sync",
-		vendor: true,
-		target_arch: "arm64",
-		version: "30",
-		arch: {
-			arm64: {
-				src: "note_memtag_heap_sync.a",
-			},
-		},
-	}
-
-`
-
-	mockFS := android.MockFS{
-		"framework/Android.bp":                          []byte(frameworkBp),
-		"framework/bin.rs":                              nil,
-		"note_memtag_heap_sync.a":                       nil,
-		"vendor/Android.bp":                             []byte(vendorProprietaryBp),
-		"vendor/bin":                                    nil,
-		"vendor/bin32":                                  nil,
-		"vendor/bin.rs":                                 nil,
-		"vendor/client.rs":                              nil,
-		"vendor/include/libvndk/a.h":                    nil,
-		"vendor/include/libvendor/b.h":                  nil,
-		"vendor/libvndk.a":                              nil,
-		"vendor/libvendor.a":                            nil,
-		"vendor/libvendor.so":                           nil,
-		"vendor/lib32.so":                               nil,
-		"vendor/lib64.so":                               nil,
-		"vendor/liblog.so":                              nil,
-		"vendor/libstd.rlib":                            nil,
-		"vendor/librust_vendor_available.rlib":          nil,
-		"vendor/librust_vendor_available.rlib-std.rlib": nil,
-		"vendor/libstd.dylib.so":                        nil,
-		"vendor/librust_vendor_available.dylib.so":      nil,
-		"vendor/crtbegin_so.o":                          nil,
-		"vendor/crtend_so.o":                            nil,
-		"vendor/libclang_rt.builtins-aarch64-android.a": nil,
-		"vendor/libclang_rt.builtins-arm-android.a":     nil,
-		"vndk/Android.bp":                               []byte(vndkBp),
-		"vndk/include/libvndk/a.h":                      nil,
-		"vndk/libvndk.so":                               nil,
-	}
-
-	sharedVariant := "android_vendor.30_arm64_armv8-a_shared"
-	rlibVariant := "android_vendor.30_arm64_armv8-a_rlib_dylib-std"
-	rlibRlibStdVariant := "android_vendor.30_arm64_armv8-a_rlib_rlib-std"
-	dylibVariant := "android_vendor.30_arm64_armv8-a_dylib"
-	staticVariant := "android_vendor.30_arm64_armv8-a_static"
-	binaryVariant := "android_vendor.30_arm64_armv8-a"
-
-	shared32Variant := "android_vendor.30_arm_armv7-a-neon_shared"
-	binary32Variant := "android_vendor.30_arm_armv7-a-neon"
-
-	ctx := testRustVndkFsVersions(t, "", mockFS, "30", "current", "31")
-
-	// libclient uses libvndk.vndk.30.arm64, libvendor.vendor_static.30.arm64, libvendor_without_snapshot
-	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("rustc").Args["linkFlags"]
-	for _, input := range [][]string{
-		[]string{sharedVariant, "libvndk.vndk.30.arm64"},
-		[]string{staticVariant, "libvendor.vendor_static.30.arm64"},
-		[]string{staticVariant, "libvendor_without_snapshot"},
-	} {
-		outputPaths := cc.GetOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
-		if !strings.Contains(libclientLdFlags, outputPaths[0].String()) {
-			t.Errorf("libflags for libclient must contain %#v, but was %#v", outputPaths[0], libclientLdFlags)
-		}
-	}
-
-	libclientAndroidMkSharedLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).transitiveAndroidMkSharedLibs.ToList()
-	if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib64", "liblog.vendor", "libc.vendor", "libm.vendor", "libdl.vendor"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted libclient AndroidMkSharedLibs %q, got %q", w, g)
-	}
-
-	libclientAndroidMkStaticLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkStaticLibs
-	if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot", "libclang_rt.builtins.vendor"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
-	}
-
-	libclientAndroidMkDylibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkDylibs
-	if g, w := libclientAndroidMkDylibs, []string{"librust_vendor_available.vendor", "libstd.vendor"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted libclient libclientAndroidMkDylibs %q, got %q", w, libclientAndroidMkDylibs)
-	}
-
-	libclient32AndroidMkSharedLibs := ctx.ModuleForTests("libclient", shared32Variant).Module().(*Module).transitiveAndroidMkSharedLibs.ToList()
-	if g, w := libclient32AndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib32", "liblog.vendor", "libc.vendor", "libm.vendor", "libdl.vendor"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted libclient32 AndroidMkSharedLibs %q, got %q", w, g)
-	}
-
-	libclientRustAndroidMkRlibs := ctx.ModuleForTests("libclient_rust", rlibVariant).Module().(*Module).Properties.AndroidMkRlibs
-	if g, w := libclientRustAndroidMkRlibs, []string{"librust_vendor_available.vendor"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted rlib libclient libclientAndroidMkRlibs %q, got %q", w, g)
-	}
-
-	libclientRlibStdRustAndroidMkRlibs := ctx.ModuleForTests("libclient_rust", rlibRlibStdVariant).Module().(*Module).Properties.AndroidMkRlibs
-	if g, w := libclientRlibStdRustAndroidMkRlibs, []string{"librust_vendor_available.vendor.rlib-std", "libstd.vendor"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted rlib libclient libclientAndroidMkRlibs %q, got %q", w, g)
-	}
-
-	libclientRustDylibAndroidMkDylibs := ctx.ModuleForTests("libclient_rust", dylibVariant).Module().(*Module).Properties.AndroidMkDylibs
-	if g, w := libclientRustDylibAndroidMkDylibs, []string{"librust_vendor_available.vendor", "libstd.vendor"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted dylib libclient libclientRustDylibAndroidMkDylibs %q, got %q", w, g)
-	}
-
-	// rust vendor snapshot must have ".vendor" suffix in AndroidMk
-	librustVendorAvailableSnapshotModule := ctx.ModuleForTests("librust_vendor_available.vendor_rlib.30.arm64", rlibVariant).Module()
-	librustVendorSnapshotMkName := android.AndroidMkEntriesForTest(t, ctx, librustVendorAvailableSnapshotModule)[0].EntryMap["LOCAL_MODULE"][0]
-	expectedRustVendorSnapshotName := "librust_vendor_available.vendor"
-	if librustVendorSnapshotMkName != expectedRustVendorSnapshotName {
-		t.Errorf("Unexpected rust vendor snapshot name in AndroidMk: %q, expected: %q\n", librustVendorSnapshotMkName, expectedRustVendorSnapshotName)
-	}
-
-	librustVendorAvailableDylibSnapshotModule := ctx.ModuleForTests("librust_vendor_available.vendor_dylib.30.arm64", dylibVariant).Module()
-	librustVendorSnapshotDylibMkName := android.AndroidMkEntriesForTest(t, ctx, librustVendorAvailableDylibSnapshotModule)[0].EntryMap["LOCAL_MODULE"][0]
-	expectedRustVendorDylibSnapshotName := "librust_vendor_available.vendor"
-	if librustVendorSnapshotDylibMkName != expectedRustVendorDylibSnapshotName {
-		t.Errorf("Unexpected rust vendor snapshot name in AndroidMk: %q, expected: %q\n", librustVendorSnapshotDylibMkName, expectedRustVendorDylibSnapshotName)
-	}
-
-	rustVendorBinModule := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Module()
-	rustVendorBinMkDylibName := android.AndroidMkEntriesForTest(t, ctx, rustVendorBinModule)[0].EntryMap["LOCAL_DYLIB_LIBRARIES"][0]
-	if rustVendorBinMkDylibName != expectedRustVendorSnapshotName {
-		t.Errorf("Unexpected rust rlib name in AndroidMk: %q, expected: %q\n", rustVendorBinMkDylibName, expectedRustVendorSnapshotName)
-	}
-
-	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustc").Args["linkFlags"]
-	libVndkStaticOutputPaths := cc.GetOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.30.arm64"})
-	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
-		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
-			libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
-	}
-
-	// bin is installed by bin.vendor_binary.30.arm64
-	ctx.ModuleForTests("bin.vendor_binary.30.arm64", binaryVariant).Output("bin")
-
-	// bin32 is installed by bin32.vendor_binary.30.arm64
-	ctx.ModuleForTests("bin32.vendor_binary.30.arm64", binary32Variant).Output("bin32")
-
-	// bin_without_snapshot is installed by bin_without_snapshot
-	ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot")
-
-	// libvendor, libvendor_available and bin don't have vendor.30 variant
-	libvendorVariants := ctx.ModuleVariantsForTests("libvendor")
-	if android.InList(sharedVariant, libvendorVariants) {
-		t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant)
-	}
-
-	libvendorAvailableVariants := ctx.ModuleVariantsForTests("libvendor_available")
-	if android.InList(sharedVariant, libvendorAvailableVariants) {
-		t.Errorf("libvendor_available must not have variant %#v, but it does", sharedVariant)
-	}
-
-	binVariants := ctx.ModuleVariantsForTests("bin")
-	if android.InList(binaryVariant, binVariants) {
-		t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
-	}
-
-	memtagStaticLibs := ctx.ModuleForTests("memtag_binary", "android_vendor.30_arm64_armv8-a").Module().(*Module).Properties.AndroidMkStaticLibs
-	if g, w := memtagStaticLibs, []string{"libclang_rt.builtins.vendor", "note_memtag_heap_sync.vendor"}; !reflect.DeepEqual(g, w) {
-		t.Errorf("wanted memtag_binary AndroidMkStaticLibs %q, got %q", w, g)
-	}
-}
-
-func TestRecoverySnapshotCapture(t *testing.T) {
-	bp := `
-	rust_ffi {
-		name: "librecovery",
-		recovery: true,
-		srcs: ["foo.rs"],
-		crate_name: "recovery",
-	}
-
-	rust_ffi {
-		name: "librecovery_available",
-		recovery_available: true,
-		srcs: ["foo.rs"],
-		crate_name: "recovery_available",
-	}
-
-	rust_library {
-		name: "librecovery_rustlib",
-		recovery: true,
-		srcs: ["foo.rs"],
-		crate_name: "recovery_rustlib",
-	}
-
-	rust_library {
-		name: "librecovery_available_rustlib",
-		recovery_available: true,
-		srcs: ["foo.rs"],
-		crate_name: "recovery_available_rustlib",
-	}
-
-	rust_binary {
-		name: "recovery_bin",
-		recovery: true,
-		srcs: ["foo.rs"],
-	}
-
-	rust_binary {
-		name: "recovery_available_bin",
-		recovery_available: true,
-		srcs: ["foo.rs"],
-	}
-
-`
-	// Check Recovery snapshot output.
-
-	ctx := testRustRecoveryFsVersions(t, bp, rustMockedFiles, "", "29", "current")
-	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
-
-	var jsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		// For shared libraries, all recovery:true and recovery_available modules are captured.
-		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(sharedDir, "librecovery.so.json"),
-			filepath.Join(sharedDir, "librecovery_available.so.json"))
-
-		// For static libraries, all recovery:true and recovery_available modules are captured.
-		staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
-		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(staticDir, "librecovery.a.json"),
-			filepath.Join(staticDir, "librecovery_available.a.json"))
-
-		// For rlib libraries, all recovery:true and recovery_available modules are captured.
-		rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_dylib-std", archType, archVariant)
-		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib", rlibDir, rlibVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.rlib", rlibDir, rlibVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(rlibDir, "librecovery_rustlib.rlib.json"),
-			filepath.Join(rlibDir, "librecovery_available_rustlib.rlib.json"))
-
-		rlibRlibStdVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(rlibDir, "librecovery_rustlib.rlib-std.rlib.json"),
-			filepath.Join(rlibDir, "librecovery_available_rustlib.rlib-std.rlib.json"))
-
-		// For dylib libraries, all recovery:true and recovery_available modules are captured.
-		dylibVariant := fmt.Sprintf("android_recovery_%s_%s_dylib", archType, archVariant)
-		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.dylib.so", dylibDir, dylibVariant)
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.dylib.so", dylibDir, dylibVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(dylibDir, "librecovery_rustlib.dylib.so.json"),
-			filepath.Join(dylibDir, "librecovery_available_rustlib.dylib.so.json"))
-
-		// For binary executables, all recovery:true and recovery_available modules are captured.
-		if archType == "arm64" {
-			binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
-			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
-			cc.CheckSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
-			cc.CheckSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
-			jsonFiles = append(jsonFiles,
-				filepath.Join(binaryDir, "recovery_bin.json"),
-				filepath.Join(binaryDir, "recovery_available_bin.json"))
-		}
-	}
-
-	for _, jsonFile := range jsonFiles {
-		// verify all json files exist
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("%q expected but not found", jsonFile)
-		}
-	}
-}
-
-func TestRecoverySnapshotExclude(t *testing.T) {
-	// This test verifies that the exclude_from_recovery_snapshot property
-	// makes its way from the Android.bp source file into the module data
-	// structure. It also verifies that modules are correctly included or
-	// excluded in the recovery snapshot based on their path (framework or
-	// vendor) and the exclude_from_recovery_snapshot property.
-
-	frameworkBp := `
-		rust_ffi_shared {
-			name: "libinclude",
-			srcs: ["src/include.rs"],
-			recovery_available: true,
-			crate_name: "include",
-		}
-		rust_ffi_shared {
-			name: "libexclude",
-			srcs: ["src/exclude.rs"],
-			recovery: true,
-			exclude_from_recovery_snapshot: true,
-			crate_name: "exclude",
-		}
-		rust_ffi_shared {
-			name: "libavailable_exclude",
-			srcs: ["src/exclude.rs"],
-			recovery_available: true,
-			exclude_from_recovery_snapshot: true,
-			crate_name: "available_exclude",
-		}
-		rust_library {
-			name: "libinclude_rustlib",
-			srcs: ["src/include.rs"],
-			recovery_available: true,
-			crate_name: "include_rustlib",
-		}
-		rust_library {
-			name: "libexclude_rustlib",
-			srcs: ["src/exclude.rs"],
-			recovery: true,
-			exclude_from_recovery_snapshot: true,
-			crate_name: "exclude_rustlib",
-		}
-		rust_library {
-			name: "libavailable_exclude_rustlib",
-			srcs: ["src/exclude.rs"],
-			recovery_available: true,
-			exclude_from_recovery_snapshot: true,
-			crate_name: "available_exclude_rustlib",
-		}
-	`
-
-	vendorProprietaryBp := `
-		rust_ffi_shared {
-			name: "librecovery",
-			srcs: ["recovery.rs"],
-			recovery: true,
-			crate_name: "recovery",
-		}
-		rust_library {
-			name: "librecovery_rustlib",
-			srcs: ["recovery.rs"],
-			recovery: true,
-			crate_name: "recovery_rustlib",
-		}
-	`
-
-	mockFS := map[string][]byte{
-		"framework/Android.bp": []byte(frameworkBp),
-		"framework/include.rs": nil,
-		"framework/exclude.rs": nil,
-		"device/Android.bp":    []byte(vendorProprietaryBp),
-		"device/recovery.rs":   nil,
-	}
-
-	ctx := testRustRecoveryFsVersions(t, "", mockFS, "", "29", "current")
-
-	// Test an include and exclude framework module.
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false, sharedRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true, sharedRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true, sharedRecoveryVariant)
-
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude_rustlib", false, rlibRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude_rustlib", true, rlibRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude_rustlib", true, rlibRlibStdRecoveryVariant)
-
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude_rustlib", false, rlibRlibStdRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude_rustlib", true, rlibRlibStdRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude_rustlib", true, rlibRlibStdRecoveryVariant)
-
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude_rustlib", false, dylibRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude_rustlib", true, dylibRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude_rustlib", true, dylibRecoveryVariant)
-
-	// A recovery module is excluded, but by its path not the exclude_from_recovery_snapshot property
-	// ('device/' and 'vendor/' are default excluded). See snapshot/recovery_snapshot.go for more detail.
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false, sharedRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery_rustlib", false, rlibRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery_rustlib", false, rlibRlibStdRecoveryVariant)
-	cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery_rustlib", false, dylibRecoveryVariant)
-
-	// Verify the content of the recovery snapshot.
-
-	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
-
-	var includeJsonFiles []string
-	var excludeJsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
-		rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_dylib-std", archType, archVariant)
-		rlibRlibStdVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
-		dylibVariant := fmt.Sprintf("android_recovery_%s_%s_dylib", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
-		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
-
-		// Included modules
-
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude_rustlib", "libinclude_rustlib.rlib", rlibDir, rlibVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "libinclude_rustlib.rlib.json"))
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude_rustlib", "libinclude_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "libinclude_rustlib.rlib-std.rlib.json"))
-
-		// Excluded modules
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
-
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude_rustlib", "libexclude_rustlib.rlib", rlibDir, rlibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libexclude_rustlib.rlib.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib", rlibDir, rlibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librecovery_rustlib.rlib.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude_rustlib", "libavailable_exclude_rustlib.rlib", rlibDir, rlibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libavailable_exclude_rustlib.rlib.json"))
-
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude_rustlib", "libexclude_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libexclude_rustlib.rlib-std.rlib.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librecovery_rustlib.rlib-std.rlib.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude_rustlib", "libavailable_exclude_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libavailable_exclude_rustlib.rlib-std.rlib.json"))
-
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude_rustlib", "libexclude_rustlib.dylib.so", dylibDir, dylibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libexclude_rustlib.dylib.so.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.dylib.so", dylibDir, dylibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librecovery_rustlib.dylib.so.json"))
-		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude_rustlib", "libavailable_exclude_rustlib.dylib.so", dylibDir, dylibVariant)
-		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libavailable_exclude_rustlib.dylib.so.json"))
-	}
-
-	// Verify that each json file for an included module has a rule.
-	for _, jsonFile := range includeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("include json file %q not found", jsonFile)
-		}
-	}
-
-	// Verify that each json file for an excluded module has no rule.
-	for _, jsonFile := range excludeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
-			t.Errorf("exclude json file %q found", jsonFile)
-		}
-	}
-}
-
-func TestRecoverySnapshotDirected(t *testing.T) {
-	bp := `
-	rust_ffi_shared {
-		name: "librecovery",
-		recovery: true,
-		crate_name: "recovery",
-		srcs: ["foo.rs"],
-	}
-
-	rust_ffi_shared {
-		name: "librecovery_available",
-		recovery_available: true,
-		crate_name: "recovery_available",
-		srcs: ["foo.rs"],
-	}
-
-	rust_library {
-		name: "librecovery_rustlib",
-		recovery: true,
-		crate_name: "recovery",
-		srcs: ["foo.rs"],
-	}
-
-	rust_library {
-		name: "librecovery_available_rustlib",
-		recovery_available: true,
-		crate_name: "recovery_available",
-		srcs: ["foo.rs"],
-	}
-
-	/* TODO: Uncomment when Rust supports the "prefer" property for prebuilts
-	rust_library_rlib {
-		name: "libfoo_rlib",
-		recovery: true,
-		crate_name: "foo",
-	}
-
-	rust_prebuilt_rlib {
-		name: "libfoo_rlib",
-		recovery: true,
-		prefer: true,
-		srcs: ["libfoo.rlib"],
-		crate_name: "foo",
-	}
-	*/
-`
-	ctx := testRustRecoveryFsVersions(t, bp, rustMockedFiles, "current", "29", "current")
-	ctx.Config().TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
-	ctx.Config().TestProductVariables.RecoverySnapshotModules["librecovery"] = true
-	ctx.Config().TestProductVariables.RecoverySnapshotModules["librecovery_rustlib"] = true
-	ctx.Config().TestProductVariables.DirectedRecoverySnapshot = true
-
-	// Check recovery snapshot output.
-	snapshotDir := "recovery-snapshot"
-	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
-
-	var includeJsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
-		rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_dylib-std", archType, archVariant)
-		rlibRlibStdVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
-		dylibVariant := fmt.Sprintf("android_recovery_%s_%s_dylib", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
-		dylibDir := filepath.Join(snapshotVariantPath, archDir, "dylib")
-
-		// Included modules
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib", rlibDir, rlibVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_rustlib.rlib.json"))
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_rustlib.rlib-std.rlib.json"))
-		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rustlib", "librecovery_rustlib.dylib.so", dylibDir, dylibVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(dylibDir, "librecovery_rustlib.dylib.so.json"))
-
-		// TODO: When Rust supports the "prefer" property for prebuilts, perform this check.
-		/*
-			// Check that snapshot captures "prefer: true" prebuilt
-			cc.CheckSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo_rlib", "libfoo_rlib.rlib", rlibDir, rlibVariant)
-			includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo_rlib.rlib.json"))
-		*/
-
-		// Excluded modules. Modules not included in the directed recovery snapshot
-		// are still included as fake modules.
-		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.so.json"))
-		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.rlib", rlibDir, rlibVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_available_rustlib.rlib.json"))
-		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.rlib-std.rlib", rlibDir, rlibRlibStdVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_available_rustlib.rlib-std.rlib.json"))
-		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available_rustlib", "librecovery_available_rustlib.dylib.so", dylibDir, dylibVariant)
-		includeJsonFiles = append(includeJsonFiles, filepath.Join(dylibDir, "librecovery_available_rustlib.dylib.so.json"))
-	}
-
-	// Verify that each json file for an included module has a rule.
-	for _, jsonFile := range includeJsonFiles {
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("include json file %q not found, %#v", jsonFile, includeJsonFiles)
-		}
-	}
-}
diff --git a/scripts/Android.bp b/scripts/Android.bp
index f36329b..91aa195 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -29,11 +29,6 @@
         "manifest_fixer_test.py",
         "manifest_fixer.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     libs: [
         "manifest_utils",
     ],
@@ -214,11 +209,6 @@
     srcs: [
         "conv_linker_config.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     libs: [
         "linker_config_proto",
     ],
@@ -299,9 +289,30 @@
     srcs: [
         "merge_directories.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
+}
+
+python_binary_host {
+    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 {
+    name: "buildinfo",
+    main: "buildinfo.py",
+    srcs: ["buildinfo.py"],
+}
+
+python_binary_host {
+    name: "extra_install_zips_file_list",
+    main: "extra_install_zips_file_list.py",
+    srcs: ["extra_install_zips_file_list.py"],
 }
diff --git a/scripts/buildinfo.py b/scripts/buildinfo.py
new file mode 100755
index 0000000..db99209
--- /dev/null
+++ b/scripts/buildinfo.py
@@ -0,0 +1,191 @@
+#!/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 json
+import os
+import subprocess
+
+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 default_cert == "" or default_cert == os.path.join(TEST_KEY_DIR, "testKey"):
+    return "test-keys"
+  return "dev-keys"
+
+def parse_args():
+  """Parse commandline arguments."""
+  parser = argparse.ArgumentParser()
+  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('--product-config', required=True, type=argparse.FileType('r'))
+  parser.add_argument('--out', required=True, type=argparse.FileType('w'))
+
+  option = parser.parse_args()
+
+  product_config = json.load(option.product_config)
+  build_flags = product_config["BuildFlags"]
+
+  option.build_flavor = get_build_flavor(product_config)
+  option.build_keys = get_build_keys(product_config)
+  option.build_id = product_config["BuildId"]
+  option.build_type = product_config["BuildType"]
+  option.build_variant = get_build_variant(product_config)
+  option.build_version_tags = product_config["BuildVersionTags"]
+  option.cpu_abis = product_config["DeviceAbi"]
+  option.default_locale = None
+  if len(product_config.get("ProductLocales", [])) > 0:
+    option.default_locale = product_config["ProductLocales"][0]
+  option.default_wifi_channels = product_config.get("ProductDefaultWifiChannels", [])
+  option.device = product_config["DeviceName"]
+  option.display_build_number = product_config["DisplayBuildNumber"]
+  option.platform_base_os = product_config["Platform_base_os"]
+  option.platform_display_version = product_config["Platform_display_version_name"]
+  option.platform_min_supported_target_sdk_version = build_flags["RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION"]
+  option.platform_preview_sdk_version = product_config["Platform_preview_sdk_version"]
+  option.platform_sdk_version = product_config["Platform_sdk_version"]
+  option.platform_security_patch = product_config["Platform_security_patch"]
+  option.platform_version = product_config["Platform_version_name"]
+  option.platform_version_codename = product_config["Platform_sdk_codename"]
+  option.platform_version_all_codenames = product_config["Platform_version_active_codenames"]
+  option.platform_version_known_codenames = product_config["Platform_version_known_codenames"]
+  option.platform_version_last_stable = product_config["Platform_version_last_stable"]
+  option.product = product_config["DeviceProduct"]
+  option.use_vbmeta_digest_in_fingerprint = product_config["BoardUseVbmetaDigestInFingerprint"]
+
+  return option
+
+def main():
+  option = parse_args()
+
+  build_hostname = option.build_hostname_file.read().strip()
+  build_number = option.build_number_file.read().strip()
+  build_version_tags_list = option.build_version_tags
+  if option.build_type == "debug":
+    build_version_tags_list.append("debug")
+  build_version_tags_list.append(option.build_keys)
+  build_version_tags = ",".join(sorted(set(build_version_tags_list)))
+
+  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 47eae07..bb88cce 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -3,52 +3,7 @@
 
 ###################################################
 # core-libart.jar & core-oj.jar
-java\.awt\.font
-java\.beans
-java\.io
-java\.lang
-java\.lang\.annotation
-java\.lang\.constant
-java\.lang\.invoke
-java\.lang\.ref
-java\.lang\.reflect
-java\.lang\.runtime
-java\.math
-java\.net
-java\.nio
-java\.nio\.file
-java\.nio\.file\.spi
-java\.nio\.file\.attribute
-java\.nio\.channels
-java\.nio\.channels\.spi
-java\.nio\.charset
-java\.nio\.charset\.spi
-java\.security
-java\.security\.acl
-java\.security\.cert
-java\.security\.interfaces
-java\.security\.spec
-java\.sql
-java\.text
-java\.text\.spi
-java\.time
-java\.time\.chrono
-java\.time\.format
-java\.time\.temporal
-java\.time\.zone
-java\.util
-java\.util\.concurrent
-java\.util\.concurrent\.atomic
-java\.util\.concurrent\.locks
-java\.util\.function
-java\.util\.jar
-java\.util\.logging
-java\.util\.prefs
-java\.util\.random
-java\.util\.regex
-java\.util\.spi
-java\.util\.stream
-java\.util\.zip
+java(\..*)?
 # TODO: Remove javax.annotation.processing if possible, see http://b/132338110:
 javax\.annotation\.processing
 javax\.crypto
@@ -72,18 +27,7 @@
 javax\.xml\.transform\.stream
 javax\.xml\.validation
 javax\.xml\.xpath
-jdk\.internal
-jdk\.internal\.access
-jdk\.internal\.math
-jdk\.internal\.misc
-jdk\.internal\.ref
-jdk\.internal\.reflect
-jdk\.internal\.util
-jdk\.internal\.util\.jar
-jdk\.internal\.util\.random
-jdk\.internal\.vm\.annotation
-jdk\.net
-jdk\.random
+jdk\..*
 org\.w3c\.dom
 org\.w3c\.dom\.ls
 org\.w3c\.dom\.traversal
diff --git a/scripts/extra_install_zips_file_list.py b/scripts/extra_install_zips_file_list.py
new file mode 100755
index 0000000..148d6cc
--- /dev/null
+++ b/scripts/extra_install_zips_file_list.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import sys
+import zipfile
+from typing import List
+
+def list_files_in_zip(zipfile_path: str) -> List[str]:
+    with zipfile.ZipFile(zipfile_path, 'r') as zf:
+        return zf.namelist()
+
+def main():
+    parser = argparse.ArgumentParser(
+        description='Lists paths to all files inside an EXTRA_INSTALL_ZIPS zip file relative to a partition staging directory. '
+        'This script is just a helper because its difficult to implement this logic in make code.'
+    )
+    parser.add_argument('staging_dir',
+        help='Path to the partition staging directory')
+    parser.add_argument('extra_install_zips', nargs='*',
+        help='The value of EXTRA_INSTALL_ZIPS from make. '
+        'It should be a list of primary_file:extraction_dir:zip_file trios. '
+        'The primary file will be ignored by this script, you should ensure that '
+        'the list of trios given to this script is already filtered by relevant primary files.')
+    args = parser.parse_args()
+
+    staging_dir = args.staging_dir.removesuffix('/') + '/'
+
+    for zip_trio in args.extra_install_zips:
+        _, d, z = zip_trio.split(':')
+        d = d.removesuffix('/') + '/'
+
+        if d.startswith(staging_dir):
+            d = os.path.relpath(d, staging_dir)
+            if d == '.':
+                d = ''
+            for f in list_files_in_zip(z):
+                print(os.path.join(d, f))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/gen-kotlin-build-file.py b/scripts/gen-kotlin-build-file.py
index 83b4cd8..99afdca 100644
--- a/scripts/gen-kotlin-build-file.py
+++ b/scripts/gen-kotlin-build-file.py
@@ -79,7 +79,7 @@
         elif src.endswith('.kt'):
           f.write('    <sources path="%s"/>\n' % path)
         else:
-          raise RuntimeError('unknown source file type %s' % file)
+          raise RuntimeError(f'unknown source file type {src} from rspfile {rsp_file}')
 
     for rsp_file in args.common_srcs:
       for src in NinjaRspFileReader(rsp_file):
diff --git a/scripts/gen_build_prop.py b/scripts/gen_build_prop.py
new file mode 100644
index 0000000..799e00b
--- /dev/null
+++ b/scripts/gen_build_prop.py
@@ -0,0 +1,565 @@
+#!/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 default_cert == "" or default_cert == os.path.join(TEST_KEY_DIR, "testKey"):
+    return "test-keys"
+  return "dev-keys"
+
+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()
+
+  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["ModelForAttestation"]:
+        print(f"ro.product.model_for_attestation={config['ModelForAttestation']}")
+    if config["BrandForAttestation"]:
+        print(f"ro.product.brand_for_attestation={config['BrandForAttestation']}")
+    if config["NameForAttestation"]:
+        print(f"ro.product.name_for_attestation={config['NameForAttestation']}")
+    if config["DeviceForAttestation"]:
+        print(f"ro.product.device_for_attestation={config['DeviceForAttestation']}")
+    if config["ManufacturerForAttestation"]:
+        print(f"ro.product.manufacturer_for_attestation={config['ManufacturerForAttestation']}")
+
+  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"]
+
+  # 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['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']}")
+
+  # 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 "build_thumbprint" 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 config["PropertySplitEnabled"] or 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
+  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
+
+    # 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_target_debugging:
+    # Enable Dalvik lock contention logging.
+    props.append("dalvik.vm.lockprof.threshold=500")
+
+    # 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['ProductShippingApiLevel']}")
+
+  # 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.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")
+
+  # 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_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",
+    "PRODUCT_PRODUCT_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):
+    if args.partition == "system":
+      build_system_prop(args)
+      '''
+    elif args.partition == "vendor":
+      build_vendor_prop(args)
+    elif args.partition == "product":
+      build_product_prop(args)
+      '''
+    else:
+      sys.exit(f"not supported partition {args.partition}")
+
+if __name__ == "__main__":
+  main()
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index 1e89efe..43edf44 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -18,19 +18,9 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-python_defaults {
-    name: "hiddenapi_defaults",
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
-}
-
 python_binary_host {
     name: "analyze_bcpf",
     main: "analyze_bcpf.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["analyze_bcpf.py"],
     // Make sure that the bpmodify tool is built.
     data: [":bpmodify"],
@@ -42,7 +32,6 @@
 python_test_host {
     name: "analyze_bcpf_test",
     main: "analyze_bcpf_test.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: [
         "analyze_bcpf.py",
         "analyze_bcpf_test.py",
@@ -60,21 +49,18 @@
 python_binary_host {
     name: "merge_csv",
     main: "merge_csv.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["merge_csv.py"],
 }
 
 python_binary_host {
     name: "generate_hiddenapi_lists",
     main: "generate_hiddenapi_lists.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["generate_hiddenapi_lists.py"],
 }
 
 python_test_host {
     name: "generate_hiddenapi_lists_test",
     main: "generate_hiddenapi_lists_test.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: [
         "generate_hiddenapi_lists.py",
         "generate_hiddenapi_lists_test.py",
@@ -92,7 +78,6 @@
 python_test_host {
     name: "signature_trie_test",
     main: "signature_trie_test.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["signature_trie_test.py"],
     libs: ["signature_trie"],
     test_options: {
@@ -103,7 +88,6 @@
 python_binary_host {
     name: "verify_overlaps",
     main: "verify_overlaps.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["verify_overlaps.py"],
     libs: [
         "signature_trie",
@@ -113,7 +97,6 @@
 python_test_host {
     name: "verify_overlaps_test",
     main: "verify_overlaps_test.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: [
         "verify_overlaps.py",
         "verify_overlaps_test.py",
@@ -129,14 +112,12 @@
 python_binary_host {
     name: "signature_patterns",
     main: "signature_patterns.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["signature_patterns.py"],
 }
 
 python_test_host {
     name: "signature_patterns_test",
     main: "signature_patterns_test.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: [
         "signature_patterns.py",
         "signature_patterns_test.py",
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index c33b104..b101259 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -52,6 +52,14 @@
         'required:false'
     )
     parser.add_argument(
+        '--missing-optional-uses-library',
+        dest='missing_optional_uses_libraries',
+        action='append',
+        help='specify uses-library entries missing from the build system with '
+        'required:false',
+        default=[]
+    )
+    parser.add_argument(
         '--enforce-uses-libraries',
         dest='enforce_uses_libraries',
         action='store_true',
@@ -91,7 +99,7 @@
 C_BOLD = "\033[1m"
 
 
-def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
+def enforce_uses_libraries(manifest, required, optional, missing_optional, relax, is_apk, path):
     """Verify that the <uses-library> tags in the manifest match those provided
 
   by the build system.
@@ -119,7 +127,12 @@
     required = trim_namespace_parts(required)
     optional = trim_namespace_parts(optional)
 
-    if manifest_required == required and manifest_optional == optional:
+    existing_manifest_optional = [
+        lib for lib in manifest_optional if lib not in missing_optional]
+
+    # The order of the existing libraries matter, while the order of the missing
+    # ones doesn't.
+    if manifest_required == required and existing_manifest_optional == optional:
         return None
 
     #pylint: disable=line-too-long
@@ -129,6 +142,7 @@
         '\t- required libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(required), C_OFF),
         '\t                 vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_required), C_OFF),
         '\t- optional libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(optional), C_OFF),
+        '\t    and missing ones in build system: %s[%s]%s\n' % (C_RED, ', '.join(missing_optional), C_OFF),
         '\t                 vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_optional), C_OFF),
         '\t- tags in the manifest (%s):\n' % path,
         '\t\t%s\n' % '\t\t'.join(tags),
@@ -340,11 +354,14 @@
 
         if args.enforce_uses_libraries:
             # Load dexpreopt.config files and build a mapping from module
-            # names to library names. This is necessary because build system
-            # addresses libraries by their module name (`uses_libs`,
-            # `optional_uses_libs`, `LOCAL_USES_LIBRARIES`,
-            # `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain module names), while
-            # the manifest addresses libraries by their name.
+            # names to library names. This is for Make only and it's necessary
+            # because Make passes module names from `LOCAL_USES_LIBRARIES`,
+            # `LOCAL_OPTIONAL_LIBRARY_NAMES`, while the manifest addresses
+            # libraries by their name. Soong doesn't use it and doesn't need it
+            # because it converts the module names to the library names and
+            # passes the library names. There is no need to translate missing
+            # optional libs because they are missing and therefore there is no
+            # mapping for them.
             mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs)
             required = translate_libnames(args.uses_libraries, mod_to_lib)
             optional = translate_libnames(args.optional_uses_libraries,
@@ -354,8 +371,8 @@
             # those in the manifest. Raise an exception on mismatch, unless the
             # script was passed a special parameter to suppress exceptions.
             errmsg = enforce_uses_libraries(manifest, required, optional,
-                                            args.enforce_uses_libraries_relax,
-                                            is_apk, args.input)
+                args.missing_optional_uses_libraries,
+                args.enforce_uses_libraries_relax, is_apk, args.input)
 
             # Create a status file that is empty on success, or contains an
             # error message on failure. When exceptions are suppressed,
diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py
index 3be7a30..8003b3e 100755
--- a/scripts/manifest_check_test.py
+++ b/scripts/manifest_check_test.py
@@ -44,15 +44,17 @@
 class EnforceUsesLibrariesTest(unittest.TestCase):
     """Unit tests for add_extract_native_libs function."""
 
-    def run_test(self, xml, apk, uses_libraries=[], 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
             manifest_check.enforce_uses_libraries(
-                doc, uses_libraries, optional_uses_libraries, relax, False,
-                'path/to/X/AndroidManifest.xml')
+                doc, uses_libraries, optional_uses_libraries, missing_optional_uses_libraries,
+                relax, False, 'path/to/X/AndroidManifest.xml')
             manifest_check.enforce_uses_libraries(apk, uses_libraries,
                                                   optional_uses_libraries,
+                                                  missing_optional_uses_libraries,
                                                   relax, True,
                                                   'path/to/X/X.apk')
             return True
@@ -102,6 +104,15 @@
         matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
         self.assertFalse(matches)
 
+    def test_expected_missing_optional_uses_library(self):
+        xml = self.xml_tmpl % (
+            uses_library_xml('foo') + uses_library_xml('missing') + uses_library_xml('bar'))
+        apk = self.apk_tmpl % (
+            uses_library_apk('foo') + uses_library_apk('missing') + uses_library_apk('bar'))
+        matches = self.run_test(xml, apk, optional_uses_libraries=['foo', 'bar'],
+                                missing_optional_uses_libraries=['missing'])
+        self.assertFalse(matches)
+
     def test_missing_uses_library(self):
         xml = self.xml_tmpl % ('')
         apk = self.apk_tmpl % ('')
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-soong-tests-with-go-tools.sh b/scripts/run-soong-tests-with-go-tools.sh
index 93c622e..1fbb1fc 100755
--- a/scripts/run-soong-tests-with-go-tools.sh
+++ b/scripts/run-soong-tests-with-go-tools.sh
@@ -74,6 +74,6 @@
     (cd "$dir";
      eval ${network_jail} -- ${GOROOT}/bin/go build ./...
      eval ${network_jail} -- ${GOROOT}/bin/go test ./...
-     eval ${network_jail} -- ${GOROOT}/bin/go test -race -short ./...
+     eval ${network_jail} -- ${GOROOT}/bin/go test -race -timeout 20m -short ./...
     )
 done
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 5d41958..7048a15 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -152,6 +152,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: [],
+}
+
 prebuilt_bootclasspath_fragment {
     name: "art-bootclasspath-fragment",
     prefer: false,
@@ -187,6 +192,7 @@
     apex_available: ["com.android.art"],
     jars: ["java_boot_libs/snapshot/jars/are/invalid/core2.jar"],
 }
+
 `),
 		checkAllCopyRules(`
 .intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
@@ -275,6 +281,19 @@
 				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
 			}
 		}),
+		// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
+		// file creation.
+		java.FixtureConfigureBootJars("platform:foo"),
+		android.FixtureModifyMockFS(func(fs android.MockFS) {
+			fs["platform/Android.bp"] = []byte(`
+		java_library {
+			name: "foo",
+			srcs: ["Test.java"],
+			compile_dex: true,
+		}
+		`)
+			fs["platform/Test.java"] = nil
+		}),
 
 		android.FixtureWithRootAndroidBp(sdk+`
 			apex {
@@ -353,6 +372,15 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: [
+        "prebuilt_myothersdklibrary",
+        "prebuilt_mysdklibrary",
+        "prebuilt_mycoreplatform",
+    ],
+}
+
 prebuilt_bootclasspath_fragment {
     name: "mybootclasspathfragment",
     prefer: false,
@@ -642,6 +670,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mysdklibrary"],
+}
+
 prebuilt_bootclasspath_fragment {
     name: "mybootclasspathfragment",
     prefer: false,
@@ -881,6 +914,14 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: [
+        "prebuilt_mynewlibrary",
+        "prebuilt_mysdklibrary",
+    ],
+}
+
 prebuilt_bootclasspath_fragment {
     name: "mybootclasspathfragment",
     prefer: false,
@@ -1008,6 +1049,9 @@
 		android.FixtureMergeEnv(map[string]string{
 			"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": targetBuildRelease,
 		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
+		}),
 
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.BuildFlags = map[string]string{
@@ -1064,7 +1108,7 @@
 
 	bcpf := result.ModuleForTests("mybootclasspathfragment", "android_common")
 	rule := bcpf.Output("out/soong/.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi" + suffix + "/stub-flags.csv")
-	android.AssertPathsRelativeToTopEquals(t, "stub flags inputs", expectedStubFlagsInputs, rule.Implicits)
+	android.AssertPathsRelativeToTopEquals(t, "stub flags inputs", android.SortedUniqueStrings(expectedStubFlagsInputs), android.SortedUniquePaths(rule.Implicits))
 
 	CheckSnapshot(t, result, "mysdk", "",
 		checkAndroidBpContents(expectedSdkSnapshot),
@@ -1122,7 +1166,7 @@
 		// of the snapshot.
 		expectedStubFlagsInputs := []string{
 			"out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar",
-			"out/soong/.intermediates/mysdklibrary/android_common/aligned/mysdklibrary.jar",
+			"out/soong/.intermediates/mysdklibrary.impl/android_common/aligned/mysdklibrary.jar",
 		}
 
 		testSnapshotWithBootClasspathFragment_MinSdkVersion(t, "S",
@@ -1203,9 +1247,9 @@
 		// they are both part of the snapshot.
 		expectedStubFlagsInputs := []string{
 			"out/soong/.intermediates/mynewsdklibrary.stubs.exportable/android_common/dex/mynewsdklibrary.stubs.exportable.jar",
-			"out/soong/.intermediates/mynewsdklibrary/android_common/aligned/mynewsdklibrary.jar",
+			"out/soong/.intermediates/mynewsdklibrary.impl/android_common/aligned/mynewsdklibrary.jar",
 			"out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar",
-			"out/soong/.intermediates/mysdklibrary/android_common/aligned/mysdklibrary.jar",
+			"out/soong/.intermediates/mysdklibrary.impl/android_common/aligned/mysdklibrary.jar",
 		}
 
 		testSnapshotWithBootClasspathFragment_MinSdkVersion(t, "Tiramisu",
@@ -1226,6 +1270,9 @@
 		android.FixtureMergeEnv(map[string]string{
 			"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
 		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
+		}),
 		android.FixtureWithRootAndroidBp(`
 			sdk {
 				name: "mysdk",
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 265579a..5d76930 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -123,6 +123,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_sdkmember"],
+}
+
 cc_prebuilt_library_shared {
     name: "sdkmember",
     prefer: false,
@@ -143,6 +148,9 @@
             srcs: ["linux_glibc/x86_64/lib/sdkmember.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -226,6 +234,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_crtobj"],
+}
+
 cc_prebuilt_object {
     name: "crtobj",
     prefer: false,
@@ -333,6 +346,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
@@ -353,6 +371,9 @@
             srcs: ["arm/lib/mynativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -406,6 +427,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
@@ -435,6 +461,9 @@
             },
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -465,6 +494,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mymodule_exports.contributions",
+    contents: ["prebuilt_mynativebinary"],
+}
+
 cc_prebuilt_binary {
     name: "mynativebinary",
     prefer: false,
@@ -523,6 +557,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_mynativebinary"],
+}
+
 cc_prebuilt_binary {
     name: "mynativebinary",
     prefer: false,
@@ -621,6 +660,14 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: [
+        "prebuilt_mynativebinary",
+        "prebuilt_mynativelib",
+    ],
+}
+
 cc_prebuilt_binary {
     name: "mynativebinary",
     prefer: false,
@@ -659,6 +706,9 @@
             srcs: ["x86_64/lib/mynativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -696,6 +746,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mymodule_exports.contributions",
+    contents: ["prebuilt_linker"],
+}
+
 cc_prebuilt_binary {
     name: "linker",
     prefer: false,
@@ -755,6 +810,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
@@ -776,6 +836,9 @@
             export_include_dirs: ["arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -856,6 +919,15 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: [
+        "prebuilt_mynativelib",
+        "prebuilt_myothernativelib",
+        "prebuilt_mysystemnativelib",
+    ],
+}
+
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
@@ -875,6 +947,9 @@
             srcs: ["arm/lib/mynativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 
 cc_prebuilt_library_shared {
@@ -893,6 +968,9 @@
             srcs: ["arm/lib/myothernativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 
 cc_prebuilt_library_shared {
@@ -910,6 +988,9 @@
             srcs: ["arm/lib/mysystemnativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -953,6 +1034,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
@@ -979,6 +1065,9 @@
             export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -1029,6 +1118,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
@@ -1060,6 +1154,9 @@
             srcs: ["windows/x86_64/lib/mynativelib.dll"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -1095,6 +1192,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library_static {
     name: "mynativelib",
     prefer: false,
@@ -1158,6 +1260,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library_static {
     name: "mynativelib",
     prefer: false,
@@ -1222,6 +1329,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library {
     name: "mynativelib",
     prefer: false,
@@ -1298,6 +1410,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library {
     name: "mynativelib",
     prefer: false,
@@ -1394,6 +1511,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library {
     name: "mynativelib",
     prefer: false,
@@ -1520,6 +1642,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library_static {
     name: "mynativelib",
     prefer: false,
@@ -1572,6 +1699,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativeheaders"],
+}
+
 cc_prebuilt_library_headers {
     name: "mynativeheaders",
     prefer: false,
@@ -1594,6 +1726,9 @@
 		PrepareForTestWithSdkBuildComponents,
 		ccTestFs.AddToFixture(),
 		prepareForTestWithNativeBridgeTarget,
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			android.RegisterApexContributionsBuildComponents(ctx)
+		}),
 	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1616,6 +1751,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativeheaders"],
+}
+
 cc_prebuilt_library_headers {
     name: "mynativeheaders",
     prefer: false,
@@ -1679,6 +1819,9 @@
 			cc.PrepareForTestWithCcDefaultModules,
 			PrepareForTestWithSdkBuildComponents,
 			ccTestFs.AddToFixture(),
+			android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+				android.RegisterApexContributionsBuildComponents(ctx)
+			}),
 		).RunTestWithBp(t, fmt.Sprintf(`
 		sdk {
 			name: "mysdk",
@@ -1701,6 +1844,11 @@
 			checkAndroidBpContents(fmt.Sprintf(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativeheaders"],
+}
+
 cc_prebuilt_library_headers {
     name: "mynativeheaders",
     prefer: false,
@@ -1750,6 +1898,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativeheaders"],
+}
+
 cc_prebuilt_library_headers {
     name: "mynativeheaders",
     prefer: false,
@@ -1807,6 +1960,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativeheaders"],
+}
+
 cc_prebuilt_library_headers {
     name: "mynativeheaders",
     prefer: false,
@@ -1870,6 +2028,15 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: [
+        "prebuilt_sslnil",
+        "prebuilt_sslempty",
+        "prebuilt_sslnonempty",
+    ],
+}
+
 cc_prebuilt_library_shared {
     name: "sslnil",
     prefer: false,
@@ -1884,6 +2051,9 @@
             srcs: ["arm/lib/sslnil.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 
 cc_prebuilt_library_shared {
@@ -1901,6 +2071,9 @@
             srcs: ["arm/lib/sslempty.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 
 cc_prebuilt_library_shared {
@@ -1918,6 +2091,9 @@
             srcs: ["arm/lib/sslnonempty.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `))
 
@@ -1943,6 +2119,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_sslvariants"],
+}
+
 cc_prebuilt_library_shared {
     name: "sslvariants",
     prefer: false,
@@ -1972,6 +2153,9 @@
             srcs: ["linux_glibc/x86/lib/sslvariants.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 	)
@@ -2002,11 +2186,17 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_stubslib"],
+}
+
 cc_prebuilt_library_shared {
     name: "stubslib",
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
+    stl: "none",
     compile_multilib: "both",
     stubs: {
         versions: [
@@ -2024,6 +2214,9 @@
             srcs: ["arm/lib/stubslib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `))
 }
@@ -2056,6 +2249,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_stubslib"],
+}
+
 cc_prebuilt_library_shared {
     name: "stubslib",
     prefer: false,
@@ -2090,6 +2288,9 @@
             srcs: ["linux_glibc/x86/lib/stubslib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 	)
@@ -2114,6 +2315,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mylib"],
+}
+
 cc_prebuilt_library_shared {
     name: "mylib",
     prefer: false,
@@ -2141,6 +2347,9 @@
             srcs: ["linux_glibc/x86/lib/mylib-host.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
@@ -2178,6 +2387,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mynativelib"],
+}
+
 cc_prebuilt_library_shared {
     name: "mynativelib",
     prefer: false,
@@ -2193,6 +2407,9 @@
             srcs: ["arm/lib/mynativelib.so"],
         },
     },
+    strip: {
+        none: true,
+    },
 }
 `),
 		checkAllCopyRules(`
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
index 45e8e0e..75b5229 100644
--- a/sdk/compat_config_sdk_test.go
+++ b/sdk/compat_config_sdk_test.go
@@ -36,6 +36,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_myconfig"],
+}
+
 prebuilt_platform_compat_config {
     name: "myconfig",
     prefer: false,
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index 2605fd1..9d0a242 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -46,6 +46,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_myjavalib"],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 1b2b0f1..0a5483b 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -108,6 +108,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_myjavalib"],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
@@ -154,6 +159,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_myjavalib"],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
@@ -193,6 +203,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_myjavalib"],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
@@ -245,6 +260,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_myjavalib"],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
@@ -291,6 +311,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: [],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
@@ -313,6 +338,9 @@
 			android.FixtureMergeEnv(map[string]string{
 				"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": targetBuildRelease,
 			}),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
+			}),
 		).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -395,6 +423,11 @@
 			checkAndroidBpContents(fmt.Sprintf(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mylib"],
+}
+
 java_import {
     name: "mylib",
     prefer: false,
@@ -459,6 +492,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: [],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
@@ -504,6 +542,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_myjavalib"],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
@@ -542,6 +585,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_myjavatests"],
+}
+
 java_test_import {
     name: "myjavatests",
     prefer: false,
@@ -582,6 +630,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: ["prebuilt_myjavatests"],
+}
+
 java_test_import {
     name: "myjavatests",
     prefer: false,
@@ -635,6 +688,12 @@
 			public: {
 				enabled: true,
 			},
+			system: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
 		}
 
 		java_system_modules {
@@ -661,6 +720,15 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: [
+        "prebuilt_exported-system-module",
+        "prebuilt_myjavalib",
+        "prebuilt_my-system-modules",
+    ],
+}
+
 java_import {
     name: "exported-system-module",
     prefer: false,
@@ -690,6 +758,20 @@
         removed_api: "sdk_library/public/myjavalib-removed.txt",
         sdk_version: "current",
     },
+    system: {
+        jars: ["sdk_library/system/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
+        current_api: "sdk_library/system/myjavalib.txt",
+        removed_api: "sdk_library/system/myjavalib-removed.txt",
+        sdk_version: "system_current",
+    },
+    module_lib: {
+        jars: ["sdk_library/module-lib/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/module-lib/myjavalib_stub_sources"],
+        current_api: "sdk_library/module-lib/myjavalib.txt",
+        removed_api: "sdk_library/module-lib/myjavalib-removed.txt",
+        sdk_version: "module_current",
+    },
 }
 
 java_system_modules_import {
@@ -709,6 +791,12 @@
 .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
+.intermediates/myjavalib.stubs.exportable.system/android_common/combined/myjavalib.stubs.exportable.system.jar -> sdk_library/system/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.system/android_common/exportable/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/exportable/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.exportable.module_lib/android_common/combined/myjavalib.stubs.exportable.module_lib.jar -> sdk_library/module-lib/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.module_lib/android_common/exportable/myjavalib.stubs.source.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
+.intermediates/myjavalib.stubs.source.module_lib/android_common/exportable/myjavalib.stubs.source.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
 `),
 		checkInfoContents(result.Config, `
 [
@@ -743,11 +831,23 @@
     "@name": "myjavalib",
     "dist_stem": "myjavalib",
     "scopes": {
+      "module-lib": {
+        "current_api": "sdk_library/module-lib/myjavalib.txt",
+        "latest_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib.api.module-lib.latest/gen/myjavalib.api.module-lib.latest",
+        "latest_removed_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib-removed.api.module-lib.latest/gen/myjavalib-removed.api.module-lib.latest",
+        "removed_api": "sdk_library/module-lib/myjavalib-removed.txt"
+      },
       "public": {
         "current_api": "sdk_library/public/myjavalib.txt",
         "latest_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib.api.public.latest/gen/myjavalib.api.public.latest",
         "latest_removed_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib-removed.api.public.latest/gen/myjavalib-removed.api.public.latest",
         "removed_api": "sdk_library/public/myjavalib-removed.txt"
+      },
+      "system": {
+        "current_api": "sdk_library/system/myjavalib.txt",
+        "latest_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib.api.system.latest/gen/myjavalib.api.system.latest",
+        "latest_removed_api": "out/soong/.intermediates/prebuilts/sdk/myjavalib-removed.api.system.latest/gen/myjavalib-removed.api.system.latest",
+        "removed_api": "sdk_library/system/myjavalib-removed.txt"
       }
     }
   },
@@ -790,6 +890,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_my-system-modules"],
+}
+
 java_import {
     name: "mysdk_system-module",
     prefer: false,
@@ -854,6 +959,15 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "myexports.contributions",
+    contents: [
+        "prebuilt_hostjavalib",
+        "prebuilt_androidjavalib",
+        "prebuilt_myjavalib",
+    ],
+}
+
 java_import {
     name: "hostjavalib",
     prefer: false,
@@ -920,6 +1034,11 @@
 		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,
@@ -993,6 +1112,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_myjavalib-foo"],
+}
+
 java_sdk_library_import {
     name: "myjavalib-foo",
     prefer: false,
@@ -1046,6 +1170,11 @@
 		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,
@@ -1093,6 +1222,11 @@
 		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,
@@ -1147,6 +1281,11 @@
 		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,
@@ -1204,6 +1343,11 @@
 		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,
@@ -1272,6 +1416,11 @@
 		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,
@@ -1320,6 +1469,11 @@
 		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,
@@ -1371,6 +1525,11 @@
 		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,
@@ -1436,6 +1595,11 @@
 		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,
@@ -1509,6 +1673,11 @@
 		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,
@@ -1569,6 +1738,11 @@
 		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,
@@ -1626,6 +1800,11 @@
 		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,
diff --git a/sdk/license_sdk_test.go b/sdk/license_sdk_test.go
index 829edf1..754f019 100644
--- a/sdk/license_sdk_test.go
+++ b/sdk/license_sdk_test.go
@@ -69,6 +69,11 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_myjavalib"],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
diff --git a/sdk/member_trait_test.go b/sdk/member_trait_test.go
index 99caf13..673d6fb 100644
--- a/sdk/member_trait_test.go
+++ b/sdk/member_trait_test.go
@@ -137,6 +137,11 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_myjavalib"],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
@@ -202,6 +207,17 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: [
+        "prebuilt_myjavalib",
+        "prebuilt_myjavalib_extra",
+        "prebuilt_myjavalib_special",
+        "prebuilt_anotherjavalib",
+        "prebuilt_anotherjavalib_special",
+    ],
+}
+
 java_test_import {
     name: "myjavalib",
     prefer: false,
diff --git a/sdk/sdk.go b/sdk/sdk.go
index fd16ab6..5b644fd 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -193,6 +193,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 {
@@ -222,18 +226,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 c4df146..4894210 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -118,6 +118,16 @@
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: [
+        "prebuilt_myjavalib",
+        "prebuilt_mypublicjavalib",
+        "prebuilt_mydefaultedjavalib",
+        "prebuilt_myprivatejavalib",
+    ],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
@@ -398,6 +408,11 @@
 			checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_myjavalib"],
+}
+
 java_import {
     name: "myjavalib",
     prefer: false,
@@ -453,6 +468,11 @@
 			checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mysdklibrary"],
+}
+
 prebuilt_bootclasspath_fragment {
     name: "mybootclasspathfragment",
     prefer: false,
@@ -499,4 +519,140 @@
 		)
 	})
 
+	t.Run("test replacing exportable module", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			prepareForSdkTestWithJava,
+			java.PrepareForTestWithJavaDefaultModules,
+			java.PrepareForTestWithJavaSdkLibraryFiles,
+			java.FixtureWithLastReleaseApis("mysdklibrary", "anothersdklibrary"),
+			android.FixtureWithRootAndroidBp(`
+			sdk {
+				name: "mysdk",
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+			}
+
+			bootclasspath_fragment {
+				name: "mybootclasspathfragment",
+				apex_available: ["myapex"],
+				contents: ["mysdklibrary"],
+				hidden_api: {
+					split_packages: ["*"],
+				},
+				core_platform_api: {
+					stub_libs: [
+						"anothersdklibrary.stubs.exportable",
+					],
+				},
+				api: {
+					stub_libs: [
+						"anothersdklibrary",
+					],
+				},
+			}
+
+			java_sdk_library {
+				name: "mysdklibrary",
+				srcs: ["Test.java"],
+				compile_dex: true,
+				min_sdk_version: "S",
+				public: {enabled: true},
+				permitted_packages: ["mysdklibrary"],
+			}
+
+			java_sdk_library {
+				name: "anothersdklibrary",
+				srcs: ["Test.java"],
+				compile_dex: true,
+				min_sdk_version: "S",
+				public: {enabled: true},
+				system: {enabled: true},
+				module_lib: {enabled: true},
+			}
+		`),
+			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",
+				}
+				variables.Platform_version_active_codenames = []string{"UpsideDownCake", "Tiramisu", "S-V2"}
+			}),
+		).RunTest(t)
+
+		CheckSnapshot(t, result, "mysdk", "",
+			checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_bootclasspath_fragment {
+    name: "mybootclasspathfragment",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["myapex"],
+    contents: ["mysdklibrary"],
+    api: {
+        stub_libs: ["anothersdklibrary"],
+    },
+    core_platform_api: {
+        stub_libs: ["anothersdklibrary.stubs"],
+    },
+    hidden_api: {
+        annotation_flags: "hiddenapi/annotation-flags.csv",
+        metadata: "hiddenapi/metadata.csv",
+        index: "hiddenapi/index.csv",
+        stub_flags: "hiddenapi/stub-flags.csv",
+        all_flags: "hiddenapi/all-flags.csv",
+    },
+}
+
+java_sdk_library_import {
+    name: "mysdklibrary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    shared_library: true,
+    compile_dex: true,
+    permitted_packages: ["mysdklibrary"],
+    public: {
+        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+        current_api: "sdk_library/public/mysdklibrary.txt",
+        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+
+java_sdk_library_import {
+    name: "anothersdklibrary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    shared_library: true,
+    compile_dex: true,
+    public: {
+        jars: ["sdk_library/public/anothersdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/anothersdklibrary_stub_sources"],
+        current_api: "sdk_library/public/anothersdklibrary.txt",
+        removed_api: "sdk_library/public/anothersdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+    system: {
+        jars: ["sdk_library/system/anothersdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/system/anothersdklibrary_stub_sources"],
+        current_api: "sdk_library/system/anothersdklibrary.txt",
+        removed_api: "sdk_library/system/anothersdklibrary-removed.txt",
+        sdk_version: "system_current",
+    },
+    module_lib: {
+        jars: ["sdk_library/module-lib/anothersdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/module-lib/anothersdklibrary_stub_sources"],
+        current_api: "sdk_library/module-lib/anothersdklibrary.txt",
+        removed_api: "sdk_library/module-lib/anothersdklibrary-removed.txt",
+        sdk_version: "module_current",
+    },
+}
+`),
+		)
+	})
+
 }
diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go
index 3c0b8ae..c1c4ed6 100644
--- a/sdk/systemserverclasspath_fragment_sdk_test.go
+++ b/sdk/systemserverclasspath_fragment_sdk_test.go
@@ -34,6 +34,9 @@
 				env["SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE"] = targetBuildRelease
 			}
 		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
+		}),
 		prepareForSdkTestWithApex,
 
 		android.FixtureWithRootAndroidBp(sdk+`
@@ -242,6 +245,11 @@
 	expectedLatestSnapshot := `
 // This is auto-generated. DO NOT EDIT.
 
+apex_contributions_defaults {
+    name: "mysdk.contributions",
+    contents: ["prebuilt_mysdklibrary"],
+}
+
 java_sdk_library_import {
     name: "mysdklibrary",
     prefer: false,
diff --git a/sdk/update.go b/sdk/update.go
index a731414..198c8d4 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -24,6 +24,7 @@
 
 	"android/soong/apex"
 	"android/soong/cc"
+	"android/soong/java"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -162,6 +163,20 @@
 	})
 }
 
+// A denylist of modules whose host variants will be removed from the generated snapshots above the ApiLevel
+// even if they are listed in the corresponding `sdk`.
+// The key is the module name
+// The value is the _last_ dessert where the host variant of the module will be present
+// This is a workaround to ensure that these modules are generated in <=$ApiLevel, but not in in >=$ApiLevel
+var ignoreHostModuleVariantsAboveDessert = map[string]android.ApiLevel{
+	// ignore host variant of libdexfile and its transitive dependencies.
+	// The platform test that depends on them (`libunwindstack_unit_test` at the time of writing)
+	// no longer requires a prebuilt variant of libdexfile.
+	"libdexfile":    android.ApiLevelUpsideDownCake,
+	"libartpalette": android.ApiLevelUpsideDownCake,
+	"libartbase":    android.ApiLevelUpsideDownCake,
+}
+
 // groupMemberVariantsByMemberThenType groups the member variant dependencies so that all the
 // variants of each member are grouped together within an sdkMember instance.
 //
@@ -180,6 +195,14 @@
 		variant := memberVariantDep.variant
 
 		name := ctx.OtherModuleName(variant)
+		targetApiLevel, err := android.ApiLevelFromUser(ctx, targetBuildRelease.name)
+		if err != nil {
+			targetApiLevel = android.FutureApiLevel
+		}
+		if lastApiLevel, exists := ignoreHostModuleVariantsAboveDessert[name]; exists && targetApiLevel.GreaterThan(lastApiLevel) && memberVariantDep.Host() {
+			// ignore host variant of this module if the targetApiLevel is V and above.
+			continue
+		}
 		member := byName[name]
 		if member == nil {
 			member = &sdkMember{memberType: memberType, name: name}
@@ -388,6 +411,7 @@
 
 	// Create the prebuilt modules for each of the member modules.
 	traits := s.gatherTraits()
+	memberNames := []string{} // soong module names of the members. contains the prebuilt_ prefix.
 	for _, member := range members {
 		memberType := member.memberType
 		if !memberType.ArePrebuiltsRequired() {
@@ -409,6 +433,46 @@
 
 		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
+			// 2. Skip non-sdk contents of BCP and SCCP fragments
+			//
+			// The non-sdk contents of BCP/SSCP fragments should only be used for dexpreopt and hiddenapi,
+			// and are not available to the rest of the build.
+			if android.InList(member.memberType,
+				[]android.SdkMemberType{
+					// bcp
+					java.BootclasspathFragmentSdkMemberType,
+					java.JavaBootLibsSdkMemberType,
+					// sscp
+					java.SystemServerClasspathFragmentSdkMemberType,
+					java.JavaSystemserverLibsSdkMemberType,
+				},
+			) {
+				continue
+			}
+
+			memberNames = append(memberNames, android.PrebuiltNameFromSource(member.name))
+		}
+	}
+
+	// create an apex_contributions_defaults for this module's sdk.
+	// this module type is supported in V and above.
+	if targetApiLevel.GreaterThan(android.ApiLevelUpsideDownCake) {
+		ac := newModule("apex_contributions_defaults")
+		ac.AddProperty("name", s.Name()+".contributions")
+		ac.AddProperty("contents", memberNames)
+		bpFile.AddModule(ac)
 	}
 
 	// Create a transformer that will transform a module by replacing any references
@@ -424,6 +488,12 @@
 		// Transform the module module to make it suitable for use in the snapshot.
 		module = transformModule(module, snapshotTransformer)
 		module = transformModule(module, emptyClasspathContentsTransformation{})
+
+		targetApiLevel, err := android.ApiLevelFromUserWithConfig(ctx.Config(), s.targetBuildRelease(ctx).name)
+		if err == nil && targetApiLevel.LessThan(android.ApiLevelVanillaIceCream) {
+			module = transformModule(module, replaceExportablePropertiesTransformer{})
+		}
+
 		if module != nil {
 			bpFile.AddModule(module)
 		}
@@ -748,6 +818,50 @@
 	}
 }
 
+type replaceExportablePropertiesTransformer struct {
+	identityTransformation
+}
+
+var _ bpTransformer = (*replaceExportablePropertiesTransformer)(nil)
+
+func handleExportableProperties[T any](value T) any {
+	switch v := any(value).(type) {
+	case string:
+		return java.AllApiScopes.ConvertStubsLibraryExportableToEverything(v)
+	case *bpPropertySet:
+		v.properties = handleExportableProperties(v.properties).(map[string]interface{})
+		return v
+	case []string:
+		result := make([]string, len(v))
+		for i, elem := range v {
+			result[i] = handleExportableProperties(elem).(string)
+		}
+		return result
+	case []any:
+		result := make([]any, len(v))
+		for i, elem := range v {
+			result[i] = handleExportableProperties(elem)
+		}
+		return result
+	case map[string]any:
+		result := make(map[string]any)
+		for k, val := range v {
+			result[k] = handleExportableProperties(val)
+		}
+		return result
+	default:
+		return value
+	}
+}
+
+func (t replaceExportablePropertiesTransformer) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
+	if name == "name" {
+		return propertySet, tag
+	}
+	propertySet.properties = handleExportableProperties(propertySet.properties).(map[string]interface{})
+	return propertySet, tag
+}
+
 func generateBpContents(bpFile *bpFile) string {
 	contents := &generatedContents{}
 	contents.IndentedPrintf("// This is auto-generated. DO NOT EDIT.\n")
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 97adeed..2e48d83 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -15,17 +15,16 @@
 package sh
 
 import (
-	"fmt"
 	"path/filepath"
 	"strings"
 
 	"android/soong/testing"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 	"android/soong/cc"
-	"android/soong/snapshot"
 	"android/soong/tradefed"
 )
 
@@ -100,6 +99,12 @@
 
 	// Make this module available when building for recovery.
 	Recovery_available *bool
+
+	// The name of the image this module is built for
+	ImageVariation string `blueprint:"mutated"`
+
+	// Suffix for the name of Android.mk entries generated by this module
+	SubName string `blueprint:"mutated"`
 }
 
 type TestProperties struct {
@@ -188,15 +193,6 @@
 	return s.outputFilePath
 }
 
-func (s *ShBinary) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return android.Paths{s.outputFilePath}, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func (s *ShBinary) SubDir() string {
 	return proptools.String(s.properties.Sub_dir)
 }
@@ -216,16 +212,24 @@
 
 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.ModuleBase.InstallInRecovery() && !s.ModuleBase.InstallInRamdisk()
+	return !s.InstallInRecovery() && !s.InstallInRamdisk() && !s.InstallInVendorRamdisk() && !s.ModuleBase.InstallInVendor()
 }
 
 func (s *ShBinary) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return proptools.Bool(s.properties.Ramdisk_available) || s.ModuleBase.InstallInRamdisk()
+	return proptools.Bool(s.properties.Ramdisk_available) || s.InstallInRamdisk()
 }
 
 func (s *ShBinary) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return proptools.Bool(s.properties.Vendor_ramdisk_available) || s.ModuleBase.InstallInVendorRamdisk()
+	return proptools.Bool(s.properties.Vendor_ramdisk_available) || s.InstallInVendorRamdisk()
 }
 
 func (s *ShBinary) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -233,14 +237,36 @@
 }
 
 func (s *ShBinary) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
-	return proptools.Bool(s.properties.Recovery_available) || s.ModuleBase.InstallInRecovery()
+	return proptools.Bool(s.properties.Recovery_available) || s.InstallInRecovery()
 }
 
 func (s *ShBinary) ExtraImageVariations(ctx android.BaseModuleContext) []string {
 	return nil
 }
 
-func (s *ShBinary) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+func (s *ShBinary) SetImageVariation(ctx android.BaseModuleContext, variation string) {
+	s.properties.ImageVariation = variation
+}
+
+// Overrides ModuleBase.InstallInRamdisk() so that the install rule respects
+// Ramdisk_available property for ramdisk variant
+func (s *ShBinary) InstallInRamdisk() bool {
+	return s.ModuleBase.InstallInRamdisk() ||
+		(proptools.Bool(s.properties.Ramdisk_available) && s.properties.ImageVariation == android.RamdiskVariation)
+}
+
+// Overrides ModuleBase.InstallInVendorRamdisk() so that the install rule respects
+// Vendor_ramdisk_available property for vendor ramdisk variant
+func (s *ShBinary) InstallInVendorRamdisk() bool {
+	return s.ModuleBase.InstallInVendorRamdisk() ||
+		(proptools.Bool(s.properties.Vendor_ramdisk_available) && s.properties.ImageVariation == android.VendorRamdiskVariation)
+}
+
+// Overrides ModuleBase.InstallInRecovery() so that the install rule respects
+// Recovery_available property for recovery variant
+func (s *ShBinary) InstallInRecovery() bool {
+	return s.ModuleBase.InstallInRecovery() ||
+		(proptools.Bool(s.properties.Recovery_available) && s.properties.ImageVariation == android.RecoveryVariation)
 }
 
 func (s *ShBinary) generateAndroidBuildActions(ctx android.ModuleContext) {
@@ -270,7 +296,22 @@
 		Output: s.outputFilePath,
 		Input:  s.sourceFilePath,
 	})
+
+	s.properties.SubName = s.GetSubname(ctx)
+
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: []string{s.sourceFilePath.String()}})
+
+	ctx.SetOutputFiles(android.Paths{s.outputFilePath}, "")
+}
+
+func (s *ShBinary) GetSubname(ctx android.ModuleContext) string {
+	ret := ""
+	if s.properties.ImageVariation != "" {
+		if s.properties.ImageVariation != android.VendorVariation {
+			ret = "." + s.properties.ImageVariation
+		}
+	}
+	return ret
 }
 
 func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -286,7 +327,7 @@
 }
 
 func (s *ShBinary) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
+	return []android.AndroidMkEntries{{
 		Class:      "EXECUTABLES",
 		OutputFile: android.OptionalPathForPath(s.outputFilePath),
 		Include:    "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
@@ -297,6 +338,7 @@
 				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !s.Installable())
 			},
 		},
+		SubName: s.properties.SubName,
 	}}
 }
 
@@ -535,5 +577,3 @@
 }
 
 var Bool = proptools.Bool
-
-var _ snapshot.RelativeInstallPath = (*ShBinary)(nil)
diff --git a/shared/Android.bp b/shared/Android.bp
index 3c84f55..d5e8614 100644
--- a/shared/Android.bp
+++ b/shared/Android.bp
@@ -15,7 +15,6 @@
         "paths_test.go",
     ],
     deps: [
-        "soong-bazel",
         "golang-protobuf-proto",
     ],
 }
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 3354993..6cb318e 100644
--- a/snapshot/Android.bp
+++ b/snapshot/Android.bp
@@ -16,15 +16,11 @@
     srcs: [
         "host_fake_snapshot.go",
         "host_snapshot.go",
-        "recovery_snapshot.go",
-        "snapshot.go",
         "snapshot_base.go",
         "util.go",
-        "vendor_snapshot.go",
     ],
     testSrcs: [
         "host_test.go",
-        "test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
index 63cd4e1..278247e 100644
--- a/snapshot/host_fake_snapshot.go
+++ b/snapshot/host_fake_snapshot.go
@@ -116,7 +116,7 @@
 			prebuilts[android.RemoveOptionalPrebuiltPrefix(module.Name())] = true
 			return
 		}
-		if !module.Enabled() || module.IsHideFromMake() {
+		if !module.Enabled(ctx) || module.IsHideFromMake() {
 			return
 		}
 		apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
@@ -129,12 +129,12 @@
 			if !seen[outFile] {
 				seen[outFile] = true
 				outputs = append(outputs, WriteStringToFileRule(ctx, "", outFile))
-				jsonData = append(jsonData, hostSnapshotFakeJsonFlags{*hostJsonDesc(module), false})
+				jsonData = append(jsonData, hostSnapshotFakeJsonFlags{*hostJsonDesc(ctx, module), false})
 			}
 		}
 	})
 	// Update any module prebuilt information
-	for idx, _ := range jsonData {
+	for idx := range jsonData {
 		if _, ok := prebuilts[jsonData[idx].ModuleName]; ok {
 			// Prebuilt exists for this module
 			jsonData[idx].Prebuilt = true
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
index edcc163..1ecab7d 100644
--- a/snapshot/host_snapshot.go
+++ b/snapshot/host_snapshot.go
@@ -101,7 +101,7 @@
 
 	// Create JSON file based on the direct dependencies
 	ctx.VisitDirectDeps(func(dep android.Module) {
-		desc := hostJsonDesc(dep)
+		desc := hostJsonDesc(ctx, dep)
 		if desc != nil {
 			jsonData = append(jsonData, *desc)
 		}
@@ -209,7 +209,7 @@
 
 // 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 {
+func hostJsonDesc(ctx android.ConfigAndErrorContext, m android.Module) *SnapshotJsonFlags {
 	path := hostToolPath(m)
 	relPath := hostRelativePathString(m)
 	procMacro := false
@@ -226,7 +226,7 @@
 		props := &SnapshotJsonFlags{
 			ModuleStemName:      moduleStem,
 			Filename:            path.String(),
-			Required:            append(m.HostRequiredModuleNames(), m.RequiredModuleNames()...),
+			Required:            append(m.HostRequiredModuleNames(), m.RequiredModuleNames(ctx)...),
 			RelativeInstallPath: relPath,
 			RustProcMacro:       procMacro,
 			CrateName:           crateName,
diff --git a/snapshot/recovery_snapshot.go b/snapshot/recovery_snapshot.go
deleted file mode 100644
index ab114b4..0000000
--- a/snapshot/recovery_snapshot.go
+++ /dev/null
@@ -1,132 +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 "android/soong/android"
-
-// Interface for modules which can be captured in the recovery snapshot.
-type RecoverySnapshotModuleInterface interface {
-	SnapshotModuleInterfaceBase
-	InRecovery() bool
-	ExcludeFromRecoverySnapshot() bool
-}
-
-func RecoverySnapshotSingleton() android.Singleton {
-	return &SnapshotSingleton{
-		"recovery",                     // name
-		"SOONG_RECOVERY_SNAPSHOT_ZIP",  // makeVar
-		android.OptionalPath{},         // snapshotZipFile
-		RecoverySnapshotImageSingleton, // Image
-		false,                          // Fake
-	}
-}
-
-// Determine if a dir under source tree is an SoC-owned proprietary directory based
-// on recovery snapshot configuration
-// Examples: device/, vendor/
-func isRecoveryProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
-	return RecoverySnapshotSingleton().(*SnapshotSingleton).Image.IsProprietaryPath(dir, deviceConfig)
-}
-
-func IsRecoveryProprietaryModule(ctx android.BaseModuleContext) bool {
-
-	// Any module in a recovery proprietary path is a recovery proprietary
-	// module.
-	if isRecoveryProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
-		return true
-	}
-
-	// However if the module is not in a recovery proprietary path, it may
-	// still be a recovery proprietary module. This happens for cc modules
-	// that are excluded from the recovery snapshot, and it means that the
-	// vendor has assumed control of the framework-provided module.
-
-	if c, ok := ctx.Module().(RecoverySnapshotModuleInterface); ok {
-		if c.ExcludeFromRecoverySnapshot() {
-			return true
-		}
-	}
-
-	return false
-}
-
-var RecoverySnapshotImageName = "recovery"
-
-type RecoverySnapshotImage struct{}
-
-func (RecoverySnapshotImage) Init(ctx android.RegistrationContext) {
-	ctx.RegisterParallelSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
-}
-
-func (RecoverySnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
-	ctx.RegisterModuleType(name, factory)
-}
-
-func (RecoverySnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
-	// RECOVERY_SNAPSHOT_VERSION must be set to 'current' in order to generate a
-	// snapshot.
-	return ctx.DeviceConfig().RecoverySnapshotVersion() == "current"
-}
-
-func (RecoverySnapshotImage) InImage(m SnapshotModuleInterfaceBase) func() bool {
-	r, ok := m.(RecoverySnapshotModuleInterface)
-
-	if !ok {
-		// This module does not support recovery snapshot
-		return func() bool { return false }
-	}
-	return r.InRecovery
-}
-
-func (RecoverySnapshotImage) IsProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
-	return isDirectoryExcluded(dir, deviceConfig.RecoverySnapshotDirsExcludedMap(), deviceConfig.RecoverySnapshotDirsIncludedMap())
-}
-
-func (RecoverySnapshotImage) ExcludeFromSnapshot(m SnapshotModuleInterfaceBase) bool {
-	r, ok := m.(RecoverySnapshotModuleInterface)
-
-	if !ok {
-		// This module does not support recovery snapshot
-		return true
-	}
-	return r.ExcludeFromRecoverySnapshot()
-}
-
-func (RecoverySnapshotImage) IsUsingSnapshot(cfg android.DeviceConfig) bool {
-	recoverySnapshotVersion := cfg.RecoverySnapshotVersion()
-	return recoverySnapshotVersion != "current" && recoverySnapshotVersion != ""
-}
-
-func (RecoverySnapshotImage) TargetSnapshotVersion(cfg android.DeviceConfig) string {
-	return cfg.RecoverySnapshotVersion()
-}
-
-func (RecoverySnapshotImage) ExcludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
-	// If we're using full snapshot, not directed snapshot, capture every module
-	if !cfg.DirectedRecoverySnapshot() {
-		return false
-	}
-	// Else, checks if name is in RECOVERY_SNAPSHOT_MODULES.
-	return !cfg.RecoverySnapshotModules()[name]
-}
-
-func (RecoverySnapshotImage) ImageName() string {
-	return RecoverySnapshotImageName
-}
-
-var RecoverySnapshotImageSingleton RecoverySnapshotImage
-
-func init() {
-	RecoverySnapshotImageSingleton.Init(android.InitRegistrationContext)
-}
diff --git a/snapshot/snapshot.go b/snapshot/snapshot.go
deleted file mode 100644
index c95a537..0000000
--- a/snapshot/snapshot.go
+++ /dev/null
@@ -1,146 +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"
-	"sort"
-
-	"android/soong/android"
-)
-
-// This file contains singletons to capture snapshots. This singleton will generate snapshot of each target
-// image, and capturing snapshot module will be delegated to each module which implements GenerateSnapshotAction
-// function and register with RegisterSnapshotAction.
-
-var pctx = android.NewPackageContext("android/soong/snapshot")
-
-func init() {
-	pctx.Import("android/soong/android")
-}
-
-type SnapshotSingleton struct {
-	// Name, e.g., "vendor", "recovery", "ramdisk".
-	name string
-
-	// Make variable that points to the snapshot file, e.g.,
-	// "SOONG_RECOVERY_SNAPSHOT_ZIP".
-	makeVar string
-
-	// Path to the snapshot zip file.
-	snapshotZipFile android.OptionalPath
-
-	// Implementation of the image interface specific to the image
-	// associated with this snapshot (e.g., specific to the vendor image,
-	// recovery image, etc.).
-	Image SnapshotImage
-
-	// Whether this singleton is for fake snapshot or not.
-	// Fake snapshot is a snapshot whose prebuilt binaries and headers are empty.
-	// It is much faster to generate, and can be used to inspect dependencies.
-	Fake bool
-}
-
-// The output files to be included in the snapshot.
-type SnapshotPaths struct {
-	// All files to be included in the snapshot
-	OutputFiles android.Paths
-
-	// Notice files of the snapshot output files
-	NoticeFiles android.Paths
-}
-
-// Interface of function to capture snapshot from each module
-// Returns snapshot ouputs and notice files.
-type GenerateSnapshotAction func(snapshot SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) SnapshotPaths
-
-var snapshotActionList []GenerateSnapshotAction
-
-// Register GenerateSnapshotAction function so it can be called while generating snapshot
-func RegisterSnapshotAction(x GenerateSnapshotAction) {
-	snapshotActionList = append(snapshotActionList, x)
-}
-
-func (c *SnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	if !c.Image.shouldGenerateSnapshot(ctx) {
-		return
-	}
-
-	var snapshotOutputs android.Paths
-
-	// Snapshot zipped artifacts will be captured under {SNAPSHOT_ARCH} directory
-
-	snapshotDir := c.name + "-snapshot"
-	if c.Fake {
-		// If this is a fake snapshot singleton, place all files under fake/ subdirectory to avoid
-		// collision with real snapshot files
-		snapshotDir = filepath.Join("fake", snapshotDir)
-	}
-	snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
-	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
-	installedNotices := make(map[string]bool)
-
-	for _, f := range snapshotActionList {
-		snapshotPaths := f(*c, ctx, snapshotArchDir)
-		snapshotOutputs = append(snapshotOutputs, snapshotPaths.OutputFiles...)
-		for _, notice := range snapshotPaths.NoticeFiles {
-			if _, ok := installedNotices[notice.String()]; !ok {
-				installedNotices[notice.String()] = true
-				snapshotOutputs = append(snapshotOutputs, CopyFileRule(
-					pctx, ctx, notice, filepath.Join(noticeDir, notice.String())))
-			}
-		}
-	}
-
-	// All artifacts are ready. Sort them to normalize ninja and then zip.
-	sort.Slice(snapshotOutputs, func(i, j int) bool {
-		return snapshotOutputs[i].String() < snapshotOutputs[j].String()
-	})
-
-	zipPath := android.PathForOutput(
-		ctx,
-		snapshotDir,
-		c.name+"-"+ctx.Config().DeviceName()+".zip")
-	zipRule := android.NewRuleBuilder(pctx, ctx)
-
-	// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
-	snapshotOutputList := android.PathForOutput(
-		ctx,
-		snapshotDir,
-		c.name+"-"+ctx.Config().DeviceName()+"_list")
-	rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp")
-	zipRule.Command().
-		Text("tr").
-		FlagWithArg("-d ", "\\'").
-		FlagWithRspFileInputList("< ", rspFile, snapshotOutputs).
-		FlagWithOutput("> ", snapshotOutputList)
-
-	zipRule.Temporary(snapshotOutputList)
-
-	zipRule.Command().
-		BuiltTool("soong_zip").
-		FlagWithOutput("-o ", zipPath).
-		FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
-		FlagWithInput("-l ", snapshotOutputList)
-
-	zipRule.Build(zipPath.String(), c.name+" snapshot "+zipPath.String())
-	zipRule.DeleteTemporaryFiles()
-	c.snapshotZipFile = android.OptionalPathForPath(zipPath)
-}
-
-func (c *SnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.Strict(
-		c.makeVar,
-		c.snapshotZipFile.String())
-}
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index fb4ee0c..6bf3c87 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -15,92 +15,12 @@
 
 import (
 	"android/soong/android"
-	"path/filepath"
 )
 
-// Interface for modules which can be captured in the snapshot.
-type SnapshotModuleInterfaceBase interface{}
+var pctx = android.NewPackageContext("android/soong/snapshot")
 
-// Defines the specifics of different images to which the snapshot process is applicable, e.g.,
-// vendor, recovery, ramdisk.
-type SnapshotImage interface {
-	// Returns true if a snapshot should be generated for this image.
-	shouldGenerateSnapshot(ctx android.SingletonContext) bool
-
-	// Function that returns true if the module is included in this image.
-	// Using a function return instead of a value to prevent early
-	// evalution of a function that may be not be defined.
-	InImage(m SnapshotModuleInterfaceBase) func() bool
-
-	// Returns true if a dir under source tree is an SoC-owned proprietary
-	// directory, such as device/, vendor/, etc.
-	//
-	// For a given snapshot (e.g., vendor, recovery, etc.) if
-	// isProprietaryPath(dir, deviceConfig) returns true, then the module in dir
-	// will be built from sources.
-	IsProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool
-
-	// Whether a given module has been explicitly excluded from the
-	// snapshot, e.g., using the exclude_from_vendor_snapshot or
-	// exclude_from_recovery_snapshot properties.
-	ExcludeFromSnapshot(m SnapshotModuleInterfaceBase) bool
-
-	// Returns true if the build is using a snapshot for this image.
-	IsUsingSnapshot(cfg android.DeviceConfig) bool
-
-	// Returns a version of which the snapshot should be used in this target.
-	// This will only be meaningful when isUsingSnapshot is true.
-	TargetSnapshotVersion(cfg android.DeviceConfig) string
-
-	// Whether to exclude a given module from the directed snapshot or not.
-	// If the makefile variable DIRECTED_{IMAGE}_SNAPSHOT is true, directed snapshot is turned on,
-	// and only modules listed in {IMAGE}_SNAPSHOT_MODULES will be captured.
-	ExcludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool
-
-	// Returns target image name
-	ImageName() string
-}
-
-type directoryMap map[string]bool
-
-var (
-	// Modules under following directories are ignored. They are OEM's and vendor's
-	// proprietary modules(device/, kernel/, vendor/, and hardware/).
-	defaultDirectoryExcludedMap = directoryMap{
-		"device":   true,
-		"hardware": true,
-		"kernel":   true,
-		"vendor":   true,
-	}
-
-	// Modules under following directories are included as they are in AOSP,
-	// although hardware/ and kernel/ are normally for vendor's own.
-	defaultDirectoryIncludedMap = directoryMap{
-		"kernel/configs":              true,
-		"kernel/prebuilts":            true,
-		"kernel/tests":                true,
-		"hardware/interfaces":         true,
-		"hardware/libhardware":        true,
-		"hardware/libhardware_legacy": true,
-		"hardware/ril":                true,
-	}
-)
-
-func isDirectoryExcluded(dir string, excludedMap directoryMap, includedMap directoryMap) bool {
-	if dir == "." || dir == "/" {
-		return false
-	}
-	if includedMap[dir] {
-		return false
-	} else if excludedMap[dir] {
-		return true
-	} else if defaultDirectoryIncludedMap[dir] {
-		return false
-	} else if defaultDirectoryExcludedMap[dir] {
-		return true
-	} else {
-		return isDirectoryExcluded(filepath.Dir(dir), excludedMap, includedMap)
-	}
+func init() {
+	pctx.Import("android/soong/android")
 }
 
 // This is to be saved as .json files, which is for development/vendor_snapshot/update.py.
diff --git a/snapshot/test.go b/snapshot/test.go
deleted file mode 100644
index 346af2b..0000000
--- a/snapshot/test.go
+++ /dev/null
@@ -1,24 +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 (
-	"os"
-	"testing"
-)
-
-func TestMain(m *testing.M) {
-	os.Exit(m.Run())
-}
diff --git a/snapshot/vendor_snapshot.go b/snapshot/vendor_snapshot.go
deleted file mode 100644
index 3e5f546..0000000
--- a/snapshot/vendor_snapshot.go
+++ /dev/null
@@ -1,143 +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 "android/soong/android"
-
-// Interface for modules which can be captured in the vendor snapshot.
-type VendorSnapshotModuleInterface interface {
-	SnapshotModuleInterfaceBase
-	InVendor() bool
-	ExcludeFromVendorSnapshot() bool
-}
-
-func VendorSnapshotSingleton() android.Singleton {
-	return &SnapshotSingleton{
-		"vendor",                     // name
-		"SOONG_VENDOR_SNAPSHOT_ZIP",  // makeVar
-		android.OptionalPath{},       // snapshotZipFile
-		VendorSnapshotImageSingleton, // Image
-		false,                        // Fake
-	}
-}
-
-func VendorFakeSnapshotSingleton() android.Singleton {
-	return &SnapshotSingleton{
-		"vendor",                         // name
-		"SOONG_VENDOR_FAKE_SNAPSHOT_ZIP", // makeVar
-		android.OptionalPath{},           // snapshotZipFile
-		VendorSnapshotImageSingleton,     // Image
-		true,                             // Fake
-	}
-}
-
-// Determine if a dir under source tree is an SoC-owned proprietary directory based
-// on vendor snapshot configuration
-// Examples: device/, vendor/
-func isVendorProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
-	return VendorSnapshotSingleton().(*SnapshotSingleton).Image.IsProprietaryPath(dir, deviceConfig)
-}
-
-func IsVendorProprietaryModule(ctx android.BaseModuleContext) bool {
-	// Any module in a vendor proprietary path is a vendor proprietary
-	// module.
-	if isVendorProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
-		return true
-	}
-
-	// However if the module is not in a vendor proprietary path, it may
-	// still be a vendor proprietary module. This happens for cc modules
-	// that are excluded from the vendor snapshot, and it means that the
-	// vendor has assumed control of the framework-provided module.
-	if c, ok := ctx.Module().(VendorSnapshotModuleInterface); ok {
-		if c.ExcludeFromVendorSnapshot() {
-			return true
-		}
-	}
-
-	return false
-}
-
-var VendorSnapshotImageName = "vendor"
-
-type VendorSnapshotImage struct{}
-
-func (VendorSnapshotImage) Init(ctx android.RegistrationContext) {
-	ctx.RegisterParallelSingletonType("vendor-snapshot", VendorSnapshotSingleton)
-	ctx.RegisterParallelSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
-}
-
-func (VendorSnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
-	ctx.RegisterModuleType(name, factory)
-}
-
-func (VendorSnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
-	// BOARD_VNDK_VERSION must be set to 'current' in order to generate a snapshot.
-	return ctx.DeviceConfig().VndkVersion() == "current"
-}
-
-func (VendorSnapshotImage) InImage(m SnapshotModuleInterfaceBase) func() bool {
-	v, ok := m.(VendorSnapshotModuleInterface)
-
-	if !ok {
-		// This module does not support Vendor snapshot
-		return func() bool { return false }
-	}
-
-	return v.InVendor
-}
-
-func (VendorSnapshotImage) IsProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
-	return isDirectoryExcluded(dir, deviceConfig.VendorSnapshotDirsExcludedMap(), deviceConfig.VendorSnapshotDirsIncludedMap())
-}
-
-func (VendorSnapshotImage) ExcludeFromSnapshot(m SnapshotModuleInterfaceBase) bool {
-	v, ok := m.(VendorSnapshotModuleInterface)
-
-	if !ok {
-		// This module does not support Vendor snapshot
-		return true
-	}
-
-	return v.ExcludeFromVendorSnapshot()
-}
-
-func (VendorSnapshotImage) IsUsingSnapshot(cfg android.DeviceConfig) bool {
-	vndkVersion := cfg.VndkVersion()
-	return vndkVersion != "current" && vndkVersion != ""
-}
-
-func (VendorSnapshotImage) TargetSnapshotVersion(cfg android.DeviceConfig) string {
-	return cfg.VndkVersion()
-}
-
-// returns true iff a given module SHOULD BE EXCLUDED, false if included
-func (VendorSnapshotImage) ExcludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
-	// If we're using full snapshot, not directed snapshot, capture every module
-	if !cfg.DirectedVendorSnapshot() {
-		return false
-	}
-	// Else, checks if name is in VENDOR_SNAPSHOT_MODULES.
-	return !cfg.VendorSnapshotModules()[name]
-}
-
-func (VendorSnapshotImage) ImageName() string {
-	return VendorSnapshotImageName
-}
-
-var VendorSnapshotImageSingleton VendorSnapshotImage
-
-func init() {
-	VendorSnapshotImageSingleton.Init(android.InitRegistrationContext)
-}
diff --git a/soong_ui.bash b/soong_ui.bash
index 8e7cd19..7737880 100755
--- a/soong_ui.bash
+++ b/soong_ui.bash
@@ -35,6 +35,7 @@
 soong_build_go soong_ui android/soong/cmd/soong_ui
 soong_build_go mk2rbc android/soong/mk2rbc/mk2rbc
 soong_build_go rbcrun rbcrun/rbcrun
+soong_build_go release-config android/soong/cmd/release_config/release_config
 
 cd ${TOP}
 exec "$(getoutdir)/soong_ui" "$@"
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index b9b68be..84f20c5 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -49,8 +49,6 @@
 	android.ModuleBase
 
 	properties syspropGenProperties
-
-	genSrcjars android.Paths
 }
 
 type syspropRustGenRule struct {
@@ -59,7 +57,6 @@
 	properties rustLibraryProperties
 }
 
-var _ android.OutputFileProducer = (*syspropJavaGenRule)(nil)
 var _ rust.SourceProvider = (*syspropRustGenRule)(nil)
 
 var (
@@ -100,6 +97,7 @@
 		}
 	})
 
+	var genSrcjars android.Paths
 	for _, syspropFile := range android.PathsForModuleSrc(ctx, g.properties.Srcs) {
 		srcJarFile := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcjar")
 
@@ -114,8 +112,10 @@
 			},
 		})
 
-		g.genSrcjars = append(g.genSrcjars, srcJarFile)
+		genSrcjars = append(genSrcjars, srcJarFile)
 	}
+
+	ctx.SetOutputFiles(genSrcjars, "")
 }
 
 func (g *syspropJavaGenRule) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -124,15 +124,6 @@
 	ctx.AddFarVariationDependencies(nil, nil, proptools.String(g.properties.Check_api))
 }
 
-func (g *syspropJavaGenRule) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return g.genSrcjars, nil
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-	}
-}
-
 func syspropJavaGenFactory() android.Module {
 	g := &syspropJavaGenRule{}
 	g.AddProperties(&g.properties)
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index dfbbe7d..7d4e69d 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -134,8 +134,6 @@
 		PrepareForTestWithSyspropBuildComponents,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.DeviceSystemSdkVersions = []string{"28"}
-			variables.DeviceVndkVersion = proptools.StringPtr("current")
-			variables.Platform_vndk_version = proptools.StringPtr("29")
 			variables.DeviceCurrentApiLevelForVendorModules = proptools.StringPtr("28")
 		}),
 		java.FixtureWithPrebuiltApis(map[string][]string{
@@ -258,10 +256,10 @@
 
 	// Check for generated cc_library
 	for _, variant := range []string{
-		"android_vendor.29_arm_armv7-a-neon_shared",
-		"android_vendor.29_arm_armv7-a-neon_static",
-		"android_vendor.29_arm64_armv8-a_shared",
-		"android_vendor.29_arm64_armv8-a_static",
+		"android_vendor_arm_armv7-a-neon_shared",
+		"android_vendor_arm_armv7-a-neon_static",
+		"android_vendor_arm64_armv8-a_shared",
+		"android_vendor_arm64_armv8-a_static",
 	} {
 		result.ModuleForTests("libsysprop-platform", variant)
 		result.ModuleForTests("libsysprop-vendor", variant)
@@ -270,10 +268,10 @@
 
 	// product variant of vendor-owned sysprop_library
 	for _, variant := range []string{
-		"android_product.29_arm_armv7-a-neon_shared",
-		"android_product.29_arm_armv7-a-neon_static",
-		"android_product.29_arm64_armv8-a_shared",
-		"android_product.29_arm64_armv8-a_static",
+		"android_product_arm_armv7-a-neon_shared",
+		"android_product_arm_armv7-a-neon_static",
+		"android_product_arm64_armv8-a_shared",
+		"android_product_arm64_armv8-a_static",
 	} {
 		result.ModuleForTests("libsysprop-vendor-on-product", variant)
 	}
@@ -296,16 +294,16 @@
 
 	// Check for exported includes
 	coreVariant := "android_arm64_armv8-a_static"
-	vendorVariant := "android_vendor.29_arm64_armv8-a_static"
-	productVariant := "android_product.29_arm64_armv8-a_static"
+	vendorVariant := "android_vendor_arm64_armv8-a_static"
+	productVariant := "android_product_arm64_armv8-a_static"
 
 	platformInternalPath := "libsysprop-platform/android_arm64_armv8-a_static/gen/sysprop/include"
-	platformPublicVendorPath := "libsysprop-platform/android_vendor.29_arm64_armv8-a_static/gen/sysprop/public/include"
+	platformPublicVendorPath := "libsysprop-platform/android_vendor_arm64_armv8-a_static/gen/sysprop/public/include"
 
-	platformOnProductPath := "libsysprop-platform-on-product/android_product.29_arm64_armv8-a_static/gen/sysprop/public/include"
+	platformOnProductPath := "libsysprop-platform-on-product/android_product_arm64_armv8-a_static/gen/sysprop/public/include"
 
-	vendorInternalPath := "libsysprop-vendor/android_vendor.29_arm64_armv8-a_static/gen/sysprop/include"
-	vendorOnProductPath := "libsysprop-vendor-on-product/android_product.29_arm64_armv8-a_static/gen/sysprop/public/include"
+	vendorInternalPath := "libsysprop-vendor/android_vendor_arm64_armv8-a_static/gen/sysprop/include"
+	vendorOnProductPath := "libsysprop-vendor-on-product/android_product_arm64_armv8-a_static/gen/sysprop/public/include"
 
 	platformClient := result.ModuleForTests("cc-client-platform", coreVariant)
 	platformFlags := platformClient.Rule("cc").Args["cFlags"]
diff --git a/tests/Android.bp b/tests/Android.bp
new file mode 100644
index 0000000..458cf4b
--- /dev/null
+++ b/tests/Android.bp
@@ -0,0 +1,34 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+    default_team: "trendy_team_build",
+}
+
+python_test_host {
+    name: "run_tool_with_logging_test",
+    main: "run_tool_with_logging_test.py",
+    pkg_path: "testdata",
+    srcs: [
+        "run_tool_with_logging_test.py",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    data: [
+        ":run_tool_with_logging_script",
+        ":tool_event_logger",
+    ],
+}
diff --git a/tests/androidmk_test.sh b/tests/androidmk_test.sh
index b81828b..aecc4e8 100755
--- a/tests/androidmk_test.sh
+++ b/tests/androidmk_test.sh
@@ -5,7 +5,7 @@
 # How to run: bash path-to-script/androidmk_test.sh
 # Tests of converting license functionality of the androidmk tool
 REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
-"$REAL_TOP/build/soong/soong_ui.bash" --make-mode androidmk
+"$REAL_TOP/build/soong/soong_ui.bash" --make-mode TARGET_RELEASE=trunk_staging androidmk
 
 source "$(dirname "$0")/lib.sh"
 
diff --git a/tests/apex_cc_module_arch_variant_tests.sh b/tests/apex_cc_module_arch_variant_tests.sh
deleted file mode 100755
index 1f5e003..0000000
--- a/tests/apex_cc_module_arch_variant_tests.sh
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/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.
-
-set -uo pipefail
-
-# Integration test for verifying arch variant cflags set on cc modules included
-# in Bazel-built apexes in the real source tree.
-
-if [ ! -e "build/make/core/Makefile" ]; then
-  echo "$0 must be run from the top of the Android source tree."
-  exit 1
-fi
-
-############
-# Test Setup
-############
-
-OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
-BAZEL_OUTPUT_DIR="$OUTPUT_DIR/bazel"
-
-export TARGET_PRODUCT="aosp_arm64"
-[ "$#" -ge 1 ] && export TARGET_PRODUCT="$1"
-ARCH_VARIANT_CFLAG="armv8-a"
-[ "$#" -ge 2 ] && ARCH_VARIANT_CFLAG="$2"
-CPU_VARIANT_CFLAG=""
-[ "$#" -ge 3 ] && CPU_VARIANT_CFLAG="$3"
-
-function call_bazel() {
-  build/bazel/bin/bazel --output_base="$BAZEL_OUTPUT_DIR" $@
-}
-
-function cleanup {
-  # call bazel clean because some bazel outputs don't have w bits.
-  call_bazel clean
-  rm -rf "${OUTPUT_DIR}"
-}
-trap cleanup EXIT
-
-######################
-# Run bp2build / Bazel
-######################
-build/soong/soong_ui.bash --make-mode BP2BUILD_VERBOSE=1 --skip-soong-tests bp2build
-
-# Number of CppCompile actions with arch variant flag
-actions_with_arch_variant_num=$(call_bazel aquery --config=bp2build --config=ci --config=android \
-  'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal))' | grep -c \'-march=$ARCH_VARIANT_CFLAG\')
-
-# Number of all CppCompile actions
-all_cppcompile_actions_num=0
-aquery_summary=$(call_bazel aquery --config=bp2build --config=ci --config=android --output=summary \
-  'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal))' \
-  | egrep -o '.*opt-ST.*: ([0-9]+)$' \
-  | cut -d: -f2 -)
-
-while read -r num;
-do
-  all_cppcompile_actions_num=$(($all_cppcompile_actions_num + $num))
-done <<< "$aquery_summary"
-
-if [ $actions_with_arch_variant_num -eq $all_cppcompile_actions_num ]
-then
-  echo "Pass: arch variant is set."
-else
-  echo "Error: number of CppCompile actions with arch variant set: actual=$actions_with_arch_variant_num, expected=$all_cppcompile_actions_num"
-  exit 1
-fi
-
-if [ $CPU_VARIANT_CFLAG ]
-then
-  # Number of CppCompiler actions with cpu variant flag
-  actions_with_cpu_variant_num=$(call_bazel aquery --config=bp2build --config=ci --config=android \
-    'mnemonic("CppCompile", deps(//build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal))' | grep -c "\-mcpu=$CPU_VARIANT_CFLAG")
-
-  if [ $actions_with_cpu_variant_num -eq $all_cppcompile_actions_num ]
-  then
-    echo "Pass: cpu variant is set."
-  else
-    echo "Error: number of CppCompile actions with cpu variant set: actual=$actions_with_cpu_variant_num, expected=$all_cppcompile_actions_num"
-    exit 1
-  fi
-fi
diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh
deleted file mode 100755
index 8893060..0000000
--- a/tests/apex_comparison_tests.sh
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/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.
-
-set -euo pipefail
-
-# Soong/Bazel integration test for building unbundled apexes in the real source tree.
-#
-# These tests build artifacts from head and compares their contents.
-
-if [ ! -e "build/make/core/Makefile" ]; then
-  echo "$0 must be run from the top of the Android source tree."
-  exit 1
-fi
-
-############
-# Test Setup
-############
-
-OUTPUT_DIR="$(mktemp -d $(pwd)/tmp.XXXXXX)"
-SOONG_OUTPUT_DIR="$OUTPUT_DIR/soong"
-BAZEL_OUTPUT_DIR="$OUTPUT_DIR/bazel"
-
-export TARGET_PRODUCT="module_arm"
-[ "$#" -eq 1 ] && export TARGET_PRODUCT="$1"
-
-function call_bazel() {
-  build/bazel/bin/bazel --output_base="$BAZEL_OUTPUT_DIR" $@
-}
-
-function cleanup {
-  # call bazel clean because some bazel outputs don't have w bits.
-  call_bazel clean
-  rm -rf "${OUTPUT_DIR}"
-}
-
-function deapexer() {
-  DEBUGFS_PATH="$(realpath $(call_bazel cquery --config=bp2build --config=linux_x86_64 --config=ci --output=files //external/e2fsprogs/debugfs))"
-  call_bazel run --config=bp2build //system/apex/tools:deapexer -- --debugfs_path=$DEBUGFS_PATH $@
-}
-
-trap cleanup EXIT
-
-###########
-# Run Soong
-###########
-export UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true # don't rely on prebuilts
-export TARGET_BUILD_APPS="com.android.adbd com.android.tzdata build.bazel.examples.apex.minimal"
-packages/modules/common/build/build_unbundled_mainline_module.sh \
-  --product "$TARGET_PRODUCT" \
-  --dist_dir "$SOONG_OUTPUT_DIR"
-
-######################
-# Run bp2build / Bazel
-######################
-build/soong/soong_ui.bash --make-mode BP2BUILD_VERBOSE=1 --skip-soong-tests bp2build
-
-BAZEL_OUT="$(call_bazel info --config=bp2build output_path)"
-
-call_bazel build --config=bp2build --config=ci --config=android \
-  //packages/modules/adb/apex:com.android.adbd \
-  //system/timezone/apex:com.android.tzdata \
-  //build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal
-BAZEL_ADBD="$(realpath $(call_bazel cquery --config=bp2build --config=android --config=ci --output=files //packages/modules/adb/apex:com.android.adbd))"
-BAZEL_TZDATA="$(realpath $(call_bazel cquery --config=bp2build --config=android --config=ci --output=files //system/timezone/apex:com.android.tzdata))"
-BAZEL_MINIMAL="$(realpath $(call_bazel cquery --config=bp2build --config=android --config=ci --output=files //build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal))"
-
-# # Build debugfs separately, as it's not a dep of apexer, but needs to be an explicit arg.
-call_bazel build --config=bp2build --config=linux_x86_64 //external/e2fsprogs/debugfs
-
-#######
-# Tests
-#######
-
-function compare_deapexer_list() {
-  local BAZEL_APEX=$1; shift
-  local APEX=$1; shift
-
-  # Compare the outputs of `deapexer list`, which lists the contents of the apex filesystem image.
-  local SOONG_APEX="$SOONG_OUTPUT_DIR/$APEX"
-
-  local SOONG_LIST="$OUTPUT_DIR/soong.list"
-  local BAZEL_LIST="$OUTPUT_DIR/bazel.list"
-
-  deapexer list "$SOONG_APEX" > "$SOONG_LIST"
-  deapexer list "$BAZEL_APEX" > "$BAZEL_LIST"
-
-  if cmp -s "$SOONG_LIST" "$BAZEL_LIST"
-  then
-    echo "ok: $APEX"
-  else
-    echo "contents of $APEX are different between Soong and Bazel:"
-    echo
-    echo expected
-    echo
-    cat "$SOONG_LIST"
-    echo
-    echo got
-    echo
-    cat "$BAZEL_LIST"
-    exit 1
-  fi
-}
-
-compare_deapexer_list "${BAZEL_ADBD}" com.android.adbd.apex
-compare_deapexer_list "${BAZEL_TZDATA}" com.android.tzdata.apex
-compare_deapexer_list "${BAZEL_MINIMAL}" build.bazel.examples.apex.minimal.apex
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 5fc05f8..2e40950 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -9,6 +9,8 @@
 
 readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel"
 
+readonly target_product="${TARGET_PRODUCT:-aosp_arm}"
+
 function test_smoke {
   setup
   run_soong
@@ -18,10 +20,10 @@
   setup
   run_soong
   local -r bootstrap_mtime1=$(stat -c "%y" out/soong/bootstrap.ninja)
-  local -r output_mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r output_mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
   run_soong
   local -r bootstrap_mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
-  local -r output_mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local -r output_mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   if [[ "$bootstrap_mtime1" == "$bootstrap_mtime2" ]]; then
     # Bootstrapping is always done. It doesn't take a measurable amount of time.
@@ -60,7 +62,7 @@
   touch a/my_little_binary_host.py
   run_soong
 
-  grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja || fail "module not found"
+  grep -q "^# Module:.*my_little_binary_host" out/soong/build."${target_product}".ninja || fail "module not found"
 
   cat > a/Android.bp <<'EOF'
 python_binary_host {
@@ -71,14 +73,14 @@
   touch a/my_great_binary_host.py
   run_soong
 
-  grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja && fail "old module found"
-  grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found"
+  grep -q "^# Module:.*my_little_binary_host" out/soong/build."${target_product}".ninja && fail "old module found"
+  grep -q "^# Module:.*my_great_binary_host" out/soong/build."${target_product}".ninja || fail "new module not found"
 }
 
 function test_add_android_bp() {
   setup
   run_soong
-  local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   mkdir -p a
   cat > a/Android.bp <<'EOF'
@@ -90,12 +92,12 @@
   touch a/my_little_binary_host.py
   run_soong
 
-  local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
   if [[ "$mtime1" == "$mtime2" ]]; then
     fail "Output Ninja file did not change"
   fi
 
-  grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "New module not in output"
+  grep -q "^# Module:.*my_little_binary_host$" out/soong/build."${target_product}".ninja || fail "New module not in output"
 
   run_soong
 }
@@ -112,12 +114,12 @@
   touch a/my_little_binary_host.py
   run_soong
 
-  grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "Module not in output"
+  grep -q "^# Module:.*my_little_binary_host$" out/soong/build."${target_product}".ninja || fail "Module not in output"
 
   rm a/Android.bp
   run_soong
 
-  if grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja; then
+  if grep -q "^# Module:.*my_little_binary_host$" out/soong/build."${target_product}".ninja; then
     fail "Old module in output"
   fi
 }
@@ -141,16 +143,12 @@
 EOF
   touch a/my_little_binary_host.py
   run_soong
-  local -r ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r ninja_mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
-  local glob_deps_file=out/soong/globs/build/0.d
-
-  if [ -e "$glob_deps_file" ]; then
-    fail "Glob deps file unexpectedly written on first build"
-  fi
+  local glob_deps_file=out/soong/globs/"${target_product}"/0.d
 
   run_soong
-  local -r ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+  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
@@ -166,7 +164,7 @@
   fi
 
   run_soong
-  local -r ninja_mtime3=$(stat -c "%y" out/soong/build.ninja)
+  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
@@ -191,17 +189,17 @@
 EOF
   touch a/my_little_binary_host.py
   run_soong
-  local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   touch a/my_little_library.py
   run_soong
 
-  local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
   if [[ "$mtime1" == "$mtime2" ]]; then
     fail "Output Ninja file did not change"
   fi
 
-  grep -q my_little_library.py out/soong/build.ninja || fail "new file is not in output"
+  grep -q my_little_library.py out/soong/build."${target_product}".ninja || fail "new file is not in output"
 }
 
 function test_soong_build_rerun_iff_environment_changes() {
@@ -267,17 +265,17 @@
 
   export CHERRY=TASTY
   run_soong
-  grep -q "CHERRY IS TASTY" out/soong/build.ninja \
+  grep -q "CHERRY IS TASTY" out/soong/build."${target_product}".ninja \
     || fail "first value of environment variable is not used"
 
   export CHERRY=RED
   run_soong
-  grep -q "CHERRY IS RED" out/soong/build.ninja \
+  grep -q "CHERRY IS RED" out/soong/build."${target_product}".ninja \
     || fail "second value of environment variable not used"
-  local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   run_soong
-  local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
   if [[ "$mtime1" != "$mtime2" ]]; then
     fail "Output Ninja file changed when environment variable did not"
   fi
@@ -287,7 +285,7 @@
 function test_create_global_include_directory() {
   setup
   run_soong
-  local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   # Soong needs to know if top level directories like hardware/ exist for use
   # as global include directories.  Make sure that doesn't cause regens for
@@ -295,7 +293,7 @@
   mkdir -p system/core
 
   run_soong
-  local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
   if [[ "$mtime1" != "$mtime2" ]]; then
     fail "Output Ninja file changed when top level directory changed"
   fi
@@ -305,7 +303,7 @@
   mkdir -p system/core/include
 
   run_soong
-  local -r mtime3=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime3=$(stat -c "%y" out/soong/build."${target_product}".ninja)
   if [[ "$mtime2" = "$mtime3" ]]; then
     fail "Output Ninja file did not change when global include directory created"
   fi
@@ -315,7 +313,7 @@
 function test_add_file_to_soong_build() {
   setup
   run_soong
-  local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   mkdir -p vendor/foo/picard
   cat > vendor/foo/picard/Android.bp <<'EOF'
@@ -377,12 +375,12 @@
 EOF
 
   run_soong
-  local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
   if [[ "$mtime1" == "$mtime2" ]]; then
     fail "Output Ninja file did not change"
   fi
 
-  grep -q "Make it so" out/soong/build.ninja || fail "New action not present"
+  grep -q "Make it so" out/soong/build."${target_product}".ninja || fail "New action not present"
 }
 
 # Tests a glob in a build= statement in an Android.bp file, which is interpreted
@@ -455,9 +453,9 @@
 EOF
 
   run_soong
-  local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
-  grep -q "Make it so" out/soong/build.ninja || fail "Original action not present"
+  grep -q "Make it so" out/soong/build."${target_product}".ninja || fail "Original action not present"
 
   cat > build/soong/picard/foob.bp <<'EOF'
 bootstrap_go_package {
@@ -487,14 +485,14 @@
 EOF
 
   run_soong
-  local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
   if [[ "$mtime1" == "$mtime2" ]]; then
     fail "Output Ninja file did not change"
   fi
 
-  grep -q "Engage" out/soong/build.ninja || fail "New action not present"
+  grep -q "Engage" out/soong/build."${target_product}".ninja || fail "New action not present"
 
-  if grep -q "Make it so" out/soong/build.ninja; then
+  if grep -q "Make it so" out/soong/build."${target_product}".ninja; then
     fail "Original action still present"
   fi
 }
@@ -512,7 +510,7 @@
   setup
 
   run_soong
-  local -r ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r ninja_mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   run_soong soong_docs
   local -r docs_mtime1=$(stat -c "%y" out/soong/docs/soong_build.html)
@@ -525,7 +523,7 @@
   fi
 
   run_soong
-  local -r ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local -r ninja_mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
     fail "Output Ninja file changed on null build"
@@ -565,144 +563,6 @@
   fi
 }
 
-function test_bp2build_smoke {
-  setup
-  run_soong bp2build
-  [[ -e out/soong/bp2build_workspace_marker ]] || fail "bp2build marker file not created"
-  [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
-}
-
-function test_bp2build_generates_marker_file {
-  setup
-
-  run_soong bp2build
-
-  if [[ ! -f "./out/soong/bp2build_files_marker" ]]; then
-    fail "bp2build marker file was not generated"
-  fi
-
-  if [[ ! -f "./out/soong/bp2build_workspace_marker" ]]; then
-    fail "symlink forest marker file was not generated"
-  fi
-}
-
-function test_bp2build_add_irrelevant_file {
-  setup
-
-  mkdir -p a/b
-  touch a/b/c.txt
-  cat > a/b/Android.bp <<'EOF'
-filegroup {
-  name: "c",
-  srcs: ["c.txt"],
-  bazel_module: { bp2build_available: true },
-}
-EOF
-
-  run_soong bp2build
-  if [[ ! -e out/soong/bp2build/a/b/BUILD.bazel ]]; then
-    fail "BUILD file in symlink forest was not created";
-  fi
-
-  local -r mtime1=$(stat -c "%y" out/soong/bp2build/a/b/BUILD.bazel)
-
-  touch a/irrelevant.txt
-  run_soong bp2build
-  local -r mtime2=$(stat -c "%y" out/soong/bp2build/a/b/BUILD.bazel)
-
-  if [[ "$mtime1" != "$mtime2" ]]; then
-    fail "BUILD.bazel file was regenerated"
-  fi
-
-  if [[ ! -e "out/soong/workspace/a/irrelevant.txt" ]]; then
-    fail "New file was not symlinked into symlink forest"
-  fi
-}
-
-function test_bp2build_add_android_bp {
-  setup
-
-  mkdir -p a
-  touch a/a.txt
-  cat > a/Android.bp <<'EOF'
-filegroup {
-  name: "a",
-  srcs: ["a.txt"],
-  bazel_module: { bp2build_available: true },
-}
-EOF
-
-  run_soong bp2build
-  [[ -e out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created"
-  [[ -L out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked"
-
-  mkdir -p b
-  touch b/b.txt
-  cat > b/Android.bp <<'EOF'
-filegroup {
-  name: "b",
-  srcs: ["b.txt"],
-  bazel_module: { bp2build_available: true },
-}
-EOF
-
-  run_soong bp2build
-  [[ -e out/soong/bp2build/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created"
-  [[ -L out/soong/workspace/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked"
-}
-
-function test_bp2build_null_build {
-  setup
-
-  run_soong bp2build
-  local -r mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-
-  run_soong bp2build
-  local -r mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-
-  if [[ "$mtime1" != "$mtime2" ]]; then
-    fail "Output Ninja file changed on null build"
-  fi
-}
-
-function test_bp2build_add_to_glob {
-  setup
-
-  mkdir -p a
-  touch a/a1.txt
-  cat > a/Android.bp <<'EOF'
-filegroup {
-  name: "a",
-  srcs: ["*.txt"],
-  bazel_module: { bp2build_available: true },
-}
-EOF
-
-  run_soong bp2build
-  grep -q a1.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a1.txt not in ${GENERATED_BUILD_FILE_NAME} file"
-
-  touch a/a2.txt
-  run_soong bp2build
-  grep -q a2.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a2.txt not in ${GENERATED_BUILD_FILE_NAME} file"
-}
-
-function test_multiple_soong_build_modes() {
-  setup
-  run_soong json-module-graph bp2build nothing
-  if [[ ! -f "out/soong/bp2build_workspace_marker" ]]; then
-    fail "bp2build marker file was not generated"
-  fi
-
-
-  if [[ ! -f "out/soong/module-graph.json" ]]; then
-    fail "JSON file was not created"
-  fi
-
-  if [[ ! -f "out/soong/build.ninja" ]]; then
-    fail "Main build.ninja file was not created"
-  fi
-}
-
 function test_dump_json_module_graph() {
   setup
   run_soong json-module-graph
@@ -715,13 +575,13 @@
   setup
 
   run_soong
-  local -r ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r ninja_mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   run_soong json-module-graph
   local -r json_mtime1=$(stat -c "%y" out/soong/module-graph.json)
 
   run_soong
-  local -r ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local -r ninja_mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
   if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
     fail "Output Ninja file changed after writing JSON module graph"
   fi
@@ -734,143 +594,6 @@
 
 }
 
-function test_bp2build_bazel_workspace_structure {
-  setup
-
-  mkdir -p a/b
-  touch a/a.txt
-  touch a/b/b.txt
-  cat > a/b/Android.bp <<'EOF'
-filegroup {
-  name: "b",
-  srcs: ["b.txt"],
-  bazel_module: { bp2build_available: true },
-}
-EOF
-
-  run_soong bp2build
-  [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
-  [[ -d out/soong/workspace/a/b ]] || fail "module directory not a directory"
-  [[ -L "out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked"
-  [[ "$(readlink -f out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/b/${GENERATED_BUILD_FILE_NAME}"$ ]] \
-    || fail "BUILD files symlinked at the wrong place"
-  [[ -L out/soong/workspace/a/b/b.txt ]] || fail "a/b/b.txt not symlinked"
-  [[ -L out/soong/workspace/a/a.txt ]] || fail "a/b/a.txt not symlinked"
-  [[ ! -e out/soong/workspace/out ]] || fail "out directory symlinked"
-}
-
-function test_bp2build_bazel_workspace_add_file {
-  setup
-
-  mkdir -p a
-  touch a/a.txt
-  cat > a/Android.bp <<EOF
-filegroup {
-  name: "a",
-  srcs: ["a.txt"],
-  bazel_module: { bp2build_available: true },
-}
-EOF
-
-  run_soong bp2build
-
-  touch a/a2.txt  # No reference in the .bp file needed
-  run_soong bp2build
-  [[ -L out/soong/workspace/a/a2.txt ]] || fail "a/a2.txt not symlinked"
-}
-
-function test_bp2build_build_file_precedence {
-  setup
-
-  mkdir -p a
-  touch a/a.txt
-  touch a/${GENERATED_BUILD_FILE_NAME}
-  cat > a/Android.bp <<EOF
-filegroup {
-  name: "a",
-  srcs: ["a.txt"],
-  bazel_module: { bp2build_available: true },
-}
-EOF
-
-  run_soong bp2build
-  [[ -L "out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked"
-  [[ "$(readlink -f out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/${GENERATED_BUILD_FILE_NAME}"$ ]] \
-    || fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place"
-}
-
-function test_bp2build_fails_fast {
-  setup
-
-  mkdir -p "a/${GENERATED_BUILD_FILE_NAME}"
-  cat > a/Android.bp <<EOF
-filegroup {
-  name: "a",
-  srcs: ["a.txt"],
-  bazel_module: { bp2build_available: true },
-}
-EOF
-
-  mkdir -p "b/${GENERATED_BUILD_FILE_NAME}"
-  cat > b/Android.bp <<EOF
-filegroup {
-  name: "b",
-  srcs: ["b.txt"],
-  bazel_module: { bp2build_available: true },
-}
-EOF
-
-  if run_soong bp2build >& "$MOCK_TOP/errors"; then
-    fail "Build should have failed"
-  fi
-
-  # we should expect at least one error
-  grep -q -E "(a|b)/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for ${GENERATED_BUILD_FILE_NAME} not found"
-}
-
-function test_bp2build_back_and_forth_null_build {
-  setup
-
-  run_soong
-  local -r output_mtime1=$(stat -c "%y" out/soong/build.ninja)
-
-  run_soong bp2build
-  local -r output_mtime2=$(stat -c "%y" out/soong/build.ninja)
-  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
-    fail "Output Ninja file changed when switching to bp2build"
-  fi
-
-  local -r marker_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-
-  run_soong
-  local -r output_mtime3=$(stat -c "%y" out/soong/build.ninja)
-  local -r marker_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-  if [[ "$output_mtime1" != "$output_mtime3" ]]; then
-    fail "Output Ninja file changed when switching to regular build from bp2build"
-  fi
-  if [[ "$marker_mtime1" != "$marker_mtime2" ]]; then
-    fail "bp2build marker file changed when switching to regular build from bp2build"
-  fi
-
-  run_soong bp2build
-  local -r output_mtime4=$(stat -c "%y" out/soong/build.ninja)
-  local -r marker_mtime3=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-  if [[ "$output_mtime1" != "$output_mtime4" ]]; then
-    fail "Output Ninja file changed when switching back to bp2build"
-  fi
-  if [[ "$marker_mtime1" != "$marker_mtime3" ]]; then
-    fail "bp2build marker file changed when switching back to bp2build"
-  fi
-}
-
-function test_queryview_smoke() {
-  setup
-
-  run_soong queryview
-  [[ -e out/soong/queryview/WORKSPACE ]] || fail "queryview WORKSPACE file not created"
-
-}
-
 function test_queryview_null_build() {
   setup
 
@@ -886,14 +609,14 @@
 }
 
 # This test verifies that adding a new glob to a blueprint file only
-# causes build.ninja to be regenerated on the *next* build, and *not*
+# causes build."${target_product}".ninja to be regenerated on the *next* build, and *not*
 # the build after. (This is a regression test for a bug where globs
 # resulted in two successive regenerations.)
 function test_new_glob_incrementality {
   setup
 
   run_soong nothing
-  local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime1=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   mkdir -p globdefpkg/
   cat > globdefpkg/Android.bp <<'EOF'
@@ -904,14 +627,14 @@
 EOF
 
   run_soong nothing
-  local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime2=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   if [[ "$mtime1" == "$mtime2" ]]; then
     fail "Ninja file was not regenerated, despite a new bp file"
   fi
 
   run_soong nothing
-  local -r mtime3=$(stat -c "%y" out/soong/build.ninja)
+  local -r mtime3=$(stat -c "%y" out/soong/build."${target_product}".ninja)
 
   if [[ "$mtime2" != "$mtime3" ]]; then
     fail "Ninja file was regenerated despite no previous bp changes"
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
deleted file mode 100755
index 8a64a56..0000000
--- a/tests/bp2build_bazel_test.sh
+++ /dev/null
@@ -1,445 +0,0 @@
-#!/bin/bash -eu
-
-set -o pipefail
-
-# Test that bp2build and Bazel can play nicely together
-
-source "$(dirname "$0")/lib.sh"
-
-readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel"
-
-function test_bp2build_null_build {
-  setup
-  run_soong bp2build
-  local -r output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-
-  run_soong bp2build
-  local -r output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-
-  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
-    fail "Output bp2build marker file changed on null build"
-  fi
-}
-
-# Tests that, if bp2build reruns due to a blueprint file changing, that
-# BUILD files whose contents are unchanged are not regenerated.
-function test_bp2build_unchanged {
-  setup
-
-  mkdir -p pkg
-  touch pkg/x.txt
-  cat > pkg/Android.bp <<'EOF'
-filegroup {
-    name: "x",
-    srcs: ["x.txt"],
-    bazel_module: {bp2build_available: true},
-  }
-EOF
-
-  run_soong bp2build
-  local -r buildfile_mtime1=$(stat -c "%y" out/soong/bp2build/pkg/BUILD.bazel)
-  local -r marker_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-
-  # Force bp2build to rerun by updating the timestamp of a blueprint file.
-  touch pkg/Android.bp
-
-  run_soong bp2build
-  local -r buildfile_mtime2=$(stat -c "%y" out/soong/bp2build/pkg/BUILD.bazel)
-  local -r marker_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-
-  if [[ "$marker_mtime1" == "$marker_mtime2" ]]; then
-    fail "Expected bp2build marker file to change"
-  fi
-  if [[ "$buildfile_mtime1" != "$buildfile_mtime2" ]]; then
-    fail "BUILD.bazel was updated even though contents are same"
-  fi
-
-  # Force bp2build to rerun by updating the timestamp of the constants_exported_to_soong.bzl file.
-  touch build/bazel/constants_exported_to_soong.bzl
-
-  run_soong bp2build
-  local -r buildfile_mtime3=$(stat -c "%y" out/soong/bp2build/pkg/BUILD.bazel)
-  local -r marker_mtime3=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-
-  if [[ "$marker_mtime2" == "$marker_mtime3" ]]; then
-    fail "Expected bp2build marker file to change"
-  fi
-  if [[ "$buildfile_mtime2" != "$buildfile_mtime3" ]]; then
-    fail "BUILD.bazel was updated even though contents are same"
-  fi
-}
-
-# Tests that blueprint files that are deleted are not present when the
-# bp2build tree is regenerated.
-function test_bp2build_deleted_blueprint {
-  setup
-
-  mkdir -p pkg
-  touch pkg/x.txt
-  cat > pkg/Android.bp <<'EOF'
-filegroup {
-    name: "x",
-    srcs: ["x.txt"],
-    bazel_module: {bp2build_available: true},
-  }
-EOF
-
-  run_soong bp2build
-  if [[ ! -e "./out/soong/bp2build/pkg/BUILD.bazel" ]]; then
-    fail "Expected pkg/BUILD.bazel to be generated"
-  fi
-
-  rm pkg/Android.bp
-
-  run_soong bp2build
-  if [[ -e "./out/soong/bp2build/pkg/BUILD.bazel" ]]; then
-    fail "Expected pkg/BUILD.bazel to be deleted"
-  fi
-}
-
-function test_bp2build_null_build_with_globs {
-  setup
-
-  mkdir -p foo/bar
-  cat > foo/bar/Android.bp <<'EOF'
-filegroup {
-    name: "globs",
-    srcs: ["*.txt"],
-  }
-EOF
-  touch foo/bar/a.txt foo/bar/b.txt
-
-  run_soong bp2build
-  local -r output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-
-  run_soong bp2build
-  local -r output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
-
-  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
-    fail "Output bp2build marker file changed on null build"
-  fi
-}
-
-function test_different_relative_outdir {
-  setup
-
-  mkdir -p a
-  touch a/g.txt
-  cat > a/Android.bp <<'EOF'
-filegroup {
-    name: "g",
-    srcs: ["g.txt"],
-    bazel_module: {bp2build_available: true},
-  }
-EOF
-
-  # A directory under $MOCK_TOP
-  outdir=out2
-  trap "rm -rf $outdir" EXIT
-  # Modify OUT_DIR in a subshell so it doesn't affect the top level one.
-  (export OUT_DIR=$outdir; run_soong bp2build && run_bazel build --config=bp2build --config=ci //a:g)
-}
-
-function test_different_absolute_outdir {
-  setup
-
-  mkdir -p a
-  touch a/g.txt
-  cat > a/Android.bp <<'EOF'
-filegroup {
-    name: "g",
-    srcs: ["g.txt"],
-    bazel_module: {bp2build_available: true},
-  }
-EOF
-
-  # A directory under /tmp/...
-  outdir=$(mktemp -t -d st.XXXXX)
-  trap 'rm -rf $outdir' EXIT
-  # Modify OUT_DIR in a subshell so it doesn't affect the top level one.
-  (export OUT_DIR=$outdir; run_soong bp2build && run_bazel build --config=bp2build --config=ci //a:g)
-}
-
-function _bp2build_generates_all_buildfiles {
-  setup
-
-  mkdir -p foo/convertible_soong_module
-  cat > foo/convertible_soong_module/Android.bp <<'EOF'
-genrule {
-    name: "the_answer",
-    cmd: "echo '42' > $(out)",
-    out: [
-        "the_answer.txt",
-    ],
-    bazel_module: {
-        bp2build_available: true,
-    },
-  }
-EOF
-
-  mkdir -p foo/unconvertible_soong_module
-  cat > foo/unconvertible_soong_module/Android.bp <<'EOF'
-genrule {
-    name: "not_the_answer",
-    cmd: "echo '43' > $(out)",
-    out: [
-        "not_the_answer.txt",
-    ],
-    bazel_module: {
-        bp2build_available: false,
-    },
-  }
-EOF
-
-  run_soong bp2build
-
-  if [[ ! -f "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then
-    fail "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME} was not generated"
-  fi
-
-  if [[ ! -f "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then
-    fail "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME} was not generated"
-  fi
-
-  if ! grep "the_answer" "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then
-    fail "missing BUILD target the_answer in convertible_soong_module/${GENERATED_BUILD_FILE_NAME}"
-  fi
-
-  if grep "not_the_answer" "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then
-    fail "found unexpected BUILD target not_the_answer in unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"
-  fi
-
-  if ! grep "filegroup" "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then
-    fail "missing filegroup in unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"
-  fi
-
-  # NOTE: We don't actually use the extra BUILD file for anything here
-  run_bazel build --config=android --config=bp2build --config=ci //foo/...
-
-  local -r the_answer_file="$(find -L bazel-out -name the_answer.txt)"
-  if [[ ! -f "${the_answer_file}" ]]; then
-    fail "Expected the_answer.txt to be generated, but was missing"
-  fi
-  if ! grep 42 "${the_answer_file}"; then
-    fail "Expected to find 42 in '${the_answer_file}'"
-  fi
-}
-
-function test_bp2build_generates_all_buildfiles {
-  _save_trap=$(trap -p EXIT)
-  trap '[[ $? -ne 0 ]] && echo Are you running this locally? Try changing --sandbox_tmpfs_path to something other than /tmp/ in build/bazel/linux.bazelrc.' EXIT
-  _bp2build_generates_all_buildfiles
-  eval "${_save_trap}"
-}
-
-function test_build_files_take_precedence {
-  _save_trap=$(trap -p EXIT)
-  trap '[[ $? -ne 0 ]] && echo Are you running this locally? Try changing --sandbox_tmpfs_path to something other than /tmp/ in build/bazel/linux.bazelrc.' EXIT
-  _build_files_take_precedence
-  eval "${_save_trap}"
-}
-
-function _build_files_take_precedence {
-  setup
-
-  # This specific directory is hardcoded in bp2build as being one
-  # where the BUILD file should be intentionally kept.
-  mkdir -p testpkg/keep_build_file
-  cat > testpkg/keep_build_file/Android.bp <<'EOF'
-genrule {
-    name: "print_origin",
-    cmd: "echo 'from_soong' > $(out)",
-    out: [
-        "origin.txt",
-    ],
-    bazel_module: {
-        bp2build_available: true,
-    },
-  }
-EOF
-
-  run_soong bp2build
-  run_bazel build --config=android --config=bp2build --config=ci //testpkg/keep_build_file:print_origin
-
-  local -r output_file="$(find -L bazel-out -name origin.txt)"
-  if [[ ! -f "${output_file}" ]]; then
-    fail "Expected origin.txt to be generated, but was missing"
-  fi
-  if ! grep from_soong "${output_file}"; then
-    fail "Expected to find 'from_soong' in '${output_file}'"
-  fi
-
-  cat > testpkg/keep_build_file/BUILD.bazel <<'EOF'
-genrule(
-    name = "print_origin",
-    outs = ["origin.txt"],
-    cmd = "echo 'from_bazel' > $@",
-)
-EOF
-
-  # Clean the workspace. There is a test infrastructure bug where run_bazel
-  # will symlink Android.bp files in the source directory again and thus
-  # pollute the workspace.
-  # TODO: b/286059878 - Remove this clean after the underlying bug is fixed.
-  run_soong clean
-  run_soong bp2build
-  run_bazel build --config=android --config=bp2build --config=ci //testpkg/keep_build_file:print_origin
-  if ! grep from_bazel "${output_file}"; then
-    fail "Expected to find 'from_bazel' in '${output_file}'"
-  fi
-}
-
-function test_bp2build_symlinks_files {
-  setup
-  mkdir -p foo
-  touch foo/BLANK1
-  touch foo/BLANK2
-  touch foo/F2D
-  touch foo/BUILD
-
-  run_soong bp2build
-
-  if [[ -e "./out/soong/workspace/foo/BUILD" ]]; then
-    fail "./out/soong/workspace/foo/BUILD should be omitted"
-  fi
-  for file in BLANK1 BLANK2 F2D
-  do
-    if [[ ! -L "./out/soong/workspace/foo/$file" ]]; then
-      fail "./out/soong/workspace/foo/$file should exist"
-    fi
-  done
-  local -r BLANK1_BEFORE=$(stat -c %y "./out/soong/workspace/foo/BLANK1")
-
-  rm foo/BLANK2
-  rm foo/F2D
-  mkdir foo/F2D
-  touch foo/F2D/BUILD
-
-  run_soong bp2build
-
-  if [[ -e "./out/soong/workspace/foo/BUILD" ]]; then
-    fail "./out/soong/workspace/foo/BUILD should be omitted"
-  fi
-  local -r BLANK1_AFTER=$(stat -c %y "./out/soong/workspace/foo/BLANK1")
-  if [[ "$BLANK1_AFTER" != "$BLANK1_BEFORE" ]]; then
-    fail "./out/soong/workspace/foo/BLANK1 should be untouched"
-  fi
-  if [[  -e "./out/soong/workspace/foo/BLANK2" ]]; then
-    fail "./out/soong/workspace/foo/BLANK2 should be removed"
-  fi
-  if [[ -L "./out/soong/workspace/foo/F2D" ]] || [[ ! -d "./out/soong/workspace/foo/F2D" ]]; then
-    fail "./out/soong/workspace/foo/F2D should be a dir"
-  fi
-}
-
-function test_cc_correctness {
-  setup
-
-  mkdir -p a
-  cat > a/Android.bp <<EOF
-cc_object {
-  name: "qq",
-  srcs: ["qq.cc"],
-  bazel_module: {
-    bp2build_available: true,
-  },
-  stl: "none",
-  system_shared_libs: [],
-}
-EOF
-
-  cat > a/qq.cc <<EOF
-#include "qq.h"
-int qq() {
-  return QQ;
-}
-EOF
-
-  cat > a/qq.h <<EOF
-#define QQ 1
-EOF
-
-  run_soong bp2build
-
-  run_bazel build --config=android --config=bp2build --config=ci //a:qq
-  local -r output_mtime1=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
-
-  run_bazel build --config=android --config=bp2build --config=ci //a:qq
-  local -r output_mtime2=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
-
-  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
-    fail "output changed on null build"
-  fi
-
-  cat > a/qq.h <<EOF
-#define QQ 2
-EOF
-
-  run_bazel build --config=android --config=bp2build --config=ci //a:qq
-  local -r output_mtime3=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
-
-  if [[ "$output_mtime1" == "$output_mtime3" ]]; then
-    fail "output not changed when included header changed"
-  fi
-}
-
-# Regression test for the following failure during symlink forest creation:
-#
-#   Cannot stat '/tmp/st.rr054/foo/bar/unresolved_symlink': stat /tmp/st.rr054/foo/bar/unresolved_symlink: no such file or directory
-#
-function test_bp2build_null_build_with_unresolved_symlink_in_source() {
-  setup
-
-  mkdir -p foo/bar
-  ln -s /tmp/non-existent foo/bar/unresolved_symlink
-  cat > foo/bar/Android.bp <<'EOF'
-filegroup {
-    name: "fg",
-    srcs: ["unresolved_symlink/non-existent-file.txt"],
-  }
-EOF
-
-  run_soong bp2build
-
-  dest=$(readlink -f out/soong/workspace/foo/bar/unresolved_symlink)
-  if [[ "$dest" != "/tmp/non-existent" ]]; then
-    fail "expected to plant an unresolved symlink out/soong/workspace/foo/bar/unresolved_symlink that resolves to /tmp/non-existent"
-  fi
-}
-
-function test_bazel_standalone_output_paths_contain_product_name {
-  setup
-  mkdir -p a
-  cat > a/Android.bp <<EOF
-cc_object {
-  name: "qq",
-  srcs: ["qq.cc"],
-  bazel_module: {
-    bp2build_available: true,
-  },
-  stl: "none",
-  system_shared_libs: [],
-}
-EOF
-
-  cat > a/qq.cc <<EOF
-#include "qq.h"
-int qq() {
-  return QQ;
-}
-EOF
-
-  cat > a/qq.h <<EOF
-#define QQ 1
-EOF
-
-  export TARGET_PRODUCT=aosp_arm; run_soong bp2build
-  local -r output=$(run_bazel cquery //a:qq --output=files --config=android --config=bp2build --config=ci)
-  if [[ ! $(echo ${output} | grep "bazel-out/aosp_arm") ]]; then
-    fail "Did not find the product name '${TARGET_PRODUCT}' in the output path. This can cause " \
-      "unnecessary rebuilds when toggling between products as bazel outputs for different products will " \
-      "clobber each other. Output paths are: \n${output}"
-  fi
-}
-
-scan_and_run_tests
diff --git a/tests/dcla_apex_comparison_test.sh b/tests/dcla_apex_comparison_test.sh
deleted file mode 100755
index e3c189f..0000000
--- a/tests/dcla_apex_comparison_test.sh
+++ /dev/null
@@ -1,166 +0,0 @@
-#!/bin/bash
-
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -euo pipefail
-
-# Soong/Bazel integration test to build the mainline modules in mixed build and
-# compare the DCLA libs extracted from those modules to ensure they are identical.
-
-if [ ! -e "build/make/core/Makefile" ]; then
-  echo "$0 must be run from the top of the Android source tree."
-  exit 1
-fi
-
-TARGET_PRODUCTS=(
-  module_arm64
-  module_x86_64
-)
-
-MODULES=(
-  # These modules depend on the DCLA libs
-  com.android.adbd
-  com.android.art
-  com.android.art.debug
-  com.android.art.testing
-  com.android.btservices
-  com.android.conscrypt
-  com.android.i18n
-  com.android.media
-  com.android.media.swcodec
-  com.android.resolv
-  com.android.runtime
-  com.android.tethering
-)
-
-BAZEL_TARGETS=(
-  //packages/modules/adb/apex:com.android.adbd
-  //frameworks/av/apex:com.android.media.swcodec
-)
-
-DCLA_LIBS=(
-  libbase.so
-  libc++.so
-  libcrypto.so
-  libcutils.so
-  libstagefright_flacdec.so
-  libutils.so
-)
-
-if [[ -z ${OUT_DIR+x} ]]; then
-  OUT_DIR="out"
-fi
-
-if [[ -z ${ANDROID_HOST_OUT+x} ]]; then
-  export ANDROID_HOST_OUT="out/host/linux-x86"
-fi
-
-######################
-# Build deapexer and debugfs
-######################
-DEAPEXER="${ANDROID_HOST_OUT}/bin/deapexer"
-DEBUGFS="${ANDROID_HOST_OUT}/bin/debugfs"
-if [[ ! -f "${DEAPEXER}" ]] || [[ ! -f "${DEBUGFS}" ]]; then
-  build/soong/soong_ui.bash --make-mode --skip-soong-tests deapexer debugfs
-fi
-
-DEAPEXER="${DEAPEXER} --debugfs_path=${DEBUGFS}"
-
-############
-# Test Setup
-############
-OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
-
-function call_bazel() {
-  build/bazel/bin/bazel $@
-}
-
-function cleanup {
-  rm -rf "${OUTPUT_DIR}"
-}
-trap cleanup EXIT
-
-#######
-# Tests
-#######
-
-function extract_dcla_libs() {
-  local product=$1; shift
-  local modules=("$@"); shift
-
-  for module in "${modules[@]}"; do
-    local apex="${OUTPUT_DIR}/${product}/${module}.apex"
-    local extract_dir="${OUTPUT_DIR}/${product}/${module}/extract"
-
-    $DEAPEXER extract "${apex}" "${extract_dir}"
-  done
-}
-
-function compare_dcla_libs() {
-  local product=$1; shift
-  local modules=("$@"); shift
-
-  for lib in "${DCLA_LIBS[@]}"; do
-    for arch in lib lib64; do
-      local prev_sha=""
-      for module in "${modules[@]}"; do
-        local file="${OUTPUT_DIR}/${product}/${module}/extract/${arch}/${lib}"
-        if [[ ! -f "${file}" ]]; then
-          # not all libs are present in a module
-          echo "file doesn't exist: ${file}"
-          continue
-        fi
-        sha=$(sha1sum ${file})
-        sha="${sha% *}"
-        if [ "${prev_sha}" == "" ]; then
-          prev_sha="${sha}"
-        elif [ "${sha}" != "${prev_sha}" ] && { [ "${lib}" != "libcrypto.so" ] || [[ "${module}" != *"com.android.tethering" ]]; }; then
-          echo "Test failed, ${lib} has different hash value"
-          exit 1
-        fi
-      done
-    done
-  done
-}
-
-export UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true # don't rely on prebuilts
-export TARGET_BUILD_APPS="${MODULES[@]}"
-for product in "${TARGET_PRODUCTS[@]}"; do
-  ###########
-  # Build the mainline modules
-  ###########
-  packages/modules/common/build/build_unbundled_mainline_module.sh \
-    --product "${product}" \
-    --dist_dir "${OUTPUT_DIR}/${product}"
-
-  bazel_apexes=()
-  if [[ -n ${TEST_BAZEL+x} ]] && [ "${TEST_BAZEL}" = true ]; then
-    export TARGET_PRODUCT="${product/module/aosp}"
-    call_bazel build --config=bp2build --config=ci --config=android "${BAZEL_TARGETS[@]}"
-    for target in "${BAZEL_TARGETS[@]}"; do
-      apex_path="$(realpath $(call_bazel cquery --config=bp2build --config=android --config=ci --output=files $target))"
-      mkdir -p ${OUTPUT_DIR}/${product}
-      bazel_apex="bazel_$(basename $apex_path)"
-      mv $apex_path ${OUTPUT_DIR}/${product}/${bazel_apex}
-      bazel_apexes+=(${bazel_apex%".apex"})
-    done
-  fi
-
-  all_modeuls=(${MODULES[@]} ${bazel_apexes[@]})
-  extract_dcla_libs "${product}" "${all_modeuls[@]}"
-  compare_dcla_libs "${product}" "${all_modeuls[@]}"
-done
-
-echo "Test passed"
diff --git a/tests/lib.sh b/tests/lib.sh
index e0b319e..4c320d0 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -140,7 +140,7 @@
 
 # shellcheck disable=SC2120
 function run_soong {
-  USE_RBE=false build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
+  USE_RBE=false TARGET_PRODUCT=aosp_arm TARGET_RELEASE=trunk_staging TARGET_BUILD_VARIANT=userdebug build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
 }
 
 function create_mock_bazel {
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
deleted file mode 100755
index ca63fdf..0000000
--- a/tests/mixed_mode_test.sh
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/bin/bash
-
-set -o pipefail
-
-# This test exercises mixed builds where Soong and Bazel cooperate in building
-# Android.
-#
-# When the execroot is deleted, the Bazel server process will automatically
-# terminate itself.
-
-source "$(dirname "$0")/lib.sh"
-
-function test_bazel_smoke {
-  setup
-
-  run_soong bp2build
-
-  run_bazel info --config=bp2build
-}
-
-function test_add_irrelevant_file {
-  setup
-
-  mkdir -p soong_tests/a/b
-  touch soong_tests/a/b/c.txt
-  cat > soong_tests/a/b/Android.bp <<'EOF'
-filegroup {
-  name: "c",
-  srcs: ["c.txt"],
-  bazel_module: { bp2build_available: true },
-}
-EOF
-
-  run_soong --bazel-mode-staging nothing
-
-  if [[ ! -e out/soong/bp2build/soong_tests/a/b/BUILD.bazel ]]; then
-    fail "BUILD.bazel not created"
-  fi
-
-  if [[ ! -e out/soong/build.ninja ]]; then
-    fail "build.ninja not created"
-  fi
-
-  local mtime_build1=$(stat -c "%y" out/soong/bp2build/soong_tests/a/b/BUILD.bazel)
-  local mtime_ninja1=$(stat -c "%y" out/soong/build.ninja)
-
-  touch soong_tests/a/irrelevant.txt
-
-  run_soong --bazel-mode-staging nothing
-  local mtime_build2=$(stat -c "%y" out/soong/bp2build/soong_tests/a/b/BUILD.bazel)
-  local mtime_ninja2=$(stat -c "%y" out/soong/build.ninja)
-
-  if [[ "$mtime_build1" != "$mtime_build2" ]]; then
-    fail "BUILD.bazel was generated"
-  fi
-
-  if [[ "$mtime_ninja1" != "$mtime_ninja2" ]]; then
-    fail "build.ninja was regenerated"
-  fi
-
-  if [[ ! -e out/soong/workspace/soong_tests/a/irrelevant.txt ]]; then
-    fail "new file was not symlinked"
-  fi
-}
-
-function test_force_enabled_modules {
-  setup
-  # b/273910287 - test force enable modules
-  mkdir -p soong_tests/a/b
-  cat > soong_tests/a/b/Android.bp <<'EOF'
-genrule {
-    name: "touch-file",
-    out: ["fake-out.txt"],
-    cmd: "touch $(out)",
-    bazel_module: { bp2build_available: true },
-}
-
-genrule {
-    name: "unenabled-touch-file",
-    out: ["fake-out2.txt"],
-    cmd: "touch $(out)",
-    bazel_module: { bp2build_available: false },
-}
-EOF
-  run_soong --bazel-mode-staging --bazel-force-enabled-modules=touch-file nothing
-  local bazel_contained=`grep out/soong/.intermediates/soong_tests/a/b/touch-file/gen/fake-out.txt out/soong/build.ninja`
-  if [[ $bazel_contained == '' ]]; then
-    fail "Bazel actions not found for force-enabled module"
-  fi
-
-  unused=`run_soong --bazel-force-enabled-modules=unenabled-touch-file --ensure-allowlist-integrity nothing >/dev/null`
-
-  if [[ $? -ne 1 ]]; then
-    fail "Expected failure due to force-enabling an unenabled module "
-  fi
-}
-
-
-scan_and_run_tests
diff --git a/tests/persistent_bazel_test.sh b/tests/persistent_bazel_test.sh
deleted file mode 100755
index 9b7b58f..0000000
--- a/tests/persistent_bazel_test.sh
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/bin/bash -eu
-
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -o pipefail
-
-source "$(dirname "$0")/lib.sh"
-
-# This test verifies that adding USE_PERSISTENT_BAZEL creates a Bazel process
-# that outlasts the build process.
-# This test should only be run in sandboxed environments (because this test
-# verifies a Bazel process using global process list, and may spawn lingering
-# Bazel processes).
-function test_persistent_bazel {
-  setup
-
-  # Ensure no existing Bazel process.
-  if [[ -e out/bazel/output/server/server.pid.txt ]]; then
-    kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
-    if kill -0 $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null ; then
-      fail "Error killing pre-setup bazel"
-    fi
-  fi
-
-  USE_PERSISTENT_BAZEL=1 run_soong nothing
-
-  if ! kill -0 $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null ; then
-    fail "Persistent bazel process expected, but not found after first build"
-  fi
-  BAZEL_PID=$(cat out/bazel/output/server/server.pid.txt)
-
-  USE_PERSISTENT_BAZEL=1 run_soong nothing
-
-  if ! kill -0 $BAZEL_PID 2>/dev/null ; then
-    fail "Bazel pid $BAZEL_PID was killed after second build"
-  fi
-
-  kill $BAZEL_PID 2>/dev/null
-  if ! kill -0 $BAZEL_PID 2>/dev/null ; then
-    fail "Error killing bazel on shutdown"
-  fi
-}
-
-# Verifies that USE_PERSISTENT_BAZEL mode operates as expected in the event
-# that there are Bazel failures.
-function test_bazel_failure {
-  setup
-
-  # Ensure no existing Bazel process.
-  if [[ -e out/bazel/output/server/server.pid.txt ]]; then
-    kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
-    if kill -0 $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null ; then
-      fail "Error killing pre-setup bazel"
-    fi
-  fi
-
-  # Introduce a syntax error in a BUILD file which is used in every build
-  # (Note this is a BUILD file which is copied as part of test setup, so this
-  # has no effect on sources outside of this test.
-  rm -rf  build/bazel/rules
-
-  USE_PERSISTENT_BAZEL=1 run_soong nothing 1>out/failurelog.txt 2>&1 && fail "Expected build failure" || true
-
-  if ! grep -sq "cannot load //build/bazel/rules/common/api_constants.bzl" out/failurelog.txt ; then
-    fail "Expected error to contain 'cannot load //build/bazel/rules/common/api_constants.bzl', instead got:\n$(cat out/failurelog.txt)"
-  fi
-
-  kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
-}
-
-scan_and_run_tests
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 231e18b..0235f2b 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -4,25 +4,6 @@
 
 TOP="$(readlink -f "$(dirname "$0")"/../../..)"
 "$TOP/build/soong/tests/androidmk_test.sh"
-"$TOP/build/soong/tests/b_args_test.sh"
 "$TOP/build/soong/tests/bootstrap_test.sh"
-"$TOP/build/soong/tests/mixed_mode_test.sh"
-"$TOP/build/soong/tests/bp2build_bazel_test.sh"
-"$TOP/build/soong/tests/persistent_bazel_test.sh"
 "$TOP/build/soong/tests/soong_test.sh"
-"$TOP/build/soong/tests/stale_metrics_files_test.sh"
-"$TOP/build/soong/tests/symlink_forest_rerun_test.sh"
 "$TOP/prebuilts/build-tools/linux-x86/bin/py3-cmd" "$TOP/build/bazel/ci/rbc_dashboard.py" aosp_arm64-userdebug
-
-# The following tests build against the full source tree and don't rely on the
-# mock client.
-"$TOP/build/soong/tests/apex_comparison_tests.sh"
-"$TOP/build/soong/tests/apex_comparison_tests.sh" "module_arm64only"
-TEST_BAZEL=true extra_build_params=--bazel-mode-staging "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
-#BUILD_BROKEN_DISABLE_BAZEL=true "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
-"$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh"
-"$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_arm" "armv7-a"
-"$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_cf_arm64_phone" "armv8-a" "cortex-a53"
-
-"$TOP/build/bazel/ci/b_test.sh"
-"$TOP/build/soong/tests/symlinks_path_test.sh"
diff --git a/tests/run_tool_with_logging_test.py b/tests/run_tool_with_logging_test.py
new file mode 100644
index 0000000..57a6d62
--- /dev/null
+++ b/tests/run_tool_with_logging_test.py
@@ -0,0 +1,337 @@
+# 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.
+
+import dataclasses
+import glob
+from importlib import resources
+import logging
+import os
+from pathlib import Path
+import re
+import shutil
+import signal
+import stat
+import subprocess
+import sys
+import tempfile
+import textwrap
+import time
+import unittest
+
+EXII_RETURN_CODE = 0
+INTERRUPTED_RETURN_CODE = 130
+
+
+class RunToolWithLoggingTest(unittest.TestCase):
+
+  @classmethod
+  def setUpClass(cls):
+    super().setUpClass()
+    # Configure to print logging to stdout.
+    logging.basicConfig(filename=None, level=logging.DEBUG)
+    console = logging.StreamHandler(sys.stdout)
+    logging.getLogger("").addHandler(console)
+
+  def setUp(self):
+    super().setUp()
+    self.working_dir = tempfile.TemporaryDirectory()
+    # Run all the tests from working_dir which is our temp Android build top.
+    os.chdir(self.working_dir.name)
+
+    self.logging_script_path = self._import_executable("run_tool_with_logging")
+
+  def tearDown(self):
+    self.working_dir.cleanup()
+    super().tearDown()
+
+  def test_does_not_log_when_logger_var_empty(self):
+    test_tool = TestScript.create(self.working_dir)
+
+    self._run_script_and_wait(f"""
+      export ANDROID_TOOL_LOGGER=""
+      {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+    """)
+
+    test_tool.assert_called_once_with_args("arg1 arg2")
+
+  def test_does_not_log_with_logger_unset(self):
+    test_tool = TestScript.create(self.working_dir)
+
+    self._run_script_and_wait(f"""
+      unset ANDROID_TOOL_LOGGER
+      {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+    """)
+
+    test_tool.assert_called_once_with_args("arg1 arg2")
+
+  def test_log_success_with_logger_enabled(self):
+    test_tool = TestScript.create(self.working_dir)
+    test_logger = TestScript.create(self.working_dir)
+
+    self._run_script_and_wait(f"""
+      export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+      {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+    """)
+
+    test_tool.assert_called_once_with_args("arg1 arg2")
+    expected_logger_args = (
+        "--tool_tag=FAKE_TOOL --start_timestamp=\d+\.\d+ --end_timestamp="
+        "\d+\.\d+ --tool_args=arg1 arg2 --exit_code=0"
+    )
+    test_logger.assert_called_once_with_args(expected_logger_args)
+
+  def test_run_tool_output_is_same_with_and_without_logging(self):
+    test_tool = TestScript.create(self.working_dir, "echo 'tool called'")
+    test_logger = TestScript.create(self.working_dir)
+
+    run_tool_with_logging_stdout, run_tool_with_logging_stderr = (
+        self._run_script_and_wait(f"""
+      export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+      {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+    """)
+    )
+
+    run_tool_without_logging_stdout, run_tool_without_logging_stderr = (
+        self._run_script_and_wait(f"""
+      export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+      {test_tool.executable} arg1 arg2
+    """)
+    )
+
+    self.assertEqual(
+        run_tool_with_logging_stdout, run_tool_without_logging_stdout
+    )
+    self.assertEqual(
+        run_tool_with_logging_stderr, run_tool_without_logging_stderr
+    )
+
+  def test_logger_output_is_suppressed(self):
+    test_tool = TestScript.create(self.working_dir)
+    test_logger = TestScript.create(self.working_dir, "echo 'logger called'")
+
+    run_tool_with_logging_output, _ = self._run_script_and_wait(f"""
+      export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+      {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+    """)
+
+    self.assertNotIn("logger called", run_tool_with_logging_output)
+
+  def test_logger_error_is_suppressed(self):
+    test_tool = TestScript.create(self.working_dir)
+    test_logger = TestScript.create(
+        self.working_dir, "echo 'logger failed' > /dev/stderr; exit 1"
+    )
+
+    _, err = self._run_script_and_wait(f"""
+      export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+      {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+    """)
+
+    self.assertNotIn("logger failed", err)
+
+  def test_log_success_when_tool_interrupted(self):
+    test_tool = TestScript.create(self.working_dir, script_body="sleep 100")
+    test_logger = TestScript.create(self.working_dir)
+
+    process = self._run_script(f"""
+      export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+      {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+    """)
+
+    pgid = os.getpgid(process.pid)
+    # Give sometime for the subprocess to start.
+    time.sleep(1)
+    # Kill the subprocess and any processes created in the same group.
+    os.killpg(pgid, signal.SIGINT)
+
+    returncode, _, _ = self._wait_for_process(process)
+    self.assertEqual(returncode, INTERRUPTED_RETURN_CODE)
+
+    expected_logger_args = (
+        "--tool_tag=FAKE_TOOL --start_timestamp=\d+\.\d+ --end_timestamp="
+        "\d+\.\d+ --tool_args=arg1 arg2 --exit_code=130"
+    )
+    test_logger.assert_called_once_with_args(expected_logger_args)
+
+  def test_logger_can_be_toggled_on(self):
+    test_tool = TestScript.create(self.working_dir)
+    test_logger = TestScript.create(self.working_dir)
+
+    self._run_script_and_wait(f"""
+      export ANDROID_TOOL_LOGGER=""
+      export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+      {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+    """)
+
+    test_logger.assert_called_with_times(1)
+
+  def test_logger_can_be_toggled_off(self):
+    test_tool = TestScript.create(self.working_dir)
+    test_logger = TestScript.create(self.working_dir)
+
+    self._run_script_and_wait(f"""
+      export ANDROID_TOOL_LOGGER="{test_logger.executable}"
+      export ANDROID_TOOL_LOGGER=""
+      {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2
+    """)
+
+    test_logger.assert_not_called()
+
+  def test_integration_tool_event_logger_dry_run(self):
+    test_tool = TestScript.create(self.working_dir)
+    logger_path = self._import_executable("tool_event_logger")
+
+    self._run_script_and_wait(f"""
+      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
+    """)
+
+    self._assert_logger_dry_run()
+
+  def test_tool_args_do_not_fail_logger(self):
+    test_tool = TestScript.create(self.working_dir)
+    logger_path = self._import_executable("tool_event_logger")
+
+    self._run_script_and_wait(f"""
+      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
+    """)
+
+    self._assert_logger_dry_run()
+
+  def _import_executable(self, executable_name: str) -> Path:
+    # logger = "tool_event_logger"
+    executable_path = Path(self.working_dir.name).joinpath(executable_name)
+    with resources.as_file(
+        resources.files("testdata").joinpath(executable_name)
+    ) as p:
+      shutil.copy(p, executable_path)
+    Path.chmod(executable_path, 0o755)
+    return executable_path
+
+  def _assert_logger_dry_run(self):
+    log_files = glob.glob(self.working_dir.name + "/tool_event_logger_*/*.log")
+    self.assertEqual(len(log_files), 1)
+
+    with open(log_files[0], "r") as f:
+      lines = f.readlines()
+      self.assertEqual(len(lines), 1)
+      self.assertIn("dry run", lines[0])
+
+  def _run_script_and_wait(self, test_script: str) -> tuple[str, str]:
+    process = self._run_script(test_script)
+    returncode, out, err = self._wait_for_process(process)
+    logging.debug("script stdout: %s", out)
+    logging.debug("script stderr: %s", err)
+    self.assertEqual(returncode, EXII_RETURN_CODE)
+    return out, err
+
+  def _run_script(self, test_script: str) -> subprocess.Popen:
+    return subprocess.Popen(
+        test_script,
+        shell=True,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        text=True,
+        start_new_session=True,
+        executable="/bin/bash",
+    )
+
+  def _wait_for_process(
+      self, process: subprocess.Popen
+  ) -> tuple[int, str, str]:
+    pgid = os.getpgid(process.pid)
+    out, err = process.communicate()
+    # Wait for all process in the same group to complete since the logger runs
+    # as a separate detached process.
+    self._wait_for_process_group(pgid)
+    return (process.returncode, out, err)
+
+  def _wait_for_process_group(self, pgid: int, timeout: int = 5):
+    """Waits for all subprocesses within the process group to complete."""
+    start_time = time.time()
+    while True:
+      if time.time() - start_time > timeout:
+        raise TimeoutError(
+            f"Process group did not complete after {timeout} seconds"
+        )
+      for pid in os.listdir("/proc"):
+        if pid.isdigit():
+          try:
+            if os.getpgid(int(pid)) == pgid:
+              time.sleep(0.1)
+              break
+          except (FileNotFoundError, PermissionError, ProcessLookupError):
+            pass
+      else:
+        # All processes have completed.
+        break
+
+
+@dataclasses.dataclass
+class TestScript:
+  executable: Path
+  output_file: Path
+
+  def create(temp_dir: Path, script_body: str = ""):
+    with tempfile.NamedTemporaryFile(dir=temp_dir.name, delete=False) as f:
+      output_file = f.name
+
+    with tempfile.NamedTemporaryFile(dir=temp_dir.name, delete=False) as f:
+      executable = f.name
+      executable_contents = textwrap.dedent(f"""
+      #!/bin/bash
+
+      echo "${{@}}" >> {output_file}
+      {script_body}
+      """)
+      f.write(executable_contents.encode("utf-8"))
+
+    Path.chmod(f.name, os.stat(f.name).st_mode | stat.S_IEXEC)
+
+    return TestScript(executable, output_file)
+
+  def assert_called_with_times(self, expected_call_times: int):
+    lines = self._read_contents_from_output_file()
+    assert len(lines) == expected_call_times, (
+        f"Expect to call {expected_call_times} times, but actually called"
+        f" {len(lines)} times."
+    )
+
+  def assert_called_with_args(self, expected_args: str):
+    lines = self._read_contents_from_output_file()
+    assert len(lines) > 0
+    assert re.search(expected_args, lines[0]), (
+        f"Expect to call with args {expected_args}, but actually called with"
+        f" args {lines[0]}."
+    )
+
+  def assert_not_called(self):
+    self.assert_called_with_times(0)
+
+  def assert_called_once_with_args(self, expected_args: str):
+    self.assert_called_with_times(1)
+    self.assert_called_with_args(expected_args)
+
+  def _read_contents_from_output_file(self) -> list[str]:
+    with open(self.output_file, "r") as f:
+      return f.readlines()
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/tests/stale_metrics_files_test.sh b/tests/stale_metrics_files_test.sh
deleted file mode 100755
index 0da89c3..0000000
--- a/tests/stale_metrics_files_test.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/bin/bash -e
-
-# This test ensures that stale metrics files are deleted after each run
-
-# Run bazel
-# Note - bp2build metrics are present after clean runs, only
-build/soong/soong_ui.bash --make-mode clean
-build/bazel/bin/b build libcore:all
-soong_build_metrics_files=("out/soong_build_metrics.pb" "out/build_progress.pb" "out/soong_metrics" "out/bp2build_metrics.pb")
-bazel_build_metrics_files=("out/bazel_metrics.pb" "out/build_progress.pb" "out/soong_metrics" "out/bp2build_metrics.pb")
-
-# Ensure bazel metrics files are present
-for i in ${!bazel_build_metrics_files[@]};
-do
-  file=${bazel_build_metrics_files[$i]}
-  if [[ ! -f $file ]]; then
-     echo "Missing metrics file for Bazel build " $file
-     exit 1
-  fi
-done
-
-
-# Run a soong build
-build/soong/soong_ui.bash --make-mode nothing
-
-for i in ${!soong_build_metrics_files[@]};
-do
-  file=${soong_build_metrics_files[$i]}
-  if [[ ! -f $file ]]; then
-     echo "Missing metrics file for Soong build " $file
-     exit 1
-  fi
-done
-
-# Ensure that bazel_metrics.pb is deleted
-if [[ -f out/bazel_metrics.pb ]]; then
-   echo "Stale out/bazel_metrics.pb file detected"
-   exit 1
-fi
-
-# Run bazel again - to make sure that soong_build_metrics.pb gets deleted
-build/bazel/bin/b build libcore:all
-
-if [[ -f out/soong_build_metrics.pb ]]; then
-   echo "Stale out/soong_build_metrics.pb file detected"
-   exit 1
-fi
diff --git a/tests/symlink_forest_rerun_test.sh b/tests/symlink_forest_rerun_test.sh
deleted file mode 100755
index 74e779e..0000000
--- a/tests/symlink_forest_rerun_test.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/bash -eu
-
-set -o pipefail
-
-# Tests that symlink forest will replant if soong_build has changed
-# Any change to the build system should trigger a rerun
-
-source "$(dirname "$0")/lib.sh"
-
-function test_symlink_forest_reruns {
-  setup
-
-  mkdir -p a
-  touch a/g.txt
-  cat > a/Android.bp <<'EOF'
-filegroup {
-    name: "g",
-    srcs: ["g.txt"],
-  }
-EOF
-
-  run_soong g
-
-  mtime=`cat out/soong/workspace/soong_build_mtime`
-  # rerun with no changes - ensure that it hasn't changed
-  run_soong g
-  newmtime=`cat out/soong/workspace/soong_build_mtime`
-  if [[ ! "$mtime" == "$mtime" ]]; then
-     fail "symlink forest reran when it shouldn't have"
-  fi
-
-  # change exit codes to force a soong_build rebuild.
-  sed -i 's/os.Exit(1)/os.Exit(2)/g' build/soong/bp2build/symlink_forest.go
-
-  run_soong g
-  newmtime=`cat out/soong/workspace/soong_build_mtime`
-  if [[ "$mtime" == "$newmtime" ]]; then
-     fail "symlink forest did not rerun when it should have"
-  fi
-
-}
-
-scan_and_run_tests
diff --git a/tests/symlinks_path_test.sh b/tests/symlinks_path_test.sh
deleted file mode 100755
index ed42911..0000000
--- a/tests/symlinks_path_test.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/bash -eu
-
-set -o pipefail
-
-# Test that relative symlinks work by recreating the bug in b/259191764
-# In some cases, developers prefer to move their checkouts. This causes
-# issues in that symlinked files (namely, the bazel wrapper script)
-# cannot be found. As such, we implemented relative symlinks so that a
-# moved checkout doesn't need a full clean before rebuilding.
-# The bazel output base will still need to be removed, as Starlark
-# doesn't seem to support relative symlinks yet.
-
-source "$(dirname "$0")/lib.sh"
-
-function check_link_has_mock_top_prefix {
-  input_link=$1
-  link_target=`readlink $input_link`
-  if [[ $link_target != "$MOCK_TOP"* ]]; then
-    echo "Symlink for file $input_link -> $link_target doesn't start with $MOCK_TOP"
-    exit 1
-  fi
-}
-
-function test_symlinks_updated_when_top_dir_changed {
-  setup
-
-  mkdir -p a
-  touch a/g.txt
-  cat > a/Android.bp <<'EOF'
-filegroup {
-    name: "g",
-    srcs: ["g.txt"],
-    bazel_module: {bp2build_available: true},
-}
-EOF
-  # A directory under $MOCK_TOP
-  outdir=out2
-
-  # Modify OUT_DIR in a subshell so it doesn't affect the top level one.
-  (export OUT_DIR=$MOCK_TOP/$outdir; run_soong bp2build && run_bazel build --config=bp2build --config=ci //a:g)
-
-  g_txt="out2/soong/workspace/a/g.txt"
-  check_link_has_mock_top_prefix "$g_txt"
-
-  move_mock_top
-
-  (export OUT_DIR=$MOCK_TOP/$outdir; run_soong bp2build && run_bazel build --config=bp2build --config=ci //a:g)
-  check_link_has_mock_top_prefix "$g_txt"
-}
-
-scan_and_run_tests
\ No newline at end of file
diff --git a/tradefed/providers.go b/tradefed/providers.go
index f41e09e..0abac12 100644
--- a/tradefed/providers.go
+++ b/tradefed/providers.go
@@ -6,7 +6,8 @@
 	"github.com/google/blueprint"
 )
 
-// Output files we need from a base test that we derive from.
+// Data that test_module_config[_host] modules types will need from
+// their dependencies to write out build rules and AndroidMkEntries.
 type BaseTestProviderData struct {
 	// data files and apps for android_test
 	InstalledFiles android.Paths
@@ -16,6 +17,17 @@
 	TestConfig android.Path
 	// Other modules we require to be installed to run tests. We expect base to build them.
 	HostRequiredModuleNames []string
+	RequiredModuleNames     []string
+	// List of test suites base uses.
+	TestSuites []string
+	// True indicates the base modules is built for Host.
+	IsHost bool
+	// Base's sdk version for AndroidMkEntries, generally only used for Host modules.
+	LocalSdkVersion string
+	// Base's certificate for AndroidMkEntries, generally only used for device modules.
+	LocalCertificate string
+	// Indicates if the base module was a unit test.
+	IsUnitTest bool
 }
 
 var BaseTestProviderKey = blueprint.NewProvider[BaseTestProviderData]()
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index ba6ab94..f9622d3 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -5,6 +5,8 @@
 	"android/soong/tradefed"
 	"encoding/json"
 	"fmt"
+	"io"
+	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -17,19 +19,29 @@
 // Register the license_kind module type.
 func RegisterTestModuleConfigBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("test_module_config", TestModuleConfigFactory)
+	ctx.RegisterModuleType("test_module_config_host", TestModuleConfigHostFactory)
 }
 
 type testModuleConfigModule struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
-	base android.Module
 
 	tradefedProperties
 
 	// Our updated testConfig.
 	testConfig android.OutputPath
-	manifest   android.InstallPath
+	manifest   android.OutputPath
 	provider   tradefed.BaseTestProviderData
+
+	supportFiles android.InstallPaths
+
+	isHost bool
+}
+
+// Host is mostly the same as non-host, just some diffs for AddDependency and
+// AndroidMkEntries, but the properties are the same.
+type testModuleConfigHostModule struct {
+	testModuleConfigModule
 }
 
 // Properties to list in Android.bp for this module.
@@ -68,8 +80,9 @@
 }
 
 var (
-	testModuleConfigTag = dependencyTag{name: "TestModuleConfigBase"}
-	pctx                = android.NewPackageContext("android/soong/tradefed_modules")
+	testModuleConfigTag     = dependencyTag{name: "TestModuleConfigBase"}
+	testModuleConfigHostTag = dependencyTag{name: "TestModuleConfigHostBase"}
+	pctx                    = android.NewPackageContext("android/soong/tradefed_modules")
 )
 
 func (m *testModuleConfigModule) InstallInTestcases() bool {
@@ -77,12 +90,15 @@
 }
 
 func (m *testModuleConfigModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if m.Base == nil {
+		ctx.ModuleErrorf("'base' field must be set to a 'android_test' module.")
+		return
+	}
 	ctx.AddDependency(ctx.Module(), testModuleConfigTag, *m.Base)
 }
 
 // Takes base's Tradefed Config xml file and generates a new one with the test properties
 // appeneded from this module.
-// Rewrite the name of the apk in "test-file-name" to be our module's name, rather than the original one.
 func (m *testModuleConfigModule) fixTestConfig(ctx android.ModuleContext, baseTestConfig android.Path) android.OutputPath {
 	// Test safe to do when no test_runner_options, but check for that earlier?
 	fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", ctx.ModuleName()+".config")
@@ -94,9 +110,8 @@
 	}
 	xmlTestModuleConfigSnippet, _ := json.Marshal(options)
 	escaped := proptools.NinjaAndShellEscape(string(xmlTestModuleConfigSnippet))
-	command.FlagWithArg("--test-file-name=", ctx.ModuleName()+".apk").
-		FlagWithArg("--orig-test-file-name=", *m.tradefedProperties.Base+".apk").
-		FlagWithArg("--test-runner-options=", escaped)
+	command.FlagWithArg("--test-runner-options=", escaped)
+
 	rule.Build("fix_test_config", "fix test config")
 	return fixedConfig.OutputPath
 }
@@ -143,35 +158,23 @@
 //
 // If we change to symlinks, this all needs to change.
 func (m *testModuleConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	m.validateBase(ctx, &testModuleConfigTag, "android_test", false)
+	m.generateManifestAndConfig(ctx)
 
-	ctx.VisitDirectDepsWithTag(testModuleConfigTag, func(dep android.Module) {
-		if provider, ok := android.OtherModuleProvider(ctx, dep, tradefed.BaseTestProviderKey); ok {
-			m.base = dep
-			m.provider = provider
-		} else {
-			ctx.ModuleErrorf("The base module '%s' does not provide test BaseTestProviderData.  Only 'android_test' modules are supported.", dep.Name())
-			return
-		}
-	})
+}
 
-	// 1) A manifest file listing the base.
-	installDir := android.PathForModuleInstall(ctx, ctx.ModuleName())
-	out := android.PathForModuleOut(ctx, "test_module_config.manifest")
-	android.WriteFileRule(ctx, out, fmt.Sprintf("{%q: %q}", "base", *m.tradefedProperties.Base))
-	ctx.InstallFile(installDir, out.Base(), out)
+// Ensure at least one test_suite is listed.  Ideally it should be general-tests
+// or device-tests, whichever is listed in base and prefer general-tests if both are listed.
+// However this is not enforced yet.
+//
+// Returns true if okay and reports errors via ModuleErrorf.
+func (m *testModuleConfigModule) validateTestSuites(ctx android.ModuleContext) bool {
+	if len(m.tradefedProperties.Test_suites) == 0 {
+		ctx.ModuleErrorf("At least one test-suite must be set or this won't run. Use \"general-tests\" or \"device-tests\"")
+		return false
+	}
 
-	// 2) Module.config / AndroidTest.xml
-	// Note, there is still a "test-tag" element with base's module name, but
-	// Tradefed team says its ignored anyway.
-	m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig)
-
-	// 3) Write ARCH/Module.apk in testcases.
-	// Handled by soong_app_prebuilt and OutputFile in entries.
-	// Nothing to do here.
-
-	// 4) Copy base's data files.
-	// Handled by soong_app_prebuilt and LOCAL_COMPATIBILITY_SUPPORT_FILES.
-	// Nothing to do here.
+	return true
 }
 
 func TestModuleConfigFactory() android.Module {
@@ -184,36 +187,198 @@
 	return module
 }
 
+func TestModuleConfigHostFactory() android.Module {
+	module := &testModuleConfigHostModule{}
+
+	module.AddProperties(&module.tradefedProperties)
+	android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	module.isHost = true
+
+	return module
+}
+
 // Implements android.AndroidMkEntriesProvider
 var _ android.AndroidMkEntriesProvider = (*testModuleConfigModule)(nil)
 
 func (m *testModuleConfigModule) AndroidMkEntries() []android.AndroidMkEntries {
-	// We rely on base writing LOCAL_COMPATIBILITY_SUPPORT_FILES for its data files
-	entriesList := m.base.(android.AndroidMkEntriesProvider).AndroidMkEntries()
-	entries := &entriesList[0]
-	entries.OutputFile = android.OptionalPathForPath(m.provider.OutputFile)
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		entries.SetString("LOCAL_MODULE", m.Name()) //  out module name, not base's
+	appClass := "APPS"
+	include := "$(BUILD_SYSTEM)/soong_app_prebuilt.mk"
+	if m.isHost {
+		appClass = "JAVA_LIBRARIES"
+		include = "$(BUILD_SYSTEM)/soong_java_prebuilt.mk"
+	}
+	return []android.AndroidMkEntries{{
+		Class:      appClass,
+		OutputFile: android.OptionalPathForPath(m.manifest),
+		Include:    include,
+		Required:   []string{*m.Base},
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig)
+				entries.SetString("LOCAL_MODULE_TAGS", "tests")
+				entries.SetString("LOCAL_TEST_MODULE_CONFIG_BASE", *m.Base)
+				if m.provider.LocalSdkVersion != "" {
+					entries.SetString("LOCAL_SDK_VERSION", m.provider.LocalSdkVersion)
+				}
+				if m.provider.LocalCertificate != "" {
+					entries.SetString("LOCAL_CERTIFICATE", m.provider.LocalCertificate)
+				}
 
-		// Out update config file with extra options.
-		entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig)
-		entries.SetString("LOCAL_MODULE_TAGS", "tests")
-		// Required for atest to run additional tradefed testtypes
-		entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", m.provider.HostRequiredModuleNames...)
+				entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", m.provider.IsUnitTest)
+				entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
 
-		// Clear the JNI symbols because they belong to base not us. Either transform the names in the string
-		// or clear the variable because we don't need it, we are copying bases libraries not generating
-		// new ones.
-		entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", "")
+				// The app_prebuilt_internal.mk files try create a copy of the OutputFile as an .apk.
+				// Normally, this copies the "package.apk" from the intermediate directory here.
+				// To prevent the copy of the large apk and to prevent confusion with the real .apk we
+				// link to, we set the STEM here to a bogus name and we set OutputFile to a small file (our manifest).
+				// We do this so we don't have to add more conditionals to base_rules.mk
+				// soong_java_prebult has the same issue for .jars so use this in both module types.
+				entries.SetString("LOCAL_MODULE_STEM", fmt.Sprintf("UNUSED-%s", *m.Base))
 
-		// Don't append to base's test-suites, only use the ones we define, so clear it before
-		// appending to it.
-		entries.SetString("LOCAL_COMPATIBILITY_SUITE", "")
-		if len(m.tradefedProperties.Test_suites) > 0 {
-			entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
+				// In normal java/app modules, the module writes LOCAL_COMPATIBILITY_SUPPORT_FILES
+				// and then base_rules.mk ends up copying each of those dependencies from .intermediates to the install directory.
+				// tasks/general-tests.mk, tasks/devices-tests.mk also use these to figure out
+				// which testcase files to put in a zip for running tests on another machine.
+				//
+				// We need our files to end up in the zip, but we don't want \.mk files to
+				// `install` files for us.
+				// So we create a new make variable to indicate these should be in the zip
+				// but not installed.
+				entries.AddStrings("LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES", m.supportFiles.Strings()...)
+			},
+		},
+		// Ensure each of our supportFiles depends on the installed file in base so that our symlinks will always
+		// resolve.  The provider gives us the .intermediate path for the support file in base, we change it to
+		// the installed path with a string substitution.
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string) {
+				for _, f := range m.supportFiles.Strings() {
+					// convert out/.../testcases/FrameworksServicesTests_contentprotection/file1.apk
+					// to      out/.../testcases/FrameworksServicesTests/file1.apk
+					basePath := strings.Replace(f, "/"+m.Name()+"/", "/"+*m.Base+"/", 1)
+					fmt.Fprintf(w, "%s: %s\n", f, basePath)
+				}
+			},
+		},
+	}}
+}
+
+func (m *testModuleConfigHostModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if m.Base == nil {
+		ctx.ModuleErrorf("'base' field must be set to a 'java_test_host' module")
+		return
+	}
+	ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), testModuleConfigHostTag, *m.Base)
+}
+
+// File to write:
+// 1) out/host/linux-x86/testcases/derived-module/test_module_config.manifest # contains base's name.
+// 2) out/host/linux-x86/testcases/derived-module/derived-module.config  # Update AnroidTest.xml
+// 3) out/host/linux-x86/testcases/derived-module/base.jar
+//   - written via soong_java_prebuilt.mk
+//
+// 4) out/host/linux-x86/testcases/derived-module/* # data dependencies from base.
+//   - written via our InstallSymlink
+func (m *testModuleConfigHostModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	m.validateBase(ctx, &testModuleConfigHostTag, "java_test_host", true)
+	m.generateManifestAndConfig(ctx)
+}
+
+// Ensure the base listed is the right type by checking that we get the expected provider data.
+// Returns false on errors and the context is updated with an error indicating the baseType expected.
+func (m *testModuleConfigModule) validateBase(ctx android.ModuleContext, depTag *dependencyTag, baseType string, baseShouldBeHost bool) {
+	ctx.VisitDirectDepsWithTag(*depTag, func(dep android.Module) {
+		if provider, ok := android.OtherModuleProvider(ctx, dep, tradefed.BaseTestProviderKey); ok {
+			if baseShouldBeHost == provider.IsHost {
+				m.provider = provider
+			} else {
+				if baseShouldBeHost {
+					ctx.ModuleErrorf("'android_test' module used as base, but 'java_test_host' expected.")
+				} else {
+					ctx.ModuleErrorf("'java_test_host' module used as base, but 'android_test' expected.")
+				}
+			}
 		} else {
-			entries.AddCompatibilityTestSuites("null-suite")
+			ctx.ModuleErrorf("'%s' module used as base but it is not a '%s' module.", *m.Base, baseType)
 		}
 	})
-	return entriesList
+}
+
+// Actions to write:
+//  1. manifest file to testcases dir
+//  2. Symlink to base.apk under base's arch dir
+//  3. Symlink to all data dependencies
+//  4. New Module.config / AndroidTest.xml file with our options.
+func (m *testModuleConfigModule) generateManifestAndConfig(ctx android.ModuleContext) {
+	// Keep before early returns.
+	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+		TestOnly:       true,
+		TopLevelTarget: true,
+	})
+
+	if !m.validateTestSuites(ctx) {
+		return
+	}
+	// Ensure the base provider is accurate
+	if m.provider.TestConfig == nil {
+		return
+	}
+	// 1) A manifest file listing the base, write text to a tiny file.
+	installDir := android.PathForModuleInstall(ctx, ctx.ModuleName())
+	manifest := android.PathForModuleOut(ctx, "test_module_config.manifest")
+	android.WriteFileRule(ctx, manifest, fmt.Sprintf("{%q: %q}", "base", *m.tradefedProperties.Base))
+	// build/soong/android/androidmk.go has this comment:
+	//    Assume the primary install file is last
+	// so we need to Install our file last.
+	ctx.InstallFile(installDir, manifest.Base(), manifest)
+	m.manifest = manifest.OutputPath
+
+	// 2) Symlink to base.apk
+	baseApk := m.provider.OutputFile
+
+	// Typically looks like this for baseApk
+	// FrameworksServicesTests
+	// └── x86_64
+	//    └── FrameworksServicesTests.apk
+	symlinkName := fmt.Sprintf("%s/%s", ctx.DeviceConfig().DeviceArch(), baseApk.Base())
+	// Only android_test, not java_host_test puts the output in the DeviceArch dir.
+	if m.provider.IsHost || ctx.DeviceConfig().DeviceArch() == "" {
+		// testcases/CtsDevicePolicyManagerTestCases
+		// ├── CtsDevicePolicyManagerTestCases.jar
+		symlinkName = baseApk.Base()
+	}
+	target := installedBaseRelativeToHere(symlinkName, *m.tradefedProperties.Base)
+	installedApk := ctx.InstallAbsoluteSymlink(installDir, symlinkName, target)
+	m.supportFiles = append(m.supportFiles, installedApk)
+
+	// 3) Symlink for all data deps
+	// And like this for data files and required modules
+	// FrameworksServicesTests
+	// ├── data
+	// │   └── broken_shortcut.xml
+	// ├── JobTestApp.apk
+	for _, f := range m.provider.InstalledFiles {
+		symlinkName := f.Rel()
+		target := installedBaseRelativeToHere(symlinkName, *m.tradefedProperties.Base)
+		installedPath := ctx.InstallAbsoluteSymlink(installDir, symlinkName, target)
+		m.supportFiles = append(m.supportFiles, installedPath)
+	}
+
+	// 4) Module.config / AndroidTest.xml
+	m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig)
+}
+
+var _ android.AndroidMkEntriesProvider = (*testModuleConfigHostModule)(nil)
+
+// Given a relative path to a file in the current directory or a subdirectory,
+// return a relative path under our sibling directory named `base`.
+// There should be one "../" for each subdir we descend plus one to backup to "base".
+//
+//	 ThisDir/file1
+//	 ThisDir/subdir/file2
+//	would return "../base/file1" or "../../subdir/file2"
+func installedBaseRelativeToHere(targetFileName string, base string) string {
+	backup := strings.Repeat("../", strings.Count(targetFileName, "/")+1)
+	return fmt.Sprintf("%s%s/%s", backup, base, targetFileName)
 }
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index ff53043..97179f5 100644
--- a/tradefed_modules/test_module_config_test.go
+++ b/tradefed_modules/test_module_config_test.go
@@ -16,8 +16,12 @@
 import (
 	"android/soong/android"
 	"android/soong/java"
+	"fmt"
+	"strconv"
 	"strings"
 	"testing"
+
+	"github.com/google/blueprint"
 )
 
 const bp = `
@@ -43,6 +47,7 @@
                         base: "base",
                         exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
                         include_annotations: ["android.platform.test.annotations.LargeTest"],
+                        test_suites: ["general-tests"],
                 }
 
 `
@@ -65,15 +70,36 @@
 	entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 
 	// Ensure some entries from base are there, specifically support files for data and helper apps.
-	assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"})
+	// Do not use LOCAL_COMPATIBILITY_SUPPORT_FILES, but instead use LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES
+	android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
+		[]string{"out/soong/target/product/test_device/testcases/derived_test/arm64/base.apk",
+			"out/soong/target/product/test_device/testcases/derived_test/HelperApp.apk",
+			"out/soong/target/product/test_device/testcases/derived_test/data/testfile"},
+		entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{})
+
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_REQUIRED_MODULES"], []string{"base"})
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_CERTIFICATE"], []string{"build/make/target/product/security/testkey.x509.pem"})
+	android.AssertStringEquals(t, "", entries.Class, "APPS")
 
 	// And some new derived entries are there.
 	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE_TAGS"], []string{"tests"})
 
-	// And ones we override
-	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""})
-
 	android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config")
+
+	// Check the footer lines.  Our support files should depend on base's support files.
+	convertedActual := make([]string, 5)
+	for i, e := range entries.FooterLinesForTests() {
+		// AssertStringPathsRelativeToTop doesn't replace both instances
+		convertedActual[i] = strings.Replace(e, ctx.Config.SoongOutDir(), "", 2)
+	}
+	android.AssertArrayString(t, fmt.Sprintf("%s", ctx.Config.SoongOutDir()), convertedActual, []string{
+		"include $(BUILD_SYSTEM)/soong_app_prebuilt.mk",
+		"/target/product/test_device/testcases/derived_test/arm64/base.apk: /target/product/test_device/testcases/base/arm64/base.apk",
+		"/target/product/test_device/testcases/derived_test/HelperApp.apk: /target/product/test_device/testcases/base/HelperApp.apk",
+		"/target/product/test_device/testcases/derived_test/data/testfile: /target/product/test_device/testcases/base/data/testfile",
+		"",
+	})
 }
 
 // Make sure we call test-config-fixer with the right args.
@@ -88,11 +114,11 @@
 	derived := ctx.ModuleForTests("derived_test", "android_common")
 	rule_cmd := derived.Rule("fix_test_config").RuleParams.Command
 	android.AssertStringDoesContain(t, "Bad FixConfig rule inputs", rule_cmd,
-		`--test-file-name=derived_test.apk --orig-test-file-name=base.apk --test-runner-options='[{"Name":"exclude-filter","Key":"","Value":"android.test.example.devcodelab.DevCodelabTest#testHelloFail"},{"Name":"include-annotation","Key":"","Value":"android.platform.test.annotations.LargeTest"}]'`)
+		`--test-runner-options='[{"Name":"exclude-filter","Key":"","Value":"android.test.example.devcodelab.DevCodelabTest#testHelloFail"},{"Name":"include-annotation","Key":"","Value":"android.platform.test.annotations.LargeTest"}]'`)
 }
 
 // Ensure we error for a base we don't support.
-func TestModuleConfigBadBaseShouldFail(t *testing.T) {
+func TestModuleConfigWithHostBaseShouldFailWithExplicitMessage(t *testing.T) {
 	badBp := `
 		java_test_host {
 			name: "base",
@@ -104,16 +130,60 @@
                         base: "base",
                         exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
                         include_annotations: ["android.platform.test.annotations.LargeTest"],
+                        test_suites: ["general-tests"],
                 }`
 
-	ctx := android.GroupFixturePreparers(
+	android.GroupFixturePreparers(
 		java.PrepareForTestWithJavaDefaultModules,
 		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
 	).ExtendWithErrorHandler(
-		android.FixtureExpectsAtLeastOneErrorMatchingPattern("does not provide test BaseTestProviderData")).
+		android.FixtureExpectsAtLeastOneErrorMatchingPattern("'java_test_host' module used as base, but 'android_test' expected")).
 		RunTestWithBp(t, badBp)
+}
 
-	ctx.ModuleForTests("derived_test", "android_common")
+func TestModuleConfigBadBaseShouldFailWithGeneralMessage(t *testing.T) {
+	badBp := `
+		java_library {
+			name: "base",
+                        srcs: ["a.java"],
+		}
+
+                test_module_config {
+                        name: "derived_test",
+                        base: "base",
+                        exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+                        include_annotations: ["android.platform.test.annotations.LargeTest"],
+                        test_suites: ["general-tests"],
+                }`
+
+	android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+	).ExtendWithErrorHandler(
+		android.FixtureExpectsOneErrorPattern("'base' module used as base but it is not a 'android_test' module.")).
+		RunTestWithBp(t, badBp)
+}
+
+func TestModuleConfigNoBaseShouldFail(t *testing.T) {
+	badBp := `
+		java_library {
+			name: "base",
+                        srcs: ["a.java"],
+		}
+
+                test_module_config {
+                        name: "derived_test",
+                        exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+                        include_annotations: ["android.platform.test.annotations.LargeTest"],
+                        test_suites: ["general-tests"],
+                }`
+
+	android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+	).ExtendWithErrorHandler(
+		android.FixtureExpectsOneErrorPattern("'base' field must be set to a 'android_test' module.")).
+		RunTestWithBp(t, badBp)
 }
 
 // Ensure we error for a base we don't support.
@@ -128,6 +198,7 @@
                 test_module_config {
                         name: "derived_test",
                         base: "base",
+                        test_suites: ["general-tests"],
                 }`
 
 	ctx := android.GroupFixturePreparers(
@@ -146,18 +217,26 @@
 			name: "base",
 			sdk_version: "current",
                         srcs: ["a.java"],
+                        data: [":HelperApp", "data/testfile"],
 		}
 
+                android_test_helper_app {
+                        name: "HelperApp",
+                        srcs: ["helper.java"],
+                }
+
                 test_module_config {
                         name: "derived_test",
                         base: "base",
                         include_annotations: ["android.platform.test.annotations.LargeTest"],
+                        test_suites: ["general-tests"],
                 }
 
                 test_module_config {
                         name: "another_derived_test",
                         base: "base",
                         include_annotations: ["android.platform.test.annotations.LargeTest"],
+                        test_suites: ["general-tests"],
                 }`
 
 	ctx := android.GroupFixturePreparers(
@@ -169,8 +248,12 @@
 		derived := ctx.ModuleForTests("derived_test", "android_common")
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 		// All these should be the same in both derived tests
-		assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"})
-		android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""})
+		android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
+			[]string{"out/soong/target/product/test_device/testcases/derived_test/arm64/base.apk",
+				"out/soong/target/product/test_device/testcases/derived_test/HelperApp.apk",
+				"out/soong/target/product/test_device/testcases/derived_test/data/testfile"},
+			entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
+
 		// Except this one, which points to the updated tradefed xml file.
 		android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config")
 		// And this one, the module name.
@@ -181,8 +264,11 @@
 		derived := ctx.ModuleForTests("another_derived_test", "android_common")
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
 		// All these should be the same in both derived tests
-		assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"})
-		android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""})
+		android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
+			[]string{"out/soong/target/product/test_device/testcases/another_derived_test/arm64/base.apk",
+				"out/soong/target/product/test_device/testcases/another_derived_test/HelperApp.apk",
+				"out/soong/target/product/test_device/testcases/another_derived_test/data/testfile"},
+			entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
 		// Except this one, which points to the updated tradefed xml file.
 		android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "another_derived_test/android_common/test_config_fixer/another_derived_test.config")
 		// And this one, the module name.
@@ -190,15 +276,149 @@
 	}
 }
 
-// Use for situations where the entries map contains pairs:  [srcPath:installedPath1, srcPath2:installedPath2]
-// and we want to compare the RHS of the pairs, i.e. installedPath1, installedPath2
-func assertEntryPairValues(t *testing.T, actual []string, expected []string) {
-	for i, e := range actual {
-		parts := strings.Split(e, ":")
-		if len(parts) != 2 {
-			t.Errorf("Expected entry to have a value delimited by :, received: %s", e)
-			return
+// Test_module_config_host rule is allowed to depend on java_test_host
+func TestModuleConfigHostBasics(t *testing.T) {
+	bp := `
+               java_test_host {
+                        name: "base",
+                        srcs: ["a.java"],
+                        test_suites: ["suiteA", "general-tests",  "suiteB"],
+               }
+
+                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: ["general-tests"],
+                }`
+
+	ctx := android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+	).RunTestWithBp(t, bp)
+
+	variant := ctx.Config.BuildOS.String() + "_common"
+	derived := ctx.ModuleForTests("derived_test", variant)
+	mod := derived.Module().(*testModuleConfigHostModule)
+	allEntries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)
+	entries := allEntries[0]
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE"], []string{"derived_test"})
+	android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SDK_VERSION"], []string{"private_current"})
+	android.AssertStringEquals(t, "", entries.Class, "JAVA_LIBRARIES")
+
+	if !mod.Host() {
+		t.Errorf("host bit is not set for a java_test_host module.")
+	}
+	actualData, _ := strconv.ParseBool(entries.EntryMap["LOCAL_IS_UNIT_TEST"][0])
+	android.AssertBoolEquals(t, "LOCAL_IS_UNIT_TEST", true, actualData)
+
+}
+
+// When you pass an 'android_test' as base, the warning message is a bit obscure,
+// talking about variants, but it is something.  Ideally we could do better.
+func TestModuleConfigHostBadBaseShouldFailWithVariantWarning(t *testing.T) {
+	badBp := `
+		android_test {
+			name: "base",
+			sdk_version: "current",
+                        srcs: ["a.java"],
 		}
-		android.AssertStringEquals(t, "", parts[1], expected[i])
+
+                test_module_config_host {
+                        name: "derived_test",
+                        base: "base",
+                        exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+                        include_annotations: ["android.platform.test.annotations.LargeTest"],
+                }`
+
+	android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+	).ExtendWithErrorHandler(
+		android.FixtureExpectsAtLeastOneErrorMatchingPattern("missing variant")).
+		RunTestWithBp(t, badBp)
+}
+
+func TestModuleConfigHostNeedsATestSuite(t *testing.T) {
+	badBp := `
+		java_test_host {
+			name: "base",
+                        srcs: ["a.java"],
+		}
+
+                test_module_config_host {
+                        name: "derived_test",
+                        base: "base",
+                        exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+                        include_annotations: ["android.platform.test.annotations.LargeTest"],
+                }`
+
+	android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+	).ExtendWithErrorHandler(
+		android.FixtureExpectsAtLeastOneErrorMatchingPattern("At least one test-suite must be set")).
+		RunTestWithBp(t, badBp)
+}
+
+func TestTestOnlyProvider(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+	).RunTestWithBp(t, `
+                // These should be test-only
+                test_module_config_host {
+                        name: "host-derived-test",
+                        base: "host-base",
+                        exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+                        include_annotations: ["android.platform.test.annotations.LargeTest"],
+                        test_suites: ["general-tests"],
+                }
+
+                test_module_config {
+                        name: "derived-test",
+                        base: "base",
+                        exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+                        include_annotations: ["android.platform.test.annotations.LargeTest"],
+                        test_suites: ["general-tests"],
+                }
+
+		android_test {
+			name: "base",
+			sdk_version: "current",
+                        data: ["data/testfile"],
+		}
+
+		java_test_host {
+			name: "host-base",
+                        srcs: ["a.java"],
+                        test_suites: ["general-tests"],
+		}`,
+	)
+
+	// Visit all modules and ensure only the ones that should
+	// marked as test-only are marked as test-only.
+
+	actualTestOnly := []string{}
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok {
+			if provider.TestOnly {
+				actualTestOnly = append(actualTestOnly, m.Name())
+			}
+		}
+	})
+	expectedTestOnlyModules := []string{
+		"host-derived-test",
+		"derived-test",
+		// android_test and java_test_host are tests too.
+		"host-base",
+		"base",
+	}
+
+	notEqual, left, right := android.ListSetDifference(expectedTestOnlyModules, actualTestOnly)
+	if notEqual {
+		t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
 	}
 }
diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go
index e7896ab..bbac2db 100644
--- a/ui/build/androidmk_denylist.go
+++ b/ui/build/androidmk_denylist.go
@@ -19,10 +19,14 @@
 )
 
 var androidmk_denylist []string = []string{
+	"bionic/",
 	"chained_build_config/",
 	"cts/",
 	"dalvik/",
 	"developers/",
+	"development/",
+	"device/sample/",
+	"frameworks/",
 	// Do not block other directories in kernel/, see b/319658303.
 	"kernel/configs/",
 	"kernel/prebuilts/",
@@ -30,6 +34,10 @@
 	"libcore/",
 	"libnativehelper/",
 	"pdk/",
+	"prebuilts/",
+	"sdk/",
+	"test/",
+	"trusty/",
 	// Add back toolchain/ once defensive Android.mk files are removed
 	//"toolchain/",
 }
diff --git a/ui/build/build.go b/ui/build/build.go
index d8c336e..c7319ed 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -21,7 +21,6 @@
 	"path/filepath"
 	"sync"
 	"text/template"
-	"time"
 
 	"android/soong/elf"
 	"android/soong/ui/metrics"
@@ -67,9 +66,12 @@
 	// (to allow for source control that uses something other than numbers),
 	// but must be a single word and a valid file name.
 	//
-	// If no BUILD_NUMBER is set, create a useful "I am an engineering build
-	// from this date/time" value.  Make it start with a non-digit so that
-	// anyone trying to parse it as an integer will probably get "0".
+	// If no BUILD_NUMBER is set, create a useful "I am an engineering build"
+	// value.  Make it start with a non-digit so that anyone trying to parse
+	// it as an integer will probably get "0". This value used to contain
+	// a timestamp, but now that more dependencies are tracked in order to
+	// reduce the importance of `m installclean`, changing it every build
+	// causes unnecessary rebuilds for local development.
 	buildNumber, ok := config.environ.Get("BUILD_NUMBER")
 	if ok {
 		writeValueIfChanged(ctx, config, config.OutDir(), "file_name_tag.txt", buildNumber)
@@ -78,7 +80,7 @@
 		if username, ok = config.environ.Get("BUILD_USERNAME"); !ok {
 			ctx.Fatalln("Missing BUILD_USERNAME")
 		}
-		buildNumber = fmt.Sprintf("eng.%.6s.%s", username, time.Now().Format("20060102.150405" /* YYYYMMDD.HHMMSS */))
+		buildNumber = fmt.Sprintf("eng.%.6s.00000000.000000", 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
diff --git a/ui/build/config.go b/ui/build/config.go
index 7426a78..2470f84 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -85,6 +85,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 +99,10 @@
 	// Autodetected
 	totalRAM uint64
 
-	brokenDupRules     bool
-	brokenUsesNetwork  bool
-	brokenNinjaEnvVars []string
+	brokenDupRules       bool
+	brokenUsesNetwork    bool
+	brokenNinjaEnvVars   []string
+	brokenMissingOutputs bool
 
 	pathReplaced bool
 
@@ -119,6 +121,10 @@
 	// There's quite a bit of overlap with module-info.json and soong module graph. We
 	// could consider merging them.
 	moduleDebugFile string
+
+	// Whether to use n2 instead of ninja.  This is controlled with the
+	// environment variable SOONG_USE_N2
+	useN2 bool
 }
 
 type NinjaWeightListSource uint
@@ -281,6 +287,10 @@
 		ret.moduleDebugFile, _ = filepath.Abs(shared.JoinPath(ret.SoongOutDir(), "soong-debug-info.json"))
 	}
 
+	if os.Getenv("SOONG_USE_N2") == "true" {
+		ret.useN2 = true
+	}
+
 	ret.environ.Unset(
 		// We're already using it
 		"USE_SOONG_UI",
@@ -311,6 +321,7 @@
 		"DISPLAY",
 		"GREP_OPTIONS",
 		"JAVAC",
+		"LEX",
 		"NDK_ROOT",
 		"POSIXLY_CORRECT",
 
@@ -336,6 +347,9 @@
 
 		// We read it here already, don't let others share in the fun
 		"GENERATE_SOONG_DEBUG",
+
+		// Use config.useN2 instead.
+		"SOONG_USE_N2",
 	)
 
 	if ret.UseGoma() || ret.ForceUseGoma() {
@@ -811,6 +825,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
@@ -1164,14 +1180,6 @@
 	c.sourceRootDirs = i
 }
 
-func (c *configImpl) GetIncludeTags() []string {
-	return c.includeTags
-}
-
-func (c *configImpl) SetIncludeTags(i []string) {
-	c.includeTags = i
-}
-
 func (c *configImpl) GetLogsPrefix() string {
 	return c.logsPrefix
 }
@@ -1251,6 +1259,11 @@
 }
 
 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()
@@ -1496,6 +1509,15 @@
 	}
 }
 
+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+".extra.variables")
+	}
+}
+
 func (c *configImpl) SoongNinjaFile() string {
 	targetProduct, err := c.TargetProductOrErr()
 	if err != nil {
@@ -1599,6 +1621,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
 }
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index e17bd54..e77df44 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -147,7 +147,6 @@
 var BannerVars = []string{
 	"PLATFORM_VERSION_CODENAME",
 	"PLATFORM_VERSION",
-	"PRODUCT_INCLUDE_TAGS",
 	"PRODUCT_SOURCE_ROOT_DIRS",
 	"TARGET_PRODUCT",
 	"TARGET_BUILD_VARIANT",
@@ -236,6 +235,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",
@@ -301,6 +305,6 @@
 	config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true")
 	config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true")
 	config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(makeVars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
-	config.SetIncludeTags(strings.Fields(makeVars["PRODUCT_INCLUDE_TAGS"]))
 	config.SetSourceRootDirs(strings.Fields(makeVars["PRODUCT_SOURCE_ROOT_DIRS"]))
+	config.SetBuildBrokenMissingOutputs(makeVars["BUILD_BROKEN_MISSING_OUTPUTS"] == "true")
 }
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 551b8ab..1935e72 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -56,6 +56,17 @@
 		"-d", "stats",
 		"--frontend_file", fifo,
 	}
+	if config.useN2 {
+		executable = config.PrebuiltBuildTool("n2")
+		args = []string{
+			"-d", "trace",
+			// TODO: implement these features, or remove them.
+			//"-d", "keepdepfile",
+			//"-d", "keeprsp",
+			//"-d", "stats",
+			"--frontend-file", fifo,
+		}
+	}
 
 	args = append(args, config.NinjaArgs()...)
 
@@ -72,10 +83,22 @@
 
 	args = append(args, "-f", config.CombinedNinjaFile())
 
-	args = append(args,
-		"-o", "usesphonyoutputs=yes",
-		"-w", "dupbuild=err",
-		"-w", "missingdepfile=err")
+	if !config.useN2 {
+		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.useN2 {
+			args = append(args,
+				"-w", "missingoutfile=err",
+			)
+		}
+	}
 
 	cmd := Command(ctx, config, "ninja", executable, args...)
 
@@ -89,16 +112,22 @@
 
 	switch config.NinjaWeightListSource() {
 	case NINJA_LOG:
-		cmd.Args = append(cmd.Args, "-o", "usesninjalogasweightlist=yes")
+		if !config.useN2 {
+			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")
+		if !config.useN2 {
+			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)
+		if !config.useN2 {
+			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 +227,16 @@
 			// We don't want this build broken flag to cause reanalysis, so allow it through to the
 			// actions.
 			"BUILD_BROKEN_INCORRECT_PARTITION_IMAGES",
+			"SOONG_USE_N2",
+			"RUST_BACKTRACE",
 		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
 	}
 
 	cmd.Environment.Set("DIST_DIR", config.DistDir())
 	cmd.Environment.Set("SHELL", "/bin/bash")
+	if config.useN2 {
+		cmd.Environment.Set("RUST_BACKTRACE", "1")
+	}
 
 	// Print the environment variables that Ninja is operating in.
 	ctx.Verboseln("Ninja environment: ")
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 2f25a8c..81c678d 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -94,7 +94,6 @@
 	"gcert":          Allowed,
 	"gcertstatus":    Allowed,
 	"gcloud":         Allowed,
-	"getopt":         Allowed,
 	"git":            Allowed,
 	"hexdump":        Allowed,
 	"jar":            Allowed,
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 5142a41..8fa147f 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -159,12 +159,6 @@
 		fmt.Fprintln(ctx.Writer, "")
 		return
 	}
-	if config.GoogleProdCredsExist() {
-		return
-	}
-	fmt.Fprintln(ctx.Writer, "")
-	fmt.Fprintln(ctx.Writer, "\033[33mWARNING: Missing LOAS credentials, please run `gcert`. This is required for a successful build execution. See go/rbe-android-default-announcement for more information.\033[0m")
-	fmt.Fprintln(ctx.Writer, "")
 }
 
 // DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 79584c6..e18cc25 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,7 +15,6 @@
 package build
 
 import (
-	"android/soong/ui/tracer"
 	"fmt"
 	"io/fs"
 	"os"
@@ -26,7 +25,8 @@
 	"sync/atomic"
 	"time"
 
-	"android/soong/bazel"
+	"android/soong/ui/tracer"
+
 	"android/soong/ui/metrics"
 	"android/soong/ui/metrics/metrics_proto"
 	"android/soong/ui/status"
@@ -270,7 +270,13 @@
 	} else if !exists {
 		// The tree is out of date for the current epoch, delete files used by bootstrap
 		// and force the primary builder to rerun.
-		os.Remove(config.SoongNinjaFile())
+		soongNinjaFile := config.SoongNinjaFile()
+		os.Remove(soongNinjaFile)
+		for _, file := range blueprint.GetNinjaShardFiles(soongNinjaFile) {
+			if ok, _ := fileExists(file); ok {
+				os.Remove(file)
+			}
+		}
 		for _, globFile := range bootstrapGlobFileList(config) {
 			os.Remove(globFile)
 		}
@@ -308,6 +314,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")
 
@@ -394,7 +403,6 @@
 	}
 
 	blueprintCtx := blueprint.NewContext()
-	blueprintCtx.AddIncludeTags(config.GetIncludeTags()...)
 	blueprintCtx.AddSourceRootDirs(config.GetSourceRootDirs()...)
 	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
 	blueprintConfig := BlueprintConfig{
@@ -594,10 +602,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))
 		}
@@ -634,6 +638,22 @@
 			"--frontend_file", fifo,
 			"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
 		}
+		if config.useN2 {
+			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"),
+			}
+		}
 
 		if extra, ok := config.Environment().Get("SOONG_UI_NINJA_ARGS"); ok {
 			ctx.Printf(`CAUTION: arguments in $SOONG_UI_NINJA_ARGS=%q, e.g. "-n", can make soong_build FAIL or INCORRECT`, extra)
@@ -641,8 +661,13 @@
 		}
 
 		ninjaArgs = append(ninjaArgs, targets...)
+		ninjaCmd := config.PrebuiltBuildTool("ninja")
+		if config.useN2 {
+			ninjaCmd = config.PrebuiltBuildTool("n2")
+		}
+
 		cmd := Command(ctx, config, "soong bootstrap",
-			config.PrebuiltBuildTool("ninja"), ninjaArgs...)
+			ninjaCmd, ninjaArgs...)
 
 		var ninjaEnv Environment
 
@@ -680,8 +705,15 @@
 
 	loadSoongBuildMetrics(ctx, config, beforeSoongTimestamp)
 
-	distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
+	soongNinjaFile := config.SoongNinjaFile()
+	distGzipFile(ctx, config, soongNinjaFile, "soong")
+	for _, file := range blueprint.GetNinjaShardFiles(soongNinjaFile) {
+		if ok, _ := fileExists(file); ok {
+			distGzipFile(ctx, config, file, "soong")
+		}
+	}
 	distFile(ctx, config, config.SoongVarsFile(), "soong")
+	distFile(ctx, config, config.SoongExtraVarsFile(), "soong")
 
 	if !config.SkipKati() {
 		distGzipFile(ctx, config, config.SoongAndroidMk(), "soong")
@@ -742,18 +774,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 3095139..687ad6f 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -64,7 +64,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)
@@ -79,6 +80,10 @@
 	// 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")
+
 	danglingRules := make(map[string]bool)
 
 	scanner := bufio.NewScanner(stdout)
@@ -91,9 +96,11 @@
 		if strings.HasPrefix(line, modulePathsDir) ||
 			strings.HasPrefix(line, rawFilesDir) ||
 			line == variablesFilePath ||
+			line == extraVariablesFilePath ||
 			line == dexpreoptConfigFilePath ||
 			line == buildDatetimeFilePath ||
-			line == bpglob {
+			line == bpglob ||
+			strings.HasPrefix(line, releaseConfigDir) {
 			// Leaf node is in one of Soong's bootstrap directories, which do not have
 			// full build rules in the primary build.ninja file.
 			continue