Merge "Deprecate api bp2build  of ndk_library and ndk_headers"
diff --git a/OWNERS b/OWNERS
index 0234f27..9221d3e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -10,10 +10,12 @@
 delmerico@google.com
 dwillemsen@google.com
 eakammer@google.com
+jihoonkang@google.com
 jobredeaux@google.com
 joeo@google.com
 juu@google.com
 lamontjones@google.com
+mrziwang@google.com
 spandandas@google.com
 tradical@google.com
 usta@google.com
diff --git a/README.md b/README.md
index 70311cb..2d8f0af 100644
--- a/README.md
+++ b/README.md
@@ -565,6 +565,12 @@
 by all of the vendor's other modules using the normal namespace and visibility
 rules.
 
+`soongConfigTraceMutator` enables modules affected by soong config variables to
+write outputs into a hashed directory path. It does this by recording accesses
+to soong config variables on each module, and then accumulating records of each
+module's all dependencies. `m soong_config_trace` builds information about
+hashes to `$OUT_DIR/soong/soong_config_trace.json`.
+
 ## Build logic
 
 The build logic is written in Go using the
diff --git a/aidl_library/aidl_library.go b/aidl_library/aidl_library.go
index 9b5f0a8..5985103 100644
--- a/aidl_library/aidl_library.go
+++ b/aidl_library/aidl_library.go
@@ -22,6 +22,10 @@
 	"github.com/google/blueprint/proptools"
 )
 
+var PrepareForTestWithAidlLibrary = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+	registerAidlLibraryBuildComponents(ctx)
+})
+
 func init() {
 	registerAidlLibraryBuildComponents(android.InitRegistrationContext)
 }
@@ -103,8 +107,10 @@
 type AidlLibraryInfo struct {
 	// The direct aidl files of the module
 	Srcs android.Paths
-	// The include dirs to the direct aidl files and those provided from aidl_library deps
+	// The include dirs to the direct aidl files and those provided from transitive aidl_library deps
 	IncludeDirs android.DepSet
+	// The direct hdrs and hdrs from transitive deps
+	Hdrs android.DepSet
 }
 
 // AidlLibraryProvider provides the srcs and the transitive include dirs
@@ -112,37 +118,48 @@
 
 func (lib *AidlLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	includeDirsDepSetBuilder := android.NewDepSetBuilder(android.PREORDER)
+	hdrsDepSetBuilder := android.NewDepSetBuilder(android.PREORDER)
 
 	if len(lib.properties.Srcs) == 0 && len(lib.properties.Hdrs) == 0 {
 		ctx.ModuleErrorf("at least srcs or hdrs prop must be non-empty")
 	}
 
 	srcs := android.PathsForModuleSrc(ctx, lib.properties.Srcs)
+	hdrs := android.PathsForModuleSrc(ctx, lib.properties.Hdrs)
+
 	if lib.properties.Strip_import_prefix != nil {
 		srcs = android.PathsWithModuleSrcSubDir(
 			ctx,
 			srcs,
-			android.String(lib.properties.Strip_import_prefix))
+			android.String(lib.properties.Strip_import_prefix),
+		)
+
+		hdrs = android.PathsWithModuleSrcSubDir(
+			ctx,
+			hdrs,
+			android.String(lib.properties.Strip_import_prefix),
+		)
 	}
+	hdrsDepSetBuilder.Direct(hdrs...)
 
 	includeDir := android.PathForModuleSrc(
 		ctx,
 		proptools.StringDefault(lib.properties.Strip_import_prefix, ""),
 	)
-
 	includeDirsDepSetBuilder.Direct(includeDir)
 
 	for _, dep := range ctx.GetDirectDepsWithTag(aidlLibraryTag) {
 		if ctx.OtherModuleHasProvider(dep, AidlLibraryProvider) {
 			info := ctx.OtherModuleProvider(dep, AidlLibraryProvider).(AidlLibraryInfo)
 			includeDirsDepSetBuilder.Transitive(&info.IncludeDirs)
+			hdrsDepSetBuilder.Transitive(&info.Hdrs)
 		}
 	}
 
-	// TODO(b/279960133) Propagate direct and transitive headers/srcs when aidl action sandboxes inputs
 	ctx.SetProvider(AidlLibraryProvider, AidlLibraryInfo{
 		Srcs:        srcs,
 		IncludeDirs: *includeDirsDepSetBuilder.Build(),
+		Hdrs:        *hdrsDepSetBuilder.Build(),
 	})
 }
 
diff --git a/aidl_library/aidl_library_test.go b/aidl_library/aidl_library_test.go
index 42fa536..d9dd245 100644
--- a/aidl_library/aidl_library_test.go
+++ b/aidl_library/aidl_library_test.go
@@ -19,10 +19,6 @@
 	"testing"
 )
 
-var PrepareForTestWithAidlLibrary = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-	registerAidlLibraryBuildComponents(ctx)
-})
-
 func TestAidlLibrary(t *testing.T) {
 	t.Parallel()
 	ctx := android.GroupFixturePreparers(
@@ -41,7 +37,7 @@
 			aidl_library {
 					name: "foo",
 					srcs: ["a/b/Foo.aidl"],
-					hdrs: ["Header.aidl"],
+					hdrs: ["a/Header.aidl"],
 					strip_import_prefix: "a",
 					deps: ["bar"],
 				}
@@ -65,6 +61,13 @@
 		[]string{"package_foo/a/b/Foo.aidl"},
 		actualInfo.Srcs,
 	)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl hdrs paths",
+		[]string{"package_foo/a/Header.aidl"},
+		actualInfo.Hdrs.ToList(),
+	)
 }
 
 func TestAidlLibraryWithoutStripImportPrefix(t *testing.T) {
@@ -76,6 +79,7 @@
 			aidl_library {
 					name: "bar",
 					srcs: ["x/y/Bar.aidl"],
+					hdrs: ["BarHeader.aidl"],
 				}
 			`),
 		}.AddToFixture(),
@@ -84,7 +88,6 @@
 			aidl_library {
 					name: "foo",
 					srcs: ["a/b/Foo.aidl"],
-					hdrs: ["Header.aidl"],
 					deps: ["bar"],
 				}
 			`),
@@ -107,6 +110,13 @@
 		[]string{"package_foo/a/b/Foo.aidl"},
 		actualInfo.Srcs,
 	)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl hdrs paths",
+		[]string{"package_bar/BarHeader.aidl"},
+		actualInfo.Hdrs.ToList(),
+	)
 }
 
 func TestAidlLibraryWithNoSrcsHdrsDeps(t *testing.T) {
diff --git a/android/Android.bp b/android/Android.bp
index 118087d..94d2c04 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -77,6 +77,7 @@
         "path_properties.go",
         "paths.go",
         "phony.go",
+        "plugin.go",
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "proto.go",
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 5b7f117..0dd7dae 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -85,6 +85,7 @@
 		"development/apps/DevelopmentSettings":        Bp2BuildDefaultTrue,
 		"development/apps/Fallback":                   Bp2BuildDefaultTrue,
 		"development/apps/WidgetPreview":              Bp2BuildDefaultTrue,
+		"development/python-packages/adb":             Bp2BuildDefaultTrueRecursively,
 		"development/samples/BasicGLSurfaceView":      Bp2BuildDefaultTrue,
 		"development/samples/BluetoothChat":           Bp2BuildDefaultTrue,
 		"development/samples/BrokenKeyDerivation":     Bp2BuildDefaultTrue,
@@ -211,6 +212,7 @@
 		"frameworks/av/media/module/minijail":                Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/libs/androidfw":                     Bp2BuildDefaultTrue,
+		"frameworks/base/libs/services":                      Bp2BuildDefaultTrue,
 		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
 		"frameworks/base/proto":                              Bp2BuildDefaultTrue,
 		"frameworks/base/services/tests/servicestests/aidl":  Bp2BuildDefaultTrue,
@@ -221,6 +223,7 @@
 		"frameworks/base/tools/streaming_proto":              Bp2BuildDefaultTrueRecursively,
 		"frameworks/hardware/interfaces/stats/aidl":          Bp2BuildDefaultTrue,
 		"frameworks/libs/modules-utils/build":                Bp2BuildDefaultTrueRecursively,
+		"frameworks/libs/net/common/native":                  Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/arect":                       Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/gui":                         Bp2BuildDefaultTrue,
@@ -570,6 +573,9 @@
 		//external/fec
 		"libfec_rs",
 
+		//frameworks/base/core/java
+		"IDropBoxManagerService_aidl",
+
 		//system/core/libsparse
 		"libsparse",
 
@@ -751,6 +757,18 @@
 		"CaptivePortalLogin",
 
 		"libstagefright_headers",
+
+		// aidl
+		"aidl",
+		"libaidl-common",
+
+		// java_resources containing only a single filegroup
+		"libauto_value_plugin",
+		"auto_value_plugin_resources",
+		"auto_value_extension",
+
+		// Used by xsd_config
+		"xsdc",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -825,7 +843,6 @@
 		"libprotobuf-internal-python-srcs", // TODO(b/210751803), we don't handle path property for filegroups
 		"libprotobuf-java-full",            // TODO(b/210751803), we don't handle path property for filegroups
 		"libprotobuf-java-util-full",       // TODO(b/210751803), we don't handle path property for filegroups
-		"auto_value_plugin_resources",      // TODO(b/210751803), we don't handle path property for filegroups
 
 		// go deps:
 		"analyze_bcpf",              // depends on bpmodify a blueprint_go_binary.
@@ -1556,10 +1573,10 @@
 	// also be built - do not add them to this list.
 	StagingMixedBuildsEnabledList = []string{
 		// M13: media.swcodec launch
-		// TODO(b/282042844): reenable
-		// "com.android.media.swcodec",
-		// "test_com.android.media.swcodec",
-		// "libstagefright_foundation",
+		"com.android.media.swcodec",
+		"test_com.android.media.swcodec",
+		"libstagefright_foundation",
+		"libcodec2_hidl@1.0",
 	}
 
 	// These should be the libs that are included by the apexes in the ProdMixedBuildsEnabledList
diff --git a/android/androidmk.go b/android/androidmk.go
index aa411d1..62f82f2 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -42,7 +42,7 @@
 }
 
 func RegisterAndroidMkBuildComponents(ctx RegistrationContext) {
-	ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
+	ctx.RegisterParallelSingletonType("androidmk", AndroidMkSingleton)
 }
 
 // Enable androidmk support.
diff --git a/android/api_levels.go b/android/api_levels.go
index fa919fd..2391e6c 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -22,7 +22,7 @@
 )
 
 func init() {
-	RegisterSingletonType("api_levels", ApiLevelsSingleton)
+	RegisterParallelSingletonType("api_levels", ApiLevelsSingleton)
 }
 
 const previewAPILevelBase = 9000
diff --git a/android/bazel.go b/android/bazel.go
index 114b1f5..d326634 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -46,6 +46,10 @@
 	// that is not a platform incompatibility. Example: the module-type is not
 	// enabled, or is not bp2build-converted.
 	ModuleIncompatibility
+
+	// Missing dependencies. We can't query Bazel for modules if it has missing dependencies, there
+	// will be failures.
+	ModuleMissingDeps
 )
 
 // FileGroupAsLibrary describes a filegroup module that is converted to some library
@@ -367,16 +371,26 @@
 // As a side effect, calling this method will also log whether this module is
 // mixed build enabled for metrics reporting.
 func MixedBuildsEnabled(ctx BaseModuleContext) MixedBuildEnabledStatus {
-	module := ctx.Module()
-	apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
-	withinApex := !apexInfo.IsForPlatform()
-
 	platformIncompatible := isPlatformIncompatible(ctx.Os(), ctx.Arch().ArchType)
 	if platformIncompatible {
 		ctx.Config().LogMixedBuild(ctx, false)
 		return TechnicalIncompatibility
 	}
 
+	if ctx.Config().AllowMissingDependencies() {
+		missingDeps := ctx.getMissingDependencies()
+		// If there are missing dependencies, querying Bazel will fail. Soong instead fails at execution
+		// time, not loading/analysis. disable mixed builds and fall back to Soong to maintain that
+		// behavior.
+		if len(missingDeps) > 0 {
+			ctx.Config().LogMixedBuild(ctx, false)
+			return ModuleMissingDeps
+		}
+	}
+
+	module := ctx.Module()
+	apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
+	withinApex := !apexInfo.IsForPlatform()
 	mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
 		module.Enabled() &&
 		convertedToBazel(ctx, module) &&
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 5291eca..d71eca2 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -29,6 +29,7 @@
 	"android/soong/bazel/cquery"
 	"android/soong/shared"
 	"android/soong/starlark_fmt"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/metrics"
 
@@ -73,14 +74,12 @@
 	}
 )
 
-func init() {
-	RegisterMixedBuildsMutator(InitRegistrationContext)
+func registerMixedBuildsMutator(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
 }
 
 func RegisterMixedBuildsMutator(ctx RegistrationContext) {
-	ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
-	})
+	ctx.FinalDepsMutators(registerMixedBuildsMutator)
 }
 
 func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {
@@ -182,10 +181,6 @@
 	// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
 	GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error)
 
-	// Returns the executable binary resultant from building together the python sources
-	// TODO(b/232976601): Remove.
-	GetPythonBinary(label string, cfgKey configKey) (string, error)
-
 	// Returns the results of the GetApexInfo query (including output files)
 	GetApexInfo(label string, cfgkey configKey) (cquery.ApexInfo, error)
 
@@ -314,14 +309,6 @@
 	return result, nil
 }
 
-func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, error) {
-	result, ok := m.LabelToPythonBinary[label]
-	if !ok {
-		return "", fmt.Errorf("no target with label %q in LabelToPythonBinary", label)
-	}
-	return result, nil
-}
-
 func (m MockBazelContext) GetApexInfo(label string, _ configKey) (cquery.ApexInfo, error) {
 	result, ok := m.LabelToApexInfo[label]
 	if !ok {
@@ -430,15 +417,6 @@
 	return cquery.CcInfo{}, fmt.Errorf("no bazel response found for %v", key)
 }
 
-func (bazelCtx *mixedBuildBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
-	key := makeCqueryKey(label, cquery.GetPythonBinary, cfgKey)
-	if rawString, ok := bazelCtx.results[key]; ok {
-		bazelOutput := strings.TrimSpace(rawString)
-		return cquery.GetPythonBinary.ParseResult(bazelOutput), nil
-	}
-	return "", fmt.Errorf("no bazel response found for %v", key)
-}
-
 func (bazelCtx *mixedBuildBazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexInfo, error) {
 	key := makeCqueryKey(label, cquery.GetApexInfo, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
@@ -467,10 +445,6 @@
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetPythonBinary(_ string, _ configKey) (string, error) {
-	panic("unimplemented")
-}
-
 func (n noopBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexInfo, error) {
 	panic("unimplemented")
 }
@@ -504,7 +478,7 @@
 	return []bazel.AqueryDepset{}
 }
 
-func addToStringSet(set map[string]bool, items []string) {
+func AddToStringSet(set map[string]bool, items []string) {
 	for _, item := range items {
 		set[item] = true
 	}
@@ -516,19 +490,19 @@
 
 	switch buildMode {
 	case BazelProdMode:
-		addToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList)
+		AddToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList)
 		for enabledAdHocModule := range forceEnabled {
 			enabledModules[enabledAdHocModule] = true
 		}
 	case BazelStagingMode:
 		// Staging mode includes all prod modules plus all staging modules.
-		addToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList)
-		addToStringSet(enabledModules, allowlists.StagingMixedBuildsEnabledList)
+		AddToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList)
+		AddToStringSet(enabledModules, allowlists.StagingMixedBuildsEnabledList)
 		for enabledAdHocModule := range forceEnabled {
 			enabledModules[enabledAdHocModule] = true
 		}
 	case BazelDevMode:
-		addToStringSet(disabledModules, allowlists.MixedBuildsDisabledList)
+		AddToStringSet(disabledModules, allowlists.MixedBuildsDisabledList)
 	default:
 		panic("Expected BazelProdMode, BazelStagingMode, or BazelDevMode")
 	}
@@ -608,7 +582,7 @@
 			allowlists.StagingDclaMixedBuildsEnabledList...)
 	}
 	dclaEnabledModules := map[string]bool{}
-	addToStringSet(dclaEnabledModules, dclaMixedBuildsEnabledList)
+	AddToStringSet(dclaEnabledModules, dclaMixedBuildsEnabledList)
 	return &mixedBuildBazelContext{
 		bazelRunner:             &builtinBazelRunner{c.UseBazelProxy, absolutePath(c.outDir)},
 		paths:                   &paths,
@@ -700,8 +674,17 @@
 		// We don't need to set --host_platforms because it's set in bazelrc files
 		// that the bazel shell script wrapper passes
 
-		// Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
-		"--experimental_repository_disable_download",
+		// Optimize Ninja rebuilds by ensuring Bazel write into product-agnostic
+		// output paths for the configured targets that shouldn't be affected by
+		// TARGET_PRODUCT. Otherwise product agnostic modules will be rebuilt by
+		// Ninja when the product changes, unconditionally.
+		//
+		// For example, Mainline APEXes should be identical regardless of the
+		// product (modulo arch/cpu).
+		//
+		// This flag forcibly disables the platform prefix in the intermediate
+		// outputs during a mixed build.
+		"--noexperimental_platform_in_output_dir",
 
 		// Suppress noise
 		"--ui_event_filters=-INFO",
@@ -748,9 +731,9 @@
 #####################################################
 def _config_node_transition_impl(settings, attr):
     if attr.os == "android" and attr.arch == "target":
-        target = "{PRODUCT}-{VARIANT}"
+        target = "current_product-{VARIANT}"
     else:
-        target = "{PRODUCT}-{VARIANT}_%s_%s" % (attr.os, attr.arch)
+        target = "current_product-{VARIANT}_%s_%s" % (attr.os, attr.arch)
     apex_name = ""
     if attr.within_apex:
         # //build/bazel/rules/apex:apex_name has to be set to a non_empty value,
@@ -761,7 +744,7 @@
         # value here.
         apex_name = "dcla_apex"
     outputs = {
-        "//command_line_option:platforms": "@soong_injection//product_config_platforms/products/{PRODUCT}-{VARIANT}:%s" % target,
+        "//command_line_option:platforms": "@soong_injection//product_config_platforms:%s" % target,
         "@//build/bazel/rules/apex:within_apex": attr.within_apex,
         "@//build/bazel/rules/apex:min_sdk_version": attr.apex_sdk_version,
         "@//build/bazel/rules/apex:apex_name": apex_name,
@@ -987,9 +970,9 @@
   platform_name = platforms[0].name
   if platform_name == "host":
     return "HOST"
-  if not platform_name.startswith("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}"):
-    fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_<arch>' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
-  platform_name = platform_name.removeprefix("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}").removeprefix("_")
+  if not platform_name.startswith("current_product-{TARGET_BUILD_VARIANT}"):
+    fail("expected platform name of the form 'current_product-{TARGET_BUILD_VARIANT}_android_<arch>' or 'current_product-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
+  platform_name = platform_name.removeprefix("current_product-{TARGET_BUILD_VARIANT}").removeprefix("_")
   config_key = ""
   if not platform_name:
     config_key = "target|android"
@@ -998,7 +981,7 @@
   elif platform_name.startswith("linux_"):
     config_key = platform_name.removeprefix("linux_") + "|linux"
   else:
-    fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_<arch>' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
+    fail("expected platform name of the form 'current_product-{TARGET_BUILD_VARIANT}_android_<arch>' or 'current_product-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
 
   within_apex = buildoptions.get("//build/bazel/rules/apex:within_apex")
   apex_sdk_version = buildoptions.get("//build/bazel/rules/apex:min_sdk_version")
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 77e2515..13fd408 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -436,3 +436,150 @@
 		}
 	}
 }
+
+type mixedBuildModule struct {
+	ModuleBase
+	BazelModuleBase
+	props struct {
+		Deps                     []string
+		Mixed_build_incompatible *bool
+		QueuedBazelCall          bool `blueprint:"mutated"`
+	}
+}
+
+type mixedBuildModuleInfo struct {
+	QueuedBazelCall bool
+}
+
+var mixedBuildModuleProvider = blueprint.NewProvider(mixedBuildModuleInfo{})
+
+func mixedBuildModuleFactory() Module {
+	m := &mixedBuildModule{}
+	m.AddProperties(&m.props)
+	InitAndroidArchModule(m, HostAndDeviceDefault, MultilibBoth)
+	InitBazelModule(m)
+
+	return m
+}
+
+func (m *mixedBuildModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+}
+
+func (m *mixedBuildModule) DepsMutator(ctx BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
+}
+
+func (m *mixedBuildModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *mixedBuildModule) IsMixedBuildSupported(ctx BaseModuleContext) bool {
+	return !proptools.Bool(m.props.Mixed_build_incompatible)
+}
+
+func (m *mixedBuildModule) QueueBazelCall(ctx BaseModuleContext) {
+	m.props.QueuedBazelCall = true
+}
+
+func (m *mixedBuildModule) ProcessBazelQueryResponse(ctx ModuleContext) {
+	ctx.SetProvider(mixedBuildModuleProvider, mixedBuildModuleInfo{
+		QueuedBazelCall: m.props.QueuedBazelCall,
+	})
+}
+
+var prepareForMixedBuildTests = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	ctx.RegisterModuleType("deps", mixedBuildModuleFactory)
+	RegisterMixedBuildsMutator(ctx)
+})
+
+func TestMixedBuildsEnabledForType(t *testing.T) {
+	baseBp := `
+	deps {
+		name: "foo",
+		deps: ["bar"],
+		target: { windows: { enabled: true } },
+		%s
+	}
+`
+	depBp := `
+	deps {
+		name: "bar",
+		target: {
+			windows: {
+				enabled: true,
+			},
+		},
+	}
+`
+	testCases := []struct {
+		desc               string
+		variant            *string
+		missingDeps        bool
+		extraBpInfo        string
+		mixedBuildsEnabled bool
+	}{
+		{
+			desc:               "mixed builds works",
+			mixedBuildsEnabled: true,
+			extraBpInfo:        `bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "missing deps",
+			missingDeps:        true,
+			mixedBuildsEnabled: false,
+			extraBpInfo:        `bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "windows no mixed builds",
+			mixedBuildsEnabled: false,
+			variant:            proptools.StringPtr("windows_x86"),
+			extraBpInfo:        `bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "mixed builds disabled by type",
+			mixedBuildsEnabled: false,
+			extraBpInfo: `mixed_build_incompatible: true,
+		bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "mixed builds not bp2build available",
+			mixedBuildsEnabled: false,
+			extraBpInfo:        `bazel_module: { bp2build_available: false },`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			handlers := GroupFixturePreparers(
+				prepareForMixedBuildTests,
+				PrepareForTestWithArchMutator,
+				FixtureModifyConfig(func(config Config) {
+					config.BazelContext = MockBazelContext{
+						OutputBaseDir: "base",
+					}
+					config.Targets[Windows] = []Target{
+						{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
+						{Windows, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", true},
+					}
+				}),
+			)
+			bp := fmt.Sprintf(baseBp, tc.extraBpInfo)
+			if tc.missingDeps {
+				handlers = GroupFixturePreparers(
+					handlers,
+					PrepareForTestWithAllowMissingDependencies,
+				)
+			} else {
+				bp += depBp
+			}
+			result := handlers.RunTestWithBp(t, bp)
+
+			variant := proptools.StringDefault(tc.variant, "android_arm64_armv8-a")
+
+			m := result.ModuleForTests("foo", variant)
+			mixedBuildModuleInfo := result.TestContext.ModuleProvider(m.Module(), mixedBuildModuleProvider).(mixedBuildModuleInfo)
+			if w, g := tc.mixedBuildsEnabled, mixedBuildModuleInfo.QueuedBazelCall; w != g {
+				t.Errorf("Expected mixed builds enabled %t, got mixed builds enabled %t", w, g)
+			}
+		})
+	}
+}
diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go
index 46f6488..8e19ad5 100644
--- a/android/buildinfo_prop.go
+++ b/android/buildinfo_prop.go
@@ -23,7 +23,7 @@
 
 func init() {
 	ctx := InitRegistrationContext
-	ctx.RegisterSingletonModuleType("buildinfo_prop", buildinfoPropFactory)
+	ctx.RegisterParallelSingletonModuleType("buildinfo_prop", buildinfoPropFactory)
 }
 
 type buildinfoPropProperties struct {
diff --git a/android/config.go b/android/config.go
index 9e94e05..bed57e3 100644
--- a/android/config.go
+++ b/android/config.go
@@ -80,9 +80,10 @@
 
 type CmdArgs struct {
 	bootstrap.Args
-	RunGoTests  bool
-	OutDir      string
-	SoongOutDir string
+	RunGoTests     bool
+	OutDir         string
+	SoongOutDir    string
+	SoongVariables string
 
 	SymlinkForestMarker string
 	Bp2buildMarker      string
@@ -183,6 +184,16 @@
 	return String(c.config.productVariables.DeviceMaxPageSizeSupported)
 }
 
+// The release version passed to aconfig, derived from RELEASE_VERSION
+func (c Config) ReleaseVersion() string {
+	return c.config.productVariables.ReleaseVersion
+}
+
+// The flag values files passed to aconfig, derived from RELEASE_VERSION
+func (c Config) ReleaseDeviceConfigValueSets() []string {
+	return c.config.productVariables.ReleaseDeviceConfigValueSets
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -292,6 +303,9 @@
 	// modules that aren't mixed-built for at least one variant will cause a build
 	// failure
 	ensureAllowlistIntegrity bool
+
+	// List of Api libraries that contribute to Api surfaces.
+	apiLibraries map[string]struct{}
 }
 
 type deviceConfig struct {
@@ -478,7 +492,7 @@
 func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
-		ProductVariablesFileName: filepath.Join(cmdArgs.SoongOutDir, productVariablesFileName),
+		ProductVariablesFileName: cmdArgs.SoongVariables,
 
 		env: availableEnv,
 
@@ -612,6 +626,33 @@
 	config.BazelContext, err = NewBazelContext(config)
 	config.Bp2buildPackageConfig = GetBp2BuildAllowList()
 
+	// TODO(b/276958307): Replace the hardcoded list to a sdk_library local prop.
+	config.apiLibraries = map[string]struct{}{
+		"android.net.ipsec.ike":             {},
+		"art.module.public.api":             {},
+		"conscrypt.module.public.api":       {},
+		"framework-adservices":              {},
+		"framework-appsearch":               {},
+		"framework-bluetooth":               {},
+		"framework-connectivity":            {},
+		"framework-connectivity-t":          {},
+		"framework-graphics":                {},
+		"framework-media":                   {},
+		"framework-mediaprovider":           {},
+		"framework-ondevicepersonalization": {},
+		"framework-permission":              {},
+		"framework-permission-s":            {},
+		"framework-scheduling":              {},
+		"framework-sdkextensions":           {},
+		"framework-statsd":                  {},
+		"framework-sdksandbox":              {},
+		"framework-tethering":               {},
+		"framework-uwb":                     {},
+		"framework-virtualization":          {},
+		"framework-wifi":                    {},
+		"i18n.module.public.api":            {},
+	}
+
 	return Config{config}, err
 }
 
@@ -716,6 +757,14 @@
 	return path
 }
 
+func (c *config) HostCcSharedLibPath(ctx PathContext, lib string) Path {
+	libDir := "lib"
+	if ctx.Config().BuildArch.Multilib == "lib64" {
+		libDir = "lib64"
+	}
+	return pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, libDir, false, lib+".so")
+}
+
 // PrebuiltOS returns the name of the host OS used in prebuilts directories.
 func (c *config) PrebuiltOS() string {
 	switch runtime.GOOS {
@@ -1840,6 +1889,10 @@
 	return uncheckedFinalApiLevel(apiLevel)
 }
 
+func (c *deviceConfig) BuildBrokenPluginValidation() []string {
+	return c.config.productVariables.BuildBrokenPluginValidation
+}
+
 func (c *deviceConfig) BuildBrokenClangAsFlags() bool {
 	return c.config.productVariables.BuildBrokenClangAsFlags
 }
@@ -1876,8 +1929,8 @@
 	return InList(name, c.config.productVariables.BuildBrokenInputDirModules)
 }
 
-func (c *deviceConfig) BuildBrokenDepfile() bool {
-	return Bool(c.config.productVariables.BuildBrokenDepfile)
+func (c *deviceConfig) GenruleSandboxing() bool {
+	return Bool(c.config.productVariables.GenruleSandboxing)
 }
 
 func (c *deviceConfig) RequiresInsecureExecmemForSwiftshader() bool {
@@ -1969,8 +2022,20 @@
 func (c *config) SetBuildFromTextStub(b bool) {
 	c.buildFromTextStub = b
 }
+
 func (c *config) AddForceEnabledModules(forceEnabled []string) {
 	for _, forceEnabledModule := range forceEnabled {
 		c.bazelForceEnabledModules[forceEnabledModule] = struct{}{}
 	}
 }
+
+func (c *config) SetApiLibraries(libs []string) {
+	c.apiLibraries = make(map[string]struct{})
+	for _, lib := range libs {
+		c.apiLibraries[lib] = struct{}{}
+	}
+}
+
+func (c *config) GetApiLibraries() map[string]struct{} {
+	return c.apiLibraries
+}
diff --git a/android/filegroup.go b/android/filegroup.go
index f30ee51..c042ff1 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -177,6 +177,17 @@
 	}
 }
 
+type FileGroupPath interface {
+	GetPath(ctx TopDownMutatorContext) string
+}
+
+func (fg *fileGroup) GetPath(ctx TopDownMutatorContext) string {
+	if fg.properties.Path != nil {
+		return *fg.properties.Path
+	}
+	return ""
+}
+
 type fileGroupProperties struct {
 	// srcs lists files that will be included in this filegroup
 	Srcs []string `android:"path"`
@@ -207,6 +218,7 @@
 	BazelModuleBase
 	DefaultableModuleBase
 	FileGroupAsLibrary
+	FileGroupPath
 	properties fileGroupProperties
 	srcs       Paths
 }
@@ -214,6 +226,7 @@
 var _ MixedBuildBuildable = (*fileGroup)(nil)
 var _ SourceFileProducer = (*fileGroup)(nil)
 var _ FileGroupAsLibrary = (*fileGroup)(nil)
+var _ FileGroupPath = (*fileGroup)(nil)
 
 // filegroup contains a list of files that are referenced by other modules
 // properties (such as "srcs") using the syntax ":<name>". filegroup are
diff --git a/android/gen_notice.go b/android/gen_notice.go
index 091345b..1acc638 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -28,7 +28,7 @@
 
 // Register the gen_notice module type.
 func RegisterGenNoticeBuildComponents(ctx RegistrationContext) {
-	ctx.RegisterSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory)
+	ctx.RegisterParallelSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory)
 	ctx.RegisterModuleType("gen_notice", GenNoticeFactory)
 }
 
diff --git a/android/metrics.go b/android/metrics.go
index 3d41a1d..63c72cd 100644
--- a/android/metrics.go
+++ b/android/metrics.go
@@ -42,7 +42,7 @@
 }
 
 func init() {
-	RegisterSingletonType("soong_metrics", soongMetricsSingletonFactory)
+	RegisterParallelSingletonType("soong_metrics", soongMetricsSingletonFactory)
 }
 
 func soongMetricsSingletonFactory() Singleton { return soongMetricsSingleton{} }
diff --git a/android/module.go b/android/module.go
index db602a0..98084f3 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,6 +15,9 @@
 package android
 
 import (
+	"crypto/md5"
+	"encoding/hex"
+	"encoding/json"
 	"fmt"
 	"net/url"
 	"os"
@@ -354,6 +357,10 @@
 
 	AddMissingDependencies(missingDeps []string)
 
+	// getMissingDependencies returns the list of missing dependencies.
+	// Calling this function prevents adding new dependencies.
+	getMissingDependencies() []string
+
 	// AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build
 	AddUnconvertedBp2buildDep(dep string)
 
@@ -710,6 +717,31 @@
 	return l[:k+1]
 }
 
+// soongConfigTrace holds all references to VendorVars. Uses []string for blueprint:"mutated"
+type soongConfigTrace struct {
+	Bools   []string `json:",omitempty"`
+	Strings []string `json:",omitempty"`
+	IsSets  []string `json:",omitempty"`
+}
+
+func (c *soongConfigTrace) isEmpty() bool {
+	return len(c.Bools) == 0 && len(c.Strings) == 0 && len(c.IsSets) == 0
+}
+
+// Returns hash of serialized trace records (empty string if there's no trace recorded)
+func (c *soongConfigTrace) hash() string {
+	// Use MD5 for speed. We don't care collision or preimage attack
+	if c.isEmpty() {
+		return ""
+	}
+	j, err := json.Marshal(c)
+	if err != nil {
+		panic(fmt.Errorf("json marshal of %#v failed: %#v", *c, err))
+	}
+	hash := md5.Sum(j)
+	return hex.EncodeToString(hash[:])
+}
+
 type nameProperties struct {
 	// The name of the module.  Must be unique across all modules.
 	Name *string
@@ -939,7 +971,8 @@
 
 	NamespaceExportedToMake bool `blueprint:"mutated"`
 
-	MissingDeps []string `blueprint:"mutated"`
+	MissingDeps        []string `blueprint:"mutated"`
+	CheckedMissingDeps bool     `blueprint:"mutated"`
 
 	// Name and variant strings stored by mutators to enable Module.String()
 	DebugName       string   `blueprint:"mutated"`
@@ -953,6 +986,10 @@
 
 	// Bazel conversion status
 	BazelConversionStatus BazelConversionStatus `blueprint:"mutated"`
+
+	// SoongConfigTrace records accesses to VendorVars (soong_config)
+	SoongConfigTrace     soongConfigTrace `blueprint:"mutated"`
+	SoongConfigTraceHash string           `blueprint:"mutated"`
 }
 
 // CommonAttributes represents the common Bazel attributes from which properties
@@ -2862,6 +2899,20 @@
 	}
 }
 
+func (b *baseModuleContext) checkedMissingDeps() bool {
+	return b.Module().base().commonProperties.CheckedMissingDeps
+}
+
+func (b *baseModuleContext) getMissingDependencies() []string {
+	checked := &b.Module().base().commonProperties.CheckedMissingDeps
+	*checked = true
+	var missingDeps []string
+	missingDeps = append(missingDeps, b.Module().base().commonProperties.MissingDeps...)
+	missingDeps = append(missingDeps, b.bp.EarlyGetMissingDependencies()...)
+	missingDeps = FirstUniqueStrings(missingDeps)
+	return missingDeps
+}
+
 type AllowDisabledModuleDependency interface {
 	blueprint.DependencyTag
 	AllowDisabledModuleDependency(target Module) bool
@@ -3141,6 +3192,10 @@
 	return m.bp.ModuleSubDir()
 }
 
+func (m *moduleContext) ModuleSoongConfigHash() string {
+	return m.module.base().commonProperties.SoongConfigTraceHash
+}
+
 func (b *baseModuleContext) Target() Target {
 	return b.target
 }
@@ -3724,7 +3779,9 @@
 }
 
 func init() {
-	RegisterSingletonType("buildtarget", BuildTargetSingleton)
+	RegisterParallelSingletonType("buildtarget", BuildTargetSingleton)
+	RegisterParallelSingletonType("soongconfigtrace", soongConfigTraceSingletonFunc)
+	FinalDepsMutators(registerSoongConfigTraceMutator)
 }
 
 func BuildTargetSingleton() Singleton {
@@ -3906,3 +3963,54 @@
 	}
 	return d.depSet.ToList().(InstallPaths)
 }
+
+func registerSoongConfigTraceMutator(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("soongconfigtrace", soongConfigTraceMutator).Parallel()
+}
+
+// soongConfigTraceMutator accumulates recorded soong_config trace from children. Also it normalizes
+// SoongConfigTrace to make it consistent.
+func soongConfigTraceMutator(ctx BottomUpMutatorContext) {
+	trace := &ctx.Module().base().commonProperties.SoongConfigTrace
+	ctx.VisitDirectDeps(func(m Module) {
+		childTrace := &m.base().commonProperties.SoongConfigTrace
+		trace.Bools = append(trace.Bools, childTrace.Bools...)
+		trace.Strings = append(trace.Strings, childTrace.Strings...)
+		trace.IsSets = append(trace.IsSets, childTrace.IsSets...)
+	})
+	trace.Bools = SortedUniqueStrings(trace.Bools)
+	trace.Strings = SortedUniqueStrings(trace.Strings)
+	trace.IsSets = SortedUniqueStrings(trace.IsSets)
+
+	ctx.Module().base().commonProperties.SoongConfigTraceHash = trace.hash()
+}
+
+// soongConfigTraceSingleton writes a map from each module's config hash value to trace data.
+func soongConfigTraceSingletonFunc() Singleton {
+	return &soongConfigTraceSingleton{}
+}
+
+type soongConfigTraceSingleton struct {
+}
+
+func (s *soongConfigTraceSingleton) GenerateBuildActions(ctx SingletonContext) {
+	outFile := PathForOutput(ctx, "soong_config_trace.json")
+
+	traces := make(map[string]*soongConfigTrace)
+	ctx.VisitAllModules(func(module Module) {
+		trace := &module.base().commonProperties.SoongConfigTrace
+		if !trace.isEmpty() {
+			hash := module.base().commonProperties.SoongConfigTraceHash
+			traces[hash] = trace
+		}
+	})
+
+	j, err := json.Marshal(traces)
+	if err != nil {
+		ctx.Errorf("json marshal to %q failed: %#v", outFile, err)
+		return
+	}
+
+	WriteFileRule(ctx, outFile, string(j))
+	ctx.Phony("soong_config_trace", outFile)
+}
diff --git a/android/mutator.go b/android/mutator.go
index 0a091eb..4185315 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -67,6 +67,8 @@
 // collateGloballyRegisteredMutators constructs the list of mutators that have been registered
 // with the InitRegistrationContext and will be used at runtime.
 func collateGloballyRegisteredMutators() sortableComponents {
+	// ensure mixed builds mutator is the last mutator
+	finalDeps = append(finalDeps, registerMixedBuildsMutator)
 	return collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps)
 }
 
@@ -885,10 +887,16 @@
 }
 
 func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	return b.bp.AddDependency(module, tag, name...)
 }
 
 func (b *bottomUpMutatorContext) AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	b.bp.AddReverseDependency(module, tag, name)
 }
 
@@ -938,11 +946,17 @@
 
 func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
 	names ...string) []blueprint.Module {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	return b.bp.AddVariationDependencies(variations, tag, names...)
 }
 
 func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation,
 	tag blueprint.DependencyTag, names ...string) []blueprint.Module {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 
 	return b.bp.AddFarVariationDependencies(variations, tag, names...)
 }
@@ -952,10 +966,16 @@
 }
 
 func (b *bottomUpMutatorContext) ReplaceDependencies(name string) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	b.bp.ReplaceDependencies(name)
 }
 
 func (b *bottomUpMutatorContext) ReplaceDependenciesIf(name string, predicate blueprint.ReplaceDependencyPredicate) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	b.bp.ReplaceDependenciesIf(name, predicate)
 }
 
diff --git a/android/paths.go b/android/paths.go
index eaa6a8d..0f3d972 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1475,7 +1475,11 @@
 }
 
 func pathForModuleOut(ctx ModuleOutPathContext) OutputPath {
-	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
+	soongConfigHash := ""
+	if i, ok := ctx.(interface{ ModuleSoongConfigHash() string }); ok {
+		soongConfigHash = i.ModuleSoongConfigHash()
+	}
+	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), soongConfigHash)
 }
 
 // PathForModuleOut returns a Path representing the paths... under the module's
diff --git a/android/plugin.go b/android/plugin.go
new file mode 100644
index 0000000..4672453
--- /dev/null
+++ b/android/plugin.go
@@ -0,0 +1,140 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"github.com/google/blueprint"
+)
+
+func init() {
+	RegisterPluginSingletonBuildComponents(InitRegistrationContext)
+}
+
+func RegisterPluginSingletonBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterParallelSingletonType("plugins", pluginSingletonFactory)
+}
+
+// pluginSingleton is a singleton to handle allowlisting of the final Android-<product_name>.mk file
+// output.
+func pluginSingletonFactory() Singleton {
+	return &pluginSingleton{}
+}
+
+type pluginSingleton struct{}
+
+var allowedPluginsByName = map[string]bool{
+	"aidl-soong-rules":                       true,
+	"arm_compute_library_nn_driver":          true,
+	"cuttlefish-soong-rules":                 true,
+	"gki-soong-rules":                        true,
+	"hidl-soong-rules":                       true,
+	"kernel-config-soong-rules":              true,
+	"soong-angle-codegen":                    true,
+	"soong-api":                              true,
+	"soong-art":                              true,
+	"soong-ca-certificates":                  true,
+	"soong-ca-certificates-apex":             true,
+	"soong-clang":                            true,
+	"soong-clang-prebuilts":                  true,
+	"soong-csuite":                           true,
+	"soong-fluoride":                         true,
+	"soong-fs_config":                        true,
+	"soong-icu":                              true,
+	"soong-java-config-error_prone":          true,
+	"soong-libchrome":                        true,
+	"soong-llvm":                             true,
+	"soong-robolectric":                      true,
+	"soong-rust-prebuilts":                   true,
+	"soong-selinux":                          true,
+	"soong-wayland-protocol-codegen":         true,
+	"treble_report_app":                      true,
+	"treble_report_local":                    true,
+	"treble_report_module":                   true,
+	"vintf-compatibility-matrix-soong-rules": true,
+	"xsdc-soong-rules":                       true,
+}
+
+const (
+	internalPluginsPath = "vendor/google/build/soong/internal_plugins.json"
+)
+
+type pluginProvider interface {
+	IsPluginFor(string) bool
+}
+
+func maybeAddInternalPluginsToAllowlist(ctx SingletonContext) {
+	if path := ExistentPathForSource(ctx, internalPluginsPath); path.Valid() {
+		ctx.AddNinjaFileDeps(path.String())
+		absPath := absolutePath(path.String())
+		var moreAllowed map[string]bool
+		data, err := ioutil.ReadFile(absPath)
+		if err != nil {
+			ctx.Errorf("Failed to open internal plugins path %q %q", internalPluginsPath, err)
+		}
+		if err := json.Unmarshal(data, &moreAllowed); err != nil {
+			fmt.Fprintf(os.Stderr, "Internal plugins file %q did not parse correctly: %q", data, err)
+		}
+		for k, v := range moreAllowed {
+			allowedPluginsByName[k] = v
+		}
+	}
+}
+
+func (p *pluginSingleton) GenerateBuildActions(ctx SingletonContext) {
+	for _, p := range ctx.DeviceConfig().BuildBrokenPluginValidation() {
+		allowedPluginsByName[p] = true
+	}
+	maybeAddInternalPluginsToAllowlist(ctx)
+
+	disallowedPlugins := map[string]bool{}
+	ctx.VisitAllModulesBlueprint(func(module blueprint.Module) {
+		if ctx.ModuleType(module) != "bootstrap_go_package" {
+			return
+		}
+
+		p, ok := module.(pluginProvider)
+		if !ok || !p.IsPluginFor("soong_build") {
+			return
+		}
+
+		name := ctx.ModuleName(module)
+		if _, ok := allowedPluginsByName[name]; ok {
+			return
+		}
+
+		dir := ctx.ModuleDir(module)
+
+		// allow use of plugins within Soong to not allowlist everything
+		if strings.HasPrefix(dir, "build/soong") {
+			return
+		}
+
+		// allow third party users outside of external to create new plugins, i.e. non-google paths
+		// under vendor or hardware
+		if !strings.HasPrefix(dir, "external/") && IsThirdPartyPath(dir) {
+			return
+		}
+		disallowedPlugins[name] = true
+	})
+	if len(disallowedPlugins) > 0 {
+		ctx.Errorf("New plugins are not supported; however %q were found. Please reach out to the build team or use BUILD_BROKEN_PLUGIN_VALIDATION (see Changes.md for more info).", SortedStringKeys(disallowedPlugins))
+	}
+}
diff --git a/android/register.go b/android/register.go
index 1a3db9d..64b0207 100644
--- a/android/register.go
+++ b/android/register.go
@@ -65,16 +65,19 @@
 	// True if this should be registered as a pre-singleton, false otherwise.
 	pre bool
 
+	// True if this should be registered as a parallel singleton.
+	parallel bool
+
 	name    string
 	factory SingletonFactory
 }
 
-func newSingleton(name string, factory SingletonFactory) singleton {
-	return singleton{false, name, factory}
+func newSingleton(name string, factory SingletonFactory, parallel bool) singleton {
+	return singleton{pre: false, parallel: parallel, name: name, factory: factory}
 }
 
 func newPreSingleton(name string, factory SingletonFactory) singleton {
-	return singleton{true, name, factory}
+	return singleton{pre: true, parallel: false, name: name, factory: factory}
 }
 
 func (s singleton) componentName() string {
@@ -86,7 +89,7 @@
 	if s.pre {
 		ctx.RegisterPreSingletonType(s.name, adaptor)
 	} else {
-		ctx.RegisterSingletonType(s.name, adaptor)
+		ctx.RegisterSingletonType(s.name, adaptor, s.parallel)
 	}
 }
 
@@ -145,8 +148,16 @@
 	moduleTypeByFactory[factory] = name
 }
 
+func registerSingletonType(name string, factory SingletonFactory, parallel bool) {
+	singletons = append(singletons, newSingleton(name, factory, parallel))
+}
+
 func RegisterSingletonType(name string, factory SingletonFactory) {
-	singletons = append(singletons, newSingleton(name, factory))
+	registerSingletonType(name, factory, false)
+}
+
+func RegisterParallelSingletonType(name string, factory SingletonFactory) {
+	registerSingletonType(name, factory, true)
 }
 
 func RegisterPreSingletonType(name string, factory SingletonFactory) {
@@ -220,17 +231,17 @@
 func collateGloballyRegisteredSingletons() sortableComponents {
 	allSingletons := append(sortableComponents(nil), singletons...)
 	allSingletons = append(allSingletons,
-		singleton{false, "bazeldeps", BazelSingleton},
+		singleton{pre: false, parallel: true, name: "bazeldeps", factory: BazelSingleton},
 
 		// Register phony just before makevars so it can write out its phony rules as Make rules
-		singleton{false, "phony", phonySingletonFactory},
+		singleton{pre: false, parallel: false, name: "phony", factory: phonySingletonFactory},
 
 		// Register makevars after other singletons so they can export values through makevars
-		singleton{false, "makevars", makeVarsSingletonFunc},
+		singleton{pre: false, parallel: false, name: "makevars", factory: makeVarsSingletonFunc},
 
 		// Register env and ninjadeps last so that they can track all used environment variables and
 		// Ninja file dependencies stored in the config.
-		singleton{false, "ninjadeps", ninjaDepsSingletonFactory},
+		singleton{pre: false, parallel: false, name: "ninjadeps", factory: ninjaDepsSingletonFactory},
 	)
 
 	return allSingletons
@@ -259,7 +270,9 @@
 type RegistrationContext interface {
 	RegisterModuleType(name string, factory ModuleFactory)
 	RegisterSingletonModuleType(name string, factory SingletonModuleFactory)
+	RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory)
 	RegisterPreSingletonType(name string, factory SingletonFactory)
+	RegisterParallelSingletonType(name string, factory SingletonFactory)
 	RegisterSingletonType(name string, factory SingletonFactory)
 	PreArchMutators(f RegisterMutatorFunc)
 
@@ -315,8 +328,15 @@
 }
 
 func (ctx *initRegistrationContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+	ctx.registerSingletonModuleType(name, factory, false)
+}
+func (ctx *initRegistrationContext) RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) {
+	ctx.registerSingletonModuleType(name, factory, true)
+}
+
+func (ctx *initRegistrationContext) registerSingletonModuleType(name string, factory SingletonModuleFactory, parallel bool) {
 	s, m := SingletonModuleFactoryAdaptor(name, factory)
-	ctx.RegisterSingletonType(name, s)
+	ctx.registerSingletonType(name, s, parallel)
 	ctx.RegisterModuleType(name, m)
 	// Overwrite moduleTypesForDocs with the original factory instead of the lambda returned by
 	// SingletonModuleFactoryAdaptor so that docs can find the module type documentation on the
@@ -324,12 +344,20 @@
 	RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
 }
 
-func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
+func (ctx *initRegistrationContext) registerSingletonType(name string, factory SingletonFactory, parallel bool) {
 	if _, present := ctx.singletonTypes[name]; present {
 		panic(fmt.Sprintf("singleton type %q is already registered", name))
 	}
 	ctx.singletonTypes[name] = factory
-	RegisterSingletonType(name, factory)
+	registerSingletonType(name, factory, parallel)
+}
+
+func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
+	ctx.registerSingletonType(name, factory, false)
+}
+
+func (ctx *initRegistrationContext) RegisterParallelSingletonType(name string, factory SingletonFactory) {
+	ctx.registerSingletonType(name, factory, true)
 }
 
 func (ctx *initRegistrationContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 5fa6012..0246a08 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -421,6 +421,57 @@
 	}).(map[string]blueprint.ModuleFactory)
 }
 
+// tracingConfig is a wrapper to soongconfig.SoongConfig which records all accesses to SoongConfig.
+type tracingConfig struct {
+	config    soongconfig.SoongConfig
+	boolSet   map[string]bool
+	stringSet map[string]string
+	isSetSet  map[string]bool
+}
+
+func (c *tracingConfig) Bool(name string) bool {
+	c.boolSet[name] = c.config.Bool(name)
+	return c.boolSet[name]
+}
+
+func (c *tracingConfig) String(name string) string {
+	c.stringSet[name] = c.config.String(name)
+	return c.stringSet[name]
+}
+
+func (c *tracingConfig) IsSet(name string) bool {
+	c.isSetSet[name] = c.config.IsSet(name)
+	return c.isSetSet[name]
+}
+
+func (c *tracingConfig) getTrace() soongConfigTrace {
+	ret := soongConfigTrace{}
+
+	for k, v := range c.boolSet {
+		ret.Bools = append(ret.Bools, fmt.Sprintf("%q:%t", k, v))
+	}
+	for k, v := range c.stringSet {
+		ret.Strings = append(ret.Strings, fmt.Sprintf("%q:%q", k, v))
+	}
+	for k, v := range c.isSetSet {
+		ret.IsSets = append(ret.IsSets, fmt.Sprintf("%q:%t", k, v))
+	}
+
+	return ret
+}
+
+func newTracingConfig(config soongconfig.SoongConfig) *tracingConfig {
+	c := tracingConfig{
+		config:    config,
+		boolSet:   make(map[string]bool),
+		stringSet: make(map[string]string),
+		isSetSet:  make(map[string]bool),
+	}
+	return &c
+}
+
+var _ soongconfig.SoongConfig = (*tracingConfig)(nil)
+
 // configModuleFactory takes an existing soongConfigModuleFactory and a
 // ModuleType to create a new ModuleFactory that uses a custom loadhook.
 func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType, bp2build bool) blueprint.ModuleFactory {
@@ -485,8 +536,8 @@
 			// conditional on Soong config variables by reading the product
 			// config variables from Make.
 			AddLoadHook(module, func(ctx LoadHookContext) {
-				config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
-				newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
+				tracingConfig := newTracingConfig(ctx.Config().VendorConfig(moduleType.ConfigNamespace))
+				newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, tracingConfig)
 				if err != nil {
 					ctx.ModuleErrorf("%s", err)
 					return
@@ -494,6 +545,8 @@
 				for _, ps := range newProps {
 					ctx.AppendProperties(ps)
 				}
+
+				module.(Module).base().commonProperties.SoongConfigTrace = tracingConfig.getTrace()
 			})
 		}
 		return module, props
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index cab3e2d..79bdeb8 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 	"testing"
 )
 
@@ -34,7 +35,8 @@
 type soongConfigTestModule struct {
 	ModuleBase
 	DefaultableModuleBase
-	props soongConfigTestModuleProperties
+	props      soongConfigTestModuleProperties
+	outputPath ModuleOutPath
 }
 
 type soongConfigTestModuleProperties struct {
@@ -49,7 +51,9 @@
 	return m
 }
 
-func (t soongConfigTestModule) GenerateAndroidBuildActions(ModuleContext) {}
+func (t *soongConfigTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	t.outputPath = PathForModuleOut(ctx, "test")
+}
 
 var prepareForSoongConfigTestModule = FixtureRegisterWithContext(func(ctx RegistrationContext) {
 	ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
@@ -503,3 +507,197 @@
 		})
 	}
 }
+
+func TestSoongConfigModuleTrace(t *testing.T) {
+	bp := `
+		soong_config_module_type {
+			name: "acme_test",
+			module_type: "test",
+			config_namespace: "acme",
+			variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
+			bool_variables: ["feature2", "unused_feature", "always_true"],
+			value_variables: ["size", "unused_size"],
+			properties: ["cflags", "srcs", "defaults"],
+		}
+
+		soong_config_module_type {
+			name: "acme_test_defaults",
+			module_type: "test_defaults",
+			config_namespace: "acme",
+			variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
+			bool_variables: ["feature2", "unused_feature", "always_true"],
+			value_variables: ["size", "unused_size"],
+			properties: ["cflags", "srcs", "defaults"],
+		}
+
+		soong_config_string_variable {
+			name: "board",
+			values: ["soc_a", "soc_b", "soc_c"],
+		}
+
+		soong_config_string_variable {
+			name: "unused_string_var",
+			values: ["a", "b"],
+		}
+
+		soong_config_bool_variable {
+			name: "feature1",
+		}
+
+		soong_config_bool_variable {
+			name: "FEATURE3",
+		}
+
+		test_defaults {
+			name: "test_defaults",
+			cflags: ["DEFAULT"],
+		}
+
+		test {
+			name: "normal",
+			defaults: ["test_defaults"],
+		}
+
+		acme_test {
+			name: "board_1",
+			defaults: ["test_defaults"],
+			soong_config_variables: {
+				board: {
+					soc_a: {
+						cflags: ["-DSOC_A"],
+					},
+				},
+			},
+		}
+
+		acme_test {
+			name: "board_2",
+			defaults: ["test_defaults"],
+			soong_config_variables: {
+				board: {
+					soc_a: {
+						cflags: ["-DSOC_A"],
+					},
+				},
+			},
+		}
+
+		acme_test {
+			name: "size",
+			defaults: ["test_defaults"],
+			soong_config_variables: {
+				size: {
+					cflags: ["-DSIZE=%s"],
+				},
+			},
+		}
+
+		acme_test {
+			name: "board_and_size",
+			defaults: ["test_defaults"],
+			soong_config_variables: {
+				board: {
+					soc_a: {
+						cflags: ["-DSOC_A"],
+					},
+				},
+				size: {
+					cflags: ["-DSIZE=%s"],
+				},
+			},
+		}
+
+		acme_test_defaults {
+			name: "board_defaults",
+			soong_config_variables: {
+				board: {
+					soc_a: {
+						cflags: ["-DSOC_A"],
+					},
+				},
+			},
+		}
+
+		acme_test_defaults {
+			name: "size_defaults",
+			soong_config_variables: {
+				size: {
+					cflags: ["-DSIZE=%s"],
+				},
+			},
+		}
+
+		test {
+			name: "board_and_size_with_defaults",
+			defaults: ["board_defaults", "size_defaults"],
+		}
+    `
+
+	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.VendorVars = vars
+		})
+	}
+
+	preparer := fixtureForVendorVars(map[string]map[string]string{
+		"acme": {
+			"board":    "soc_a",
+			"size":     "42",
+			"feature1": "true",
+			"feature2": "false",
+			// FEATURE3 unset
+			"unused_feature":    "true", // unused
+			"unused_size":       "1",    // unused
+			"unused_string_var": "a",    // unused
+			"always_true":       "true",
+		},
+	})
+
+	t.Run("soong config trace hash", func(t *testing.T) {
+		result := GroupFixturePreparers(
+			preparer,
+			PrepareForTestWithDefaults,
+			PrepareForTestWithSoongConfigModuleBuildComponents,
+			prepareForSoongConfigTestModule,
+			FixtureRegisterWithContext(func(ctx RegistrationContext) {
+				ctx.FinalDepsMutators(registerSoongConfigTraceMutator)
+			}),
+			FixtureWithRootAndroidBp(bp),
+		).RunTest(t)
+
+		// Hashes of modules not using soong config should be empty
+		normal := result.ModuleForTests("normal", "").Module().(*soongConfigTestModule)
+		AssertDeepEquals(t, "normal hash", normal.base().commonProperties.SoongConfigTraceHash, "")
+		AssertDeepEquals(t, "normal hash out", normal.outputPath.RelativeToTop().String(), "out/soong/.intermediates/normal/test")
+
+		board1 := result.ModuleForTests("board_1", "").Module().(*soongConfigTestModule)
+		board2 := result.ModuleForTests("board_2", "").Module().(*soongConfigTestModule)
+		size := result.ModuleForTests("size", "").Module().(*soongConfigTestModule)
+
+		// Trace mutator sets soong config trace hash correctly
+		board1Hash := board1.base().commonProperties.SoongConfigTrace.hash()
+		board1Output := board1.outputPath.RelativeToTop().String()
+		AssertDeepEquals(t, "board hash calc", board1Hash, board1.base().commonProperties.SoongConfigTraceHash)
+		AssertDeepEquals(t, "board hash path", board1Output, filepath.Join("out/soong/.intermediates/board_1", board1Hash, "test"))
+
+		sizeHash := size.base().commonProperties.SoongConfigTrace.hash()
+		sizeOutput := size.outputPath.RelativeToTop().String()
+		AssertDeepEquals(t, "size hash calc", sizeHash, size.base().commonProperties.SoongConfigTraceHash)
+		AssertDeepEquals(t, "size hash path", sizeOutput, filepath.Join("out/soong/.intermediates/size", sizeHash, "test"))
+
+		// Trace should be identical for modules using the same set of variables
+		AssertDeepEquals(t, "board trace", board1.base().commonProperties.SoongConfigTrace, board2.base().commonProperties.SoongConfigTrace)
+		AssertDeepEquals(t, "board hash", board1.base().commonProperties.SoongConfigTraceHash, board2.base().commonProperties.SoongConfigTraceHash)
+
+		// Trace hash should be different for different sets of soong variables
+		AssertBoolEquals(t, "board hash not equal to size hash", board1.base().commonProperties.SoongConfigTraceHash == size.commonProperties.SoongConfigTraceHash, false)
+
+		boardSize := result.ModuleForTests("board_and_size", "").Module().(*soongConfigTestModule)
+		boardSizeDefaults := result.ModuleForTests("board_and_size_with_defaults", "").Module()
+
+		// Trace should propagate
+		AssertDeepEquals(t, "board_size hash calc", boardSize.base().commonProperties.SoongConfigTrace.hash(), boardSize.base().commonProperties.SoongConfigTraceHash)
+		AssertDeepEquals(t, "board_size trace", boardSize.base().commonProperties.SoongConfigTrace, boardSizeDefaults.base().commonProperties.SoongConfigTrace)
+		AssertDeepEquals(t, "board_size hash", boardSize.base().commonProperties.SoongConfigTraceHash, boardSizeDefaults.base().commonProperties.SoongConfigTraceHash)
+	})
+}
diff --git a/android/test_asserts.go b/android/test_asserts.go
index 4143f15..5cc7e4a 100644
--- a/android/test_asserts.go
+++ b/android/test_asserts.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"reflect"
+	"regexp"
 	"strings"
 	"testing"
 )
@@ -137,6 +138,20 @@
 	}
 }
 
+// AssertStringMatches checks if the string matches the given regular expression. If it does not match,
+// then an error is reported with the supplied message including a reason for why it failed.
+func AssertStringMatches(t *testing.T, message, s, expectedRex string) {
+	t.Helper()
+	ok, err := regexp.MatchString(expectedRex, s)
+	if err != nil {
+		t.Fatalf("regexp failure trying to match %s against `%s` expression: %s", s, expectedRex, err)
+		return
+	}
+	if !ok {
+		t.Errorf("%s does not match regular expression %s", s, expectedRex)
+	}
+}
+
 // AssertStringListContains checks if the list of strings contains the expected string. If it does
 // not then it reports an error prefixed with the supplied message and including a reason for why it
 // failed.
diff --git a/android/test_suites.go b/android/test_suites.go
index b570b23..b48d71a 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -15,7 +15,7 @@
 package android
 
 func init() {
-	RegisterSingletonType("testsuites", testSuiteFilesFactory)
+	RegisterParallelSingletonType("testsuites", testSuiteFilesFactory)
 }
 
 func testSuiteFilesFactory() Singleton {
diff --git a/android/testing.go b/android/testing.go
index 2a9c658..5ad7ad0 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -495,8 +495,18 @@
 	ctx.RegisterModuleType(name, m)
 }
 
+func (ctx *TestContext) RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) {
+	s, m := SingletonModuleFactoryAdaptor(name, factory)
+	ctx.RegisterParallelSingletonType(name, s)
+	ctx.RegisterModuleType(name, m)
+}
+
 func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
-	ctx.singletons = append(ctx.singletons, newSingleton(name, factory))
+	ctx.singletons = append(ctx.singletons, newSingleton(name, factory, false))
+}
+
+func (ctx *TestContext) RegisterParallelSingletonType(name string, factory SingletonFactory) {
+	ctx.singletons = append(ctx.singletons, newSingleton(name, factory, true))
 }
 
 func (ctx *TestContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
diff --git a/android/variable.go b/android/variable.go
index bf66135..77888e5 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -440,10 +440,11 @@
 
 	ShippingApiLevel *string `json:",omitempty"`
 
+	BuildBrokenPluginValidation        []string `json:",omitempty"`
 	BuildBrokenClangAsFlags            bool     `json:",omitempty"`
 	BuildBrokenClangCFlags             bool     `json:",omitempty"`
 	BuildBrokenClangProperty           bool     `json:",omitempty"`
-	BuildBrokenDepfile                 *bool    `json:",omitempty"`
+	GenruleSandboxing                  *bool    `json:",omitempty"`
 	BuildBrokenEnforceSyspropOwner     bool     `json:",omitempty"`
 	BuildBrokenTrebleSyspropNeverallow bool     `json:",omitempty"`
 	BuildBrokenUsesSoongPython2Modules bool     `json:",omitempty"`
@@ -473,6 +474,9 @@
 	ProductManufacturer string   `json:",omitempty"`
 	ProductBrand        string   `json:",omitempty"`
 	BuildVersionTags    []string `json:",omitempty"`
+
+	ReleaseVersion               string   `json:",omitempty"`
+	ReleaseDeviceConfigValueSets []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
@@ -844,6 +848,9 @@
 		// 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++ {
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 61058df..9623a8b 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -242,7 +242,7 @@
 			fmt.Fprintln(w, ".PHONY:", name, "sdk_repo", "sdk-repo-"+name)
 			fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", strings.Join(s.FilesToInstall().Strings(), " "))
 
-			fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-$(FILE_NAME_TAG).zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName)
+			fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-FILE_NAME_TAG_PLACEHOLDER.zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName)
 		},
 	}
 }
diff --git a/apex/Android.bp b/apex/Android.bp
index 61d7fb2..0791497 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -29,7 +29,6 @@
         "bp2build.go",
         "deapexer.go",
         "key.go",
-        "metadata.go",
         "prebuilt.go",
         "testing.go",
         "vndk.go",
@@ -39,7 +38,6 @@
         "bootclasspath_fragment_test.go",
         "classpath_element_test.go",
         "dexpreopt_bootjars_test.go",
-        "metadata_test.go",
         "platform_bootclasspath_test.go",
         "systemserver_classpath_fragment_test.go",
         "vndk_test.go",
diff --git a/apex/apex.go b/apex/apex.go
index 026734e..f49492e 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -404,7 +404,7 @@
 	///////////////////////////////////////////////////////////////////////////////////////////
 	// Inputs
 
-	// Keys for apex_paylaod.img
+	// Keys for apex_payload.img
 	publicKeyFile  android.Path
 	privateKeyFile android.Path
 
@@ -1871,14 +1871,17 @@
 	}); ok {
 		af.overriddenPackageName = app.OverriddenManifestPackageName()
 	}
-	apexFiles := []apexFile{af}
+
+	apexFiles := []apexFile{}
 
 	if allowlist := aapp.PrivAppAllowlist(); allowlist.Valid() {
 		dirInApex := filepath.Join("etc", "permissions")
-		privAppAllowlist := newApexFile(ctx, allowlist.Path(), aapp.BaseModuleName()+"privapp", dirInApex, etc, aapp)
+		privAppAllowlist := newApexFile(ctx, allowlist.Path(), aapp.BaseModuleName()+"_privapp", dirInApex, etc, aapp)
 		apexFiles = append(apexFiles, privAppAllowlist)
 	}
 
+	apexFiles = append(apexFiles, af)
+
 	return apexFiles
 }
 
@@ -2606,8 +2609,45 @@
 	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) })
 	vctx.normalizeFileInfo(ctx)
 	if a.privateKeyFile == nil {
-		ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key))
-		return
+		if ctx.Config().AllowMissingDependencies() {
+			// TODO(b/266099037): a better approach for slim manifests.
+			ctx.AddMissingDependencies([]string{String(a.overridableProperties.Key)})
+			// Create placeholder paths for later stages that expect to see those paths,
+			// though they won't be used.
+			var unusedPath = android.PathForModuleOut(ctx, "nonexistentprivatekey")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.ErrorRule,
+				Output: unusedPath,
+				Args: map[string]string{
+					"error": "Private key not available",
+				},
+			})
+			a.privateKeyFile = unusedPath
+		} else {
+			ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key))
+			return
+		}
+	}
+
+	if a.publicKeyFile == nil {
+		if ctx.Config().AllowMissingDependencies() {
+			// TODO(b/266099037): a better approach for slim manifests.
+			ctx.AddMissingDependencies([]string{String(a.overridableProperties.Key)})
+			// Create placeholder paths for later stages that expect to see those paths,
+			// though they won't be used.
+			var unusedPath = android.PathForModuleOut(ctx, "nonexistentpublickey")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.ErrorRule,
+				Output: unusedPath,
+				Args: map[string]string{
+					"error": "Public key not available",
+				},
+			})
+			a.publicKeyFile = unusedPath
+		} else {
+			ctx.PropertyErrorf("key", "public_key for %q could not be found", String(a.overridableProperties.Key))
+			return
+		}
 	}
 
 	////////////////////////////////////////////////////////////////////////////////////////////
@@ -3236,7 +3276,7 @@
 	//
 	// Module separator
 	//
-	m["com.android.cellbroadcast"] = []string{"CellBroadcastApp", "CellBroadcastServiceModule"}
+	m["com.android.cellbroadcast"] = []string{}
 	//
 	// Module separator
 	//
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index ebc35cf..a63344f 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -27,7 +27,7 @@
 }
 
 func registerApexDepsInfoComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory)
+	ctx.RegisterParallelSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory)
 }
 
 type apexDepsInfoSingleton struct {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 6ca5afb..38e24e8 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1368,6 +1368,8 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
+			static_libs: ["libstatic"],
+			shared_libs: ["libshared"],
 			runtime_libs: ["libfoo", "libbar"],
 			system_shared_libs: [],
 			stl: "none",
@@ -1392,6 +1394,39 @@
 			apex_available: [ "myapex" ],
 		}
 
+		cc_library {
+			name: "libstatic",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			runtime_libs: ["libstatic_to_runtime"],
+		}
+
+		cc_library {
+			name: "libshared",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			runtime_libs: ["libshared_to_runtime"],
+		}
+
+		cc_library {
+			name: "libstatic_to_runtime",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "libshared_to_runtime",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
 	`)
 
 	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
@@ -1405,11 +1440,14 @@
 
 	// Ensure that runtime_libs dep in included
 	ensureContains(t, copyCmds, "image.apex/lib64/libbar.so")
+	ensureContains(t, copyCmds, "image.apex/lib64/libshared.so")
+	ensureContains(t, copyCmds, "image.apex/lib64/libshared_to_runtime.so")
+
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libstatic_to_runtime.so")
 
 	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
 	ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
 	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.so")
-
 }
 
 var prepareForTestOfRuntimeApexWithHwasan = android.GroupFixturePreparers(
@@ -6222,8 +6260,7 @@
 			sdk_version: "current",
 			system_modules: "none",
 			privileged: true,
-			privapp_allowlist: "perms.xml",
-			package_name: "com.android.AppFooPriv",
+			privapp_allowlist: "privapp_allowlist_com.android.AppFooPriv.xml",
 			stl: "none",
 			apex_available: [ "myapex" ],
 		}
@@ -6268,6 +6305,18 @@
 		// ... and not directly inside the APEX
 		ensureNotContains(t, copyCmds, "image.apex/lib64/"+jni+".so")
 	}
+
+	apexBundle := module.Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
+	var builder strings.Builder
+	data.Custom(&builder, apexBundle.Name(), "TARGET_", "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_MODULE := AppFooPriv.myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE := AppFoo.myapex")
+	ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALLED_MODULE := \\S+AppFooPriv.apk")
+	ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALLED_MODULE := \\S+AppFoo.apk")
+	ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS := \\S+AppFooPriv.apk")
+	ensureContains(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS := privapp_allowlist_com.android.AppFooPriv.xml:$(PRODUCT_OUT)/apex/myapex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml")
 }
 
 func TestApexWithAppImportBuildId(t *testing.T) {
@@ -6541,6 +6590,72 @@
 	}`)
 }
 
+func TestApexAvailable_IndirectStaticDep(t *testing.T) {
+	testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		static_libs: ["libbar"],
+		system_shared_libs: [],
+		apex_available: ["myapex"],
+	}
+
+	cc_library {
+		name: "libbar",
+		stl: "none",
+		shared_libs: ["libbaz"],
+		system_shared_libs: [],
+		apex_available: ["myapex"],
+	}
+
+	cc_library {
+		name: "libbaz",
+		stl: "none",
+		system_shared_libs: [],
+	}`)
+
+	testApexError(t, `requires "libbar" that doesn't list the APEX under 'apex_available'.`, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		static_libs: ["libbar"],
+		system_shared_libs: [],
+		apex_available: ["myapex"],
+	}
+
+	cc_library {
+		name: "libbar",
+		stl: "none",
+		system_shared_libs: [],
+	}`)
+}
+
 func TestApexAvailable_InvalidApexName(t *testing.T) {
 	testApexError(t, "\"otherapex\" is not a valid module name", `
 	apex {
diff --git a/apex/key.go b/apex/key.go
index 0a7e80f..3010d76 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -33,7 +33,7 @@
 
 func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
-	ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
+	ctx.RegisterParallelSingletonType("apex_keys_text", apexKeysTextFactory)
 }
 
 type apexKey struct {
diff --git a/apex/metadata.go b/apex/metadata.go
deleted file mode 100644
index b1dff3e..0000000
--- a/apex/metadata.go
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package apex
-
-import (
-	"encoding/json"
-
-	"github.com/google/blueprint"
-
-	"android/soong/android"
-)
-
-var (
-	mtctx = android.NewPackageContext("android/soong/multitree_apex")
-)
-
-func init() {
-	RegisterModulesSingleton(android.InitRegistrationContext)
-}
-
-func RegisterModulesSingleton(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("apex_multitree_singleton", multitreeAnalysisSingletonFactory)
-}
-
-var PrepareForTestWithApexMultitreeSingleton = android.FixtureRegisterWithContext(RegisterModulesSingleton)
-
-func multitreeAnalysisSingletonFactory() android.Singleton {
-	return &multitreeAnalysisSingleton{}
-}
-
-type multitreeAnalysisSingleton struct {
-	multitreeApexMetadataPath android.OutputPath
-}
-
-type ApexMultitreeMetadataEntry struct {
-	// The name of the apex.
-	Name string
-
-	// TODO: Add other properties as needed.
-}
-
-type ApexMultitreeMetadata struct {
-	// Information about the installable apexes.
-	Apexes map[string]ApexMultitreeMetadataEntry
-}
-
-func (p *multitreeAnalysisSingleton) GenerateBuildActions(context android.SingletonContext) {
-	data := ApexMultitreeMetadata{
-		Apexes: make(map[string]ApexMultitreeMetadataEntry, 0),
-	}
-	context.VisitAllModules(func(module android.Module) {
-		// If this module is not being installed, ignore it.
-		if !module.Enabled() || module.IsSkipInstall() {
-			return
-		}
-		// Actual apexes provide ApexBundleInfoProvider.
-		if _, ok := context.ModuleProvider(module, ApexBundleInfoProvider).(ApexBundleInfo); !ok {
-			return
-		}
-		bundle, ok := module.(*apexBundle)
-		if ok && !bundle.testApex && !bundle.vndkApex && bundle.primaryApexType {
-			name := module.Name()
-			entry := ApexMultitreeMetadataEntry{
-				Name: name,
-			}
-			data.Apexes[name] = entry
-		}
-	})
-	p.multitreeApexMetadataPath = android.PathForOutput(context, "multitree_apex_metadata.json")
-
-	jsonStr, err := json.Marshal(data)
-	if err != nil {
-		context.Errorf(err.Error())
-	}
-	android.WriteFileRule(context, p.multitreeApexMetadataPath, string(jsonStr))
-	// This seems cleaner, but doesn't emit the phony rule in testing.
-	// context.Phony("multitree_apex_metadata", p.multitreeApexMetadataPath)
-
-	context.Build(mtctx, android.BuildParams{
-		Rule:        blueprint.Phony,
-		Description: "phony rule for multitree_apex_metadata",
-		Inputs:      []android.Path{p.multitreeApexMetadataPath},
-		Output:      android.PathForPhony(context, "multitree_apex_metadata"),
-	})
-}
diff --git a/apex/metadata_test.go b/apex/metadata_test.go
deleted file mode 100644
index fed5bea..0000000
--- a/apex/metadata_test.go
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package apex
-
-import (
-	"strings"
-	"testing"
-
-	"android/soong/android"
-	"android/soong/java"
-)
-
-func TestModulesSingleton(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		PrepareForTestWithApexMultitreeSingleton,
-		java.PrepareForTestWithJavaDefaultModules,
-		PrepareForTestWithApexBuildComponents,
-		java.FixtureConfigureApexBootJars("myapex:foo"),
-		java.PrepareForTestWithJavaSdkLibraryFiles,
-	).RunTestWithBp(t, `
-		prebuilt_apex {
-			name: "myapex",
-			src: "myapex.apex",
-			exported_bootclasspath_fragments: ["mybootclasspath-fragment"],
-		}
-
-		// A prebuilt java_sdk_library_import that is not preferred by default but will be preferred
-		// because AlwaysUsePrebuiltSdks() is true.
-		java_sdk_library_import {
-			name: "foo",
-			prefer: false,
-			shared_library: false,
-			permitted_packages: ["foo"],
-			public: {
-				jars: ["sdk_library/public/foo-stubs.jar"],
-				stub_srcs: ["sdk_library/public/foo_stub_sources"],
-				current_api: "sdk_library/public/foo.txt",
-				removed_api: "sdk_library/public/foo-removed.txt",
-				sdk_version: "current",
-			},
-			apex_available: ["myapex"],
-		}
-
-		prebuilt_bootclasspath_fragment {
-			name: "mybootclasspath-fragment",
-			apex_available: [
-				"myapex",
-			],
-			contents: [
-				"foo",
-			],
-			hidden_api: {
-				stub_flags: "prebuilt-stub-flags.csv",
-				annotation_flags: "prebuilt-annotation-flags.csv",
-				metadata: "prebuilt-metadata.csv",
-				index: "prebuilt-index.csv",
-				all_flags: "prebuilt-all-flags.csv",
-			},
-		}
-
-		platform_bootclasspath {
-			name: "myplatform-bootclasspath",
-			fragments: [
-				{
-					apex: "myapex",
-					module:"mybootclasspath-fragment",
-				},
-			],
-		}
-`,
-	)
-
-	outputs := result.SingletonForTests("apex_multitree_singleton").AllOutputs()
-	for _, output := range outputs {
-		testingBuildParam := result.SingletonForTests("apex_multitree_singleton").Output(output)
-		switch {
-		case strings.Contains(output, "soong/multitree_apex_metadata.json"):
-			android.AssertStringEquals(t, "Invalid build rule", "android/soong/android.writeFile", testingBuildParam.Rule.String())
-			android.AssertIntEquals(t, "Invalid input", len(testingBuildParam.Inputs), 0)
-			android.AssertStringDoesContain(t, "Invalid output path", output, "soong/multitree_apex_metadata.json")
-
-		case strings.HasSuffix(output, "multitree_apex_metadata"):
-			android.AssertStringEquals(t, "Invalid build rule", "<builtin>:phony", testingBuildParam.Rule.String())
-			android.AssertStringEquals(t, "Invalid input", testingBuildParam.Inputs[0].String(), "out/soong/multitree_apex_metadata.json")
-			android.AssertStringEquals(t, "Invalid output path", output, "multitree_apex_metadata")
-			android.AssertIntEquals(t, "Invalid args", len(testingBuildParam.Args), 0)
-		}
-	}
-}
diff --git a/bazel/configurability.go b/bazel/configurability.go
index d042fe8..8f63ec4 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -31,11 +31,11 @@
 
 	// OsType names in arch.go
 	OsAndroid     = "android"
-	osDarwin      = "darwin"
-	osLinux       = "linux_glibc"
+	OsDarwin      = "darwin"
+	OsLinux       = "linux_glibc"
 	osLinuxMusl   = "linux_musl"
 	osLinuxBionic = "linux_bionic"
-	osWindows     = "windows"
+	OsWindows     = "windows"
 
 	// Targets in arch.go
 	osArchAndroidArm        = "android_arm"
@@ -156,11 +156,11 @@
 	// constraint_value for the @platforms//os:os constraint_setting
 	platformOsMap = map[string]string{
 		OsAndroid:                  "//build/bazel/platforms/os:android",
-		osDarwin:                   "//build/bazel/platforms/os:darwin",
-		osLinux:                    "//build/bazel/platforms/os:linux_glibc",
+		OsDarwin:                   "//build/bazel/platforms/os:darwin",
+		OsLinux:                    "//build/bazel/platforms/os:linux_glibc",
 		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
 		osLinuxBionic:              "//build/bazel/platforms/os:linux_bionic",
-		osWindows:                  "//build/bazel/platforms/os:windows",
+		OsWindows:                  "//build/bazel/platforms/os:windows",
 		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
 	}
 
@@ -192,22 +192,22 @@
 	// in a cyclic dependency.
 	osToArchMap = map[string][]string{
 		OsAndroid:     {archArm, archArm64, archRiscv64, archX86, archX86_64},
-		osLinux:       {archX86, archX86_64},
+		OsLinux:       {archX86, archX86_64},
 		osLinuxMusl:   {archX86, archX86_64},
-		osDarwin:      {archArm64, archX86_64},
+		OsDarwin:      {archArm64, archX86_64},
 		osLinuxBionic: {archArm64, archX86_64},
 		// TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well.
-		osWindows: {archX86, archX86_64},
+		OsWindows: {archX86, archX86_64},
 	}
 
 	osAndInApexMap = map[string]string{
 		AndroidAndInApex:           "//build/bazel/rules/apex:android-in_apex",
 		AndroidPlatform:            "//build/bazel/rules/apex:system",
-		osDarwin:                   "//build/bazel/platforms/os:darwin",
-		osLinux:                    "//build/bazel/platforms/os:linux_glibc",
+		OsDarwin:                   "//build/bazel/platforms/os:darwin",
+		OsLinux:                    "//build/bazel/platforms/os:linux_glibc",
 		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
 		osLinuxBionic:              "//build/bazel/platforms/os:linux_bionic",
-		osWindows:                  "//build/bazel/platforms/os:windows",
+		OsWindows:                  "//build/bazel/platforms/os:windows",
 		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
 	}
 
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 6a3b3c8..ca96f23 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -8,7 +8,6 @@
 
 var (
 	GetOutputFiles      = &getOutputFilesRequestType{}
-	GetPythonBinary     = &getPythonBinaryRequestType{}
 	GetCcInfo           = &getCcInfoType{}
 	GetApexInfo         = &getApexInfoType{}
 	GetCcUnstrippedInfo = &getCcUnstrippedInfoType{}
@@ -45,8 +44,6 @@
 
 type getOutputFilesRequestType struct{}
 
-type getPythonBinaryRequestType struct{}
-
 // Name returns a string name for this request type. Such request type names must be unique,
 // and must only consist of alphanumeric characters.
 func (g getOutputFilesRequestType) Name() string {
@@ -72,31 +69,6 @@
 	return splitOrEmpty(rawString, ", ")
 }
 
-// Name returns a string name for this request type. Such request type names must be unique,
-// and must only consist of alphanumeric characters.
-func (g getPythonBinaryRequestType) Name() string {
-	return "getPythonBinary"
-}
-
-// StarlarkFunctionBody returns a starlark function body to process this request type.
-// The returned string is the body of a Starlark function which obtains
-// all request-relevant information about a target and returns a string containing
-// this information.
-// The function should have the following properties:
-//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
-//   - The return value must be a string.
-//   - The function body should not be indented outside of its own scope.
-func (g getPythonBinaryRequestType) StarlarkFunctionBody() string {
-	return "return providers(target)['FilesToRunProvider'].executable.path"
-}
-
-// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
-// The given rawString must correspond to the string output which was created by evaluating the
-// Starlark given in StarlarkFunctionBody.
-func (g getPythonBinaryRequestType) ParseResult(rawString string) string {
-	return rawString
-}
-
 type getCcInfoType struct{}
 
 // Name returns a string name for this request type. Such request type names must be unique,
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 7003ce1..e772bb7 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -40,34 +40,6 @@
 	}
 }
 
-func TestGetPythonBinaryParseResults(t *testing.T) {
-	t.Parallel()
-	testCases := []struct {
-		description    string
-		input          string
-		expectedOutput string
-	}{
-		{
-			description:    "no result",
-			input:          "",
-			expectedOutput: "",
-		},
-		{
-			description:    "one result",
-			input:          "test",
-			expectedOutput: "test",
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.description, func(t *testing.T) {
-			actualOutput := GetPythonBinary.ParseResult(tc.input)
-			if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-				t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
-			}
-		})
-	}
-}
-
 func TestGetCcInfoParseResults(t *testing.T) {
 	t.Parallel()
 	testCases := []struct {
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index 50f241f..3cff60f 100644
--- a/bloaty/bloaty.go
+++ b/bloaty/bloaty.go
@@ -51,7 +51,7 @@
 	pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
 	pctx.SourcePathVariable("bloaty", "prebuilts/build-tools/${hostPrebuiltTag}/bin/bloaty")
 	pctx.HostBinToolVariable("bloatyMerger", "bloaty_merger")
-	android.RegisterSingletonType("file_metrics", fileSizesSingleton)
+	android.RegisterParallelSingletonType("file_metrics", fileSizesSingleton)
 	fileSizeMeasurerKey = blueprint.NewProvider(measuredFiles{})
 }
 
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index 3abef9d..66d0cc5 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -68,6 +68,14 @@
 	"@//build/bazel/product_config:__subpackages__",
 	"@soong_injection//product_config_platforms:__subpackages__",
 ])
+
+load("//{PRODUCT_FOLDER}:soong.variables.bzl", _soong_variables = "variables")
+load("@//build/bazel/product_config:android_product.bzl", "android_product")
+
+android_product(
+    name = "current_product-{VARIANT}",
+    soong_variables = _soong_variables,
+)
 `)),
 		newFile(
 			"product_config_platforms",
@@ -78,6 +86,7 @@
 # TODO: When we start generating the platforms for more than just the
 # currently lunched product, they should all be listed here
 product_labels = [
+  "@soong_injection//product_config_platforms:current_product-{VARIANT}",
   "@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}"
 ]
 `)),
@@ -85,25 +94,30 @@
 			"product_config_platforms",
 			"common.bazelrc",
 			productReplacer.Replace(`
-build --platforms @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+# current_product refers to the current TARGET_PRODUCT set, usually through
+# 'lunch' or 'banchan'.  Every build will have a primary TARGET_PRODUCT, but
+# bazel supports using other products in tests or configuration transitions. The
+# other products can be found in
+# @soong_injection//product_config_platforms/products/...
+build --platforms @soong_injection//product_config_platforms:current_product-{VARIANT}_linux_x86_64
 
-build:android --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
-build:linux_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
-build:linux_bionic_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_bionic_x86_64
-build:linux_musl_x86 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86
-build:linux_musl_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86_64
+build:android --platforms=@soong_injection//product_config_platforms:current_product-{VARIANT}
+build:linux_x86_64 --platforms=@soong_injection//product_config_platforms:current_product-{VARIANT}_linux_x86_64
+build:linux_bionic_x86_64 --platforms=@soong_injection//product_config_platforms:current_product-{VARIANT}_linux_bionic_x86_64
+build:linux_musl_x86 --platforms=@soong_injection//product_config_platforms:current_product-{VARIANT}_linux_musl_x86
+build:linux_musl_x86_64 --platforms=@soong_injection//product_config_platforms:current_product-{VARIANT}_linux_musl_x86_64
 `)),
 		newFile(
 			"product_config_platforms",
 			"linux.bazelrc",
 			productReplacer.Replace(`
-build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+build --host_platform @soong_injection//product_config_platforms:current_product-{VARIANT}_linux_x86_64
 `)),
 		newFile(
 			"product_config_platforms",
 			"darwin.bazelrc",
 			productReplacer.Replace(`
-build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_darwin_x86_64
+build --host_platform product_config_platforms:current_product-{VARIANT}_darwin_x86_64
 `)),
 		newFile(
 			"product_config_platforms",
@@ -111,7 +125,7 @@
 			productReplacer.Replace(`
 flags:
   --cpu=k8
-    @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
+	@soong_injection//product_config_platforms:current_product-{VARIANT}
 `)),
 	}
 
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index 89eac8a..8a83cc0 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -222,6 +222,7 @@
         "-Wl,--version-script,$(location vs)",
         "-Wl,--dynamic-list,$(location dynamic.list)",
     ]`,
+				"features": `["android_cfi_exports_map"]`,
 			},
 			},
 		},
@@ -249,6 +250,7 @@
         "version_script",
         "dynamic.list",
     ]`,
+				"features": `["android_cfi_exports_map"]`,
 				"linkopts": `[
         "--nospace_flag",
         "-z",
@@ -644,10 +646,7 @@
 		targets: []testBazelTarget{
 			{"cc_binary", "foo", AttrNameToString{
 				"features": `select({
-        "//build/bazel/platforms/arch:arm": [
-            "arm_isa_arm",
-            "-arm_isa_thumb",
-        ],
+        "//build/bazel/platforms/arch:arm": ["arm_isa_arm"],
         "//conditions:default": [],
     })`,
 				"local_includes": `["."]`,
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 1b681ef..3dd9373 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"testing"
 
+	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/cc"
 )
@@ -63,6 +64,7 @@
 	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
 	ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
 	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+	ctx.RegisterModuleType("aidl_library", aidl_library.AidlLibraryFactory)
 }
 
 func TestCcLibrarySimple(t *testing.T) {
@@ -898,7 +900,8 @@
         "-Wl,--version-script,$(location v.map)",
         "-Wl,--dynamic-list,$(location dynamic.list)",
     ]`,
-			"srcs": `["a.cpp"]`,
+			"srcs":     `["a.cpp"]`,
+			"features": `["android_cfi_exports_map"]`,
 		}),
 	},
 	)
@@ -956,6 +959,11 @@
         "//conditions:default": [],
     })`,
 			"srcs": `["a.cpp"]`,
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": ["android_cfi_exports_map"],
+        "//build/bazel/platforms/arch:arm64": ["android_cfi_exports_map"],
+        "//conditions:default": [],
+    })`,
 		}),
 	},
 	)
@@ -983,12 +991,15 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features": `["android_cfi_exports_map"]`,
+			}),
 			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"additional_linker_inputs": `[
         "version_script",
         "dynamic.list",
     ]`,
+				"features": `["android_cfi_exports_map"]`,
 				"linkopts": `[
         "--nospace_flag",
         "-z",
@@ -3219,10 +3230,7 @@
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo", AttrNameToString{
 			"features": `select({
-        "//build/bazel/platforms/arch:arm": [
-            "arm_isa_arm",
-            "-arm_isa_thumb",
-        ],
+        "//build/bazel/platforms/arch:arm": ["arm_isa_arm"],
         "//conditions:default": [],
     })`,
 			"local_includes": `["."]`,
@@ -3315,6 +3323,51 @@
 	})
 }
 
+func TestCcLibraryWithAidlLibrary(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with aidl_library",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+aidl_library {
+    name: "A_aidl",
+    srcs: ["aidl/A.aidl"],
+	hdrs: ["aidl/Header.aidl"],
+	strip_import_prefix: "aidl",
+}
+cc_library {
+	name: "foo",
+	aidl: {
+		libs: ["A_aidl"],
+	},
+	export_include_dirs: ["include"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("aidl_library", "A_aidl", AttrNameToString{
+				"srcs":                `["aidl/A.aidl"]`,
+				"hdrs":                `["aidl/Header.aidl"]`,
+				"strip_import_prefix": `"aidl"`,
+				"tags":                `["apex_available=//apex_available:anyapex"]`,
+			}),
+			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+				"deps":            `[":A_aidl"]`,
+				"local_includes":  `["."]`,
+				"export_includes": `["include"]`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":                    `["."]`,
+				"export_includes":                   `["include"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":                    `["."]`,
+				"export_includes":                   `["include"]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibraryWithAidlSrcs(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library with aidl srcs",
@@ -3343,6 +3396,7 @@
 				"srcs": `["B.aidl"]`,
 			}),
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+				"local_includes": `["."]`,
 				"deps": `[
         ":A_aidl",
         ":foo_aidl_library",
@@ -3382,7 +3436,8 @@
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
-				"deps": `["//path/to/A:A_aidl"]`,
+				"local_includes": `["."]`,
+				"deps":           `["//path/to/A:A_aidl"]`,
 			}),
 			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
@@ -3397,37 +3452,78 @@
 }
 
 func TestCcLibraryWithExportAidlHeaders(t *testing.T) {
-	runCcLibraryTestCase(t, Bp2buildTestCase{
-		Description:                "cc_library with export aidl headers",
-		ModuleTypeUnderTest:        "cc_library",
-		ModuleTypeUnderTestFactory: cc.LibraryFactory,
-		Blueprint: `
-cc_library {
-    name: "foo",
-    srcs: [
-        "Foo.aidl",
-    ],
-    aidl: {
-        export_aidl_headers: true,
-    }
-}`,
-		ExpectedBazelTargets: []string{
-			MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
-				"srcs": `["Foo.aidl"]`,
-			}),
-			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
-				"deps": `[":foo_aidl_library"]`,
-			}),
-			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
-				"whole_archive_deps": `[":foo_cc_aidl_library"]`,
-				"local_includes":     `["."]`,
-			}),
-			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
-				"whole_archive_deps": `[":foo_cc_aidl_library"]`,
-				"local_includes":     `["."]`,
-			}),
+	t.Parallel()
+
+	expectedBazelTargets := []string{
+		MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+			"local_includes": `["."]`,
+			"deps":           `[":foo_aidl_library"]`,
+		}),
+		MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+			"whole_archive_deps": `[":foo_cc_aidl_library"]`,
+			"local_includes":     `["."]`,
+		}),
+		MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			"whole_archive_deps": `[":foo_cc_aidl_library"]`,
+			"local_includes":     `["."]`,
+		}),
+	}
+	testCases := []struct {
+		description          string
+		bp                   string
+		expectedBazelTargets []string
+	}{
+		{
+			description: "cc_library with aidl srcs and aidl.export_aidl_headers set",
+			bp: `
+			cc_library {
+				name: "foo",
+				srcs: [
+					"Foo.aidl",
+				],
+				aidl: {
+					export_aidl_headers: true,
+				}
+			}`,
+			expectedBazelTargets: append(
+				expectedBazelTargets,
+				MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
+					"srcs": `["Foo.aidl"]`,
+				})),
 		},
-	})
+		{
+			description: "cc_library with aidl.libs and aidl.export_aidl_headers set",
+			bp: `
+			aidl_library {
+				name: "foo_aidl_library",
+				srcs: ["Foo.aidl"],
+			}
+			cc_library {
+				name: "foo",
+				aidl: {
+					libs: ["foo_aidl_library"],
+					export_aidl_headers: true,
+				}
+			}`,
+			expectedBazelTargets: append(
+				expectedBazelTargets,
+				MakeBazelTargetNoRestrictions("aidl_library", "foo_aidl_library", AttrNameToString{
+					"srcs": `["Foo.aidl"]`,
+					"tags": `["apex_available=//apex_available:anyapex"]`,
+				}),
+			),
+		},
+	}
+
+	for _, testCase := range testCases {
+		runCcLibraryTestCase(t, Bp2buildTestCase{
+			Description:                "cc_library with export aidl headers",
+			ModuleTypeUnderTest:        "cc_library",
+			ModuleTypeUnderTestFactory: cc.LibraryFactory,
+			Blueprint:                  testCase.bp,
+			ExpectedBazelTargets:       testCase.expectedBazelTargets,
+		})
+	}
 }
 
 func TestCcLibraryWithTargetApex(t *testing.T) {
@@ -3642,7 +3738,8 @@
 				"srcs": `["Foo.aidl"]`,
 			}),
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
-				"deps": `[":foo_aidl_library"]`,
+				"local_includes": `["."]`,
+				"deps":           `[":foo_aidl_library"]`,
 				"implementation_deps": `[
         ":baz-static",
         ":bar-static",
@@ -4519,3 +4616,105 @@
 	},
 	)
 }
+
+func TestCcLibraryYaccConversion(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library is built from .y/.yy files",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcLibraryPreamble + `cc_library {
+    name: "a",
+    srcs: [
+	"a.cpp",
+	"a.yy",
+    ],
+    shared_libs: ["sharedlib"],
+    static_libs: ["staticlib"],
+    yacc: {
+	    flags: ["someYaccFlag"],
+	    gen_location_hh: true,
+	    gen_position_hh: true,
+	},
+}
+cc_library_static {
+	name: "staticlib",
+	bazel_module: { bp2build_available: false },
+}
+cc_library {
+	name: "sharedlib",
+	bazel_module: { bp2build_available: false },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_yacc_static_library", "a_yacc", AttrNameToString{
+				"src":                         `"a.yy"`,
+				"implementation_deps":         `[":staticlib"]`,
+				"implementation_dynamic_deps": `[":sharedlib"]`,
+				"flags":                       `["someYaccFlag"]`,
+				"gen_location_hh":             "True",
+				"gen_position_hh":             "True",
+				"local_includes":              `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+				"srcs":                              `["a.cpp"]`,
+				"implementation_deps":               `[":staticlib"]`,
+				"implementation_dynamic_deps":       `[":sharedlib"]`,
+				"implementation_whole_archive_deps": `[":a_yacc"]`,
+				"local_includes":                    `["."]`,
+			}),
+			MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+				"srcs":                              `["a.cpp"]`,
+				"implementation_deps":               `[":staticlib"]`,
+				"implementation_dynamic_deps":       `[":sharedlib"]`,
+				"implementation_whole_archive_deps": `[":a_yacc"]`,
+				"local_includes":                    `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryHostLdLibs(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_binary linker flags for host_ldlibs",
+		ModuleTypeUnderTest:        "cc_binary",
+		ModuleTypeUnderTestFactory: cc.BinaryFactory,
+		Blueprint: soongCcLibraryPreamble + `cc_binary {
+    name: "a",
+    host_supported: true,
+    ldflags: ["-lcommon"],
+    target: {
+	linux: {
+		host_ldlibs: [
+			"-llinux",
+		],
+	},
+	darwin: {
+		ldflags: ["-ldarwinadditional"],
+		host_ldlibs: [
+			"-ldarwin",
+		],
+	},
+	windows: {
+		host_ldlibs: [
+			"-lwindows",
+		],
+	},
+    },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("cc_binary", "a", AttrNameToString{
+				"linkopts": `["-lcommon"] + select({
+        "//build/bazel/platforms/os:darwin": [
+            "-ldarwinadditional",
+            "-ldarwin",
+        ],
+        "//build/bazel/platforms/os:linux_glibc": ["-llinux"],
+        "//build/bazel/platforms/os:windows": ["-lwindows"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 2ee9c99..6c9f9a1 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -362,6 +362,7 @@
         "-Wl,--version-script,$(location version_script)",
         "-Wl,--dynamic-list,$(location dynamic.list)",
     ]`,
+				"features": `["android_cfi_exports_map"]`,
 			}),
 		},
 	})
@@ -398,6 +399,7 @@
         "-Wl,--version-script,$(location version_script)",
         "-Wl,--dynamic-list,$(location dynamic.list)",
     ]`,
+				"features": `["android_cfi_exports_map"]`,
 			}),
 		},
 	})
@@ -913,6 +915,7 @@
         "header.h",
     ]`,
 				"linkopts": `["-Wl,--version-script,$(location version_script)"]`,
+				"features": `["android_cfi_exports_map"]`,
 			}),
 		},
 	})
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 60766f4..fd92e95 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -826,3 +826,43 @@
 		},
 	})
 }
+
+func TestJavaLibraryJavaResourcesSingleFilegroup(t *testing.T) {
+	runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{
+		Filesystem: map[string]string{
+			"res/a.res":      "",
+			"res/b.res":      "",
+			"res/dir1/b.res": "",
+		},
+		Description: "java_library",
+		Blueprint: `java_library {
+    name: "java-lib-1",
+    srcs: ["a.java"],
+    java_resources: [":filegroup1"],
+    bazel_module: { bp2build_available: true },
+}
+
+filegroup {
+    name: "filegroup1",
+    path: "foo",
+    srcs: ["foo/a", "foo/b"],
+}
+
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+				"srcs":                  `["a.java"]`,
+				"resources":             `[":filegroup1"]`,
+				"resource_strip_prefix": `"foo"`,
+			}),
+			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+			MakeBazelTargetNoRestrictions("filegroup", "filegroup1", AttrNameToString{
+				"srcs": `[
+        "foo/a",
+        "foo/b",
+    ]`}),
+		},
+	}, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	})
+}
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index ad07f68..813773d 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -200,6 +200,53 @@
 )`}})
 }
 
+func TestSoongConfigModuleType_MultipleBoolVar_PartialUseNotPanic(t *testing.T) {
+	bp := `
+soong_config_bool_variable {
+	name: "feature1",
+}
+
+soong_config_bool_variable {
+	name: "feature2",
+}
+
+soong_config_module_type {
+	name: "custom_cc_library_static",
+	module_type: "cc_library_static",
+	config_namespace: "acme",
+	variables: ["feature1", "feature2",],
+	properties: ["cflags"],
+}
+
+custom_cc_library_static {
+	name: "foo",
+	bazel_module: { bp2build_available: true },
+	host_supported: true,
+	soong_config_variables: {
+		feature1: {
+			conditions_default: {
+				cflags: ["-DDEFAULT1"],
+			},
+			cflags: ["-DFEATURE1"],
+		},
+	},
+}`
+
+	runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+		Description:                "soong config variables - used part of multiple bool variable do not panic",
+		ModuleTypeUnderTest:        "cc_library_static",
+		ModuleTypeUnderTestFactory: cc.LibraryStaticFactory,
+		Blueprint:                  bp,
+		ExpectedBazelTargets: []string{`cc_library_static(
+    name = "foo",
+    copts = select({
+        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
+        "//conditions:default": ["-DDEFAULT1"],
+    }),
+    local_includes = ["."],
+)`}})
+}
+
 func TestSoongConfigModuleType_StringAndBoolVar(t *testing.T) {
 	bp := `
 soong_config_bool_variable {
diff --git a/cc/Android.bp b/cc/Android.bp
index be2cc5a..f49dc1a 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "blueprint-pathtools",
         "soong",
+        "soong-aidl-library",
         "soong-android",
         "soong-bazel",
         "soong-cc-config",
@@ -22,7 +23,6 @@
     srcs: [
         "afdo.go",
         "fdo_profile.go",
-
         "androidmk.go",
         "api_level.go",
         "bp2build.go",
diff --git a/cc/bp2build.go b/cc/bp2build.go
index cf5f74d..259ba39 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -38,6 +38,7 @@
 	protoSrcPartition   = "proto"
 	aidlSrcPartition    = "aidl"
 	syspropSrcPartition = "sysprop"
+	yaccSrcPartition    = "yacc"
 
 	stubsSuffix = "_stub_libs_current"
 )
@@ -154,6 +155,7 @@
 		// know the language of these sources until the genrule is executed.
 		cppSrcPartition:     bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
 		syspropSrcPartition: bazel.LabelPartition{Extensions: []string{".sysprop"}},
+		yaccSrcPartition:    bazel.LabelPartition{Extensions: []string{".y", "yy"}},
 	}
 
 	return bazel.PartitionLabelListAttribute(ctx, &srcs, labels)
@@ -404,6 +406,12 @@
 	// Sysprop sources
 	syspropSrcs bazel.LabelListAttribute
 
+	// Yacc sources
+	yaccSrc               *bazel.LabelAttribute
+	yaccFlags             bazel.StringListAttribute
+	yaccGenLocationHeader bazel.BoolAttribute
+	yaccGenPositionHeader bazel.BoolAttribute
+
 	hdrs bazel.LabelListAttribute
 
 	rtti bazel.BoolAttribute
@@ -492,7 +500,7 @@
 
 	instructionSet := proptools.StringDefault(props.Instruction_set, "")
 	if instructionSet == "arm" {
-		ca.features.SetSelectValue(axis, config, []string{"arm_isa_arm", "-arm_isa_thumb"})
+		ca.features.SetSelectValue(axis, config, []string{"arm_isa_arm"})
 	} else if instructionSet != "" && instructionSet != "thumb" {
 		ctx.ModuleErrorf("Unknown value for instruction_set: %s", instructionSet)
 	}
@@ -566,6 +574,12 @@
 	ca.asmSrcs = partitionedSrcs[asmSrcPartition]
 	ca.lSrcs = partitionedSrcs[lSrcPartition]
 	ca.llSrcs = partitionedSrcs[llSrcPartition]
+	if yacc := partitionedSrcs[yaccSrcPartition]; !yacc.IsEmpty() {
+		if len(yacc.Value.Includes) > 1 {
+			ctx.PropertyErrorf("srcs", "Found multiple yacc (.y/.yy) files in library")
+		}
+		ca.yaccSrc = bazel.MakeLabelAttribute(yacc.Value.Includes[0].Label)
+	}
 	ca.syspropSrcs = partitionedSrcs[syspropSrcPartition]
 
 	ca.absoluteIncludes.DeduplicateAxesFromBase()
@@ -728,6 +742,8 @@
 	compilerAttrs := compilerAttributes{}
 	linkerAttrs := linkerAttributes{}
 
+	var aidlLibs bazel.LabelList
+
 	// Iterate through these axes in a deterministic order. This is required
 	// because processing certain dependencies may result in concatenating
 	// elements along other axes. (For example, processing NoConfig may result
@@ -742,7 +758,13 @@
 				if baseCompilerProps.Lex != nil {
 					compilerAttrs.lexopts.SetSelectValue(axis, cfg, baseCompilerProps.Lex.Flags)
 				}
+				if baseCompilerProps.Yacc != nil {
+					compilerAttrs.yaccFlags.SetSelectValue(axis, cfg, baseCompilerProps.Yacc.Flags)
+					compilerAttrs.yaccGenLocationHeader.SetSelectValue(axis, cfg, baseCompilerProps.Yacc.Gen_location_hh)
+					compilerAttrs.yaccGenPositionHeader.SetSelectValue(axis, cfg, baseCompilerProps.Yacc.Gen_position_hh)
+				}
 				(&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, cfg, baseCompilerProps)
+				aidlLibs.Append(android.BazelLabelForModuleDeps(ctx, baseCompilerProps.Aidl.Libs))
 			}
 
 			var exportHdrs []string
@@ -815,7 +837,15 @@
 	(&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
 	(&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
 
-	aidlDep := bp2buildCcAidlLibrary(ctx, module, compilerAttrs.aidlSrcs, linkerAttrs)
+	aidlDep := bp2buildCcAidlLibrary(
+		ctx, module,
+		compilerAttrs.aidlSrcs,
+		bazel.LabelListAttribute{
+			Value: aidlLibs,
+		},
+		linkerAttrs,
+		compilerAttrs,
+	)
 	if aidlDep != nil {
 		if lib, ok := module.linker.(*libraryDecorator); ok {
 			if proptools.Bool(lib.Properties.Aidl.Export_aidl_headers) {
@@ -826,6 +856,12 @@
 		}
 	}
 
+	// Create a cc_yacc_static_library if srcs contains .y/.yy files
+	// This internal target will produce an .a file that will be statically linked to the parent library
+	if yaccDep := bp2buildCcYaccLibrary(ctx, compilerAttrs, linkerAttrs); yaccDep != nil {
+		(&linkerAttrs).implementationWholeArchiveDeps.Add(yaccDep)
+	}
+
 	convertedLSrcs := bp2BuildLex(ctx, module.Name(), compilerAttrs)
 	(&compilerAttrs).srcs.Add(&convertedLSrcs.srcName)
 	(&compilerAttrs).cSrcs.Add(&convertedLSrcs.cSrcName)
@@ -864,6 +900,48 @@
 	}
 }
 
+type ccYaccLibraryAttributes struct {
+	Src                         bazel.LabelAttribute
+	Flags                       bazel.StringListAttribute
+	Gen_location_hh             bazel.BoolAttribute
+	Gen_position_hh             bazel.BoolAttribute
+	Local_includes              bazel.StringListAttribute
+	Implementation_deps         bazel.LabelListAttribute
+	Implementation_dynamic_deps bazel.LabelListAttribute
+}
+
+func bp2buildCcYaccLibrary(ctx android.Bp2buildMutatorContext, ca compilerAttributes, la linkerAttributes) *bazel.LabelAttribute {
+	if ca.yaccSrc == nil {
+		return nil
+	}
+	yaccLibraryLabel := ctx.Module().Name() + "_yacc"
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_yacc_static_library",
+			Bzl_load_location: "//build/bazel/rules/cc:cc_yacc_library.bzl",
+		},
+		android.CommonAttributes{
+			Name: yaccLibraryLabel,
+		},
+		&ccYaccLibraryAttributes{
+			Src:                         *ca.yaccSrc,
+			Flags:                       ca.yaccFlags,
+			Gen_location_hh:             ca.yaccGenLocationHeader,
+			Gen_position_hh:             ca.yaccGenPositionHeader,
+			Local_includes:              ca.localIncludes,
+			Implementation_deps:         la.implementationDeps,
+			Implementation_dynamic_deps: la.implementationDynamicDeps,
+		},
+	)
+
+	yaccLibrary := &bazel.LabelAttribute{
+		Value: &bazel.Label{
+			Label: ":" + yaccLibraryLabel,
+		},
+	}
+	return yaccLibrary
+}
+
 // As a workaround for b/261657184, we are manually adding the default value
 // of system_dynamic_deps for the linux_musl os.
 // TODO: Solve this properly
@@ -912,11 +990,16 @@
 func bp2buildCcAidlLibrary(
 	ctx android.Bp2buildMutatorContext,
 	m *Module,
-	aidlLabelList bazel.LabelListAttribute,
+	aidlSrcs bazel.LabelListAttribute,
+	aidlLibs bazel.LabelListAttribute,
 	linkerAttrs linkerAttributes,
+	compilerAttrs compilerAttributes,
 ) *bazel.LabelAttribute {
-	if !aidlLabelList.IsEmpty() {
-		aidlLibs, aidlSrcs := aidlLabelList.Partition(func(src bazel.Label) bool {
+	var aidlLibsFromSrcs, aidlFiles bazel.LabelListAttribute
+	apexAvailableTags := android.ApexAvailableTagsWithoutTestApexes(ctx.(android.TopDownMutatorContext), ctx.Module())
+
+	if !aidlSrcs.IsEmpty() {
+		aidlLibsFromSrcs, aidlFiles = aidlSrcs.Partition(func(src bazel.Label) bool {
 			if fg, ok := android.ToFileGroupAsLibrary(ctx, src.OriginalModuleName); ok &&
 				fg.ShouldConvertToAidlLibrary(ctx) {
 				return true
@@ -924,57 +1007,71 @@
 			return false
 		})
 
-		apexAvailableTags := android.ApexAvailableTagsWithoutTestApexes(ctx.(android.TopDownMutatorContext), ctx.Module())
-		sdkAttrs := bp2BuildParseSdkAttributes(m)
-
-		if !aidlSrcs.IsEmpty() {
+		if !aidlFiles.IsEmpty() {
 			aidlLibName := m.Name() + "_aidl_library"
 			ctx.CreateBazelTargetModule(
 				bazel.BazelTargetModuleProperties{
 					Rule_class:        "aidl_library",
 					Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl",
 				},
-				android.CommonAttributes{Name: aidlLibName},
-				&aidlLibraryAttributes{
-					Srcs: aidlSrcs,
+				android.CommonAttributes{
+					Name: aidlLibName,
 					Tags: apexAvailableTags,
 				},
-			)
-			aidlLibs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
-		}
-
-		if !aidlLibs.IsEmpty() {
-			ccAidlLibrarylabel := m.Name() + "_cc_aidl_library"
-			// Since parent cc_library already has these dependencies, we can add them as implementation
-			// deps so that they don't re-export
-			implementationDeps := linkerAttrs.deps.Clone()
-			implementationDeps.Append(linkerAttrs.implementationDeps)
-			implementationDynamicDeps := linkerAttrs.dynamicDeps.Clone()
-			implementationDynamicDeps.Append(linkerAttrs.implementationDynamicDeps)
-
-			ctx.CreateBazelTargetModule(
-				bazel.BazelTargetModuleProperties{
-					Rule_class:        "cc_aidl_library",
-					Bzl_load_location: "//build/bazel/rules/cc:cc_aidl_library.bzl",
-				},
-				android.CommonAttributes{Name: ccAidlLibrarylabel},
-				&ccAidlLibraryAttributes{
-					Deps:                        aidlLibs,
-					Implementation_deps:         *implementationDeps,
-					Implementation_dynamic_deps: *implementationDynamicDeps,
-					Tags:                        apexAvailableTags,
-					sdkAttributes:               sdkAttrs,
+				&aidlLibraryAttributes{
+					Srcs: aidlFiles,
 				},
 			)
-			label := &bazel.LabelAttribute{
-				Value: &bazel.Label{
-					Label: ":" + ccAidlLibrarylabel,
-				},
-			}
-			return label
+			aidlLibsFromSrcs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
 		}
 	}
 
+	allAidlLibs := aidlLibs.Clone()
+	allAidlLibs.Append(aidlLibsFromSrcs)
+
+	if !allAidlLibs.IsEmpty() {
+		ccAidlLibrarylabel := m.Name() + "_cc_aidl_library"
+		// Since parent cc_library already has these dependencies, we can add them as implementation
+		// deps so that they don't re-export
+		implementationDeps := linkerAttrs.deps.Clone()
+		implementationDeps.Append(linkerAttrs.implementationDeps)
+		implementationDynamicDeps := linkerAttrs.dynamicDeps.Clone()
+		implementationDynamicDeps.Append(linkerAttrs.implementationDynamicDeps)
+
+		sdkAttrs := bp2BuildParseSdkAttributes(m)
+
+		exportedIncludes := bp2BuildParseExportedIncludes(ctx, m, &compilerAttrs.includes)
+		includeAttrs := includesAttributes{
+			Export_includes:          exportedIncludes.Includes,
+			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+			Export_system_includes:   exportedIncludes.SystemIncludes,
+			Local_includes:           compilerAttrs.localIncludes,
+			Absolute_includes:        compilerAttrs.absoluteIncludes,
+		}
+
+		ctx.CreateBazelTargetModule(
+			bazel.BazelTargetModuleProperties{
+				Rule_class:        "cc_aidl_library",
+				Bzl_load_location: "//build/bazel/rules/cc:cc_aidl_library.bzl",
+			},
+			android.CommonAttributes{Name: ccAidlLibrarylabel},
+			&ccAidlLibraryAttributes{
+				Deps:                        *allAidlLibs,
+				Implementation_deps:         *implementationDeps,
+				Implementation_dynamic_deps: *implementationDynamicDeps,
+				Tags:                        apexAvailableTags,
+				sdkAttributes:               sdkAttrs,
+				includesAttributes:          includeAttrs,
+			},
+		)
+		label := &bazel.LabelAttribute{
+			Value: &bazel.Label{
+				Label: ":" + ccAidlLibrarylabel,
+			},
+		}
+		return label
+	}
+
 	return nil
 }
 
@@ -1160,6 +1257,7 @@
 		label := android.BazelLabelForModuleSrcSingle(ctx, *props.Version_script)
 		additionalLinkerInputs.Add(&label)
 		linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--version-script,$(location %s)", label.Label))
+		axisFeatures = append(axisFeatures, "android_cfi_exports_map")
 	}
 
 	if props.Dynamic_list != nil {
@@ -1169,6 +1267,9 @@
 	}
 
 	la.additionalLinkerInputs.SetSelectValue(axis, config, additionalLinkerInputs)
+	if axis == bazel.OsConfigurationAxis && (config == bazel.OsDarwin || config == bazel.OsLinux || config == bazel.OsWindows) {
+		linkerFlags = append(linkerFlags, props.Host_ldlibs...)
+	}
 	la.linkopts.SetSelectValue(axis, config, linkerFlags)
 
 	if axisFeatures != nil {
diff --git a/cc/builder.go b/cc/builder.go
index fef00d4..f5e0dcc 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -125,6 +125,14 @@
 		},
 		"objcopyCmd", "prefix")
 
+	// Rule to run objcopy --remove-section=.llvm_addrsig on a partially linked object
+	noAddrSig = pctx.AndroidStaticRule("noAddrSig",
+		blueprint.RuleParams{
+			Command:     "rm -f ${out} && $objcopyCmd --remove-section=.llvm_addrsig ${in} ${out}",
+			CommandDeps: []string{"$objcopyCmd"},
+		},
+		"objcopyCmd")
+
 	_ = pctx.SourcePathVariable("stripPath", "build/soong/scripts/strip.sh")
 	_ = pctx.SourcePathVariable("xzCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/xz")
 	_ = pctx.SourcePathVariable("createMiniDebugInfo", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/create_minidebuginfo")
@@ -1008,6 +1016,21 @@
 	})
 }
 
+// Generate a rule for running objcopy --remove-section=.llvm_addrsig on a partially linked object
+func transformObjectNoAddrSig(ctx android.ModuleContext, inputFile android.Path, outputFile android.WritablePath) {
+	objcopyCmd := "${config.ClangBin}/llvm-objcopy"
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        noAddrSig,
+		Description: "remove addrsig " + outputFile.Base(),
+		Output:      outputFile,
+		Input:       inputFile,
+		Args: map[string]string{
+			"objcopyCmd": objcopyCmd,
+		},
+	})
+}
+
 // Registers a build statement to invoke `strip` (to discard symbols and data from object files).
 func transformStrip(ctx android.ModuleContext, inputFile android.Path,
 	outputFile android.WritablePath, flags StripFlags) {
diff --git a/cc/cc.go b/cc/cc.go
index bbb3c72..c9f00e2 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -27,6 +27,7 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
+	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/bazel/cquery"
 	"android/soong/cc/config"
@@ -82,7 +83,7 @@
 		ctx.TopDown("sabi_deps", sabiDepsMutator)
 	})
 
-	ctx.RegisterSingletonType("kythe_extract_all", kytheExtractAllFactory)
+	ctx.RegisterParallelSingletonType("kythe_extract_all", kytheExtractAllFactory)
 }
 
 // Deps is a struct containing module names of dependencies, separated by the kind of dependency.
@@ -110,6 +111,9 @@
 	// Used by DepsMutator to pass system_shared_libs information to check_elf_file.py.
 	SystemSharedLibs []string
 
+	// Used by DepMutator to pass aidl_library modules to aidl compiler
+	AidlLibs []string
+
 	// If true, statically link the unwinder into native libraries/binaries.
 	StaticUnwinderIfLegacy bool
 
@@ -182,6 +186,9 @@
 	// For Darwin builds, the path to the second architecture's output that should
 	// be combined with this architectures's output into a FAT MachO file.
 	DarwinSecondArchOutput android.OptionalPath
+
+	// Paths to direct srcs and transitive include dirs from direct aidl_library deps
+	AidlLibraryInfos []aidl_library.AidlLibraryInfo
 }
 
 // LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
@@ -765,6 +772,7 @@
 	stubImplDepTag        = dependencyTag{name: "stub_impl"}
 	JniFuzzLibTag         = dependencyTag{name: "jni_fuzz_lib_tag"}
 	FdoProfileTag         = dependencyTag{name: "fdo_profile"}
+	aidlLibraryTag        = dependencyTag{name: "aidl_library"}
 )
 
 func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
@@ -2751,6 +2759,14 @@
 		}
 	}
 
+	if len(deps.AidlLibs) > 0 {
+		actx.AddDependency(
+			c,
+			aidlLibraryTag,
+			deps.AidlLibs...,
+		)
+	}
+
 	updateImportedLibraryDependency(ctx)
 }
 
@@ -3055,6 +3071,17 @@
 			return
 		}
 
+		if depTag == aidlLibraryTag {
+			if ctx.OtherModuleHasProvider(dep, aidl_library.AidlLibraryProvider) {
+				depPaths.AidlLibraryInfos = append(
+					depPaths.AidlLibraryInfos,
+					ctx.OtherModuleProvider(
+						dep,
+						aidl_library.AidlLibraryProvider).(aidl_library.AidlLibraryInfo),
+				)
+			}
+		}
+
 		ccDep, ok := dep.(LinkableInterface)
 		if !ok {
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index f9e661f..173911b 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -24,6 +24,7 @@
 	"strings"
 	"testing"
 
+	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/bazel/cquery"
 )
@@ -4418,9 +4419,80 @@
 	}
 }
 
+func TestAidlLibraryWithHeaders(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		aidl_library.PrepareForTestWithAidlLibrary,
+		android.MockFS{
+			"package_bar/Android.bp": []byte(`
+			aidl_library {
+				name: "bar",
+				srcs: ["x/y/Bar.aidl"],
+				hdrs: ["x/HeaderBar.aidl"],
+				strip_import_prefix: "x",
+			}
+			`)}.AddToFixture(),
+		android.MockFS{
+			"package_foo/Android.bp": []byte(`
+			aidl_library {
+				name: "foo",
+				srcs: ["a/b/Foo.aidl"],
+				hdrs: ["a/HeaderFoo.aidl"],
+				strip_import_prefix: "a",
+				deps: ["bar"],
+			}
+			cc_library {
+				name: "libfoo",
+				aidl: {
+					libs: ["foo"],
+				}
+			}
+			`),
+		}.AddToFixture(),
+	).RunTest(t).TestContext
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl headers",
+		[]string{
+			"package_bar/x/HeaderBar.aidl",
+			"package_foo/a/HeaderFoo.aidl",
+			"package_foo/a/b/Foo.aidl",
+			"out/soong/.intermediates/package_foo/libfoo/android_arm64_armv8-a_static/gen/aidl_library.sbox.textproto",
+		},
+		libfoo.Rule("aidl_library").Implicits,
+	)
+
+	manifest := android.RuleBuilderSboxProtoForTests(t, libfoo.Output("aidl_library.sbox.textproto"))
+	aidlCommand := manifest.Commands[0].GetCommand()
+
+	expectedAidlFlags := "-Ipackage_foo/a -Ipackage_bar/x"
+	if !strings.Contains(aidlCommand, expectedAidlFlags) {
+		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlags)
+	}
+
+	outputs := strings.Join(libfoo.AllOutputs(), " ")
+
+	android.AssertStringDoesContain(t, "aidl-generated header", outputs, "gen/aidl_library/b/BpFoo.h")
+	android.AssertStringDoesContain(t, "aidl-generated header", outputs, "gen/aidl_library/b/BnFoo.h")
+	android.AssertStringDoesContain(t, "aidl-generated header", outputs, "gen/aidl_library/b/Foo.h")
+	android.AssertStringDoesContain(t, "aidl-generated cpp", outputs, "b/Foo.cpp")
+	// Confirm that the aidl header doesn't get compiled to cpp and h files
+	android.AssertStringDoesNotContain(t, "aidl-generated header", outputs, "gen/aidl_library/y/BpBar.h")
+	android.AssertStringDoesNotContain(t, "aidl-generated header", outputs, "gen/aidl_library/y/BnBar.h")
+	android.AssertStringDoesNotContain(t, "aidl-generated header", outputs, "gen/aidl_library/y/Bar.h")
+	android.AssertStringDoesNotContain(t, "aidl-generated cpp", outputs, "y/Bar.cpp")
+}
+
 func TestAidlFlagsPassedToTheAidlCompiler(t *testing.T) {
 	t.Parallel()
-	ctx := testCc(t, `
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		aidl_library.PrepareForTestWithAidlLibrary,
+	).RunTestWithBp(t, `
 		cc_library {
 			name: "libfoo",
 			srcs: ["a/Foo.aidl"],
@@ -4490,6 +4562,55 @@
 	}
 }
 
+func TestInvalidAidlProp(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		description string
+		bp          string
+	}{
+		{
+			description: "Invalid use of aidl.libs and aidl.include_dirs",
+			bp: `
+			cc_library {
+				name: "foo",
+				aidl: {
+					libs: ["foo_aidl"],
+					include_dirs: ["bar/include"],
+				}
+			}
+			`,
+		},
+		{
+			description: "Invalid use of aidl.libs and aidl.local_include_dirs",
+			bp: `
+			cc_library {
+				name: "foo",
+				aidl: {
+					libs: ["foo_aidl"],
+					local_include_dirs: ["include"],
+				}
+			}
+			`,
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.description, func(t *testing.T) {
+			bp := `
+			aidl_library {
+				name: "foo_aidl",
+				srcs: ["Foo.aidl"],
+			} ` + testCase.bp
+			android.GroupFixturePreparers(
+				prepareForCcTest,
+				aidl_library.PrepareForTestWithAidlLibrary.
+					ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("For aidl headers, please only use aidl.libs prop")),
+			).RunTestWithBp(t, bp)
+		})
+	}
+}
+
 func TestMinSdkVersionInClangTriple(t *testing.T) {
 	t.Parallel()
 	ctx := testCc(t, `
@@ -4705,7 +4826,15 @@
 	})
 
 	t.Run("ensure only aidl headers are exported", func(t *testing.T) {
-		ctx := testCc(t, genRuleModules+`
+		ctx := android.GroupFixturePreparers(
+			prepareForCcTest,
+			aidl_library.PrepareForTestWithAidlLibrary,
+		).RunTestWithBp(t, `
+		aidl_library {
+			name: "libfoo_aidl",
+			srcs: ["x/y/Bar.aidl"],
+			strip_import_prefix: "x",
+		}
 		cc_library_shared {
 			name: "libfoo",
 			srcs: [
@@ -4714,25 +4843,33 @@
 				"a.proto",
 			],
 			aidl: {
+				libs: ["libfoo_aidl"],
 				export_aidl_headers: true,
 			}
 		}
-		`)
+		`).TestContext
 		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
 		checkIncludeDirs(t, ctx, foo,
 			expectedIncludeDirs(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library
 			`),
 			expectedSystemIncludeDirs(``),
 			expectedGeneratedHeaders(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/Bar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BnBar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BpBar.h
 			`),
 			expectedOrderOnlyDeps(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/Bar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BnBar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BpBar.h
 			`),
 		)
 	})
diff --git a/cc/ccdeps.go b/cc/ccdeps.go
index 75e1faf..d30abba 100644
--- a/cc/ccdeps.go
+++ b/cc/ccdeps.go
@@ -30,7 +30,7 @@
 // The info file is generated in $OUT/module_bp_cc_depend.json.
 
 func init() {
-	android.RegisterSingletonType("ccdeps_generator", ccDepsGeneratorSingleton)
+	android.RegisterParallelSingletonType("ccdeps_generator", ccDepsGeneratorSingleton)
 }
 
 func ccDepsGeneratorSingleton() android.Singleton {
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index ad130ba..0f3f02d 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -29,7 +29,7 @@
 // structure (see variable CLionOutputProjectsDirectory for root).
 
 func init() {
-	android.RegisterSingletonType("cmakelists_generator", cMakeListsGeneratorSingleton)
+	android.RegisterParallelSingletonType("cmakelists_generator", cMakeListsGeneratorSingleton)
 }
 
 func cMakeListsGeneratorSingleton() android.Singleton {
diff --git a/cc/compdb.go b/cc/compdb.go
index ea12443..617be1a 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -32,7 +32,7 @@
 // make SOONG_GEN_COMPDB=1 nothing to get all targets.
 
 func init() {
-	android.RegisterSingletonType("compdb_generator", compDBGeneratorSingleton)
+	android.RegisterParallelSingletonType("compdb_generator", compDBGeneratorSingleton)
 }
 
 func compDBGeneratorSingleton() android.Singleton {
diff --git a/cc/compiler.go b/cc/compiler.go
index 88985b6..16f4a6e 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -120,6 +120,9 @@
 	Lex  *LexProperties
 
 	Aidl struct {
+		// List of aidl_library modules
+		Libs []string
+
 		// list of directories that will be added to the aidl include paths.
 		Include_dirs []string
 
@@ -272,6 +275,7 @@
 	deps.GeneratedSources = append(deps.GeneratedSources, compiler.Properties.Generated_sources...)
 	deps.GeneratedSources = removeListFromList(deps.GeneratedSources, compiler.Properties.Exclude_generated_sources)
 	deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers...)
+	deps.AidlLibs = append(deps.AidlLibs, compiler.Properties.Aidl.Libs...)
 
 	android.ProtoDeps(ctx, &compiler.Proto)
 	if compiler.hasSrcExt(".proto") {
@@ -561,7 +565,12 @@
 			"-I"+android.PathForModuleGen(ctx, "yacc", ctx.ModuleDir()).String())
 	}
 
-	if compiler.hasSrcExt(".aidl") {
+	if len(compiler.Properties.Aidl.Libs) > 0 &&
+		(len(compiler.Properties.Aidl.Include_dirs) > 0 || len(compiler.Properties.Aidl.Local_include_dirs) > 0) {
+		ctx.ModuleErrorf("aidl.libs and (aidl.include_dirs or aidl.local_include_dirs) can't be set at the same time. For aidl headers, please only use aidl.libs prop")
+	}
+
+	if compiler.hasAidl(deps) {
 		flags.aidlFlags = append(flags.aidlFlags, compiler.Properties.Aidl.Flags...)
 		if len(compiler.Properties.Aidl.Local_include_dirs) > 0 {
 			localAidlIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Aidl.Local_include_dirs)
@@ -572,6 +581,14 @@
 			flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(rootAidlIncludeDirs))
 		}
 
+		var rootAidlIncludeDirs android.Paths
+		for _, aidlLibraryInfo := range deps.AidlLibraryInfos {
+			rootAidlIncludeDirs = append(rootAidlIncludeDirs, aidlLibraryInfo.IncludeDirs.ToList()...)
+		}
+		if len(rootAidlIncludeDirs) > 0 {
+			flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(rootAidlIncludeDirs))
+		}
+
 		if proptools.BoolDefault(compiler.Properties.Aidl.Generate_traces, true) {
 			flags.aidlFlags = append(flags.aidlFlags, "-t")
 		}
@@ -582,8 +599,14 @@
 		}
 		flags.aidlFlags = append(flags.aidlFlags, "--min_sdk_version="+aidlMinSdkVersion)
 
-		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
-			"-I"+android.PathForModuleGen(ctx, "aidl").String())
+		if compiler.hasSrcExt(".aidl") {
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags,
+				"-I"+android.PathForModuleGen(ctx, "aidl").String())
+		}
+		if len(deps.AidlLibraryInfos) > 0 {
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags,
+				"-I"+android.PathForModuleGen(ctx, "aidl_library").String())
+		}
 	}
 
 	if compiler.hasSrcExt(".rscript") || compiler.hasSrcExt(".fs") {
@@ -660,6 +683,10 @@
 	return nil
 }
 
+func (compiler *baseCompiler) hasAidl(deps PathDeps) bool {
+	return len(deps.AidlLibraryInfos) > 0 || compiler.hasSrcExt(".aidl")
+}
+
 func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
 	pathDeps := deps.GeneratedDeps
 	pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
@@ -668,7 +695,7 @@
 
 	srcs := append(android.Paths(nil), compiler.srcsBeforeGen...)
 
-	srcs, genDeps, info := genSources(ctx, srcs, buildFlags)
+	srcs, genDeps, info := genSources(ctx, deps.AidlLibraryInfos, srcs, buildFlags)
 	pathDeps = append(pathDeps, genDeps...)
 
 	compiler.pathDeps = pathDeps
diff --git a/cc/config/global.go b/cc/config/global.go
index 530b79a..d63e324 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -111,9 +111,6 @@
 
 		// Turn off FMA which got enabled by default in clang-r445002 (http://b/218805949)
 		"-ffp-contract=off",
-
-		// Turn off stack protector check for noreturn calls. (http://b/264965700)
-		"-mllvm -disable-check-noreturn-call",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -150,9 +147,6 @@
 	commonGlobalLldflags = []string{
 		"-fuse-ld=lld",
 		"-Wl,--icf=safe",
-
-		// Turn off stack protector check for noreturn calls. (http://b/264965700)
-		"-Wl,-mllvm,-disable-check-noreturn-call",
 	}
 
 	deviceGlobalCppflags = []string{
@@ -360,14 +354,14 @@
 		// Automatically initialize any uninitialized stack variables.
 		// Prefer zero-init if multiple options are set.
 		if ctx.Config().IsEnvTrue("AUTO_ZERO_INITIALIZE") {
-			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang -Wno-unused-command-line-argument")
+			flags = append(flags, "-ftrivial-auto-var-init=zero")
 		} else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=pattern")
 		} else if ctx.Config().IsEnvTrue("AUTO_UNINITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=uninitialized")
 		} else {
 			// Default to zero initialization.
-			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang -Wno-unused-command-line-argument")
+			flags = append(flags, "-ftrivial-auto-var-init=zero")
 		}
 
 		// Workaround for ccache with clang.
@@ -444,6 +438,8 @@
 	pctx.StaticVariableWithEnvOverride("ClangShortVersion", "LLVM_RELEASE_VERSION", ClangDefaultShortVersion)
 	pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib/clang/${ClangShortVersion}/lib/linux")
 
+	exportedVars.ExportStringListStaticVariable("WarningAllowedProjects", WarningAllowedProjects)
+
 	// These are tied to the version of LLVM directly in external/llvm, so they might trail the host prebuilts
 	// being used for the rest of the build process.
 	pctx.SourcePathVariable("RSClangBase", "prebuilts/clang/host")
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 6a10e14..a0ef575 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -20,6 +20,12 @@
 	"android/soong/android"
 )
 
+func init() {
+	exportedVars.ExportStringListStaticVariable("DarwinAvailableLibraries", darwinAvailableLibraries)
+	exportedVars.ExportStringListStaticVariable("LinuxAvailableLibraries", linuxAvailableLibraries)
+	exportedVars.ExportStringListStaticVariable("WindowsAvailableLibraries", windowsAvailableLibraries)
+}
+
 type toolchainFactory func(arch android.Arch) Toolchain
 
 var toolchainFactories = make(map[android.OsType]map[android.ArchType]toolchainFactory)
diff --git a/cc/fuzz.go b/cc/fuzz.go
index dfefc11..c897501 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -28,7 +28,7 @@
 
 func init() {
 	android.RegisterModuleType("cc_fuzz", LibFuzzFactory)
-	android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
+	android.RegisterParallelSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
 }
 
 type FuzzProperties struct {
@@ -398,7 +398,9 @@
 		}
 
 		hostOrTargetString := "target"
-		if ccModule.Host() {
+		if ccModule.Target().HostCross {
+			hostOrTargetString = "host_cross"
+		} else if ccModule.Host() {
 			hostOrTargetString = "host"
 		}
 
diff --git a/cc/gen.go b/cc/gen.go
index dfbb177..b15f164 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -18,7 +18,9 @@
 	"path/filepath"
 	"strings"
 
+	"android/soong/aidl_library"
 	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 
 	"android/soong/android"
@@ -105,7 +107,14 @@
 	return ret
 }
 
-func genAidl(ctx android.ModuleContext, rule *android.RuleBuilder, aidlFile android.Path, aidlFlags string) (cppFile android.OutputPath, headerFiles android.Paths) {
+func genAidl(
+	ctx android.ModuleContext,
+	rule *android.RuleBuilder,
+	outDirBase string,
+	aidlFile android.Path,
+	aidlHdrs android.Paths,
+	aidlFlags string,
+) (cppFile android.OutputPath, headerFiles android.Paths) {
 	aidlPackage := strings.TrimSuffix(aidlFile.Rel(), aidlFile.Base())
 	baseName := strings.TrimSuffix(aidlFile.Base(), aidlFile.Ext())
 	shortName := baseName
@@ -117,20 +126,17 @@
 		shortName = strings.TrimPrefix(baseName, "I")
 	}
 
-	outDir := android.PathForModuleGen(ctx, "aidl")
+	outDir := android.PathForModuleGen(ctx, outDirBase)
 	cppFile = outDir.Join(ctx, aidlPackage, baseName+".cpp")
 	depFile := outDir.Join(ctx, aidlPackage, baseName+".cpp.d")
 	headerI := outDir.Join(ctx, aidlPackage, baseName+".h")
 	headerBn := outDir.Join(ctx, aidlPackage, "Bn"+shortName+".h")
 	headerBp := outDir.Join(ctx, aidlPackage, "Bp"+shortName+".h")
 
-	baseDir := strings.TrimSuffix(aidlFile.String(), aidlFile.Rel())
-	if baseDir != "" {
-		aidlFlags += " -I" + baseDir
-	}
-
 	cmd := rule.Command()
 	cmd.BuiltTool("aidl-cpp").
+		// libc++ is default stl for aidl-cpp (a cc_binary_host module)
+		ImplicitTool(ctx.Config().HostCcSharedLibPath(ctx, "libc++")).
 		FlagWithDepFile("-d", depFile).
 		Flag("--ninja").
 		Flag(aidlFlags).
@@ -143,6 +149,10 @@
 			headerBp,
 		})
 
+	if aidlHdrs != nil {
+		cmd.Implicits(aidlHdrs)
+	}
+
 	return cppFile, android.Paths{
 		headerI,
 		headerBn,
@@ -282,15 +292,23 @@
 	syspropOrderOnlyDeps android.Paths
 }
 
-func genSources(ctx android.ModuleContext, srcFiles android.Paths,
-	buildFlags builderFlags) (android.Paths, android.Paths, generatedSourceInfo) {
+func genSources(
+	ctx android.ModuleContext,
+	aidlLibraryInfos []aidl_library.AidlLibraryInfo,
+	srcFiles android.Paths,
+	buildFlags builderFlags,
+) (android.Paths, android.Paths, generatedSourceInfo) {
 
 	var info generatedSourceInfo
 
 	var deps android.Paths
 	var rsFiles android.Paths
 
+	// aidlRule supports compiling aidl files from srcs prop while aidlLibraryRule supports
+	// compiling aidl files from aidl_library modules specified in aidl.libs prop.
+	// The rules are separated so that they don't wipe out the other's outputDir
 	var aidlRule *android.RuleBuilder
+	var aidlLibraryRule *android.RuleBuilder
 
 	var yaccRule_ *android.RuleBuilder
 	yaccRule := func() *android.RuleBuilder {
@@ -330,7 +348,15 @@
 				aidlRule = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "aidl"),
 					android.PathForModuleGen(ctx, "aidl.sbox.textproto"))
 			}
-			cppFile, aidlHeaders := genAidl(ctx, aidlRule, srcFile, buildFlags.aidlFlags)
+			baseDir := strings.TrimSuffix(srcFile.String(), srcFile.Rel())
+			cppFile, aidlHeaders := genAidl(
+				ctx,
+				aidlRule,
+				"aidl",
+				srcFile,
+				nil,
+				buildFlags.aidlFlags+" -I"+baseDir,
+			)
 			srcFiles[i] = cppFile
 
 			info.aidlHeaders = append(info.aidlHeaders, aidlHeaders...)
@@ -352,10 +378,40 @@
 		}
 	}
 
+	for _, aidlLibraryInfo := range aidlLibraryInfos {
+		if aidlLibraryRule == nil {
+			aidlLibraryRule = android.NewRuleBuilder(pctx, ctx).Sbox(
+				android.PathForModuleGen(ctx, "aidl_library"),
+				android.PathForModuleGen(ctx, "aidl_library.sbox.textproto"),
+			).SandboxInputs()
+		}
+		for _, aidlSrc := range aidlLibraryInfo.Srcs {
+			cppFile, aidlHeaders := genAidl(
+				ctx,
+				aidlLibraryRule,
+				"aidl_library",
+				aidlSrc,
+				aidlLibraryInfo.Hdrs.ToList(),
+				buildFlags.aidlFlags,
+			)
+
+			srcFiles = append(srcFiles, cppFile)
+			info.aidlHeaders = append(info.aidlHeaders, aidlHeaders...)
+			// Use the generated headers as order only deps to ensure that they are up to date when
+			// needed.
+			// TODO: Reduce the size of the ninja file by using one order only dep for the whole rule
+			info.aidlOrderOnlyDeps = append(info.aidlOrderOnlyDeps, aidlHeaders...)
+		}
+	}
+
 	if aidlRule != nil {
 		aidlRule.Build("aidl", "gen aidl")
 	}
 
+	if aidlLibraryRule != nil {
+		aidlLibraryRule.Build("aidl_library", "gen aidl_library")
+	}
+
 	if yaccRule_ != nil {
 		yaccRule_.Build("yacc", "gen yacc")
 	}
diff --git a/cc/library.go b/cc/library.go
index 13b333a..98096a8 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -271,7 +271,9 @@
 	Implementation_deps         bazel.LabelListAttribute
 	Implementation_dynamic_deps bazel.LabelListAttribute
 	Tags                        bazel.StringListAttribute
+
 	sdkAttributes
+	includesAttributes
 }
 
 type stripAttributes struct {
@@ -335,6 +337,14 @@
 		Native_coverage:                   baseAttributes.Native_coverage,
 	}
 
+	includeAttrs := includesAttributes{
+		Export_includes:          exportedIncludes.Includes,
+		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+		Export_system_includes:   exportedIncludes.SystemIncludes,
+		Local_includes:           compilerAttrs.localIncludes,
+		Absolute_includes:        compilerAttrs.absoluteIncludes,
+	}
+
 	sharedCommonAttrs := staticOrSharedAttributes{
 		Srcs:    *srcs.Clone().Append(sharedAttrs.Srcs),
 		Srcs_c:  *compilerAttrs.cSrcs.Clone().Append(sharedAttrs.Srcs_c),
@@ -356,41 +366,34 @@
 
 	staticTargetAttrs := &bazelCcLibraryStaticAttributes{
 		staticOrSharedAttributes: staticCommonAttrs,
+		includesAttributes:       includeAttrs,
 
 		Cppflags:   compilerAttrs.cppFlags,
 		Conlyflags: compilerAttrs.conlyFlags,
 		Asflags:    asFlags,
 
-		Export_includes:          exportedIncludes.Includes,
-		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-		Export_system_includes:   exportedIncludes.SystemIncludes,
-		Local_includes:           compilerAttrs.localIncludes,
-		Absolute_includes:        compilerAttrs.absoluteIncludes,
-		Rtti:                     compilerAttrs.rtti,
-		Stl:                      compilerAttrs.stl,
-		Cpp_std:                  compilerAttrs.cppStd,
-		C_std:                    compilerAttrs.cStd,
+		Rtti:    compilerAttrs.rtti,
+		Stl:     compilerAttrs.stl,
+		Cpp_std: compilerAttrs.cppStd,
+		C_std:   compilerAttrs.cStd,
 
 		Features: *staticFeatures,
 	}
 
 	sharedTargetAttrs := &bazelCcLibrarySharedAttributes{
 		staticOrSharedAttributes: sharedCommonAttrs,
-		Cppflags:                 compilerAttrs.cppFlags,
-		Conlyflags:               compilerAttrs.conlyFlags,
-		Asflags:                  asFlags,
+		includesAttributes:       includeAttrs,
 
-		Export_includes:          exportedIncludes.Includes,
-		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-		Export_system_includes:   exportedIncludes.SystemIncludes,
-		Local_includes:           compilerAttrs.localIncludes,
-		Absolute_includes:        compilerAttrs.absoluteIncludes,
-		Linkopts:                 linkerAttrs.linkopts,
-		Rtti:                     compilerAttrs.rtti,
-		Stl:                      compilerAttrs.stl,
-		Cpp_std:                  compilerAttrs.cppStd,
-		C_std:                    compilerAttrs.cStd,
-		Use_version_lib:          linkerAttrs.useVersionLib,
+		Cppflags:   compilerAttrs.cppFlags,
+		Conlyflags: compilerAttrs.conlyFlags,
+		Asflags:    asFlags,
+
+		Linkopts:        linkerAttrs.linkopts,
+		Rtti:            compilerAttrs.rtti,
+		Stl:             compilerAttrs.stl,
+		Cpp_std:         compilerAttrs.cppStd,
+		C_std:           compilerAttrs.cStd,
+		Use_version_lib: linkerAttrs.useVersionLib,
 
 		Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
 
@@ -2114,9 +2117,15 @@
 
 	// Optionally export aidl headers.
 	if Bool(library.Properties.Aidl.Export_aidl_headers) {
-		if library.baseCompiler.hasSrcExt(".aidl") {
-			dir := android.PathForModuleGen(ctx, "aidl")
-			library.reexportDirs(dir)
+		if library.baseCompiler.hasAidl(deps) {
+			if library.baseCompiler.hasSrcExt(".aidl") {
+				dir := android.PathForModuleGen(ctx, "aidl")
+				library.reexportDirs(dir)
+			}
+			if len(deps.AidlLibraryInfos) > 0 {
+				dir := android.PathForModuleGen(ctx, "aidl_library")
+				library.reexportDirs(dir)
+			}
 
 			library.reexportDeps(library.baseCompiler.aidlOrderOnlyDeps...)
 			library.addExportedGeneratedHeaders(library.baseCompiler.aidlHeaders...)
@@ -2880,6 +2889,13 @@
 	linkerAttrs := baseAttributes.linkerAttributes
 
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, &compilerAttrs.includes)
+	includeAttrs := includesAttributes{
+		Export_includes:          exportedIncludes.Includes,
+		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+		Export_system_includes:   exportedIncludes.SystemIncludes,
+		Local_includes:           compilerAttrs.localIncludes,
+		Absolute_includes:        compilerAttrs.absoluteIncludes,
+	}
 
 	// Append shared/static{} stanza properties. These won't be specified on
 	// cc_library_* itself, but may be specified in cc_defaults that this module
@@ -2937,11 +2953,7 @@
 			Cpp_std:                  compilerAttrs.cppStd,
 			C_std:                    compilerAttrs.cStd,
 
-			Export_includes:          exportedIncludes.Includes,
-			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-			Export_system_includes:   exportedIncludes.SystemIncludes,
-			Local_includes:           compilerAttrs.localIncludes,
-			Absolute_includes:        compilerAttrs.absoluteIncludes,
+			includesAttributes: includeAttrs,
 
 			Cppflags:   compilerAttrs.cppFlags,
 			Conlyflags: compilerAttrs.conlyFlags,
@@ -2967,11 +2979,8 @@
 			Cpp_std: compilerAttrs.cppStd,
 			C_std:   compilerAttrs.cStd,
 
-			Export_includes:          exportedIncludes.Includes,
-			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-			Export_system_includes:   exportedIncludes.SystemIncludes,
-			Local_includes:           compilerAttrs.localIncludes,
-			Absolute_includes:        compilerAttrs.absoluteIncludes,
+			includesAttributes: includeAttrs,
+
 			Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
 
 			Strip: stripAttrsFromLinkerAttrs(&linkerAttrs),
@@ -3007,9 +3016,18 @@
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name(), Tags: tags}, attrs)
 }
 
+type includesAttributes struct {
+	Export_includes          bazel.StringListAttribute
+	Export_absolute_includes bazel.StringListAttribute
+	Export_system_includes   bazel.StringListAttribute
+	Local_includes           bazel.StringListAttribute
+	Absolute_includes        bazel.StringListAttribute
+}
+
 // TODO(b/199902614): Can this be factored to share with the other Attributes?
 type bazelCcLibraryStaticAttributes struct {
 	staticOrSharedAttributes
+	includesAttributes
 
 	Use_version_lib bazel.BoolAttribute
 	Rtti            bazel.BoolAttribute
@@ -3017,12 +3035,7 @@
 	Cpp_std         *string
 	C_std           *string
 
-	Export_includes          bazel.StringListAttribute
-	Export_absolute_includes bazel.StringListAttribute
-	Export_system_includes   bazel.StringListAttribute
-	Local_includes           bazel.StringListAttribute
-	Absolute_includes        bazel.StringListAttribute
-	Hdrs                     bazel.LabelListAttribute
+	Hdrs bazel.LabelListAttribute
 
 	Cppflags   bazel.StringListAttribute
 	Conlyflags bazel.StringListAttribute
@@ -3034,6 +3047,7 @@
 // TODO(b/199902614): Can this be factored to share with the other Attributes?
 type bazelCcLibrarySharedAttributes struct {
 	staticOrSharedAttributes
+	includesAttributes
 
 	Linkopts        bazel.StringListAttribute
 	Use_version_lib bazel.BoolAttribute
@@ -3043,12 +3057,7 @@
 	Cpp_std *string
 	C_std   *string
 
-	Export_includes          bazel.StringListAttribute
-	Export_absolute_includes bazel.StringListAttribute
-	Export_system_includes   bazel.StringListAttribute
-	Local_includes           bazel.StringListAttribute
-	Absolute_includes        bazel.StringListAttribute
-	Hdrs                     bazel.LabelListAttribute
+	Hdrs bazel.LabelListAttribute
 
 	Strip                    stripAttributes
 	Additional_linker_inputs bazel.LabelListAttribute
diff --git a/cc/lto.go b/cc/lto.go
index 1afa1dd..be8fc56 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -56,9 +56,6 @@
 	ThinDep      bool `blueprint:"mutated"`
 	NoLtoDep     bool `blueprint:"mutated"`
 
-	// Use clang lld instead of gnu ld.
-	Use_clang_lld *bool
-
 	// Use -fwhole-program-vtables cflag.
 	Whole_program_vtables *bool
 }
@@ -77,13 +74,6 @@
 	}
 }
 
-func (lto *lto) useClangLld(ctx BaseModuleContext) bool {
-	if lto.Properties.Use_clang_lld != nil {
-		return Bool(lto.Properties.Use_clang_lld)
-	}
-	return true
-}
-
 func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags {
 	// TODO(b/131771163): Disable LTO when using explicit fuzzing configurations.
 	// LTO breaks fuzzer builds.
@@ -91,11 +81,6 @@
 		return flags
 	}
 
-	// TODO(b/254713216): LTO doesn't work on riscv64 yet.
-	if ctx.Arch().ArchType == android.Riscv64 {
-		return flags
-	}
-
 	if lto.LTO(ctx) {
 		var ltoCFlag string
 		var ltoLdFlag string
@@ -117,7 +102,7 @@
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fwhole-program-vtables")
 		}
 
-		if (lto.DefaultThinLTO(ctx) || lto.ThinLTO()) && ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && lto.useClangLld(ctx) {
+		if (lto.DefaultThinLTO(ctx) || lto.ThinLTO()) && ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") {
 			// Set appropriate ThinLTO cache policy
 			cacheDirFormat := "-Wl,--thinlto-cache-dir="
 			cacheDir := android.PathForOutput(ctx, "thinlto-cache").String()
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index 3456c32..86166dc 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -19,8 +19,8 @@
 )
 
 func init() {
-	android.RegisterSingletonType("ndk_abi_dump", NdkAbiDumpSingleton)
-	android.RegisterSingletonType("ndk_abi_diff", NdkAbiDiffSingleton)
+	android.RegisterParallelSingletonType("ndk_abi_dump", NdkAbiDumpSingleton)
+	android.RegisterParallelSingletonType("ndk_abi_diff", NdkAbiDiffSingleton)
 }
 
 func getNdkAbiDumpInstallBase(ctx android.PathContext) android.OutputPath {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index dffc6c6..0cf21b6 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -66,7 +66,7 @@
 	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
 	ctx.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
 	ctx.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
-	ctx.RegisterSingletonType("ndk", NdkSingleton)
+	ctx.RegisterParallelSingletonType("ndk", NdkSingleton)
 }
 
 func getNdkInstallBase(ctx android.PathContext) android.InstallPath {
diff --git a/cc/object.go b/cc/object.go
index 5d61872..ca14845 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -309,6 +309,8 @@
 			})
 		}
 	} else {
+		outputAddrSig := android.PathForModuleOut(ctx, "addrsig", outputName)
+
 		if String(object.Properties.Prefix_symbols) != "" {
 			input := android.PathForModuleOut(ctx, "unprefixed", outputName)
 			transformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), input,
@@ -316,7 +318,12 @@
 			output = input
 		}
 
-		transformObjsToObj(ctx, objs.objFiles, builderFlags, output, flags.LdFlagsDeps)
+		transformObjsToObj(ctx, objs.objFiles, builderFlags, outputAddrSig, flags.LdFlagsDeps)
+
+		// ld -r reorders symbols and invalidates the .llvm_addrsig section, which then causes warnings
+		// if the resulting object is used with ld --icf=safe.  Strip the .llvm_addrsig section to
+		// prevent the warnings.
+		transformObjectNoAddrSig(ctx, outputAddrSig, output)
 	}
 
 	ctx.CheckbuildFile(outputFile)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 7fddc1b..6e732b6 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -665,6 +665,21 @@
 		s.Diag.Cfi = nil
 	}
 
+	// TODO(b/280478629): runtimes don't exist for musl arm64 yet.
+	if ctx.toolchain().Musl() && ctx.Arch().ArchType == android.Arm64 {
+		s.Address = nil
+		s.Hwaddress = nil
+		s.Thread = nil
+		s.Scudo = nil
+		s.Fuzzer = nil
+		s.Cfi = nil
+		s.Diag.Cfi = nil
+		s.Misc_undefined = nil
+		s.Undefined = nil
+		s.All_undefined = nil
+		s.Integer_overflow = nil
+	}
+
 	// Also disable CFI for VNDK variants of components
 	if ctx.isVndk() && ctx.useVndk() {
 		s.Cfi = nil
diff --git a/cc/stub_library.go b/cc/stub_library.go
index f324dcc..3a6d0ae 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -23,7 +23,7 @@
 
 func init() {
 	// Use singleton type to gather all generated soong modules.
-	android.RegisterSingletonType("stublibraries", stubLibrariesSingleton)
+	android.RegisterParallelSingletonType("stublibraries", stubLibrariesSingleton)
 }
 
 type stubLibraries struct {
diff --git a/cc/tidy.go b/cc/tidy.go
index bbcaece..7b123cb 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -201,7 +201,7 @@
 }
 
 func init() {
-	android.RegisterSingletonType("tidy_phony_targets", TidyPhonySingleton)
+	android.RegisterParallelSingletonType("tidy_phony_targets", TidyPhonySingleton)
 }
 
 // This TidyPhonySingleton generates both tidy-* and obj-* phony targets for C/C++ files.
diff --git a/cc/vndk.go b/cc/vndk.go
index 9b70004..7a2286e 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -417,16 +417,16 @@
 
 func init() {
 	RegisterVndkLibraryTxtTypes(android.InitRegistrationContext)
-	android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
+	android.RegisterParallelSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 }
 
 func RegisterVndkLibraryTxtTypes(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
 }
 
 type vndkLibrariesTxt struct {
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 10b09d3..e006c9d 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -78,6 +78,7 @@
 	flag.StringVar(&cmdlineArgs.Bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
 	flag.StringVar(&cmdlineArgs.SymlinkForestMarker, "symlink_forest_marker", "", "If set, create the bp2build symlink forest, touch the specified marker file, then exit")
 	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
+	flag.StringVar(&cmdlineArgs.SoongVariables, "soong_variables", "soong.variables", "the file contains all build variables")
 	flag.StringVar(&cmdlineArgs.BazelForceEnabledModules, "bazel-force-enabled-modules", "", "additional modules to build with Bazel. Comma-delimited")
 	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
 	flag.BoolVar(&cmdlineArgs.MultitreeBuild, "multitree-build", false, "this is a multitree build")
@@ -133,6 +134,10 @@
 	ninjaDeps = append(ninjaDeps, writeBuildGlobsNinjaFile(ctx)...)
 
 	writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
+
+	if ctx.Config().IsEnvTrue("SOONG_GENERATES_NINJA_HINT") {
+		writeNinjaHint(ctx)
+	}
 	return cmdlineArgs.OutFile
 }
 
@@ -455,6 +460,9 @@
 		// The actual output (build.ninja) was written in the RunBlueprint() call
 		// above
 		writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
+		if ctx.Config().IsEnvTrue("SOONG_GENERATES_NINJA_HINT") {
+			writeNinjaHint(ctx)
+		}
 		return cmdlineArgs.OutFile
 	}
 }
@@ -510,6 +518,8 @@
 
 	var finalOutputFile string
 
+	writeSymlink := false
+
 	// Run Soong for a specific activity, like bp2build, queryview
 	// or the actual Soong build for the build.ninja file.
 	switch configuration.BuildMode {
@@ -532,11 +542,13 @@
 					maybeQuit(err, "")
 				}
 			}
+			writeSymlink = true
 		} else {
 			finalOutputFile = runSoongOnlyBuild(ctx, extraNinjaDeps)
-		}
-		if ctx.Config().IsEnvTrue("SOONG_GENERATES_NINJA_HINT") {
-			writeNinjaHint(ctx)
+
+			if configuration.BuildMode == android.AnalysisNoBazel {
+				writeSymlink = true
+			}
 		}
 		writeMetrics(configuration, ctx.EventHandler, metricsDir)
 	}
@@ -547,6 +559,24 @@
 	// are ninja inputs to the main output file, then ninja would superfluously
 	// rebuild this output file on the next build invocation.
 	touch(shared.JoinPath(topDir, finalOutputFile))
+
+	// TODO(b/277029044): Remove this function once build.<product>.ninja lands
+	if writeSymlink {
+		writeBuildNinjaSymlink(configuration, finalOutputFile)
+	}
+}
+
+// TODO(b/277029044): Remove this function once build.<product>.ninja lands
+func writeBuildNinjaSymlink(config android.Config, source string) {
+	targetPath := shared.JoinPath(topDir, config.SoongOutDir(), "build.ninja")
+	sourcePath := shared.JoinPath(topDir, source)
+
+	if targetPath == sourcePath {
+		return
+	}
+
+	os.Remove(targetPath)
+	os.Symlink(sourcePath, targetPath)
 }
 
 func writeUsedEnvironmentFile(configuration android.Config) {
diff --git a/device_config/Android.bp b/device_config/Android.bp
new file mode 100644
index 0000000..360b389
--- /dev/null
+++ b/device_config/Android.bp
@@ -0,0 +1,30 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-device_config",
+    pkgPath: "android/soong/device_config",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "sbox_proto",
+        "soong",
+        "soong-android",
+        "soong-bazel",
+        "soong-shared",
+    ],
+    srcs: [
+        "device_config_definitions.go",
+        "device_config_values.go",
+        "device_config_value_set.go",
+        "init.go",
+        //"testing.go"
+    ],
+    /*
+    testSrcs: [
+        "device_config_test.go",
+    ],
+    */
+    pluginFor: ["soong_build"],
+}
diff --git a/device_config/device_config_definitions.go b/device_config/device_config_definitions.go
new file mode 100644
index 0000000..c565766
--- /dev/null
+++ b/device_config/device_config_definitions.go
@@ -0,0 +1,150 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package device_config
+
+import (
+	"android/soong/android"
+	"fmt"
+	"github.com/google/blueprint"
+	"strings"
+)
+
+type DefinitionsModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	// Properties for "device_config_definitions"
+	properties struct {
+		// aconfig files, relative to this Android.bp file
+		Srcs []string `android:"path"`
+
+		// Release config flag namespace
+		Namespace string
+
+		// Values from TARGET_RELEASE / RELEASE_DEVICE_CONFIG_VALUE_SETS
+		Values []string `blueprint:"mutated"`
+	}
+
+	intermediatePath android.WritablePath
+	srcJarPath       android.WritablePath
+}
+
+func DefinitionsFactory() android.Module {
+	module := &DefinitionsModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+	// TODO: bp2build
+	//android.InitBazelModule(module)
+
+	return module
+}
+
+type implicitValuesTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var implicitValuesTag = implicitValuesTagType{}
+
+func (module *DefinitionsModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Validate Properties
+	if len(module.properties.Srcs) == 0 {
+		ctx.PropertyErrorf("srcs", "missing source files")
+		return
+	}
+	if len(module.properties.Namespace) == 0 {
+		ctx.PropertyErrorf("namespace", "missing namespace property")
+	}
+
+	// Add a dependency on the device_config_value_sets defined in
+	// RELEASE_DEVICE_CONFIG_VALUE_SETS, and add any device_config_values that
+	// match our namespace.
+	valuesFromConfig := ctx.Config().ReleaseDeviceConfigValueSets()
+	ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig...)
+}
+
+func (module *DefinitionsModule) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case ".srcjar":
+		return []android.Path{module.srcJarPath}, nil
+	case "":
+		// The default output of this module is the intermediates format, which is
+		// not installable and in a private format that no other rules can handle
+		// correctly.
+		return []android.Path{module.intermediatePath}, nil
+	default:
+		return nil, fmt.Errorf("unsupported device_config_definitions module reference tag %q", tag)
+	}
+}
+
+func joinAndPrefix(prefix string, values []string) string {
+	var sb strings.Builder
+	for _, v := range values {
+		sb.WriteString(prefix)
+		sb.WriteString(v)
+	}
+	return sb.String()
+}
+
+func (module *DefinitionsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Get the values that came from the global RELEASE_DEVICE_CONFIG_VALUE_SETS flag
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if !ctx.OtherModuleHasProvider(dep, valueSetProviderKey) {
+			// Other modules get injected as dependencies too, for example the license modules
+			return
+		}
+		depData := ctx.OtherModuleProvider(dep, valueSetProviderKey).(valueSetProviderData)
+		valuesFiles, ok := depData.AvailableNamespaces[module.properties.Namespace]
+		if ok {
+			for _, path := range valuesFiles {
+				module.properties.Values = append(module.properties.Values, path.String())
+			}
+		}
+	})
+
+	// Intermediate format
+	inputFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs)
+	module.intermediatePath = android.PathForModuleOut(ctx, "intermediate.json")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        aconfigRule,
+		Inputs:      inputFiles,
+		Output:      module.intermediatePath,
+		Description: "device_config_definitions",
+		Args: map[string]string{
+			"release_version": ctx.Config().ReleaseVersion(),
+			"namespace":       module.properties.Namespace,
+			"values":          joinAndPrefix(" --values ", module.properties.Values),
+		},
+	})
+
+	// Generated java inside a srcjar
+	module.srcJarPath = android.PathForModuleGen(ctx, ctx.ModuleName()+".srcjar")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        srcJarRule,
+		Input:       module.intermediatePath,
+		Output:      module.srcJarPath,
+		Description: "device_config.srcjar",
+	})
+
+	// TODO: C++
+
+	// Phony target for debugging convenience
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   blueprint.Phony,
+		Output: android.PathForPhony(ctx, ctx.ModuleName()),
+		Inputs: []android.Path{module.srcJarPath}, // TODO: C++
+	})
+}
diff --git a/device_config/device_config_test.go b/device_config/device_config_test.go
new file mode 100644
index 0000000..91a06a7
--- /dev/null
+++ b/device_config/device_config_test.go
@@ -0,0 +1,61 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package device_config
+
+import (
+	"os"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
+)
+
+func TestMain(m *testing.M) {
+	os.Exit(m.Run())
+}
+
+func test(t *testing.T, bp string) *android.TestResult {
+	t.Helper()
+
+	mockFS := android.MockFS{
+		"config.aconfig": nil,
+	}
+
+	result := android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithSyspropBuildComponents,
+		// TODO: Consider values files, although maybe in its own test
+		// android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		//	variables.ReleaseConfigValuesBasePaths = ...
+		//})
+		mockFS.AddToFixture(),
+		android.FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	return result
+}
+
+func TestOutputs(t *testing.T) {
+	/*result := */ test(t, `
+        device_config {
+            name: "my_device_config",
+            srcs: ["config.aconfig"],
+        }
+	`)
+
+	// TODO: Make sure it exports a .srcjar, which is used by java libraries
+	// TODO: Make sure it exports an intermediates file
+	// TODO: Make sure the intermediates file is propagated to the Android.mk file
+}
diff --git a/device_config/device_config_value_set.go b/device_config/device_config_value_set.go
new file mode 100644
index 0000000..e406d20
--- /dev/null
+++ b/device_config/device_config_value_set.go
@@ -0,0 +1,92 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package device_config
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+// Properties for "device_config_value_set"
+type ValueSetModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties struct {
+		// device_config_values modules
+		Values []string
+	}
+}
+
+func ValueSetFactory() android.Module {
+	module := &ValueSetModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+	// TODO: bp2build
+	//android.InitBazelModule(module)
+
+	return module
+}
+
+// Dependency tag for values property
+type valueSetType struct {
+	blueprint.BaseDependencyTag
+}
+
+var valueSetTag = valueSetType{}
+
+// Provider published by device_config_value_set
+type valueSetProviderData struct {
+	// The namespace of each of the
+	// (map of namespace --> device_config_module)
+	AvailableNamespaces map[string]android.Paths
+}
+
+var valueSetProviderKey = blueprint.NewProvider(valueSetProviderData{})
+
+func (module *ValueSetModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	deps := ctx.AddDependency(ctx.Module(), valueSetTag, module.properties.Values...)
+	for _, dep := range deps {
+		_, ok := dep.(*ValuesModule)
+		if !ok {
+			ctx.PropertyErrorf("values", "values must be a device_config_values module")
+			return
+		}
+	}
+}
+
+func (module *ValueSetModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Accumulate the namespaces of the values modules listed, and set that as an
+	// valueSetProviderKey provider that device_config modules can read and use
+	// to append values to their aconfig actions.
+	namespaces := make(map[string]android.Paths)
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if !ctx.OtherModuleHasProvider(dep, valuesProviderKey) {
+			// Other modules get injected as dependencies too, for example the license modules
+			return
+		}
+		depData := ctx.OtherModuleProvider(dep, valuesProviderKey).(valuesProviderData)
+
+		srcs := make([]android.Path, len(depData.Values))
+		copy(srcs, depData.Values)
+		namespaces[depData.Namespace] = srcs
+
+	})
+	ctx.SetProvider(valueSetProviderKey, valueSetProviderData{
+		AvailableNamespaces: namespaces,
+	})
+}
diff --git a/device_config/device_config_values.go b/device_config/device_config_values.go
new file mode 100644
index 0000000..110f12a
--- /dev/null
+++ b/device_config/device_config_values.go
@@ -0,0 +1,70 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package device_config
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+// Properties for "device_config_value"
+type ValuesModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties struct {
+		// aconfig files, relative to this Android.bp file
+		Srcs []string `android:"path"`
+
+		// Release config flag namespace
+		Namespace string
+	}
+}
+
+func ValuesFactory() android.Module {
+	module := &ValuesModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+	// TODO: bp2build
+	//android.InitBazelModule(module)
+
+	return module
+}
+
+// Provider published by device_config_value_set
+type valuesProviderData struct {
+	// The namespace that this values module values
+	Namespace string
+
+	// The values aconfig files, relative to the root of the tree
+	Values android.Paths
+}
+
+var valuesProviderKey = blueprint.NewProvider(valuesProviderData{})
+
+func (module *ValuesModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if len(module.properties.Namespace) == 0 {
+		ctx.PropertyErrorf("namespace", "missing namespace property")
+	}
+
+	// Provide the our source files list to the device_config_value_set as a list of files
+	providerData := valuesProviderData{
+		Namespace: module.properties.Namespace,
+		Values:    android.PathsForModuleSrc(ctx, module.properties.Srcs),
+	}
+	ctx.SetProvider(valuesProviderKey, providerData)
+}
diff --git a/device_config/init.go b/device_config/init.go
new file mode 100644
index 0000000..0a4645b
--- /dev/null
+++ b/device_config/init.go
@@ -0,0 +1,70 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package device_config
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+var (
+	pctx = android.NewPackageContext("android/soong/device_config")
+
+	// For device_config_definitions: Generate cache file
+	// TODO: Restat
+	aconfigRule = pctx.AndroidStaticRule("aconfig",
+		blueprint.RuleParams{
+			Command: `${aconfig} create-cache` +
+				` --namespace ${namespace}` +
+				` --declarations ${in}` +
+				` ${values}` +
+				` --cache ${out}.tmp` +
+				` && ( if cmp -s ${out}.tmp ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+			//				` --build-id ${release_version}` +
+			CommandDeps: []string{
+				"${aconfig}",
+			},
+			Restat: true,
+		}, "release_version", "namespace", "values")
+
+	// For device_config_definitions: Generate java file
+	srcJarRule = pctx.AndroidStaticRule("aconfig_srcjar",
+		blueprint.RuleParams{
+			Command: `rm -rf ${out}.tmp` +
+				` && mkdir -p ${out}.tmp` +
+				` && ${aconfig} create-java-lib` +
+				`    --cache ${in}` +
+				`    --out ${out}.tmp` +
+				` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
+				` && rm -rf ${out}.tmp`,
+			CommandDeps: []string{
+				"$aconfig",
+				"$soong_zip",
+			},
+			Restat: true,
+		})
+)
+
+func init() {
+	registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("device_config_definitions", DefinitionsFactory)
+	ctx.RegisterModuleType("device_config_values", ValuesFactory)
+	ctx.RegisterModuleType("device_config_value_set", ValueSetFactory)
+	pctx.HostBinToolVariable("aconfig", "aconfig")
+	pctx.HostBinToolVariable("soong_zip", "soong_zip")
+}
diff --git a/device_config/testing.go b/device_config/testing.go
new file mode 100644
index 0000000..f054476
--- /dev/null
+++ b/device_config/testing.go
@@ -0,0 +1,19 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package device_config
+
+import "android/soong/android"
+
+var PrepareForTestWithSyspropBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents)
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index afb3de3..6ead589 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -17,11 +17,11 @@
 import (
 	"encoding/json"
 	"fmt"
-	"sort"
 	"strconv"
-	"strings"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
 )
 
 // This comment describes the following:
@@ -310,8 +310,8 @@
 	// Nested class loader context shouldn't have conditional part (it is allowed only at the top level).
 	for ver, _ := range nestedClcMap {
 		if ver != AnySdkVersion {
-			clcStr, _ := ComputeClassLoaderContext(nestedClcMap)
-			return fmt.Errorf("nested class loader context shouldn't have conditional part: %s", clcStr)
+			clcPaths := ComputeClassLoaderContextDependencies(nestedClcMap)
+			return fmt.Errorf("nested class loader context shouldn't have conditional part: %+v", clcPaths)
 		}
 	}
 	subcontexts := nestedClcMap[AnySdkVersion]
@@ -418,6 +418,15 @@
 	return string(bytes)
 }
 
+func (clcMap ClassLoaderContextMap) DumpForFlag() string {
+	jsonCLC := toJsonClassLoaderContext(clcMap)
+	bytes, err := json.Marshal(jsonCLC)
+	if err != nil {
+		panic(err)
+	}
+	return proptools.ShellEscapeIncludingSpaces(string(bytes))
+}
+
 // excludeLibsFromCLCList excludes the libraries from the ClassLoaderContext in this list.
 //
 // This treats the supplied list as being immutable (as it may come from a dependency). So, it
@@ -544,67 +553,27 @@
 	return true, nil
 }
 
-// Return the class loader context as a string, and a slice of build paths for all dependencies.
+// Returns a slice of build paths for all possible dependencies that the class loader context may
+// refer to.
 // Perform a depth-first preorder traversal of the class loader context tree for each SDK version.
-// Return the resulting string and a slice of on-host build paths to all library dependencies.
-func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) {
-	// CLC for different SDK versions should come in specific order that agrees with PackageManager.
-	// Since PackageManager processes SDK versions in ascending order and prepends compatibility
-	// libraries at the front, the required order is descending, except for AnySdkVersion that has
-	// numerically the largest order, but must be the last one. Example of correct order: [30, 29,
-	// 28, AnySdkVersion]. There are Soong tests to ensure that someone doesn't change this by
-	// accident, but there is no way to guard against changes in the PackageManager, except for
-	// grepping logcat on the first boot for absence of the following messages:
-	//
-	//   `logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch`
-	//
-	versions := make([]int, 0, len(clcMap))
-	for ver, _ := range clcMap {
-		if ver != AnySdkVersion {
-			versions = append(versions, ver)
-		}
-	}
-	sort.Sort(sort.Reverse(sort.IntSlice(versions))) // descending order
-	versions = append(versions, AnySdkVersion)
-
-	for _, sdkVer := range versions {
-		sdkVerStr := fmt.Sprintf("%d", sdkVer)
-		if sdkVer == AnySdkVersion {
-			sdkVerStr = "any" // a special keyword that means any SDK version
-		}
-		hostClc, targetClc, hostPaths := computeClassLoaderContextRec(clcMap[sdkVer])
-		if hostPaths != nil {
-			clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc)
-			clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc)
-		}
+func ComputeClassLoaderContextDependencies(clcMap ClassLoaderContextMap) android.Paths {
+	var paths android.Paths
+	for _, clcs := range clcMap {
+		hostPaths := ComputeClassLoaderContextDependenciesRec(clcs)
 		paths = append(paths, hostPaths...)
 	}
-	return clcStr, android.FirstUniquePaths(paths)
+	return android.FirstUniquePaths(paths)
 }
 
-// Helper function for ComputeClassLoaderContext() that handles recursion.
-func computeClassLoaderContextRec(clcs []*ClassLoaderContext) (string, string, android.Paths) {
+// Helper function for ComputeClassLoaderContextDependencies() that handles recursion.
+func ComputeClassLoaderContextDependenciesRec(clcs []*ClassLoaderContext) android.Paths {
 	var paths android.Paths
-	var clcsHost, clcsTarget []string
-
 	for _, clc := range clcs {
-		subClcHost, subClcTarget, subPaths := computeClassLoaderContextRec(clc.Subcontexts)
-		if subPaths != nil {
-			subClcHost = "{" + subClcHost + "}"
-			subClcTarget = "{" + subClcTarget + "}"
-		}
-
-		clcsHost = append(clcsHost, "PCL["+clc.Host.String()+"]"+subClcHost)
-		clcsTarget = append(clcsTarget, "PCL["+clc.Device+"]"+subClcTarget)
-
+		subPaths := ComputeClassLoaderContextDependenciesRec(clc.Subcontexts)
 		paths = append(paths, clc.Host)
 		paths = append(paths, subPaths...)
 	}
-
-	clcHost := strings.Join(clcsHost, "#")
-	clcTarget := strings.Join(clcsTarget, "#")
-
-	return clcHost, clcTarget, paths
+	return paths
 }
 
 // Class loader contexts that come from Make via JSON dexpreopt.config. JSON CLC representation is
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 8b3c013..39b4652 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -20,6 +20,7 @@
 import (
 	"fmt"
 	"reflect"
+	"sort"
 	"strings"
 	"testing"
 
@@ -34,7 +35,7 @@
 	// │   └── android.hidl.base
 	// │
 	// └── any
-	//     ├── a
+	//     ├── a'  (a single quotation mark (') is there to test escaping)
 	//     ├── b
 	//     ├── c
 	//     ├── d
@@ -53,7 +54,7 @@
 
 	m := make(ClassLoaderContextMap)
 
-	m.AddContext(ctx, AnySdkVersion, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, AnySdkVersion, "a'", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
 	m.AddContext(ctx, AnySdkVersion, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
 	m.AddContext(ctx, AnySdkVersion, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
 
@@ -96,11 +97,10 @@
 
 	fixClassLoaderContext(m)
 
-	var haveStr string
 	var havePaths android.Paths
 	var haveUsesLibsReq, haveUsesLibsOpt []string
 	if valid && validationError == nil {
-		haveStr, havePaths = ComputeClassLoaderContext(m)
+		havePaths = ComputeClassLoaderContextDependencies(m)
 		haveUsesLibsReq, haveUsesLibsOpt = m.UsesLibs()
 	}
 
@@ -111,29 +111,6 @@
 		}
 	})
 
-	// Test that class loader context structure is correct.
-	t.Run("string", func(t *testing.T) {
-		wantStr := " --host-context-for-sdk 29 " +
-			"PCL[out/soong/" + AndroidHidlManager + ".jar]#" +
-			"PCL[out/soong/" + AndroidHidlBase + ".jar]" +
-			" --target-context-for-sdk 29 " +
-			"PCL[/system/framework/" + AndroidHidlManager + ".jar]#" +
-			"PCL[/system/framework/" + AndroidHidlBase + ".jar]" +
-			" --host-context-for-sdk any " +
-			"PCL[out/soong/a.jar]#PCL[out/soong/b.jar]#PCL[out/soong/c.jar]#PCL[out/soong/d.jar]" +
-			"{PCL[out/soong/a2.jar]#PCL[out/soong/b2.jar]#PCL[out/soong/c2.jar]" +
-			"{PCL[out/soong/a1.jar]#PCL[out/soong/b1.jar]}}#" +
-			"PCL[out/soong/f.jar]#PCL[out/soong/a3.jar]#PCL[out/soong/b3.jar]" +
-			" --target-context-for-sdk any " +
-			"PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]" +
-			"{PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]" +
-			"{PCL[/system/a1.jar]#PCL[/system/b1.jar]}}#" +
-			"PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]"
-		if wantStr != haveStr {
-			t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
-		}
-	})
-
 	// Test that all expected build paths are gathered.
 	t.Run("paths", func(t *testing.T) {
 		wantPaths := []string{
@@ -143,14 +120,28 @@
 			"out/soong/a1.jar", "out/soong/b1.jar",
 			"out/soong/f.jar", "out/soong/a3.jar", "out/soong/b3.jar",
 		}
-		if !reflect.DeepEqual(wantPaths, havePaths.Strings()) {
-			t.Errorf("\nwant paths: %s\nhave paths: %s", wantPaths, havePaths)
-		}
+		actual := havePaths.Strings()
+		// The order does not matter.
+		sort.Strings(wantPaths)
+		sort.Strings(actual)
+		android.AssertArrayString(t, "", wantPaths, actual)
+	})
+
+	// Test the JSON passed to construct_context.py.
+	t.Run("json", func(t *testing.T) {
+		// The tree structure within each SDK version should be kept exactly the same when serialized
+		// to JSON. The order matters because the Python script keeps the order within each SDK version
+		// as is.
+		// The JSON is passed to the Python script as a commandline flag, so quotation ('') and escaping
+		// must be performed.
+		android.AssertStringEquals(t, "", strings.TrimSpace(`
+'{"29":[{"Name":"android.hidl.manager-V1.0-java","Optional":false,"Host":"out/soong/android.hidl.manager-V1.0-java.jar","Device":"/system/framework/android.hidl.manager-V1.0-java.jar","Subcontexts":[]},{"Name":"android.hidl.base-V1.0-java","Optional":false,"Host":"out/soong/android.hidl.base-V1.0-java.jar","Device":"/system/framework/android.hidl.base-V1.0-java.jar","Subcontexts":[]}],"30":[],"42":[],"any":[{"Name":"a'\''","Optional":false,"Host":"out/soong/a.jar","Device":"/system/a.jar","Subcontexts":[]},{"Name":"b","Optional":false,"Host":"out/soong/b.jar","Device":"/system/b.jar","Subcontexts":[]},{"Name":"c","Optional":false,"Host":"out/soong/c.jar","Device":"/system/c.jar","Subcontexts":[]},{"Name":"d","Optional":false,"Host":"out/soong/d.jar","Device":"/system/d.jar","Subcontexts":[{"Name":"a2","Optional":false,"Host":"out/soong/a2.jar","Device":"/system/a2.jar","Subcontexts":[]},{"Name":"b2","Optional":false,"Host":"out/soong/b2.jar","Device":"/system/b2.jar","Subcontexts":[]},{"Name":"c2","Optional":false,"Host":"out/soong/c2.jar","Device":"/system/c2.jar","Subcontexts":[{"Name":"a1","Optional":false,"Host":"out/soong/a1.jar","Device":"/system/a1.jar","Subcontexts":[]},{"Name":"b1","Optional":false,"Host":"out/soong/b1.jar","Device":"/system/b1.jar","Subcontexts":[]}]}]},{"Name":"f","Optional":false,"Host":"out/soong/f.jar","Device":"/system/f.jar","Subcontexts":[]},{"Name":"a3","Optional":false,"Host":"out/soong/a3.jar","Device":"/system/a3.jar","Subcontexts":[]},{"Name":"b3","Optional":false,"Host":"out/soong/b3.jar","Device":"/system/b3.jar","Subcontexts":[]}]}'
+`), m.DumpForFlag())
 	})
 
 	// Test for libraries that are added by the manifest_fixer.
 	t.Run("uses libs", func(t *testing.T) {
-		wantUsesLibsReq := []string{"a", "b", "c", "d", "f", "a3", "b3"}
+		wantUsesLibsReq := []string{"a'", "b", "c", "d", "f", "a3", "b3"}
 		wantUsesLibsOpt := []string{}
 		if !reflect.DeepEqual(wantUsesLibsReq, haveUsesLibsReq) {
 			t.Errorf("\nwant required uses libs: %s\nhave required uses libs: %s", wantUsesLibsReq, haveUsesLibsReq)
@@ -236,49 +227,6 @@
 	checkError(t, err, "nested class loader context shouldn't have conditional part")
 }
 
-// Test for SDK version order in conditional CLC: no matter in what order the libraries are added,
-// they end up in the order that agrees with PackageManager.
-func TestCLCSdkVersionOrder(t *testing.T) {
-	ctx := testContext()
-	optional := false
-	m := make(ClassLoaderContextMap)
-	m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, 29, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, 30, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
-	m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
-
-	valid, validationError := validateClassLoaderContext(m)
-
-	fixClassLoaderContext(m)
-
-	var haveStr string
-	if valid && validationError == nil {
-		haveStr, _ = ComputeClassLoaderContext(m)
-	}
-
-	// Test that validation is successful (all paths are known).
-	t.Run("validate", func(t *testing.T) {
-		if !(valid && validationError == nil) {
-			t.Errorf("invalid class loader context")
-		}
-	})
-
-	// Test that class loader context structure is correct.
-	t.Run("string", func(t *testing.T) {
-		wantStr := " --host-context-for-sdk 30 PCL[out/soong/c.jar]" +
-			" --target-context-for-sdk 30 PCL[/system/c.jar]" +
-			" --host-context-for-sdk 29 PCL[out/soong/b.jar]" +
-			" --target-context-for-sdk 29 PCL[/system/b.jar]" +
-			" --host-context-for-sdk 28 PCL[out/soong/a.jar]" +
-			" --target-context-for-sdk 28 PCL[/system/a.jar]" +
-			" --host-context-for-sdk any PCL[out/soong/d.jar]" +
-			" --target-context-for-sdk any PCL[/system/d.jar]"
-		if wantStr != haveStr {
-			t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
-		}
-	})
-}
-
 func TestCLCMExcludeLibs(t *testing.T) {
 	ctx := testContext()
 	const optional = false
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 0cc3bd6..e61ebe6 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -197,7 +197,7 @@
 
 func init() {
 	pctx.Import("android/soong/android")
-	android.RegisterSingletonType("dexpreopt-soong-config", func() android.Singleton {
+	android.RegisterParallelSingletonType("dexpreopt-soong-config", func() android.Singleton {
 		return &globalSoongConfigSingleton{}
 	})
 }
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 2b38793..20737e3 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -52,7 +52,8 @@
 // GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
 // ModuleConfig.  The produced files and their install locations will be available through rule.Installs().
 func GenerateDexpreoptRule(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
-	global *GlobalConfig, module *ModuleConfig) (rule *android.RuleBuilder, err error) {
+	global *GlobalConfig, module *ModuleConfig, productPackages android.Path) (
+	rule *android.RuleBuilder, err error) {
 
 	defer func() {
 		if r := recover(); r != nil {
@@ -92,7 +93,8 @@
 			generateDM := shouldGenerateDM(module, global)
 
 			for archIdx, _ := range module.Archs {
-				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
+				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage,
+					generateDM, productPackages)
 			}
 		}
 	}
@@ -232,9 +234,9 @@
 		pathtools.ReplaceExtension(filepath.Base(path), "odex"))
 }
 
-func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
-	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
-	appImage bool, generateDM bool) {
+func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
+	global *GlobalConfig, module *ModuleConfig, rule *android.RuleBuilder, archIdx int,
+	profile android.WritablePath, appImage bool, generateDM bool, productPackages android.Path) {
 
 	arch := module.Archs[archIdx]
 
@@ -351,11 +353,13 @@
 		}
 
 		// Generate command that saves host and target class loader context in shell variables.
-		clc, paths := ComputeClassLoaderContext(module.ClassLoaderContexts)
+		paths := ComputeClassLoaderContextDependencies(module.ClassLoaderContexts)
 		rule.Command().
 			Text(`eval "$(`).Tool(globalSoong.ConstructContext).
 			Text(` --target-sdk-version ${target_sdk_version}`).
-			Text(clc).Implicits(paths).
+			FlagWithArg("--context-json=", module.ClassLoaderContexts.DumpForFlag()).
+			FlagWithInput("--product-packages=", productPackages).
+			Implicits(paths).
 			Text(`)"`)
 	}
 
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index ba05d94..8033b48 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -42,7 +42,8 @@
 	// The flag is useful when running dex2oat on system image and vendor image which are built separately.
 	usesTargetFiles = flag.Bool("uses_target_files", false, "whether or not dexpreopt is running on target_files")
 	// basePath indicates the path where target_files.zip is extracted.
-	basePath = flag.String("base_path", ".", "base path where images and tools are extracted")
+	basePath            = flag.String("base_path", ".", "base path where images and tools are extracted")
+	productPackagesPath = flag.String("product_packages", "", "path to product_packages.txt")
 )
 
 type builderContext struct {
@@ -87,6 +88,10 @@
 		usage("--module configuration file is required")
 	}
 
+	if *productPackagesPath == "" {
+		usage("--product_packages configuration file is required")
+	}
+
 	// NOTE: duplicating --out_dir here is incorrect (one should be the another
 	// plus "/soong" but doing so apparently breaks dexpreopt
 	ctx := &builderContext{android.NullConfig(*outDir, *outDir)}
@@ -159,11 +164,12 @@
 			moduleConfig.DexPreoptImageLocationsOnHost[i] = *basePath + location
 		}
 	}
-	writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath)
+	writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath, *productPackagesPath)
 }
 
 func writeScripts(ctx android.BuilderContext, globalSoong *dexpreopt.GlobalSoongConfig,
-	global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string) {
+	global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string,
+	productPackagesPath string) {
 	write := func(rule *android.RuleBuilder, file string) {
 		script := &bytes.Buffer{}
 		script.WriteString(scriptHeader)
@@ -199,7 +205,8 @@
 			panic(err)
 		}
 	}
-	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
+		ctx, globalSoong, global, module, android.PathForTesting(productPackagesPath))
 	if err != nil {
 		panic(err)
 	}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 429b5ff..2b19c9d 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -100,8 +100,9 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testSystemModuleConfig(ctx, "test")
+	productPackages := android.PathForTesting("product_packages.txt")
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -124,6 +125,7 @@
 	systemModule := testSystemModuleConfig(ctx, "Stest")
 	systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
 	productModule := testProductModuleConfig(ctx, "Ptest")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	global.HasSystemOther = true
 
@@ -157,7 +159,7 @@
 	for _, test := range tests {
 		global.PatternsOnSystemOther = test.patterns
 		for _, mt := range test.moduleTests {
-			rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module)
+			rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module, productPackages)
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -182,11 +184,12 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -205,11 +208,12 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testPlatformSystemServerModuleConfig(ctx, "service-A")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"platform:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -228,11 +232,12 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testSystemExtSystemServerModuleConfig(ctx, "service-A")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"system_ext:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -251,11 +256,12 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	global.ApexStandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -274,10 +280,11 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testSystemModuleConfig(ctx, "test")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 6817dce..3e1bbde 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -652,9 +652,7 @@
 		prop := snapshot.SnapshotJsonFlags{}
 		propOut := snapshotLibOut + ".json"
 		prop.InitBaseSnapshotProps(m)
-		if m.subdirProperties.Relative_install_path != nil {
-			prop.RelativeInstallPath = *m.subdirProperties.Relative_install_path
-		}
+		prop.RelativeInstallPath = m.SubDir()
 
 		if m.properties.Filename != nil {
 			prop.Filename = *m.properties.Filename
diff --git a/genrule/Android.bp b/genrule/Android.bp
index 8fb5c40..b201cae 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -15,6 +15,7 @@
         "soong-shared",
     ],
     srcs: [
+        "allowlists.go",
         "genrule.go",
         "locations.go",
     ],
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
new file mode 100644
index 0000000..875dbab
--- /dev/null
+++ b/genrule/allowlists.go
@@ -0,0 +1,147 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package genrule
+
+import (
+	"android/soong/android"
+)
+
+var (
+	DepfileAllowList = []string{
+		"depfile_allowed_for_test",
+		"tflite_support_spm_config",
+		"tflite_support_spm_encoder_config",
+		"gen_uwb_core_proto",
+		"libtextclassifier_fbgen_utils_flatbuffers_flatbuffers_test",
+		"libtextclassifier_fbgen_utils_lua_utils_tests",
+		"libtextclassifier_fbgen_lang_id_common_flatbuffers_model",
+		"libtextclassifier_fbgen_lang_id_common_flatbuffers_embedding-network",
+		"libtextclassifier_fbgen_annotator_datetime_datetime",
+		"libtextclassifier_fbgen_annotator_model",
+		"libtextclassifier_fbgen_annotator_experimental_experimental",
+		"libtextclassifier_fbgen_annotator_entity-data",
+		"libtextclassifier_fbgen_annotator_person_name_person_name_model",
+		"libtextclassifier_fbgen_utils_tflite_text_encoder_config",
+		"libtextclassifier_fbgen_utils_codepoint-range",
+		"libtextclassifier_fbgen_utils_intents_intent-config",
+		"libtextclassifier_fbgen_utils_flatbuffers_flatbuffers",
+		"libtextclassifier_fbgen_utils_zlib_buffer",
+		"libtextclassifier_fbgen_utils_tokenizer",
+		"libtextclassifier_fbgen_utils_grammar_rules",
+		"libtextclassifier_fbgen_utils_grammar_semantics_expression",
+		"libtextclassifier_fbgen_utils_resources",
+		"libtextclassifier_fbgen_utils_i18n_language-tag",
+		"libtextclassifier_fbgen_utils_normalization",
+		"libtextclassifier_fbgen_utils_container_bit-vector",
+		"libtextclassifier_fbgen_actions_actions-entity-data",
+		"libtextclassifier_fbgen_actions_actions_model",
+		"libtextclassifier_fbgen_utils_grammar_testing_value",
+	}
+
+	SandboxingDenyModuleList = []string{
+		"framework-javastream-protos",
+		"RsBalls-rscript",
+		"CtsRsBlasTestCases-rscript",
+		"pvmfw_fdt_template_rs",
+		"RSTest_v14-rscript",
+		"com.android.apex.test.bar_stripped",
+		"com.android.apex.test.sharedlibs_secondary_generated",
+		"ImageProcessingJB-rscript",
+		"RSTest-rscript",
+		"BluetoothGeneratedDumpsysBinarySchema_bfbs",
+		"WmediumdServerProto_h",
+		"TracingVMProtoStub_h",
+		"FrontendStub_h",
+		"VehicleServerProtoStub_cc",
+		"AudioFocusControlProtoStub_cc",
+		"AudioFocusControlProtoStub_h",
+		"TracingVMProtoStub_cc",
+		"VehicleServerProtoStub_h",
+		"hidl2aidl_translate_cpp_test_gen_headers",
+		"hidl2aidl_translate_cpp_test_gen_src",
+		"hidl2aidl_translate_java_test_gen_src",
+		"hidl2aidl_translate_ndk_test_gen_headers",
+		"hidl2aidl_translate_ndk_test_gen_src",
+		"hidl_hash_test_gen",
+		"nos_app_avb_service_genc++",
+		"nos_app_avb_service_genc++_headers",
+		"nos_app_avb_service_genc++_mock",
+		"nos_app_identity_service_genc++",
+		"nos_app_keymaster_service_genc++",
+		"nos_generator_test_service_genc++_headers",
+		"nos_generator_test_service_genc++_mock",
+		"r8retrace-run-retrace",
+		"ltp_config_arm",
+		"ltp_config_arm_64_hwasan",
+		"ltp_config_arm_lowmem",
+		"ltp_config_arm_64",
+		"ltp_config_riscv_64",
+		"ltp_config_x86_64",
+		"vm-tests-tf-lib",
+		"hidl_cpp_impl_test_gen-headers",
+		"pandora_experimental-python-gen-src",
+		"framework-cppstream-protos",
+		"Refocus-rscript",
+		"RSTest_v11-rscript",
+		"RSTest_v16-rscript",
+		"ScriptGroupTest-rscript",
+		"ImageProcessing2-rscript",
+		"ImageProcessing-rscript",
+		"com.android.apex.test.pony_stripped",
+		"com.android.apex.test.baz_stripped",
+		"com.android.apex.test.foo_stripped",
+		"com.android.apex.test.sharedlibs_generated",
+		"CtsRenderscriptTestCases-rscript",
+		"BlueberryFacadeAndCertGeneratedStub_py",
+		"BlueberryFacadeGeneratedStub_cc",
+		"BlueberryFacadeGeneratedStub_h",
+		"BluetoothGeneratedDumpsysDataSchema_h",
+		"FrontendStub_cc",
+		"OpenwrtControlServerProto_cc",
+		"OpenwrtControlServerProto_h",
+		"WmediumdServerProto_cc",
+		"c2hal_test_genc++",
+		"c2hal_test_genc++_headers",
+		"hidl2aidl_test_gen_aidl",
+		"hidl_error_test_gen",
+		"hidl_export_test_gen-headers",
+		"hidl_format_test_diff",
+		"hidl_hash_version_gen",
+		"libbt_topshim_facade_py_proto",
+		"nos_app_identity_service_genc++_headers",
+		"nos_app_identity_service_genc++_mock",
+		"nos_app_keymaster_service_genc++_headers",
+		"nos_app_keymaster_service_genc++_mock",
+		"nos_app_weaver_service_genc++",
+		"nos_app_weaver_service_genc++_headers",
+		"nos_app_weaver_service_genc++_mock",
+		"nos_generator_test_service_genc++",
+		"pandora-python-gen-src",
+	}
+
+	SandboxingDenyPathList = []string{
+		"art/test",
+		"external/perfetto",
+	}
+)
+var DepfileAllowSet = map[string]bool{}
+var SandboxingDenyModuleSet = map[string]bool{}
+var SandboxingDenyPathSet = map[string]bool{}
+
+func init() {
+	android.AddToStringSet(DepfileAllowSet, DepfileAllowList)
+	android.AddToStringSet(SandboxingDenyModuleSet, append(DepfileAllowList, SandboxingDenyModuleList...))
+	android.AddToStringSet(SandboxingDenyPathSet, SandboxingDenyPathList)
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 00adb70..c830fcc 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -452,7 +452,7 @@
 		manifestPath := android.PathForModuleOut(ctx, manifestName)
 
 		// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
-		rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath).SandboxTools()
+		rule := getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath))
 		cmd := rule.Command()
 
 		for _, out := range task.out {
@@ -594,14 +594,16 @@
 func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Allowlist genrule to use depfile until we have a solution to remove it.
 	// TODO(b/235582219): Remove allowlist for genrule
-	if ctx.ModuleType() == "gensrcs" &&
-		!ctx.DeviceConfig().BuildBrokenDepfile() &&
-		Bool(g.properties.Depfile) {
-		ctx.PropertyErrorf(
-			"depfile",
-			"Deprecated to ensure the module type is convertible to Bazel. "+
-				"Try specifying the dependencies explicitly so that there is no need to use depfile. "+
-				"If not possible, the escape hatch is to use BUILD_BROKEN_DEPFILE to bypass the error.")
+	if Bool(g.properties.Depfile) {
+		// TODO(b/283852474): Checking the GenruleSandboxing flag is temporary in
+		// order to pass the presubmit before internal master is updated.
+		if ctx.DeviceConfig().GenruleSandboxing() && !DepfileAllowSet[g.Name()] {
+			ctx.PropertyErrorf(
+				"depfile",
+				"Deprecated to ensure the module type is convertible to Bazel. "+
+					"Try specifying the dependencies explicitly so that there is no need to use depfile. "+
+					"If not possible, the escape hatch is to add the module to allowlists.go to bypass the error.")
+		}
 	}
 
 	g.generateCommonBuildActions(ctx)
@@ -737,7 +739,7 @@
 			// TODO(ccross): this RuleBuilder is a hack to be able to call
 			// rule.Command().PathForOutput.  Replace this with passing the rule into the
 			// generator.
-			rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil).SandboxTools()
+			rule := getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil))
 
 			for _, in := range shard {
 				outFile := android.GenPathWithExt(ctx, finalSubDir, in, String(properties.Output_extension))
@@ -1020,3 +1022,11 @@
 
 	return module
 }
+
+func getSandboxedRuleBuilder(ctx android.ModuleContext, r *android.RuleBuilder) *android.RuleBuilder {
+	if !ctx.DeviceConfig().GenruleSandboxing() || SandboxingDenyPathSet[ctx.ModuleDir()] ||
+		SandboxingDenyModuleSet[ctx.ModuleName()] {
+		return r.SandboxTools()
+	}
+	return r.SandboxInputs()
+}
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 63f8fa9..370fe3d 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -97,8 +97,9 @@
 
 func TestGenruleCmd(t *testing.T) {
 	testcases := []struct {
-		name string
-		prop string
+		name       string
+		moduleName string
+		prop       string
 
 		allowMissingDependencies bool
 
@@ -285,7 +286,8 @@
 			expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out2",
 		},
 		{
-			name: "depfile",
+			name:       "depfile",
+			moduleName: "depfile_allowed_for_test",
 			prop: `
 				out: ["out"],
 				depfile: true,
@@ -397,7 +399,8 @@
 			err: "$(depfile) used without depfile property",
 		},
 		{
-			name: "error no depfile",
+			name:       "error no depfile",
+			moduleName: "depfile_allowed_for_test",
 			prop: `
 				out: ["out"],
 				depfile: true,
@@ -440,11 +443,15 @@
 
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
-			bp := "genrule {\n"
-			bp += "name: \"gen\",\n"
-			bp += test.prop
-			bp += "}\n"
-
+			moduleName := "gen"
+			if test.moduleName != "" {
+				moduleName = test.moduleName
+			}
+			bp := fmt.Sprintf(`
+			genrule {
+			   name: "%s",
+			   %s
+			}`, moduleName, test.prop)
 			var expectedErrors []string
 			if test.err != "" {
 				expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
@@ -455,6 +462,9 @@
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
 				}),
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.GenruleSandboxing = proptools.BoolPtr(true)
+				}),
 				android.FixtureModifyContext(func(ctx *android.TestContext) {
 					ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
 				}),
@@ -466,7 +476,7 @@
 				return
 			}
 
-			gen := result.Module("gen", "").(*Module)
+			gen := result.Module(moduleName, "").(*Module)
 			android.AssertStringEquals(t, "raw commands", test.expect, gen.rawCommands[0])
 		})
 	}
@@ -627,53 +637,42 @@
 	}
 }
 
-func TestGensrcsBuildBrokenDepfile(t *testing.T) {
+func TestGenruleAllowlistingDepfile(t *testing.T) {
 	tests := []struct {
-		name               string
-		prop               string
-		BuildBrokenDepfile *bool
-		err                string
+		name       string
+		prop       string
+		err        string
+		moduleName string
 	}{
 		{
-			name: `error when BuildBrokenDepfile is set to false`,
+			name: `error when module is not allowlisted`,
 			prop: `
 				depfile: true,
 				cmd: "cat $(in) > $(out) && cat $(depfile)",
 			`,
-			BuildBrokenDepfile: proptools.BoolPtr(false),
-			err:                "depfile: Deprecated to ensure the module type is convertible to Bazel",
+			err: "depfile: Deprecated to ensure the module type is convertible to Bazel",
 		},
 		{
-			name: `error when BuildBrokenDepfile is not set`,
+			name: `no error when module is allowlisted`,
 			prop: `
 				depfile: true,
 				cmd: "cat $(in) > $(out) && cat $(depfile)",
 			`,
-			err: "depfile: Deprecated to ensure the module type is convertible to Bazel.",
-		},
-		{
-			name: `no error when BuildBrokenDepfile is explicitly set to true`,
-			prop: `
-				depfile: true,
-				cmd: "cat $(in) > $(out) && cat $(depfile)",
-			`,
-			BuildBrokenDepfile: proptools.BoolPtr(true),
-		},
-		{
-			name: `no error if depfile is not set`,
-			prop: `
-				cmd: "cat $(in) > $(out)",
-			`,
+			moduleName: `depfile_allowed_for_test`,
 		},
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
+			moduleName := "foo"
+			if test.moduleName != "" {
+				moduleName = test.moduleName
+			}
 			bp := fmt.Sprintf(`
 			gensrcs {
-			   name: "foo",
+			   name: "%s",
 			   srcs: ["data.txt"],
 			   %s
-			}`, test.prop)
+			}`, moduleName, test.prop)
 
 			var expectedErrors []string
 			if test.err != "" {
@@ -682,9 +681,7 @@
 			android.GroupFixturePreparers(
 				prepareForGenRuleTest,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-					if test.BuildBrokenDepfile != nil {
-						variables.BuildBrokenDepfile = test.BuildBrokenDepfile
-					}
+					variables.GenruleSandboxing = proptools.BoolPtr(true)
 				}),
 			).
 				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
diff --git a/java/androidmk.go b/java/androidmk.go
index 148d7c2..9c21633 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -133,13 +133,19 @@
 	return entriesList
 }
 
-func (j *JavaFuzzLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+func (j *JavaFuzzTest) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := j.Library.AndroidMkEntries()
 	entries := &entriesList[0]
 	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", "null-suite")
-		androidMkWriteTestData(j.jniFilePaths, entries)
 		androidMkWriteTestData(android.Paths{j.implementationJarFile}, entries)
+		androidMkWriteTestData(j.jniFilePaths, entries)
+		if j.fuzzPackagedModule.Corpus != nil {
+			androidMkWriteTestData(j.fuzzPackagedModule.Corpus, entries)
+		}
+		if j.fuzzPackagedModule.Dictionary != nil {
+			androidMkWriteTestData(android.Paths{j.fuzzPackagedModule.Dictionary}, entries)
+		}
 	})
 	return entriesList
 }
diff --git a/java/app.go b/java/app.go
index 3344647..561ce1d 100755
--- a/java/app.go
+++ b/java/app.go
@@ -585,19 +585,6 @@
 		certificates = append([]Certificate{mainCert}, certificates...)
 	}
 
-	if !m.Platform() {
-		certPath := certificates[0].Pem.String()
-		systemCertPath := ctx.Config().DefaultAppCertificateDir(ctx).String()
-		if strings.HasPrefix(certPath, systemCertPath) {
-			enforceSystemCert := ctx.Config().EnforceSystemCertificate()
-			allowed := ctx.Config().EnforceSystemCertificateAllowList()
-
-			if enforceSystemCert && !inList(m.Name(), allowed) {
-				ctx.PropertyErrorf("certificate", "The module in product partition cannot be signed with certificate in system.")
-			}
-		}
-	}
-
 	if len(certificates) > 0 {
 		mainCertificate = certificates[0]
 	} else {
@@ -613,6 +600,19 @@
 		}
 	}
 
+	if !m.Platform() {
+		certPath := mainCertificate.Pem.String()
+		systemCertPath := ctx.Config().DefaultAppCertificateDir(ctx).String()
+		if strings.HasPrefix(certPath, systemCertPath) {
+			enforceSystemCert := ctx.Config().EnforceSystemCertificate()
+			allowed := ctx.Config().EnforceSystemCertificateAllowList()
+
+			if enforceSystemCert && !inList(m.Name(), allowed) {
+				ctx.PropertyErrorf("certificate", "The module in product partition cannot be signed with certificate in system.")
+			}
+		}
+	}
+
 	return mainCertificate, certificates
 }
 
@@ -620,13 +620,21 @@
 	return a.installApkName
 }
 
-func (a *AndroidApp) createPrivappAllowlist(ctx android.ModuleContext) *android.OutputPath {
+func (a *AndroidApp) createPrivappAllowlist(ctx android.ModuleContext) android.Path {
 	if a.appProperties.Privapp_allowlist == nil {
 		return nil
 	}
+
+	isOverrideApp := a.GetOverriddenBy() != ""
+	if !isOverrideApp {
+		// if this is not an override, we don't need to rewrite the existing privapp allowlist
+		return android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist)
+	}
+
 	if a.overridableAppProperties.Package_name == nil {
 		ctx.PropertyErrorf("privapp_allowlist", "package_name must be set to use privapp_allowlist")
 	}
+
 	packageName := *a.overridableAppProperties.Package_name
 	fileName := "privapp_allowlist_" + packageName + ".xml"
 	outPath := android.PathForModuleOut(ctx, fileName).OutputPath
@@ -786,17 +794,17 @@
 	// Install the app package.
 	shouldInstallAppPackage := (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() && !a.appProperties.PreventInstall
 	if shouldInstallAppPackage {
+		if a.privAppAllowlist.Valid() {
+			installPath := android.PathForModuleInstall(ctx, "etc", "permissions")
+			ctx.InstallFile(installPath, a.privAppAllowlist.Path().Base(), a.privAppAllowlist.Path())
+		}
+
 		var extraInstalledPaths android.Paths
 		for _, extra := range a.extraOutputFiles {
 			installed := ctx.InstallFile(a.installDir, extra.Base(), extra)
 			extraInstalledPaths = append(extraInstalledPaths, installed)
 		}
 		ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...)
-
-		if a.privAppAllowlist.Valid() {
-			installPath := android.PathForModuleInstall(ctx, "etc", "permissions")
-			ctx.InstallFile(installPath, a.privAppAllowlist.Path().Base(), a.privAppAllowlist.Path())
-		}
 	}
 
 	a.buildAppDependencyInfo(ctx)
@@ -976,6 +984,10 @@
 // For OutputFileProducer interface
 func (a *AndroidApp) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
+	// In some instances, it can be useful to reference the aapt-generated flags from another
+	// target, e.g., system server implements services declared in the framework-res manifest.
+	case ".aapt.proguardOptionsFile":
+		return []android.Path{a.proguardOptionsFile}, nil
 	case ".aapt.srcjar":
 		return []android.Path{a.aaptSrcJar}, nil
 	case ".export-package.apk":
diff --git a/java/app_test.go b/java/app_test.go
index daff94c..7f9f0ed 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -443,9 +443,9 @@
 	inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
 	var crtbeginFound, crtendFound bool
 	expectedCrtBegin := ctx.ModuleForTests("crtbegin_so",
-		"android_arm64_armv8-a_sdk_29").Rule("partialLd").Output
+		"android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output
 	expectedCrtEnd := ctx.ModuleForTests("crtend_so",
-		"android_arm64_armv8-a_sdk_29").Rule("partialLd").Output
+		"android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output
 	implicits := []string{}
 	for _, input := range inputs {
 		implicits = append(implicits, input.String())
@@ -2693,52 +2693,11 @@
 		`--optional-uses-library baz `
 	android.AssertStringDoesContain(t, "verify apk cmd args", verifyApkCmd, verifyApkArgs)
 
-	// Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs
+	// Test that necessary args are passed for constructing CLC in Ninja phase.
 	cmd := app.Rule("dexpreopt").RuleParams.Command
-	w := `--target-context-for-sdk any ` +
-		`PCL[/system/framework/qux.jar]#` +
-		`PCL[/system/framework/quuz.jar]#` +
-		`PCL[/system/framework/foo.jar]#` +
-		`PCL[/system/framework/non-sdk-lib.jar]#` +
-		`PCL[/system/framework/bar.jar]#` +
-		`PCL[/system/framework/runtime-library.jar]#` +
-		`PCL[/system/framework/runtime-required-x.jar]#` +
-		`PCL[/system/framework/runtime-optional-x.jar]#` +
-		`PCL[/system/framework/runtime-required-y.jar]#` +
-		`PCL[/system/framework/runtime-optional-y.jar] `
-	android.AssertStringDoesContain(t, "dexpreopt app cmd args", cmd, w)
-
-	// Test conditional context for target SDK version 28.
-	android.AssertStringDoesContain(t, "dexpreopt app cmd 28", cmd,
-		`--target-context-for-sdk 28`+
-			` PCL[/system/framework/org.apache.http.legacy.jar] `)
-
-	// Test conditional context for target SDK version 29.
-	android.AssertStringDoesContain(t, "dexpreopt app cmd 29", cmd,
-		`--target-context-for-sdk 29`+
-			` PCL[/system/framework/android.hidl.manager-V1.0-java.jar]`+
-			`#PCL[/system/framework/android.hidl.base-V1.0-java.jar] `)
-
-	// Test conditional context for target SDK version 30.
-	// "android.test.mock" is absent because "android.test.runner" is not used.
-	android.AssertStringDoesContain(t, "dexpreopt app cmd 30", cmd,
-		`--target-context-for-sdk 30`+
-			` PCL[/system/framework/android.test.base.jar] `)
-
-	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
-	android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd", cmd,
-		`--target-context-for-sdk any`+
-			` PCL[/system/framework/foo.jar]`+
-			`#PCL[/system/framework/non-sdk-lib.jar]`+
-			`#PCL[/system/framework/android.test.runner.jar]`+
-			`#PCL[/system/framework/bar.jar] `)
-
-	// Test conditional context for target SDK version 30.
-	// "android.test.mock" is present because "android.test.runner" is used.
-	android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd 30", cmd,
-		`--target-context-for-sdk 30`+
-			` PCL[/system/framework/android.test.base.jar]`+
-			`#PCL[/system/framework/android.test.mock.jar] `)
+	android.AssertStringDoesContain(t, "dexpreopt app cmd context", cmd, "--context-json=")
+	android.AssertStringDoesContain(t, "dexpreopt app cmd product_packages", cmd,
+		"--product-packages=out/soong/target/product/test_device/product_packages.txt")
 }
 
 func TestDexpreoptBcp(t *testing.T) {
@@ -3382,6 +3341,14 @@
 			srcs: ["a.java"],
 			certificate: ":missing_certificate",
 			sdk_version: "current",
+		}
+
+		android_app {
+			name: "bar",
+			srcs: ["a.java"],
+			certificate: ":missing_certificate",
+			product_specific: true,
+			sdk_version: "current",
 		}`)
 
 	foo := result.ModuleForTests("foo", "android_common")
@@ -3555,9 +3522,8 @@
 		android_app {
 			name: "foo",
 			srcs: ["a.java"],
-			privapp_allowlist: "perms.xml",
+			privapp_allowlist: "privapp_allowlist_com.android.foo.xml",
 			privileged: true,
-			package_name: "com.android.foo",
 			sdk_version: "current",
 		}
 		override_android_app {
@@ -3570,20 +3536,94 @@
 	app := result.ModuleForTests("foo", "android_common")
 	overrideApp := result.ModuleForTests("foo", "android_common_bar")
 
-	// verify that privapp allowlist is created
-	app.Output("out/soong/.intermediates/foo/android_common/privapp_allowlist_com.android.foo.xml")
+	// verify that privapp allowlist is created for override apps
 	overrideApp.Output("out/soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml")
-	expectedAllowlist := "perms.xml"
-	actualAllowlist := app.Rule("modifyAllowlist").Input.String()
-	if expectedAllowlist != actualAllowlist {
-		t.Errorf("expected allowlist to be %q; got %q", expectedAllowlist, actualAllowlist)
-	}
-	overrideActualAllowlist := overrideApp.Rule("modifyAllowlist").Input.String()
-	if expectedAllowlist != overrideActualAllowlist {
-		t.Errorf("expected override allowlist to be %q; got %q", expectedAllowlist, overrideActualAllowlist)
+	expectedAllowlistInput := "privapp_allowlist_com.android.foo.xml"
+	overrideActualAllowlistInput := overrideApp.Rule("modifyAllowlist").Input.String()
+	if expectedAllowlistInput != overrideActualAllowlistInput {
+		t.Errorf("expected override allowlist to be %q; got %q", expectedAllowlistInput, overrideActualAllowlistInput)
 	}
 
 	// verify that permissions are copied to device
 	app.Output("out/soong/target/product/test_device/system/etc/permissions/privapp_allowlist_com.android.foo.xml")
 	overrideApp.Output("out/soong/target/product/test_device/system/etc/permissions/privapp_allowlist_com.google.android.foo.xml")
 }
+
+func TestPrivappAllowlistAndroidMk(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.PrepareForTestWithAndroidMk,
+	).RunTestWithBp(
+		t,
+		`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			privapp_allowlist: "privapp_allowlist_com.android.foo.xml",
+			privileged: true,
+			sdk_version: "current",
+		}
+		override_android_app {
+			name: "bar",
+			base: "foo",
+			package_name: "com.google.android.foo",
+		}
+		`,
+	)
+	baseApp := result.ModuleForTests("foo", "android_common")
+	overrideApp := result.ModuleForTests("foo", "android_common_bar")
+
+	baseAndroidApp := baseApp.Module().(*AndroidApp)
+	baseEntries := android.AndroidMkEntriesForTest(t, result.TestContext, baseAndroidApp)[0]
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALLED_MODULE; expected to find foo.apk",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALLED_MODULE"][0],
+		"\\S+foo.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include foo.apk",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+foo.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include app",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+foo.apk:\\S+/target/product/test_device/system/priv-app/foo/foo.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include privapp_allowlist",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"privapp_allowlist_com.android.foo.xml:\\S+/target/product/test_device/system/etc/permissions/privapp_allowlist_com.android.foo.xml",
+	)
+
+	overrideAndroidApp := overrideApp.Module().(*AndroidApp)
+	overrideEntries := android.AndroidMkEntriesForTest(t, result.TestContext, overrideAndroidApp)[0]
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALLED_MODULE; expected to find bar.apk",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALLED_MODULE"][0],
+		"\\S+bar.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include bar.apk",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+bar.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include app",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+bar.apk:\\S+/target/product/test_device/system/priv-app/bar/bar.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include privapp_allowlist",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml:\\S+/target/product/test_device/system/etc/permissions/privapp_allowlist_com.google.android.foo.xml",
+	)
+}
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 2541f14..9bdef74 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -432,3 +432,39 @@
 	fragment = result.Module("a_test_fragment", "android_common").(*BootclasspathFragmentModule)
 	android.AssertBoolEquals(t, "is a test fragment by type", true, fragment.isTestFragment())
 }
+
+func TestBootclassFragment_LinkTextStub(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		prepareForTestWithBootclasspathFragment,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("mysdklibrary"),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetBuildFromTextStub(true)
+		}),
+	).RunTestWithBp(t, `
+        bootclasspath_fragment {
+            name: "myfragment",
+            contents: ["mysdklibrary"],
+            hidden_api: {split_packages: ["*"]},
+            additional_stubs: [
+                "android-non-updatable",
+            ],
+        }
+        java_sdk_library {
+            name: "mysdklibrary",
+            srcs: ["a.java"],
+            shared_library: false,
+            public: {enabled: true},
+            system: {enabled: true},
+        }
+    `)
+
+	fragment := result.ModuleForTests("myfragment", "android_common")
+	ruleCommand := fragment.Rule("modularHiddenAPIStubFlagsFile").RuleParams.Command
+	android.AssertStringDoesContain(t, "Command expected to contain library as dependency stub dex",
+		ruleCommand, "--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.module_lib.from-text/android_common/dex/android-non-updatable.stubs.module_lib.from-text.jar")
+	android.AssertStringDoesNotContain(t,
+		"Command not expected to contain multiple api_library as dependency stub dex", ruleCommand,
+		"--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.from-text/android_common/dex/android-non-updatable.stubs.from-text.jar")
+}
diff --git a/java/config/config.go b/java/config/config.go
index b82a137..195dae1 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -98,7 +98,7 @@
 		"-JDcom.android.tools.r8.emitRecordAnnotationsExInDex",
 	}, dexerJavaVmFlagsList...))
 	exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
-		"-JXmx2048M",
+		"-JXmx4096M",
 		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitRecordAnnotationsExInDex",
diff --git a/java/core-libraries/TxtStubLibraries.bp b/java/core-libraries/TxtStubLibraries.bp
index 813187e..0cf0f36 100644
--- a/java/core-libraries/TxtStubLibraries.bp
+++ b/java/core-libraries/TxtStubLibraries.bp
@@ -22,8 +22,6 @@
     libs: [
         "core-current-stubs-for-system-modules-no-annotations.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -36,8 +34,6 @@
         "core.current.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 // Same as core-module-lib-stubs-system-modules, but the stubs are generated from .txt files
@@ -47,8 +43,6 @@
     libs: [
         "core-module-lib-stubs-for-system-modules-no-annotations.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -61,8 +55,6 @@
         "core.module_lib.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -79,8 +71,6 @@
     sdk_version: "none",
     system_modules: "none",
     visibility: ["//visibility:private"],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 // Same as legacy-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files
@@ -91,8 +81,6 @@
         "legacy.core.platform.api.no.annotations.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -108,8 +96,6 @@
         "legacy.core.platform.api.stubs.from-text",
     ],
     patch_module: "java.base",
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 // Same as stable-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files
@@ -120,8 +106,6 @@
         "stable.core.platform.api.no.annotations.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -137,8 +121,6 @@
         "stable.core.platform.api.stubs.from-text",
     ],
     patch_module: "java.base",
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_api_library {
@@ -151,6 +133,4 @@
         // LambdaMetaFactory depends on CallSite etc. which is part of the Core API surface
         "core.current.stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
diff --git a/java/dex.go b/java/dex.go
index f7c1361..7e7da00 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -161,7 +161,7 @@
 		"$r8Template": &remoteexec.REParams{
 			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
 			Inputs:          []string{"$implicits", "${config.R8Jar}"},
-			OutputFiles:     []string{"${outUsage}"},
+			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}"},
 			ExecStrategy:    "${config.RER8ExecStrategy}",
 			ToolchainInputs: []string{"${config.JavaCmd}"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
@@ -399,13 +399,16 @@
 			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
 		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:            rule,
-			Description:     "r8",
-			Output:          javalibJar,
-			ImplicitOutputs: android.WritablePaths{proguardDictionary, proguardUsageZip},
-			Input:           dexParams.classesJar,
-			Implicits:       r8Deps,
-			Args:            args,
+			Rule:        rule,
+			Description: "r8",
+			Output:      javalibJar,
+			ImplicitOutputs: android.WritablePaths{
+				proguardDictionary,
+				proguardUsageZip,
+				proguardConfiguration},
+			Input:     dexParams.classesJar,
+			Implicits: r8Deps,
+			Args:      args,
 		})
 	} else {
 		d8Flags, d8Deps := d8Flags(dexParams.flags)
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index a96b312..e588c9a 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -390,7 +390,11 @@
 
 	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 
-	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
+	// "product_packages.txt" is generated by `build/make/core/Makefile`.
+	productPackages := android.PathForModuleInPartitionInstall(ctx, "", "product_packages.txt")
+
+	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
+		ctx, globalSoong, global, dexpreoptConfig, productPackages)
 	if err != nil {
 		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
 		return
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f477f40..116c833 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -465,7 +465,7 @@
 }
 
 func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
+	ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
 }
 
 func SkipDexpreoptBootJars(ctx android.PathContext) bool {
diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go
index 83c088c..7499481 100644
--- a/java/dexpreopt_check.go
+++ b/java/dexpreopt_check.go
@@ -28,7 +28,7 @@
 }
 
 func RegisterDexpreoptCheckBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory)
+	ctx.RegisterParallelSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory)
 }
 
 // A build-time check to verify if all compilation artifacts of system server jars are installed
diff --git a/java/fuzz.go b/java/fuzz.go
index 9a0c908..5dfaacf 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -19,12 +19,12 @@
 	"sort"
 	"strings"
 
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
-
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/fuzz"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 const (
@@ -32,63 +32,63 @@
 	targetString = "target"
 )
 
-type jniProperties struct {
-	// list of jni libs
-	Jni_libs []string
-
-	// sanitization
-	Sanitizers []string
-}
-
 func init() {
 	RegisterJavaFuzzBuildComponents(android.InitRegistrationContext)
 }
 
 func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("java_fuzz", JavaFuzzFactory)
-	ctx.RegisterModuleType("java_fuzz_host", JavaFuzzHostFactory)
-	ctx.RegisterSingletonType("java_fuzz_host_packaging", javaFuzzHostPackagingFactory)
-	ctx.RegisterSingletonType("java_fuzz_device_packaging", javaFuzzDevicePackagingFactory)
+	ctx.RegisterParallelSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory)
 }
 
-type JavaFuzzLibrary struct {
-	Library
+type JavaFuzzTest struct {
+	Test
 	fuzzPackagedModule fuzz.FuzzPackagedModule
-	jniProperties      jniProperties
 	jniFilePaths       android.Paths
 }
 
-// IsSanitizerEnabledForJni implemented to make JavaFuzzLibrary implement
-// cc.JniSanitizeable. It returns a bool for whether a cc dependency should be
-// sanitized for the given sanitizer or not.
-func (j *JavaFuzzLibrary) IsSanitizerEnabledForJni(ctx android.BaseModuleContext, sanitizerName string) bool {
-	// TODO: once b/231370928 is resolved, please uncomment the loop
-	//     for _, s := range j.jniProperties.Sanitizers {
-	//         if sanitizerName == s {
-	//             return true
-	//         }
-	//     }
-	return false
+// java_fuzz builds and links sources into a `.jar` file for the device.
+// This generates .class files in a jar which can then be instrumented before
+// fuzzing in Android Runtime (ART: Android OS on emulator or device)
+func JavaFuzzFactory() android.Module {
+	module := &JavaFuzzTest{}
+
+	module.addHostAndDeviceProperties()
+	module.AddProperties(&module.testProperties)
+	module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
+
+	module.Module.properties.Installable = proptools.BoolPtr(true)
+	module.Module.dexpreopter.isTest = true
+	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		disableLinuxBionic := struct {
+			Target struct {
+				Linux_bionic struct {
+					Enabled *bool
+				}
+			}
+		}{}
+		disableLinuxBionic.Target.Linux_bionic.Enabled = proptools.BoolPtr(false)
+		ctx.AppendProperties(&disableLinuxBionic)
+	})
+
+	InitJavaModuleMultiTargets(module, android.HostAndDeviceSupported)
+	return module
 }
 
-func (j *JavaFuzzLibrary) DepsMutator(mctx android.BottomUpMutatorContext) {
-	if len(j.jniProperties.Jni_libs) > 0 {
-		if j.fuzzPackagedModule.FuzzProperties.Fuzz_config == nil {
-			config := &fuzz.FuzzConfig{}
-			j.fuzzPackagedModule.FuzzProperties.Fuzz_config = config
-		}
-		// this will be used by the ingestion pipeline to determine the version
-		// of jazzer to add to the fuzzer package
-		j.fuzzPackagedModule.FuzzProperties.Fuzz_config.IsJni = proptools.BoolPtr(true)
-		for _, target := range mctx.MultiTargets() {
+func (j *JavaFuzzTest) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if len(j.testProperties.Jni_libs) > 0 {
+		for _, target := range ctx.MultiTargets() {
 			sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
-			mctx.AddFarVariationDependencies(sharedLibVariations, cc.JniFuzzLibTag, j.jniProperties.Jni_libs...)
+			ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, j.testProperties.Jni_libs...)
 		}
 	}
-	j.Library.DepsMutator(mctx)
+
+	j.deps(ctx)
 }
 
-func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (j *JavaFuzzTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
 		j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
 	}
@@ -105,21 +105,15 @@
 	}
 
 	_, sharedDeps := cc.CollectAllSharedDependencies(ctx)
-
 	for _, dep := range sharedDeps {
 		sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo)
 		if sharedLibInfo.SharedLibrary != nil {
-			// The .class jars are output in slightly different locations
-			// relative to the jni libs. Therefore, for consistency across
-			// host and device fuzzers of jni lib location, we save it in a
-			// native_libs directory.
-			var relPath string
+			arch := "lib"
 			if sharedLibInfo.Target.Arch.ArchType.Multilib == "lib64" {
-				relPath = filepath.Join("lib64", sharedLibInfo.SharedLibrary.Base())
-			} else {
-				relPath = filepath.Join("lib", sharedLibInfo.SharedLibrary.Base())
+				arch = "lib64"
 			}
-			libPath := android.PathForModuleOut(ctx, relPath)
+
+			libPath := android.PathForModuleOut(ctx, filepath.Join(arch, sharedLibInfo.SharedLibrary.Base()))
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   android.Cp,
 				Input:  sharedLibInfo.SharedLibrary,
@@ -129,104 +123,37 @@
 		} else {
 			ctx.PropertyErrorf("jni_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
 		}
+
 	}
 
-	j.Library.GenerateAndroidBuildActions(ctx)
+	j.Test.GenerateAndroidBuildActions(ctx)
 }
 
-// java_fuzz_host builds and links sources into a `.jar` file for the host.
-//
-// By default, a java_fuzz produces a `.jar` file containing `.class` files.
-// This jar is not suitable for installing on a device.
-func JavaFuzzHostFactory() android.Module {
-	module := &JavaFuzzLibrary{}
-	module.addHostProperties()
-	module.AddProperties(&module.jniProperties)
-	module.Module.properties.Installable = proptools.BoolPtr(true)
-	module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
-
-	// java_fuzz packaging rules collide when both linux_glibc and linux_bionic are enabled, disable the linux_bionic variants.
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		disableLinuxBionic := struct {
-			Target struct {
-				Linux_bionic struct {
-					Enabled *bool
-				}
-			}
-		}{}
-		disableLinuxBionic.Target.Linux_bionic.Enabled = proptools.BoolPtr(false)
-		ctx.AppendProperties(&disableLinuxBionic)
-	})
-
-	InitJavaModuleMultiTargets(module, android.HostSupportedNoCross)
-	return module
-}
-
-// java_fuzz builds and links sources into a `.jar` file for the device.
-// This generates .class files in a jar which can then be instrumented before
-// fuzzing in Android Runtime (ART: Android OS on emulator or device)
-func JavaFuzzFactory() android.Module {
-	module := &JavaFuzzLibrary{}
-	module.addHostAndDeviceProperties()
-	module.AddProperties(&module.jniProperties)
-	module.Module.properties.Installable = proptools.BoolPtr(true)
-	module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
-	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
-	InitJavaModuleMultiTargets(module, android.DeviceSupported)
-	return module
-}
-
-// Responsible for generating rules that package host fuzz targets into
-// a zip file.
-type javaFuzzHostPackager struct {
+type javaFuzzPackager struct {
 	fuzz.FuzzPackager
 }
 
-// Responsible for generating rules that package device fuzz targets into
-// a zip file.
-type javaFuzzDevicePackager struct {
-	fuzz.FuzzPackager
+func javaFuzzPackagingFactory() android.Singleton {
+	return &javaFuzzPackager{}
 }
 
-func javaFuzzHostPackagingFactory() android.Singleton {
-	return &javaFuzzHostPackager{}
-}
-
-func javaFuzzDevicePackagingFactory() android.Singleton {
-	return &javaFuzzDevicePackager{}
-}
-
-func (s *javaFuzzHostPackager) GenerateBuildActions(ctx android.SingletonContext) {
-	generateBuildActions(&s.FuzzPackager, hostString, ctx)
-}
-
-func (s *javaFuzzDevicePackager) GenerateBuildActions(ctx android.SingletonContext) {
-	generateBuildActions(&s.FuzzPackager, targetString, ctx)
-}
-
-func generateBuildActions(s *fuzz.FuzzPackager, hostOrTargetString string, ctx android.SingletonContext) {
+func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
 	// Map between each architecture + host/device combination.
 	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
 
-	// List of individual fuzz targets.
 	s.FuzzTargets = make(map[string]bool)
-
 	ctx.VisitAllModules(func(module android.Module) {
 		// Discard non-fuzz targets.
-		javaFuzzModule, ok := module.(*JavaFuzzLibrary)
+		javaFuzzModule, ok := module.(*JavaFuzzTest)
 		if !ok {
 			return
 		}
 
-		if hostOrTargetString == hostString {
-			if !javaFuzzModule.Host() {
-				return
-			}
-		} else if hostOrTargetString == targetString {
-			if javaFuzzModule.Host() || javaFuzzModule.Target().HostCross {
-				return
-			}
+		hostOrTargetString := "target"
+		if javaFuzzModule.Target().HostCross {
+			hostOrTargetString = "host_cross"
+		} else if javaFuzzModule.Host() {
+			hostOrTargetString = "host"
 		}
 
 		fuzzModuleValidator := fuzz.FuzzModule{
@@ -246,11 +173,15 @@
 		var files []fuzz.FileToZip
 		builder := android.NewRuleBuilder(pctx, ctx)
 
-		// Package the artifacts (data, corpus, config and dictionary into a zipfile.
+		// Package the artifacts (data, corpus, config and dictionary) into a zipfile.
 		files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder)
 
 		// Add .jar
-		files = append(files, fuzz.FileToZip{SourceFilePath: javaFuzzModule.implementationJarFile})
+		if !javaFuzzModule.Host() {
+			files = append(files, fuzz.FileToZip{SourceFilePath: javaFuzzModule.implementationJarFile, DestinationPathPrefix: "classes"})
+		}
+
+		files = append(files, fuzz.FileToZip{SourceFilePath: javaFuzzModule.outputFile})
 
 		// Add jni .so files
 		for _, fPath := range javaFuzzModule.jniFilePaths {
@@ -261,27 +192,14 @@
 		if !ok {
 			return
 		}
-
 	})
 	s.CreateFuzzPackage(ctx, archDirs, fuzz.Java, pctx)
 }
 
-func (s *javaFuzzHostPackager) MakeVars(ctx android.MakeVarsContext) {
+func (s *javaFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
 	packages := s.Packages.Strings()
 	sort.Strings(packages)
-
-	ctx.Strict("SOONG_JAVA_FUZZ_HOST_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
-
+	ctx.Strict("SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
 	// Preallocate the slice of fuzz targets to minimize memory allocations.
-	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_HOST_TARGETS")
-}
-
-func (s *javaFuzzDevicePackager) MakeVars(ctx android.MakeVarsContext) {
-	packages := s.Packages.Strings()
-	sort.Strings(packages)
-
-	ctx.Strict("SOONG_JAVA_FUZZ_DEVICE_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
-
-	// Preallocate the slice of fuzz targets to minimize memory allocations.
-	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_DEVICE_TARGETS")
+	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_TARGETS")
 }
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
index 186c3aa..dd1e96b 100644
--- a/java/fuzz_test.go
+++ b/java/fuzz_test.go
@@ -31,18 +31,16 @@
 
 func TestJavaFuzz(t *testing.T) {
 	result := prepForJavaFuzzTest.RunTestWithBp(t, `
-		java_fuzz_host {
+		java_fuzz {
 			name: "foo",
 			srcs: ["a.java"],
+			host_supported: true,
+			device_supported: false,
 			libs: ["bar"],
 			static_libs: ["baz"],
             jni_libs: [
                 "libjni",
             ],
-            sanitizers: [
-                "address",
-                "fuzzer",
-            ],
 		}
 
 		java_library_host {
@@ -84,7 +82,7 @@
 	}
 
 	ctx := result.TestContext
-	foo := ctx.ModuleForTests("foo", osCommonTarget).Module().(*JavaFuzzLibrary)
+	foo := ctx.ModuleForTests("foo", osCommonTarget).Module().(*JavaFuzzTest)
 
 	expected := "lib64/libjni.so"
 	if runtime.GOOS == "darwin" {
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 96e084a..e54275b 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -647,7 +647,7 @@
 	// public version is provided by the art.module.public.api module. In those cases it is necessary
 	// to treat all those modules as they were the same name, otherwise it will result in multiple
 	// definitions of a single class being passed to hidden API processing which will cause an error.
-	if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule {
+	if name == scope.nonUpdatablePrebuiltModule || name == android.JavaApiLibraryName(ctx.Config(), scope.nonUpdatableSourceModule) {
 		// Treat all *android-non-updatable* modules as if they were part of an android-non-updatable
 		// java_sdk_library.
 		// TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent.
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 52934a3..d4ee4fc 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -25,7 +25,7 @@
 }
 
 func RegisterHiddenApiSingletonComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
+	ctx.RegisterParallelSingletonType("hiddenapi", hiddenAPISingletonFactory)
 }
 
 var PrepareForTestWithHiddenApiBuildComponents = android.FixtureRegisterWithContext(RegisterHiddenApiSingletonComponents)
diff --git a/java/java.go b/java/java.go
index a98762c..aa9f936 100644
--- a/java/java.go
+++ b/java/java.go
@@ -73,8 +73,8 @@
 		ctx.BottomUp("jacoco_deps", jacocoDepsMutator).Parallel()
 	})
 
-	ctx.RegisterSingletonType("logtags", LogtagsSingleton)
-	ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
+	ctx.RegisterParallelSingletonType("logtags", LogtagsSingleton)
+	ctx.RegisterParallelSingletonType("kythe_java_extract", kytheExtractJavaFactory)
 }
 
 func RegisterJavaSdkMemberTypes() {
@@ -1156,7 +1156,6 @@
 	}
 
 	j.addDataDeviceBinsDeps(ctx)
-
 	j.deps(ctx)
 }
 
@@ -1651,7 +1650,7 @@
 	// list of api.txt files relative to this directory that contribute to the
 	// API surface.
 	// This is a list of relative paths
-	Api_files []string
+	Api_files []string `android:"path"`
 
 	// List of flags to be passed to the javac compiler to generate jar file
 	Javacflags []string
@@ -1814,7 +1813,7 @@
 		case javaApiContributionTag:
 			provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
 			providerApiFile := provider.ApiFile
-			if providerApiFile == nil {
+			if providerApiFile == nil && !ctx.Config().AllowMissingDependencies() {
 				ctx.ModuleErrorf("Error: %s has an empty api file.", dep.Name())
 			}
 			srcFiles = append(srcFiles, android.PathForSource(ctx, providerApiFile.String()))
@@ -1833,12 +1832,10 @@
 
 	// Add the api_files inputs
 	for _, api := range al.properties.Api_files {
-		// Use MaybeExistentPathForSource since the api file might not exist during analysis.
-		// This will be provided by the orchestrator in the combined execution.
-		srcFiles = append(srcFiles, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), api))
+		srcFiles = append(srcFiles, android.PathForModuleSrc(ctx, api))
 	}
 
-	if srcFiles == nil {
+	if srcFiles == nil && !ctx.Config().AllowMissingDependencies() {
 		ctx.ModuleErrorf("Error: %s has an empty api file.", ctx.ModuleName())
 	}
 
@@ -2609,6 +2606,7 @@
 		&appProperties{},
 		&appTestProperties{},
 		&overridableAppProperties{},
+		&hostTestProperties{},
 		&testProperties{},
 		&ImportProperties{},
 		&AARImportProperties{},
@@ -2706,6 +2704,15 @@
 	Resource_strip_prefix *string
 }
 
+func (m *Library) javaResourcesGetSingleFilegroupStripPrefix(ctx android.TopDownMutatorContext) (string, bool) {
+	if otherM, ok := ctx.ModuleFromName(m.properties.Java_resources[0]); ok && len(m.properties.Java_resources) == 1 {
+		if fg, isFilegroup := otherM.(android.FileGroupPath); isFilegroup {
+			return filepath.Join(ctx.OtherModuleDir(otherM), fg.GetPath(ctx)), true
+		}
+	}
+	return "", false
+}
+
 func (m *Library) convertJavaResourcesAttributes(ctx android.TopDownMutatorContext) *javaResourcesAttributes {
 	var resources bazel.LabelList
 	var resourceStripPrefix *string
@@ -2715,8 +2722,12 @@
 	}
 
 	if m.properties.Java_resources != nil {
+		if prefix, ok := m.javaResourcesGetSingleFilegroupStripPrefix(ctx); ok {
+			resourceStripPrefix = proptools.StringPtr(prefix)
+		} else {
+			resourceStripPrefix = proptools.StringPtr(ctx.ModuleDir())
+		}
 		resources.Append(android.BazelLabelForModuleSrc(ctx, m.properties.Java_resources))
-		resourceStripPrefix = proptools.StringPtr(ctx.ModuleDir())
 	}
 
 	//TODO(b/179889880) handle case where glob includes files outside package
diff --git a/java/java_test.go b/java/java_test.go
index 2a4913e..ea89e6e 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -2252,6 +2252,29 @@
 	android.AssertStringDoesContain(t, "Command expected to contain output files list text file flag", manifestCommand, "--out __SBOX_SANDBOX_DIR__/out/sources.txt")
 }
 
+func TestJavaApiLibraryFilegroupInput(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+	    filegroup {
+			name: "default_current.txt",
+			srcs: ["current.txt"],
+		}
+
+		java_api_library {
+			name: "foo",
+			api_files: [":default_current.txt"],
+		}
+		`,
+		map[string][]byte{
+			"current.txt": nil,
+		})
+
+	m := ctx.ModuleForTests("foo", "android_common")
+	outputs := fmt.Sprint(m.AllOutputs())
+	if !strings.Contains(outputs, "foo/foo.jar") {
+		t.Errorf("Module output does not contain expected jar %s", "foo/foo.jar")
+	}
+}
+
 func TestTradefedOptions(t *testing.T) {
 	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
 java_test_host {
diff --git a/java/jdeps.go b/java/jdeps.go
index a52b867..4c8c11c 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -26,7 +26,7 @@
 // called. Dependency info file is generated in $OUT/module_bp_java_depend.json.
 
 func init() {
-	android.RegisterSingletonType("jdeps_generator", jDepsGeneratorSingleton)
+	android.RegisterParallelSingletonType("jdeps_generator", jDepsGeneratorSingleton)
 }
 
 func jDepsGeneratorSingleton() android.Singleton {
diff --git a/java/lint.go b/java/lint.go
index 40ef484..a0f9970 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -705,7 +705,7 @@
 var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
 
 func init() {
-	android.RegisterSingletonType("lint",
+	android.RegisterParallelSingletonType("lint",
 		func() android.Singleton { return &lintSingleton{} })
 
 	registerLintBuildComponents(android.InitRegistrationContext)
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 07fb92c..0d4db7c 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -26,7 +26,7 @@
 }
 
 func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory)
+	ctx.RegisterParallelSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory)
 }
 
 // The tags used for the dependencies between the platform bootclasspath and any configured boot
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index d417291..2197304 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -36,7 +36,7 @@
 }
 
 func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
+	ctx.RegisterParallelSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
 	ctx.RegisterModuleType("platform_compat_config", PlatformCompatConfigFactory)
 	ctx.RegisterModuleType("prebuilt_platform_compat_config", prebuiltCompatConfigFactory)
 	ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory)
diff --git a/java/robolectric.go b/java/robolectric.go
index 008b8b1..6bbe872 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -144,29 +144,37 @@
 	roboTestConfig := android.PathForModuleGen(ctx, "robolectric").
 		Join(ctx, "com/android/tools/test_config.properties")
 
+	var ok bool
+	var instrumentedApp *AndroidApp
+
 	// TODO: this inserts paths to built files into the test, it should really be inserting the contents.
 	instrumented := ctx.GetDirectDepsWithTag(instrumentationForTag)
 
-	if len(instrumented) != 1 {
+	if len(instrumented) == 1 {
+		instrumentedApp, ok = instrumented[0].(*AndroidApp)
+		if !ok {
+			ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app")
+		}
+	} else if !ctx.Config().AllowMissingDependencies() {
 		panic(fmt.Errorf("expected exactly 1 instrumented dependency, got %d", len(instrumented)))
 	}
 
-	instrumentedApp, ok := instrumented[0].(*AndroidApp)
-	if !ok {
-		ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app")
+	if instrumentedApp != nil {
+		r.manifest = instrumentedApp.mergedManifestFile
+		r.resourceApk = instrumentedApp.outputFile
+
+		generateRoboTestConfig(ctx, roboTestConfig, instrumentedApp)
+		r.extraResources = android.Paths{roboTestConfig}
 	}
 
-	r.manifest = instrumentedApp.mergedManifestFile
-	r.resourceApk = instrumentedApp.outputFile
-
-	generateRoboTestConfig(ctx, roboTestConfig, instrumentedApp)
-	r.extraResources = android.Paths{roboTestConfig}
-
 	r.Library.GenerateAndroidBuildActions(ctx)
 
 	roboSrcJar := android.PathForModuleGen(ctx, "robolectric", ctx.ModuleName()+".srcjar")
-	r.generateRoboSrcJar(ctx, roboSrcJar, instrumentedApp)
-	r.roboSrcJar = roboSrcJar
+
+	if instrumentedApp != nil {
+		r.generateRoboSrcJar(ctx, roboSrcJar, instrumentedApp)
+		r.roboSrcJar = roboSrcJar
+	}
 
 	roboTestConfigJar := android.PathForModuleOut(ctx, "robolectric_samedir", "samedir_config.jar")
 	generateSameDirRoboTestConfigJar(ctx, roboTestConfigJar)
@@ -177,7 +185,10 @@
 		// once the Make test runner is removed.
 		roboTestConfigJar,
 		r.outputFile,
-		instrumentedApp.implementationAndResourcesJar,
+	}
+
+	if instrumentedApp != nil {
+		combinedJarJars = append(combinedJarJars, instrumentedApp.implementationAndResourcesJar)
 	}
 
 	handleLibDeps := func(dep android.Module) {
@@ -213,21 +224,28 @@
 		r.tests = append(r.tests, s)
 	}
 
-	r.data = append(r.data, r.manifest, r.resourceApk)
+	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
+	var installDeps android.Paths
+
+	if r.manifest != nil {
+		r.data = append(r.data, r.manifest)
+		installedManifest := ctx.InstallFile(installPath, ctx.ModuleName()+"-AndroidManifest.xml", r.manifest)
+		installDeps = append(installDeps, installedManifest)
+	}
+
+	if r.resourceApk != nil {
+		r.data = append(r.data, r.resourceApk)
+		installedResourceApk := ctx.InstallFile(installPath, ctx.ModuleName()+".apk", r.resourceApk)
+		installDeps = append(installDeps, installedResourceApk)
+	}
 
 	runtimes := ctx.GetDirectDepWithTag("robolectric-android-all-prebuilts", roboRuntimesTag)
-
-	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
-
-	installedResourceApk := ctx.InstallFile(installPath, ctx.ModuleName()+".apk", r.resourceApk)
-	installedManifest := ctx.InstallFile(installPath, ctx.ModuleName()+"-AndroidManifest.xml", r.manifest)
-	installedConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig)
-
-	var installDeps android.Paths
 	for _, runtime := range runtimes.(*robolectricRuntimes).runtimes {
 		installDeps = append(installDeps, runtime)
 	}
-	installDeps = append(installDeps, installedResourceApk, installedManifest, installedConfig)
+
+	installedConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig)
+	installDeps = append(installDeps, installedConfig)
 
 	for _, data := range android.PathsForModuleSrc(ctx, r.testProperties.Data) {
 		installedData := ctx.InstallFile(installPath, data.Rel(), data)
@@ -340,7 +358,9 @@
 	fmt.Fprintln(w, "LOCAL_MODULE :=", name)
 	android.AndroidMkEmitAssignList(w, "LOCAL_JAVA_LIBRARIES", []string{module}, r.libs)
 	fmt.Fprintln(w, "LOCAL_TEST_PACKAGE :=", String(r.robolectricProperties.Instrumentation_for))
-	fmt.Fprintln(w, "LOCAL_INSTRUMENT_SRCJARS :=", r.roboSrcJar.String())
+	if r.roboSrcJar != nil {
+		fmt.Fprintln(w, "LOCAL_INSTRUMENT_SRCJARS :=", r.roboSrcJar.String())
+	}
 	android.AndroidMkEmitAssignList(w, "LOCAL_ROBOTEST_FILES", tests)
 	if t := r.robolectricProperties.Test_options.Timeout; t != nil {
 		fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
diff --git a/java/sdk.go b/java/sdk.go
index 8b4918a..7fa604f 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -28,7 +28,7 @@
 
 func init() {
 	android.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
-	android.RegisterSingletonType("sdk", sdkSingletonFactory)
+	android.RegisterParallelSingletonType("sdk", sdkSingletonFactory)
 	android.RegisterMakeVarsProvider(pctx, sdkMakeVars)
 }
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 103f1ac..89da19a 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -156,6 +156,9 @@
 
 	// Whether the api scope can be treated as unstable, and should skip compat checks.
 	unstable bool
+
+	// Represents the SDK kind of this scope.
+	kind android.SdkKind
 }
 
 // Initialize a scope, creating and adding appropriate dependency tags
@@ -229,6 +232,10 @@
 	return ".stubs" + scope.moduleSuffix
 }
 
+func (scope *apiScope) apiLibraryModuleName(baseName string) string {
+	return scope.stubsLibraryModuleName(baseName) + ".from-text"
+}
+
 func (scope *apiScope) stubsLibraryModuleName(baseName string) string {
 	return baseName + scope.stubsLibraryModuleNameSuffix()
 }
@@ -289,6 +296,7 @@
 			return &module.sdkLibraryProperties.Public
 		},
 		sdkVersion: "current",
+		kind:       android.SdkPublic,
 	})
 	apiScopeSystem = initApiScope(&apiScope{
 		name:                "system",
@@ -301,6 +309,7 @@
 		moduleSuffix:  ".system",
 		sdkVersion:    "system_current",
 		annotation:    "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS)",
+		kind:          android.SdkSystem,
 	})
 	apiScopeTest = initApiScope(&apiScope{
 		name:                "test",
@@ -314,6 +323,7 @@
 		sdkVersion:    "test_current",
 		annotation:    "android.annotation.TestApi",
 		unstable:      true,
+		kind:          android.SdkTest,
 	})
 	apiScopeModuleLib = initApiScope(&apiScope{
 		name:    "module-lib",
@@ -331,6 +341,7 @@
 		moduleSuffix:  ".module_lib",
 		sdkVersion:    "module_current",
 		annotation:    "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)",
+		kind:          android.SdkModule,
 	})
 	apiScopeSystemServer = initApiScope(&apiScope{
 		name:    "system-server",
@@ -361,6 +372,7 @@
 			// com.android.* classes are okay in this interface"
 			"--hide", "InternalClasses",
 		},
+		kind: android.SdkSystemServer,
 	})
 	allApiScopes = apiScopes{
 		apiScopePublic,
@@ -842,6 +854,13 @@
 	return c.namingScheme.stubsSourceModuleName(apiScope, baseName)
 }
 
+// Name of the java_api_library module that generates the from-text stubs source
+// and compiles to a jar file.
+func (c *commonToSdkLibraryAndImport) apiLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.BaseModuleName()
+	return c.namingScheme.apiLibraryModuleName(apiScope, baseName)
+}
+
 // The component names for different outputs of the java_sdk_library.
 //
 // They are similar to the names used for the child modules it creates
@@ -1269,7 +1288,9 @@
 		// Add dependencies to the stubs library
 		stubModuleName := module.stubsLibraryModuleName(apiScope)
 		// Use JavaApiLibraryName function to be redirected to stubs generated from .txt if applicable
-		stubModuleName = android.JavaApiLibraryName(ctx.Config(), stubModuleName)
+		if module.contributesToApiSurface(ctx.Config()) {
+			stubModuleName = android.JavaApiLibraryName(ctx.Config(), stubModuleName)
+		}
 		ctx.AddVariationDependencies(nil, apiScope.stubsTag, stubModuleName)
 
 		// Add a dependency on the stubs source in order to access both stubs source and api information.
@@ -1477,6 +1498,11 @@
 	return latestPrebuiltApiModuleName(module.distStem()+"-incompatibilities", apiScope)
 }
 
+func (module *SdkLibrary) contributesToApiSurface(c android.Config) bool {
+	_, exists := c.GetApiLibraries()[module.Name()]
+	return exists
+}
+
 func childModuleVisibility(childVisibility []string) []string {
 	if childVisibility == nil {
 		// No child visibility set. The child will use the visibility of the sdk_library.
@@ -1758,6 +1784,46 @@
 	mctx.CreateModule(DroidstubsFactory, &props).(*Droidstubs).CallHookIfAvailable(mctx)
 }
 
+func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
+	props := struct {
+		Name              *string
+		Visibility        []string
+		Api_contributions []string
+		Libs              []string
+		Static_libs       []string
+		Dep_api_srcs      *string
+	}{}
+
+	props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope))
+	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
+
+	apiContributions := []string{}
+
+	// Api surfaces are not independent of each other, but have subset relationships,
+	// and so does the api files. To generate from-text stubs for api surfaces other than public,
+	// all subset api domains' api_contriubtions must be added as well.
+	scope := apiScope
+	for scope != nil {
+		apiContributions = append(apiContributions, module.stubsSourceModuleName(scope)+".api.contribution")
+		scope = scope.extends
+	}
+
+	props.Api_contributions = apiContributions
+	props.Libs = module.properties.Libs
+	props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...)
+	props.Libs = append(props.Libs, "stub-annotations")
+	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
+	props.Dep_api_srcs = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + ".from-text")
+
+	// android_module_lib_stubs_current.from-text only comprises api contributions from art, conscrypt and i18n.
+	// Thus, replace with android_module_lib_stubs_current_full.from-text, which comprises every api domains.
+	if apiScope.kind == android.SdkModule {
+		props.Dep_api_srcs = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + "_full.from-text")
+	}
+
+	mctx.CreateModule(ApiLibraryFactory, &props)
+}
+
 func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool {
 	return !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api)
 }
@@ -1954,6 +2020,10 @@
 		module.createStubsSourcesAndApi(mctx, scope, module.stubsSourceModuleName(scope), scope.droidstubsArgs)
 
 		module.createStubsLibrary(mctx, scope)
+
+		if module.contributesToApiSurface(mctx.Config()) {
+			module.createApiLibrary(mctx, scope)
+		}
 	}
 
 	if module.requiresRuntimeImplementationLibrary() {
@@ -2006,6 +2076,8 @@
 	stubsLibraryModuleName(scope *apiScope, baseName string) string
 
 	stubsSourceModuleName(scope *apiScope, baseName string) string
+
+	apiLibraryModuleName(scope *apiScope, baseName string) string
 }
 
 type defaultNamingScheme struct {
@@ -2019,6 +2091,10 @@
 	return scope.stubsSourceModuleName(baseName)
 }
 
+func (s *defaultNamingScheme) apiLibraryModuleName(scope *apiScope, baseName string) string {
+	return scope.apiLibraryModuleName(baseName)
+}
+
 var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
 
 func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 1d0c13d..141e16b 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -35,6 +35,9 @@
 			"29": {"foo"},
 			"30": {"bar", "barney", "baz", "betty", "foo", "fred", "quuz", "wilma"},
 		}),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetApiLibraries([]string{"foo"})
+		}),
 	).RunTestWithBp(t, `
 		droiddoc_exported_dir {
 			name: "droiddoc-templates-sdk",
@@ -121,6 +124,7 @@
 	result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
 	result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
 	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "")
+	result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common")
 	result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
 	result.ModuleForTests("foo.api.public.28", "")
 	result.ModuleForTests("foo.api.system.28", "")
@@ -1412,3 +1416,62 @@
 	fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs)
 	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs, "bar-lib")
 }
+
+func TestJavaSdkLibrary_ApiLibrary(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetApiLibraries([]string{"foo"})
+		}),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
+			test: {
+				enabled: true,
+			},
+		}
+	`)
+
+	testCases := []struct {
+		scope            *apiScope
+		apiContributions []string
+		depApiSrcs       string
+	}{
+		{
+			scope:            apiScopePublic,
+			apiContributions: []string{"foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_stubs_current.from-text",
+		},
+		{
+			scope:            apiScopeSystem,
+			apiContributions: []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_system_stubs_current.from-text",
+		},
+		{
+			scope:            apiScopeTest,
+			apiContributions: []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_test_stubs_current.from-text",
+		},
+		{
+			scope:            apiScopeModuleLib,
+			apiContributions: []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_module_lib_stubs_current_full.from-text",
+		},
+	}
+
+	for _, c := range testCases {
+		m := result.ModuleForTests(c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary)
+		android.AssertArrayString(t, "Module expected to contain api contributions", c.apiContributions, m.properties.Api_contributions)
+		android.AssertStringEquals(t, "Module expected to contain full api surface api library", c.depApiSrcs, *m.properties.Dep_api_srcs)
+	}
+}
diff --git a/java/testing.go b/java/testing.go
index 6671bf0..3a238d7 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -71,7 +71,12 @@
 		// Needed for framework
 		defaultJavaDir + "/framework/aidl": nil,
 		// Needed for various deps defined in GatherRequiredDepsForTest()
-		defaultJavaDir + "/a.java": nil,
+		defaultJavaDir + "/a.java":                        nil,
+		defaultJavaDir + "/api/current.txt":               nil,
+		defaultJavaDir + "/api/system-current.txt":        nil,
+		defaultJavaDir + "/api/test-current.txt":          nil,
+		defaultJavaDir + "/api/module-lib-current.txt":    nil,
+		defaultJavaDir + "/api/system-server-current.txt": nil,
 
 		// Needed for R8 rules on apps
 		"build/make/core/proguard.flags":             nil,
@@ -395,16 +400,20 @@
 	}
 
 	extraApiLibraryModules := map[string]string{
-		"android_stubs_current.from-text":                 "api/current.txt",
-		"android_system_stubs_current.from-text":          "api/system-current.txt",
-		"android_test_stubs_current.from-text":            "api/test-current.txt",
-		"android_module_lib_stubs_current.from-text":      "api/module-lib-current.txt",
-		"android_module_lib_stubs_current_full.from-text": "api/module-lib-current.txt",
-		"android_system_server_stubs_current.from-text":   "api/system-server-current.txt",
-		"core.current.stubs.from-text":                    "api/current.txt",
-		"legacy.core.platform.api.stubs.from-text":        "api/current.txt",
-		"stable.core.platform.api.stubs.from-text":        "api/current.txt",
-		"core-lambda-stubs.from-text":                     "api/current.txt",
+		"android_stubs_current.from-text":                  "api/current.txt",
+		"android_system_stubs_current.from-text":           "api/system-current.txt",
+		"android_test_stubs_current.from-text":             "api/test-current.txt",
+		"android_module_lib_stubs_current.from-text":       "api/module-lib-current.txt",
+		"android_module_lib_stubs_current_full.from-text":  "api/module-lib-current.txt",
+		"android_system_server_stubs_current.from-text":    "api/system-server-current.txt",
+		"core.current.stubs.from-text":                     "api/current.txt",
+		"legacy.core.platform.api.stubs.from-text":         "api/current.txt",
+		"stable.core.platform.api.stubs.from-text":         "api/current.txt",
+		"core-lambda-stubs.from-text":                      "api/current.txt",
+		"android-non-updatable.stubs.from-text":            "api/current.txt",
+		"android-non-updatable.stubs.system.from-text":     "api/system-current.txt",
+		"android-non-updatable.stubs.test.from-text":       "api/test-current.txt",
+		"android-non-updatable.stubs.module_lib.from-text": "api/module-lib-current.txt",
 	}
 
 	for libName, apiFile := range extraApiLibraryModules {
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 8225df6..c0ae0c0 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -870,7 +870,7 @@
 	}
 
 	// Safeguard against $(call inherit-product,$(PRODUCT_PATH))
-	const maxMatchingFiles = 150
+	const maxMatchingFiles = 155 // temporarily increased to 155 for b/284854738
 	if len(matchingPaths) > maxMatchingFiles {
 		return []starlarkNode{ctx.newBadNode(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)}
 	}
diff --git a/multitree/metadata.go b/multitree/metadata.go
index 3fd7215..0eb0efc 100644
--- a/multitree/metadata.go
+++ b/multitree/metadata.go
@@ -20,7 +20,7 @@
 )
 
 func init() {
-	android.RegisterSingletonType("update-meta", UpdateMetaSingleton)
+	android.RegisterParallelSingletonType("update-meta", UpdateMetaSingleton)
 }
 
 func UpdateMetaSingleton() android.Singleton {
diff --git a/provenance/provenance_singleton.go b/provenance/provenance_singleton.go
index fbb6212..5d27c0c 100644
--- a/provenance/provenance_singleton.go
+++ b/provenance/provenance_singleton.go
@@ -51,7 +51,7 @@
 }
 
 func RegisterProvenanceSingleton(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("provenance_metadata_singleton", provenanceInfoSingletonFactory)
+	ctx.RegisterParallelSingletonType("provenance_metadata_singleton", provenanceInfoSingletonFactory)
 }
 
 var PrepareForTestWithProvenanceSingleton = android.FixtureRegisterWithContext(RegisterProvenanceSingleton)
diff --git a/python/test.go b/python/test.go
index 31da17e..6e23a44 100644
--- a/python/test.go
+++ b/python/test.go
@@ -39,7 +39,7 @@
 }
 
 func PythonTestHostFactory() android.Module {
-	return NewTest(android.HostSupportedNoCross).init()
+	return NewTest(android.HostSupported).init()
 }
 
 func PythonTestFactory() android.Module {
@@ -66,6 +66,10 @@
 
 	// Test options.
 	Test_options TestOptions
+
+	// list of device binary modules that should be installed alongside the test
+	// This property adds 64bit AND 32bit variants of the dependency
+	Data_device_bins_both []string `android:"arch_variant"`
 }
 
 type TestOptions struct {
@@ -98,12 +102,48 @@
 	android.InitAndroidArchModule(p, p.hod, p.multilib)
 	android.InitDefaultableModule(p)
 	android.InitBazelModule(p)
-	if p.hod == android.HostSupportedNoCross && p.testProperties.Test_options.Unit_test == nil {
+	if p.isTestHost() && p.testProperties.Test_options.Unit_test == nil {
 		p.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
 	}
 	return p
 }
 
+func (p *PythonTestModule) isTestHost() bool {
+	return p.hod == android.HostSupported
+}
+
+var dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"}
+
+// python_test_host DepsMutator uses this method to add multilib dependencies of
+// data_device_bin_both
+func (p *PythonTestModule) addDataDeviceBinsDeps(ctx android.BottomUpMutatorContext, filter string) {
+	if len(p.testProperties.Data_device_bins_both) < 1 {
+		return
+	}
+
+	var maybeAndroidTarget *android.Target
+	androidTargetList := android.FirstTarget(ctx.Config().Targets[android.Android], filter)
+	if len(androidTargetList) > 0 {
+		maybeAndroidTarget = &androidTargetList[0]
+	}
+
+	if maybeAndroidTarget != nil {
+		ctx.AddFarVariationDependencies(
+			maybeAndroidTarget.Variations(),
+			dataDeviceBinsTag,
+			p.testProperties.Data_device_bins_both...,
+		)
+	}
+}
+
+func (p *PythonTestModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	p.PythonBinaryModule.DepsMutator(ctx)
+	if p.isTestHost() {
+		p.addDataDeviceBinsDeps(ctx, "lib32")
+		p.addDataDeviceBinsDeps(ctx, "lib64")
+	}
+}
+
 func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// We inherit from only the library's GenerateAndroidBuildActions, and then
 	// just use buildBinary() so that the binary is not installed into the location
@@ -153,6 +193,12 @@
 		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
 	}
 
+	if p.isTestHost() && len(p.testProperties.Data_device_bins_both) > 0 {
+		ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) {
+			p.data = append(p.data, android.DataPath{SrcPath: android.OutputFileForModule(ctx, dep, "")})
+		})
+	}
+
 	// Emulate the data property for java_data dependencies.
 	for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
 		for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
diff --git a/rust/doc.go b/rust/doc.go
index fe3581b..6970d79 100644
--- a/rust/doc.go
+++ b/rust/doc.go
@@ -19,7 +19,7 @@
 )
 
 func init() {
-	android.RegisterSingletonType("rustdoc", RustdocSingleton)
+	android.RegisterParallelSingletonType("rustdoc", RustdocSingleton)
 }
 
 func RustdocSingleton() android.Singleton {
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index 865665e..7fa9f5c 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -46,18 +46,16 @@
 
 	// Check that compiler flags are set appropriately .
 	fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
-	if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
-		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov-module'") ||
+	if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov-module'") ||
 		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
-		t.Errorf("rust_fuzz module does not contain the expected flags (sancov-module, cfg fuzzing, hwaddress sanitizer).")
+		t.Errorf("rust_fuzz module does not contain the expected flags (sancov-module, cfg fuzzing).")
 
 	}
 
 	// Check that dependencies have 'fuzzer' variants produced for them as well.
 	libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
-	if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
-		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov-module'") ||
+	if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov-module'") ||
 		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
-		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov-module, cfg fuzzing, hwaddress sanitizer).")
+		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov-module, cfg fuzzing).")
 	}
 }
diff --git a/rust/project_json.go b/rust/project_json.go
index fe259d6..40aa7c7 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -74,7 +74,7 @@
 }
 
 func init() {
-	android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+	android.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
 }
 
 // sourceProviderVariantSource returns the path to the source file if this
diff --git a/rust/rust.go b/rust/rust.go
index dc53cc0..4324ecb 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -45,7 +45,7 @@
 	})
 	pctx.Import("android/soong/rust/config")
 	pctx.ImportAs("cc_config", "android/soong/cc/config")
-	android.InitRegistrationContext.RegisterSingletonType("kythe_rust_extract", kytheExtractRustFactory)
+	android.InitRegistrationContext.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory)
 }
 
 type Flags struct {
diff --git a/rust/sanitize.go b/rust/sanitize.go
index c68137e..83cf055 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -226,11 +226,6 @@
 	}
 	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
 		flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
-		if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() {
-			flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
-		} else {
-			flags.RustFlags = append(flags.RustFlags, asanFlags...)
-		}
 	} else if Bool(sanitize.Properties.Sanitize.Hwaddress) {
 		flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
 	} else if Bool(sanitize.Properties.Sanitize.Address) {
@@ -424,14 +419,6 @@
 		return true
 	}
 
-	// TODO(b/178365482): Rust/CC interop doesn't work just yet; don't sanitize rust_ffi modules until
-	// linkage issues are resolved.
-	if lib, ok := mod.compiler.(libraryInterface); ok {
-		if lib.shared() || lib.static() {
-			return true
-		}
-	}
-
 	return mod.sanitize.isSanitizerExplicitlyDisabled(t)
 }
 
diff --git a/rust/testing.go b/rust/testing.go
index 0a6a870..7f30569 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -200,8 +200,8 @@
 		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
-	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
-	ctx.RegisterSingletonType("kythe_rust_extract", kytheExtractRustFactory)
+	ctx.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+	ctx.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory)
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
 	})
diff --git a/scripts/construct_context.py b/scripts/construct_context.py
index 3f601c3..fc3a89e 100755
--- a/scripts/construct_context.py
+++ b/scripts/construct_context.py
@@ -19,6 +19,7 @@
 from __future__ import print_function
 
 import argparse
+import json
 import sys
 
 from manifest import compare_version_gt
@@ -33,20 +34,14 @@
         dest='sdk',
         help='specify target SDK version (as it appears in the manifest)')
     parser.add_argument(
-        '--host-context-for-sdk',
-        dest='host_contexts',
-        action='append',
-        nargs=2,
-        metavar=('sdk', 'context'),
-        help='specify context on host for a given SDK version or "any" version')
+        '--context-json',
+        default='',
+        dest='context_json',
+    )
     parser.add_argument(
-        '--target-context-for-sdk',
-        dest='target_contexts',
-        action='append',
-        nargs=2,
-        metavar=('sdk', 'context'),
-        help='specify context on target for a given SDK version or "any" '
-        'version'
+        '--product-packages',
+        default='',
+        dest='product_packages_file',
     )
     return parser.parse_args(args)
 
@@ -55,28 +50,69 @@
 # context regardless of the target SDK version.
 any_sdk = 'any'
 
-
-# We assume that the order of context arguments passed to this script is
-# correct (matches the order computed by package manager). It is possible to
-# sort them here, but Soong needs to use deterministic order anyway, so it can
-# as well use the correct order.
-def construct_context(versioned_contexts, target_sdk):
-    context = []
-    for [sdk, ctx] in versioned_contexts:
-        if sdk == any_sdk or compare_version_gt(sdk, target_sdk):
-            context.append(ctx)
-    return context
+context_sep = '#'
 
 
-def construct_contexts(args):
-    host_context = construct_context(args.host_contexts, args.sdk)
-    target_context = construct_context(args.target_contexts, args.sdk)
-    context_sep = '#'
+def encode_class_loader(context, product_packages):
+    host_sub_contexts, target_sub_contexts = encode_class_loaders(
+            context['Subcontexts'], product_packages)
+
+    return ('PCL[%s]%s' % (context['Host'], host_sub_contexts),
+            'PCL[%s]%s' % (context['Device'], target_sub_contexts))
+
+
+def encode_class_loaders(contexts, product_packages):
+    host_contexts = []
+    target_contexts = []
+
+    for context in contexts:
+        if not context['Optional'] or context['Name'] in product_packages:
+            host_context, target_context = encode_class_loader(
+                    context, product_packages)
+            host_contexts.append(host_context)
+            target_contexts.append(target_context)
+
+    if host_contexts:
+        return ('{%s}' % context_sep.join(host_contexts),
+                '{%s}' % context_sep.join(target_contexts))
+    else:
+        return '', ''
+
+
+def construct_context_args(target_sdk, context_json, product_packages):
+    all_contexts = []
+
+    # CLC for different SDK versions should come in specific order that agrees
+    # with PackageManager. Since PackageManager processes SDK versions in
+    # ascending order and prepends compatibility libraries at the front, the
+    # required order is descending, except for any_sdk that has numerically
+    # the largest order, but must be the last one. Example of correct order:
+    # [30, 29, 28, any_sdk]. There are Python tests to ensure that someone
+    # doesn't change this by accident, but there is no way to guard against
+    # changes in the PackageManager, except for grepping logcat on the first
+    # boot for absence of the following messages:
+    #
+    #   `logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch`
+
+    for sdk, contexts in sorted(
+            ((sdk, contexts)
+             for sdk, contexts in context_json.items()
+             if sdk != any_sdk and compare_version_gt(sdk, target_sdk)),
+            key=lambda item: int(item[0]), reverse=True):
+        all_contexts += contexts
+
+    if any_sdk in context_json:
+        all_contexts += context_json[any_sdk]
+
+    host_contexts, target_contexts = encode_class_loaders(
+            all_contexts, product_packages)
+
     return (
-        'class_loader_context_arg=--class-loader-context=PCL[]{%s} ; ' %
-        context_sep.join(host_context) +
-        'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{%s}' #pylint: disable=line-too-long
-        % context_sep.join(target_context))
+        'class_loader_context_arg=--class-loader-context=PCL[]%s ; ' %
+        host_contexts +
+        'stored_class_loader_context_arg='
+        '--stored-class-loader-context=PCL[]%s'
+        % target_contexts)
 
 
 def main():
@@ -85,12 +121,12 @@
         args = parse_args(sys.argv[1:])
         if not args.sdk:
             raise SystemExit('target sdk version is not set')
-        if not args.host_contexts:
-            args.host_contexts = []
-        if not args.target_contexts:
-            args.target_contexts = []
 
-        print(construct_contexts(args))
+        context_json = json.loads(args.context_json)
+        with open(args.product_packages_file, 'r') as f:
+            product_packages = set(line.strip() for line in f if line.strip())
+
+        print(construct_context_args(args.sdk, context_json, product_packages))
 
     # pylint: disable=broad-except
     except Exception as err:
diff --git a/scripts/construct_context_test.py b/scripts/construct_context_test.py
index 2ff5ac5..34a0e16 100755
--- a/scripts/construct_context_test.py
+++ b/scripts/construct_context_test.py
@@ -24,62 +24,249 @@
 sys.dont_write_bytecode = True
 
 
-def construct_contexts(arglist):
-    args = cc.parse_args(arglist)
-    return cc.construct_contexts(args)
+CONTEXT_JSON = {
+    '28': [
+        {
+            'Name': 'z',
+            'Optional': False,
+            'Host': 'out/zdir/z.jar',
+            'Device': '/system/z.jar',
+            'Subcontexts': [],
+        },
+    ],
+    '29': [
+        {
+            'Name': 'x',
+            'Optional': False,
+            'Host': 'out/xdir/x.jar',
+            'Device': '/system/x.jar',
+            'Subcontexts': [],
+        },
+        {
+            'Name': 'y',
+            'Optional': False,
+            'Host': 'out/ydir/y.jar',
+            'Device': '/product/y.jar',
+            'Subcontexts': [],
+        },
+    ],
+    'any': [
+        {
+            'Name': 'a',
+            'Optional': False,
+            'Host': 'out/adir/a.jar',
+            'Device': '/system/a.jar',
+            'Subcontexts': [
+                {  # Not installed optional, being the only child.
+                    'Name': 'a1',
+                    'Optional': True,
+                    'Host': 'out/a1dir/a1.jar',
+                    'Device': '/product/a1.jar',
+                    'Subcontexts': [],
+                },
+            ],
+        },
+        {
+            'Name': 'b',
+            'Optional': True,
+            'Host': 'out/bdir/b.jar',
+            'Device': '/product/b.jar',
+            'Subcontexts': [
+                {  # Not installed but required.
+                    'Name': 'b1',
+                    'Optional': False,
+                    'Host': 'out/b1dir/b1.jar',
+                    'Device': '/product/b1.jar',
+                    'Subcontexts': [],
+                },
+                {  # Installed optional.
+                    'Name': 'b2',
+                    'Optional': True,
+                    'Host': 'out/b2dir/b2.jar',
+                    'Device': '/product/b2.jar',
+                    'Subcontexts': [],
+                },
+                {  # Not installed optional.
+                    'Name': 'b3',
+                    'Optional': True,
+                    'Host': 'out/b3dir/b3.jar',
+                    'Device': '/product/b3.jar',
+                    'Subcontexts': [],
+                },
+                {  # Installed optional with one more level of nested deps.
+                    'Name': 'b4',
+                    'Optional': True,
+                    'Host': 'out/b4dir/b4.jar',
+                    'Device': '/product/b4.jar',
+                    'Subcontexts': [
+                        {
+                            'Name': 'b41',
+                            'Optional': True,
+                            'Host': 'out/b41dir/b41.jar',
+                            'Device': '/product/b41.jar',
+                            'Subcontexts': [],
+                        },
+                        {
+                            'Name': 'b42',
+                            'Optional': True,
+                            'Host': 'out/b42dir/b42.jar',
+                            'Device': '/product/b42.jar',
+                            'Subcontexts': [],
+                        },
+                    ],
+                },
+            ],
+        },
+        {  # Not installed optional, at the top-level.
+            'Name': 'c',
+            'Optional': True,
+            'Host': 'out/cdir/c.jar',
+            'Device': '/product/c.jar',
+            'Subcontexts': [],
+        },
+    ],
+}
 
 
-contexts = [
-    '--host-context-for-sdk',
-    '28',
-    'PCL[out/zdir/z.jar]',
-    '--target-context-for-sdk',
-    '28',
-    'PCL[/system/z.jar]',
-    '--host-context-for-sdk',
-    '29',
-    'PCL[out/xdir/x.jar]#PCL[out/ydir/y.jar]',
-    '--target-context-for-sdk',
-    '29',
-    'PCL[/system/x.jar]#PCL[/product/y.jar]',
-    '--host-context-for-sdk',
-    'any',
-    'PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]',
-    '--target-context-for-sdk',
-    'any',
-    'PCL[/system/a.jar]#PCL[/product/b.jar]',
-]
+PRODUCT_PACKAGES = ['a', 'b', 'b2', 'b4', 'b41', 'b42', 'x', 'y', 'z']
 
-#pylint: disable=line-too-long
+
+def construct_context_args(target_sdk):
+    return cc.construct_context_args(target_sdk, CONTEXT_JSON, PRODUCT_PACKAGES)
+
+
 class ConstructContextTest(unittest.TestCase):
+    def test_construct_context_27(self):
+        actual = construct_context_args('27')
+        # The order matters.
+        expected = (
+            'class_loader_context_arg='
+            '--class-loader-context=PCL[]{'
+            'PCL[out/xdir/x.jar]#'
+            'PCL[out/ydir/y.jar]#'
+            'PCL[out/zdir/z.jar]#'
+            'PCL[out/adir/a.jar]#'
+            'PCL[out/bdir/b.jar]{'
+            'PCL[out/b1dir/b1.jar]#'
+            'PCL[out/b2dir/b2.jar]#'
+            'PCL[out/b4dir/b4.jar]{'
+            'PCL[out/b41dir/b41.jar]#'
+            'PCL[out/b42dir/b42.jar]'
+            '}'
+            '}'
+            '}'
+            ' ; '
+            'stored_class_loader_context_arg='
+            '--stored-class-loader-context=PCL[]{'
+            'PCL[/system/x.jar]#'
+            'PCL[/product/y.jar]#'
+            'PCL[/system/z.jar]#'
+            'PCL[/system/a.jar]#'
+            'PCL[/product/b.jar]{'
+            'PCL[/product/b1.jar]#'
+            'PCL[/product/b2.jar]#'
+            'PCL[/product/b4.jar]{'
+            'PCL[/product/b41.jar]#'
+            'PCL[/product/b42.jar]'
+            '}'
+            '}'
+            '}')
+        self.assertEqual(actual, expected)
 
     def test_construct_context_28(self):
-        args = ['--target-sdk-version', '28'] + contexts
-        result = construct_contexts(args)
-        expect = (
-            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/xdir/x.jar]#PCL[out/ydir/y.jar]#PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+        actual = construct_context_args('28')
+        expected = (
+            'class_loader_context_arg='
+            '--class-loader-context=PCL[]{'
+            'PCL[out/xdir/x.jar]#'
+            'PCL[out/ydir/y.jar]#'
+            'PCL[out/adir/a.jar]#'
+            'PCL[out/bdir/b.jar]{'
+            'PCL[out/b1dir/b1.jar]#'
+            'PCL[out/b2dir/b2.jar]#'
+            'PCL[out/b4dir/b4.jar]{'
+            'PCL[out/b41dir/b41.jar]#'
+            'PCL[out/b42dir/b42.jar]'
+            '}'
+            '}'
+            '}'
             ' ; '
-            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/x.jar]#PCL[/product/y.jar]#PCL[/system/a.jar]#PCL[/product/b.jar]}')
-        self.assertEqual(result, expect)
+            'stored_class_loader_context_arg='
+            '--stored-class-loader-context=PCL[]{'
+            'PCL[/system/x.jar]#'
+            'PCL[/product/y.jar]#'
+            'PCL[/system/a.jar]#'
+            'PCL[/product/b.jar]{'
+            'PCL[/product/b1.jar]#'
+            'PCL[/product/b2.jar]#'
+            'PCL[/product/b4.jar]{'
+            'PCL[/product/b41.jar]#'
+            'PCL[/product/b42.jar]'
+            '}'
+            '}'
+            '}')
+        self.assertEqual(actual, expected)
 
     def test_construct_context_29(self):
-        args = ['--target-sdk-version', '29'] + contexts
-        result = construct_contexts(args)
-        expect = (
-            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+        actual = construct_context_args('29')
+        expected = (
+            'class_loader_context_arg='
+            '--class-loader-context=PCL[]{'
+            'PCL[out/adir/a.jar]#'
+            'PCL[out/bdir/b.jar]{'
+            'PCL[out/b1dir/b1.jar]#'
+            'PCL[out/b2dir/b2.jar]#'
+            'PCL[out/b4dir/b4.jar]{'
+            'PCL[out/b41dir/b41.jar]#'
+            'PCL[out/b42dir/b42.jar]'
+            '}'
+            '}'
+            '}'
             ' ; '
-            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]#PCL[/product/b.jar]}')
-        self.assertEqual(result, expect)
+            'stored_class_loader_context_arg='
+            '--stored-class-loader-context=PCL[]{'
+            'PCL[/system/a.jar]#'
+            'PCL[/product/b.jar]{'
+            'PCL[/product/b1.jar]#'
+            'PCL[/product/b2.jar]#'
+            'PCL[/product/b4.jar]{'
+            'PCL[/product/b41.jar]#'
+            'PCL[/product/b42.jar]'
+            '}'
+            '}'
+            '}')
+        self.assertEqual(actual, expected)
 
     def test_construct_context_S(self):
-        args = ['--target-sdk-version', 'S'] + contexts
-        result = construct_contexts(args)
-        expect = (
-            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+        actual = construct_context_args('S')
+        expected = (
+            'class_loader_context_arg='
+            '--class-loader-context=PCL[]{'
+            'PCL[out/adir/a.jar]#'
+            'PCL[out/bdir/b.jar]{'
+            'PCL[out/b1dir/b1.jar]#'
+            'PCL[out/b2dir/b2.jar]#'
+            'PCL[out/b4dir/b4.jar]{'
+            'PCL[out/b41dir/b41.jar]#'
+            'PCL[out/b42dir/b42.jar]'
+            '}'
+            '}'
+            '}'
             ' ; '
-            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]#PCL[/product/b.jar]}')
-        self.assertEqual(result, expect)
-#pylint: enable=line-too-long
+            'stored_class_loader_context_arg='
+            '--stored-class-loader-context=PCL[]{'
+            'PCL[/system/a.jar]#'
+            'PCL[/product/b.jar]{'
+            'PCL[/product/b1.jar]#'
+            'PCL[/product/b2.jar]#'
+            'PCL[/product/b4.jar]{'
+            'PCL[/product/b41.jar]#'
+            'PCL[/product/b42.jar]'
+            '}'
+            '}'
+            '}')
+        self.assertEqual(actual, expected)
+
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
index b04657d..c4cfbb5 100644
--- a/snapshot/host_fake_snapshot.go
+++ b/snapshot/host_fake_snapshot.go
@@ -75,7 +75,7 @@
 }
 
 func registerHostSnapshotComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
+	ctx.RegisterParallelSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
 }
 
 type hostFakeSingleton struct {
diff --git a/snapshot/recovery_snapshot.go b/snapshot/recovery_snapshot.go
index ac002be..8ff59cb 100644
--- a/snapshot/recovery_snapshot.go
+++ b/snapshot/recovery_snapshot.go
@@ -68,7 +68,7 @@
 type RecoverySnapshotImage struct{}
 
 func (RecoverySnapshotImage) Init(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
+	ctx.RegisterParallelSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
 }
 
 func (RecoverySnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
diff --git a/snapshot/vendor_snapshot.go b/snapshot/vendor_snapshot.go
index 8f7b8c2..4484c85 100644
--- a/snapshot/vendor_snapshot.go
+++ b/snapshot/vendor_snapshot.go
@@ -78,8 +78,8 @@
 type VendorSnapshotImage struct{}
 
 func (VendorSnapshotImage) Init(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
-	ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+	ctx.RegisterParallelSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+	ctx.RegisterParallelSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
 }
 
 func (VendorSnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 5935247..5fc05f8 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -207,8 +207,8 @@
 function test_soong_build_rerun_iff_environment_changes() {
   setup
 
-  mkdir -p cherry
-  cat > cherry/Android.bp <<'EOF'
+  mkdir -p build/soong/cherry
+  cat > build/soong/cherry/Android.bp <<'EOF'
 bootstrap_go_package {
   name: "cherry",
   pkgPath: "android/soong/cherry",
@@ -224,7 +224,7 @@
 }
 EOF
 
-  cat > cherry/cherry.go <<'EOF'
+  cat > build/soong/cherry/cherry.go <<'EOF'
 package cherry
 
 import (
@@ -317,8 +317,8 @@
   run_soong
   local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  mkdir -p a
-  cat > a/Android.bp <<'EOF'
+  mkdir -p vendor/foo/picard
+  cat > vendor/foo/picard/Android.bp <<'EOF'
 bootstrap_go_package {
   name: "picard-soong-rules",
   pkgPath: "android/soong/picard",
@@ -334,7 +334,7 @@
 }
 EOF
 
-  cat > a/picard.go <<'EOF'
+  cat > vendor/foo/picard/picard.go <<'EOF'
 package picard
 
 import (
@@ -390,11 +390,11 @@
 function test_glob_during_bootstrapping() {
   setup
 
-  mkdir -p a
-  cat > a/Android.bp <<'EOF'
+  mkdir -p build/soong/picard
+  cat > build/soong/picard/Android.bp <<'EOF'
 build=["foo*.bp"]
 EOF
-  cat > a/fooa.bp <<'EOF'
+  cat > build/soong/picard/fooa.bp <<'EOF'
 bootstrap_go_package {
   name: "picard-soong-rules",
   pkgPath: "android/soong/picard",
@@ -410,7 +410,7 @@
 }
 EOF
 
-  cat > a/picard.go <<'EOF'
+  cat > build/soong/picard/picard.go <<'EOF'
 package picard
 
 import (
@@ -459,7 +459,7 @@
 
   grep -q "Make it so" out/soong/build.ninja || fail "Original action not present"
 
-  cat > a/foob.bp <<'EOF'
+  cat > build/soong/picard/foob.bp <<'EOF'
 bootstrap_go_package {
   name: "worf-soong-rules",
   pkgPath: "android/soong/worf",
@@ -476,7 +476,7 @@
 }
 EOF
 
-  cat > a/worf.go <<'EOF'
+  cat > build/soong/picard/worf.go <<'EOF'
 package worf
 
 import "android/soong/picard"
diff --git a/tests/dcla_apex_comparison_test.sh b/tests/dcla_apex_comparison_test.sh
index 97ae97e..667dde0 100755
--- a/tests/dcla_apex_comparison_test.sh
+++ b/tests/dcla_apex_comparison_test.sh
@@ -45,6 +45,11 @@
   com.android.tethering
 )
 
+BAZEL_TARGETS=(
+  //packages/modules/adb/apex:com.android.adbd
+  //frameworks/av/apex:com.android.media.swcodec
+)
+
 DCLA_LIBS=(
   libbase.so
   libc++.so
@@ -76,6 +81,10 @@
 ############
 OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
 
+function call_bazel() {
+  build/bazel/bin/bazel $@
+}
+
 function cleanup {
   rm -rf "${OUTPUT_DIR}"
 }
@@ -87,7 +96,9 @@
 
 function extract_dcla_libs() {
   local product=$1; shift
-  for module in "${MODULES[@]}"; do
+  local modules=("$@"); shift
+
+  for module in "${modules[@]}"; do
     local apex="${OUTPUT_DIR}/${product}/${module}.apex"
     local extract_dir="${OUTPUT_DIR}/${product}/${module}/extract"
 
@@ -97,11 +108,12 @@
 
 function compare_dcla_libs() {
   local product=$1; shift
+  local modules=("$@"); shift
 
   for lib in "${DCLA_LIBS[@]}"; do
     for arch in lib lib64; do
       local prev_sha=""
-      for module in "${MODULES[@]}"; do
+      for module in "${modules[@]}"; do
         local file="${OUTPUT_DIR}/${product}/${module}/extract/${arch}/${lib}"
         if [[ ! -f "${file}" ]]; then
           # not all libs are present in a module
@@ -112,7 +124,7 @@
         sha="${sha% *}"
         if [ "${prev_sha}" == "" ]; then
           prev_sha="${sha}"
-        elif [ "${sha}" != "${prev_sha}" ] && { [ "${lib}" != "libcrypto.so" ] || [ "${module}" != "com.android.tethering" ]; }; then
+        elif [ "${sha}" != "${prev_sha}" ] && { [ "${lib}" != "libcrypto.so" ] || [[ "${module}" != *"com.android.tethering" ]]; }; then
           echo "Test failed, ${lib} has different hash value"
           exit 1
         fi
@@ -131,8 +143,22 @@
     --product "${product}" \
     --dist_dir "${OUTPUT_DIR}/${product}"
 
-  extract_dcla_libs "${product}"
-  compare_dcla_libs "${product}"
+  bazel_apexes=()
+  if [[ -n ${TEST_BAZEL+x} ]] && [ "${TEST_BAZEL}" = true ]; then
+    export TARGET_PRODUCT="${product/module/aosp}"
+    call_bazel build --config=bp2build --config=ci --config=android "${BAZEL_TARGETS[@]}"
+    for target in "${BAZEL_TARGETS[@]}"; do
+      apex_path="$(realpath $(call_bazel cquery --config=bp2build --config=android --config=ci --output=files $target))"
+      mkdir -p ${OUTPUT_DIR}/${product}
+      bazel_apex="bazel_$(basename $apex_path)"
+      mv $apex_path ${OUTPUT_DIR}/${product}/${bazel_apex}
+      bazel_apexes+=(${bazel_apex%".apex"})
+    done
+  fi
+
+  all_modeuls=(${MODULES[@]} ${bazel_apexes[@]})
+  extract_dcla_libs "${product}" "${all_modeuls[@]}"
+  compare_dcla_libs "${product}" "${all_modeuls[@]}"
 done
 
 echo "Test passed"
diff --git a/tests/lib.sh b/tests/lib.sh
index 715eac1..7f3970e 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -99,6 +99,7 @@
   symlink_directory external/python
   symlink_directory external/sqlite
   symlink_directory external/spdx-tools
+  symlink_directory libcore
 
   touch "$MOCK_TOP/Android.bp"
 }
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index e1aa70c..db14bd4 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -15,8 +15,8 @@
 # mock client.
 "$TOP/build/soong/tests/apex_comparison_tests.sh"
 "$TOP/build/soong/tests/apex_comparison_tests.sh" "module_arm64only"
-extra_build_params=--bazel-mode-staging "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
-BUILD_BROKEN_DISABLE_BAZEL=true "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
+TEST_BAZEL=true extra_build_params=--bazel-mode-staging "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
+#BUILD_BROKEN_DISABLE_BAZEL=true "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_arm" "armv7-a"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_cf_arm64_phone" "armv8-a" "cortex-a53"
diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh
index 2f154cd..94fe51d 100755
--- a/tests/sbom_test.sh
+++ b/tests/sbom_test.sh
@@ -90,10 +90,12 @@
  -I /system/lib64/android.hardware.security.sharedsecret-V1-ndk.so \
  -I /system/lib64/android.security.compat-ndk.so \
  -I /system/lib64/libkeymaster4_1support.so \
+ -I /system/lib64/libkeymaster4support.so \
  -I /system/lib64/libkeymint.so \
  -I /system/lib64/libkeystore2_aaid.so \
  -I /system/lib64/libkeystore2_apc_compat.so \
  -I /system/lib64/libkeystore2_crypto.so \
+ -I /system/lib64/libkeystore-attestation-application-id.so \
  -I /system/lib64/libkm_compat_service.so \
  -I /system/lib64/libkm_compat.so \
  -I /system/lib64/vndk-29 \
diff --git a/tests/soong_test.sh b/tests/soong_test.sh
index f7bee40..6779d8a 100755
--- a/tests/soong_test.sh
+++ b/tests/soong_test.sh
@@ -9,12 +9,8 @@
 function test_m_clean_works {
   setup
 
-  # Create a directory with files that cannot be removed
-  mkdir -p out/bad_directory_permissions
-  touch out/bad_directory_permissions/unremovable_file
-  # File permissions are fine but directory permissions are bad
-  chmod a+rwx out/bad_directory_permissions/unremovable_file
-  chmod a-rwx out/bad_directory_permissions
+  mkdir -p out/some_directory
+  touch out/some_directory/some_file
 
   run_soong clean
 }
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index fd60177..ee53327 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -17,7 +17,6 @@
 import (
 	"bytes"
 	"fmt"
-	"io/fs"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -59,37 +58,9 @@
 	FILEMODE_USER_EXECUTE = FILEMODE_EXECUTE << FILEMODE_USER_SHIFT
 )
 
-// Ensures that files and directories in the out dir can be deleted.
-// For example, Bazen can generate output directories where the write bit isn't set, causing 'm' clean' to fail.
-func ensureOutDirRemovable(ctx Context, config Config) {
-	err := filepath.WalkDir(config.OutDir(), func(path string, d fs.DirEntry, err error) error {
-		if err != nil {
-			return err
-		}
-		if d.IsDir() {
-			info, err := d.Info()
-			if err != nil {
-				return err
-			}
-			// Equivalent to running chmod u+rwx on each directory
-			newMode := info.Mode() | FILEMODE_USER_READ | FILEMODE_USER_WRITE | FILEMODE_USER_EXECUTE
-			if err := os.Chmod(path, newMode); err != nil {
-				return err
-			}
-		}
-		// Continue walking the out dir...
-		return nil
-	})
-	if err != nil && !os.IsNotExist(err) {
-		// Display the error, but don't crash.
-		ctx.Println(err.Error())
-	}
-}
-
 // Remove everything under the out directory. Don't remove the out directory
 // itself in case it's a symlink.
 func clean(ctx Context, config Config) {
-	ensureOutDirRemovable(ctx, config)
 	removeGlobs(ctx, filepath.Join(config.OutDir(), "*"))
 	ctx.Println("Entire build directory removed.")
 }
diff --git a/ui/build/config.go b/ui/build/config.go
index 8ec9680..bfbf85d 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -15,7 +15,6 @@
 package build
 
 import (
-	"context"
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
@@ -39,9 +38,6 @@
 const (
 	envConfigDir = "vendor/google/tools/soong_config"
 	jsonSuffix   = "json"
-
-	configFetcher         = "vendor/google/tools/soong/expconfigfetcher"
-	envConfigFetchTimeout = 10 * time.Second
 )
 
 var (
@@ -170,87 +166,6 @@
 	}
 }
 
-// fetchEnvConfig optionally fetches a configuration file that can then subsequently be
-// loaded into Soong environment to control certain aspects of build behavior (e.g., enabling RBE).
-// If a configuration file already exists on disk, the fetch is run in the background
-// so as to NOT block the rest of the build execution.
-func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
-	configName := envConfigName + "." + jsonSuffix
-	expConfigFetcher := &smpb.ExpConfigFetcher{Filename: &configName}
-	defer func() {
-		ctx.Metrics.ExpConfigFetcher(expConfigFetcher)
-	}()
-	if !config.GoogleProdCredsExist() {
-		status := smpb.ExpConfigFetcher_MISSING_GCERT
-		expConfigFetcher.Status = &status
-		return nil
-	}
-
-	s, err := os.Stat(configFetcher)
-	if err != nil {
-		if os.IsNotExist(err) {
-			return nil
-		}
-		return err
-	}
-	if s.Mode()&0111 == 0 {
-		status := smpb.ExpConfigFetcher_ERROR
-		expConfigFetcher.Status = &status
-		return fmt.Errorf("configuration fetcher binary %v is not executable: %v", configFetcher, s.Mode())
-	}
-
-	configExists := false
-	outConfigFilePath := filepath.Join(config.OutDir(), configName)
-	if _, err := os.Stat(outConfigFilePath); err == nil {
-		configExists = true
-	}
-
-	tCtx, cancel := context.WithTimeout(ctx, envConfigFetchTimeout)
-	fetchStart := time.Now()
-	cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir(),
-		"-output_config_name", configName)
-	if err := cmd.Start(); err != nil {
-		status := smpb.ExpConfigFetcher_ERROR
-		expConfigFetcher.Status = &status
-		return err
-	}
-
-	fetchCfg := func() error {
-		if err := cmd.Wait(); err != nil {
-			status := smpb.ExpConfigFetcher_ERROR
-			expConfigFetcher.Status = &status
-			return err
-		}
-		fetchEnd := time.Now()
-		expConfigFetcher.Micros = proto.Uint64(uint64(fetchEnd.Sub(fetchStart).Microseconds()))
-		expConfigFetcher.Filename = proto.String(outConfigFilePath)
-
-		if _, err := os.Stat(outConfigFilePath); err != nil {
-			status := smpb.ExpConfigFetcher_NO_CONFIG
-			expConfigFetcher.Status = &status
-			return err
-		}
-		status := smpb.ExpConfigFetcher_CONFIG
-		expConfigFetcher.Status = &status
-		return nil
-	}
-
-	// If a config file does not exist, wait for the config file to be fetched. Otherwise
-	// fetch the config file in the background and return immediately.
-	if !configExists {
-		defer cancel()
-		return fetchCfg()
-	}
-
-	go func() {
-		defer cancel()
-		if err := fetchCfg(); err != nil {
-			ctx.Verbosef("Failed to fetch config file %v: %v\n", configName, err)
-		}
-	}()
-	return nil
-}
-
 func loadEnvConfig(ctx Context, config *configImpl, bc string) error {
 	if bc == "" {
 		return nil
@@ -350,9 +265,6 @@
 	bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
 
 	if bc != "" {
-		if err := fetchEnvConfig(ctx, ret, bc); err != nil {
-			ctx.Verbosef("Failed to fetch config file: %v\n", err)
-		}
 		if err := loadEnvConfig(ctx, ret, bc); err != nil {
 			ctx.Fatalln("Failed to parse env config files: %v", err)
 		}
@@ -1229,6 +1141,13 @@
 	panic("TARGET_PRODUCT is not defined")
 }
 
+func (c *configImpl) TargetProductOrErr() (string, error) {
+	if v, ok := c.environ.Get("TARGET_PRODUCT"); ok {
+		return v, nil
+	}
+	return "", fmt.Errorf("TARGET_PRODUCT is not defined")
+}
+
 func (c *configImpl) TargetDevice() string {
 	return c.targetDevice
 }
@@ -1486,7 +1405,7 @@
 	if googleProdCredsExistCache {
 		return googleProdCredsExistCache
 	}
-	if _, err := exec.Command("/usr/bin/prodcertstatus", "--simple_output", "--nocheck_loas").Output(); err != nil {
+	if _, err := exec.Command("/usr/bin/gcertstatus").Output(); err != nil {
 		return false
 	}
 	googleProdCredsExistCache = true
@@ -1554,11 +1473,21 @@
 }
 
 func (c *configImpl) SoongVarsFile() string {
-	return filepath.Join(c.SoongOutDir(), "soong.variables")
+	targetProduct, err := c.TargetProductOrErr()
+	if err != nil {
+		return filepath.Join(c.SoongOutDir(), "soong.variables")
+	} else {
+		return filepath.Join(c.SoongOutDir(), "soong."+targetProduct+".variables")
+	}
 }
 
 func (c *configImpl) SoongNinjaFile() string {
-	return filepath.Join(c.SoongOutDir(), "build.ninja")
+	targetProduct, err := c.TargetProductOrErr()
+	if err != nil {
+		return filepath.Join(c.SoongOutDir(), "build.ninja")
+	} else {
+		return filepath.Join(c.SoongOutDir(), "build."+targetProduct+".ninja")
+	}
 }
 
 func (c *configImpl) CombinedNinjaFile() string {
diff --git a/ui/build/kati.go b/ui/build/kati.go
index dad68fa..aea56d3 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -22,6 +22,7 @@
 	"os/user"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"android/soong/ui/metrics"
 	"android/soong/ui/status"
@@ -66,6 +67,21 @@
 	}
 }
 
+func writeValueIfChanged(ctx Context, config Config, dir string, filename string, value string) {
+	filePath := filepath.Join(dir, filename)
+	previousValue := ""
+	rawPreviousValue, err := ioutil.ReadFile(filePath)
+	if err == nil {
+		previousValue = string(rawPreviousValue)
+	}
+
+	if previousValue != value {
+		if err = ioutil.WriteFile(filePath, []byte(value), 0666); err != nil {
+			ctx.Fatalf("Failed to write: %v", err)
+		}
+	}
+}
+
 // Base function to construct and run the Kati command line with additional
 // arguments, and a custom function closure to mutate the environment Kati runs
 // in.
@@ -157,28 +173,60 @@
 	}
 	cmd.Stderr = cmd.Stdout
 
-	// Apply the caller's function closure to mutate the environment variables.
-	envFunc(cmd.Environment)
-
+	var username string
 	// Pass on various build environment metadata to Kati.
-	if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
-		username := "unknown"
+	if usernameFromEnv, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
+		username = "unknown"
 		if u, err := user.Current(); err == nil {
 			username = u.Username
 		} else {
 			ctx.Println("Failed to get current user:", err)
 		}
 		cmd.Environment.Set("BUILD_USERNAME", username)
+	} else {
+		username = usernameFromEnv
 	}
 
-	if _, ok := cmd.Environment.Get("BUILD_HOSTNAME"); !ok {
-		hostname, err := os.Hostname()
+	hostname, ok := cmd.Environment.Get("BUILD_HOSTNAME")
+	// Unset BUILD_HOSTNAME during kati run to avoid kati rerun, kati will use BUILD_HOSTNAME from a file.
+	cmd.Environment.Unset("BUILD_HOSTNAME")
+	if !ok {
+		hostname, err = os.Hostname()
 		if err != nil {
 			ctx.Println("Failed to read hostname:", err)
 			hostname = "unknown"
 		}
-		cmd.Environment.Set("BUILD_HOSTNAME", hostname)
 	}
+	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_hostname.txt", hostname)
+
+	// BUILD_NUMBER should be set to the source control value that
+	// represents the current state of the source code.  E.g., a
+	// perforce changelist number or a git hash.  Can be an arbitrary string
+	// (to allow for source control that uses something other than numbers),
+	// but must be a single word and a valid file name.
+	//
+	// If no BUILD_NUMBER is set, create a useful "I am an engineering build
+	// from this date/time" value.  Make it start with a non-digit so that
+	// anyone trying to parse it as an integer will probably get "0".
+	cmd.Environment.Unset("HAS_BUILD_NUMBER")
+	buildNumber, ok := cmd.Environment.Get("BUILD_NUMBER")
+	// Unset BUILD_NUMBER during kati run to avoid kati rerun, kati will use BUILD_NUMBER from a file.
+	cmd.Environment.Unset("BUILD_NUMBER")
+	if ok {
+		cmd.Environment.Set("HAS_BUILD_NUMBER", "true")
+		writeValueIfChanged(ctx, config, config.OutDir(), "file_name_tag.txt", buildNumber)
+	} else {
+		buildNumber = fmt.Sprintf("eng.%.6s.%s", username, time.Now().Format("20060102.150405" /* YYYYMMDD.HHMMSS */))
+		cmd.Environment.Set("HAS_BUILD_NUMBER", "false")
+		writeValueIfChanged(ctx, config, config.OutDir(), "file_name_tag.txt", username)
+	}
+	// Write the build number to a file so it can be read back in
+	// without changing the command line every time.  Avoids rebuilds
+	// when using ninja.
+	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_number.txt", buildNumber)
+
+	// Apply the caller's function closure to mutate the environment variables.
+	envFunc(cmd.Environment)
 
 	cmd.StartOrFatal()
 	// Set up the ToolStatus command line reader for Kati for a consistent UI
@@ -336,6 +384,7 @@
 			"ANDROID_BUILD_SHELL",
 			"DIST_DIR",
 			"OUT_DIR",
+			"FILE_NAME_TAG",
 		}...)
 
 		if config.Dist() {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 18bf3b9..6de8d6f 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -235,7 +235,7 @@
 	} else if !exists {
 		// The tree is out of date for the current epoch, delete files used by bootstrap
 		// and force the primary builder to rerun.
-		os.Remove(filepath.Join(config.SoongOutDir(), "build.ninja"))
+		os.Remove(config.SoongNinjaFile())
 		for _, globFile := range bootstrapGlobFileList(config) {
 			os.Remove(globFile)
 		}
@@ -263,7 +263,9 @@
 	// Clean up some files for incremental builds across incompatible changes.
 	bootstrapEpochCleanup(ctx, config)
 
-	mainSoongBuildExtraArgs := []string{"-o", config.SoongNinjaFile()}
+	baseArgs := []string{"--soong_variables", config.SoongVarsFile()}
+
+	mainSoongBuildExtraArgs := append(baseArgs, "-o", config.SoongNinjaFile())
 	if config.EmptyNinjaFile() {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--empty-ninja-file")
 	}
@@ -306,49 +308,59 @@
 			specificArgs: mainSoongBuildExtraArgs,
 		},
 		{
-			name:         bp2buildFilesTag,
-			description:  fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
-			config:       config,
-			output:       config.Bp2BuildFilesMarkerFile(),
-			specificArgs: []string{"--bp2build_marker", config.Bp2BuildFilesMarkerFile()},
+			name:        bp2buildFilesTag,
+			description: fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
+			config:      config,
+			output:      config.Bp2BuildFilesMarkerFile(),
+			specificArgs: append(baseArgs,
+				"--bp2build_marker", config.Bp2BuildFilesMarkerFile(),
+			),
 		},
 		{
-			name:         bp2buildWorkspaceTag,
-			description:  "Creating Bazel symlink forest",
-			config:       config,
-			output:       config.Bp2BuildWorkspaceMarkerFile(),
-			specificArgs: []string{"--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile()},
+			name:        bp2buildWorkspaceTag,
+			description: "Creating Bazel symlink forest",
+			config:      config,
+			output:      config.Bp2BuildWorkspaceMarkerFile(),
+			specificArgs: append(baseArgs,
+				"--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile(),
+			),
 		},
 		{
 			name:        jsonModuleGraphTag,
 			description: fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()),
 			config:      config,
 			output:      config.ModuleGraphFile(),
-			specificArgs: []string{
+			specificArgs: append(baseArgs,
 				"--module_graph_file", config.ModuleGraphFile(),
 				"--module_actions_file", config.ModuleActionsFile(),
-			},
+			),
 		},
 		{
-			name:         queryviewTag,
-			description:  fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
-			config:       config,
-			output:       config.QueryviewMarkerFile(),
-			specificArgs: []string{"--bazel_queryview_dir", queryviewDir},
+			name:        queryviewTag,
+			description: fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
+			config:      config,
+			output:      config.QueryviewMarkerFile(),
+			specificArgs: append(baseArgs,
+				"--bazel_queryview_dir", queryviewDir,
+			),
 		},
 		{
-			name:         apiBp2buildTag,
-			description:  fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
-			config:       config,
-			output:       config.ApiBp2buildMarkerFile(),
-			specificArgs: []string{"--bazel_api_bp2build_dir", apiBp2buildDir},
+			name:        apiBp2buildTag,
+			description: fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
+			config:      config,
+			output:      config.ApiBp2buildMarkerFile(),
+			specificArgs: append(baseArgs,
+				"--bazel_api_bp2build_dir", apiBp2buildDir,
+			),
 		},
 		{
-			name:         soongDocsTag,
-			description:  fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
-			config:       config,
-			output:       config.SoongDocsHtml(),
-			specificArgs: []string{"--soong_docs", config.SoongDocsHtml()},
+			name:        soongDocsTag,
+			description: fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
+			config:      config,
+			output:      config.SoongDocsHtml(),
+			specificArgs: append(baseArgs,
+				"--soong_docs", config.SoongDocsHtml(),
+			),
 		},
 	}
 
diff --git a/ui/metrics/BUILD.bazel b/ui/metrics/BUILD.bazel
index 15ebb88..2dc1ab6 100644
--- a/ui/metrics/BUILD.bazel
+++ b/ui/metrics/BUILD.bazel
@@ -16,7 +16,7 @@
 
 py_proto_library(
     name = "metrics-py-proto",
-    visibility = ["//build/bazel/scripts/incremental_build:__pkg__"],
+    visibility = ["//build/bazel/scripts:__subpackages__"],
     deps = [":metrics-proto"],
 )