Merge "Ensure opt-in platform ABI dumps are configured with ref_dump_dirs" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 9f386ca..6eabd7c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -3,5 +3,14 @@
     {
       "path": "packages/modules/SdkExtensions"
     }
+  ],
+
+  "postsubmit": [
+    {
+      "name": "MicrodroidHostTestCases"
+    },
+    {
+      "name": "MicrodroidTestApp"
+    }
   ]
 }
diff --git a/android/Android.bp b/android/Android.bp
index f0be44b..9ce8cdc 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -41,6 +41,7 @@
         "buildinfo_prop.go",
         "config.go",
         "test_config.go",
+        "configurable_properties.go",
         "configured_jars.go",
         "csuite_config.go",
         "deapexer.go",
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index 4c1782b..7317587 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -43,14 +43,6 @@
 
 var AconfigDeclarationsProviderKey = blueprint.NewProvider[AconfigDeclarationsProviderData]()
 
-// This is used to collect the aconfig declarations info on the transitive closure,
-// the data is keyed on the container.
-type AconfigTransitiveDeclarationsInfo struct {
-	AconfigFiles map[string]Paths
-}
-
-var AconfigTransitiveDeclarationsInfoProvider = blueprint.NewProvider[AconfigTransitiveDeclarationsInfo]()
-
 type ModeInfo struct {
 	Container string
 	Mode      string
@@ -80,54 +72,15 @@
 	}
 }
 
-// CollectDependencyAconfigFiles is used by some module types to provide finer dependency graphing than
-// we can do in ModuleBase.
-func CollectDependencyAconfigFiles(ctx ModuleContext, mergedAconfigFiles *map[string]Paths) {
-	if *mergedAconfigFiles == nil {
-		*mergedAconfigFiles = make(map[string]Paths)
-	}
-	ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) {
-		if dep, _ := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); dep.IntermediateCacheOutputPath != nil {
-			(*mergedAconfigFiles)[dep.Container] = append((*mergedAconfigFiles)[dep.Container], dep.IntermediateCacheOutputPath)
-			return
-		}
-		if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok {
-			for container, v := range dep.AconfigFiles {
-				(*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...)
-			}
-		}
-		// We process these last, so that they determine the final value, eliminating any duplicates that we picked up
-		// from UpdateAndroidBuildActions.
-		if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok {
-			for container, v := range dep.AconfigFiles {
-				(*mergedAconfigFiles)[container] = append((*mergedAconfigFiles)[container], v...)
-			}
-		}
-	})
-
-	for _, container := range SortedKeys(*mergedAconfigFiles) {
-		aconfigFiles := (*mergedAconfigFiles)[container]
-		(*mergedAconfigFiles)[container] = mergeAconfigFiles(ctx, container, aconfigFiles, false)
-	}
-
-	SetProvider(ctx, AconfigTransitiveDeclarationsInfoProvider, AconfigTransitiveDeclarationsInfo{
-		AconfigFiles: *mergedAconfigFiles,
-	})
-}
-
-func SetAconfigFileMkEntries(m *ModuleBase, entries *AndroidMkEntries, aconfigFiles map[string]Paths) {
-	setAconfigFileMkEntries(m, entries, aconfigFiles)
-}
-
 type aconfigPropagatingDeclarationsInfo struct {
 	AconfigFiles map[string]Paths
 	ModeInfos    map[string]ModeInfo
 }
 
-var aconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]()
+var AconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]()
 
 func VerifyAconfigBuildMode(ctx ModuleContext, container string, module blueprint.Module, asError bool) {
-	if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok {
+	if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok {
 		for k, v := range dep.ModeInfos {
 			msg := fmt.Sprintf("%s/%s depends on %s/%s/%s across containers\n",
 				module.Name(), container, k, v.Container, v.Mode)
@@ -159,17 +112,12 @@
 		if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok {
 			mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath)
 		}
-		if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok {
+		if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok {
 			for container, v := range dep.AconfigFiles {
 				mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...)
 			}
 			propagateModeInfos(ctx, module, mergedModeInfos, dep.ModeInfos)
 		}
-		if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok {
-			for container, v := range dep.AconfigFiles {
-				mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...)
-			}
-		}
 	})
 	// We only need to set the provider if we have aconfig files.
 	if len(mergedAconfigFiles) > 0 {
@@ -178,7 +126,7 @@
 			mergedAconfigFiles[container] = mergeAconfigFiles(ctx, container, aconfigFiles, true)
 		}
 
-		SetProvider(ctx, aconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{
+		SetProvider(ctx, AconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{
 			AconfigFiles: mergedAconfigFiles,
 			ModeInfos:    mergedModeInfos,
 		})
@@ -186,7 +134,7 @@
 }
 
 func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) {
-	info, ok := SingletonModuleProvider(ctx, mod, aconfigPropagatingProviderKey)
+	info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
 	// If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done.
 	if !ok || len(info.AconfigFiles) == 0 {
 		return
@@ -217,7 +165,7 @@
 	if len(*entries) == 0 {
 		return
 	}
-	info, ok := SingletonModuleProvider(ctx, mod, aconfigPropagatingProviderKey)
+	info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
 	if !ok || len(info.AconfigFiles) == 0 {
 		return
 	}
@@ -225,7 +173,7 @@
 	for idx, _ := range *entries {
 		(*entries)[idx].ExtraEntries = append((*entries)[idx].ExtraEntries,
 			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
-				setAconfigFileMkEntries(mod.base(), entries, info.AconfigFiles)
+				entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
 			},
 		)
 
@@ -255,10 +203,6 @@
 	return Paths{output}
 }
 
-func setAconfigFileMkEntries(m *ModuleBase, entries *AndroidMkEntries, aconfigFiles map[string]Paths) {
-	entries.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(m, aconfigFiles))
-}
-
 func getAconfigFilePaths(m *ModuleBase, aconfigFiles map[string]Paths) (paths Paths) {
 	// TODO(b/311155208): The default container here should be system.
 	container := "system"
diff --git a/android/androidmk.go b/android/androidmk.go
index 53f0609..66f42f9 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -855,7 +855,7 @@
 	mod blueprint.Module, provider AndroidMkDataProvider) error {
 
 	amod := mod.(Module).base()
-	if shouldSkipAndroidMkProcessing(amod) {
+	if shouldSkipAndroidMkProcessing(ctx, amod) {
 		return nil
 	}
 
@@ -945,7 +945,7 @@
 
 func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
 	mod blueprint.Module, provider AndroidMkEntriesProvider) error {
-	if shouldSkipAndroidMkProcessing(mod.(Module).base()) {
+	if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) {
 		return nil
 	}
 
@@ -967,11 +967,11 @@
 	return nil
 }
 
-func ShouldSkipAndroidMkProcessing(module Module) bool {
-	return shouldSkipAndroidMkProcessing(module.base())
+func ShouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module Module) bool {
+	return shouldSkipAndroidMkProcessing(ctx, module.base())
 }
 
-func shouldSkipAndroidMkProcessing(module *ModuleBase) bool {
+func shouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module *ModuleBase) bool {
 	if !module.commonProperties.NamespaceExportedToMake {
 		// TODO(jeffrygaston) do we want to validate that there are no modules being
 		// exported to Kati that depend on this module?
@@ -990,7 +990,7 @@
 		return true
 	}
 
-	return !module.Enabled() ||
+	return !module.Enabled(ctx) ||
 		module.commonProperties.HideFromMake ||
 		// Make does not understand LinuxBionic
 		module.Os() == LinuxBionic ||
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index f5c50d3..91549e5 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -101,12 +101,12 @@
 }
 
 var (
-	acDepTag = apexContributionsDepTag{}
+	AcDepTag = apexContributionsDepTag{}
 )
 
 // Creates a dep to each selected apex_contributions
 func (a *allApexContributions) DepsMutator(ctx BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), acDepTag, ctx.Config().AllApexContributions()...)
+	ctx.AddDependency(ctx.Module(), AcDepTag, ctx.Config().AllApexContributions()...)
 }
 
 // Set PrebuiltSelectionInfoProvider in post deps phase
@@ -131,7 +131,7 @@
 	// (e.g. shiba and shiba_fullmte)
 	// Eventually these product variants will have their own release config maps.
 	if !proptools.Bool(ctx.Config().BuildIgnoreApexContributionContents()) {
-		ctx.VisitDirectDepsWithTag(acDepTag, func(child Module) {
+		ctx.VisitDirectDepsWithTag(AcDepTag, func(child Module) {
 			if m, ok := child.(*apexContributions); ok {
 				addContentsToProvider(&p, m)
 			} else {
diff --git a/android/arch.go b/android/arch.go
index cd8882b..e0c6908 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -486,7 +486,7 @@
 			// dependencies on OsType variants that are explicitly disabled in their
 			// properties. The CommonOS variant will still depend on disabled variants
 			// if they are disabled afterwards, e.g. in archMutator if
-			if module.Enabled() {
+			if module.Enabled(mctx) {
 				mctx.AddInterVariantDependency(commonOsToOsSpecificVariantTag, commonOSVariant, module)
 			}
 		}
@@ -511,7 +511,7 @@
 	var variants []Module
 	mctx.VisitDirectDeps(func(m Module) {
 		if mctx.OtherModuleDependencyTag(m) == commonOsToOsSpecificVariantTag {
-			if m.Enabled() {
+			if m.Enabled(mctx) {
 				variants = append(variants, m)
 			}
 		}
diff --git a/android/arch_test.go b/android/arch_test.go
index 5021a67..f0a58a9 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -423,7 +423,7 @@
 		variants := ctx.ModuleVariantsForTests(name)
 		for _, variant := range variants {
 			m := ctx.ModuleForTests(name, variant)
-			if m.Module().Enabled() {
+			if m.Module().Enabled(PanickingConfigAndErrorContext(ctx)) {
 				ret = append(ret, variant)
 			}
 		}
@@ -533,7 +533,7 @@
 		variants := ctx.ModuleVariantsForTests(name)
 		for _, variant := range variants {
 			m := ctx.ModuleForTests(name, variant)
-			if m.Module().Enabled() {
+			if m.Module().Enabled(PanickingConfigAndErrorContext(ctx)) {
 				ret = append(ret, variant)
 			}
 		}
diff --git a/android/base_module_context.go b/android/base_module_context.go
index c5fe585..5506000 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -325,7 +325,7 @@
 		return nil
 	}
 
-	if !aModule.Enabled() {
+	if !aModule.Enabled(b) {
 		if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) {
 			if b.Config().AllowMissingDependencies() {
 				b.AddMissingDependencies([]string{b.OtherModuleName(aModule)})
@@ -536,7 +536,7 @@
 		return true
 	} else if tag == licensesTag {
 		return true
-	} else if tag == acDepTag {
+	} else if tag == AcDepTag {
 		return true
 	}
 	return false
diff --git a/android/config.go b/android/config.go
index f6711e6..76c590a 100644
--- a/android/config.go
+++ b/android/config.go
@@ -2109,6 +2109,7 @@
 		"RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS",
 		"RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION",
 		"RELEASE_APEX_CONTRIBUTIONS_PERMISSION",
+		"RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS",
 		"RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING",
 		"RELEASE_APEX_CONTRIBUTIONS_RESOLV",
 		"RELEASE_APEX_CONTRIBUTIONS_SCHEDULING",
diff --git a/android/configurable_properties.go b/android/configurable_properties.go
new file mode 100644
index 0000000..dad42fa
--- /dev/null
+++ b/android/configurable_properties.go
@@ -0,0 +1,28 @@
+package android
+
+import "github.com/google/blueprint/proptools"
+
+// CreateSelectOsToBool is a utility function that makes it easy to create a
+// Configurable property value that maps from os to a bool. Use an empty string
+// to indicate a "default" case.
+func CreateSelectOsToBool(cases map[string]*bool) proptools.Configurable[bool] {
+	var resultCases []proptools.ConfigurableCase[bool]
+	for pattern, value := range cases {
+		if pattern == "" {
+			resultCases = append(resultCases, proptools.NewConfigurableCase(
+				[]proptools.ConfigurablePattern{proptools.NewDefaultConfigurablePattern()},
+				value,
+			))
+		} else {
+			resultCases = append(resultCases, proptools.NewConfigurableCase(
+				[]proptools.ConfigurablePattern{proptools.NewStringConfigurablePattern(pattern)},
+				value,
+			))
+		}
+	}
+
+	return proptools.NewConfigurable(
+		[]proptools.ConfigurableCondition{proptools.NewConfigurableCondition("os", nil)},
+		resultCases,
+	)
+}
diff --git a/android/deptag.go b/android/deptag.go
index c7ba4d3..77b9d61 100644
--- a/android/deptag.go
+++ b/android/deptag.go
@@ -44,6 +44,21 @@
 	return false
 }
 
+// Dependency tags can implement this interface and return true from SkipToTransitiveDeps to
+// annotate that this dependency isn't installed, but its transitive dependencies are. This is
+// useful when a module is built into another module (ex: static linking) but the module still has
+// runtime dependencies.
+type SkipToTransitiveDepsTag interface {
+	SkipToTransitiveDeps() bool
+}
+
+func IsSkipToTransitiveDepsTag(tag blueprint.DependencyTag) bool {
+	if i, ok := tag.(SkipToTransitiveDepsTag); ok {
+		return i.SkipToTransitiveDeps()
+	}
+	return false
+}
+
 type PropagateAconfigValidationDependencyTag interface {
 	PropagateAconfigValidation() bool
 }
diff --git a/android/early_module_context.go b/android/early_module_context.go
index cf1b5fc..23f4c90 100644
--- a/android/early_module_context.go
+++ b/android/early_module_context.go
@@ -173,5 +173,5 @@
 }
 
 func (e *earlyModuleContext) OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{}) {
-	e.EarlyModuleContext.OtherModulePropertyErrorf(module, property, fmt, args)
+	e.EarlyModuleContext.OtherModulePropertyErrorf(module, property, fmt, args...)
 }
diff --git a/android/filegroup.go b/android/filegroup.go
index 86d7b4b..a8326d4 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -56,9 +56,6 @@
 	DefaultableModuleBase
 	properties fileGroupProperties
 	srcs       Paths
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]Paths
 }
 
 var _ SourceFileProducer = (*fileGroup)(nil)
@@ -97,7 +94,6 @@
 		fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
 	}
 	SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: fg.srcs.Strings()})
-	CollectDependencyAconfigFiles(ctx, &fg.mergedAconfigFiles)
 
 	var aconfigDeclarations []string
 	var intermediateCacheOutputPaths Paths
diff --git a/android/gen_notice.go b/android/gen_notice.go
index 1acc638..6815f64 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -62,7 +62,7 @@
 				if mod == nil {
 					continue
 				}
-				if !mod.Enabled() { // don't depend on variants without build rules
+				if !mod.Enabled(ctx) { // don't depend on variants without build rules
 					continue
 				}
 				modules = append(modules, mod)
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 0cb6c96..3fea029 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -36,7 +36,7 @@
 func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
 	base := ctx.Module().base()
 
-	if !base.Enabled() {
+	if !base.Enabled(ctx) {
 		return
 	}
 
@@ -69,7 +69,7 @@
 		if dep == nil {
 			return
 		}
-		if !dep.Enabled() {
+		if !dep.Enabled(ctx) {
 			return
 		}
 
diff --git a/android/makevars.go b/android/makevars.go
index 4039e7e..b6bc14e 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -98,6 +98,7 @@
 	BlueprintFile(module blueprint.Module) string
 
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
+	OtherModulePropertyErrorf(module Module, property, format string, args ...interface{})
 	Errorf(format string, args ...interface{})
 
 	VisitAllModules(visit func(Module))
@@ -265,7 +266,7 @@
 	}
 
 	ctx.VisitAllModules(func(m Module) {
-		if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled() {
+		if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled(ctx) {
 			mctx := &makeVarsContext{
 				SingletonContext: ctx,
 			}
@@ -472,7 +473,7 @@
 
 # Values written by Soong to generate install rules that can be amended by Kati.
 
-
+EXTRA_INSTALL_ZIPS :=
 `)
 
 	preserveSymlinksFlag := "-d"
@@ -506,9 +507,12 @@
 		if extraFiles := install.extraFiles; extraFiles != nil {
 			fmt.Fprintf(buf, "\t( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} ) || \\\n", extraFiles.dir.String(), extraFiles.zip.String())
 			fmt.Fprintf(buf, "\t  ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )\n")
+			fmt.Fprintf(buf, "EXTRA_INSTALL_ZIPS += %s:%s\n", extraFiles.dir.String(), extraFiles.zip.String())
 		}
+
 		fmt.Fprintln(buf)
 	}
+	fmt.Fprintf(buf, ".KATI_READONLY := EXTRA_INSTALL_ZIPS\n")
 
 	for _, symlink := range symlinks {
 		fmt.Fprintf(buf, "%s:", symlink.to.String())
diff --git a/android/module.go b/android/module.go
index f8fec3a..d7f0537 100644
--- a/android/module.go
+++ b/android/module.go
@@ -60,7 +60,7 @@
 
 	base() *ModuleBase
 	Disable()
-	Enabled() bool
+	Enabled(ctx ConfigAndErrorContext) bool
 	Target() Target
 	MultiTargets() []Target
 
@@ -287,7 +287,7 @@
 	// but are not usually required (e.g. superceded by a prebuilt) should not be
 	// disabled as that will prevent them from being built by the checkbuild target
 	// and so prevent early detection of changes that have broken those modules.
-	Enabled *bool `android:"arch_variant"`
+	Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 
 	// Controls the visibility of this module to other modules. Allowable values are one or more of
 	// these formats:
@@ -1397,14 +1397,11 @@
 	return partition
 }
 
-func (m *ModuleBase) Enabled() bool {
+func (m *ModuleBase) Enabled(ctx ConfigAndErrorContext) bool {
 	if m.commonProperties.ForcedDisabled {
 		return false
 	}
-	if m.commonProperties.Enabled == nil {
-		return !m.Os().DefaultDisabled
-	}
-	return *m.commonProperties.Enabled
+	return m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled)
 }
 
 func (m *ModuleBase) Disable() {
@@ -1473,15 +1470,27 @@
 	var installDeps []*DepSet[InstallPath]
 	var packagingSpecs []*DepSet[PackagingSpec]
 	ctx.VisitDirectDeps(func(dep Module) {
-		if isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
+		depTag := ctx.OtherModuleDependencyTag(dep)
+		// If this is true, the direct outputs from the module is not gathered, but its
+		// transitive deps are still gathered.
+		skipToTransitive := IsSkipToTransitiveDepsTag(depTag)
+		if isInstallDepNeeded(dep, depTag) || skipToTransitive {
 			// Installation is still handled by Make, so anything hidden from Make is not
 			// installable.
 			if !dep.IsHideFromMake() && !dep.IsSkipInstall() {
-				installDeps = append(installDeps, dep.base().installFilesDepSet)
+				if skipToTransitive {
+					installDeps = append(installDeps, dep.base().installFilesDepSet.transitive...)
+				} else {
+					installDeps = append(installDeps, dep.base().installFilesDepSet)
+				}
 			}
 			// Add packaging deps even when the dependency is not installed so that uninstallable
 			// modules can still be packaged.  Often the package will be installed instead.
-			packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet)
+			if skipToTransitive {
+				packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet.transitive...)
+			} else {
+				packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet)
+			}
 		}
 	})
 
@@ -1648,7 +1657,7 @@
 		// not be created if the module is not exported to make.
 		// Those could depend on the build target and fail to compile
 		// for the current build target.
-		if !ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(a) {
+		if !ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(ctx, a) {
 			allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
 		}
 	})
@@ -1840,7 +1849,7 @@
 		checkDistProperties(ctx, fmt.Sprintf("dists[%d]", i), &m.distProperties.Dists[i])
 	}
 
-	if m.Enabled() {
+	if m.Enabled(ctx) {
 		// ensure all direct android.Module deps are enabled
 		ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) {
 			if m, ok := bm.(Module); ok {
@@ -1895,12 +1904,14 @@
 			}
 		}
 
-		m.module.GenerateAndroidBuildActions(ctx)
+		// Call aconfigUpdateAndroidBuildActions to collect merged aconfig files before being used
+		// in m.module.GenerateAndroidBuildActions
+		aconfigUpdateAndroidBuildActions(ctx)
 		if ctx.Failed() {
 			return
 		}
 
-		aconfigUpdateAndroidBuildActions(ctx)
+		m.module.GenerateAndroidBuildActions(ctx)
 		if ctx.Failed() {
 			return
 		}
@@ -2141,16 +2152,16 @@
 }
 
 func (e configurationEvalutor) PropertyErrorf(property string, fmt string, args ...interface{}) {
-	e.ctx.OtherModulePropertyErrorf(e.m, property, fmt, args)
+	e.ctx.OtherModulePropertyErrorf(e.m, property, fmt, args...)
 }
 
 func (e configurationEvalutor) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue {
 	ctx := e.ctx
 	m := e.m
 	switch condition.FunctionName() {
-	case "release_variable":
+	case "release_flag":
 		if condition.NumArgs() != 1 {
-			ctx.OtherModulePropertyErrorf(m, property, "release_variable requires 1 argument, found %d", condition.NumArgs())
+			ctx.OtherModulePropertyErrorf(m, property, "release_flag requires 1 argument, found %d", condition.NumArgs())
 			return proptools.ConfigurableValueUndefined()
 		}
 		if v, ok := ctx.Config().productVariables.BuildFlags[condition.Arg(0)]; ok {
@@ -2540,7 +2551,7 @@
 	}
 	osDeps := map[osAndCross]Paths{}
 	ctx.VisitAllModules(func(module Module) {
-		if module.Enabled() {
+		if module.Enabled(ctx) {
 			key := osAndCross{os: module.Target().Os, hostCross: module.Target().HostCross}
 			osDeps[key] = append(osDeps[key], module.base().checkbuildFiles...)
 		}
diff --git a/android/mutator.go b/android/mutator.go
index 9cfdb60..440b906 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -678,13 +678,11 @@
 // on component modules to be added so that they can depend directly on a prebuilt
 // module.
 func componentDepsMutator(ctx BottomUpMutatorContext) {
-	if m := ctx.Module(); m.Enabled() {
-		m.ComponentDepsMutator(ctx)
-	}
+	ctx.Module().ComponentDepsMutator(ctx)
 }
 
 func depsMutator(ctx BottomUpMutatorContext) {
-	if m := ctx.Module(); m.Enabled() {
+	if m := ctx.Module(); m.Enabled(ctx) {
 		m.base().baseDepsMutator(ctx)
 		m.DepsMutator(ctx)
 	}
diff --git a/android/override_module.go b/android/override_module.go
index 163f7b7..21cf381 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -28,7 +28,6 @@
 // module based on it.
 
 import (
-	"fmt"
 	"sort"
 	"sync"
 
@@ -121,7 +120,7 @@
 	addOverride(o OverrideModule)
 	getOverrides() []OverrideModule
 
-	override(ctx BaseModuleContext, bm OverridableModule, o OverrideModule)
+	override(ctx BaseModuleContext, m Module, o OverrideModule)
 	GetOverriddenBy() string
 	GetOverriddenByModuleDir() string
 
@@ -192,14 +191,15 @@
 }
 
 // Overrides a base module with the given OverrideModule.
-func (b *OverridableModuleBase) override(ctx BaseModuleContext, bm OverridableModule, o OverrideModule) {
+func (b *OverridableModuleBase) override(ctx BaseModuleContext, m Module, o OverrideModule) {
+
 	for _, p := range b.overridableProperties {
 		for _, op := range o.getOverridingProperties() {
 			if proptools.TypeEqual(p, op) {
 				err := proptools.ExtendProperties(p, op, nil, proptools.OrderReplace)
 				if err != nil {
 					if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-						ctx.OtherModulePropertyErrorf(bm, propertyErr.Property, "%s", propertyErr.Err.Error())
+						ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
 					} else {
 						panic(err)
 					}
@@ -210,7 +210,7 @@
 	// Adds the base module to the overrides property, if exists, of the overriding module. See the
 	// comment on OverridableModuleBase.overridesProperty for details.
 	if b.overridesProperty != nil {
-		*b.overridesProperty = append(*b.overridesProperty, ctx.OtherModuleName(bm))
+		*b.overridesProperty = append(*b.overridesProperty, ctx.ModuleName())
 	}
 	b.overridableModuleProperties.OverriddenBy = o.Name()
 	b.overridableModuleProperties.OverriddenByModuleDir = o.ModuleDir()
@@ -235,7 +235,7 @@
 // to keep them in this order and not put any order mutators between them.
 func RegisterOverridePostDepsMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel()
-	ctx.Transition("override", &overrideTransitionMutator{})
+	ctx.BottomUp("perform_override", performOverrideMutator).Parallel()
 	// overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
 	// add deps from overridable properties.
 	ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel()
@@ -262,6 +262,18 @@
 			ctx.PropertyErrorf("base", "%q is not a valid module name", base)
 			return
 		}
+		// See if there's a prebuilt module that overrides this override module with prefer flag,
+		// in which case we call HideFromMake on the corresponding variant later.
+		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) {
+			prebuilt := GetEmbeddedPrebuilt(dep)
+			if prebuilt == nil {
+				panic("PrebuiltDepTag leads to a non-prebuilt module " + dep.Name())
+			}
+			if prebuilt.UsePrebuilt() {
+				module.setOverriddenByPrebuilt(dep)
+				return
+			}
+		})
 		baseModule := ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)[0]
 		if o, ok := baseModule.(OverridableModule); ok {
 			overrideModule := ctx.Module().(OverrideModule)
@@ -273,13 +285,11 @@
 
 // Now, goes through all overridable modules, finds all modules overriding them, creates a local
 // variant for each of them, and performs the actual overriding operation by calling override().
-type overrideTransitionMutator struct{}
-
-func (overrideTransitionMutator) Split(ctx BaseModuleContext) []string {
+func performOverrideMutator(ctx BottomUpMutatorContext) {
 	if b, ok := ctx.Module().(OverridableModule); ok {
 		overrides := b.getOverrides()
 		if len(overrides) == 0 {
-			return []string{""}
+			return
 		}
 		variants := make([]string, len(overrides)+1)
 		// The first variant is for the original, non-overridden, base module.
@@ -287,74 +297,32 @@
 		for i, o := range overrides {
 			variants[i+1] = o.(Module).Name()
 		}
-		return variants
+		mods := ctx.CreateLocalVariations(variants...)
+		// Make the original variation the default one to depend on if no other override module variant
+		// is specified.
+		ctx.AliasVariation(variants[0])
+		for i, o := range overrides {
+			mods[i+1].(OverridableModule).override(ctx, mods[i+1], o)
+			if prebuilt := o.getOverriddenByPrebuilt(); prebuilt != nil {
+				// The overriding module itself, too, is overridden by a prebuilt.
+				// Perform the same check for replacement
+				checkInvariantsForSourceAndPrebuilt(ctx, mods[i+1], prebuilt)
+				// Copy the flag and hide it in make
+				mods[i+1].ReplacedByPrebuilt()
+			}
+		}
 	} else if o, ok := ctx.Module().(OverrideModule); ok {
 		// Create a variant of the overriding module with its own name. This matches the above local
 		// variant name rule for overridden modules, and thus allows ReplaceDependencies to match the
 		// two.
-		return []string{o.Name()}
-	}
-
-	return []string{""}
-}
-
-func (overrideTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
-	if o, ok := ctx.Module().(OverrideModule); ok {
-		if ctx.DepTag() == overrideBaseDepTag {
-			return o.Name()
-		}
-	}
-
-	// Variations are always local and shouldn't affect the variant used for dependencies
-	return ""
-}
-
-func (overrideTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
-	if _, ok := ctx.Module().(OverridableModule); ok {
-		return incomingVariation
-	} else if o, ok := ctx.Module().(OverrideModule); ok {
-		// To allow dependencies to be added without having to know the variation.
-		return o.Name()
-	}
-
-	return ""
-}
-
-func (overrideTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
-	if o, ok := ctx.Module().(OverrideModule); ok {
-		overridableDeps := ctx.GetDirectDepsWithTag(overrideBaseDepTag)
-		if len(overridableDeps) > 1 {
-			panic(fmt.Errorf("expected a single dependency with overrideBaseDepTag, found %q", overridableDeps))
-		} else if len(overridableDeps) == 1 {
-			b := overridableDeps[0].(OverridableModule)
-			b.override(ctx, b, o)
-
-			checkPrebuiltReplacesOverride(ctx, b)
-		}
-	}
-}
-
-func checkPrebuiltReplacesOverride(ctx BottomUpMutatorContext, b OverridableModule) {
-	// See if there's a prebuilt module that overrides this override module with prefer flag,
-	// in which case we call HideFromMake on the corresponding variant later.
-	prebuiltDeps := ctx.GetDirectDepsWithTag(PrebuiltDepTag)
-	for _, prebuiltDep := range prebuiltDeps {
-		prebuilt := GetEmbeddedPrebuilt(prebuiltDep)
-		if prebuilt == nil {
-			panic("PrebuiltDepTag leads to a non-prebuilt module " + prebuiltDep.Name())
-		}
-		if prebuilt.UsePrebuilt() {
-			// The overriding module itself, too, is overridden by a prebuilt.
-			// Perform the same check for replacement
-			checkInvariantsForSourceAndPrebuilt(ctx, b, prebuiltDep)
-			// Copy the flag and hide it in make
-			b.ReplacedByPrebuilt()
-		}
+		ctx.CreateLocalVariations(o.Name())
+		// To allow dependencies to be added without having to know the above variation.
+		ctx.AliasVariation(o.Name())
 	}
 }
 
 func overridableModuleDepsMutator(ctx BottomUpMutatorContext) {
-	if b, ok := ctx.Module().(OverridableModule); ok && b.Enabled() {
+	if b, ok := ctx.Module().(OverridableModule); ok && b.Enabled(ctx) {
 		b.OverridablePropertiesDepsMutator(ctx)
 	}
 }
diff --git a/android/packaging_test.go b/android/packaging_test.go
index 3833437..4b72c24 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -25,8 +25,9 @@
 type componentTestModule struct {
 	ModuleBase
 	props struct {
-		Deps         []string
-		Skip_install *bool
+		Deps            []string
+		Build_only_deps []string
+		Skip_install    *bool
 	}
 }
 
@@ -36,6 +37,18 @@
 	InstallAlwaysNeededDependencyTag
 }
 
+// dep tag for build_only_deps
+type buildOnlyDepTag struct {
+	blueprint.BaseDependencyTag
+	InstallAlwaysNeededDependencyTag
+}
+
+var _ SkipToTransitiveDepsTag = (*buildOnlyDepTag)(nil)
+
+func (tag buildOnlyDepTag) SkipToTransitiveDeps() bool {
+	return true
+}
+
 func componentTestModuleFactory() Module {
 	m := &componentTestModule{}
 	m.AddProperties(&m.props)
@@ -45,6 +58,7 @@
 
 func (m *componentTestModule) DepsMutator(ctx BottomUpMutatorContext) {
 	ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
+	ctx.AddDependency(ctx.Module(), buildOnlyDepTag{}, m.props.Build_only_deps...)
 }
 
 func (m *componentTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -398,3 +412,30 @@
 		}
 		`, []string{"lib64/foo", "lib64/bar", "lib64/baz"})
 }
+
+func TestPackagingWithSkipToTransitvDeps(t *testing.T) {
+	// packag -[deps]-> foo -[build_only_deps]-> bar -[deps]-> baz
+	// bar isn't installed, but it brings baz to its parent.
+	multiTarget := false
+	runPackagingTest(t, multiTarget,
+		`
+		component {
+			name: "foo",
+			build_only_deps: ["bar"],
+		}
+
+		component {
+			name: "bar",
+			deps: ["baz"],
+		}
+
+		component {
+			name: "baz",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+		}
+		`, []string{"lib64/foo", "lib64/baz"})
+}
diff --git a/android/paths.go b/android/paths.go
index 39b660c..8d92aa4 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -60,6 +60,7 @@
 
 	ModuleDir() string
 	ModuleErrorf(fmt string, args ...interface{})
+	OtherModulePropertyErrorf(module Module, property, fmt string, args ...interface{})
 }
 
 var _ EarlyModulePathContext = ModuleContext(nil)
@@ -561,7 +562,7 @@
 	if module == nil {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
-	if aModule, ok := module.(Module); ok && !aModule.Enabled() {
+	if aModule, ok := module.(Module); ok && !aModule.Enabled(ctx) {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
 	if outProducer, ok := module.(OutputFileProducer); ok {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 2b7b55b..51b86a5 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -275,7 +275,7 @@
 	srcPropertyName := proptools.PropertyNameForField(srcField)
 
 	srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
-		if !module.Enabled() {
+		if !module.Enabled(ctx) {
 			return nil
 		}
 		value := srcPropsValue.FieldByIndex(srcFieldIndex)
@@ -425,7 +425,7 @@
 	m := ctx.Module()
 	// If this module is a prebuilt, is enabled and has not been renamed to source then add a
 	// dependency onto the source if it is present.
-	if p := GetEmbeddedPrebuilt(m); p != nil && m.Enabled() && !p.properties.PrebuiltRenamedToSource {
+	if p := GetEmbeddedPrebuilt(m); p != nil && m.Enabled(ctx) && !p.properties.PrebuiltRenamedToSource {
 		bmn, _ := m.(baseModuleName)
 		name := bmn.BaseModuleName()
 		if ctx.OtherModuleReverseDependencyVariantExists(name) {
@@ -437,7 +437,7 @@
 		// TODO: When all branches contain this singleton module, make this strict
 		// TODO: Add this dependency only for mainline prebuilts and not every prebuilt module
 		if ctx.OtherModuleExists("all_apex_contributions") {
-			ctx.AddDependency(m, acDepTag, "all_apex_contributions")
+			ctx.AddDependency(m, AcDepTag, "all_apex_contributions")
 		}
 
 	}
@@ -474,7 +474,7 @@
 		}
 		// Propagate the provider received from `all_apex_contributions`
 		// to the source module
-		ctx.VisitDirectDepsWithTag(acDepTag, func(am Module) {
+		ctx.VisitDirectDepsWithTag(AcDepTag, func(am Module) {
 			psi, _ := OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
 			SetProvider(ctx, PrebuiltSelectionInfoProvider, psi)
 		})
@@ -580,7 +580,7 @@
 		bmn, _ := m.(baseModuleName)
 		name := bmn.BaseModuleName()
 		psi := PrebuiltSelectionInfoMap{}
-		ctx.VisitDirectDepsWithTag(acDepTag, func(am Module) {
+		ctx.VisitDirectDepsWithTag(AcDepTag, func(am Module) {
 			psi, _ = OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
 		})
 
@@ -702,7 +702,7 @@
 	}
 
 	// If source is not available or is disabled then always use the prebuilt.
-	if source == nil || !source.Enabled() {
+	if source == nil || !source.Enabled(ctx) {
 		return true
 	}
 
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 575b926..d775ac3 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -351,7 +351,7 @@
 						}
 					})
 
-					moduleIsDisabled := !foo.Module().Enabled()
+					moduleIsDisabled := !foo.Module().Enabled(PanickingConfigAndErrorContext(result.TestContext))
 					deps := foo.Module().(*sourceModule).deps
 					if moduleIsDisabled {
 						if len(deps) > 0 {
diff --git a/android/register.go b/android/register.go
index d00c15f..aeb3b4c 100644
--- a/android/register.go
+++ b/android/register.go
@@ -16,8 +16,9 @@
 
 import (
 	"fmt"
-	"github.com/google/blueprint"
 	"reflect"
+
+	"github.com/google/blueprint"
 )
 
 // A sortable component is one whose registration order affects the order in which it is executed
diff --git a/android/singleton.go b/android/singleton.go
index 76df1eb..d364384 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -284,5 +284,5 @@
 }
 
 func (s *singletonContextAdaptor) OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) {
-	s.blueprintSingletonContext().OtherModulePropertyErrorf(module, property, format, args)
+	s.blueprintSingletonContext().OtherModulePropertyErrorf(module, property, format, args...)
 }
diff --git a/android/testing.go b/android/testing.go
index 7b4411e..6518f4a 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1122,7 +1122,7 @@
 
 	entriesList := p.AndroidMkEntries()
 	aconfigUpdateAndroidMkEntries(ctx, mod.(Module), &entriesList)
-	for i, _ := range entriesList {
+	for i := range entriesList {
 		entriesList[i].fillInEntries(ctx, mod)
 	}
 	return entriesList
@@ -1287,3 +1287,21 @@
 		t.Errorf("%q is not found in %v", expected, result)
 	}
 }
+
+type panickingConfigAndErrorContext struct {
+	ctx *TestContext
+}
+
+func (ctx *panickingConfigAndErrorContext) OtherModulePropertyErrorf(module Module, property, fmt string, args ...interface{}) {
+	panic(ctx.ctx.PropertyErrorf(module, property, fmt, args...).Error())
+}
+
+func (ctx *panickingConfigAndErrorContext) Config() Config {
+	return ctx.ctx.Config()
+}
+
+func PanickingConfigAndErrorContext(ctx *TestContext) ConfigAndErrorContext {
+	return &panickingConfigAndErrorContext{
+		ctx: ctx,
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index 2649fbd..419bd61 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -58,13 +58,13 @@
 		// unbundled_build is a catch-all property to annotate modules that don't build in one or
 		// more unbundled branches, usually due to dependencies missing from the manifest.
 		Unbundled_build struct {
-			Enabled *bool `android:"arch_variant"`
+			Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 		} `android:"arch_variant"`
 
 		// similar to `Unbundled_build`, but `Always_use_prebuilt_sdks` means that it uses prebuilt
 		// sdk specifically.
 		Always_use_prebuilt_sdks struct {
-			Enabled *bool `android:"arch_variant"`
+			Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 		} `android:"arch_variant"`
 
 		Malloc_low_memory struct {
diff --git a/apex/apex.go b/apex/apex.go
index ef57d7e..9a80ec6 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -490,9 +490,6 @@
 	javaApisUsedByModuleFile     android.ModuleOutPath
 
 	aconfigFiles []android.Path
-
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
 }
 
 // apexFileClass represents a type of file that can be included in APEX.
@@ -1077,7 +1074,7 @@
 // specific variant to modules that support the ApexInfoMutator.
 // It also propagates updatable=true to apps of updatable apexes
 func apexInfoMutator(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 
@@ -1094,7 +1091,7 @@
 // apexStrictUpdatibilityLintMutator propagates strict_updatability_linting to transitive deps of a mainline module
 // This check is enforced for updatable modules
 func apexStrictUpdatibilityLintMutator(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting() {
@@ -1121,7 +1118,7 @@
 
 // enforceAppUpdatability propagates updatable=true to apps of updatable apexes
 func enforceAppUpdatability(mctx android.TopDownMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if apex, ok := mctx.Module().(*apexBundle); ok && apex.Updatable() {
@@ -1199,7 +1196,7 @@
 // unique apex variations for this module. See android/apex.go for more about unique apex variant.
 // TODO(jiyong): move this to android/apex.go?
 func apexUniqueVariationsMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if am, ok := mctx.Module().(android.ApexModule); ok {
@@ -1211,7 +1208,7 @@
 // the apex in order to retrieve its contents later.
 // TODO(jiyong): move this to android/apex.go?
 func apexTestForDepsMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if am, ok := mctx.Module().(android.ApexModule); ok {
@@ -1226,7 +1223,7 @@
 
 // TODO(jiyong): move this to android/apex.go?
 func apexTestForMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if _, ok := mctx.Module().(android.ApexModule); ok {
@@ -1340,7 +1337,7 @@
 // See android.UpdateDirectlyInAnyApex
 // TODO(jiyong): move this to android/apex.go?
 func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
-	if !mctx.Module().Enabled() {
+	if !mctx.Module().Enabled(mctx) {
 		return
 	}
 	if am, ok := mctx.Module().(android.ApexModule); ok {
@@ -1677,7 +1674,13 @@
 	af.jacocoReportClassesFile = module.JacocoReportClassesFile()
 	af.lintDepSets = module.LintDepSets()
 	af.customStem = module.Stem() + ".jar"
-	if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
+	// TODO: b/338641779 - Remove special casing of sdkLibrary once bcpf and sscpf depends
+	// on the implementation library
+	if sdkLib, ok := module.(*java.SdkLibrary); ok {
+		for _, install := range sdkLib.BuiltInstalledForApex() {
+			af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
+		}
+	} else if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
 		for _, install := range dexpreopter.DexpreoptBuiltInstalledForApex() {
 			af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
 		}
@@ -1968,7 +1971,7 @@
 	if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
 		return false
 	}
-	if mod, ok := child.(android.Module); ok && !mod.Enabled() {
+	if mod, ok := child.(android.Module); ok && !mod.Enabled(ctx) {
 		return false
 	}
 	depName := ctx.OtherModuleName(child)
@@ -2320,7 +2323,7 @@
 }
 
 func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) {
-	if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigTransitiveDeclarationsInfoProvider); ok {
+	if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigPropagatingProviderKey); ok {
 		if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil {
 			vctx.aconfigFiles = append(vctx.aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...)
 		}
@@ -2408,7 +2411,6 @@
 			return
 		}
 	}
-	android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles)
 
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 3) some fields in apexBundle struct are configured
@@ -2580,9 +2582,6 @@
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
-
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
 }
 
 // apex_defaults provides defaultable properties to other apex modules.
@@ -2605,10 +2604,6 @@
 	android.OverrideModuleBase
 }
 
-func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles)
-}
-
 func (o *OverrideApex) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 }
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 0b26f16..9a5c2b4 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -5601,8 +5601,21 @@
 			compile_dex: true,
 		}
 	`
+		// This test disables libbar, which causes the ComponentDepsMutator to add
+		// deps on libbar.stubs and other sub-modules that don't exist. We can
+		// enable AllowMissingDependencies to work around that, but enabling that
+		// causes extra checks for missing source files to dex_bootjars, so add those
+		// to the mock fs as well.
+		preparer2 := android.GroupFixturePreparers(
+			preparer,
+			android.PrepareForTestWithAllowMissingDependencies,
+			android.FixtureMergeMockFs(map[string][]byte{
+				"build/soong/scripts/check_boot_jars/package_allowed_list.txt": nil,
+				"frameworks/base/config/boot-profile.txt":                      nil,
+			}),
+		)
 
-		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer2, fragment)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
@@ -7146,7 +7159,7 @@
 
 	// The bar library should depend on the implementation jar.
 	barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
-	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -7287,7 +7300,7 @@
 
 	// The bar library should depend on the implementation jar.
 	barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
-	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -9220,7 +9233,7 @@
 								continue
 							}
 							mod := ctx.ModuleForTests(modName, variant).Module().(*cc.Module)
-							if !mod.Enabled() || mod.IsHideFromMake() {
+							if !mod.Enabled(android.PanickingConfigAndErrorContext(ctx)) || mod.IsHideFromMake() {
 								continue
 							}
 							for _, ent := range android.AndroidMkEntriesForTest(t, ctx, mod) {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index ea847e1..72a9e52 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -503,9 +503,6 @@
 	inputApex android.Path
 
 	provenanceMetaDataFile android.OutputPath
-
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
 }
 
 type ApexFileProperties struct {
@@ -881,8 +878,6 @@
 		p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks...)
 		p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile)
 	}
-
-	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
 }
 
 func (p *Prebuilt) ProvenanceMetaDataFile() android.OutputPath {
diff --git a/cc/Android.bp b/cc/Android.bp
index 9ce8933..3bbcaa9 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -51,6 +51,7 @@
         "vndk.go",
         "vndk_prebuilt.go",
 
+        "cmake_snapshot.go",
         "cmakelists.go",
         "compdb.go",
         "compiler.go",
@@ -92,6 +93,7 @@
         "binary_test.go",
         "cc_test.go",
         "cc_test_only_property_test.go",
+        "cmake_snapshot_test.go",
         "compiler_test.go",
         "gen_test.go",
         "genrule_test.go",
@@ -109,5 +111,12 @@
         "tidy_test.go",
         "vendor_public_library_test.go",
     ],
+    embedSrcs: [
+        "cmake_ext_add_aidl_library.txt",
+        "cmake_ext_append_flags.txt",
+        "cmake_main.txt",
+        "cmake_module_aidl.txt",
+        "cmake_module_cc.txt",
+    ],
     pluginFor: ["soong_build"],
 }
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 071d829..0c6f97c 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -119,16 +119,13 @@
 				} else if c.InProduct() {
 					entries.SetBool("LOCAL_IN_PRODUCT", true)
 				}
-				if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
-					// Make the SDK variant uninstallable so that there are not two rules to install
-					// to the same location.
-					entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+				if c.Properties.SdkAndPlatformVariantVisibleToMake {
 					// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
 					// dependencies to the .sdk suffix when building a module that uses the SDK.
 					entries.SetString("SOONG_SDK_VARIANT_MODULES",
 						"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
 				}
-				android.SetAconfigFileMkEntries(c.AndroidModuleBase(), entries, c.mergedAconfigFiles)
+				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", c.IsSkipInstall())
 			},
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
diff --git a/cc/cc.go b/cc/cc.go
index b48a573..eb6e974 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -353,6 +353,10 @@
 	// for building binaries that are started before APEXes are activated.
 	Bootstrap *bool
 
+	// Allows this module to be included in CMake release snapshots to be built outside of Android
+	// build system and source tree.
+	Cmake_snapshot_supported *bool
+
 	// Even if DeviceConfig().VndkUseCoreVariant() is set, this module must use vendor variant.
 	// see soong/cc/config/vndk.go
 	MustUseVendorVariant bool `blueprint:"mutated"`
@@ -588,6 +592,7 @@
 	compilerDeps(ctx DepsContext, deps Deps) Deps
 	compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags
 	compilerProps() []interface{}
+	baseCompilerProps() BaseCompilerProperties
 
 	appendCflags([]string)
 	appendAsflags([]string)
@@ -602,6 +607,7 @@
 	linkerDeps(ctx DepsContext, deps Deps) Deps
 	linkerFlags(ctx ModuleContext, flags Flags) Flags
 	linkerProps() []interface{}
+	baseLinkerProps() BaseLinkerProperties
 	useClangLld(actx ModuleContext) bool
 
 	link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path
@@ -907,9 +913,6 @@
 
 	hideApexVariantFromMake bool
 
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
-
 	logtagsPaths android.Paths
 }
 
@@ -1993,10 +1996,6 @@
 	return false
 }
 
-func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles)
-}
-
 func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
 	ctx := moduleContextFromAndroidModuleContext(actx, c)
 
@@ -2163,7 +2162,9 @@
 
 	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
 
-	android.CollectDependencyAconfigFiles(ctx, &c.mergedAconfigFiles)
+	if Bool(c.Properties.Cmake_snapshot_supported) {
+		android.SetProvider(ctx, cmakeSnapshotSourcesProvider, android.GlobFiles(ctx, ctx.ModuleDir()+"/**/*", nil))
+	}
 
 	c.maybeInstall(ctx, apexInfo)
 
@@ -2516,7 +2517,7 @@
 }
 
 func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
-	if !c.Enabled() {
+	if !c.Enabled(actx) {
 		return
 	}
 
@@ -2764,7 +2765,7 @@
 }
 
 func BeginMutator(ctx android.BottomUpMutatorContext) {
-	if c, ok := ctx.Module().(*Module); ok && c.Enabled() {
+	if c, ok := ctx.Module().(*Module); ok && c.Enabled(ctx) {
 		c.beginMutator(ctx)
 	}
 }
@@ -4063,9 +4064,6 @@
 	android.ModuleBase
 	android.DefaultsModuleBase
 	android.ApexModuleBase
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
 }
 
 // cc_defaults provides a set of properties that can be inherited by other cc
diff --git a/cc/cmake_ext_add_aidl_library.txt b/cc/cmake_ext_add_aidl_library.txt
new file mode 100644
index 0000000..dcf805a
--- /dev/null
+++ b/cc/cmake_ext_add_aidl_library.txt
@@ -0,0 +1,47 @@
+function(add_aidl_library NAME LANG SOURCES AIDLFLAGS)
+    if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20")
+        cmake_policy(SET CMP0116 NEW)
+    endif()
+
+    set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/.intermediates/${NAME}-source")
+    set(GEN_SOURCES)
+    foreach(SOURCE ${SOURCES})
+        get_filename_component(SOURCE_WE ${SOURCE} NAME_WE)
+        get_filename_component(SOURCE_ABSOLUTE ${SOURCE} ABSOLUTE)
+        get_filename_component(SOURCE_DIR ${SOURCE_ABSOLUTE} DIRECTORY)
+        set(GEN_SOURCE "${GEN_DIR}/${SOURCE_WE}.cpp")
+        set(DEPFILE_ARG)
+        if (NOT ${CMAKE_GENERATOR} MATCHES "Unix Makefiles")
+            set(DEPFILE_ARG DEPFILE "${GEN_SOURCE}.d")
+        endif()
+        add_custom_command(
+            OUTPUT "${GEN_SOURCE}"
+            MAIN_DEPENDENCY "${SOURCE_ABSOLUTE}"
+            ${DEPFILE_ARG}
+            COMMAND "${AIDL_BIN}"
+            ARGS
+            --lang=${LANG}
+            --include="${SOURCE_DIR}"
+            --dep="${GEN_SOURCE}.d"
+            --out="${GEN_DIR}"
+            --header_out="${GEN_DIR}/include"
+            --ninja
+            --structured
+            --min_sdk_version=current
+            ${AIDLFLAGS}
+            "${SOURCE_ABSOLUTE}"
+        )
+        list(APPEND GEN_SOURCES "${GEN_SOURCE}")
+    endforeach()
+
+    add_library(${NAME} ${GEN_SOURCES})
+
+    target_include_directories(${NAME}
+        PUBLIC
+        "${GEN_DIR}/include"
+        "${ANDROID_BUILD_TOP}/frameworks/native/libs/binder/ndk/include_${LANG}"
+    )
+    target_link_libraries(${NAME}
+        libbinder_sdk
+    )
+endfunction()
diff --git a/cc/cmake_ext_append_flags.txt b/cc/cmake_ext_append_flags.txt
new file mode 100644
index 0000000..2cfb1ac
--- /dev/null
+++ b/cc/cmake_ext_append_flags.txt
@@ -0,0 +1,10 @@
+include(CheckCXXCompilerFlag)
+
+macro(append_cxx_flags_if_supported VAR)
+  foreach(FLAG ${ARGN})
+    check_cxx_compiler_flag(${FLAG} HAS_FLAG${FLAG})
+    if(${HAS_FLAG${FLAG}})
+      list(APPEND ${VAR} ${FLAG})
+    endif()
+  endforeach()
+endmacro()
diff --git a/cc/cmake_main.txt b/cc/cmake_main.txt
new file mode 100644
index 0000000..deb1de1
--- /dev/null
+++ b/cc/cmake_main.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 3.18)
+project(<<.M.Name>> CXX)
+set(CMAKE_CXX_STANDARD 20)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+include(AddAidlLibrary)
+include(AppendCxxFlagsIfSupported)
+
+if (NOT ANDROID_BUILD_TOP)
+    set(ANDROID_BUILD_TOP "${CMAKE_CURRENT_SOURCE_DIR}")
+endif()
+
+set(PREBUILTS_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/prebuilts/host/linux-x86/bin")
+if (NOT AIDL_BIN)
+    find_program(AIDL_BIN aidl REQUIRED HINTS "${PREBUILTS_BIN_DIR}")
+endif()
+
+<<cflagsList .M.Name "_CFLAGS" .M.Properties.Cflags .M.Properties.Unportable_flags .M.Properties.Cflags_ignored>>
+
+<<range .Pprop.SystemPackages ->>
+find_package(<<.>> REQUIRED)
+<<end >>
+<<range .Pprop.PregeneratedPackages ->>
+add_subdirectory("${ANDROID_BUILD_TOP}/<<.>>" "<<.>>/build" EXCLUDE_FROM_ALL)
+<<end>>
+add_compile_options(${<<.M.Name>>_CFLAGS})
+<<range $moduleDir, $value := .ModuleDirs ->>
+add_subdirectory(<<$moduleDir>>)
+<<end>>
diff --git a/cc/cmake_module_aidl.txt b/cc/cmake_module_aidl.txt
new file mode 100644
index 0000000..4509a88
--- /dev/null
+++ b/cc/cmake_module_aidl.txt
@@ -0,0 +1,8 @@
+# <<.M.Name>>
+
+<<setList .M.Name "_SRCS" "${ANDROID_BUILD_TOP}/" (getCompilerProperties .M).AidlInterface.Sources>>
+
+<<setList .M.Name "_AIDLFLAGS" "" (getCompilerProperties .M).AidlInterface.Flags>>
+
+add_aidl_library(<<.M.Name>> <<(getCompilerProperties .M).AidlInterface.Lang>> "${<<.M.Name>>_SRCS}" "${<<.M.Name>>_AIDLFLAGS}")
+add_library(android::<<.M.Name>> ALIAS <<.M.Name>>)
diff --git a/cc/cmake_module_cc.txt b/cc/cmake_module_cc.txt
new file mode 100644
index 0000000..571f27c
--- /dev/null
+++ b/cc/cmake_module_cc.txt
@@ -0,0 +1,35 @@
+<<$srcs := getSources .M>>
+<<$includeDirs := getIncludeDirs .Ctx .M>>
+<<$cflags := (getCompilerProperties .M).Cflags>>
+<<$deps := mapLibraries (concat5
+(getLinkerProperties .M).Whole_static_libs
+(getLinkerProperties .M).Static_libs
+(getLinkerProperties .M).Shared_libs
+(getLinkerProperties .M).Header_libs
+(getExtraLibs .M)
+) .Pprop.LibraryMapping>>
+
+# <<.M.Name>>
+<<if $srcs>>
+<<setList .M.Name "_SRCS" "${ANDROID_BUILD_TOP}/" (toStrings $srcs)>>
+add_<<getModuleType .M>>(<<.M.Name>> ${<<.M.Name>>_SRCS})
+<<- else>>
+add_<<getModuleType .M>>(<<.M.Name>> INTERFACE)
+<<- end>>
+add_<<getModuleType .M>>(android::<<.M.Name>> ALIAS <<.M.Name>>)
+<<print "">>
+
+<<- if $includeDirs>>
+<<setList .M.Name "_INCLUDES" "${ANDROID_BUILD_TOP}/" $includeDirs>>
+target_include_directories(<<.M.Name>> <<if $srcs>>PUBLIC<<else>>INTERFACE<<end>> ${<<.M.Name>>_INCLUDES})
+<<end>>
+
+<<- if and $srcs $cflags>>
+<<cflagsList .M.Name "_CFLAGS" $cflags .Snapshot.Properties.Unportable_flags .Snapshot.Properties.Cflags_ignored>>
+target_compile_options(<<.M.Name>> PRIVATE ${<<.M.Name>>_CFLAGS})
+<<end>>
+
+<<- if $deps>>
+<<setList .M.Name "_DEPENDENCIES" "" $deps>>
+target_link_libraries(<<.M.Name>> <<if not $srcs>>INTERFACE <<end ->> ${<<.M.Name>>_DEPENDENCIES})
+<<end>>
diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go
new file mode 100644
index 0000000..0635a29
--- /dev/null
+++ b/cc/cmake_snapshot.go
@@ -0,0 +1,526 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"android/soong/android"
+	"bytes"
+	_ "embed"
+	"fmt"
+	"path/filepath"
+	"slices"
+	"sort"
+	"strings"
+	"text/template"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+const veryVerbose bool = false
+
+//go:embed cmake_main.txt
+var templateCmakeMainRaw string
+var templateCmakeMain *template.Template = parseTemplate(templateCmakeMainRaw)
+
+//go:embed cmake_module_cc.txt
+var templateCmakeModuleCcRaw string
+var templateCmakeModuleCc *template.Template = parseTemplate(templateCmakeModuleCcRaw)
+
+//go:embed cmake_module_aidl.txt
+var templateCmakeModuleAidlRaw string
+var templateCmakeModuleAidl *template.Template = parseTemplate(templateCmakeModuleAidlRaw)
+
+//go:embed cmake_ext_add_aidl_library.txt
+var cmakeExtAddAidlLibrary string
+
+//go:embed cmake_ext_append_flags.txt
+var cmakeExtAppendFlags string
+
+var defaultUnportableFlags []string = []string{
+	"-Wno-class-memaccess",
+	"-Wno-exit-time-destructors",
+	"-Wno-inconsistent-missing-override",
+	"-Wreorder-init-list",
+	"-Wno-reorder-init-list",
+	"-Wno-restrict",
+	"-Wno-stringop-overread",
+	"-Wno-subobject-linkage",
+}
+
+var ignoredSystemLibs []string = []string{
+	"libc++",
+	"libc++_static",
+	"prebuilt_libclang_rt.builtins",
+	"prebuilt_libclang_rt.ubsan_minimal",
+}
+
+// Mapping entry between Android's library name and the one used when building outside Android tree.
+type LibraryMappingProperty struct {
+	// Android library name.
+	Android_name string
+
+	// Library name used when building outside Android.
+	Mapped_name string
+
+	// If the make file is already present in Android source tree, specify its location.
+	Package_pregenerated string
+
+	// If the package is expected to be installed on the build host OS, specify its name.
+	Package_system string
+}
+
+type CmakeSnapshotProperties struct {
+	// Modules to add to the snapshot package. Their dependencies are pulled in automatically.
+	Modules []string
+
+	// Host prebuilts to bundle with the snapshot. These are tools needed to build outside Android.
+	Prebuilts []string
+
+	// Global cflags to add when building outside Android.
+	Cflags []string
+
+	// Flags to skip when building outside Android.
+	Cflags_ignored []string
+
+	// Mapping between library names used in Android tree and externally.
+	Library_mapping []LibraryMappingProperty
+
+	// List of cflags that are not portable between compilers that could potentially be used to
+	// build a generated package. If left empty, it's initialized with a default list.
+	Unportable_flags []string
+
+	// Whether to include source code as part of the snapshot package.
+	Include_sources bool
+}
+
+var cmakeSnapshotSourcesProvider = blueprint.NewProvider[android.Paths]()
+
+type CmakeSnapshot struct {
+	android.ModuleBase
+
+	Properties CmakeSnapshotProperties
+
+	zipPath android.WritablePath
+}
+
+type cmakeProcessedProperties struct {
+	LibraryMapping       map[string]LibraryMappingProperty
+	PregeneratedPackages []string
+	SystemPackages       []string
+}
+
+type cmakeSnapshotDependencyTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
+var (
+	cmakeSnapshotModuleTag   = cmakeSnapshotDependencyTag{name: "cmake-snapshot-module"}
+	cmakeSnapshotPrebuiltTag = cmakeSnapshotDependencyTag{name: "cmake-snapshot-prebuilt"}
+)
+
+func parseTemplate(templateContents string) *template.Template {
+	funcMap := template.FuncMap{
+		"setList": func(name string, nameSuffix string, itemPrefix string, items []string) string {
+			var list strings.Builder
+			list.WriteString("set(" + name + nameSuffix)
+			templateListBuilder(&list, itemPrefix, items)
+			return list.String()
+		},
+		"toStrings": func(files android.Paths) []string {
+			strings := make([]string, len(files))
+			for idx, file := range files {
+				strings[idx] = file.String()
+			}
+			return strings
+		},
+		"concat5": func(list1 []string, list2 []string, list3 []string, list4 []string, list5 []string) []string {
+			return append(append(append(append(list1, list2...), list3...), list4...), list5...)
+		},
+		"cflagsList": func(name string, nameSuffix string, flags []string,
+			unportableFlags []string, ignoredFlags []string) string {
+			if len(unportableFlags) == 0 {
+				unportableFlags = defaultUnportableFlags
+			}
+
+			var filteredPortable []string
+			var filteredUnportable []string
+			for _, flag := range flags {
+				if slices.Contains(ignoredFlags, flag) {
+					continue
+				} else if slices.Contains(unportableFlags, flag) {
+					filteredUnportable = append(filteredUnportable, flag)
+				} else {
+					filteredPortable = append(filteredPortable, flag)
+				}
+			}
+
+			var list strings.Builder
+
+			list.WriteString("set(" + name + nameSuffix)
+			templateListBuilder(&list, "", filteredPortable)
+
+			if len(filteredUnportable) > 0 {
+				list.WriteString("\nappend_cxx_flags_if_supported(" + name + nameSuffix)
+				templateListBuilder(&list, "", filteredUnportable)
+			}
+
+			return list.String()
+		},
+		"getSources": func(m *Module) android.Paths {
+			return m.compiler.(CompiledInterface).Srcs()
+		},
+		"getModuleType": getModuleType,
+		"getCompilerProperties": func(m *Module) BaseCompilerProperties {
+			return m.compiler.baseCompilerProps()
+		},
+		"getLinkerProperties": func(m *Module) BaseLinkerProperties {
+			return m.linker.baseLinkerProps()
+		},
+		"getExtraLibs":   getExtraLibs,
+		"getIncludeDirs": getIncludeDirs,
+		"mapLibraries": func(libs []string, mapping map[string]LibraryMappingProperty) []string {
+			var mappedLibs []string
+			for _, lib := range libs {
+				mappedLib, exists := mapping[lib]
+				if exists {
+					lib = mappedLib.Mapped_name
+				} else {
+					lib = "android::" + lib
+				}
+				if lib == "" {
+					continue
+				}
+				mappedLibs = append(mappedLibs, lib)
+			}
+			sort.Strings(mappedLibs)
+			mappedLibs = slices.Compact(mappedLibs)
+			return mappedLibs
+		},
+	}
+
+	return template.Must(template.New("").Delims("<<", ">>").Funcs(funcMap).Parse(templateContents))
+}
+
+func sliceWithPrefix(prefix string, slice []string) []string {
+	output := make([]string, len(slice))
+	for i, elem := range slice {
+		output[i] = prefix + elem
+	}
+	return output
+}
+
+func templateListBuilder(builder *strings.Builder, itemPrefix string, items []string) {
+	if len(items) > 0 {
+		builder.WriteString("\n")
+		for _, item := range items {
+			builder.WriteString("    " + itemPrefix + item + "\n")
+		}
+	}
+	builder.WriteString(")")
+}
+
+func executeTemplate(templ *template.Template, buffer *bytes.Buffer, data any) string {
+	buffer.Reset()
+	if err := templ.Execute(buffer, data); err != nil {
+		panic(err)
+	}
+	output := strings.TrimSpace(buffer.String())
+	buffer.Reset()
+	return output
+}
+
+func (m *CmakeSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
+	variations := []blueprint.Variation{
+		{"os", "linux_glibc"},
+		{"arch", "x86_64"},
+	}
+	ctx.AddVariationDependencies(variations, cmakeSnapshotModuleTag, m.Properties.Modules...)
+	ctx.AddVariationDependencies(variations, cmakeSnapshotPrebuiltTag, m.Properties.Prebuilts...)
+}
+
+func (m *CmakeSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var templateBuffer bytes.Buffer
+	var pprop cmakeProcessedProperties
+	m.zipPath = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
+
+	// Process Library_mapping for more efficient lookups
+	pprop.LibraryMapping = map[string]LibraryMappingProperty{}
+	for _, elem := range m.Properties.Library_mapping {
+		pprop.LibraryMapping[elem.Android_name] = elem
+
+		if elem.Package_pregenerated != "" {
+			pprop.PregeneratedPackages = append(pprop.PregeneratedPackages, elem.Package_pregenerated)
+		}
+		sort.Strings(pprop.PregeneratedPackages)
+		pprop.PregeneratedPackages = slices.Compact(pprop.PregeneratedPackages)
+
+		if elem.Package_system != "" {
+			pprop.SystemPackages = append(pprop.SystemPackages, elem.Package_system)
+		}
+		sort.Strings(pprop.SystemPackages)
+		pprop.SystemPackages = slices.Compact(pprop.SystemPackages)
+	}
+
+	// Generating CMakeLists.txt rules for all modules in dependency tree
+	moduleDirs := map[string][]string{}
+	sourceFiles := map[string]android.Path{}
+	visitedModules := map[string]bool{}
+	var pregeneratedModules []*Module
+	ctx.WalkDeps(func(dep_a android.Module, parent android.Module) bool {
+		moduleName := ctx.OtherModuleName(dep_a)
+		dep, ok := dep_a.(*Module)
+		if !ok {
+			return false // not a cc module
+		}
+		if visited := visitedModules[moduleName]; visited {
+			return false // visit only once
+		}
+		visitedModules[moduleName] = true
+		if mapping, ok := pprop.LibraryMapping[moduleName]; ok {
+			if mapping.Package_pregenerated != "" {
+				pregeneratedModules = append(pregeneratedModules, dep)
+			}
+			return false // mapped to system or pregenerated (we'll handle these later)
+		}
+		if ctx.OtherModuleDependencyTag(dep) == cmakeSnapshotPrebuiltTag {
+			return false // we'll handle cmakeSnapshotPrebuiltTag later
+		}
+		if slices.Contains(ignoredSystemLibs, moduleName) {
+			return false // system libs built in-tree for Android
+		}
+		if dep.compiler == nil {
+			return false // unsupported module type (e.g. prebuilt)
+		}
+		isAidlModule := dep.compiler.baseCompilerProps().AidlInterface.Lang != ""
+
+		if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
+			ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
+				"CMake snapshots not supported, despite being a dependency for %s",
+				ctx.OtherModuleName(parent))
+			return false
+		}
+
+		if veryVerbose {
+			fmt.Println("WalkDeps: " + ctx.OtherModuleName(parent) + " -> " + moduleName)
+		}
+
+		// Generate CMakeLists.txt fragment for this module
+		templateToUse := templateCmakeModuleCc
+		if isAidlModule {
+			templateToUse = templateCmakeModuleAidl
+		}
+		moduleFragment := executeTemplate(templateToUse, &templateBuffer, struct {
+			Ctx      *android.ModuleContext
+			M        *Module
+			Snapshot *CmakeSnapshot
+			Pprop    *cmakeProcessedProperties
+		}{
+			&ctx,
+			dep,
+			m,
+			&pprop,
+		})
+		moduleDir := ctx.OtherModuleDir(dep)
+		moduleDirs[moduleDir] = append(moduleDirs[moduleDir], moduleFragment)
+
+		if m.Properties.Include_sources {
+			files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
+			for _, file := range files {
+				sourceFiles[file.String()] = file
+			}
+		}
+
+		// if it's AIDL module, no need to dive into their dependencies
+		return !isAidlModule
+	})
+
+	// Enumerate sources for pregenerated modules
+	if m.Properties.Include_sources {
+		for _, dep := range pregeneratedModules {
+			if !proptools.Bool(dep.Properties.Cmake_snapshot_supported) {
+				ctx.OtherModulePropertyErrorf(dep, "cmake_snapshot_supported",
+					"Pregenerated CMake snapshots not supported, despite being requested for %s",
+					ctx.ModuleName())
+				continue
+			}
+
+			files, _ := android.OtherModuleProvider(ctx, dep, cmakeSnapshotSourcesProvider)
+			for _, file := range files {
+				sourceFiles[file.String()] = file
+			}
+		}
+	}
+
+	// Merging CMakeLists.txt contents for every module directory
+	var makefilesList android.Paths
+	for moduleDir, fragments := range moduleDirs {
+		moduleCmakePath := android.PathForModuleGen(ctx, moduleDir, "CMakeLists.txt")
+		makefilesList = append(makefilesList, moduleCmakePath)
+		sort.Strings(fragments)
+		android.WriteFileRule(ctx, moduleCmakePath, strings.Join(fragments, "\n\n\n"))
+	}
+
+	// Generating top-level CMakeLists.txt
+	mainCmakePath := android.PathForModuleGen(ctx, "CMakeLists.txt")
+	makefilesList = append(makefilesList, mainCmakePath)
+	mainContents := executeTemplate(templateCmakeMain, &templateBuffer, struct {
+		Ctx        *android.ModuleContext
+		M          *CmakeSnapshot
+		ModuleDirs map[string][]string
+		Pprop      *cmakeProcessedProperties
+	}{
+		&ctx,
+		m,
+		moduleDirs,
+		&pprop,
+	})
+	android.WriteFileRule(ctx, mainCmakePath, mainContents)
+
+	// Generating CMake extensions
+	extPath := android.PathForModuleGen(ctx, "cmake", "AppendCxxFlagsIfSupported.cmake")
+	makefilesList = append(makefilesList, extPath)
+	android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAppendFlags)
+	extPath = android.PathForModuleGen(ctx, "cmake", "AddAidlLibrary.cmake")
+	makefilesList = append(makefilesList, extPath)
+	android.WriteFileRuleVerbatim(ctx, extPath, cmakeExtAddAidlLibrary)
+
+	// Generating the final zip file
+	zipRule := android.NewRuleBuilder(pctx, ctx)
+	zipCmd := zipRule.Command().
+		BuiltTool("soong_zip").
+		FlagWithOutput("-o ", m.zipPath)
+
+	// Packaging all sources into the zip file
+	if m.Properties.Include_sources {
+		var sourcesList android.Paths
+		for _, file := range sourceFiles {
+			sourcesList = append(sourcesList, file)
+		}
+
+		sourcesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_sources.rsp")
+		zipCmd.FlagWithRspFileInputList("-r ", sourcesRspFile, sourcesList)
+	}
+
+	// Packaging all make files into the zip file
+	makefilesRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_makefiles.rsp")
+	zipCmd.
+		FlagWithArg("-C ", android.PathForModuleGen(ctx).OutputPath.String()).
+		FlagWithRspFileInputList("-r ", makefilesRspFile, makefilesList)
+
+	// Packaging all prebuilts into the zip file
+	if len(m.Properties.Prebuilts) > 0 {
+		var prebuiltsList android.Paths
+
+		ctx.VisitDirectDepsWithTag(cmakeSnapshotPrebuiltTag, func(dep android.Module) {
+			for _, file := range dep.FilesToInstall() {
+				prebuiltsList = append(prebuiltsList, file)
+			}
+		})
+
+		prebuiltsRspFile := android.PathForModuleObj(ctx, ctx.ModuleName()+"_prebuilts.rsp")
+		zipCmd.
+			FlagWithArg("-C ", android.PathForArbitraryOutput(ctx).String()).
+			FlagWithArg("-P ", "prebuilts").
+			FlagWithRspFileInputList("-r ", prebuiltsRspFile, prebuiltsList)
+	}
+
+	// Finish generating the final zip file
+	zipRule.Build(m.zipPath.String(), "archiving "+ctx.ModuleName())
+}
+
+func (m *CmakeSnapshot) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{m.zipPath}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
+func (m *CmakeSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "DATA",
+		OutputFile: android.OptionalPathForPath(m.zipPath),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+			},
+		},
+	}}
+}
+
+func getModuleType(m *Module) string {
+	switch m.linker.(type) {
+	case *binaryDecorator:
+		return "executable"
+	case *libraryDecorator:
+		return "library"
+	case *testBinary:
+		return "executable"
+	}
+	panic(fmt.Sprintf("Unexpected module type: %T", m.compiler))
+}
+
+func getExtraLibs(m *Module) []string {
+	switch decorator := m.linker.(type) {
+	case *testBinary:
+		if decorator.testDecorator.gtest() {
+			return []string{"libgtest"}
+		}
+	}
+	return nil
+}
+
+func getIncludeDirs(ctx android.ModuleContext, m *Module) []string {
+	moduleDir := ctx.OtherModuleDir(m) + string(filepath.Separator)
+	switch decorator := m.compiler.(type) {
+	case *libraryDecorator:
+		return sliceWithPrefix(moduleDir, decorator.flagExporter.Properties.Export_include_dirs)
+	}
+	return nil
+}
+
+func cmakeSnapshotLoadHook(ctx android.LoadHookContext) {
+	props := struct {
+		Target struct {
+			Darwin struct {
+				Enabled *bool
+			}
+			Windows struct {
+				Enabled *bool
+			}
+		}
+	}{}
+	props.Target.Darwin.Enabled = proptools.BoolPtr(false)
+	props.Target.Windows.Enabled = proptools.BoolPtr(false)
+	ctx.AppendProperties(&props)
+}
+
+// cmake_snapshot allows defining source packages for release outside of Android build tree.
+// As a result of cmake_snapshot module build, a zip file is generated with CMake build definitions
+// for selected source modules, their dependencies and optionally also the source code itself.
+func CmakeSnapshotFactory() android.Module {
+	module := &CmakeSnapshot{}
+	module.AddProperties(&module.Properties)
+	android.AddLoadHook(module, cmakeSnapshotLoadHook)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
+func init() {
+	android.InitRegistrationContext.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory)
+}
diff --git a/cc/cmake_snapshot_test.go b/cc/cmake_snapshot_test.go
new file mode 100644
index 0000000..8fca6c1
--- /dev/null
+++ b/cc/cmake_snapshot_test.go
@@ -0,0 +1,115 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"runtime"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func wasGenerated(t *testing.T, m *android.TestingModule, fileName string, ruleType string) {
+	t.Helper()
+	ruleName := "<nil>"
+	if rule := m.MaybeOutput(fileName).Rule; rule != nil {
+		ruleName = rule.String()
+	}
+	if !strings.HasSuffix(ruleName, ruleType) {
+		t.Errorf("Main Cmake file wasn't generated properly, expected rule %v, found %v", ruleType, ruleName)
+	}
+}
+
+func TestEmptyCmakeSnapshot(t *testing.T) {
+	t.Parallel()
+	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+		cc_cmake_snapshot {
+			name: "foo",
+			modules: [],
+			prebuilts: ["libc++"],
+			include_sources: true,
+		}`)
+
+	if runtime.GOOS != "linux" {
+		t.Skip("CMake snapshots are only supported on Linux")
+	}
+
+	snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64")
+
+	wasGenerated(t, &snapshotModule, "CMakeLists.txt", "rawFileCopy")
+	wasGenerated(t, &snapshotModule, "foo.zip", "")
+}
+
+func TestCmakeSnapshotWithBinary(t *testing.T) {
+	t.Parallel()
+	xtra := android.FixtureAddTextFile("some/module/Android.bp", `
+		cc_binary {
+			name: "foo_binary",
+			host_supported: true,
+			cmake_snapshot_supported: true,
+		}
+	`)
+	result := android.GroupFixturePreparers(PrepareForIntegrationTestWithCc, xtra).RunTestWithBp(t, `
+		cc_cmake_snapshot {
+			name: "foo",
+			modules: [
+				"foo_binary",
+			],
+			include_sources: true,
+		}`)
+
+	if runtime.GOOS != "linux" {
+		t.Skip("CMake snapshots are only supported on Linux")
+	}
+
+	snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64")
+
+	wasGenerated(t, &snapshotModule, "some/module/CMakeLists.txt", "rawFileCopy")
+}
+
+func TestCmakeSnapshotAsTestData(t *testing.T) {
+	t.Parallel()
+	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+		cc_test {
+			name: "foo_test",
+			gtest: false,
+			srcs: [
+				"foo_test.c",
+			],
+			data: [
+				":foo",
+			],
+			target: {
+				android: {enabled: false},
+			},
+		}
+
+		cc_cmake_snapshot {
+			name: "foo",
+			modules: [],
+			prebuilts: ["libc++"],
+			include_sources: true,
+		}`)
+
+	if runtime.GOOS != "linux" {
+		t.Skip("CMake snapshots are only supported on Linux")
+	}
+
+	snapshotModule := result.ModuleForTests("foo", "linux_glibc_x86_64")
+
+	wasGenerated(t, &snapshotModule, "CMakeLists.txt", "rawFileCopy")
+	wasGenerated(t, &snapshotModule, "foo.zip", "")
+}
diff --git a/cc/compiler.go b/cc/compiler.go
index 9a961cf..aee584d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -141,6 +141,19 @@
 		Flags []string
 	}
 
+	// Populated by aidl_interface CPP backend to let other modules (e.g. cc_cmake_snapshot)
+	// access actual source files and not generated cpp intermediary sources.
+	AidlInterface struct {
+		// list of aidl_interface sources
+		Sources []string `blueprint:"mutated"`
+
+		// AIDL backend language (e.g. "cpp", "ndk")
+		Lang string `blueprint:"mutated"`
+
+		// list of flags passed to AIDL generator
+		Flags []string `blueprint:"mutated"`
+	} `blueprint:"mutated"`
+
 	Renderscript struct {
 		// list of directories that will be added to the llvm-rs-cc include paths
 		Include_dirs []string
@@ -265,6 +278,10 @@
 	return []interface{}{&compiler.Properties, &compiler.Proto}
 }
 
+func (compiler *baseCompiler) baseCompilerProps() BaseCompilerProperties {
+	return compiler.Properties
+}
+
 func includeBuildDirectory(prop *bool) bool {
 	return proptools.BoolDefault(prop, true)
 }
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 2436f33..b3e6639 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -433,7 +433,7 @@
 			return
 		}
 		// Discard non-fuzz targets.
-		if ok := fuzz.IsValid(ccModule.FuzzModuleStruct()); !ok {
+		if ok := fuzz.IsValid(ctx, ccModule.FuzzModuleStruct()); !ok {
 			return
 		}
 
diff --git a/cc/linker.go b/cc/linker.go
index 56a68b2..1d0f205 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -287,6 +287,10 @@
 	return []interface{}{&linker.Properties, &linker.dynamicProperties}
 }
 
+func (linker *baseLinker) baseLinkerProps() BaseLinkerProperties {
+	return linker.Properties
+}
+
 func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...)
 	deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs...)
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index cd931f6..5b86c64 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -186,7 +186,7 @@
 		return
 	}
 
-	if shouldSkipLlndkMutator(m) {
+	if shouldSkipLlndkMutator(mctx, m) {
 		return
 	}
 
@@ -206,8 +206,8 @@
 }
 
 // Check for modules that mustn't be LLNDK
-func shouldSkipLlndkMutator(m *Module) bool {
-	if !m.Enabled() {
+func shouldSkipLlndkMutator(mctx android.BottomUpMutatorContext, m *Module) bool {
+	if !m.Enabled(mctx) {
 		return true
 	}
 	if !m.Device() {
diff --git a/cc/makevars.go b/cc/makevars.go
index 9251d6a..51bcbf0 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -279,7 +279,7 @@
 		sanitizerLibs := android.SortedStringValues(sanitizerVariables)
 		var sanitizerLibStems []string
 		ctx.VisitAllModules(func(m android.Module) {
-			if !m.Enabled() {
+			if !m.Enabled(ctx) {
 				return
 			}
 
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index 86166dc..5beeab1 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -40,7 +40,7 @@
 func (n *ndkAbiDumpSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var depPaths android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled() {
+		if !module.Enabled(ctx) {
 			return
 		}
 
@@ -78,7 +78,7 @@
 func (n *ndkAbiDiffSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var depPaths android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		if m, ok := module.(android.Module); ok && !m.Enabled() {
+		if m, ok := module.(android.Module); ok && !m.Enabled(ctx) {
 			return
 		}
 
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 25231fd..f326068 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -148,7 +148,7 @@
 }
 
 func (this *stubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
-	if !ctx.Module().Enabled() {
+	if !ctx.Module().Enabled(ctx) {
 		return nil
 	}
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index e815172..3c48f68 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -150,7 +150,7 @@
 	var installPaths android.Paths
 	var licensePaths android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		if m, ok := module.(android.Module); ok && !m.Enabled() {
+		if m, ok := module.(android.Module); ok && !m.Enabled(ctx) {
 			return
 		}
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 0cb54e5..1a94729 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1385,7 +1385,7 @@
 // Add the dependency to the runtime library for each of the sanitizer variants
 func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
 	if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
-		if !c.Enabled() {
+		if !c.Enabled(mctx) {
 			return
 		}
 		var sanitizers []string
diff --git a/cc/sdk.go b/cc/sdk.go
index ce0fdc2..4925ce1 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -49,15 +49,18 @@
 			modules[1].(*Module).Properties.IsSdkVariant = true
 
 			if ctx.Config().UnbundledBuildApps() {
-				// For an unbundled apps build, hide the platform variant from Make.
+				// For an unbundled apps build, hide the platform variant from Make
+				// so that other Make modules don't link against it, but against the
+				// SDK variant.
 				modules[0].(*Module).Properties.HideFromMake = true
-				modules[0].(*Module).Properties.PreventInstall = true
 			} else {
 				// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
 				// exposed to Make.
 				modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
-				modules[1].(*Module).Properties.PreventInstall = true
 			}
+			// SDK variant never gets installed because the variant is to be embedded in
+			// APKs, not to be installed to the platform.
+			modules[1].(*Module).Properties.PreventInstall = true
 			ctx.AliasVariation("")
 		} else {
 			if isCcModule {
diff --git a/cc/testing.go b/cc/testing.go
index 4b4e866..c3a33cb 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -35,6 +35,7 @@
 
 	ctx.RegisterModuleType("prebuilt_build_tool", android.NewPrebuiltBuildTool)
 	ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
+	ctx.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory)
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("cc_genrule", GenRuleFactory)
 	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
diff --git a/cc/tidy.go b/cc/tidy.go
index 76ac7d5..ec1e8a2 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -220,7 +220,7 @@
 
 	// (1) Collect all obj/tidy files into OS-specific groups.
 	ctx.VisitAllModuleVariants(module, func(variant android.Module) {
-		if ctx.Config().KatiEnabled() && android.ShouldSkipAndroidMkProcessing(variant) {
+		if ctx.Config().KatiEnabled() && android.ShouldSkipAndroidMkProcessing(ctx, variant) {
 			return
 		}
 		if m, ok := variant.(*Module); ok {
diff --git a/cc/vndk.go b/cc/vndk.go
index 50e6d4b..548992d 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -322,8 +322,8 @@
 }
 
 // Check for modules that mustn't be VNDK
-func shouldSkipVndkMutator(m *Module) bool {
-	if !m.Enabled() {
+func shouldSkipVndkMutator(ctx android.ConfigAndErrorContext, m *Module) bool {
+	if !m.Enabled(ctx) {
 		return true
 	}
 	if !m.Device() {
@@ -338,7 +338,7 @@
 }
 
 func IsForVndkApex(mctx android.BottomUpMutatorContext, m *Module) bool {
-	if shouldSkipVndkMutator(m) {
+	if shouldSkipVndkMutator(mctx, m) {
 		return false
 	}
 
@@ -369,7 +369,7 @@
 		return
 	}
 
-	if shouldSkipVndkMutator(m) {
+	if shouldSkipVndkMutator(mctx, m) {
 		return
 	}
 
@@ -548,6 +548,7 @@
 func (txt *vndkLibrariesTxt) OutputFiles(tag string) (android.Paths, error) {
 	return android.Paths{txt.outputFile}, nil
 }
+
 func getVndkFileName(m *Module) (string, error) {
 	if library, ok := m.linker.(*libraryDecorator); ok {
 		return library.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
diff --git a/cmd/release_config/build_flag/main.go b/cmd/release_config/build_flag/main.go
index 56c49d8..cc2b57a 100644
--- a/cmd/release_config/build_flag/main.go
+++ b/cmd/release_config/build_flag/main.go
@@ -266,7 +266,7 @@
 		return fmt.Errorf("Unknown build flag %s", name)
 	}
 	if valueDir == "" {
-		mapDir, err := GetMapDir(*flagArtifact.Traces[len(flagArtifact.Traces)-1].Source)
+		mapDir, err := configs.GetFlagValueDirectory(release, flagArtifact)
 		if err != nil {
 			return err
 		}
diff --git a/cmd/release_config/crunch_flags/main.go b/cmd/release_config/crunch_flags/main.go
index 4d763c8..cd39ffd 100644
--- a/cmd/release_config/crunch_flags/main.go
+++ b/cmd/release_config/crunch_flags/main.go
@@ -137,7 +137,9 @@
 		workflow := rc_proto.Workflow(rc_proto.Workflow_PREBUILT)
 		switch {
 		case declName == "RELEASE_ACONFIG_VALUE_SETS":
-			rootAconfigModule = declValue[1 : len(declValue)-1]
+			if strings.HasPrefix(declValue, "\"") {
+				rootAconfigModule = declValue[1 : len(declValue)-1]
+			}
 			continue
 		case strings.HasPrefix(declValue, "\""):
 			// String values mean that the flag workflow is (most likely) either MANUAL or PREBUILT.
diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go
index 101dbe3..5432806 100644
--- a/cmd/release_config/release_config/main.go
+++ b/cmd/release_config/release_config/main.go
@@ -83,20 +83,24 @@
 		// We were told to guard operation and either we have no build flag, or it is False.
 		// Write an empty file so that release_config.mk will use the old process.
 		os.WriteFile(makefilePath, []byte{}, 0644)
-	} else if allMake {
+		return
+	}
+	// Write the makefile where release_config.mk is going to look for it.
+	err = configs.WriteMakefile(makefilePath, targetRelease)
+	if err != nil {
+		panic(err)
+	}
+	if allMake {
 		// Write one makefile per release config, using the canonical release name.
 		for k, _ := range configs.ReleaseConfigs {
-			makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, k))
-			err = configs.WriteMakefile(makefilePath, k)
-			if err != nil {
-				panic(err)
+			if k != targetRelease {
+				makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, k))
+				err = configs.WriteMakefile(makefilePath, k)
+				if err != nil {
+					panic(err)
+				}
 			}
 		}
-	} else {
-		err = configs.WriteMakefile(makefilePath, targetRelease)
-		if err != nil {
-			panic(err)
-		}
 	}
 	if json {
 		err = configs.WriteArtifact(outputDir, product, "json")
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
index f25cc6e..8204822 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -29,7 +29,7 @@
 
 // One directory's contribution to the a release config.
 type ReleaseConfigContribution struct {
-	// Paths to files providing this config.
+	// Path of the file providing this config contribution.
 	path string
 
 	// The index of the config directory where this release config
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index 3429400..2487f2e 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -131,6 +131,32 @@
 	return m
 }
 
+// Find the top of the release config contribution directory.
+// Returns the parent of the flag_declarations and flag_values directories.
+func (configs *ReleaseConfigs) GetDirIndex(path string) (int, error) {
+	for p := path; p != "."; p = filepath.Dir(p) {
+		if idx, ok := configs.configDirIndexes[p]; ok {
+			return idx, nil
+		}
+	}
+	return -1, fmt.Errorf("Could not determine release config directory from %s", path)
+}
+
+// Determine the default directory for writing a flag value.
+//
+// Returns the path of the highest-Indexed one of:
+//   - Where the flag is declared
+//   - Where the release config is first declared
+//   - The last place the value is being written.
+func (configs *ReleaseConfigs) GetFlagValueDirectory(config *ReleaseConfig, flag *FlagArtifact) (string, error) {
+	current, err := configs.GetDirIndex(*flag.Traces[len(flag.Traces)-1].Source)
+	if err != nil {
+		return "", err
+	}
+	index := max(flag.DeclarationIndex, config.DeclarationIndex, current)
+	return configs.configDirs[index], nil
+}
+
 func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex int) error {
 	if _, err := os.Stat(path); err != nil {
 		return fmt.Errorf("%s does not exist\n", path)
@@ -265,6 +291,9 @@
 		allReleaseNames = append(allReleaseNames, v.Name)
 		allReleaseNames = append(allReleaseNames, v.OtherNames...)
 	}
+	slices.SortFunc(allReleaseNames, func(a, b string) int {
+		return cmp.Compare(a, b)
+	})
 	config, err := configs.GetReleaseConfig(targetRelease)
 	if err != nil {
 		return err
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 025ba27..6ab3b88 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -158,9 +158,6 @@
 	additionalDependencies *android.Paths
 
 	makeClass string
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
 }
 
 type Defaults struct {
@@ -421,7 +418,6 @@
 	for _, ip := range installs {
 		ip.addInstallRules(ctx)
 	}
-	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
 }
 
 type installProperties struct {
@@ -486,7 +482,6 @@
 				if p.additionalDependencies != nil {
 					entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", p.additionalDependencies.Strings()...)
 				}
-				android.SetAconfigFileMkEntries(p.AndroidModuleBase(), entries, p.mergedAconfigFiles)
 			},
 		},
 	}}
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 47fd8f4..306d65e 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -449,10 +449,10 @@
 	}
 }
 
-func IsValid(fuzzModule FuzzModule) bool {
+func IsValid(ctx android.ConfigAndErrorContext, fuzzModule FuzzModule) bool {
 	// Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
 	// fuzz targets we're going to package anyway.
-	if !fuzzModule.Enabled() || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() {
+	if !fuzzModule.Enabled(ctx) || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() {
 		return false
 	}
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 4ff82e6..dd980cb 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -180,9 +180,6 @@
 
 	subName string
 	subDir  string
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
 }
 
 type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
@@ -299,7 +296,7 @@
 				case android.HostToolProvider:
 					// A HostToolProvider provides the path to a tool, which will be copied
 					// into the sandbox.
-					if !t.(android.Module).Enabled() {
+					if !t.(android.Module).Enabled(ctx) {
 						if ctx.Config().AllowMissingDependencies() {
 							ctx.AddMissingDependencies([]string{tool})
 						} else {
@@ -588,24 +585,6 @@
 		})
 		g.outputDeps = android.Paths{phonyFile}
 	}
-	android.CollectDependencyAconfigFiles(ctx, &g.mergedAconfigFiles)
-}
-
-func (g *Module) AndroidMkEntries() []android.AndroidMkEntries {
-	ret := android.AndroidMkEntries{
-		OutputFile: android.OptionalPathForPath(g.outputFiles[0]),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				android.SetAconfigFileMkEntries(g.AndroidModuleBase(), entries, g.mergedAconfigFiles)
-			},
-		},
-	}
-
-	return []android.AndroidMkEntries{ret}
-}
-
-func (g *Module) AndroidModuleBase() *android.ModuleBase {
-	return &g.ModuleBase
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
diff --git a/java/aar.go b/java/aar.go
index 47c64bf..07392f6 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -1014,9 +1014,6 @@
 
 	usesLibrary
 	classLoaderContexts dexpreopt.ClassLoaderContextMap
-
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
 }
 
 var _ android.OutputFileProducer = (*AARImport)(nil)
@@ -1386,7 +1383,6 @@
 	android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{
 		JniPackages: a.jniPackages,
 	})
-	android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles)
 }
 
 func (a *AARImport) HeaderJars() android.Paths {
diff --git a/java/androidmk.go b/java/androidmk.go
index 9cd0baf..4316074 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"io"
-	"strings"
 
 	"android/soong/android"
 
@@ -124,7 +123,6 @@
 					if library.dexpreopter.configPath != nil {
 						entries.SetPath("LOCAL_SOONG_DEXPREOPT_CONFIG", library.dexpreopter.configPath)
 					}
-					android.SetAconfigFileMkEntries(&library.ModuleBase, entries, library.mergedAconfigFiles)
 				},
 			},
 		})
@@ -298,7 +296,6 @@
 					if len(binary.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled)
 					}
-					android.SetAconfigFileMkEntries(&binary.ModuleBase, entries, binary.mergedAconfigFiles)
 				},
 			},
 			ExtraFooters: []android.AndroidMkExtraFootersFunc{
@@ -415,23 +412,6 @@
 				if app.embeddedJniLibs {
 					jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String())
 					entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String())
-				} else {
-					for _, jniLib := range app.jniLibs {
-						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
-						var partitionTag string
-
-						// Mimic the creation of partition_tag in build/make,
-						// which defaults to an empty string when the partition is system.
-						// Otherwise, capitalize with a leading _
-						if jniLib.partition == "system" {
-							partitionTag = ""
-						} else {
-							split := strings.Split(jniLib.partition, "/")
-							partitionTag = "_" + strings.ToUpper(split[len(split)-1])
-						}
-						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(),
-							jniLib.name+":"+partitionTag)
-					}
 				}
 
 				if len(app.jniCoverageOutputs) > 0 {
@@ -450,10 +430,6 @@
 
 				entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports)
 
-				if app.Name() != "framework-res" {
-					android.SetAconfigFileMkEntries(&app.ModuleBase, entries, app.mergedAconfigFiles)
-				}
-
 				entries.AddStrings("LOCAL_SOONG_LOGTAGS_FILES", app.logtagsSrcs.Strings()...)
 			},
 		},
@@ -531,7 +507,6 @@
 		entries.SetPath("LOCAL_FULL_MANIFEST_FILE", a.mergedManifestFile)
 		entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.combinedExportedProguardFlagsFile)
 		entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
-		android.SetAconfigFileMkEntries(&a.ModuleBase, entries, a.mergedAconfigFiles)
 	})
 
 	return entriesList
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 1232cd1..875e06f 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -19,9 +19,6 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/cc"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func TestRequired(t *testing.T) {
@@ -161,8 +158,8 @@
 		moduleName string
 		expected   []string
 	}{
-		{"foo-shared_library", []string{"foo-shared_library.xml"}},
-		{"foo-no_shared_library", nil},
+		{"foo-shared_library", []string{"foo-shared_library.impl", "foo-shared_library.xml"}},
+		{"foo-no_shared_library", []string{"foo-no_shared_library.impl"}},
 	}
 	for _, tc := range testCases {
 		mod := result.ModuleForTests(tc.moduleName, "android_common").Module()
@@ -255,149 +252,3 @@
 		android.AssertDeepEquals(t, "overrides property", expected.overrides, actual)
 	}
 }
-
-func TestJniPartition(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libjni_system",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-		}
-
-		cc_library {
-			name: "libjni_system_ext",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			system_ext_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_odm",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			device_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_product",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			product_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_vendor",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			soc_specific: true,
-		}
-
-		android_app {
-			name: "test_app_system_jni_system",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system"],
-		}
-
-		android_app {
-			name: "test_app_system_jni_system_ext",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system_ext"],
-		}
-
-		android_app {
-			name: "test_app_system_ext_jni_system",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system"],
-			system_ext_specific: true
-		}
-
-		android_app {
-			name: "test_app_system_ext_jni_system_ext",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_system_ext"],
-			system_ext_specific: true
-		}
-
-		android_app {
-			name: "test_app_product_jni_product",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_product"],
-			product_specific: true
-		}
-
-		android_app {
-			name: "test_app_vendor_jni_odm",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_odm"],
-			soc_specific: true
-		}
-
-		android_app {
-			name: "test_app_odm_jni_vendor",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_vendor"],
-			device_specific: true
-		}
-		android_app {
-			name: "test_app_system_jni_multiple",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system", "libjni_system_ext"],
-		}
-		android_app {
-			name: "test_app_vendor_jni_multiple",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_odm", "libjni_vendor"],
-			soc_specific: true
-		}
-		`
-	arch := "arm64"
-	ctx := android.GroupFixturePreparers(
-		PrepareForTestWithJavaDefaultModules,
-		cc.PrepareForTestWithCcDefaultModules,
-		android.PrepareForTestWithAndroidMk,
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.TestProductVariables.DeviceArch = proptools.StringPtr(arch)
-		}),
-	).
-		RunTestWithBp(t, bp)
-	testCases := []struct {
-		name           string
-		partitionNames []string
-		partitionTags  []string
-	}{
-		{"test_app_system_jni_system", []string{"libjni_system"}, []string{""}},
-		{"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
-		{"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}},
-		{"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
-		{"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}},
-		{"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}},
-		{"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}},
-		{"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}},
-		{"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}},
-	}
-
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			mod := ctx.ModuleForTests(test.name, "android_common").Module()
-			entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
-			for i := range test.partitionNames {
-				actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i]
-				expected := test.partitionNames[i] + ":" + test.partitionTags[i]
-				android.AssertStringEquals(t, "Expected and actual differ", expected, actual)
-			}
-		})
-	}
-}
diff --git a/java/app.go b/java/app.go
index 50d1a2f..254fbf4 100644
--- a/java/app.go
+++ b/java/app.go
@@ -90,20 +90,17 @@
 	Stl *string `android:"arch_variant"`
 
 	// Store native libraries uncompressed in the APK and set the android:extractNativeLibs="false" manifest
-	// flag so that they are used from inside the APK at runtime.  Defaults to true for android_test modules unless
-	// sdk_version or min_sdk_version is set to a version that doesn't support it (<23), defaults to true for
-	// android_app modules that are embedded to APEXes, defaults to false for other module types where the native
-	// libraries are generally preinstalled outside the APK.
+	// flag so that they are used from inside the APK at runtime. This property is respected only for
+	// APKs built using android_test or android_test_helper_app. For other APKs, this property is ignored
+	// and native libraries are always embedded compressed.
 	Use_embedded_native_libs *bool
 
 	// Store dex files uncompressed in the APK and set the android:useEmbeddedDex="true" manifest attribute so that
 	// they are used from inside the APK at runtime.
 	Use_embedded_dex *bool
 
-	// Forces native libraries to always be packaged into the APK,
-	// Use_embedded_native_libs still selects whether they are stored uncompressed and aligned or compressed.
-	// True for android_test* modules.
-	AlwaysPackageNativeLibs bool `blueprint:"mutated"`
+	// Allows compressing of embedded native libs. Only for android_test and android_test_helper_app.
+	AllowCompressingNativeLibs bool `blueprint:"mutated"`
 
 	// If set, find and merge all NOTICE files that this module and its dependencies have and store
 	// it in the APK as an asset.
@@ -403,14 +400,20 @@
 // Returns true if the native libraries should be stored in the APK uncompressed and the
 // extractNativeLibs application flag should be set to false in the manifest.
 func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool {
+	var useEmbedded bool
+	if a.appProperties.AllowCompressingNativeLibs {
+		useEmbedded = BoolDefault(a.appProperties.Use_embedded_native_libs, true)
+	} else {
+		useEmbedded = true // always uncompress for non-test apps
+	}
+
 	minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.MinSdkVersion(ctx), err)
 	}
+	supported := minSdkVersion.FinalOrFutureInt() >= 23
 
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
-	return (minSdkVersion.FinalOrFutureInt() >= 23 && Bool(a.appProperties.Use_embedded_native_libs)) ||
-		!apexInfo.IsForPlatform()
+	return useEmbedded && supported
 }
 
 // Returns whether this module should have the dex file stored uncompressed in the APK.
@@ -433,9 +436,23 @@
 }
 
 func (a *AndroidApp) shouldEmbedJnis(ctx android.BaseModuleContext) bool {
+	// Always!
+	return true
+}
+
+func (a *AndroidApp) shouldCollectRecursiveNativeDeps(ctx android.ModuleContext) bool {
+	// JNI libs are always embedded, but whether to embed their transitive dependencies as well
+	// or not is determined here. For most of the apps built here (using the platform build
+	// system), we don't need to collect the transitive deps because they will anyway be
+	// available in the partition image where the app will be installed to.
+	//
+	// Collecting transitive dependencies is required only for unbundled apps.
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
-	return ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) ||
-		!apexInfo.IsForPlatform() || a.appProperties.AlwaysPackageNativeLibs
+	apkInApex := !apexInfo.IsForPlatform()
+	testApp := a.appProperties.AllowCompressingNativeLibs
+	unbundledApp := ctx.Config().UnbundledBuild() || apkInApex || testApp
+
+	return a.shouldEmbedJnis(ctx) && unbundledApp
 }
 
 func generateAaptRenamePackageFlags(packageName string, renameResourcesPackage bool) []string {
@@ -829,7 +846,7 @@
 
 	dexJarFile, packageResources := a.dexBuildActions(ctx)
 
-	jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
+	jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldCollectRecursiveNativeDeps(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
 	jniJarFile := a.jniBuildActions(jniLibs, prebuiltJniPackages, ctx)
 
 	if ctx.Failed() {
@@ -1400,8 +1417,7 @@
 	module.Module.properties.Instrument = true
 	module.Module.properties.Supports_static_instrumentation = true
 	module.Module.properties.Installable = proptools.BoolPtr(true)
-	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
-	module.appProperties.AlwaysPackageNativeLibs = true
+	module.appProperties.AllowCompressingNativeLibs = true
 	module.Module.dexpreopter.isTest = true
 	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
 
@@ -1456,8 +1472,7 @@
 	module.Module.dexProperties.Optimize.EnabledByDefault = true
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
-	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
-	module.appProperties.AlwaysPackageNativeLibs = true
+	module.appProperties.AllowCompressingNativeLibs = true
 	module.Module.dexpreopter.isTest = true
 	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
 
diff --git a/java/app_import.go b/java/app_import.go
index bb07c42..dc8470d 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -87,9 +87,6 @@
 	hideApexVariantFromMake bool
 
 	provenanceMetaDataFile android.OutputPath
-
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
 }
 
 type AndroidAppImportProperties struct {
@@ -416,7 +413,6 @@
 		artifactPath := android.PathForModuleSrc(ctx, *a.properties.Apk)
 		a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath)
 	}
-	android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles)
 
 	providePrebuiltInfo(ctx,
 		prebuiltInfoProps{
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 5de50e7..496fc13 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -509,7 +509,7 @@
 
 			variant := ctx.ModuleForTests("foo", "android_common")
 			if test.expected == "" {
-				if variant.Module().Enabled() {
+				if variant.Module().Enabled(android.PanickingConfigAndErrorContext(ctx)) {
 					t.Error("module should have been disabled, but wasn't")
 				}
 				rule := variant.MaybeRule("genProvenanceMetaData")
@@ -586,7 +586,7 @@
 
 			variant := ctx.ModuleForTests("foo", "android_common")
 			if test.expected == "" {
-				if variant.Module().Enabled() {
+				if variant.Module().Enabled(android.PanickingConfigAndErrorContext(ctx)) {
 					t.Error("module should have been disabled, but wasn't")
 				}
 				rule := variant.MaybeRule("genProvenanceMetaData")
@@ -629,7 +629,7 @@
 	if !a.prebuilt.UsePrebuilt() {
 		t.Errorf("prebuilt foo module is not active")
 	}
-	if !a.Enabled() {
+	if !a.Enabled(android.PanickingConfigAndErrorContext(ctx)) {
 		t.Errorf("prebuilt foo module is disabled")
 	}
 }
diff --git a/java/app_test.go b/java/app_test.go
index a7c48a1..d6ba0f1 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2013,8 +2013,8 @@
 		packaged   bool
 		compressed bool
 	}{
-		{"app", false, false},
-		{"app_noembed", false, false},
+		{"app", true, false},
+		{"app_noembed", true, false},
 		{"app_embed", true, false},
 		{"test", true, false},
 		{"test_noembed", true, true},
@@ -2043,6 +2043,44 @@
 	}
 }
 
+func TestJNITranstiveDepsInstallation(t *testing.T) {
+	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+		android_app {
+			name: "app",
+			jni_libs: ["libjni"],
+			platform_apis: true,
+		}
+
+		cc_library {
+			name: "libjni",
+			shared_libs: ["libplatform"],
+			system_shared_libs: [],
+			stl: "none",
+			required: ["librequired"],
+		}
+
+		cc_library {
+			name: "libplatform",
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "librequired",
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		`)
+
+	app := ctx.ModuleForTests("app", "android_common")
+	jniLibZip := app.Output("jnilibs.zip")
+	android.AssertPathsEndWith(t, "embedd jni lib mismatch", []string{"libjni.so"}, jniLibZip.Implicits)
+
+	install := app.Rule("Cp")
+	android.AssertPathsEndWith(t, "install dep mismatch", []string{"libplatform.so", "librequired.so"}, install.OrderOnly)
+}
+
 func TestJNISDK(t *testing.T) {
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
@@ -3319,8 +3357,7 @@
 	// These also include explicit `uses_libs`/`optional_uses_libs` entries, as they may be
 	// propagated from dependencies.
 	actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-	expectManifestFixerArgs := `--extract-native-libs=true ` +
-		`--uses-library foo ` +
+	expectManifestFixerArgs := `--uses-library foo ` +
 		`--uses-library com.non.sdk.lib ` +
 		`--uses-library qux ` +
 		`--uses-library quuz ` +
@@ -4110,7 +4147,7 @@
 		},
 		{
 			name:       "aary-no-use-embedded",
-			hasPackage: false,
+			hasPackage: true,
 		},
 	}
 
diff --git a/java/base.go b/java/base.go
index 938ac5e..0c28671 100644
--- a/java/base.go
+++ b/java/base.go
@@ -537,9 +537,6 @@
 	// or the module should override Stem().
 	stem string
 
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
-
 	// Values that will be set in the JarJarProvider data for jarjar repackaging,
 	// and merged with our dependencies' rules.
 	jarjarRenameRules map[string]string
@@ -1682,7 +1679,11 @@
 			j.dexJarFile = makeDexJarPathFromPath(dexOutputFile)
 
 			// Dexpreopting
-			j.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), dexOutputFile)
+			libName := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
+			if j.SdkLibraryName() != nil && strings.HasSuffix(ctx.ModuleName(), ".impl") {
+				libName = strings.TrimSuffix(libName, ".impl")
+			}
+			j.dexpreopt(ctx, libName, dexOutputFile)
 
 			outputFile = dexOutputFile
 		} else {
@@ -1730,8 +1731,6 @@
 
 	ctx.CheckbuildFile(outputFile)
 
-	android.CollectDependencyAconfigFiles(ctx, &j.mergedAconfigFiles)
-
 	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
 		HeaderJars:                          android.PathsIfNonNil(j.headerJarFile),
 		RepackagedHeaderJars:                android.PathsIfNonNil(j.repackagedHeaderJarFile),
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 5d40ec3..6223ded 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -21,8 +21,8 @@
 // isActiveModule returns true if the given module should be considered for boot
 // jars, i.e. if it's enabled and the preferred one in case of source and
 // prebuilt alternatives.
-func isActiveModule(module android.Module) bool {
-	if !module.Enabled() {
+func isActiveModule(ctx android.ConfigAndErrorContext, module android.Module) bool {
+	if !module.Enabled(ctx) {
 		return false
 	}
 	return android.IsModulePreferred(module)
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index c7dc3af..77ddf5c 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -127,7 +127,10 @@
 // added by addDependencyOntoApexModulePair.
 func gatherApexModulePairDepsWithTag(ctx android.BaseModuleContext, tag blueprint.DependencyTag) []android.Module {
 	var modules []android.Module
-	ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
+	isActiveModulePred := func(module android.Module) bool {
+		return isActiveModule(ctx, module)
+	}
+	ctx.VisitDirectDepsIf(isActiveModulePred, func(module android.Module) {
 		t := ctx.OtherModuleDependencyTag(module)
 		if t == tag {
 			modules = append(modules, module)
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index cc3da76..82a34ca 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -474,7 +474,7 @@
 	// Only perform a consistency check if this module is the active module. That will prevent an
 	// unused prebuilt that was created without instrumentation from breaking an instrumentation
 	// build.
-	if isActiveModule(ctx.Module()) {
+	if isActiveModule(ctx, ctx.Module()) {
 		b.bootclasspathFragmentPropertyCheck(ctx)
 	}
 
@@ -519,7 +519,7 @@
 // empty string if this module should not provide a boot image profile.
 func (b *BootclasspathFragmentModule) getProfileProviderApex(ctx android.BaseModuleContext) string {
 	// Only use the profile from the module that is preferred.
-	if !isActiveModule(ctx.Module()) {
+	if !isActiveModule(ctx, ctx.Module()) {
 		return ""
 	}
 
@@ -590,7 +590,7 @@
 		// So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS.
 		// TODO(b/202896428): Add better way to handle this.
 		_, unknown = android.RemoveFromList("android.car-module", unknown)
-		if isActiveModule(ctx.Module()) && len(unknown) > 0 {
+		if isActiveModule(ctx, ctx.Module()) && len(unknown) > 0 {
 			ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
 		}
 	}
diff --git a/java/code_metadata_test.go b/java/code_metadata_test.go
index 0ef348a..99b1f52 100644
--- a/java/code_metadata_test.go
+++ b/java/code_metadata_test.go
@@ -7,6 +7,7 @@
 	"android/soong/android"
 	soongTesting "android/soong/testing"
 	"android/soong/testing/code_metadata_internal_proto"
+
 	"google.golang.org/protobuf/proto"
 )
 
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 25e95db..1acac1b 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -196,8 +196,10 @@
 	}
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	psi := android.PrebuiltSelectionInfoMap{}
-	ctx.VisitDirectDepsWithTag(android.PrebuiltDepTag, func(am android.Module) {
-		psi, _ = android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider)
+	ctx.VisitDirectDeps(func(am android.Module) {
+		if prebuiltSelectionInfo, ok := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); ok {
+			psi = prebuiltSelectionInfo
+		}
 	})
 	// Find the apex variant for this module
 	_, apexVariantsWithoutTestApexes, _ := android.ListSetDifference(apexInfo.InApexVariants, apexInfo.TestApexes)
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f7e3cb9..7229ca0 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -562,7 +562,7 @@
 	return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} {
 		fragments := make(map[string]android.Module)
 		ctx.WalkDeps(func(child, parent android.Module) bool {
-			if !isActiveModule(child) {
+			if !isActiveModule(ctx, child) {
 				return false
 			}
 			tag := ctx.OtherModuleDependencyTag(child)
@@ -1125,7 +1125,7 @@
 	image.unstrippedInstalls = unstrippedInstalls
 
 	// Only set the licenseMetadataFile from the active module.
-	if isActiveModule(ctx.Module()) {
+	if isActiveModule(ctx, ctx.Module()) {
 		image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
 	}
 
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 730be14..ca81343 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -106,9 +106,6 @@
 	everythingArtifacts stubsArtifacts
 	exportableArtifacts stubsArtifacts
 
-	// Single aconfig "cache file" merged from this module and all dependencies.
-	mergedAconfigFiles map[string]android.Paths
-
 	exportableApiFile        android.WritablePath
 	exportableRemovedApiFile android.WritablePath
 }
@@ -1342,7 +1339,6 @@
 
 		rule.Build("nullabilityWarningsCheck", "nullability warnings check")
 	}
-	android.CollectDependencyAconfigFiles(ctx, &d.mergedAconfigFiles)
 }
 
 func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) {
diff --git a/java/fuzz.go b/java/fuzz.go
index fb31ce7..d37c558 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -179,7 +179,7 @@
 			javaFuzzModule.ApexModuleBase,
 		}
 
-		if ok := fuzz.IsValid(fuzzModuleValidator); !ok {
+		if ok := fuzz.IsValid(ctx, fuzzModuleValidator); !ok {
 			return
 		}
 
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index ae587ea..cab5402 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -1428,7 +1428,7 @@
 		// should not contribute to anything. So, rather than have a missing dex jar cause a Soong
 		// failure defer the error reporting to Ninja. Unless the prebuilt build target is explicitly
 		// built Ninja should never use the dex jar file.
-		if !isActiveModule(module) {
+		if !isActiveModule(ctx, module) {
 			return true
 		}
 
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 8cb78cd..7d21b7a 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -150,6 +150,10 @@
 	// Strip a prebuilt_ prefix so that this can match a prebuilt module that has not been renamed.
 	name = android.RemoveOptionalPrebuiltPrefix(name)
 
+	// Strip the ".impl" suffix, so that the implementation library of the java_sdk_library is
+	// treated identical to the top level java_sdk_library.
+	name = strings.TrimSuffix(name, ".impl")
+
 	// Ignore any module that is not listed in the boot image configuration.
 	index := configuredBootJars.IndexOfJar(name)
 	if index == -1 {
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index c1fee21..330013e 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -303,7 +303,7 @@
 	`)
 
 	checkDexEncoded := func(t *testing.T, name, unencodedDexJar, encodedDexJar string) {
-		moduleForTests := result.ModuleForTests(name, "android_common")
+		moduleForTests := result.ModuleForTests(name+".impl", "android_common")
 
 		encodeDexRule := moduleForTests.Rule("hiddenAPIEncodeDex")
 		actualUnencodedDexJar := encodeDexRule.Input
@@ -319,18 +319,8 @@
 
 	// The java_library embedded with the java_sdk_library must be dex encoded.
 	t.Run("foo", func(t *testing.T) {
-		expectedUnencodedDexJar := "out/soong/.intermediates/foo/android_common/aligned/foo.jar"
-		expectedEncodedDexJar := "out/soong/.intermediates/foo/android_common/hiddenapi/foo.jar"
+		expectedUnencodedDexJar := "out/soong/.intermediates/foo.impl/android_common/aligned/foo.jar"
+		expectedEncodedDexJar := "out/soong/.intermediates/foo.impl/android_common/hiddenapi/foo.jar"
 		checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar)
 	})
-
-	// The dex jar of the child implementation java_library of the java_sdk_library is not currently
-	// dex encoded.
-	t.Run("foo.impl", func(t *testing.T) {
-		fooImpl := result.ModuleForTests("foo.impl", "android_common")
-		encodeDexRule := fooImpl.MaybeRule("hiddenAPIEncodeDex")
-		if encodeDexRule.Rule != nil {
-			t.Errorf("foo.impl is not expected to be encoded")
-		}
-	})
 }
diff --git a/java/jacoco.go b/java/jacoco.go
index a820b38..696a0cc 100644
--- a/java/jacoco.go
+++ b/java/jacoco.go
@@ -53,7 +53,7 @@
 	}
 
 	j, ok := ctx.Module().(instrumentable)
-	if !ctx.Module().Enabled() || !ok {
+	if !ctx.Module().Enabled(ctx) || !ok {
 		return
 	}
 
diff --git a/java/java.go b/java/java.go
index 30581f2..fc7e5c5 100644
--- a/java/java.go
+++ b/java/java.go
@@ -368,6 +368,17 @@
 	static bool
 }
 
+var _ android.SkipToTransitiveDepsTag = (*dependencyTag)(nil)
+
+func (depTag dependencyTag) SkipToTransitiveDeps() bool {
+	// jni_libs are not installed because they are always embedded into the app. However,
+	// transitive deps of jni_libs themselves should be installed along with the app.
+	if IsJniDepTag(depTag) {
+		return true
+	}
+	return false
+}
+
 // installDependencyTag is a dependency tag that is annotated to cause the installed files of the
 // dependency to be installed when the parent module is installed.
 type installDependencyTag struct {
@@ -674,6 +685,10 @@
 
 var _ android.ApexModule = (*Library)(nil)
 
+func (j *Library) CheckDepsMinSdkVersion(ctx android.ModuleContext) {
+	CheckMinSdkVersion(ctx, j)
+}
+
 // Provides access to the list of permitted packages from apex boot jars.
 type PermittedPackagesForUpdatableBootJars interface {
 	PermittedPackagesForUpdatableBootJars() []string
@@ -902,6 +917,12 @@
 	j.minSdkVersion = j.MinSdkVersion(ctx)
 	j.maxSdkVersion = j.MaxSdkVersion(ctx)
 
+	// Check min_sdk_version of the transitive dependencies if this module is created from
+	// java_sdk_library.
+	if j.deviceProperties.Min_sdk_version != nil && j.SdkLibraryName() != nil {
+		j.CheckDepsMinSdkVersion(ctx)
+	}
+
 	// SdkLibrary.GenerateAndroidBuildActions(ctx) sets the stubsLinkType to Unknown.
 	// If the stubsLinkType has already been set to Unknown, the stubsLinkType should
 	// not be overridden.
@@ -932,8 +953,12 @@
 	j.checkSdkVersions(ctx)
 	j.checkHeadersOnly(ctx)
 	if ctx.Device() {
+		libName := j.Name()
+		if j.SdkLibraryName() != nil && strings.HasSuffix(libName, ".impl") {
+			libName = proptools.String(j.SdkLibraryName())
+		}
 		j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
-			ctx, j.Name(), android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
+			ctx, libName, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
 		j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
 		setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
 		j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
@@ -944,8 +969,24 @@
 	}
 	j.compile(ctx, nil, nil, nil)
 
-	exclusivelyForApex := !apexInfo.IsForPlatform()
-	if (Bool(j.properties.Installable) || ctx.Host()) && !exclusivelyForApex {
+	// If this module is an impl library created from java_sdk_library,
+	// install the files under the java_sdk_library module outdir instead of this module outdir.
+	if j.SdkLibraryName() != nil && strings.HasSuffix(j.Name(), ".impl") {
+		j.setInstallRules(ctx, proptools.String(j.SdkLibraryName()))
+	} else {
+		j.setInstallRules(ctx, ctx.ModuleName())
+	}
+
+	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+		TestOnly:       Bool(j.sourceProperties.Test_only),
+		TopLevelTarget: j.sourceProperties.Top_level_test_target,
+	})
+}
+
+func (j *Library) setInstallRules(ctx android.ModuleContext, installModuleName string) {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+
+	if (Bool(j.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() {
 		var extraInstallDeps android.InstallPaths
 		if j.InstallMixin != nil {
 			extraInstallDeps = j.InstallMixin(ctx, j.outputFile)
@@ -962,22 +1003,27 @@
 			if !ctx.Host() {
 				archDir = ctx.DeviceConfig().DeviceArch()
 			}
-			installDir = android.PathForModuleInstall(ctx, ctx.ModuleName(), archDir)
+			installDir = android.PathForModuleInstall(ctx, installModuleName, archDir)
 		} else {
 			installDir = android.PathForModuleInstall(ctx, "framework")
 		}
 		j.installFile = ctx.InstallFile(installDir, j.Stem()+".jar", j.outputFile, extraInstallDeps...)
 	}
-
-	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
-		TestOnly:       Bool(j.sourceProperties.Test_only),
-		TopLevelTarget: j.sourceProperties.Top_level_test_target,
-	})
 }
 
 func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
 	j.usesLibrary.deps(ctx, false)
 	j.deps(ctx)
+
+	if j.SdkLibraryName() != nil && strings.HasSuffix(j.Name(), ".impl") {
+		if dexpreopt.IsDex2oatNeeded(ctx) {
+			dexpreopt.RegisterToolDeps(ctx)
+		}
+		prebuiltSdkLibExists := ctx.OtherModuleExists(android.PrebuiltNameFromSource(proptools.String(j.SdkLibraryName())))
+		if prebuiltSdkLibExists && ctx.OtherModuleExists("all_apex_contributions") {
+			ctx.AddDependency(ctx.Module(), android.AcDepTag, "all_apex_contributions")
+		}
+	}
 }
 
 const (
diff --git a/java/jdeps.go b/java/jdeps.go
index 91f7ce7..3400263 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -48,7 +48,7 @@
 	moduleInfos := make(map[string]android.IdeInfo)
 
 	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled() {
+		if !module.Enabled(ctx) {
 			return
 		}
 
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 37ff639..0d2acae 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -353,7 +353,7 @@
 
 	// All the intermediate rules use the same inputs.
 	expectedIntermediateInputs := `
-		out/soong/.intermediates/bar/android_common/javac/bar.jar
+		out/soong/.intermediates/bar.impl/android_common/javac/bar.jar
 		out/soong/.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
 		out/soong/.intermediates/foo/android_common/javac/foo.jar
 	`
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 2fc6c02..99fa092 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -19,6 +19,7 @@
 	"path/filepath"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -233,7 +234,7 @@
 	var compatConfigMetadata android.Paths
 
 	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled() {
+		if !module.Enabled(ctx) {
 			return
 		}
 		if c, ok := module.(platformCompatConfigMetadataProvider); ok {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 113071f..677b32a 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -950,6 +950,10 @@
 	// Path to the header jars of the implementation library
 	// This is non-empty only when api_only is false.
 	implLibraryHeaderJars android.Paths
+
+	// The reference to the implementation library created by the source module.
+	// Is nil if the source module does not exist.
+	implLibraryModule *Library
 }
 
 func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImportModule) {
@@ -996,6 +1000,10 @@
 	c.doctagPaths = android.PathsForModuleSrc(ctx, c.commonSdkLibraryProperties.Doctag_files)
 }
 
+func (c *commonToSdkLibraryAndImport) getImplLibraryModule() *Library {
+	return c.implLibraryModule
+}
+
 // Module name of the runtime implementation library
 func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
 	return c.module.RootLibraryName() + ".impl"
@@ -1372,6 +1380,8 @@
 
 	// sharedLibrary returns true if this can be used as a shared library.
 	sharedLibrary() bool
+
+	getImplLibraryModule() *Library
 }
 
 type SdkLibrary struct {
@@ -1383,6 +1393,8 @@
 	scopeToProperties map[*apiScope]*ApiScopeProperties
 
 	commonToSdkLibraryAndImport
+
+	builtInstalledForApex []dexpreopterInstall
 }
 
 var _ SdkLibraryDependency = (*SdkLibrary)(nil)
@@ -1391,6 +1403,20 @@
 	return module.sdkLibraryProperties.Generate_system_and_test_apis
 }
 
+func (module *SdkLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
+	if module.implLibraryModule != nil {
+		return module.implLibraryModule.DexJarBuildPath(ctx)
+	}
+	return makeUnsetDexJarPath()
+}
+
+func (module *SdkLibrary) DexJarInstallPath() android.Path {
+	if module.implLibraryModule != nil {
+		return module.implLibraryModule.DexJarInstallPath()
+	}
+	return nil
+}
+
 func (module *SdkLibrary) getGeneratedApiScopes(ctx android.EarlyModuleContext) apiScopes {
 	// Check to see if any scopes have been explicitly enabled. If any have then all
 	// must be.
@@ -1442,6 +1468,10 @@
 var _ android.ModuleWithMinSdkVersionCheck = (*SdkLibrary)(nil)
 
 func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) {
+	CheckMinSdkVersion(ctx, &module.Library)
+}
+
+func CheckMinSdkVersion(ctx android.ModuleContext, module *Library) {
 	android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.ModuleContext, do android.PayloadDepsCallback) {
 		ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
 			isExternal := !module.depIsInSameApex(ctx, child)
@@ -1538,10 +1568,6 @@
 		m += "Please see the documentation of the prebuilt_apis module type (and a usage example in prebuilts/sdk) for a convenient way to generate these."
 		ctx.ModuleErrorf(m)
 	}
-	if module.requiresRuntimeImplementationLibrary() {
-		// Only add the deps for the library if it is actually going to be built.
-		module.Library.deps(ctx)
-	}
 }
 
 func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
@@ -1550,7 +1576,7 @@
 		return paths, err
 	}
 	if module.requiresRuntimeImplementationLibrary() {
-		return module.Library.OutputFiles(tag)
+		return module.implLibraryModule.OutputFiles(tag)
 	}
 	if tag == "" {
 		return nil, nil
@@ -1565,18 +1591,12 @@
 		// TODO (b/331665856): Implement a principled solution for this.
 		module.HideFromMake()
 	}
-	if proptools.String(module.deviceProperties.Min_sdk_version) != "" {
-		module.CheckMinSdkVersion(ctx)
-	}
 
 	module.generateCommonBuildActions(ctx)
 
-	// Only build an implementation library if required.
-	if module.requiresRuntimeImplementationLibrary() {
-		// stubsLinkType must be set before calling Library.GenerateAndroidBuildActions
-		module.Library.stubsLinkType = Unknown
-		module.Library.GenerateAndroidBuildActions(ctx)
-	}
+	module.stem = proptools.StringDefault(module.overridableProperties.Stem, ctx.ModuleName())
+
+	module.provideHiddenAPIPropertyInfo(ctx)
 
 	// Collate the components exported by this module. All scope specific modules are exported but
 	// the impl and xml component modules are not.
@@ -1603,10 +1623,40 @@
 		if tag == implLibraryTag {
 			if dep, ok := android.OtherModuleProvider(ctx, to, JavaInfoProvider); ok {
 				module.implLibraryHeaderJars = append(module.implLibraryHeaderJars, dep.HeaderJars...)
+				module.implLibraryModule = to.(*Library)
+				android.SetProvider(ctx, JavaInfoProvider, dep)
 			}
 		}
 	})
 
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	if !apexInfo.IsForPlatform() {
+		module.hideApexVariantFromMake = true
+	}
+
+	if module.implLibraryModule != nil {
+		if ctx.Device() {
+			module.classesJarPaths = android.Paths{module.implLibraryModule.implementationJarFile}
+			module.bootDexJarPath = module.implLibraryModule.bootDexJarPath
+			module.uncompressDexState = module.implLibraryModule.uncompressDexState
+			module.active = module.implLibraryModule.active
+		}
+
+		module.outputFile = module.implLibraryModule.outputFile
+		module.dexJarFile = makeDexJarPathFromPath(module.implLibraryModule.dexJarFile.Path())
+		module.headerJarFile = module.implLibraryModule.headerJarFile
+		module.implementationAndResourcesJar = module.implLibraryModule.implementationAndResourcesJar
+		module.builtInstalledForApex = module.implLibraryModule.builtInstalledForApex
+		module.dexpreopter.configPath = module.implLibraryModule.dexpreopter.configPath
+		module.dexpreopter.outputProfilePathOnHost = module.implLibraryModule.dexpreopter.outputProfilePathOnHost
+
+		if !module.Host() {
+			module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile
+		}
+
+		android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: module.implLibraryModule.uniqueSrcFiles.Strings()})
+	}
+
 	// Make the set of components exported by this module available for use elsewhere.
 	exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedKeys(exportedComponents)}
 	android.SetProvider(ctx, android.ExportedComponentsInfoProvider, exportedComponentInfo)
@@ -1636,13 +1686,18 @@
 	android.SetProvider(ctx, android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo})
 }
 
+func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall {
+	return module.builtInstalledForApex
+}
+
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
 	if !module.requiresRuntimeImplementationLibrary() {
 		return nil
 	}
 	entriesList := module.Library.AndroidMkEntries()
+	entries := &entriesList[0]
+	entries.Required = append(entries.Required, module.implLibraryModuleName())
 	if module.sharedLibrary() {
-		entries := &entriesList[0]
 		entries.Required = append(entries.Required, module.xmlPermissionsModuleName())
 	}
 	return entriesList
@@ -1763,20 +1818,21 @@
 		Libs           []string
 		Static_libs    []string
 		Apex_available []string
+		Stem           *string
 	}{
 		Name:       proptools.StringPtr(module.implLibraryModuleName()),
 		Visibility: visibility,
 		// Set the instrument property to ensure it is instrumented when instrumentation is required.
 		Instrument: true,
-		// Set the impl_only libs. Note that the module's "Libs" get appended as well, via the
-		// addition of &module.properties below.
-		Libs: module.sdkLibraryProperties.Impl_only_libs,
-		// Set the impl_only static libs. Note that the module's "static_libs" get appended as well, via the
-		// addition of &module.properties below.
-		Static_libs: module.sdkLibraryProperties.Impl_only_static_libs,
+
+		Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...),
+
+		Static_libs: append(module.properties.Static_libs, module.sdkLibraryProperties.Impl_only_static_libs...),
 		// Pass the apex_available settings down so that the impl library can be statically
 		// embedded within a library that is added to an APEX. Needed for updatable-media.
 		Apex_available: module.ApexAvailable(),
+
+		Stem: proptools.StringPtr(module.Name()),
 	}
 
 	properties := []interface{}{
@@ -2167,6 +2223,9 @@
 	if depTag == xmlPermissionsFileTag {
 		return true
 	}
+	if dep.Name() == module.implLibraryModuleName() {
+		return true
+	}
 	return module.Library.DepIsInSameApex(mctx, dep)
 }
 
@@ -2294,7 +2353,7 @@
 // once for public API level and once for system API level
 func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookContext) {
 	// If the module has been disabled then don't create any child modules.
-	if !module.Enabled() {
+	if !module.Enabled(mctx) {
 		return
 	}
 
@@ -2592,10 +2651,6 @@
 
 	commonToSdkLibraryAndImport
 
-	// The reference to the implementation library created by the source module.
-	// Is nil if the source module does not exist.
-	implLibraryModule *Library
-
 	// The reference to the xml permissions module created by the source module.
 	// Is nil if the source module does not exist.
 	xmlPermissionsFileModule *sdkLibraryXml
@@ -3560,7 +3615,8 @@
 	s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk
 	s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk
 
-	if sdk.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
+	implLibrary := sdk.getImplLibraryModule()
+	if implLibrary != nil && implLibrary.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
 		s.DexPreoptProfileGuided = proptools.BoolPtr(true)
 	}
 }
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 34c63ac..d240e70 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -186,13 +186,13 @@
 	// test if quuz have created the api_contribution module
 	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("quuz")+".api.contribution", "")
 
-	fooDexJar := result.ModuleForTests("foo", "android_common").Rule("d8")
-	// tests if kotlinc generated files are NOT excluded from output of foo.
-	android.AssertStringDoesNotContain(t, "foo dex", fooDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
+	fooImplDexJar := result.ModuleForTests("foo.impl", "android_common").Rule("d8")
+	// tests if kotlinc generated files are NOT excluded from output of foo.impl.
+	android.AssertStringDoesNotContain(t, "foo.impl dex", fooImplDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
 
-	barDexJar := result.ModuleForTests("bar", "android_common").Rule("d8")
-	// tests if kotlinc generated files are excluded from output of bar.
-	android.AssertStringDoesContain(t, "bar dex", barDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
+	barImplDexJar := result.ModuleForTests("bar.impl", "android_common").Rule("d8")
+	// tests if kotlinc generated files are excluded from output of bar.impl.
+	android.AssertStringDoesContain(t, "bar.impl dex", barImplDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) {
@@ -1457,11 +1457,11 @@
 	preparer.RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
-            srcs: ["a.java"],
-            static_libs: ["util"],
-            min_sdk_version: "30",
+			srcs: ["a.java"],
+			static_libs: ["util"],
+			min_sdk_version: "30",
 			unsafe_ignore_missing_latest_api: true,
-        }
+		}
 
 		java_library {
 			name: "util",
diff --git a/provenance/provenance_singleton.go b/provenance/provenance_singleton.go
index 97345af..679632c 100644
--- a/provenance/provenance_singleton.go
+++ b/provenance/provenance_singleton.go
@@ -18,6 +18,7 @@
 
 import (
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
@@ -68,6 +69,15 @@
 
 func (p *provenanceInfoSingleton) GenerateBuildActions(context android.SingletonContext) {
 	allMetaDataFiles := make([]android.Path, 0)
+	moduleFilter := func(module android.Module) bool {
+		if !module.Enabled(context) || module.IsSkipInstall() {
+			return false
+		}
+		if p, ok := module.(ProvenanceMetadata); ok {
+			return p.ProvenanceMetaDataFile().String() != ""
+		}
+		return false
+	}
 	context.VisitAllModulesIf(moduleFilter, func(module android.Module) {
 		if p, ok := module.(ProvenanceMetadata); ok {
 			allMetaDataFiles = append(allMetaDataFiles, p.ProvenanceMetaDataFile())
@@ -91,16 +101,6 @@
 	context.Phony("droidcore", android.PathForPhony(context, "provenance_metadata"))
 }
 
-func moduleFilter(module android.Module) bool {
-	if !module.Enabled() || module.IsSkipInstall() {
-		return false
-	}
-	if p, ok := module.(ProvenanceMetadata); ok {
-		return p.ProvenanceMetaDataFile().String() != ""
-	}
-	return false
-}
-
 func GenerateArtifactProvenanceMetaData(ctx android.ModuleContext, artifactPath android.Path, installedFile android.InstallPath) android.OutputPath {
 	onDevicePathOfInstalledFile := android.InstallPathToOnDevicePath(ctx, installedFile)
 	artifactMetaDataFile := android.PathForIntermediates(ctx, "provenance_metadata", ctx.ModuleDir(), ctx.ModuleName(), "provenance_metadata.textproto")
diff --git a/provenance/tools/Android.bp b/provenance/tools/Android.bp
index 0eddd76..b42e543 100644
--- a/provenance/tools/Android.bp
+++ b/provenance/tools/Android.bp
@@ -23,11 +23,6 @@
     srcs: [
         "gen_provenance_metadata.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     libs: [
         "provenance_metadata_proto",
         "libprotobuf-python",
diff --git a/python/binary.go b/python/binary.go
index c84eeee..b935aba 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -71,9 +71,6 @@
 	installedDest android.Path
 
 	androidMkSharedLibs []string
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
 }
 
 var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil)
@@ -106,7 +103,6 @@
 	p.buildBinary(ctx)
 	p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
 		p.installSource.Base(), p.installSource)
-	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
 }
 
 func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
@@ -170,7 +166,6 @@
 			entries.SetString("LOCAL_MODULE_STEM", stem)
 			entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...)
 			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
-			android.SetAconfigFileMkEntries(&p.ModuleBase, entries, p.mergedAconfigFiles)
 		})
 
 	return []android.AndroidMkEntries{entries}
diff --git a/python/python.go b/python/python.go
index e14fdf3..1ee533f 100644
--- a/python/python.go
+++ b/python/python.go
@@ -506,8 +506,8 @@
 	}
 
 	for _, d := range expandedData {
-		if d.Ext() == pyExt || d.Ext() == protoExt {
-			ctx.PropertyErrorf("data", "found (.py|.proto) file: %q!", d.String())
+		if d.Ext() == pyExt {
+			ctx.PropertyErrorf("data", "found (.py) file: %q!", d.String())
 			continue
 		}
 		runfilesPath := filepath.Join(pkgPath, d.Rel())
@@ -523,19 +523,19 @@
 	relativeRootMap := make(map[string]android.Paths)
 	var protoSrcs android.Paths
 	addPathMapping := func(path pathMapping) {
-		// handle proto sources separately
-		if path.src.Ext() == protoExt {
-			protoSrcs = append(protoSrcs, path.src)
-		} else {
-			relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
-			relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
-		}
+		relativeRoot := strings.TrimSuffix(path.src.String(), path.src.Rel())
+		relativeRootMap[relativeRoot] = append(relativeRootMap[relativeRoot], path.src)
 	}
 
 	// "srcs" or "data" properties may contain filegroups so it might happen that
 	// the root directory for each source path is different.
 	for _, path := range p.srcsPathMappings {
-		addPathMapping(path)
+		// handle proto sources separately
+		if path.src.Ext() == protoExt {
+			protoSrcs = append(protoSrcs, path.src)
+		} else {
+			addPathMapping(path)
+		}
 	}
 	for _, path := range p.dataPathMappings {
 		addPathMapping(path)
diff --git a/python/python_test.go b/python/python_test.go
index c0b7295..6a6bd1d 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -50,7 +50,7 @@
 		" Second file: in module %s at path %q."
 	noSrcFileErr      = moduleVariantErrTemplate + "doesn't have any source files!"
 	badSrcFileExtErr  = moduleVariantErrTemplate + "srcs: found non (.py|.proto) file: %q!"
-	badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py|.proto) file: %q!"
+	badDataFileExtErr = moduleVariantErrTemplate + "data: found (.py) file: %q!"
 	bpFile            = "Android.bp"
 
 	data = []struct {
diff --git a/python/test.go b/python/test.go
index 2b939e7..85decf9 100644
--- a/python/test.go
+++ b/python/test.go
@@ -18,6 +18,7 @@
 	"fmt"
 
 	"android/soong/testing"
+
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -151,7 +152,6 @@
 	// just use buildBinary() so that the binary is not installed into the location
 	// it would be for regular binaries.
 	p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
-	android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles)
 	p.buildBinary(ctx)
 
 	var configs []tradefed.Option
@@ -227,7 +227,6 @@
 			}
 
 			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
-			android.SetAconfigFileMkEntries(&p.ModuleBase, entries, p.mergedAconfigFiles)
 
 			p.testProperties.Test_options.SetAndroidMkEntries(entries)
 		})
diff --git a/python/tests/Android.bp b/python/tests/Android.bp
index e5569ba..056f7ed 100644
--- a/python/tests/Android.bp
+++ b/python/tests/Android.bp
@@ -27,9 +27,4 @@
     test_options: {
         unit_test: false,
     },
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
 }
diff --git a/python/tests/dont_import_folder_of_entrypoint/Android.bp b/python/tests/dont_import_folder_of_entrypoint/Android.bp
index e54e9b2..ab2e314 100644
--- a/python/tests/dont_import_folder_of_entrypoint/Android.bp
+++ b/python/tests/dont_import_folder_of_entrypoint/Android.bp
@@ -10,17 +10,3 @@
         "mypkg/mymodule.py",
     ],
 }
-
-python_test_host {
-    name: "py_dont_import_folder_of_entrypoint_test_embedded_launcher",
-    main: "mypkg/main.py",
-    srcs: [
-        "mypkg/main.py",
-        "mypkg/mymodule.py",
-    ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
-}
diff --git a/rust/afdo.go b/rust/afdo.go
index 6116c5e..6bd4bae 100644
--- a/rust/afdo.go
+++ b/rust/afdo.go
@@ -39,7 +39,7 @@
 		return
 	}
 
-	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() {
+	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled(ctx) {
 		fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName())
 		if err != nil {
 			ctx.ModuleErrorf("%s", err.Error())
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 021dd60..8de6b60 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -69,7 +69,6 @@
 				} else if mod.InProduct() {
 					entries.SetBool("LOCAL_IN_PRODUCT", true)
 				}
-				android.SetAconfigFileMkEntries(mod.AndroidModuleBase(), entries, mod.mergedAconfigFiles)
 			},
 		},
 	}
diff --git a/rust/doc.go b/rust/doc.go
index 6970d79..fe20523 100644
--- a/rust/doc.go
+++ b/rust/doc.go
@@ -38,7 +38,7 @@
 		FlagWithArg("-D ", docDir.String())
 
 	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled() {
+		if !module.Enabled(ctx) {
 			return
 		}
 
diff --git a/rust/library.go b/rust/library.go
index 6be4917..f58a54f 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -713,7 +713,7 @@
 	if sourceVariant {
 		sv := modules[0]
 		for _, v := range modules[1:] {
-			if !v.Enabled() {
+			if !v.Enabled(mctx) {
 				continue
 			}
 			mctx.AddInterVariantDependency(sourceDepTag, v, sv)
diff --git a/rust/project_json.go b/rust/project_json.go
index 05fc09b..24dcc89 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -119,7 +119,7 @@
 	if !ok {
 		return nil, false
 	}
-	if !rModule.Enabled() {
+	if !rModule.Enabled(ctx) {
 		return nil, false
 	}
 	return rModule, true
diff --git a/rust/rust.go b/rust/rust.go
index c2b6151..e4bb99c 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -172,9 +172,6 @@
 	apexSdkVersion android.ApiLevel
 
 	transitiveAndroidMkSharedLibs *android.DepSet[string]
-
-	// Aconfig files for all transitive deps.  Also exposed via TransitiveDeclarationsInfo
-	mergedAconfigFiles map[string]android.Paths
 }
 
 func (mod *Module) Header() bool {
@@ -998,8 +995,6 @@
 	if mod.testModule {
 		android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 	}
-
-	android.CollectDependencyAconfigFiles(ctx, &mod.mergedAconfigFiles)
 }
 
 func (mod *Module) deps(ctx DepsContext) Deps {
@@ -1697,7 +1692,7 @@
 }
 
 func BeginMutator(ctx android.BottomUpMutatorContext) {
-	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() {
+	if mod, ok := ctx.Module().(*Module); ok && mod.Enabled(ctx) {
 		mod.beginMutator(ctx)
 	}
 }
diff --git a/rust/sanitize.go b/rust/sanitize.go
index bfd3971..c086880 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -258,7 +258,7 @@
 
 func rustSanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
 	if mod, ok := mctx.Module().(*Module); ok && mod.sanitize != nil {
-		if !mod.Enabled() {
+		if !mod.Enabled(mctx) {
 			return
 		}
 
diff --git a/scripts/Android.bp b/scripts/Android.bp
index d039a81..80cd935 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -29,11 +29,6 @@
         "manifest_fixer_test.py",
         "manifest_fixer.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     libs: [
         "manifest_utils",
     ],
@@ -214,11 +209,6 @@
     srcs: [
         "conv_linker_config.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
     libs: [
         "linker_config_proto",
     ],
@@ -299,20 +289,16 @@
     srcs: [
         "merge_directories.py",
     ],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
 }
 
 python_binary_host {
     name: "buildinfo",
     main: "buildinfo.py",
     srcs: ["buildinfo.py"],
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
+}
+
+python_binary_host {
+    name: "extra_install_zips_file_list",
+    main: "extra_install_zips_file_list.py",
+    srcs: ["extra_install_zips_file_list.py"],
 }
diff --git a/scripts/extra_install_zips_file_list.py b/scripts/extra_install_zips_file_list.py
new file mode 100755
index 0000000..8ea2a4b
--- /dev/null
+++ b/scripts/extra_install_zips_file_list.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import sys
+import zipfile
+from typing import List
+
+def list_files_in_zip(zipfile_path: str) -> List[str]:
+    with zipfile.ZipFile(zipfile_path, 'r') as zf:
+        return zf.namelist()
+
+def main():
+    parser = argparse.ArgumentParser(
+        description='Lists paths to all files inside an EXTRA_INSTALL_ZIPS zip file relative to a partition staging directory. '
+        'This script is just a helper because its difficult to implement this logic in make code.'
+    )
+    parser.add_argument('staging_dir',
+        help='Path to the partition staging directory')
+    parser.add_argument('extra_install_zips', nargs='*',
+        help='The value of EXTRA_INSTALL_ZIPS from make. It should be a list of extraction_dir:zip_file pairs.')
+    args = parser.parse_args()
+
+    staging_dir = args.staging_dir.removesuffix('/') + '/'
+
+    for zip_pair in args.extra_install_zips:
+        d, z = zip_pair.split(':')
+        d = d.removesuffix('/') + '/'
+
+        if d.startswith(staging_dir):
+            d = os.path.relpath(d, staging_dir)
+            if d == '.':
+                d = ''
+            for f in list_files_in_zip(z):
+                print(os.path.join(d, f))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index 1e89efe..43edf44 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -18,19 +18,9 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-python_defaults {
-    name: "hiddenapi_defaults",
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
-}
-
 python_binary_host {
     name: "analyze_bcpf",
     main: "analyze_bcpf.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["analyze_bcpf.py"],
     // Make sure that the bpmodify tool is built.
     data: [":bpmodify"],
@@ -42,7 +32,6 @@
 python_test_host {
     name: "analyze_bcpf_test",
     main: "analyze_bcpf_test.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: [
         "analyze_bcpf.py",
         "analyze_bcpf_test.py",
@@ -60,21 +49,18 @@
 python_binary_host {
     name: "merge_csv",
     main: "merge_csv.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["merge_csv.py"],
 }
 
 python_binary_host {
     name: "generate_hiddenapi_lists",
     main: "generate_hiddenapi_lists.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["generate_hiddenapi_lists.py"],
 }
 
 python_test_host {
     name: "generate_hiddenapi_lists_test",
     main: "generate_hiddenapi_lists_test.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: [
         "generate_hiddenapi_lists.py",
         "generate_hiddenapi_lists_test.py",
@@ -92,7 +78,6 @@
 python_test_host {
     name: "signature_trie_test",
     main: "signature_trie_test.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["signature_trie_test.py"],
     libs: ["signature_trie"],
     test_options: {
@@ -103,7 +88,6 @@
 python_binary_host {
     name: "verify_overlaps",
     main: "verify_overlaps.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["verify_overlaps.py"],
     libs: [
         "signature_trie",
@@ -113,7 +97,6 @@
 python_test_host {
     name: "verify_overlaps_test",
     main: "verify_overlaps_test.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: [
         "verify_overlaps.py",
         "verify_overlaps_test.py",
@@ -129,14 +112,12 @@
 python_binary_host {
     name: "signature_patterns",
     main: "signature_patterns.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: ["signature_patterns.py"],
 }
 
 python_test_host {
     name: "signature_patterns_test",
     main: "signature_patterns_test.py",
-    defaults: ["hiddenapi_defaults"],
     srcs: [
         "signature_patterns.py",
         "signature_patterns_test.py",
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index 58079aa..35d2a1c 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -62,8 +62,8 @@
                             'in the manifest.'))
   parser.add_argument('--extract-native-libs', dest='extract_native_libs',
                       default=None, type=lambda x: (str(x).lower() == 'true'),
-                      help=('specify if the app wants to use embedded native libraries. Must not conflict '
-                            'if already declared in the manifest.'))
+                      help=('specify if the app wants to use embedded native libraries. Must not '
+                            'be true if manifest says false.'))
   parser.add_argument('--has-no-code', dest='has_no_code', action='store_true',
                       help=('adds hasCode="false" attribute to application. Ignored if application elem '
                             'already has a hasCode attribute.'))
@@ -299,7 +299,16 @@
     attr = doc.createAttributeNS(android_ns, 'android:extractNativeLibs')
     attr.value = value
     application.setAttributeNode(attr)
-  elif attr.value != value:
+  elif attr.value == "false" and value == "true":
+    # Note that we don't disallow the case of extractNativeLibs="true" in manifest and
+    # --extract-native-libs="false". This is fine because --extract-native-libs="false" means that
+    # the build system didn't compress the JNI libs, which is a fine choice for built-in apps. At
+    # runtime the JNI libs will be extracted to outside of the APK, but everything will still work
+    # okay.
+    #
+    # The opposite (extractNativeLibs="false" && --extract-native-libs="true") should however be
+    # disallowed because otherwise that would make an ill-formed APK; JNI libs are stored compressed
+    # but they won't be extracted. There's no way to execute the JNI libs.
     raise RuntimeError('existing attribute extractNativeLibs="%s" conflicts with --extract-native-libs="%s"' %
                        (attr.value, value))
 
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index 0a62b10..9fce6b9 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -479,8 +479,8 @@
     self.assert_xml_equal(output, expected)
 
   def test_conflict(self):
-    manifest_input = self.manifest_tmpl % self.extract_native_libs('true')
-    self.assertRaises(RuntimeError, self.run_test, manifest_input, False)
+    manifest_input = self.manifest_tmpl % self.extract_native_libs('false')
+    self.assertRaises(RuntimeError, self.run_test, manifest_input, True)
 
 
 class AddNoCodeApplicationTest(unittest.TestCase):
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 8d3bbfa..6e25122 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -1095,7 +1095,7 @@
 
 	bcpf := result.ModuleForTests("mybootclasspathfragment", "android_common")
 	rule := bcpf.Output("out/soong/.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi" + suffix + "/stub-flags.csv")
-	android.AssertPathsRelativeToTopEquals(t, "stub flags inputs", expectedStubFlagsInputs, rule.Implicits)
+	android.AssertPathsRelativeToTopEquals(t, "stub flags inputs", android.SortedUniqueStrings(expectedStubFlagsInputs), android.SortedUniquePaths(rule.Implicits))
 
 	CheckSnapshot(t, result, "mysdk", "",
 		checkAndroidBpContents(expectedSdkSnapshot),
@@ -1153,7 +1153,7 @@
 		// of the snapshot.
 		expectedStubFlagsInputs := []string{
 			"out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar",
-			"out/soong/.intermediates/mysdklibrary/android_common/aligned/mysdklibrary.jar",
+			"out/soong/.intermediates/mysdklibrary.impl/android_common/aligned/mysdklibrary.jar",
 		}
 
 		testSnapshotWithBootClasspathFragment_MinSdkVersion(t, "S",
@@ -1234,9 +1234,9 @@
 		// they are both part of the snapshot.
 		expectedStubFlagsInputs := []string{
 			"out/soong/.intermediates/mynewsdklibrary.stubs.exportable/android_common/dex/mynewsdklibrary.stubs.exportable.jar",
-			"out/soong/.intermediates/mynewsdklibrary/android_common/aligned/mynewsdklibrary.jar",
+			"out/soong/.intermediates/mynewsdklibrary.impl/android_common/aligned/mynewsdklibrary.jar",
 			"out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar",
-			"out/soong/.intermediates/mysdklibrary/android_common/aligned/mysdklibrary.jar",
+			"out/soong/.intermediates/mysdklibrary.impl/android_common/aligned/mysdklibrary.jar",
 		}
 
 		testSnapshotWithBootClasspathFragment_MinSdkVersion(t, "Tiramisu",
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
index 63cd4e1..b416ebd 100644
--- a/snapshot/host_fake_snapshot.go
+++ b/snapshot/host_fake_snapshot.go
@@ -116,7 +116,7 @@
 			prebuilts[android.RemoveOptionalPrebuiltPrefix(module.Name())] = true
 			return
 		}
-		if !module.Enabled() || module.IsHideFromMake() {
+		if !module.Enabled(ctx) || module.IsHideFromMake() {
 			return
 		}
 		apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)