Merge "Revert^2 "Remove compilation actions from java sdk library"" 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/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..140822b 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,13 +579,15 @@
 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
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 d29e312..71a64dd 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -73,8 +73,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
diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go
index 5201fed..c37274c 100644
--- a/aconfig/aconfig_declarations_test.go
+++ b/aconfig/aconfig_declarations_test.go
@@ -88,19 +88,49 @@
 	android.AssertStringEquals(t, "rule must contain container", rule.Args["container"], "--container com.android.foo")
 }
 
-func TestAconfigDeclarationsWithoutContainer(t *testing.T) {
-	bp := `
-		aconfig_declarations {
-			name: "module_name",
-			package: "com.example.package",
-			srcs: [
-				"foo.aconfig",
-			],
-		}
-	`
-	result := runTest(t, android.FixtureExpectsNoErrors, bp)
-
-	module := result.ModuleForTests("module_name", "")
-	rule := module.Rule("aconfig")
-	android.AssertIntEquals(t, "rule must not contain container", len(rule.Args["container"]), 0)
+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)
+		})
+	}
 }
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..d01d13b 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,26 @@
     		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_library {
+    		name: "libaconfig_storage_protos_cc",
+    		srcs: ["libaconfig_storage_protos_cc.cc"],
+			}
+
 			cc_aconfig_library {
 				name: "my_cc_aconfig_library",
 				aconfig_declarations: "my_aconfig_declarations",
@@ -92,6 +113,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 
@@ -100,6 +122,27 @@
     		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_library {
+    		name: "libaconfig_storage_protos_cc",
+    		srcs: ["libaconfig_storage_protos_cc.cc"],
+			}
+
+
 			cc_aconfig_library {
 				name: "my_cc_aconfig_library",
 				aconfig_declarations: "my_aconfig_declarations",
@@ -126,6 +169,7 @@
 		aconfig_declarations {
 			name: "my_aconfig_declarations_bar",
 			package: "com.example.package",
+			container: "com.android.foo",
 			srcs: ["bar.aconfig"],
 		}
 
@@ -152,6 +196,30 @@
 			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,
+		}
+
+		cc_library {
+			name: "libaconfig_storage_protos_cc",
+			srcs: ["libaconfig_storage_protos_cc.cc"],
+			vendor_available: true,
+		}
 	`
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithAconfigBuildComponents,
@@ -176,6 +244,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 
@@ -184,6 +253,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 a46ce52..9f42e21 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -115,6 +115,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", "")
 	}
 
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_test.go b/aconfig/codegen/rust_aconfig_library_test.go
index fe28f94..523b464 100644
--- a/aconfig/codegen/rust_aconfig_library_test.go
+++ b/aconfig/codegen/rust_aconfig_library_test.go
@@ -46,6 +46,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 
@@ -131,6 +132,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 			rust_aconfig_library {
@@ -193,6 +195,7 @@
 			aconfig_declarations {
 				name: "my_aconfig_declarations",
 				package: "com.example.package",
+				container: "com.android.foo",
 				srcs: ["foo.aconfig"],
 			}
 			rust_aconfig_library {
diff --git a/android/Android.bp b/android/Android.bp
index 4c59592..9ce8cdc 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -41,6 +41,7 @@
         "buildinfo_prop.go",
         "config.go",
         "test_config.go",
+        "configurable_properties.go",
         "configured_jars.go",
         "csuite_config.go",
         "deapexer.go",
@@ -60,6 +61,7 @@
         "license_metadata.go",
         "license_sdk_member.go",
         "licenses.go",
+        "logtags.go",
         "makevars.go",
         "metrics.go",
         "module.go",
@@ -138,6 +140,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/androidmk.go b/android/androidmk.go
index 07f7c58..66f42f9 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() {
@@ -541,6 +542,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 +855,7 @@
 	mod blueprint.Module, provider AndroidMkDataProvider) error {
 
 	amod := mod.(Module).base()
-	if shouldSkipAndroidMkProcessing(amod) {
+	if shouldSkipAndroidMkProcessing(ctx, amod) {
 		return nil
 	}
 
@@ -939,7 +945,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 +967,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 +990,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/apex.go b/android/apex.go
index c0acada..dc0aeed 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -90,7 +90,13 @@
 	TestApexes []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{}{
@@ -586,75 +592,131 @@
 	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 this module has no apex variations the use the platform variation.
+	if len(apexInfos) == 0 {
+		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)
 
-	apexInfos := base.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
-
-	slices.SortFunc(apexInfos, func(a, b ApexInfo) int {
-		return strings.Compare(a.ApexVariationName, b.ApexVariationName)
-	})
-
-	var aliases [][2]string
-	if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
-		apexInfos, aliases = mergeApexVariations(apexInfos)
+	if !module.UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
+		apexInfos, _ = mergeApexVariations(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
@@ -665,13 +727,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)
@@ -689,7 +754,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
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index 03e32d6..91549e5 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"
 )
@@ -115,15 +113,6 @@
 func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BaseModuleContext) {
 	addContentsToProvider := func(p *PrebuiltSelectionInfoMap, m *apexContributions) {
 		for _, content := range m.Contents() {
-			// 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())
 			}
diff --git a/android/arch.go b/android/arch.go
index 27ce4d4..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)
 			}
 		}
@@ -980,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.
-		values = RemoveListFromList(values, []string{"arch_variant", "variant_prepend", "path", "replace_instead_of_append"})
+		// 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
@@ -1899,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_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 2dc1fd7..5506000 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -325,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)})
diff --git a/android/config.go b/android/config.go
index 75d135f..f6711e6 100644
--- a/android/config.go
+++ b/android/config.go
@@ -229,6 +229,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)
@@ -813,12 +818,12 @@
 }
 
 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"
 }
 
@@ -2099,6 +2104,7 @@
 		"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",
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/early_module_context.go b/android/early_module_context.go
index cf1b5fc..23f4c90 100644
--- a/android/early_module_context.go
+++ b/android/early_module_context.go
@@ -173,5 +173,5 @@
 }
 
 func (e *earlyModuleContext) OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{}) {
-	e.EarlyModuleContext.OtherModulePropertyErrorf(module, property, fmt, args)
+	e.EarlyModuleContext.OtherModulePropertyErrorf(module, property, fmt, args...)
 }
diff --git a/android/gen_notice.go b/android/gen_notice.go
index 1acc638..6815f64 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)
diff --git a/android/license_metadata.go b/android/license_metadata.go
index eabb1b1..3fea029 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
 	}
 
@@ -69,7 +69,7 @@
 		if dep == nil {
 			return
 		}
-		if !dep.Enabled() {
+		if !dep.Enabled(ctx) {
 			return
 		}
 
@@ -195,7 +195,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..e73645f 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,
 			}
diff --git a/android/module.go b/android/module.go
index 5fe379c..40a5910 100644
--- a/android/module.go
+++ b/android/module.go
@@ -60,7 +60,7 @@
 
 	base() *ModuleBase
 	Disable()
-	Enabled() bool
+	Enabled(ctx ConfigAndErrorContext) bool
 	Target() Target
 	MultiTargets() []Target
 
@@ -287,7 +287,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:
@@ -484,6 +484,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"`
@@ -1392,14 +1397,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() {
@@ -1643,7 +1645,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...)
 		}
 	})
@@ -1835,7 +1837,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 {
@@ -2136,19 +2138,19 @@
 }
 
 func (e configurationEvalutor) PropertyErrorf(property string, fmt string, args ...interface{}) {
-	e.ctx.OtherModulePropertyErrorf(e.m, property, fmt, args)
+	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 {
+	switch condition.FunctionName() {
 	case "release_variable":
-		if len(condition.Args) != 1 {
-			ctx.OtherModulePropertyErrorf(m, property, "release_variable requires 1 argument, found %d", len(condition.Args))
+		if condition.NumArgs() != 1 {
+			ctx.OtherModulePropertyErrorf(m, property, "release_variable requires 1 argument, found %d", condition.NumArgs())
 			return proptools.ConfigurableValueUndefined()
 		}
-		if v, ok := ctx.Config().productVariables.BuildFlags[condition.Args[0]]; ok {
+		if v, ok := ctx.Config().productVariables.BuildFlags[condition.Arg(0)]; ok {
 			return proptools.ConfigurableValueString(v)
 		}
 		return proptools.ConfigurableValueUndefined()
@@ -2157,12 +2159,12 @@
 		ctx.OtherModulePropertyErrorf(m, property, "TODO(b/323382414): Product variables are not yet supported in selects")
 		return proptools.ConfigurableValueUndefined()
 	case "soong_config_variable":
-		if len(condition.Args) != 2 {
-			ctx.OtherModulePropertyErrorf(m, property, "soong_config_variable requires 2 arguments, found %d", len(condition.Args))
+		if condition.NumArgs() != 2 {
+			ctx.OtherModulePropertyErrorf(m, property, "soong_config_variable requires 2 arguments, found %d", condition.NumArgs())
 			return proptools.ConfigurableValueUndefined()
 		}
-		namespace := condition.Args[0]
-		variable := condition.Args[1]
+		namespace := condition.Arg(0)
+		variable := condition.Arg(1)
 		if n, ok := ctx.Config().productVariables.VendorVars[namespace]; ok {
 			if v, ok := n[variable]; ok {
 				return proptools.ConfigurableValueString(v)
@@ -2170,8 +2172,8 @@
 		}
 		return proptools.ConfigurableValueUndefined()
 	case "arch":
-		if len(condition.Args) != 0 {
-			ctx.OtherModulePropertyErrorf(m, property, "arch requires no arguments, found %d", len(condition.Args))
+		if condition.NumArgs() != 0 {
+			ctx.OtherModulePropertyErrorf(m, property, "arch requires no arguments, found %d", condition.NumArgs())
 			return proptools.ConfigurableValueUndefined()
 		}
 		if !m.base().ArchReady() {
@@ -2180,8 +2182,8 @@
 		}
 		return proptools.ConfigurableValueString(m.base().Arch().ArchType.Name)
 	case "os":
-		if len(condition.Args) != 0 {
-			ctx.OtherModulePropertyErrorf(m, property, "os requires no arguments, found %d", len(condition.Args))
+		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.
@@ -2194,8 +2196,8 @@
 		// 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 len(condition.Args) != 0 {
-			ctx.OtherModulePropertyErrorf(m, property, "boolean_var_for_testing requires 0 arguments, found %d", len(condition.Args))
+		if condition.NumArgs() != 0 {
+			ctx.OtherModulePropertyErrorf(m, property, "boolean_var_for_testing requires 0 arguments, found %d", condition.NumArgs())
 			return proptools.ConfigurableValueUndefined()
 		}
 
@@ -2535,7 +2537,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...)
 		}
diff --git a/android/module_context.go b/android/module_context.go
index d3e2770..605d3ba 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -444,6 +444,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)
@@ -476,6 +491,7 @@
 		executable:            executable,
 		effectiveLicenseFiles: &licenseFiles,
 		partition:             fullInstallPath.partition,
+		skipInstall:           m.skipInstall(),
 	}
 	m.packagingSpecs = append(m.packagingSpecs, spec)
 	return spec
@@ -489,7 +505,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...)
@@ -562,7 +578,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
@@ -599,6 +615,7 @@
 		symlinkTarget:    relPath,
 		executable:       false,
 		partition:        fullInstallPath.partition,
+		skipInstall:      m.skipInstall(),
 	})
 
 	return fullInstallPath
@@ -610,7 +627,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
@@ -640,6 +657,7 @@
 		symlinkTarget:    absPath,
 		executable:       false,
 		partition:        fullInstallPath.partition,
+		skipInstall:      m.skipInstall(),
 	})
 
 	return fullInstallPath
diff --git a/android/mutator.go b/android/mutator.go
index 75ba650..440b906 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)
 
@@ -595,11 +594,16 @@
 
 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)
-		base := am.base()
-		base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, a.name)
-		base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variation)
 		a.mutator.Mutate(mctx, variation)
 	}
 }
@@ -674,13 +678,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/override_module.go b/android/override_module.go
index 1341f53..55f384f 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()
@@ -262,18 +262,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 +273,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 +287,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..fe61da1 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -43,6 +43,30 @@
 	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
+}
+
+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 +98,10 @@
 	return p.partition
 }
 
+func (p *PackagingSpec) SkipInstall() bool {
+	return p.skipInstall
+}
+
 type PackageModule interface {
 	Module
 	packagingBase() *PackagingBase
@@ -234,9 +262,15 @@
 					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
diff --git a/android/paths.go b/android/paths.go
index 2b33f67..8d92aa4 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -60,6 +60,7 @@
 
 	ModuleDir() string
 	ModuleErrorf(fmt string, args ...interface{})
+	OtherModulePropertyErrorf(module Module, property, fmt string, args ...interface{})
 }
 
 var _ EarlyModulePathContext = ModuleContext(nil)
@@ -277,6 +278,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 +297,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 {
@@ -550,7 +562,7 @@
 	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 {
@@ -1507,6 +1519,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))
 }
@@ -1594,6 +1617,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))
 }
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 794ddcc..51b86a5 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -275,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)
@@ -425,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) {
@@ -702,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..d775ac3 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -295,158 +295,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 +351,7 @@
 						}
 					})
 
-					moduleIsDisabled := !foo.Module().Enabled()
+					moduleIsDisabled := !foo.Module().Enabled(PanickingConfigAndErrorContext(result.TestContext))
 					deps := foo.Module().(*sourceModule).deps
 					if moduleIsDisabled {
 						if len(deps) > 0 {
@@ -762,45 +610,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/register.go b/android/register.go
index d00c15f..aeb3b4c 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
diff --git a/android/selects_test.go b/android/selects_test.go
index f912ce6..d9499a5 100644
--- a/android/selects_test.go
+++ b/android/selects_test.go
@@ -28,6 +28,7 @@
 		name          string
 		bp            string
 		provider      selectsTestProvider
+		providers     map[string]selectsTestProvider
 		vendorVars    map[string]map[string]string
 		expectedError string
 	}{
@@ -411,6 +412,42 @@
 			},
 		},
 		{
+			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 {
@@ -596,6 +633,61 @@
 			},
 			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"`,
+		},
 	}
 
 	for _, tc := range testCases {
@@ -617,10 +709,19 @@
 			result := fixtures.RunTestWithBp(t, tc.bp)
 
 			if tc.expectedError == "" {
-				m := result.ModuleForTests("foo", "android_arm64_armv8-a")
-				p, _ := OtherModuleProvider(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())
+					}
 				}
 			}
 		})
@@ -628,11 +729,14 @@
 }
 
 type selectsTestProvider struct {
-	my_bool               *bool
-	my_string             *string
-	my_string_list        *[]string
-	my_paths              *[]string
-	replacing_string_list *[]string
+	my_bool                        *bool
+	my_string                      *string
+	my_string_list                 *[]string
+	my_paths                       *[]string
+	replacing_string_list          *[]string
+	my_nonconfigurable_bool        *bool
+	my_nonconfigurable_string      *string
+	my_nonconfigurable_string_list []string
 }
 
 func (p *selectsTestProvider) String() string {
@@ -644,23 +748,42 @@
 	if p.my_string != nil {
 		myStringStr = *p.my_string
 	}
+	myNonconfigurableStringStr := "nil"
+	if p.my_string != nil {
+		myNonconfigurableStringStr = *p.my_nonconfigurable_string
+	}
 	return fmt.Sprintf(`selectsTestProvider {
 	my_bool: %v,
 	my_string: %s,
     my_string_list: %s,
     my_paths: %s,
 	replacing_string_list %s,
-}`, myBoolStr, myStringStr, p.my_string_list, p.my_paths, p.replacing_string_list)
+	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.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"`
-	Replacing_string_list proptools.Configurable[[]string] `android:"replace_instead_of_append,arch_variant"`
+	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"`
+	My_nonconfigurable_bool        *bool
+	My_nonconfigurable_string      *string
+	My_nonconfigurable_string_list []string
 }
 
 type selectsMockModule struct {
@@ -671,11 +794,14 @@
 
 func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 	SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{
-		my_bool:               p.properties.My_bool.Get(ctx),
-		my_string:             p.properties.My_string.Get(ctx),
-		my_string_list:        p.properties.My_string_list.Get(ctx),
-		my_paths:              p.properties.My_paths.Get(ctx),
-		replacing_string_list: p.properties.Replacing_string_list.Get(ctx),
+		my_bool:                        p.properties.My_bool.Get(ctx),
+		my_string:                      p.properties.My_string.Get(ctx),
+		my_string_list:                 p.properties.My_string_list.Get(ctx),
+		my_paths:                       p.properties.My_paths.Get(ctx),
+		replacing_string_list:          p.properties.Replacing_string_list.Get(ctx),
+		my_nonconfigurable_bool:        p.properties.My_nonconfigurable_bool,
+		my_nonconfigurable_string:      p.properties.My_nonconfigurable_string,
+		my_nonconfigurable_string_list: p.properties.My_nonconfigurable_string_list,
 	})
 }
 
diff --git a/android/singleton.go b/android/singleton.go
index 76df1eb..d364384 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -284,5 +284,5 @@
 }
 
 func (s *singletonContextAdaptor) OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) {
-	s.blueprintSingletonContext().OtherModulePropertyErrorf(module, property, format, args)
+	s.blueprintSingletonContext().OtherModulePropertyErrorf(module, property, format, args...)
 }
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 90b49eb..38db929 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{}
 
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index c78b726..c910974 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
 }
 
@@ -730,6 +746,90 @@
 	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:
+			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)
+		}
+	}
+	return nil
+}
+
 func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
 	s := propertyValue.String()
 
@@ -739,7 +839,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/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 a67624a..6518f4a 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1287,3 +1287,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/variable.go b/android/variable.go
index 0040d83..419bd61 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -20,8 +20,6 @@
 	"runtime"
 	"strings"
 
-	"android/soong/bazel"
-
 	"github.com/google/blueprint/proptools"
 )
 
@@ -60,16 +58,16 @@
 		// 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"`
@@ -280,7 +278,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"`
@@ -492,10 +490,6 @@
 
 	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"`
 
 	BuildFromSourceStub *bool `json:",omitempty"`
@@ -618,7 +612,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),
@@ -644,387 +638,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/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 9d61e1c..8a8bb2e 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",
diff --git a/apex/aconfig_test.go b/apex/aconfig_test.go
index 3e44f0b..726041c 100644
--- a/apex/aconfig_test.go
+++ b/apex/aconfig_test.go
@@ -162,6 +162,21 @@
 					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"],
+				}
+				cc_library {
+					name: "libaconfig_storage_protos_cc",
+					srcs: ["libaconfig_storage_protos_cc.cc"],
+				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_bar",
 					package: "com.example.package",
@@ -410,6 +425,21 @@
 					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"],
+				}
+				cc_library {
+					name: "libaconfig_storage_protos_cc",
+					srcs: ["libaconfig_storage_protos_cc.cc"],
+				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_foo",
 					package: "com.example.package",
@@ -460,6 +490,21 @@
 					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"],
+				}
+				cc_library {
+					name: "libaconfig_storage_protos_cc",
+					srcs: ["libaconfig_storage_protos_cc.cc"],
+				}
 				aconfig_declarations {
 					name: "my_aconfig_declarations_foo",
 					package: "com.example.package",
diff --git a/apex/apex.go b/apex/apex.go
index 2e7fbf6..40eb712 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -73,7 +73,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
@@ -1077,20 +1077,24 @@
 // 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() {
@@ -1117,7 +1121,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() {
@@ -1195,7 +1199,7 @@
 // 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 {
@@ -1207,7 +1211,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 {
@@ -1222,7 +1226,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 {
@@ -1284,40 +1288,41 @@
 	}
 }
 
-// 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.
-	// Note that a default variation "" is also created as an alias, and the default dependency
-	// variation is set to the default variation. This is to allow an apex to depend on another
-	// module which is outside of the apex. This is because the dependent module is not mutated
-	// for this apex variant.
-	createApexVariation := func(apexBundleName string) {
-		defaultVariation := ""
-		mctx.SetDefaultDependencyVariation(&defaultVariation)
-		mctx.CreateVariations(apexBundleName)
-		mctx.CreateAliasVariation(defaultVariation, apexBundleName)
-	}
-
-	if ai, ok := mctx.Module().(ApexInfoMutator); ok && apexModuleTypeRequiresVariant(ai) {
-		createApexVariation(ai.ApexVariationName())
-	} else if o, ok := mctx.Module().(*OverrideApex); ok {
+	if ai, ok := ctx.Module().(ApexInfoMutator); ok && apexModuleTypeRequiresVariant(ai) {
+		return []string{ai.ApexVariationName()}
+	} else if o, ok := ctx.Module().(*OverrideApex); ok {
 		apexBundleName := o.GetOverriddenModuleName()
 		if apexBundleName == "" {
-			mctx.ModuleErrorf("base property is not set")
-			return
+			ctx.ModuleErrorf("base property is not set")
 		}
-		createApexVariation(apexBundleName)
+		return []string{apexBundleName}
+	}
+	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 {
+		return ai.ApexVariationName()
+	} else if o, ok := ctx.Module().(*OverrideApex); ok {
+		return o.GetOverriddenModuleName()
+	}
+
+	return ""
+}
+
+func (a *apexTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+	if am, ok := ctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
+		android.MutateApexTransition(ctx, variation)
 	}
 }
 
@@ -1335,7 +1340,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 {
@@ -1969,7 +1974,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)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 0825ab9..9a5c2b4 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3671,34 +3671,13 @@
 
 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
@@ -5622,8 +5601,21 @@
 			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")
 
@@ -9241,7 +9233,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) {
@@ -10691,6 +10683,21 @@
 			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"],
+		}
+		cc_library {
+			name: "libaconfig_storage_protos_cc",
+			srcs: ["libaconfig_storage_protos_cc.cc"],
+		}
 	`)
 
 	mod := ctx.ModuleForTests("myapex", "android_common_myapex")
@@ -11413,6 +11420,7 @@
 			aconfig_declarations {
 				name: "%[1]s",
 				package: "com.example.package",
+				container: "system",
 				srcs: [
 					"%[1]s.aconfig",
 				],
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/cc/Android.bp b/cc/Android.bp
index 5ba9427..9ce8933 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -96,7 +96,6 @@
         "gen_test.go",
         "genrule_test.go",
         "library_headers_test.go",
-        "library_stub_test.go",
         "library_test.go",
         "lto_test.go",
         "ndk_test.go",
diff --git a/cc/androidmk.go b/cc/androidmk.go
index ef26366..df356df 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -88,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,
@@ -119,16 +119,15 @@
 				} 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{
diff --git a/cc/builder.go b/cc/builder.go
index 845176e..e255cbe 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -608,6 +608,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
@@ -855,8 +859,8 @@
 // into a single .ldump sAbi dump file
 func transformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Paths, soFile android.Path,
 	baseName string, exportedIncludeDirs []string, symbolFile android.OptionalPath,
-	excludedSymbolVersions, excludedSymbolTags []string,
-	api string) android.Path {
+	excludedSymbolVersions, excludedSymbolTags, includedSymbolTags []string,
+	api string, isLlndk bool) android.Path {
 
 	outputFile := android.PathForModuleOut(ctx, baseName+".lsdump")
 
@@ -874,6 +878,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()
diff --git a/cc/cc.go b/cc/cc.go
index e3954d7..2980c1a 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -50,6 +50,7 @@
 	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()
@@ -319,7 +320,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
@@ -908,6 +909,8 @@
 
 	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
 	mergedAconfigFiles map[string]android.Paths
+
+	logtagsPaths android.Paths
 }
 
 func (c *Module) AddJSONData(d *map[string]interface{}) {
@@ -1997,6 +2000,11 @@
 func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
 	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()
@@ -2508,7 +2516,7 @@
 }
 
 func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
-	if !c.Enabled() {
+	if !c.Enabled(actx) {
 		return
 	}
 
@@ -2756,7 +2764,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)
 	}
 }
diff --git a/cc/compiler.go b/cc/compiler.go
index 9a961cf..a1b329e 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -141,6 +141,19 @@
 		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"`
+
+		// 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
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 2436f33..b3e6639 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -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
 		}
 
diff --git a/cc/library.go b/cc/library.go
index 5b24809..12ecc13 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1249,16 +1249,29 @@
 
 func (library *libraryDecorator) linkLlndkSAbiDumpFiles(ctx ModuleContext,
 	deps PathDeps, sAbiDumpFiles android.Paths, soFile android.Path, libFileName string,
-	excludeSymbolVersions, excludeSymbolTags []string) android.Path {
+	excludeSymbolVersions, excludeSymbolTags []string,
+	vendorApiLevel string) android.Path {
 	// NDK symbols in version 34 are LLNDK symbols. Those in version 35 are not.
-	// TODO(b/314010764): Add parameters to read LLNDK symbols from the symbol file.
 	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...),
-		"34")
+		[]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,
@@ -1276,21 +1289,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
 		}
 	}
 }
@@ -1309,7 +1322,7 @@
 // sourceAbiDiff registers a build statement to compare linked sAbi dump files (.lsdump).
 func (library *libraryDecorator) sourceAbiDiff(ctx android.ModuleContext,
 	sourceDump, referenceDump android.Path,
-	baseName, nameExt string, isLlndkOrNdk, allowExtensions bool,
+	baseName, nameExt string, isLlndk, allowExtensions bool,
 	sourceVersion, errorMessage string) {
 
 	extraFlags := []string{"-target-version", sourceVersion}
@@ -1321,7 +1334,7 @@
 			"-allow-unreferenced-changes",
 			"-allow-unreferenced-elf-symbol-changes")
 	}
-	if isLlndkOrNdk {
+	if isLlndk {
 		extraFlags = append(extraFlags, "-consider-opaque-types-different")
 	}
 	if allowExtensions {
@@ -1337,23 +1350,23 @@
 
 func (library *libraryDecorator) crossVersionAbiDiff(ctx android.ModuleContext,
 	sourceDump, referenceDump android.Path,
-	baseName string, isLlndkOrNdk bool, sourceVersion, prevVersion string) {
+	baseName string, isLlndk bool, sourceVersion, prevVersion 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 + "."
 
 	library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, prevVersion,
-		isLlndkOrNdk, true /* allowExtensions */, sourceVersion, errorMessage)
+		isLlndk, true /* allowExtensions */, sourceVersion, errorMessage)
 }
 
 func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext,
 	sourceDump, referenceDump android.Path,
-	baseName, nameExt string, isLlndkOrNdk bool) {
+	baseName, nameExt string, isLlndk bool) {
 
 	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
 
 	library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt,
-		isLlndkOrNdk, false /* allowExtensions */, "current", errorMessage)
+		isLlndk, false /* allowExtensions */, "current", errorMessage)
 }
 
 func (library *libraryDecorator) optInAbiDiff(ctx android.ModuleContext,
@@ -1368,7 +1381,7 @@
 	}
 
 	library.sourceAbiDiff(ctx, sourceDump, referenceDump, baseName, nameExt,
-		false /* isLlndkOrNdk */, false /* allowExtensions */, "current", errorMessage)
+		false /* isLlndk */, false /* allowExtensions */, "current", errorMessage)
 }
 
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathDeps, objs Objects, fileName string, soFile android.Path) {
@@ -1385,20 +1398,30 @@
 			android.OptionalPathForModuleSrc(ctx, library.symbolFileForAbiCheck(ctx)),
 			headerAbiChecker.Exclude_symbol_versions,
 			headerAbiChecker.Exclude_symbol_tags,
-			currSdkVersion)
+			[]string{} /* includeSymbolTags */, currSdkVersion, false /* isLlndk */)
 
-		var llndkDump android.Path
+		var llndkDump, apexVariantDump android.Path
 		tags := classifySourceAbiDump(ctx)
 		for _, tag := range tags {
-			if tag == llndkLsdumpTag {
+			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)
+						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 {
 				addLsdumpPath(string(tag) + ":" + implDump.String())
 			}
@@ -1412,11 +1435,13 @@
 			}
 			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
@@ -1430,13 +1455,19 @@
 					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, sourceDump, prevDumpFile.Path(),
-					fileName, isLlndk || isNdk, currVersion, nameExt+prevVersion)
+					fileName, isLlndk, currVersion, nameExt+prevVersion)
 			}
 			// Check against the current version.
 			sourceDump = implDump
@@ -1447,12 +1478,16 @@
 				}
 			} 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, sourceDump, currDumpFile.Path(),
-					fileName, nameExt, isLlndk || isNdk)
+					fileName, nameExt, isLlndk)
 			}
 		}
 		// Check against the opt-in reference dumps.
diff --git a/cc/library_stub_test.go b/cc/library_stub_test.go
deleted file mode 100644
index 4df0a41..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_arm64_armv8-a_shared").Module()
-	libbarApiImportVendor := ctx.ModuleForTests("libbar.apiimport", "android_vendor_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_arm64_armv8-a_shared").Module()
-	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor_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_arm64_armv8-a_shared").Module()
-	libbar := ctx.ModuleForTests("libbar", "android_vendor_arm64_armv8-a_shared").Module()
-	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor_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_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_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_arm64_armv8-a").Module()
-	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor_arm64_armv8-a_shared").Module()
-	libbarApiVariant := ctx.ModuleForTests("libbar.llndk.apiimport", "android_vendor_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_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_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_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_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/linker.go b/cc/linker.go
index 9686697..56a68b2 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -330,6 +330,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 +343,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)
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 9e727a1..5b86c64 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -16,12 +16,12 @@
 
 import (
 	"android/soong/android"
+	"android/soong/etc"
 	"strings"
 )
 
 var (
 	llndkLibrarySuffix = ".llndk"
-	llndkHeadersSuffix = ".llndk"
 )
 
 // Holds properties to describe a stub shared library based on the provided version file.
@@ -78,3 +78,143 @@
 	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{}
+var _ android.OutputFileProducer = &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)
+}
+
+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) OutputFile() android.OutputPath {
+	return txt.outputFile
+}
+
+// PrebuiltEtcModule interface
+func (txt *llndkLibrariesTxtModule) BaseDir() string {
+	return "etc"
+}
+
+// PrebuiltEtcModule interface
+func (txt *llndkLibrariesTxtModule) SubDir() string {
+	return ""
+}
+
+func (txt *llndkLibrariesTxtModule) OutputFiles(tag string) (android.Paths, error) {
+	return android.Paths{txt.outputFile}, nil
+}
+
+func llndkMutator(mctx android.BottomUpMutatorContext) {
+	m, ok := mctx.Module().(*Module)
+	if !ok {
+		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 m.IsVndkPrebuiltLibrary() && !m.IsVndk() {
+		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/makevars.go b/cc/makevars.go
index 9251d6a..51bcbf0 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -279,7 +279,7 @@
 		sanitizerLibs := android.SortedStringValues(sanitizerVariables)
 		var sanitizerLibStems []string
 		ctx.VisitAllModules(func(m android.Module) {
-			if !m.Enabled() {
+			if !m.Enabled(ctx) {
 				return
 			}
 
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_library.go b/cc/ndk_library.go
index 25231fd..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 {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index e815172..3c48f68 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -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/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 ef43c8d..edd9cfe 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,8 +39,8 @@
 // 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:
@@ -134,11 +134,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() {
+		// APEX and opt-in platform dumps are placed in the same directory.
+		if m.library.hasStubsVariants() {
+			result = append(result, apexLsdumpTag)
+		} else if headerAbiChecker.enabled() {
 			result = append(result, platformLsdumpTag)
 		}
 	} else if headerAbiChecker.enabled() {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index db046ec..1a94729 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -55,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",
 	}
 
@@ -82,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"}
@@ -176,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))
@@ -407,6 +407,7 @@
 
 	android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
 	android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
+	android.RegisterMakeVarsProvider(pctx, memtagStackMakeVarsProvider)
 }
 
 func (sanitize *sanitize) props() []interface{} {
@@ -683,10 +684,14 @@
 		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() {
@@ -858,7 +863,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))
+		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)
 		}
@@ -879,6 +884,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() {
@@ -1303,6 +1315,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) {
@@ -1371,7 +1385,7 @@
 // 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
 		}
 		var sanitizers []string
@@ -1552,7 +1566,7 @@
 			addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain), 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*
@@ -1715,6 +1729,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
@@ -1761,3 +1783,7 @@
 func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
 	hwasanStaticLibs(ctx.Config()).exportToMake(ctx)
 }
+
+func memtagStackMakeVarsProvider(ctx android.MakeVarsContext) {
+	memtagStackStaticLibs(ctx.Config()).exportToMake(ctx)
+}
diff --git a/cc/sdk.go b/cc/sdk.go
index 736a958..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
diff --git a/cc/testing.go b/cc/testing.go
index 20c435a..4b4e866 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -554,6 +554,7 @@
 		ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 
 		RegisterVndkLibraryTxtTypes(ctx)
+		RegisterLlndkLibraryTxtType(ctx)
 	}),
 
 	// Additional files needed in tests that disallow non-existent source files.
@@ -570,17 +571,17 @@
 
 	// Additional files needed in tests that disallow non-existent source.
 	android.MockFS{
-		"defaults/cc/common/libc.map.txt":      nil,
-		"defaults/cc/common/libdl.map.txt":     nil,
-		"defaults/cc/common/libft2.map.txt":    nil,
-		"defaults/cc/common/libm.map.txt":      nil,
-		"defaults/cc/common/ndk_libc++_shared": nil,
-		"defaults/cc/common/crtbegin_so.c":     nil,
-		"defaults/cc/common/crtbegin.c":        nil,
-		"defaults/cc/common/crtend_so.c":       nil,
-		"defaults/cc/common/crtend.c":          nil,
-		"defaults/cc/common/crtbrand.c":        nil,
-		"external/compiler-rt/lib/cfi/cfi_blocklist.txt":   nil,
+		"defaults/cc/common/libc.map.txt":                nil,
+		"defaults/cc/common/libdl.map.txt":               nil,
+		"defaults/cc/common/libft2.map.txt":              nil,
+		"defaults/cc/common/libm.map.txt":                nil,
+		"defaults/cc/common/ndk_libc++_shared":           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,
@@ -702,6 +703,7 @@
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 
 	RegisterVndkLibraryTxtTypes(ctx)
+	RegisterLlndkLibraryTxtType(ctx)
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	android.RegisterPrebuiltMutators(ctx)
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/vndk.go b/cc/vndk.go
index 14b44b6..548992d 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -219,7 +219,6 @@
 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 })
@@ -323,8 +322,8 @@
 }
 
 // Check for modules that mustn't be VNDK
-func shouldSkipVndkMutator(m *Module) bool {
-	if !m.Enabled() {
+func shouldSkipVndkMutator(ctx android.ConfigAndErrorContext, m *Module) bool {
+	if !m.Enabled(ctx) {
 		return true
 	}
 	if !m.Device() {
@@ -339,7 +338,7 @@
 }
 
 func IsForVndkApex(mctx android.BottomUpMutatorContext, m *Module) bool {
-	if shouldSkipVndkMutator(m) {
+	if shouldSkipVndkMutator(mctx, m) {
 		return false
 	}
 
@@ -370,7 +369,7 @@
 		return
 	}
 
-	if shouldSkipVndkMutator(m) {
+	if shouldSkipVndkMutator(mctx, m) {
 		return
 	}
 
@@ -378,19 +377,12 @@
 	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)
@@ -404,8 +396,6 @@
 }
 
 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)
@@ -435,25 +425,6 @@
 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.
@@ -577,6 +548,7 @@
 func (txt *vndkLibrariesTxt) OutputFiles(tag string) (android.Paths, error) {
 	return android.Paths{txt.outputFile}, nil
 }
+
 func getVndkFileName(m *Module) (string, error) {
 	if library, ok := m.linker.(*libraryDecorator); ok {
 		return library.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
diff --git a/cmd/release_config/build_flag/main.go b/cmd/release_config/build_flag/main.go
index 6f909af..cc2b57a 100644
--- a/cmd/release_config/build_flag/main.go
+++ b/cmd/release_config/build_flag/main.go
@@ -1,10 +1,12 @@
 package main
 
 import (
+	"cmp"
 	"flag"
 	"fmt"
 	"os"
 	"path/filepath"
+	"slices"
 	"strings"
 
 	rc_lib "android/soong/cmd/release_config/release_config_lib"
@@ -36,6 +38,16 @@
 
 	// 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
 }
 
 type CommandFunc func(*rc_lib.ReleaseConfigs, Flags, string, []string) error
@@ -60,6 +72,14 @@
 	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 {
@@ -68,19 +88,41 @@
 	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("set", flag.ExitOnError)
-	relFlags.BoolVar(&all, "all", false, "Display all flags")
+	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 {
+	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
@@ -92,12 +134,17 @@
 
 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("set", flag.ExitOnError)
+	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
@@ -113,21 +160,72 @@
 		}
 	}
 
-	showName := len(releaseConfigList) > 1 || len(args) > 1
-	for _, config := range releaseConfigList {
-		var configName string
-		if len(releaseConfigList) > 1 {
-			configName = fmt.Sprintf("%s.", config.Name)
-		}
+	var maxVariableNameLen, maxReleaseNameLen int
+	var releaseNameFormat, variableNameFormat string
+	valueFormat := "%s"
+	showReleaseName := len(releaseConfigList) > 1
+	showVariableName := len(args) > 1
+	if showVariableName {
 		for _, arg := range args {
-			val, err := MarshalFlagValue(config, arg)
-			if err != nil {
-				return err
+			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()
+				}
 			}
-			if showName {
-				fmt.Printf("%s%s=%s\n", configName, arg, val)
+			val, err := MarshalFlagValue(config, arg)
+			if err == nil {
+				outputOneLine(arg, config.Name, val, valueFormat)
 			} else {
-				fmt.Printf("%s\n", val)
+				outputOneLine(arg, config.Name, "REDACTED", "%s")
 			}
 			if isTrace {
 				for _, trace := range config.FlagArtifacts[arg].Traces {
@@ -160,12 +258,15 @@
 	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 := GetMapDir(*flagArtifact.Traces[len(flagArtifact.Traces)-1].Source)
+		mapDir, err := configs.GetFlagValueDirectory(release, flagArtifact)
 		if err != nil {
 			return err
 		}
@@ -177,53 +278,79 @@
 		Value: rc_lib.UnmarshalValue(value),
 	}
 	flagPath := filepath.Join(valueDir, "flag_values", targetRelease, fmt.Sprintf("%s.textproto", name))
-	return rc_lib.WriteMessage(flagPath, flagValue)
+	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)
+	if err != nil {
+		return err
+	}
+	err = GetCommand(configs, commonFlags, cmd, args[0:1])
+	if err != nil {
+		return err
+	}
+	fmt.Printf("Updated: %s\n", flagPath)
+	return nil
 }
 
 func main() {
-	var err error
 	var commonFlags Flags
 	var configs *rc_lib.ReleaseConfigs
+	topDir, err := rc_lib.GetTopDir()
 
-	outEnv := os.Getenv("OUT_DIR")
-	if outEnv == "" {
-		outEnv = "out"
-	}
 	// Handle the common arguments
-	flag.StringVar(&commonFlags.top, "top", ".", "path to top of workspace")
+	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.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.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 {
-		commonFlags.targetReleases = rc_lib.StringList{"trunk_staging"}
+		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 {
-		panic(err)
+		errorExit(err)
 	}
 
 	// Get the current state of flagging.
 	relName := commonFlags.targetReleases[0]
 	if relName == "--all" || relName == "-all" {
-		// If the users said `--release --all`, grab trunk staging for simplicity.
-		relName = "trunk_staging"
+		commonFlags.allReleases = true
 	}
-	configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, relName)
+	configs, err = rc_lib.ReadReleaseConfigMaps(commonFlags.maps, relName, commonFlags.useGetBuildVar)
 	if err != nil {
-		panic(err)
+		errorExit(err)
 	}
 
 	if cmd, ok := commandMap[flag.Arg(0)]; ok {
 		args := flag.Args()
 		if err = cmd(configs, commonFlags, args[0], args[1:]); err != nil {
-			panic(err)
+			errorExit(err)
 		}
 	}
 }
diff --git a/cmd/release_config/crunch_flags/main.go b/cmd/release_config/crunch_flags/main.go
index 616674b..4d763c8 100644
--- a/cmd/release_config/crunch_flags/main.go
+++ b/cmd/release_config/crunch_flags/main.go
@@ -15,18 +15,30 @@
 	"google.golang.org/protobuf/proto"
 )
 
-// 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.
-var manualFlagNamePrefixes []string = []string{
-	"RELEASE_ACONFIG_",
-	"RELEASE_PLATFORM_",
-}
+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_",
+	}
 
-var defaultFlagNamespace string = "android_UNKNOWN"
+	// 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 "ap3a"
+		return nextName
 	}
 	return name
 }
@@ -89,7 +101,10 @@
 	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.
-			description += fmt.Sprintf(" %s", strings.TrimSpace(comment[commentRegexp.SubexpIndex("comment")]))
+			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))
@@ -99,10 +114,13 @@
 			description = ""
 			continue
 		}
-		declValue := matches[declRegexp.SubexpIndex("value")]
 		declName := matches[declRegexp.SubexpIndex("name")]
-		container := rc_proto.Container(rc_proto.Container_value[matches[declRegexp.SubexpIndex("container")]])
+		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 {
@@ -112,7 +130,7 @@
 			Name:        proto.String(declName),
 			Namespace:   proto.String(namespace),
 			Description: proto.String(description),
-			Container:   &container,
+			Containers:  containers,
 		}
 		description = ""
 		// Most build flags are `workflow: PREBUILT`.
@@ -160,7 +178,7 @@
 }
 
 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>[^,)]*)")
+	valRegexp, err := regexp.Compile("[[:space:]]+value.\"(?<name>[A-Z_0-9]+)\",[[:space:]]*(?<value>(\"[^\"]*\"|[^\",)]*))")
 	if err != nil {
 		return err
 	}
@@ -199,6 +217,9 @@
 				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 {
@@ -210,6 +231,12 @@
 	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 {
@@ -218,7 +245,7 @@
 	} else {
 		fmt.Printf("Processing %s\n", path)
 	}
-	configRegexp, err := regexp.Compile("^..call[[:space:]]+declare-release-config,[[:space:]]+(?<name>[_a-z0-0A-Z]+),[[:space:]]+(?<files>[^,]*)(,[[:space:]]*(?<inherits>.*)|[[:space:]]*)[)]$")
+	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
 	}
@@ -232,16 +259,16 @@
 		return err
 	}
 	cleanDir := strings.TrimLeft(dir, "../")
-	var defaultContainer rc_proto.Container
+	var defaultContainers []string
 	switch {
 	case strings.HasPrefix(cleanDir, "build/") || cleanDir == "vendor/google_shared/build":
-		defaultContainer = rc_proto.Container(rc_proto.Container_ALL)
+		defaultContainers = allContainers
 	case cleanDir == "vendor/google/release":
-		defaultContainer = rc_proto.Container(rc_proto.Container_ALL)
+		defaultContainers = allContainers
 	default:
-		defaultContainer = rc_proto.Container(rc_proto.Container_VENDOR)
+		defaultContainers = []string{"vendor"}
 	}
-	releaseConfigMap := &rc_proto.ReleaseConfigMap{DefaultContainer: &defaultContainer}
+	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)
@@ -273,6 +300,9 @@
 		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")]
@@ -299,15 +329,26 @@
 	var dirs rc_lib.StringList
 	var namespacesFile string
 	var descriptionsFile string
+	var debug bool
+	defaultTopDir, err := rc_lib.GetTopDir()
 
-	flag.StringVar(&top, "top", ".", "path to top of workspace")
+	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 {
-		panic(err)
+		errorExit(err)
 	}
 	if len(dirs) == 0 {
 		dirs = rc_lib.StringList{"build/release", "vendor/google_shared/build/release", "vendor/google/release"}
@@ -317,12 +358,12 @@
 	if namespacesFile != "" {
 		data, err := os.ReadFile(namespacesFile)
 		if err != nil {
-			panic(err)
+			errorExit(err)
 		}
 		for idx, line := range strings.Split(string(data), "\n") {
 			fields := strings.Split(line, " ")
 			if len(fields) > 2 {
-				panic(fmt.Errorf("line %d: too many fields: %s", idx, line))
+				errorExit(fmt.Errorf("line %d: too many fields: %s", idx, line))
 			}
 			namespaceMap[fields[0]] = fields[1]
 		}
@@ -334,7 +375,7 @@
 	if descriptionsFile != "" {
 		data, err := os.ReadFile(descriptionsFile)
 		if err != nil {
-			panic(err)
+			errorExit(err)
 		}
 		for _, line := range strings.Split(string(data), "\n") {
 			if strings.TrimSpace(line) != "" {
@@ -348,12 +389,12 @@
 	for _, dir := range dirs {
 		err = ProcessBuildFlags(dir, namespaceMap)
 		if err != nil {
-			panic(err)
+			errorExit(err)
 		}
 
 		err = ProcessReleaseConfigMap(dir, descriptionMap)
 		if err != nil {
-			panic(err)
+			errorExit(err)
 		}
 	}
 }
diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go
index a43fdcc..5432806 100644
--- a/cmd/release_config/release_config/main.go
+++ b/cmd/release_config/release_config/main.go
@@ -33,6 +33,9 @@
 	var configs *rc_lib.ReleaseConfigs
 	var json, pb, textproto bool
 	var product string
+	var allMake bool
+	var useBuildVar bool
+	var guard bool
 
 	defaultRelease := os.Getenv("TARGET_RELEASE")
 	if defaultRelease == "" {
@@ -48,6 +51,10 @@
 	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", true, "write makefiles for all release configs")
+	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 {
@@ -57,7 +64,7 @@
 	if err = os.Chdir(top); err != nil {
 		panic(err)
 	}
-	configs, err = rc_lib.ReadReleaseConfigMaps(releaseConfigMapPaths, targetRelease)
+	configs, err = rc_lib.ReadReleaseConfigMaps(releaseConfigMapPaths, targetRelease, useBuildVar)
 	if err != nil {
 		panic(err)
 	}
@@ -65,16 +72,36 @@
 	if err != nil {
 		panic(err)
 	}
-	releaseName := config.Name
 	err = os.MkdirAll(outputDir, 0775)
 	if err != nil {
 		panic(err)
 	}
-	makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, releaseName))
+
+	makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", 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 = configs.WriteMakefile(makefilePath, targetRelease)
 	if err != nil {
 		panic(err)
 	}
+	if allMake {
+		// Write one makefile per release config, using the canonical release name.
+		for k, _ := range configs.ReleaseConfigs {
+			if k != targetRelease {
+				makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, k))
+				err = configs.WriteMakefile(makefilePath, k)
+				if err != nil {
+					panic(err)
+				}
+			}
+		}
+	}
 	if json {
 		err = configs.WriteArtifact(outputDir, product, "json")
 		if err != nil {
@@ -93,4 +120,8 @@
 			panic(err)
 		}
 	}
+	if err = config.WritePartitionBuildFlags(outputDir, product, targetRelease); err != nil {
+		panic(err)
+	}
+
 }
diff --git a/cmd/release_config/release_config_lib/flag_artifact.go b/cmd/release_config/release_config_lib/flag_artifact.go
index 4446655..cba1b5c 100644
--- a/cmd/release_config/release_config_lib/flag_artifact.go
+++ b/cmd/release_config/release_config_lib/flag_artifact.go
@@ -36,6 +36,10 @@
 
 	// 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.
@@ -85,6 +89,11 @@
 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)
 	}
@@ -111,9 +120,23 @@
 
 // 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_value.go b/cmd/release_config/release_config_lib/flag_value.go
index e155e77..59021e2 100644
--- a/cmd/release_config/release_config_lib/flag_value.go
+++ b/cmd/release_config/release_config_lib/flag_value.go
@@ -52,6 +52,9 @@
 }
 
 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.
diff --git a/cmd/release_config/release_config_lib/flag_value_test.go b/cmd/release_config/release_config_lib/flag_value_test.go
index aaa4caf..8a98baf 100644
--- a/cmd/release_config/release_config_lib/flag_value_test.go
+++ b/cmd/release_config/release_config_lib/flag_value_test.go
@@ -24,7 +24,7 @@
 	"google.golang.org/protobuf/proto"
 )
 
-type testCaseFlagValue struct {
+type testCaseFlagValueFactory struct {
 	protoPath string
 	name      string
 	data      []byte
@@ -32,14 +32,14 @@
 	err       error
 }
 
-func (tc testCaseFlagValue) assertProtoEqual(t *testing.T, expected, actual proto.Message) {
+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 TestFlagValue(t *testing.T) {
-	testCases := []testCaseFlagValue{
+func TestFlagValueFactory(t *testing.T) {
+	testCases := []testCaseFlagValueFactory{
 		{
 			name:      "stringVal",
 			protoPath: "build/release/flag_values/test/RELEASE_FOO.textproto",
@@ -65,3 +65,50 @@
 		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
index c67cee5..8204822 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -15,7 +15,11 @@
 package release_config_lib
 
 import (
+	"cmp"
 	"fmt"
+	"path/filepath"
+	"slices"
+	"sort"
 	"strings"
 
 	rc_proto "android/soong/cmd/release_config/release_config_proto"
@@ -25,7 +29,7 @@
 
 // One directory's contribution to the a release config.
 type ReleaseConfigContribution struct {
-	// Paths to files providing this config.
+	// Path of the file providing this config contribution.
 	path string
 
 	// The index of the config directory where this release config
@@ -58,6 +62,10 @@
 	// 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
 
@@ -66,12 +74,37 @@
 
 	// We have begun compiling this release config.
 	compileInProgress bool
+
+	// Partitioned artifacts for {partition}/etc/build_flags.json
+	PartitionBuildFlags map[string]*rc_proto.FlagArtifacts
 }
 
 func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) {
 	return &ReleaseConfig{Name: name, DeclarationIndex: index}
 }
 
+func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error {
+	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 len(fa.Traces) > 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) GenerateReleaseConfig(configs *ReleaseConfigs) error {
 	if config.ReleaseConfigArtifact != nil {
 		return nil
@@ -82,6 +115,10 @@
 	config.compileInProgress = true
 	isRoot := config.Name == "root"
 
+	// Start with only the flag declarations.
+	config.FlagArtifacts = configs.FlagArtifacts.Clone()
+	releaseAconfigValueSets := config.FlagArtifacts["RELEASE_ACONFIG_VALUE_SETS"]
+
 	// Generate any configs we need to inherit.  This will detect loops in
 	// the config.
 	contributionsToApply := []*ReleaseConfigContribution{}
@@ -103,72 +140,68 @@
 			return err
 		}
 		iConfig.GenerateReleaseConfig(configs)
-		contributionsToApply = append(contributionsToApply, iConfig.Contributions...)
+		if err := config.InheritConfig(iConfig); err != nil {
+			return err
+		}
 	}
 	contributionsToApply = append(contributionsToApply, config.Contributions...)
 
-	myAconfigValueSets := []string{}
-	myAconfigValueSetsMap := map[string]bool{}
-	myFlags := configs.FlagArtifacts.Clone()
 	workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL)
-	container := rc_proto.Container(rc_proto.Container_ALL)
-	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,
-			Container:   &container,
-			Value:       &rc_proto.Value{Val: &rc_proto.Value_StringValue{""}},
-		},
-		DeclarationIndex: -1,
-		Traces: []*rc_proto.Tracepoint{
-			&rc_proto.Tracepoint{
-				Source: proto.String("$release-config"),
-				Value:  &rc_proto.Value{Val: &rc_proto.Value_StringValue{""}},
-			},
-		},
-	}
-	myFlags["RELEASE_ACONFIG_VALUE_SETS"] = &releaseAconfigValueSets
 	myDirsMap := make(map[int]bool)
 	for _, contrib := range contributionsToApply {
-		if len(contrib.proto.AconfigValueSets) > 0 {
-			contribAconfigValueSets := []string{}
-			for _, v := range contrib.proto.AconfigValueSets {
-				if _, ok := myAconfigValueSetsMap[v]; !ok {
-					contribAconfigValueSets = append(contribAconfigValueSets, v)
-					myAconfigValueSetsMap[v] = true
-				}
-			}
-			myAconfigValueSets = append(myAconfigValueSets, contribAconfigValueSets...)
-			releaseAconfigValueSets.Traces = append(
-				releaseAconfigValueSets.Traces,
-				&rc_proto.Tracepoint{
-					Source: proto.String(contrib.path),
-					Value:  &rc_proto.Value{Val: &rc_proto.Value_StringValue{strings.Join(contribAconfigValueSets, " ")}},
-				})
+		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}},
+			})
+
 		myDirsMap[contrib.DeclarationIndex] = true
+		if config.AconfigFlagsOnly && len(contrib.FlagValues) > 0 {
+			return fmt.Errorf("%s does not allow build flag overrides", config.Name)
+		}
 		for _, value := range contrib.FlagValues {
-			fa, ok := myFlags[*value.proto.Name]
+			name := *value.proto.Name
+			fa, ok := config.FlagArtifacts[name]
 			if !ok {
-				return fmt.Errorf("Setting value for undefined flag %s in %s\n", *value.proto.Name, value.path)
+				return fmt.Errorf("Setting value for undefined flag %s in %s\n", name, value.path)
 			}
 			myDirsMap[fa.DeclarationIndex] = true
 			if fa.DeclarationIndex > contrib.DeclarationIndex {
 				// Setting location is to the left of declaration.
-				return fmt.Errorf("Setting value for flag %s not allowed in %s\n", *value.proto.Name, value.path)
+				return fmt.Errorf("Setting value for flag %s not allowed in %s\n", name, 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", *value.proto.Name, value.path)
+				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)
+			}
 		}
 	}
-	releaseAconfigValueSets.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{strings.Join(myAconfigValueSets, " ")}}
+	// 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 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{}
 	for idx, confDir := range configs.configDirs {
@@ -177,13 +210,32 @@
 		}
 	}
 
-	config.FlagArtifacts = myFlags
+	// 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].FlagArtifacts = append(config.PartitionBuildFlags[container].FlagArtifacts, artifact)
+		}
+	}
 	config.ReleaseConfigArtifact = &rc_proto.ReleaseConfigArtifact{
 		Name:       proto.String(config.Name),
 		OtherNames: config.OtherNames,
 		FlagArtifacts: func() []*rc_proto.FlagArtifact {
 			ret := []*rc_proto.FlagArtifact{}
-			for _, flag := range myFlags {
+			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,
@@ -200,3 +252,16 @@
 	config.compileInProgress = false
 	return nil
 }
+
+func (config *ReleaseConfig) WritePartitionBuildFlags(outDir, product, targetRelease string) error {
+	var err error
+	for partition, flags := range config.PartitionBuildFlags {
+		slices.SortFunc(flags.FlagArtifacts, func(a, b *rc_proto.FlagArtifact) int {
+			return cmp.Compare(*a.FlagDeclaration.Name, *b.FlagDeclaration.Name)
+		})
+		if err = WriteMessage(filepath.Join(outDir, fmt.Sprintf("build_flags_%s-%s-%s.json", partition, config.Name, product)), flags); err != nil {
+			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
index 6efdb2f..8a4e2d5 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -95,7 +95,7 @@
 }
 
 func ReleaseConfigsFactory() (c *ReleaseConfigs) {
-	return &ReleaseConfigs{
+	configs := ReleaseConfigs{
 		Aliases:              make(map[string]*string),
 		FlagArtifacts:        make(map[string]*FlagArtifact),
 		ReleaseConfigs:       make(map[string]*ReleaseConfig),
@@ -103,6 +103,21 @@
 		configDirs:           []string{},
 		configDirIndexes:     make(ReleaseConfigDirMap),
 	}
+	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 ReleaseConfigMapFactory(protoPath string) (m *ReleaseConfigMap) {
@@ -116,10 +131,44 @@
 	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.DefaultContainer == nil {
-		return fmt.Errorf("Release config map %s lacks default_container", 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)
+		}
 	}
 	dir := filepath.Dir(path)
 	// Record any aliases, checking for duplicates.
@@ -138,9 +187,16 @@
 	err = WalkTextprotoFiles(dir, "flag_declarations", func(path string, d fs.DirEntry, err error) error {
 		flagDeclaration := FlagDeclarationFactory(path)
 		// Container must be specified.
-		if flagDeclaration.Container == nil {
-			flagDeclaration.Container = m.proto.DefaultContainer
+		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")
@@ -151,6 +207,9 @@
 		}
 		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) {
@@ -160,6 +219,9 @@
 		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 {
@@ -185,12 +247,18 @@
 			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)
+			}
 			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
@@ -250,19 +318,12 @@
 		flag := myFlagArtifacts[name]
 		decl := flag.FlagDeclaration
 
-		// cName := strings.ToLower(rc_proto.Container_name[decl.GetContainer()])
-		cName := strings.ToLower(decl.Container.String())
-		if cName == strings.ToLower(rc_proto.Container_ALL.String()) {
-			partitions["product"] = append(partitions["product"], name)
-			partitions["system"] = append(partitions["system"], name)
-			partitions["system_ext"] = append(partitions["system_ext"], name)
-			partitions["vendor"] = append(partitions["vendor"], name)
-		} else {
-			partitions[cName] = append(partitions[cName], name)
+		for _, container := range decl.Containers {
+			partitions[container] = append(partitions[container], name)
 		}
 		value := MarshalValue(flag.Value)
 		makeVars[name] = value
-		addVar(name, "PARTITIONS", cName)
+		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)
@@ -357,23 +418,35 @@
 	return nil
 }
 
-func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease string) (*ReleaseConfigs, error) {
+func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease string, useBuildVar bool) (*ReleaseConfigs, error) {
 	var err error
 
 	if len(releaseConfigMapPaths) == 0 {
-		releaseConfigMapPaths = GetDefaultMapPaths()
+		releaseConfigMapPaths, err = GetDefaultMapPaths(useBuildVar)
+		if err != nil {
+			return nil, err
+		}
 		if len(releaseConfigMapPaths) == 0 {
 			return nil, fmt.Errorf("No maps found")
 		}
-		fmt.Printf("No --map argument provided.  Using: --map %s\n", strings.Join(releaseConfigMapPaths, " --map "))
+		if !useBuildVar {
+			warnf("No --map argument provided.  Using: --map %s\n", strings.Join(releaseConfigMapPaths, " --map "))
+		}
 	}
 
 	configs := ReleaseConfigsFactory()
+	mapsRead := make(map[string]bool)
 	for idx, 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
diff --git a/cmd/release_config/release_config_lib/util.go b/cmd/release_config/release_config_lib/util.go
index 86940da..c0ea789 100644
--- a/cmd/release_config/release_config_lib/util.go
+++ b/cmd/release_config/release_config_lib/util.go
@@ -19,14 +19,19 @@
 	"fmt"
 	"io/fs"
 	"os"
+	"os/exec"
 	"path/filepath"
+	"regexp"
 	"strings"
 
 	"google.golang.org/protobuf/encoding/prototext"
 	"google.golang.org/protobuf/proto"
 )
 
-var disableWarnings bool
+var (
+	disableWarnings    bool
+	containerRegexp, _ = regexp.Compile("^[a-z][a-z0-9]*([._][a-z][a-z0-9]*)*$")
+)
 
 type StringList []string
 
@@ -123,11 +128,15 @@
 
 func warnf(format string, args ...any) (n int, err error) {
 	if !disableWarnings {
-		return fmt.Printf(format, args...)
+		return fmt.Fprintf(os.Stderr, format, args...)
 	}
 	return 0, nil
 }
 
+func validContainer(container string) bool {
+	return containerRegexp.MatchString(container)
+}
+
 // Returns the default value for release config artifacts.
 func GetDefaultOutDir() string {
 	outEnv := os.Getenv("OUT_DIR")
@@ -137,22 +146,64 @@
 	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() StringList {
-	var defaultMapPaths StringList
-	defaultLocations := StringList{
+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 {
+		if _, err = os.Stat(path); err == nil {
 			defaultMapPaths = append(defaultMapPaths, path)
 		}
 	}
-	prodMaps := os.Getenv("PRODUCT_RELEASE_CONFIG_MAPS")
-	if prodMaps != "" {
+
+	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
+		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 defaultMapPaths
+	return
 }
diff --git a/cmd/release_config/release_config_proto/build_flags_out.pb.go b/cmd/release_config/release_config_proto/build_flags_out.pb.go
index 0372d63..483cffa 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_out.pb.go
@@ -11,7 +11,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.33.0
+// 	protoc-gen-go v1.30.0
 // 	protoc        v3.21.12
 // source: build_flags_out.proto
 
@@ -153,6 +153,54 @@
 	return nil
 }
 
+type FlagArtifacts struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The artifacts
+	FlagArtifacts []*FlagArtifact `protobuf:"bytes,1,rep,name=flag_artifacts,json=flagArtifacts" json:"flag_artifacts,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) GetFlagArtifacts() []*FlagArtifact {
+	if x != nil {
+		return x.FlagArtifacts
+	}
+	return nil
+}
+
 type ReleaseConfigArtifact struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -179,7 +227,7 @@
 func (x *ReleaseConfigArtifact) Reset() {
 	*x = ReleaseConfigArtifact{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_build_flags_out_proto_msgTypes[2]
+		mi := &file_build_flags_out_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -192,7 +240,7 @@
 func (*ReleaseConfigArtifact) ProtoMessage() {}
 
 func (x *ReleaseConfigArtifact) ProtoReflect() protoreflect.Message {
-	mi := &file_build_flags_out_proto_msgTypes[2]
+	mi := &file_build_flags_out_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -205,7 +253,7 @@
 
 // Deprecated: Use ReleaseConfigArtifact.ProtoReflect.Descriptor instead.
 func (*ReleaseConfigArtifact) Descriptor() ([]byte, []int) {
-	return file_build_flags_out_proto_rawDescGZIP(), []int{2}
+	return file_build_flags_out_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *ReleaseConfigArtifact) GetName() string {
@@ -266,7 +314,7 @@
 func (x *ReleaseConfigsArtifact) Reset() {
 	*x = ReleaseConfigsArtifact{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_build_flags_out_proto_msgTypes[3]
+		mi := &file_build_flags_out_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -279,7 +327,7 @@
 func (*ReleaseConfigsArtifact) ProtoMessage() {}
 
 func (x *ReleaseConfigsArtifact) ProtoReflect() protoreflect.Message {
-	mi := &file_build_flags_out_proto_msgTypes[3]
+	mi := &file_build_flags_out_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -292,7 +340,7 @@
 
 // Deprecated: Use ReleaseConfigsArtifact.ProtoReflect.Descriptor instead.
 func (*ReleaseConfigsArtifact) Descriptor() ([]byte, []int) {
-	return file_build_flags_out_proto_rawDescGZIP(), []int{3}
+	return file_build_flags_out_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *ReleaseConfigsArtifact) GetReleaseConfig() *ReleaseConfigArtifact {
@@ -344,58 +392,64 @@
 	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, 0x8e, 0x02, 0x0a, 0x17, 0x72, 0x65,
-	0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x61, 0x72, 0x74,
-	0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x74, 0x68,
-	0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a,
-	0x6f, 0x74, 0x68, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x0e, 0x66, 0x6c,
-	0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c,
-	0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x52,
-	0x0d, 0x66, 0x6c, 0x61, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x12, 0x2c,
-	0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f,
-	0x73, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08,
-	0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08,
-	0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65,
-	0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x64,
-	0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 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,
+	0x74, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x22, 0x64, 0x0a, 0x0e, 0x66, 0x6c, 0x61,
+	0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x12, 0x52, 0x0a, 0x0e, 0x66,
+	0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65,
+	0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74,
+	0x52, 0x0d, 0x66, 0x6c, 0x61, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x22,
+	0x8e, 0x02, 0x0a, 0x17, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+	0x1f, 0x0a, 0x0b, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73,
+	0x12, 0x52, 0x0a, 0x0e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63,
+	0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x61, 0x72, 0x74,
+	0x69, 0x66, 0x61, 0x63, 0x74, 0x52, 0x0d, 0x66, 0x6c, 0x61, 0x67, 0x41, 0x72, 0x74, 0x69, 0x66,
+	0x61, 0x63, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65,
+	0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x05,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x20,
+	0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73,
+	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, 0x5f, 0x6d, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
-	0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
-	0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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 (
@@ -410,32 +464,34 @@
 	return file_build_flags_out_proto_rawDescData
 }
 
-var file_build_flags_out_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+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
-	(*ReleaseConfigArtifact)(nil),  // 2: android.release_config_proto.release_config_artifact
-	(*ReleaseConfigsArtifact)(nil), // 3: android.release_config_proto.release_configs_artifact
-	nil,                            // 4: android.release_config_proto.release_configs_artifact.ReleaseConfigMapsMapEntry
-	(*Value)(nil),                  // 5: android.release_config_proto.value
-	(*FlagDeclaration)(nil),        // 6: android.release_config_proto.flag_declaration
-	(*ReleaseConfigMap)(nil),       // 7: android.release_config_proto.release_config_map
+	(*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{
-	5, // 0: android.release_config_proto.tracepoint.value:type_name -> android.release_config_proto.value
-	6, // 1: android.release_config_proto.flag_artifact.flag_declaration:type_name -> android.release_config_proto.flag_declaration
-	5, // 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.release_config_artifact.flag_artifacts:type_name -> android.release_config_proto.flag_artifact
-	2, // 5: android.release_config_proto.release_configs_artifact.release_config:type_name -> android.release_config_proto.release_config_artifact
-	2, // 6: android.release_config_proto.release_configs_artifact.other_release_configs:type_name -> android.release_config_proto.release_config_artifact
-	4, // 7: android.release_config_proto.release_configs_artifact.release_config_maps_map:type_name -> android.release_config_proto.release_configs_artifact.ReleaseConfigMapsMapEntry
-	7, // 8: android.release_config_proto.release_configs_artifact.ReleaseConfigMapsMapEntry.value:type_name -> android.release_config_proto.release_config_map
-	9, // [9:9] is the sub-list for method output_type
-	9, // [9:9] is the sub-list for method input_type
-	9, // [9:9] is the sub-list for extension type_name
-	9, // [9:9] is the sub-list for extension extendee
-	0, // [0:9] is the sub-list for field type_name
+	6,  // 0: android.release_config_proto.tracepoint.value:type_name -> android.release_config_proto.value
+	7,  // 1: android.release_config_proto.flag_artifact.flag_declaration:type_name -> android.release_config_proto.flag_declaration
+	6,  // 2: android.release_config_proto.flag_artifact.value:type_name -> android.release_config_proto.value
+	0,  // 3: android.release_config_proto.flag_artifact.traces:type_name -> android.release_config_proto.tracepoint
+	1,  // 4: android.release_config_proto.flag_artifacts.flag_artifacts:type_name -> android.release_config_proto.flag_artifact
+	1,  // 5: android.release_config_proto.release_config_artifact.flag_artifacts:type_name -> android.release_config_proto.flag_artifact
+	3,  // 6: android.release_config_proto.release_configs_artifact.release_config:type_name -> android.release_config_proto.release_config_artifact
+	3,  // 7: android.release_config_proto.release_configs_artifact.other_release_configs:type_name -> android.release_config_proto.release_config_artifact
+	5,  // 8: android.release_config_proto.release_configs_artifact.release_config_maps_map:type_name -> android.release_config_proto.release_configs_artifact.ReleaseConfigMapsMapEntry
+	8,  // 9: android.release_config_proto.release_configs_artifact.ReleaseConfigMapsMapEntry.value:type_name -> android.release_config_proto.release_config_map
+	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() }
@@ -470,7 +526,7 @@
 			}
 		}
 		file_build_flags_out_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ReleaseConfigArtifact); i {
+			switch v := v.(*FlagArtifacts); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -482,6 +538,18 @@
 			}
 		}
 		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
@@ -500,7 +568,7 @@
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_build_flags_out_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   5,
+			NumMessages:   6,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/cmd/release_config/release_config_proto/build_flags_out.proto b/cmd/release_config/release_config_proto/build_flags_out.proto
index 05e770f..6f34d6f 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.proto
+++ b/cmd/release_config/release_config_proto/build_flags_out.proto
@@ -52,6 +52,11 @@
   repeated tracepoint traces = 8;
 }
 
+message flag_artifacts {
+  // The artifacts
+  repeated flag_artifact flag_artifacts = 1;
+}
+
 message release_config_artifact {
   // The name of the release config.
   // See # name for format detail
diff --git a/cmd/release_config/release_config_proto/build_flags_src.pb.go b/cmd/release_config/release_config_proto/build_flags_src.pb.go
index d0c924d..dded975 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_src.pb.go
@@ -11,7 +11,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.33.0
+// 	protoc-gen-go v1.30.0
 // 	protoc        v3.21.12
 // source: build_flags_src.proto
 
@@ -98,76 +98,6 @@
 	return file_build_flags_src_proto_rawDescGZIP(), []int{0}
 }
 
-type Container int32
-
-const (
-	Container_UNSPECIFIED_container Container = 0
-	// All containers
-	Container_ALL Container = 1
-	// Specific containers
-	Container_PRODUCT    Container = 2
-	Container_SYSTEM     Container = 3
-	Container_SYSTEM_EXT Container = 4
-	Container_VENDOR     Container = 5
-)
-
-// Enum value maps for Container.
-var (
-	Container_name = map[int32]string{
-		0: "UNSPECIFIED_container",
-		1: "ALL",
-		2: "PRODUCT",
-		3: "SYSTEM",
-		4: "SYSTEM_EXT",
-		5: "VENDOR",
-	}
-	Container_value = map[string]int32{
-		"UNSPECIFIED_container": 0,
-		"ALL":                   1,
-		"PRODUCT":               2,
-		"SYSTEM":                3,
-		"SYSTEM_EXT":            4,
-		"VENDOR":                5,
-	}
-)
-
-func (x Container) Enum() *Container {
-	p := new(Container)
-	*p = x
-	return p
-}
-
-func (x Container) String() string {
-	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
-}
-
-func (Container) Descriptor() protoreflect.EnumDescriptor {
-	return file_build_flags_src_proto_enumTypes[1].Descriptor()
-}
-
-func (Container) Type() protoreflect.EnumType {
-	return &file_build_flags_src_proto_enumTypes[1]
-}
-
-func (x Container) Number() protoreflect.EnumNumber {
-	return protoreflect.EnumNumber(x)
-}
-
-// Deprecated: Do not use.
-func (x *Container) UnmarshalJSON(b []byte) error {
-	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
-	if err != nil {
-		return err
-	}
-	*x = Container(num)
-	return nil
-}
-
-// Deprecated: Use Container.Descriptor instead.
-func (Container) EnumDescriptor() ([]byte, []int) {
-	return file_build_flags_src_proto_rawDescGZIP(), []int{1}
-}
-
 type Value struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -298,7 +228,7 @@
 	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.
-	Container *Container `protobuf:"varint,206,opt,name=container,enum=android.release_config_proto.Container" json:"container,omitempty"`
+	Containers []string `protobuf:"bytes,206,rep,name=containers" json:"containers,omitempty"`
 }
 
 func (x *FlagDeclaration) Reset() {
@@ -368,11 +298,11 @@
 	return Workflow_UNSPECIFIED_workflow
 }
 
-func (x *FlagDeclaration) GetContainer() Container {
-	if x != nil && x.Container != nil {
-		return *x.Container
+func (x *FlagDeclaration) GetContainers() []string {
+	if x != nil {
+		return x.Containers
 	}
-	return Container_UNSPECIFIED_container
+	return nil
 }
 
 type FlagValue struct {
@@ -385,6 +315,9 @@
 	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() {
@@ -433,6 +366,13 @@
 	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
@@ -447,6 +387,8 @@
 	// 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"`
 }
 
 func (x *ReleaseConfig) Reset() {
@@ -502,6 +444,13 @@
 	return nil
 }
 
+func (x *ReleaseConfig) GetAconfigFlagsOnly() bool {
+	if x != nil && x.AconfigFlagsOnly != nil {
+		return *x.AconfigFlagsOnly
+	}
+	return false
+}
+
 // Any aliases.  These are used for continuous integration builder config.
 type ReleaseAlias struct {
 	state         protoimpl.MessageState
@@ -571,7 +520,7 @@
 	// 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.
-	DefaultContainer *Container `protobuf:"varint,3,opt,name=default_container,json=defaultContainer,enum=android.release_config_proto.Container" json:"default_container,omitempty"`
+	DefaultContainers []string `protobuf:"bytes,3,rep,name=default_containers,json=defaultContainers" json:"default_containers,omitempty"`
 }
 
 func (x *ReleaseConfigMap) Reset() {
@@ -620,11 +569,11 @@
 	return ""
 }
 
-func (x *ReleaseConfigMap) GetDefaultContainer() Container {
-	if x != nil && x.DefaultContainer != nil {
-		return *x.DefaultContainer
+func (x *ReleaseConfigMap) GetDefaultContainers() []string {
+	if x != nil {
+		return x.DefaultContainers
 	}
-	return Container_UNSPECIFIED_container
+	return nil
 }
 
 var File_build_flags_src_proto protoreflect.FileDescriptor
@@ -643,7 +592,7 @@
 	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, 0xbd, 0x02,
+	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,
@@ -658,57 +607,50 @@
 	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, 0x46, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61,
-	0x69, 0x6e, 0x65, 0x72, 0x18, 0xce, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 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, 0x63, 0x6f, 0x6e, 0x74, 0x61,
-	0x69, 0x6e, 0x65, 0x72, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4a,
-	0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x06, 0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01, 0x22, 0x5c, 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, 0x22, 0x6e, 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, 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, 0xd3, 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, 0x54, 0x0a, 0x11, 0x64, 0x65, 0x66, 0x61,
-	0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x0e, 0x32, 0x27, 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, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x10, 0x64, 0x65,
-	0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2a, 0x4a,
-	0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x4e,
-	0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
-	0x6f, 0x77, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10, 0x01,
-	0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x0a,
-	0x0a, 0x06, 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x2a, 0x64, 0x0a, 0x09, 0x63, 0x6f,
-	0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x53, 0x50, 0x45,
-	0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
-	0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50,
-	0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x59, 0x53, 0x54,
-	0x45, 0x4d, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x45,
-	0x58, 0x54, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x56, 0x45, 0x4e, 0x44, 0x4f, 0x52, 0x10, 0x05,
-	0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e,
-	0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-	0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
-	0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1f, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61,
+	0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0xce, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f,
+	0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x06,
+	0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01, 0x22, 0x79, 0x0a, 0x0a, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
+	0x65, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65, 0x64,
+	0x18, 0xca, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x65,
+	0x64, 0x22, 0x9c, 0x01, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x68, 0x65,
+	0x72, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68, 0x65,
+	0x72, 0x69, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65,
+	0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x6c,
+	0x61, 0x67, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
+	0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x4f, 0x6e, 0x6c, 0x79,
+	0x22, 0x3b, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61,
+	0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xac, 0x01,
+	0x0a, 0x12, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x5f, 0x6d, 0x61, 0x70, 0x12, 0x45, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69,
+	0x61, 0x73, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a,
+	0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
+	0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75,
+	0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x2a, 0x4a, 0x0a, 0x08,
+	0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x4e, 0x53, 0x50,
+	0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
+	0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x0c,
+	0x0a, 0x08, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06,
+	0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
+	0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
@@ -723,30 +665,27 @@
 	return file_build_flags_src_proto_rawDescData
 }
 
-var file_build_flags_src_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_build_flags_src_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_build_flags_src_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
 var file_build_flags_src_proto_goTypes = []interface{}{
 	(Workflow)(0),            // 0: android.release_config_proto.workflow
-	(Container)(0),           // 1: android.release_config_proto.container
-	(*Value)(nil),            // 2: android.release_config_proto.value
-	(*FlagDeclaration)(nil),  // 3: android.release_config_proto.flag_declaration
-	(*FlagValue)(nil),        // 4: android.release_config_proto.flag_value
-	(*ReleaseConfig)(nil),    // 5: android.release_config_proto.release_config
-	(*ReleaseAlias)(nil),     // 6: android.release_config_proto.release_alias
-	(*ReleaseConfigMap)(nil), // 7: android.release_config_proto.release_config_map
+	(*Value)(nil),            // 1: android.release_config_proto.value
+	(*FlagDeclaration)(nil),  // 2: android.release_config_proto.flag_declaration
+	(*FlagValue)(nil),        // 3: android.release_config_proto.flag_value
+	(*ReleaseConfig)(nil),    // 4: android.release_config_proto.release_config
+	(*ReleaseAlias)(nil),     // 5: android.release_config_proto.release_alias
+	(*ReleaseConfigMap)(nil), // 6: android.release_config_proto.release_config_map
 }
 var file_build_flags_src_proto_depIdxs = []int32{
-	2, // 0: android.release_config_proto.flag_declaration.value:type_name -> android.release_config_proto.value
+	1, // 0: android.release_config_proto.flag_declaration.value:type_name -> android.release_config_proto.value
 	0, // 1: android.release_config_proto.flag_declaration.workflow:type_name -> android.release_config_proto.workflow
-	1, // 2: android.release_config_proto.flag_declaration.container:type_name -> android.release_config_proto.container
-	2, // 3: android.release_config_proto.flag_value.value:type_name -> android.release_config_proto.value
-	6, // 4: android.release_config_proto.release_config_map.aliases:type_name -> android.release_config_proto.release_alias
-	1, // 5: android.release_config_proto.release_config_map.default_container:type_name -> android.release_config_proto.container
-	6, // [6:6] is the sub-list for method output_type
-	6, // [6:6] is the sub-list for method input_type
-	6, // [6:6] is the sub-list for extension type_name
-	6, // [6:6] is the sub-list for extension extendee
-	0, // [0:6] is the sub-list for field type_name
+	1, // 2: android.release_config_proto.flag_value.value:type_name -> android.release_config_proto.value
+	5, // 3: android.release_config_proto.release_config_map.aliases:type_name -> android.release_config_proto.release_alias
+	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() }
@@ -839,7 +778,7 @@
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_build_flags_src_proto_rawDesc,
-			NumEnums:      2,
+			NumEnums:      1,
 			NumMessages:   6,
 			NumExtensions: 0,
 			NumServices:   0,
diff --git a/cmd/release_config/release_config_proto/build_flags_src.proto b/cmd/release_config/release_config_proto/build_flags_src.proto
index c077f5c..0ef1a5f 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.proto
+++ b/cmd/release_config/release_config_proto/build_flags_src.proto
@@ -53,19 +53,6 @@
   MANUAL = 3;
 }
 
-enum container {
-  UNSPECIFIED_container = 0;
-
-  // All containers
-  ALL = 1;
-
-  // Specific containers
-  PRODUCT = 2;
-  SYSTEM = 3;
-  SYSTEM_EXT = 4;
-  VENDOR = 5;
-}
-
 message value {
   oneof val {
     bool unspecified_value = 200;
@@ -100,7 +87,7 @@
 
   // The container for this flag.  This overrides any default container given
   // in the release_config_map message.
-  optional container container = 206;
+  repeated string containers = 206;
 
   // The package associated with this flag.
   // (when Gantry is ready for it) optional string package = 207;
@@ -114,6 +101,10 @@
 
   // 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).
@@ -128,6 +119,9 @@
   // 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;
 }
 
 // Any aliases.  These are used for continuous integration builder config.
@@ -148,7 +142,7 @@
   optional string description = 2;
 
   // The default container for flags declared here.
-  optional container default_container = 3;
+  repeated string default_containers = 3;
 
   // If needed, we can add these fields instead of hardcoding the location.
   // Flag declarations: `flag_declarations/*.textproto`
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index d1c1d85..025ba27 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -55,6 +55,9 @@
 	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_firmware", PrebuiltFirmwareFactory)
 	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
@@ -609,6 +612,39 @@
 	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{}
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index dd9958c..3ee2340 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -287,6 +287,48 @@
 	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 {
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index cadf9c24..b342ae9 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -127,6 +127,10 @@
 	// 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
+
 	Fsverity fsverityProperties
 }
 
@@ -137,6 +141,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
 }
@@ -189,6 +194,12 @@
 	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) {
@@ -288,6 +299,7 @@
 	f.buildNonDepsFiles(ctx, builder, rootDir)
 	f.addMakeBuiltFiles(ctx, builder, rootDir)
 	f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
+	f.buildEventLogtagsFile(ctx, builder, rebasedDir)
 
 	// run host_init_verifier
 	// Ideally we should have a concept of pluggable linters that verify the generated image.
@@ -428,6 +440,7 @@
 
 	f.buildNonDepsFiles(ctx, builder, rootDir)
 	f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
+	f.buildEventLogtagsFile(ctx, builder, rebasedDir)
 
 	output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
 	cmd := builder.Command().
@@ -485,6 +498,37 @@
 		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)
+	}
+}
+
 type partition interface {
 	PartitionType() string
 }
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 3a5071d..acd4813 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -299,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,
@@ -479,3 +442,26 @@
 		}
 	`)
 }
+
+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",
+		}
+	`)
+}
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 5028a49..15cacfb 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -98,5 +98,5 @@
 // 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"
 }
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 43f4fe5..67b96ca 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -299,7 +299,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 {
@@ -714,13 +714,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
 				}
@@ -786,6 +786,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..1df887b 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
diff --git a/java/aar.go b/java/aar.go
index f8955ce..47c64bf 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -418,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) {
@@ -968,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 {
@@ -990,7 +996,7 @@
 	exportPackage                      android.WritablePath
 	transitiveAaptResourcePackagesFile android.Path
 	extraAaptPackagesFile              android.WritablePath
-	manifest                           android.WritablePath
+	manifest                           android.Path
 	assetsPackage                      android.WritablePath
 	rTxt                               android.WritablePath
 	rJar                               android.WritablePath
@@ -1166,7 +1172,15 @@
 	jarName := ctx.ModuleName() + ".jar"
 	extractedAARDir := android.PathForModuleOut(ctx, "aar")
 	classpathFile := extractedAARDir.Join(ctx, jarName)
-	a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
+
+	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")
@@ -1187,7 +1201,7 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        unzipAAR,
 		Input:       a.aarPath,
-		Outputs:     android.WritablePaths{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(),
diff --git a/java/aar_test.go b/java/aar_test.go
index d6dbe3c..18efd20 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",
 			],
diff --git a/java/androidmk.go b/java/androidmk.go
index a52d439..9cd0baf 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -92,11 +92,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 {
@@ -457,6 +453,8 @@
 				if app.Name() != "framework-res" {
 					android.SetAconfigFileMkEntries(&app.ModuleBase, entries, app.mergedAconfigFiles)
 				}
+
+				entries.AddStrings("LOCAL_SOONG_LOGTAGS_FILES", app.logtagsSrcs.Strings()...)
 			},
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
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 eab40e7..a7c48a1 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -4382,6 +4382,7 @@
 		aconfig_declarations {
 			name: "bar",
 			package: "com.example.package.bar",
+			container: "com.android.foo",
 			srcs: [
 				"bar.aconfig",
 			],
@@ -4389,6 +4390,7 @@
 		aconfig_declarations {
 			name: "baz",
 			package: "com.example.package.baz",
+			container: "com.android.foo",
 			srcs: [
 				"baz.aconfig",
 			],
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 cc3da76..82a34ca 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -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,7 +519,7 @@
 // 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 ""
 	}
 
@@ -590,7 +590,7 @@
 		// 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 {
+		if isActiveModule(ctx, ctx.Module()) && len(unknown) > 0 {
 			ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
 		}
 	}
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index 0ebab4d..07bc5c1 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -151,10 +151,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 +185,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/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f7e3cb9..7229ca0 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)
@@ -1125,7 +1125,7 @@
 	image.unstrippedInstalls = unstrippedInstalls
 
 	// 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/droidstubs.go b/java/droidstubs.go
index 02b81a4..730be14 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -532,8 +532,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 +604,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 +619,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 +702,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)
 		}
 	}
 
@@ -692,11 +740,11 @@
 		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))
+	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.FlagWithInput("--check-compatibility:api:released ", apiFile)
-	cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
+	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() {
@@ -708,8 +756,8 @@
 	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())
 
@@ -739,14 +787,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, ":")
 	}
@@ -827,8 +875,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)
@@ -950,12 +997,12 @@
 
 	// Add API lint options.
 	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"
 
@@ -1323,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,
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 fb31ce7..d37c558 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -179,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/hiddenapi_modular.go b/java/hiddenapi_modular.go
index ae587ea..cab5402 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -1428,7 +1428,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
 		}
 
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 54d2aa6..0df96a3 100644
--- a/java/java.go
+++ b/java/java.go
@@ -75,7 +75,6 @@
 		ctx.BottomUp("jacoco_deps", jacocoDepsMutator).Parallel()
 	})
 
-	ctx.RegisterParallelSingletonType("logtags", LogtagsSingleton)
 	ctx.RegisterParallelSingletonType("kythe_java_extract", kytheExtractJavaFactory)
 }
 
@@ -587,6 +586,7 @@
 	JAVA_VERSION_9           = 9
 	JAVA_VERSION_11          = 11
 	JAVA_VERSION_17          = 17
+	JAVA_VERSION_21          = 21
 )
 
 func (v javaVersion) String() string {
@@ -605,6 +605,8 @@
 		return "11"
 	case JAVA_VERSION_17:
 		return "17"
+	case JAVA_VERSION_21:
+		return "21"
 	default:
 		return "unsupported"
 	}
@@ -647,6 +649,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
diff --git a/java/java_test.go b/java/java_test.go
index a1192bb..2f27932 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -2801,6 +2801,7 @@
 	aconfig_declarations {
 		name: "bar",
 		package: "com.example.package",
+		container: "com.android.foo",
 		srcs: [
 			"bar.aconfig",
 		],
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..82fac91 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -612,7 +612,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 +620,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/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 4db426e..b3c9ce5 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -221,6 +221,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 {
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 2fc6c02..99fa092 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -19,6 +19,7 @@
 	"path/filepath"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -233,7 +234,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 {
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 6a79e58..00613ee 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -367,10 +367,23 @@
 		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]
-		srcs = append(srcs, PrebuiltApiModuleName(info.module, currentApiScope.name, "latest"))
+		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)
 	}
 }
diff --git a/java/robolectric.go b/java/robolectric.go
index 9e8850c..18386c9 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -46,6 +46,7 @@
 var (
 	roboCoverageLibsTag = dependencyTag{name: "roboCoverageLibs"}
 	roboRuntimesTag     = dependencyTag{name: "roboRuntimes"}
+	roboRuntimeOnlyTag  = dependencyTag{name: "roboRuntimeOnlyTag"}
 )
 
 type robolectricProperties struct {
@@ -70,6 +71,9 @@
 	// 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
 }
 
 type robolectricTest struct {
@@ -112,7 +116,7 @@
 
 	if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
 		ctx.AddVariationDependencies(nil, libTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v))
-	} else {
+	} else if !proptools.Bool(r.robolectricProperties.Strict_mode) {
 		if proptools.Bool(r.robolectricProperties.Upstream) {
 			ctx.AddVariationDependencies(nil, libTag, robolectricCurrentLib+"_upstream")
 		} else {
@@ -120,6 +124,10 @@
 		}
 	}
 
+	if proptools.Bool(r.robolectricProperties.Strict_mode) {
+		ctx.AddVariationDependencies(nil, roboRuntimeOnlyTag, robolectricCurrentLib+"_upstream")
+	}
+
 	ctx.AddVariationDependencies(nil, libTag, robolectricDefaultLibs...)
 
 	ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...)
@@ -192,19 +200,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())
diff --git a/java/rro_test.go b/java/rro_test.go
index d697ec6..742c839 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -421,6 +421,7 @@
 		aconfig_declarations {
 			name: "bar",
 			package: "com.example.package.bar",
+			container: "com.android.foo",
 			srcs: [
 				"bar.aconfig",
 			],
@@ -428,6 +429,7 @@
 		aconfig_declarations {
 			name: "baz",
 			package: "com.example.package.baz",
+			container: "com.android.foo",
 			srcs: [
 				"baz.aconfig",
 			],
diff --git a/java/sdk_library.go b/java/sdk_library.go
index bc3e7e9..677b32a 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -705,10 +705,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 {
@@ -829,28 +829,25 @@
 	})
 }
 
-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
 }
 
@@ -1675,11 +1672,15 @@
 		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})
@@ -2352,7 +2353,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
 	}
 
@@ -3370,6 +3371,7 @@
 	android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent)
 
 	module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
+	ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
 }
 
 func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 1c94a8a..d240e70 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -1085,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
@@ -1727,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 b291e70..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 {
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/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/python/binary.go b/python/binary.go
index d6750c6..c84eeee 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -203,7 +203,7 @@
 }
 
 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 621e429..e14fdf3 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
 }
 
diff --git a/python/scripts/precompile_python.py b/python/scripts/precompile_python.py
index 07b8fe9..b3cf950 100644
--- a/python/scripts/precompile_python.py
+++ b/python/scripts/precompile_python.py
@@ -30,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())
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/bindgen.go b/rust/bindgen.go
index 11ba74d..4277753 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -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...)
 
@@ -232,6 +256,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 +309,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 +325,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,6 +384,14 @@
 		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.HeaderLibs, b.ClangProperties.Header_libs...)
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index 0c0a6da..2b7362f 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -227,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/config/global.go b/rust/config/global.go
index ba08560..e83e23a 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var (
 	pctx = android.NewPackageContext("android/soong/rust/config")
 
-	RustDefaultVersion = "1.77.1"
+	RustDefaultVersion = "1.77.1.p1"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
@@ -53,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{
diff --git a/rust/coverage.go b/rust/coverage.go
index bc6504d..91a7806 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -39,9 +39,11 @@
 
 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() {
@@ -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/library.go b/rust/library.go
index 6be4917..f58a54f 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -713,7 +713,7 @@
 	if sourceVariant {
 		sv := modules[0]
 		for _, v := range modules[1:] {
-			if !v.Enabled() {
+			if !v.Enabled(mctx) {
 				continue
 			}
 			mctx.AddInterVariantDependency(sourceDepTag, v, sv)
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 c2b6151..de049f7 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -1697,7 +1697,7 @@
 }
 
 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)
 	}
 }
diff --git a/rust/sanitize.go b/rust/sanitize.go
index bfd3971..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
 		}
 
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/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/java_sdk_test.go b/sdk/java_sdk_test.go
index 275860f..0a5483b 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -688,6 +688,12 @@
 			public: {
 				enabled: true,
 			},
+			system: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
 		}
 
 		java_system_modules {
@@ -752,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 {
@@ -771,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, `
 [
@@ -805,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"
       }
     }
   },
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
index 63cd4e1..b416ebd 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)
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index 6867537..b2d5631 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -160,35 +160,17 @@
 
 }
 
-// Any test suites in base should not be repeated in the derived class, except "general-tests".
-// We may restrict derived tests to only be "general-tests" as it doesn't make sense to add a slice
-// of a test to compatibility suite.
+// 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 ErrorMessage, false on problems
-// Returns _, true if okay.
+// 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\"")
+		ctx.ModuleErrorf("At least one test-suite must be set or this won't run. Use \"general-tests\" or \"device-tests\"")
 		return false
 	}
 
-	derivedSuites := make(map[string]bool)
-	// See if any suites in base is also in derived (other than general-tests)
-	for _, s := range m.tradefedProperties.Test_suites {
-		if s != "general-tests" {
-			derivedSuites[s] = true
-		}
-	}
-	if len(derivedSuites) == 0 {
-		return true
-	}
-	for _, baseSuite := range m.provider.TestSuites {
-		if derivedSuites[baseSuite] {
-			ctx.ModuleErrorf("TestSuite %s exists in the base, do not add it here", baseSuite)
-			return false
-		}
-	}
-
 	return true
 }
 
@@ -297,10 +279,16 @@
 //  1. manifest file to testcases dir
 //  2. 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 provider is accurate
+	// Ensure the base provider is accurate
 	if m.provider.TestConfig == nil {
 		return
 	}
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index 41dd3d4..b2049b1 100644
--- a/tradefed_modules/test_module_config_test.go
+++ b/tradefed_modules/test_module_config_test.go
@@ -19,6 +19,8 @@
 	"strconv"
 	"strings"
 	"testing"
+
+	"github.com/google/blueprint"
 )
 
 const bp = `
@@ -323,28 +325,65 @@
 		RunTestWithBp(t, badBp)
 }
 
-func TestModuleConfigHostDuplicateTestSuitesGiveErrors(t *testing.T) {
-	badBp := `
-		java_test_host {
-			name: "base",
-                        srcs: ["a.java"],
-                        test_suites: ["general-tests", "some-compat"],
-		}
-
+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: "derived_test",
+                        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", "some-compat"],
-                }`
+                        test_suites: ["general-tests"],
+                }
 
-	android.GroupFixturePreparers(
-		java.PrepareForTestWithJavaDefaultModules,
-		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
-	).ExtendWithErrorHandler(
-		android.FixtureExpectsAtLeastOneErrorMatchingPattern("TestSuite some-compat exists in the base")).
-		RunTestWithBp(t, badBp)
+		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)
+	}
 }
 
 // Use for situations where the entries map contains pairs:  [srcPath:installedPath1, srcPath2:installedPath2]
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.