Merge "Integrate RELEASE_R8_GLOBAL_CHECK_NOT_NULL_FLAGS into soong" into main
diff --git a/Android.bp b/Android.bp
index 523f55c..337545b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -150,10 +150,11 @@
     arch: {
         arm64: {
             cflags: [
-                // Prevent the compiler from optimizing code using SVE, as the
-                // baremetal environment might not have configured the hardware.
-                "-Xclang -target-feature",
-                "-Xclang -sve",
+                // Override the global -march= flag (as set by TARGET_ARCH_VARIANT)
+                // and explicitly use the baseline architecture (ARMv8-A is the first
+                // version with 64-bit support) to avoid emitting potentially
+                // unsupported instructions.
+                "-march=armv8-a",
             ],
         },
     },
diff --git a/android/androidmk.go b/android/androidmk.go
index 7d6b056..c081ba3 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -165,7 +165,7 @@
 
 type androidMkExtraEntriesContext struct {
 	ctx fillInEntriesContext
-	mod blueprint.Module
+	mod Module
 }
 
 func (a *androidMkExtraEntriesContext) Provider(provider blueprint.AnyProviderKey) (any, bool) {
@@ -352,8 +352,8 @@
 }
 
 // Compute the contributions that the module makes to the dist.
-func (a *AndroidMkEntries) getDistContributions(mod blueprint.Module) *distContributions {
-	amod := mod.(Module).base()
+func (a *AndroidMkEntries) getDistContributions(mod Module) *distContributions {
+	amod := mod.base()
 	name := amod.BaseModuleName()
 
 	// Collate the set of associated tag/paths available for copying to the dist.
@@ -390,7 +390,7 @@
 	// Collate the contributions this module makes to the dist.
 	distContributions := &distContributions{}
 
-	if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) {
+	if !exemptFromRequiredApplicableLicensesProperty(mod) {
 		distContributions.licenseMetadataFile = info.LicenseMetadataFile
 	}
 
@@ -501,7 +501,7 @@
 
 // Compute the list of Make strings to declare phony goals and dist-for-goals
 // calls from the module's dist and dists properties.
-func (a *AndroidMkEntries) GetDistForGoals(mod blueprint.Module) []string {
+func (a *AndroidMkEntries) GetDistForGoals(mod Module) []string {
 	distContributions := a.getDistContributions(mod)
 	if distContributions == nil {
 		return nil
@@ -522,11 +522,10 @@
 	HasMutatorFinished(mutatorName string) bool
 }
 
-func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
+func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod Module) {
 	a.entryContext = ctx
 	a.EntryMap = make(map[string][]string)
-	amod := mod.(Module)
-	base := amod.base()
+	base := mod.base()
 	name := base.BaseModuleName()
 	if a.OverrideName != "" {
 		name = a.OverrideName
@@ -535,10 +534,10 @@
 	if a.Include == "" {
 		a.Include = "$(BUILD_PREBUILT)"
 	}
-	a.Required = append(a.Required, amod.RequiredModuleNames(ctx)...)
-	a.Required = append(a.Required, amod.VintfFragmentModuleNames(ctx)...)
-	a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
-	a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
+	a.Required = append(a.Required, mod.RequiredModuleNames(ctx)...)
+	a.Required = append(a.Required, mod.VintfFragmentModuleNames(ctx)...)
+	a.Host_required = append(a.Host_required, mod.HostRequiredModuleNames()...)
+	a.Target_required = append(a.Target_required, mod.TargetRequiredModuleNames()...)
 
 	for _, distString := range a.GetDistForGoals(mod) {
 		fmt.Fprintln(&a.header, distString)
@@ -554,7 +553,7 @@
 	a.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
 	a.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
 	a.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
-	a.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
+	a.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(mod))
 
 	// If the install rule was generated by Soong tell Make about it.
 	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
@@ -718,10 +717,10 @@
 
 type androidMkSingleton struct{}
 
-func allModulesSorted(ctx SingletonContext) []blueprint.Module {
-	var allModules []blueprint.Module
+func allModulesSorted(ctx SingletonContext) []Module {
+	var allModules []Module
 
-	ctx.VisitAllModulesBlueprint(func(module blueprint.Module) {
+	ctx.VisitAllModules(func(module Module) {
 		allModules = append(allModules, module)
 	})
 
@@ -776,20 +775,9 @@
 // In soong-only mode, we don't do most of the androidmk stuff. But disted files are still largely
 // defined through the androidmk mechanisms, so this function is an alternate implementation of
 // the androidmk singleton that just focuses on getting the dist contributions
-func (so *soongOnlyAndroidMkSingleton) soongOnlyBuildActions(ctx SingletonContext, mods []blueprint.Module) {
+func (so *soongOnlyAndroidMkSingleton) soongOnlyBuildActions(ctx SingletonContext, mods []Module) {
 	allDistContributions, moduleInfoJSONs := getSoongOnlyDataFromMods(ctx, mods)
 
-	for _, provider := range append(makeVarsInitProviders, *getSingletonMakevarsProviders(ctx.Config())...) {
-		mctx := &makeVarsContext{
-			SingletonContext: ctx,
-			pctx:             provider.pctx,
-		}
-		provider.call(mctx)
-		if contribution := distsToDistContributions(mctx.dists); contribution != nil {
-			allDistContributions = append(allDistContributions, *contribution)
-		}
-	}
-
 	singletonDists := getSingletonDists(ctx.Config())
 	singletonDists.lock.Lock()
 	if contribution := distsToDistContributions(singletonDists.dists); contribution != nil {
@@ -897,7 +885,7 @@
 
 // getSoongOnlyDataFromMods gathers data from the given modules needed in soong-only builds.
 // Currently, this is the dist contributions, and the module-info.json contents.
-func getSoongOnlyDataFromMods(ctx fillInEntriesContext, mods []blueprint.Module) ([]distContributions, []*ModuleInfoJSON) {
+func getSoongOnlyDataFromMods(ctx fillInEntriesContext, mods []Module) ([]distContributions, []*ModuleInfoJSON) {
 	var allDistContributions []distContributions
 	var moduleInfoJSONs []*ModuleInfoJSON
 	for _, mod := range mods {
@@ -907,7 +895,7 @@
 			}
 		}
 
-		if amod, ok := mod.(Module); ok && shouldSkipAndroidMkProcessing(ctx, amod.base()) {
+		if shouldSkipAndroidMkProcessing(ctx, mod.base()) {
 			continue
 		}
 		if info, ok := OtherModuleProvider(ctx, mod, AndroidMkInfoProvider); ok {
@@ -966,37 +954,12 @@
 					}
 				}
 			}
-			if x, ok := mod.(ModuleMakeVarsProvider); ok {
-				mctx := &makeVarsContext{
-					SingletonContext: ctx.(SingletonContext),
-					config:           ctx.Config(),
-					pctx:             pctx,
-				}
-				if !x.Enabled(ctx) {
-					continue
-				}
-				x.MakeVars(mctx)
-				if contribution := distsToDistContributions(mctx.dists); contribution != nil {
-					allDistContributions = append(allDistContributions, *contribution)
-				}
-			}
-			if x, ok := mod.(SingletonMakeVarsProvider); ok {
-				mctx := &makeVarsContext{
-					SingletonContext: ctx.(SingletonContext),
-					config:           ctx.Config(),
-					pctx:             pctx,
-				}
-				x.MakeVars(mctx)
-				if contribution := distsToDistContributions(mctx.dists); contribution != nil {
-					allDistContributions = append(allDistContributions, *contribution)
-				}
-			}
 		}
 	}
 	return allDistContributions, moduleInfoJSONs
 }
 
-func translateAndroidMk(ctx SingletonContext, absMkFile string, moduleInfoJSONPath WritablePath, mods []blueprint.Module) error {
+func translateAndroidMk(ctx SingletonContext, absMkFile string, moduleInfoJSONPath WritablePath, mods []Module) error {
 	buf := &bytes.Buffer{}
 
 	var moduleInfoJSONs []*ModuleInfoJSON
@@ -1011,8 +974,8 @@
 			return err
 		}
 
-		if amod, ok := mod.(Module); ok && ctx.PrimaryModule(amod) == amod {
-			typeStats[ctx.ModuleType(amod)] += 1
+		if ctx.PrimaryModule(mod) == mod {
+			typeStats[ctx.ModuleType(mod)] += 1
 		}
 	}
 
@@ -1056,7 +1019,7 @@
 	return nil
 }
 
-func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON, mod blueprint.Module) error {
+func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON, mod Module) error {
 	defer func() {
 		if r := recover(); r != nil {
 			panic(fmt.Errorf("%s in translateAndroidMkModule for module %s variant %s",
@@ -1087,7 +1050,7 @@
 	return err
 }
 
-func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Module) {
+func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod Module) {
 	// Get the preamble content through AndroidMkEntries logic.
 	data.Entries = AndroidMkEntries{
 		Class:           data.Class,
@@ -1110,9 +1073,9 @@
 // A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
 // instead.
 func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
-	mod blueprint.Module, provider AndroidMkDataProvider) error {
+	mod Module, provider AndroidMkDataProvider) error {
 
-	amod := mod.(Module).base()
+	amod := mod.base()
 	if shouldSkipAndroidMkProcessing(ctx, amod) {
 		return nil
 	}
@@ -1124,7 +1087,7 @@
 	}
 
 	data.fillInData(ctx, mod)
-	aconfigUpdateAndroidMkData(ctx, mod.(Module), &data)
+	aconfigUpdateAndroidMkData(ctx, mod, &data)
 
 	prefix := ""
 	if amod.ArchSpecific() {
@@ -1205,13 +1168,13 @@
 }
 
 func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
-	mod blueprint.Module, provider AndroidMkEntriesProvider) error {
-	if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) {
+	mod Module, provider AndroidMkEntriesProvider) error {
+	if shouldSkipAndroidMkProcessing(ctx, mod.base()) {
 		return nil
 	}
 
 	entriesList := provider.AndroidMkEntries()
-	aconfigUpdateAndroidMkEntries(ctx, mod.(Module), &entriesList)
+	aconfigUpdateAndroidMkEntries(ctx, mod, &entriesList)
 
 	moduleInfoJSON, providesModuleInfoJSON := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider)
 
@@ -1371,15 +1334,15 @@
 var AndroidMkInfoProvider = blueprint.NewProvider[*AndroidMkProviderInfo]()
 
 func translateAndroidMkEntriesInfoModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
-	mod blueprint.Module, providerInfo *AndroidMkProviderInfo) error {
-	if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) {
+	mod Module, providerInfo *AndroidMkProviderInfo) error {
+	if shouldSkipAndroidMkProcessing(ctx, mod.base()) {
 		return nil
 	}
 
 	// Deep copy the provider info since we need to modify the info later
 	info := deepCopyAndroidMkProviderInfo(providerInfo)
 
-	aconfigUpdateAndroidMkInfos(ctx, mod.(Module), &info)
+	aconfigUpdateAndroidMkInfos(ctx, mod, &info)
 
 	// Any new or special cases here need review to verify correct propagation of license information.
 	info.PrimaryInfo.fillInEntries(ctx, mod)
@@ -1515,13 +1478,12 @@
 	a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...)
 }
 
-func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
+func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod Module) {
 	helperInfo := AndroidMkInfo{
 		EntryMap: make(map[string][]string),
 	}
 
-	amod := mod.(Module)
-	base := amod.base()
+	base := mod.base()
 	name := base.BaseModuleName()
 	if a.OverrideName != "" {
 		name = a.OverrideName
@@ -1530,10 +1492,10 @@
 	if a.Include == "" {
 		a.Include = "$(BUILD_PREBUILT)"
 	}
-	a.Required = append(a.Required, amod.RequiredModuleNames(ctx)...)
-	a.Required = append(a.Required, amod.VintfFragmentModuleNames(ctx)...)
-	a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
-	a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
+	a.Required = append(a.Required, mod.RequiredModuleNames(ctx)...)
+	a.Required = append(a.Required, mod.VintfFragmentModuleNames(ctx)...)
+	a.Host_required = append(a.Host_required, mod.HostRequiredModuleNames()...)
+	a.Target_required = append(a.Target_required, mod.TargetRequiredModuleNames()...)
 
 	for _, distString := range a.GetDistForGoals(ctx, mod) {
 		a.HeaderStrings = append(a.HeaderStrings, distString)
@@ -1549,7 +1511,7 @@
 	helperInfo.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
 	helperInfo.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
 	helperInfo.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
-	helperInfo.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
+	helperInfo.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(mod))
 
 	// If the install rule was generated by Soong tell Make about it.
 	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
@@ -1690,7 +1652,7 @@
 
 // Compute the list of Make strings to declare phony goals and dist-for-goals
 // calls from the module's dist and dists properties.
-func (a *AndroidMkInfo) GetDistForGoals(ctx fillInEntriesContext, mod blueprint.Module) []string {
+func (a *AndroidMkInfo) GetDistForGoals(ctx fillInEntriesContext, mod Module) []string {
 	distContributions := a.getDistContributions(ctx, mod)
 	if distContributions == nil {
 		return nil
@@ -1700,8 +1662,8 @@
 }
 
 // Compute the contributions that the module makes to the dist.
-func (a *AndroidMkInfo) getDistContributions(ctx fillInEntriesContext, mod blueprint.Module) *distContributions {
-	amod := mod.(Module).base()
+func (a *AndroidMkInfo) getDistContributions(ctx fillInEntriesContext, mod Module) *distContributions {
+	amod := mod.base()
 	name := amod.BaseModuleName()
 
 	// Collate the set of associated tag/paths available for copying to the dist.
@@ -1738,7 +1700,7 @@
 	// Collate the contributions this module makes to the dist.
 	distContributions := &distContributions{}
 
-	if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) {
+	if !exemptFromRequiredApplicableLicensesProperty(mod) {
 		distContributions.licenseMetadataFile = info.LicenseMetadataFile
 	}
 
diff --git a/android/api_levels.go b/android/api_levels.go
index 6d0c389..c042eeb 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -468,6 +468,7 @@
 		"Tiramisu":        33,
 		"UpsideDownCake":  34,
 		"VanillaIceCream": 35,
+		"Baklava":         36,
 	}, nil
 }
 
diff --git a/android/base_module_context.go b/android/base_module_context.go
index 4b90083..5e05f54 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -53,6 +53,9 @@
 	// dependencies on the module being visited, it returns the dependency tag used for the current dependency.
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
 
+	// OtherModuleSubDir returns the string representing the variations of a module.
+	OtherModuleSubDir(m blueprint.Module) string
+
 	// OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface
 	// passed to Context.SetNameInterface, or SimpleNameInterface if it was not called.
 	OtherModuleExists(name string) bool
@@ -88,6 +91,11 @@
 	// This method shouldn't be used directly, prefer the type-safe android.OtherModuleProvider instead.
 	otherModuleProvider(m blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 
+	// OtherModuleHasProvider returns true if the module has the given provider set. This
+	// can avoid copying the provider if the caller only cares about the existence of
+	// the provider.
+	OtherModuleHasProvider(m blueprint.Module, provider blueprint.AnyProviderKey) bool
+
 	// OtherModuleIsAutoGenerated returns true if the module is auto generated by another module
 	// instead of being defined in Android.bp file.
 	OtherModuleIsAutoGenerated(m blueprint.Module) bool
@@ -279,6 +287,9 @@
 func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag {
 	return b.bp.OtherModuleDependencyTag(getWrappedModule(m))
 }
+func (b *baseModuleContext) OtherModuleSubDir(m blueprint.Module) string {
+	return b.bp.OtherModuleSubDir(getWrappedModule(m))
+}
 func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) }
 func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool {
 	return b.bp.OtherModuleDependencyVariantExists(variations, name)
@@ -297,6 +308,10 @@
 	return b.bp.OtherModuleProvider(getWrappedModule(m), provider)
 }
 
+func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.AnyProviderKey) bool {
+	return b.bp.OtherModuleHasProvider(getWrappedModule(m), provider)
+}
+
 func (b *baseModuleContext) OtherModuleIsAutoGenerated(m blueprint.Module) bool {
 	return b.bp.OtherModuleIsAutoGenerated(m)
 }
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
index 35805a2..a6dbb8d 100644
--- a/android/compliance_metadata.go
+++ b/android/compliance_metadata.go
@@ -275,16 +275,18 @@
 	writerToCsv(csvWriter, columnNames)
 
 	rowId := -1
-	ctx.VisitAllModules(func(module Module) {
-		if !module.Enabled(ctx) {
+	ctx.VisitAllModuleProxies(func(module ModuleProxy) {
+		commonInfo, _ := OtherModuleProvider(ctx, module, CommonModuleInfoKey)
+		if !commonInfo.Enabled {
 			return
 		}
+
 		moduleType := ctx.ModuleType(module)
 		if moduleType == "package" {
 			metadataMap := map[string]string{
 				ComplianceMetadataProp.NAME:                            ctx.ModuleName(module),
 				ComplianceMetadataProp.MODULE_TYPE:                     ctx.ModuleType(module),
-				ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(module.base().primaryLicensesProperty.getStrings(), " "),
+				ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(commonInfo.PrimaryLicensesProperty.getStrings(), " "),
 			}
 			rowId = rowId + 1
 			metadata := []string{strconv.Itoa(rowId)}
@@ -294,8 +296,7 @@
 			writerToCsv(csvWriter, metadata)
 			return
 		}
-		if provider, ok := ctx.otherModuleProvider(module, ComplianceMetadataProvider); ok {
-			metadataInfo := provider.(*ComplianceMetadataInfo)
+		if metadataInfo, ok := OtherModuleProvider(ctx, module, ComplianceMetadataProvider); ok {
 			rowId = rowId + 1
 			metadata := []string{strconv.Itoa(rowId)}
 			for _, propertyName := range COMPLIANCE_METADATA_PROPS {
diff --git a/android/config.go b/android/config.go
index 82acfc3..f6d08b8 100644
--- a/android/config.go
+++ b/android/config.go
@@ -394,6 +394,17 @@
 	// Add others as needed.
 }
 
+// These are the flags when `SOONG_PARTIAL_COMPILE` is empty or not set.
+var defaultPartialCompileFlags = partialCompileFlags{
+	Enabled: false,
+}
+
+// These are the flags when `SOONG_PARTIAL_COMPILE=true`.
+var enabledPartialCompileFlags = partialCompileFlags{
+	Enabled: true,
+	Use_d8:  true,
+}
+
 type deviceConfig struct {
 	config *config
 	OncePer
@@ -427,11 +438,6 @@
 // To add a new feature to the list, add the field in the struct
 // `partialCompileFlags` above, and then add the name of the field in the
 // switch statement below.
-var defaultPartialCompileFlags = partialCompileFlags{
-	// Set any opt-out flags here.  Opt-in flags are off by default.
-	Enabled: false,
-}
-
 func (c *config) parsePartialCompileFlags(isEngBuild bool) (partialCompileFlags, error) {
 	if !isEngBuild {
 		return partialCompileFlags{}, nil
@@ -472,8 +478,7 @@
 		}
 		switch tok {
 		case "true":
-			ret = defaultPartialCompileFlags
-			ret.Enabled = true
+			ret = enabledPartialCompileFlags
 		case "false":
 			// Set everything to false.
 			ret = partialCompileFlags{}
@@ -2171,14 +2176,6 @@
 	return c.productVariables.GetBuildFlagBool("RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT")
 }
 
-func (c *config) UseResourceProcessorByDefault() bool {
-	return c.productVariables.GetBuildFlagBool("RELEASE_USE_RESOURCE_PROCESSOR_BY_DEFAULT")
-}
-
-func (c *config) UseTransitiveJarsInClasspath() bool {
-	return c.productVariables.GetBuildFlagBool("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH")
-}
-
 func (c *config) UseR8FullModeByDefault() bool {
 	return c.productVariables.GetBuildFlagBool("RELEASE_R8_FULL_MODE_BY_DEFAULT")
 }
diff --git a/android/config_test.go b/android/config_test.go
index 4bdf05f..3d86860 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -239,10 +239,10 @@
 	}{
 		{"", true, defaultPartialCompileFlags},
 		{"false", true, partialCompileFlags{}},
-		{"true", true, defaultPartialCompileFlags.updateEnabled(true)},
+		{"true", true, enabledPartialCompileFlags},
 		{"true", false, partialCompileFlags{}},
-		{"true,use_d8", true, defaultPartialCompileFlags.updateEnabled(true).updateUseD8(true)},
-		{"true,-use_d8", true, defaultPartialCompileFlags.updateEnabled(true).updateUseD8(false)},
+		{"true,use_d8", true, enabledPartialCompileFlags.updateUseD8(true)},
+		{"true,-use_d8", true, enabledPartialCompileFlags.updateUseD8(false)},
 		{"use_d8,false", true, partialCompileFlags{}},
 		{"false,+use_d8", true, partialCompileFlags{}.updateUseD8(true)},
 	}
diff --git a/android/makevars.go b/android/makevars.go
index baa1d44..2931d0b 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -65,24 +65,6 @@
 	// dependencies to be added to it.  Phony can be called on the same name multiple
 	// times to add additional dependencies.
 	Phony(names string, deps ...Path)
-
-	// DistForGoal creates a rule to copy one or more Paths to the artifacts
-	// directory on the build server when the specified goal is built.
-	DistForGoal(goal string, paths ...Path)
-
-	// DistForGoalWithFilename creates a rule to copy a Path to the artifacts
-	// directory on the build server with the given filename when the specified
-	// goal is built.
-	DistForGoalWithFilename(goal string, path Path, filename string)
-
-	// DistForGoals creates a rule to copy one or more Paths to the artifacts
-	// directory on the build server when any of the specified goals are built.
-	DistForGoals(goals []string, paths ...Path)
-
-	// DistForGoalsWithFilename creates a rule to copy a Path to the artifacts
-	// directory on the build server with the given filename when any of the
-	// specified goals are built.
-	DistForGoalsWithFilename(goals []string, path Path, filename string)
 }
 
 // MakeVarsContext contains the set of functions available for MakeVarsProvider
@@ -198,11 +180,9 @@
 
 type makeVarsContext struct {
 	SingletonContext
-	config  Config
 	pctx    PackageContext
 	vars    []makeVarsVariable
 	phonies []phony
-	dists   []dist
 }
 
 var _ MakeVarsContext = &makeVarsContext{}
@@ -263,7 +243,6 @@
 
 		vars = append(vars, mctx.vars...)
 		phonies = append(phonies, mctx.phonies...)
-		dists = append(dists, mctx.dists...)
 	}
 
 	singletonDists := getSingletonDists(ctx.Config())
@@ -281,7 +260,6 @@
 
 			vars = append(vars, mctx.vars...)
 			phonies = append(phonies, mctx.phonies...)
-			dists = append(dists, mctx.dists...)
 		}
 
 		if m.ExportedToMake() {
@@ -611,13 +589,6 @@
 	c.phonies = append(c.phonies, phony{name, deps})
 }
 
-func (c *makeVarsContext) addDist(goals []string, paths []distCopy) {
-	c.dists = append(c.dists, dist{
-		goals: goals,
-		paths: paths,
-	})
-}
-
 func (c *makeVarsContext) Strict(name, ninjaStr string) {
 	c.addVariable(name, ninjaStr, true, false)
 }
@@ -641,26 +612,3 @@
 func (c *makeVarsContext) Phony(name string, deps ...Path) {
 	c.addPhony(name, Paths(deps).Strings())
 }
-
-func (c *makeVarsContext) DistForGoal(goal string, paths ...Path) {
-	c.DistForGoals([]string{goal}, paths...)
-}
-
-func (c *makeVarsContext) DistForGoalWithFilename(goal string, path Path, filename string) {
-	c.DistForGoalsWithFilename([]string{goal}, path, filename)
-}
-
-func (c *makeVarsContext) DistForGoals(goals []string, paths ...Path) {
-	var copies distCopies
-	for _, path := range paths {
-		copies = append(copies, distCopy{
-			from: path,
-			dest: path.Base(),
-		})
-	}
-	c.addDist(goals, copies)
-}
-
-func (c *makeVarsContext) DistForGoalsWithFilename(goals []string, path Path, filename string) {
-	c.addDist(goals, distCopies{{from: path, dest: filename}})
-}
diff --git a/android/metrics.go b/android/metrics.go
index 6834b1b..dc51703 100644
--- a/android/metrics.go
+++ b/android/metrics.go
@@ -57,8 +57,8 @@
 
 func (soongMetricsSingleton) GenerateBuildActions(ctx SingletonContext) {
 	metrics := getSoongMetrics(ctx.Config())
-	ctx.VisitAllModules(func(m Module) {
-		if ctx.PrimaryModule(m) == m {
+	ctx.VisitAllModuleProxies(func(m ModuleProxy) {
+		if ctx.PrimaryModuleProxy(m) == m {
 			metrics.modules++
 		}
 		metrics.variants++
diff --git a/android/module.go b/android/module.go
index 0ffb6cb..f359e9f 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1908,12 +1908,20 @@
 	// is used to avoid adding install or packaging dependencies into libraries provided
 	// by apexes.
 	UninstallableApexPlatformVariant bool
-	HideFromMake                     bool
-	SkipInstall                      bool
-	IsStubsModule                    bool
-	Host                             bool
 	MinSdkVersionSupported           ApiLevel
 	ModuleWithMinSdkVersionCheck     bool
+	// Tests if this module can be installed to APEX as a file. For example, this would return
+	// true for shared libs while return false for static libs because static libs are not
+	// installable module (but it can still be mutated for APEX)
+	IsInstallableToApex bool
+	HideFromMake        bool
+	SkipInstall         bool
+	IsStubsModule       bool
+	Host                bool
+	IsApexModule        bool
+	// The primary licenses property, may be nil, records license metadata for the module.
+	PrimaryLicensesProperty applicableLicensesProperty
+	Owner                   string
 }
 
 type ApiLevelOrPlatform struct {
@@ -2249,14 +2257,16 @@
 	buildComplianceMetadataProvider(ctx, m)
 
 	commonData := CommonModuleInfo{
+		Enabled:                          m.Enabled(ctx),
 		ReplacedByPrebuilt:               m.commonProperties.ReplacedByPrebuilt,
 		Target:                           m.commonProperties.CompileTarget,
 		SkipAndroidMkProcessing:          shouldSkipAndroidMkProcessing(ctx, m),
-		BaseModuleName:                   m.BaseModuleName(),
 		UninstallableApexPlatformVariant: m.commonProperties.UninstallableApexPlatformVariant,
 		HideFromMake:                     m.commonProperties.HideFromMake,
 		SkipInstall:                      m.commonProperties.SkipInstall,
 		Host:                             m.Host(),
+		PrimaryLicensesProperty:          m.primaryLicensesProperty,
+		Owner:                            m.Owner(),
 	}
 	if mm, ok := m.module.(interface {
 		MinSdkVersion(ctx EarlyModuleContext) ApiLevel
@@ -2285,16 +2295,13 @@
 		commonData.SdkVersion = mm.SdkVersion()
 	}
 
-	if m.commonProperties.ForcedDisabled {
-		commonData.Enabled = false
-	} else {
-		commonData.Enabled = m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled)
-	}
 	if am, ok := m.module.(ApexModule); ok {
 		commonData.CanHaveApexVariants = am.CanHaveApexVariants()
 		commonData.NotAvailableForPlatform = am.NotAvailableForPlatform()
 		commonData.NotInPlatform = am.NotInPlatform()
 		commonData.MinSdkVersionSupported = am.MinSdkVersionSupported(ctx)
+		commonData.IsInstallableToApex = am.IsInstallableToApex()
+		commonData.IsApexModule = true
 	}
 
 	if _, ok := m.module.(ModuleWithMinSdkVersionCheck); ok {
@@ -2304,6 +2311,9 @@
 	if st, ok := m.module.(StubsAvailableModule); ok {
 		commonData.IsStubsModule = st.IsStubsModule()
 	}
+	if mm, ok := m.module.(interface{ BaseModuleName() string }); ok {
+		commonData.BaseModuleName = mm.BaseModuleName()
+	}
 	SetProvider(ctx, CommonModuleInfoKey, commonData)
 	if p, ok := m.module.(PrebuiltInterface); ok && p.Prebuilt() != nil {
 		SetProvider(ctx, PrebuiltModuleInfoProvider, PrebuiltModuleInfo{
diff --git a/android/module_context.go b/android/module_context.go
index f279fd9..fb62e67 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -655,6 +655,7 @@
 		owner:                 owner,
 		requiresFullInstall:   requiresFullInstall,
 		fullInstallPath:       fullInstallPath,
+		variation:             m.ModuleSubDir(),
 	}
 	m.packagingSpecs = append(m.packagingSpecs, spec)
 	return spec
@@ -806,6 +807,7 @@
 		owner:               owner,
 		requiresFullInstall: m.requiresFullInstall(),
 		fullInstallPath:     fullInstallPath,
+		variation:           m.ModuleSubDir(),
 	})
 
 	return fullInstallPath
@@ -856,6 +858,7 @@
 		owner:               owner,
 		requiresFullInstall: m.requiresFullInstall(),
 		fullInstallPath:     fullInstallPath,
+		variation:           m.ModuleSubDir(),
 	})
 
 	return fullInstallPath
diff --git a/android/package.go b/android/package.go
index eb76751..385326e 100644
--- a/android/package.go
+++ b/android/package.go
@@ -56,7 +56,10 @@
 }
 
 func (p *packageModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
-	// Nothing to do.
+	ctx.SetProvider(CommonModuleInfoKey, CommonModuleInfo{
+		Enabled:                 true,
+		PrimaryLicensesProperty: p.primaryLicensesProperty,
+	})
 }
 
 func (p *packageModule) qualifiedModuleId(ctx BaseModuleContext) qualifiedModuleName {
diff --git a/android/packaging.go b/android/packaging.go
index 4e0c74a..6146f02 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -72,6 +72,9 @@
 	// tools that want to interact with these files outside of the build. You should not use it
 	// inside of the build. Will be nil if this module doesn't require a "full install".
 	fullInstallPath InstallPath
+
+	// String representation of the variation of the module where this packaging spec is output of
+	variation string
 }
 
 type packagingSpecGob struct {
@@ -86,6 +89,15 @@
 	ArchType              ArchType
 	Overrides             []string
 	Owner                 string
+	Variation             string
+}
+
+func (p *PackagingSpec) Owner() string {
+	return p.owner
+}
+
+func (p *PackagingSpec) Variation() string {
+	return p.variation
 }
 
 func (p *PackagingSpec) ToGob() *packagingSpecGob {
@@ -101,6 +113,7 @@
 		ArchType:              p.archType,
 		Overrides:             p.overrides.ToSlice(),
 		Owner:                 p.owner,
+		Variation:             p.variation,
 	}
 }
 
@@ -116,6 +129,7 @@
 	p.archType = data.ArchType
 	p.overrides = uniquelist.Make(data.Overrides)
 	p.owner = data.Owner
+	p.variation = data.Variation
 }
 
 func (p *PackagingSpec) GobEncode() ([]byte, error) {
diff --git a/android/plugin.go b/android/plugin.go
index d62fc94..4348f14 100644
--- a/android/plugin.go
+++ b/android/plugin.go
@@ -135,6 +135,6 @@
 		disallowedPlugins[name] = true
 	})
 	if len(disallowedPlugins) > 0 {
-		ctx.Errorf("New plugins are not supported; however %q were found. Please reach out to the build team or use BUILD_BROKEN_PLUGIN_VALIDATION (see Changes.md for more info).", SortedStringKeys(disallowedPlugins))
+		ctx.Errorf("New plugins are not supported; however %q were found. Please reach out to the build team or use BUILD_BROKEN_PLUGIN_VALIDATION (see Changes.md for more info).", SortedKeys(disallowedPlugins))
 	}
 }
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 6b076b7..7273599 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -357,6 +357,17 @@
 	return true
 }
 
+func IsModulePreferredProxy(ctx OtherModuleProviderContext, module ModuleProxy) bool {
+	if OtherModuleProviderOrDefault(ctx, module, CommonModuleInfoKey).ReplacedByPrebuilt {
+		// A source module that has been replaced by a prebuilt counterpart.
+		return false
+	}
+	if p, ok := OtherModuleProvider(ctx, module, PrebuiltModuleInfoProvider); ok {
+		return p.UsePrebuilt
+	}
+	return true
+}
+
 // IsModulePrebuilt returns true if the module implements PrebuiltInterface and
 // has been initialized as a prebuilt and so returns a non-nil value from the
 // PrebuiltInterface.Prebuilt() method.
diff --git a/android/singleton.go b/android/singleton.go
index df22045..a03ea74 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -84,6 +84,9 @@
 	VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy))
 
 	PrimaryModule(module Module) Module
+
+	PrimaryModuleProxy(module ModuleProxy) ModuleProxy
+
 	IsFinalModule(module Module) bool
 
 	AddNinjaFileDeps(deps ...string)
@@ -271,6 +274,22 @@
 	}
 }
 
+func (s *singletonContextAdaptor) ModuleName(module blueprint.Module) string {
+	return s.SingletonContext.ModuleName(getWrappedModule(module))
+}
+
+func (s *singletonContextAdaptor) ModuleDir(module blueprint.Module) string {
+	return s.SingletonContext.ModuleDir(getWrappedModule(module))
+}
+
+func (s *singletonContextAdaptor) ModuleSubDir(module blueprint.Module) string {
+	return s.SingletonContext.ModuleSubDir(getWrappedModule(module))
+}
+
+func (s *singletonContextAdaptor) ModuleType(module blueprint.Module) string {
+	return s.SingletonContext.ModuleType(getWrappedModule(module))
+}
+
 func (s *singletonContextAdaptor) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
 	s.SingletonContext.VisitAllModules(visit)
 }
@@ -315,6 +334,10 @@
 	return s.SingletonContext.PrimaryModule(module).(Module)
 }
 
+func (s *singletonContextAdaptor) PrimaryModuleProxy(module ModuleProxy) ModuleProxy {
+	return ModuleProxy{s.SingletonContext.PrimaryModuleProxy(module.module)}
+}
+
 func (s *singletonContextAdaptor) IsFinalModule(module Module) bool {
 	return s.SingletonContext.IsFinalModule(module)
 }
diff --git a/android/test_suites.go b/android/test_suites.go
index 18744f1..39317ec 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -17,6 +17,8 @@
 import (
 	"path/filepath"
 	"strings"
+
+	"github.com/google/blueprint"
 )
 
 func init() {
@@ -37,18 +39,24 @@
 	TestSuites() []string
 }
 
+type TestSuiteInfo struct {
+	TestSuites []string
+}
+
+var TestSuiteInfoProvider = blueprint.NewProvider[TestSuiteInfo]()
+
 func (t *testSuiteFiles) GenerateBuildActions(ctx SingletonContext) {
 	files := make(map[string]map[string]InstallPaths)
 
-	ctx.VisitAllModules(func(m Module) {
-		if tsm, ok := m.(TestSuiteModule); ok {
-			for _, testSuite := range tsm.TestSuites() {
+	ctx.VisitAllModuleProxies(func(m ModuleProxy) {
+		if tsm, ok := OtherModuleProvider(ctx, m, TestSuiteInfoProvider); ok {
+			for _, testSuite := range tsm.TestSuites {
 				if files[testSuite] == nil {
 					files[testSuite] = make(map[string]InstallPaths)
 				}
 				name := ctx.ModuleName(m)
 				files[testSuite][name] = append(files[testSuite][name],
-					OtherModuleProviderOrDefault(ctx, tsm, InstallFilesProvider).InstallFiles...)
+					OtherModuleProviderOrDefault(ctx, m, InstallFilesProvider).InstallFiles...)
 			}
 		}
 	})
diff --git a/android/test_suites_test.go b/android/test_suites_test.go
index bf4de19..dda9329 100644
--- a/android/test_suites_test.go
+++ b/android/test_suites_test.go
@@ -110,6 +110,10 @@
 	for _, output := range f.props.Outputs {
 		ctx.InstallFile(pathForTestCases(ctx), output, nil)
 	}
+
+	SetProvider(ctx, TestSuiteInfoProvider, TestSuiteInfo{
+		TestSuites: f.TestSuites(),
+	})
 }
 
 func (f *fake_module) TestSuites() []string {
diff --git a/android/testing.go b/android/testing.go
index 8e38b3b..0cab0ab 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -931,6 +931,7 @@
 }
 
 func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams {
+	b.t.Helper()
 	p, searchRules := b.maybeBuildParamsFromRule(rule)
 	if p.Rule == nil {
 		b.t.Fatalf("couldn't find rule %q.\nall rules:\n%s", rule, strings.Join(searchRules, "\n"))
@@ -950,6 +951,7 @@
 }
 
 func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBuildParams {
+	b.t.Helper()
 	p, searchedDescriptions := b.maybeBuildParamsFromDescription(desc)
 	if p.Rule == nil {
 		b.t.Fatalf("couldn't find description %q\nall descriptions:\n%s", desc, strings.Join(searchedDescriptions, "\n"))
@@ -983,6 +985,7 @@
 }
 
 func (b baseTestingComponent) buildParamsFromOutput(file string) TestingBuildParams {
+	b.t.Helper()
 	p, searchedOutputs := b.maybeBuildParamsFromOutput(file)
 	if p.Rule == nil {
 		b.t.Fatalf("couldn't find output %q.\nall outputs:\n    %s\n",
@@ -1008,6 +1011,7 @@
 
 // Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name.  Panics if no rule is found.
 func (b baseTestingComponent) Rule(rule string) TestingBuildParams {
+	b.t.Helper()
 	return b.buildParamsFromRule(rule)
 }
 
@@ -1021,6 +1025,7 @@
 // Description finds a call to ctx.Build with BuildParams.Description set to a the given string.  Panics if no rule is
 // found.
 func (b baseTestingComponent) Description(desc string) TestingBuildParams {
+	b.t.Helper()
 	return b.buildParamsFromDescription(desc)
 }
 
@@ -1034,6 +1039,7 @@
 // Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
 // value matches the provided string.  Panics if no rule is found.
 func (b baseTestingComponent) Output(file string) TestingBuildParams {
+	b.t.Helper()
 	return b.buildParamsFromOutput(file)
 }
 
@@ -1161,7 +1167,7 @@
 	config.katiEnabled = true
 }
 
-func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
+func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod Module) []AndroidMkEntries {
 	t.Helper()
 	var p AndroidMkEntriesProvider
 	var ok bool
@@ -1170,15 +1176,15 @@
 	}
 
 	entriesList := p.AndroidMkEntries()
-	aconfigUpdateAndroidMkEntries(ctx, mod.(Module), &entriesList)
+	aconfigUpdateAndroidMkEntries(ctx, mod, &entriesList)
 	for i := range entriesList {
 		entriesList[i].fillInEntries(ctx, mod)
 	}
 	return entriesList
 }
 
-func AndroidMkInfoForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) *AndroidMkProviderInfo {
-	if runtime.GOOS == "darwin" && mod.(Module).base().Os() != Darwin {
+func AndroidMkInfoForTest(t *testing.T, ctx *TestContext, mod Module) *AndroidMkProviderInfo {
+	if runtime.GOOS == "darwin" && mod.base().Os() != Darwin {
 		// The AndroidMkInfo provider is not set in this case.
 		t.Skip("AndroidMkInfo provider is not set on darwin")
 	}
@@ -1190,7 +1196,7 @@
 	}
 
 	info := OtherModuleProviderOrDefault(ctx, mod, AndroidMkInfoProvider)
-	aconfigUpdateAndroidMkInfos(ctx, mod.(Module), info)
+	aconfigUpdateAndroidMkInfos(ctx, mod, info)
 	info.PrimaryInfo.fillInEntries(ctx, mod)
 	if len(info.ExtraInfo) > 0 {
 		for _, ei := range info.ExtraInfo {
@@ -1201,7 +1207,7 @@
 	return info
 }
 
-func AndroidMkDataForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) AndroidMkData {
+func AndroidMkDataForTest(t *testing.T, ctx *TestContext, mod Module) AndroidMkData {
 	t.Helper()
 	var p AndroidMkDataProvider
 	var ok bool
@@ -1210,7 +1216,7 @@
 	}
 	data := p.AndroidMk()
 	data.fillInData(ctx, mod)
-	aconfigUpdateAndroidMkData(ctx, mod.(Module), &data)
+	aconfigUpdateAndroidMkData(ctx, mod, &data)
 	return data
 }
 
diff --git a/android/util.go b/android/util.go
index 8591cc6..7b305b5 100644
--- a/android/util.go
+++ b/android/util.go
@@ -102,13 +102,6 @@
 	return buf.String()
 }
 
-// SortedStringKeys returns the keys of the given map in the ascending order.
-//
-// Deprecated: Use SortedKeys instead.
-func SortedStringKeys[V any](m map[string]V) []string {
-	return SortedKeys(m)
-}
-
 // SortedKeys returns the keys of the given map in the ascending order.
 func SortedKeys[T cmp.Ordered, V any](m map[T]V) []T {
 	if len(m) == 0 {
diff --git a/android/vintf_fragment.go b/android/vintf_fragment.go
index 49cf999..4a29fee 100644
--- a/android/vintf_fragment.go
+++ b/android/vintf_fragment.go
@@ -14,6 +14,8 @@
 
 package android
 
+import "github.com/google/blueprint"
+
 type vintfFragmentProperties struct {
 	// Vintf fragment XML file.
 	Src string `android:"path"`
@@ -37,6 +39,12 @@
 	ctx.RegisterModuleType("vintf_fragment", vintfLibraryFactory)
 }
 
+type VintfFragmentInfo struct {
+	OutputFile Path
+}
+
+var VintfFragmentInfoProvider = blueprint.NewProvider[VintfFragmentInfo]()
+
 // vintf_fragment module processes vintf fragment file and installs under etc/vintf/manifest.
 // Vintf fragment files formerly listed in vintf_fragment property would be transformed into
 // this module type.
@@ -68,6 +76,10 @@
 	m.outputFilePath = processedVintfFragment
 
 	ctx.InstallFile(m.installDirPath, processedVintfFragment.Base(), processedVintfFragment)
+
+	SetProvider(ctx, VintfFragmentInfoProvider, VintfFragmentInfo{
+		OutputFile: m.OutputFile(),
+	})
 }
 
 func (m *VintfFragmentModule) OutputFile() Path {
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 570f36c..6485cc5 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -103,25 +103,26 @@
 func init() {
 	addStandardProperties(bpparser.StringType,
 		map[string]string{
-			"LOCAL_MODULE":                  "name",
-			"LOCAL_CXX_STL":                 "stl",
-			"LOCAL_MULTILIB":                "compile_multilib",
-			"LOCAL_ARM_MODE_HACK":           "instruction_set",
-			"LOCAL_SDK_VERSION":             "sdk_version",
-			"LOCAL_MIN_SDK_VERSION":         "min_sdk_version",
-			"LOCAL_TARGET_SDK_VERSION":      "target_sdk_version",
-			"LOCAL_NDK_STL_VARIANT":         "stl",
-			"LOCAL_JAR_MANIFEST":            "manifest",
-			"LOCAL_CERTIFICATE":             "certificate",
-			"LOCAL_CERTIFICATE_LINEAGE":     "lineage",
-			"LOCAL_PACKAGE_NAME":            "name",
-			"LOCAL_MODULE_RELATIVE_PATH":    "relative_install_path",
-			"LOCAL_PROTOC_OPTIMIZE_TYPE":    "proto.type",
-			"LOCAL_MODULE_OWNER":            "owner",
-			"LOCAL_RENDERSCRIPT_TARGET_API": "renderscript.target_api",
-			"LOCAL_JAVA_LANGUAGE_VERSION":   "java_version",
-			"LOCAL_INSTRUMENTATION_FOR":     "instrumentation_for",
-			"LOCAL_MANIFEST_FILE":           "manifest",
+			"LOCAL_MODULE":                   "name",
+			"LOCAL_CXX_STL":                  "stl",
+			"LOCAL_MULTILIB":                 "compile_multilib",
+			"LOCAL_ARM_MODE_HACK":            "instruction_set",
+			"LOCAL_SDK_VERSION":              "sdk_version",
+			"LOCAL_MIN_SDK_VERSION":          "min_sdk_version",
+			"LOCAL_ROTATION_MIN_SDK_VERSION": "rotationMinSdkVersion",
+			"LOCAL_TARGET_SDK_VERSION":       "target_sdk_version",
+			"LOCAL_NDK_STL_VARIANT":          "stl",
+			"LOCAL_JAR_MANIFEST":             "manifest",
+			"LOCAL_CERTIFICATE":              "certificate",
+			"LOCAL_CERTIFICATE_LINEAGE":      "lineage",
+			"LOCAL_PACKAGE_NAME":             "name",
+			"LOCAL_MODULE_RELATIVE_PATH":     "relative_install_path",
+			"LOCAL_PROTOC_OPTIMIZE_TYPE":     "proto.type",
+			"LOCAL_MODULE_OWNER":             "owner",
+			"LOCAL_RENDERSCRIPT_TARGET_API":  "renderscript.target_api",
+			"LOCAL_JAVA_LANGUAGE_VERSION":    "java_version",
+			"LOCAL_INSTRUMENTATION_FOR":      "instrumentation_for",
+			"LOCAL_MANIFEST_FILE":            "manifest",
 
 			"LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING": "dex_preopt.profile",
 			"LOCAL_TEST_CONFIG":                      "test_config",
diff --git a/apex/apex.go b/apex/apex.go
index 4d0e3f1..4b510f8 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -624,7 +624,8 @@
 }
 
 // TODO(jiyong): shorten the arglist using an option struct
-func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, androidMkModuleName string, installDir string, class apexFileClass, module android.Module) apexFile {
+func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, androidMkModuleName string,
+	installDir string, class apexFileClass, module android.Module) apexFile {
 	ret := apexFile{
 		builtFile:           builtFile,
 		installDir:          installDir,
@@ -1375,24 +1376,29 @@
 	}
 }
 
+func setDirInApexForNativeBridge(commonInfo *android.CommonModuleInfo, dir *string) {
+	if commonInfo.Target.NativeBridge == android.NativeBridgeEnabled {
+		*dir = filepath.Join(*dir, commonInfo.Target.NativeBridgeRelativePath)
+	}
+}
+
 // apexFileFor<Type> functions below create an apexFile struct for a given Soong module. The
 // returned apexFile saves information about the Soong module that will be used for creating the
 // build rules.
-func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod cc.VersionedLinkableInterface, handleSpecialLibs bool) apexFile {
+func apexFileForNativeLibrary(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo, ccMod *cc.LinkableInfo, handleSpecialLibs bool) apexFile {
 	// Decide the APEX-local directory by the multilib of the library In the future, we may
 	// query this to the module.
 	// TODO(jiyong): use the new PackagingSpec
 	var dirInApex string
-	switch ccMod.Multilib() {
+	switch ccMod.Multilib {
 	case "lib32":
 		dirInApex = "lib"
 	case "lib64":
 		dirInApex = "lib64"
 	}
-	if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
-		dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
-	}
-	if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), ctx.Config()) {
+	setDirInApexForNativeBridge(commonInfo, &dirInApex)
+	if handleSpecialLibs && cc.InstallToBootstrap(commonInfo.BaseModuleName, ctx.Config()) {
 		// Special case for Bionic libs and other libs installed with them. This is to
 		// prevent those libs from being included in the search path
 		// /apex/com.android.runtime/${LIB}. This exclusion is required because those libs
@@ -1407,66 +1413,68 @@
 	// This needs to go after the runtime APEX handling because otherwise we would get
 	// weird paths like lib64/rel_install_path/bionic rather than
 	// lib64/bionic/rel_install_path.
-	dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
+	dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath)
 
-	fileToCopy := android.OutputFileForModule(ctx, ccMod, "")
-	androidMkModuleName := ccMod.BaseModuleName() + ccMod.SubName()
-	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, ccMod)
+	fileToCopy := android.OutputFileForModule(ctx, module, "")
+	androidMkModuleName := commonInfo.BaseModuleName + ccMod.SubName
+	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, module)
 }
 
-func apexFileForExecutable(ctx android.BaseModuleContext, cc *cc.Module) apexFile {
+func apexFileForExecutable(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo, ccInfo *cc.CcInfo) apexFile {
+	linkableInfo := android.OtherModuleProviderOrDefault(ctx, module, cc.LinkableInfoProvider)
 	dirInApex := "bin"
-	if cc.Target().NativeBridge == android.NativeBridgeEnabled {
-		dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
-	}
-	dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
-	fileToCopy := android.OutputFileForModule(ctx, cc, "")
-	androidMkModuleName := cc.BaseModuleName() + cc.Properties.SubName
-	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, cc)
-	af.symlinks = cc.Symlinks()
-	af.dataPaths = cc.DataPaths()
+	setDirInApexForNativeBridge(commonInfo, &dirInApex)
+	dirInApex = filepath.Join(dirInApex, linkableInfo.RelativeInstallPath)
+	fileToCopy := android.OutputFileForModule(ctx, module, "")
+	androidMkModuleName := commonInfo.BaseModuleName + linkableInfo.SubName
+	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, module)
+	af.symlinks = linkableInfo.Symlinks
+	af.dataPaths = ccInfo.DataPaths
 	return af
 }
 
-func apexFileForRustExecutable(ctx android.BaseModuleContext, rustm *rust.Module) apexFile {
+func apexFileForRustExecutable(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo) apexFile {
+	linkableInfo := android.OtherModuleProviderOrDefault(ctx, module, cc.LinkableInfoProvider)
 	dirInApex := "bin"
-	if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
-		dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
-	}
-	dirInApex = filepath.Join(dirInApex, rustm.RelativeInstallPath())
-	fileToCopy := android.OutputFileForModule(ctx, rustm, "")
-	androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
-	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, rustm)
+	setDirInApexForNativeBridge(commonInfo, &dirInApex)
+	dirInApex = filepath.Join(dirInApex, linkableInfo.RelativeInstallPath)
+	fileToCopy := android.OutputFileForModule(ctx, module, "")
+	androidMkModuleName := commonInfo.BaseModuleName + linkableInfo.SubName
+	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, module)
 	return af
 }
 
-func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFile {
-	dirInApex := filepath.Join("bin", sh.SubDir())
-	if sh.Target().NativeBridge == android.NativeBridgeEnabled {
-		dirInApex = filepath.Join(dirInApex, sh.Target().NativeBridgeRelativePath)
-	}
-	fileToCopy := sh.OutputFile()
-	af := newApexFile(ctx, fileToCopy, sh.BaseModuleName(), dirInApex, shBinary, sh)
-	af.symlinks = sh.Symlinks()
+func apexFileForShBinary(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo, sh *sh.ShBinaryInfo) apexFile {
+	dirInApex := filepath.Join("bin", sh.SubDir)
+	setDirInApexForNativeBridge(commonInfo, &dirInApex)
+	fileToCopy := sh.OutputFile
+	af := newApexFile(ctx, fileToCopy, commonInfo.BaseModuleName, dirInApex, shBinary, module)
+	af.symlinks = sh.Symlinks
 	return af
 }
 
-func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, outputFile android.Path) apexFile {
-	dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir())
+func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, module android.Module,
+	prebuilt *prebuilt_etc.PrebuiltEtcInfo, outputFile android.Path) apexFile {
+	dirInApex := filepath.Join(prebuilt.BaseDir, prebuilt.SubDir)
 	makeModuleName := strings.ReplaceAll(filepath.Join(dirInApex, outputFile.Base()), "/", "_")
-	return newApexFile(ctx, outputFile, makeModuleName, dirInApex, etc, prebuilt)
+	return newApexFile(ctx, outputFile, makeModuleName, dirInApex, etc, module)
 }
 
-func apexFileForCompatConfig(ctx android.BaseModuleContext, config java.PlatformCompatConfigIntf, depName string) apexFile {
-	dirInApex := filepath.Join("etc", config.SubDir())
-	fileToCopy := config.CompatConfig()
-	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, config)
+func apexFileForCompatConfig(ctx android.BaseModuleContext, module android.Module,
+	config *java.PlatformCompatConfigInfo, depName string) apexFile {
+	dirInApex := filepath.Join("etc", config.SubDir)
+	fileToCopy := config.CompatConfig
+	return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, module)
 }
 
-func apexFileForVintfFragment(ctx android.BaseModuleContext, vintfFragment *android.VintfFragmentModule) apexFile {
+func apexFileForVintfFragment(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo, vf *android.VintfFragmentInfo) apexFile {
 	dirInApex := filepath.Join("etc", "vintf")
 
-	return newApexFile(ctx, vintfFragment.OutputFile(), vintfFragment.BaseModuleName(), dirInApex, etc, vintfFragment)
+	return newApexFile(ctx, vf.OutputFile, commonInfo.BaseModuleName, dirInApex, etc, module)
 }
 
 // javaModule is an interface to handle all Java modules (java_library, dex_import, etc) in the same
@@ -1486,60 +1494,42 @@
 var _ javaModule = (*java.SdkLibraryImport)(nil)
 
 // apexFileForJavaModule creates an apexFile for a java module's dex implementation jar.
-func apexFileForJavaModule(ctx android.ModuleContext, module javaModule) apexFile {
-	return apexFileForJavaModuleWithFile(ctx, module, module.DexJarBuildPath(ctx).PathOrNil())
+func apexFileForJavaModule(ctx android.ModuleContext, module android.Module, javaInfo *java.JavaInfo) apexFile {
+	return apexFileForJavaModuleWithFile(ctx, module, javaInfo, javaInfo.DexJarBuildPath.PathOrNil())
 }
 
 // apexFileForJavaModuleWithFile creates an apexFile for a java module with the supplied file.
-func apexFileForJavaModuleWithFile(ctx android.ModuleContext, module javaModule, dexImplementationJar android.Path) apexFile {
+func apexFileForJavaModuleWithFile(ctx android.ModuleContext, module android.Module,
+	javaInfo *java.JavaInfo, dexImplementationJar android.Path) apexFile {
 	dirInApex := "javalib"
-	af := newApexFile(ctx, dexImplementationJar, module.BaseModuleName(), dirInApex, javaSharedLib, module)
-	af.jacocoReportClassesFile = module.JacocoReportClassesFile()
+	commonInfo := android.OtherModuleProviderOrDefault(ctx, module, android.CommonModuleInfoKey)
+	af := newApexFile(ctx, dexImplementationJar, commonInfo.BaseModuleName, dirInApex, javaSharedLib, module)
+	af.jacocoReportClassesFile = javaInfo.JacocoReportClassesFile
 	if lintInfo, ok := android.OtherModuleProvider(ctx, module, java.LintProvider); ok {
 		af.lintInfo = lintInfo
 	}
-	af.customStem = module.Stem() + ".jar"
+	af.customStem = javaInfo.Stem + ".jar"
 	// Collect any system server dex jars and dexpreopt artifacts for installation alongside the apex.
 	// TODO: b/338641779 - Remove special casing of sdkLibrary once bcpf and sscpf depends
 	// on the implementation library
-	if sdkLib, ok := module.(*java.SdkLibrary); ok {
-		af.systemServerDexpreoptInstalls = append(af.systemServerDexpreoptInstalls, sdkLib.ApexSystemServerDexpreoptInstalls()...)
-		af.systemServerDexJars = append(af.systemServerDexJars, sdkLib.ApexSystemServerDexJars()...)
-	} else if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
-		af.systemServerDexpreoptInstalls = append(af.systemServerDexpreoptInstalls, dexpreopter.ApexSystemServerDexpreoptInstalls()...)
-		af.systemServerDexJars = append(af.systemServerDexJars, dexpreopter.ApexSystemServerDexJars()...)
+	if javaInfo.DexpreopterInfo != nil {
+		af.systemServerDexpreoptInstalls = append(af.systemServerDexpreoptInstalls, javaInfo.DexpreopterInfo.ApexSystemServerDexpreoptInstalls...)
+		af.systemServerDexJars = append(af.systemServerDexJars, javaInfo.DexpreopterInfo.ApexSystemServerDexJars...)
 	}
 	return af
 }
 
-func apexFileForJavaModuleProfile(ctx android.BaseModuleContext, module javaModule) *apexFile {
-	if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
-		if profilePathOnHost := dexpreopter.OutputProfilePathOnHost(); profilePathOnHost != nil {
-			dirInApex := "javalib"
-			af := newApexFile(ctx, profilePathOnHost, module.BaseModuleName()+"-profile", dirInApex, etc, nil)
-			af.customStem = module.Stem() + ".jar.prof"
-			return &af
-		}
+func apexFileForJavaModuleProfile(ctx android.BaseModuleContext, commonInfo *android.CommonModuleInfo,
+	javaInfo *java.JavaInfo) *apexFile {
+	if profilePathOnHost := javaInfo.DexpreopterInfo.OutputProfilePathOnHost; profilePathOnHost != nil {
+		dirInApex := "javalib"
+		af := newApexFile(ctx, profilePathOnHost, commonInfo.BaseModuleName+"-profile", dirInApex, etc, nil)
+		af.customStem = javaInfo.Stem + ".jar.prof"
+		return &af
 	}
 	return nil
 }
 
-// androidApp is an interface to handle all app modules (android_app, android_app_import, etc.) in
-// the same way.
-type androidApp interface {
-	android.Module
-	Privileged() bool
-	InstallApkName() string
-	OutputFile() android.Path
-	JacocoReportClassesFile() android.Path
-	Certificate() java.Certificate
-	BaseModuleName() string
-	PrivAppAllowlist() android.OptionalPath
-}
-
-var _ androidApp = (*java.AndroidApp)(nil)
-var _ androidApp = (*java.AndroidAppImport)(nil)
-
 func sanitizedBuildIdForPath(ctx android.BaseModuleContext) string {
 	buildId := ctx.Config().BuildId()
 
@@ -1555,36 +1545,35 @@
 	return buildId
 }
 
-func apexFilesForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) []apexFile {
+func apexFilesForAndroidApp(ctx android.BaseModuleContext, module android.Module,
+	commonInfo *android.CommonModuleInfo, aapp *java.AppInfo) []apexFile {
 	appDir := "app"
-	if aapp.Privileged() {
+	if aapp.Privileged {
 		appDir = "priv-app"
 	}
 
 	// TODO(b/224589412, b/226559955): Ensure that the subdirname is suffixed
 	// so that PackageManager correctly invalidates the existing installed apk
 	// in favour of the new APK-in-APEX.  See bugs for more information.
-	dirInApex := filepath.Join(appDir, aapp.InstallApkName()+"@"+sanitizedBuildIdForPath(ctx))
-	fileToCopy := aapp.OutputFile()
+	dirInApex := filepath.Join(appDir, aapp.InstallApkName+"@"+sanitizedBuildIdForPath(ctx))
+	fileToCopy := aapp.OutputFile
 
-	af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp)
-	af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
-	if lintInfo, ok := android.OtherModuleProvider(ctx, aapp, java.LintProvider); ok {
+	af := newApexFile(ctx, fileToCopy, commonInfo.BaseModuleName, dirInApex, app, module)
+	af.jacocoReportClassesFile = aapp.JacocoReportClassesFile
+	if lintInfo, ok := android.OtherModuleProvider(ctx, module, java.LintProvider); ok {
 		af.lintInfo = lintInfo
 	}
-	af.certificate = aapp.Certificate()
+	af.certificate = aapp.Certificate
 
-	if app, ok := aapp.(interface {
-		OverriddenManifestPackageName() string
-	}); ok {
-		af.overriddenPackageName = app.OverriddenManifestPackageName()
+	if aapp.OverriddenManifestPackageName != nil {
+		af.overriddenPackageName = *aapp.OverriddenManifestPackageName
 	}
 
 	apexFiles := []apexFile{}
 
-	if allowlist := aapp.PrivAppAllowlist(); allowlist.Valid() {
+	if allowlist := aapp.PrivAppAllowlist; allowlist.Valid() {
 		dirInApex := filepath.Join("etc", "permissions")
-		privAppAllowlist := newApexFile(ctx, allowlist.Path(), aapp.BaseModuleName()+"_privapp", dirInApex, etc, aapp)
+		privAppAllowlist := newApexFile(ctx, allowlist.Path(), commonInfo.BaseModuleName+"_privapp", dirInApex, etc, module)
 		apexFiles = append(apexFiles, privAppAllowlist)
 	}
 
@@ -1593,29 +1582,24 @@
 	return apexFiles
 }
 
-func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile {
+func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, module android.Module, rro java.RuntimeResourceOverlayInfo) apexFile {
 	rroDir := "overlay"
-	dirInApex := filepath.Join(rroDir, rro.Theme())
-	fileToCopy := rro.OutputFile()
-	af := newApexFile(ctx, fileToCopy, rro.Name(), dirInApex, app, rro)
-	af.certificate = rro.Certificate()
+	dirInApex := filepath.Join(rroDir, rro.Theme)
+	fileToCopy := rro.OutputFile
+	af := newApexFile(ctx, fileToCopy, module.Name(), dirInApex, app, module)
+	af.certificate = rro.Certificate
 
-	if a, ok := rro.(interface {
-		OverriddenManifestPackageName() string
-	}); ok {
-		af.overriddenPackageName = a.OverriddenManifestPackageName()
-	}
 	return af
 }
 
-func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, apex_sub_dir string, bpfProgram bpf.BpfModule) apexFile {
+func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, apex_sub_dir string, bpfProgram android.Module) apexFile {
 	dirInApex := filepath.Join("etc", "bpf", apex_sub_dir)
 	return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
 }
 
-func apexFileForFilesystem(ctx android.BaseModuleContext, buildFile android.Path, fs filesystem.Filesystem) apexFile {
+func apexFileForFilesystem(ctx android.BaseModuleContext, buildFile android.Path, module android.Module) apexFile {
 	dirInApex := filepath.Join("etc", "fs")
-	return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, fs)
+	return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, module)
 }
 
 // WalkPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
@@ -1864,11 +1848,12 @@
 	if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
 		return false
 	}
-	if !child.Enabled(ctx) {
+	commonInfo := android.OtherModuleProviderOrDefault(ctx, child, android.CommonModuleInfoKey)
+	if !commonInfo.Enabled {
 		return false
 	}
 	depName := ctx.OtherModuleName(child)
-	if _, isDirectDep := parent.(*apexBundle); isDirectDep {
+	if ctx.EqualModules(parent, ctx.Module()) {
 		switch depTag {
 		case sharedLibTag, jniLibTag:
 			isJniLib := depTag == jniLibTag
@@ -1877,17 +1862,17 @@
 				propertyName = "jni_libs"
 			}
 
-			if ch, ok := child.(cc.VersionedLinkableInterface); ok {
-				if ch.IsStubs() {
+			if ch, ok := android.OtherModuleProvider(ctx, child, cc.LinkableInfoProvider); ok {
+				if ch.IsStubs {
 					ctx.PropertyErrorf(propertyName, "%q is a stub. Remove it from the list.", depName)
 				}
-				fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
+				fi := apexFileForNativeLibrary(ctx, child, &commonInfo, ch, vctx.handleSpecialLibs)
 				fi.isJniLib = isJniLib
 				vctx.filesInfo = append(vctx.filesInfo, fi)
 				// Collect the list of stub-providing libs except:
 				// - VNDK libs are only for vendors
 				// - bootstrap bionic libs are treated as provided by system
-				if ch.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(ch.BaseModuleName(), ctx.Config()) {
+				if ch.HasStubsVariants && !a.vndkApex && !cc.InstallToBootstrap(commonInfo.BaseModuleName, ctx.Config()) {
 					vctx.provideNativeLibs = append(vctx.provideNativeLibs, fi.stem())
 				}
 				return true // track transitive dependencies
@@ -1897,34 +1882,33 @@
 			}
 
 		case executableTag:
-			switch ch := child.(type) {
-			case *cc.Module:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
+			if ccInfo, ok := android.OtherModuleProvider(ctx, child, cc.CcInfoProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, child, &commonInfo, ccInfo))
 				return true // track transitive dependencies
-			case *rust.Module:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, ch))
+			}
+			if _, ok := android.OtherModuleProvider(ctx, child, rust.RustInfoProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, child, &commonInfo))
 				return true // track transitive dependencies
-			default:
+			} else {
 				ctx.PropertyErrorf("binaries",
 					"%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, nor (host) bootstrap_go_binary", depName)
 			}
 		case shBinaryTag:
-			if csh, ok := child.(*sh.ShBinary); ok {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForShBinary(ctx, csh))
+			if csh, ok := android.OtherModuleProvider(ctx, child, sh.ShBinaryInfoProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForShBinary(ctx, child, &commonInfo, &csh))
 			} else {
 				ctx.PropertyErrorf("sh_binaries", "%q is not a sh_binary module", depName)
 			}
 		case bcpfTag:
-			_, ok := child.(*java.BootclasspathFragmentModule)
+			_, ok := android.OtherModuleProvider(ctx, child, java.BootclasspathFragmentInfoProvider)
 			if !ok {
 				ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
 				return false
 			}
-
 			vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
 			return true
 		case sscpfTag:
-			if _, ok := child.(*java.SystemServerClasspathModule); !ok {
+			if _, ok := android.OtherModuleProvider(ctx, child, java.LibraryNameToPartitionInfoProvider); !ok {
 				ctx.PropertyErrorf("systemserverclasspath_fragments",
 					"%q is not a systemserverclasspath_fragment module", depName)
 				return false
@@ -1934,83 +1918,84 @@
 			}
 			return true
 		case javaLibTag:
-			switch child.(type) {
-			case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
-				af := apexFileForJavaModule(ctx, child.(javaModule))
+			if ctx.OtherModuleHasProvider(child, java.JavaLibraryInfoProvider) ||
+				ctx.OtherModuleHasProvider(child, java.JavaDexImportInfoProvider) ||
+				ctx.OtherModuleHasProvider(child, java.SdkLibraryInfoProvider) {
+				javaInfo := android.OtherModuleProviderOrDefault(ctx, child, java.JavaInfoProvider)
+				af := apexFileForJavaModule(ctx, child, javaInfo)
 				if !af.ok() {
 					ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
 					return false
 				}
 				vctx.filesInfo = append(vctx.filesInfo, af)
 				return true // track transitive dependencies
-			default:
+			} else {
 				ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
 			}
 		case androidAppTag:
-			switch ap := child.(type) {
-			case *java.AndroidApp:
-				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
-				return true // track transitive dependencies
-			case *java.AndroidAppImport:
-				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
-			case *java.AndroidTestHelperApp:
-				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
-			case *java.AndroidAppSet:
-				appDir := "app"
-				if ap.Privileged() {
-					appDir = "priv-app"
+			if appInfo, ok := android.OtherModuleProvider(ctx, child, java.AppInfoProvider); ok {
+				if appInfo.AppSet {
+					appDir := "app"
+					if appInfo.Privileged {
+						appDir = "priv-app"
+					}
+					// TODO(b/224589412, b/226559955): Ensure that the dirname is
+					// suffixed so that PackageManager correctly invalidates the
+					// existing installed apk in favour of the new APK-in-APEX.
+					// See bugs for more information.
+					appDirName := filepath.Join(appDir, commonInfo.BaseModuleName+"@"+sanitizedBuildIdForPath(ctx))
+					af := newApexFile(ctx, appInfo.OutputFile, commonInfo.BaseModuleName, appDirName, appSet, child)
+					af.certificate = java.PresignedCertificate
+					vctx.filesInfo = append(vctx.filesInfo, af)
+				} else {
+					vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, child, &commonInfo, appInfo)...)
+					if !appInfo.Prebuilt && !appInfo.TestHelperApp {
+						return true // track transitive dependencies
+					}
 				}
-				// TODO(b/224589412, b/226559955): Ensure that the dirname is
-				// suffixed so that PackageManager correctly invalidates the
-				// existing installed apk in favour of the new APK-in-APEX.
-				// See bugs for more information.
-				appDirName := filepath.Join(appDir, ap.BaseModuleName()+"@"+sanitizedBuildIdForPath(ctx))
-				af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap)
-				af.certificate = java.PresignedCertificate
-				vctx.filesInfo = append(vctx.filesInfo, af)
-			default:
+			} else {
 				ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
 			}
 		case rroTag:
-			if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
+			if rro, ok := android.OtherModuleProvider(ctx, child, java.RuntimeResourceOverlayInfoProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForRuntimeResourceOverlay(ctx, child, rro))
 			} else {
 				ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
 			}
 		case bpfTag:
-			if bpfProgram, ok := child.(bpf.BpfModule); ok {
-				filesToCopy := android.OutputFilesForModule(ctx, bpfProgram, "")
-				apex_sub_dir := bpfProgram.SubDir()
+			if bpfProgram, ok := android.OtherModuleProvider(ctx, child, bpf.BpfInfoProvider); ok {
+				filesToCopy := android.OutputFilesForModule(ctx, child, "")
+				apex_sub_dir := bpfProgram.SubDir
 				for _, bpfFile := range filesToCopy {
-					vctx.filesInfo = append(vctx.filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
+					vctx.filesInfo = append(vctx.filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, child))
 				}
 			} else {
 				ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
 			}
 		case fsTag:
-			if fs, ok := child.(filesystem.Filesystem); ok {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
+			if fs, ok := android.OtherModuleProvider(ctx, child, filesystem.FilesystemProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForFilesystem(ctx, fs.Output, child))
 			} else {
 				ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
 			}
 		case prebuiltTag:
-			if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
-				filesToCopy := android.OutputFilesForModule(ctx, prebuilt, "")
+			if prebuilt, ok := android.OtherModuleProvider(ctx, child, prebuilt_etc.PrebuiltEtcInfoProvider); ok {
+				filesToCopy := android.OutputFilesForModule(ctx, child, "")
 				for _, etcFile := range filesToCopy {
-					vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile))
+					vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, child, &prebuilt, etcFile))
 				}
 			} else {
 				ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
 			}
 		case compatConfigTag:
-			if compatConfig, ok := child.(java.PlatformCompatConfigIntf); ok {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForCompatConfig(ctx, compatConfig, depName))
+			if compatConfig, ok := android.OtherModuleProvider(ctx, child, java.PlatformCompatConfigInfoProvider); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForCompatConfig(ctx, child, &compatConfig, depName))
 			} else {
 				ctx.PropertyErrorf("compat_configs", "%q is not a platform_compat_config module", depName)
 			}
 		case testTag:
-			if ccTest, ok := child.(*cc.Module); ok {
-				af := apexFileForExecutable(ctx, ccTest)
+			if ccInfo, ok := android.OtherModuleProvider(ctx, child, cc.CcInfoProvider); ok {
+				af := apexFileForExecutable(ctx, child, &commonInfo, ccInfo)
 				af.class = nativeTest
 				vctx.filesInfo = append(vctx.filesInfo, af)
 				return true // track transitive dependencies
@@ -2018,14 +2003,14 @@
 				ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
 			}
 		case keyTag:
-			if key, ok := child.(*apexKey); ok {
-				a.privateKeyFile = key.privateKeyFile
-				a.publicKeyFile = key.publicKeyFile
+			if key, ok := android.OtherModuleProvider(ctx, child, ApexKeyInfoProvider); ok {
+				a.privateKeyFile = key.PrivateKeyFile
+				a.publicKeyFile = key.PublicKeyFile
 			} else {
 				ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
 			}
 		case certificateTag:
-			if dep, ok := child.(*java.AndroidAppCertificate); ok {
+			if dep, ok := android.OtherModuleProvider(ctx, child, java.AndroidAppCertificateInfoProvider); ok {
 				a.containerCertificateFile = dep.Certificate.Pem
 				a.containerPrivateKeyFile = dep.Certificate.Key
 			} else {
@@ -2040,18 +2025,17 @@
 	}
 
 	// indirect dependencies
-	am, ok := child.(android.ApexModule)
-	if !ok {
+	if !commonInfo.IsApexModule {
 		return false
 	}
 	// We cannot use a switch statement on `depTag` here as the checked
 	// tags used below are private (e.g. `cc.sharedDepTag`).
 	if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
-		if ch, ok := child.(cc.VersionedLinkableInterface); ok {
-			af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
+		if ch, ok := android.OtherModuleProvider(ctx, child, cc.LinkableInfoProvider); ok {
+			af := apexFileForNativeLibrary(ctx, child, &commonInfo, ch, vctx.handleSpecialLibs)
 			af.transitiveDep = true
 
-			if ch.IsStubs() || ch.HasStubsVariants() {
+			if ch.IsStubs || ch.HasStubsVariants {
 				// If the dependency is a stubs lib, don't include it in this APEX,
 				// but make sure that the lib is installed on the device.
 				// In case no APEX is having the lib, the lib is installed to the system
@@ -2062,10 +2046,10 @@
 				//
 				// Skip the dependency in unbundled builds where the device image is not
 				// being built.
-				if ch.VersionedInterface().IsStubsImplementationRequired() &&
-					!am.NotInPlatform() && !ctx.Config().UnbundledBuild() {
+				if ch.IsStubsImplementationRequired &&
+					!commonInfo.NotInPlatform && !ctx.Config().UnbundledBuild() {
 					// we need a module name for Make
-					name := ch.ImplementationModuleNameForMake(ctx) + ch.SubName()
+					name := ch.ImplementationModuleNameForMake + ch.SubName
 					if !android.InList(name, a.makeModulesToInstall) {
 						a.makeModulesToInstall = append(a.makeModulesToInstall, name)
 					}
@@ -2084,7 +2068,7 @@
 			// like to record requiredNativeLibs even when
 			// DepIsInSameAPex is false. We also shouldn't do
 			// this for host.
-			if !android.IsDepInSameApex(ctx, parent, am) {
+			if !android.IsDepInSameApex(ctx, parent, child) {
 				return false
 			}
 
@@ -2096,19 +2080,21 @@
 	} else if java.IsJniDepTag(depTag) {
 		// Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
 	} else if java.IsXmlPermissionsFileDepTag(depTag) {
-		if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
-			filesToCopy := android.OutputFilesForModule(ctx, prebuilt, "")
+		if prebuilt, ok := android.OtherModuleProvider(ctx, child, prebuilt_etc.PrebuiltEtcInfoProvider); ok {
+			filesToCopy := android.OutputFilesForModule(ctx, child, "")
 			for _, etcFile := range filesToCopy {
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile))
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, child, &prebuilt, etcFile))
 			}
 		}
 	} else if rust.IsDylibDepTag(depTag) {
-		if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
-			if !android.IsDepInSameApex(ctx, am, am) {
+		if _, ok := android.OtherModuleProvider(ctx, child, rust.RustInfoProvider); ok &&
+			commonInfo.IsInstallableToApex {
+			if !android.IsDepInSameApex(ctx, parent, child) {
 				return false
 			}
 
-			af := apexFileForNativeLibrary(ctx, child.(cc.VersionedLinkableInterface), vctx.handleSpecialLibs)
+			linkableInfo := android.OtherModuleProviderOrDefault(ctx, child, cc.LinkableInfoProvider)
+			af := apexFileForNativeLibrary(ctx, child, &commonInfo, linkableInfo, vctx.handleSpecialLibs)
 			af.transitiveDep = true
 			vctx.filesInfo = append(vctx.filesInfo, af)
 			return true // track transitive dependencies
@@ -2119,10 +2105,9 @@
 		return true
 	} else if java.IsBootclasspathFragmentContentDepTag(depTag) {
 		// Add the contents of the bootclasspath fragment to the apex.
-		switch child.(type) {
-		case *java.Library, *java.SdkLibrary:
-			javaModule := child.(javaModule)
-			af := apexFileForBootclasspathFragmentContentModule(ctx, parent, javaModule)
+		if ctx.OtherModuleHasProvider(child, java.JavaLibraryInfoProvider) ||
+			ctx.OtherModuleHasProvider(child, java.SdkLibraryInfoProvider) {
+			af := apexFileForBootclasspathFragmentContentModule(ctx, parent, child)
 			if !af.ok() {
 				ctx.PropertyErrorf("bootclasspath_fragments",
 					"bootclasspath_fragment content %q is not configured to be compiled into dex", depName)
@@ -2130,21 +2115,22 @@
 			}
 			vctx.filesInfo = append(vctx.filesInfo, af)
 			return true // track transitive dependencies
-		default:
+		} else {
 			ctx.PropertyErrorf("bootclasspath_fragments",
 				"bootclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
 		}
 	} else if java.IsSystemServerClasspathFragmentContentDepTag(depTag) {
 		// Add the contents of the systemserverclasspath fragment to the apex.
-		switch child.(type) {
-		case *java.Library, *java.SdkLibrary:
-			af := apexFileForJavaModule(ctx, child.(javaModule))
+		if ctx.OtherModuleHasProvider(child, java.JavaLibraryInfoProvider) ||
+			ctx.OtherModuleHasProvider(child, java.SdkLibraryInfoProvider) {
+			javaInfo := android.OtherModuleProviderOrDefault(ctx, child, java.JavaInfoProvider)
+			af := apexFileForJavaModule(ctx, child, javaInfo)
 			vctx.filesInfo = append(vctx.filesInfo, af)
-			if profileAf := apexFileForJavaModuleProfile(ctx, child.(javaModule)); profileAf != nil {
+			if profileAf := apexFileForJavaModuleProfile(ctx, &commonInfo, javaInfo); profileAf != nil {
 				vctx.filesInfo = append(vctx.filesInfo, *profileAf)
 			}
 			return true // track transitive dependencies
-		default:
+		} else {
 			ctx.PropertyErrorf("systemserverclasspath_fragments",
 				"systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
 		}
@@ -2152,11 +2138,11 @@
 		// nothing
 	} else if depTag == android.RequiredDepTag {
 		// nothing
-	} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
+	} else if commonInfo.IsInstallableToApex {
 		ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
 	} else if android.IsVintfDepTag(depTag) {
-		if vf, ok := child.(*android.VintfFragmentModule); ok {
-			apexFile := apexFileForVintfFragment(ctx, vf)
+		if vf, ok := android.OtherModuleProvider(ctx, child, android.VintfFragmentInfoProvider); ok {
+			apexFile := apexFileForVintfFragment(ctx, child, &commonInfo, &vf)
 			vctx.filesInfo = append(vctx.filesInfo, apexFile)
 		}
 	}
@@ -2325,8 +2311,8 @@
 		// checking direct deps is sufficient since apex->apk is a direct edge, even when inherited via apex_defaults
 		mctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 			if appInfo, ok := android.OtherModuleProvider(mctx, module, java.AppInfoProvider); ok {
-				// ignore android_test_app
-				if !appInfo.TestHelperApp && !appInfo.Updatable {
+				// ignore android_test_app and android_app_import
+				if !appInfo.TestHelperApp && !appInfo.Prebuilt && !appInfo.Updatable {
 					mctx.ModuleErrorf("app dependency %s must have updatable: true", mctx.OtherModuleName(module))
 				}
 			}
@@ -2336,7 +2322,7 @@
 
 // apexBootclasspathFragmentFiles returns the list of apexFile structures defining the files that
 // the bootclasspath_fragment contributes to the apex.
-func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module blueprint.Module) []apexFile {
+func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module android.Module) []apexFile {
 	bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, module, java.BootclasspathFragmentApexContentInfoProvider)
 	var filesToAdd []apexFile
 
@@ -2385,7 +2371,7 @@
 
 // apexClasspathFragmentProtoFile returns *apexFile structure defining the classpath.proto config that
 // the module contributes to the apex; or nil if the proto config was not generated.
-func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) *apexFile {
+func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module android.Module) *apexFile {
 	info, _ := android.OtherModuleProvider(ctx, module, java.ClasspathFragmentProtoContentInfoProvider)
 	if !info.ClasspathFragmentProtoGenerated {
 		return nil
@@ -2397,7 +2383,7 @@
 
 // apexFileForBootclasspathFragmentContentModule creates an apexFile for a bootclasspath_fragment
 // content module, i.e. a library that is part of the bootclasspath.
-func apexFileForBootclasspathFragmentContentModule(ctx android.ModuleContext, fragmentModule blueprint.Module, javaModule javaModule) apexFile {
+func apexFileForBootclasspathFragmentContentModule(ctx android.ModuleContext, fragmentModule, javaModule android.Module) apexFile {
 	bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, fragmentModule, java.BootclasspathFragmentApexContentInfoProvider)
 
 	// Get the dexBootJar from the bootclasspath_fragment as that is responsible for performing the
@@ -2409,7 +2395,8 @@
 
 	// Create an apexFile as for a normal java module but with the dex boot jar provided by the
 	// bootclasspath_fragment.
-	af := apexFileForJavaModuleWithFile(ctx, javaModule, dexBootJar)
+	javaInfo := android.OtherModuleProviderOrDefault(ctx, javaModule, java.JavaInfoProvider)
+	af := apexFileForJavaModuleWithFile(ctx, javaModule, javaInfo, dexBootJar)
 	return af
 }
 
@@ -2896,14 +2883,14 @@
 		if !inApex && !inApkInApex {
 			ctx.ModuleErrorf("library in apex transitively linked against implementation library %q not in apex", lib)
 			var depPath []android.Module
-			ctx.WalkDeps(func(child, parent android.Module) bool {
+			ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
 				if depPath != nil {
 					return false
 				}
 
 				tag := ctx.OtherModuleDependencyTag(child)
 
-				if parent == ctx.Module() {
+				if ctx.EqualModules(parent, ctx.Module()) {
 					if !checkApexTag(tag) {
 						return false
 					}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 5519bd2..9eaf814 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -7874,7 +7874,7 @@
 
 	// The bar library should depend on the implementation jar.
 	barLibrary := ctx.ModuleForTests(t, "bar", "android_common_apex10000").Rule("javac")
-	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -7926,7 +7926,7 @@
 
 	// The bar library should depend on the stubs jar.
 	barLibrary := ctx.ModuleForTests(t, "bar", "android_common").Rule("javac")
-	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/foo\.stubs\.from-text/foo\.stubs\.from-text\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -8020,7 +8020,7 @@
 
 	// The bar library should depend on the implementation jar.
 	barLibrary := ctx.ModuleForTests(t, "bar", "android_common_apex10000").Rule("javac")
-	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
diff --git a/apex/key.go b/apex/key.go
index 1622c65..cc66a13 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -33,6 +33,13 @@
 	ctx.RegisterParallelSingletonModuleType("all_apex_certs", allApexCertsFactory)
 }
 
+type ApexKeyInfo struct {
+	PublicKeyFile  android.Path
+	PrivateKeyFile android.Path
+}
+
+var ApexKeyInfoProvider = blueprint.NewProvider[ApexKeyInfo]()
+
 type apexKey struct {
 	android.ModuleBase
 
@@ -95,6 +102,11 @@
 			m.publicKeyFile.String(), pubKeyName, m.privateKeyFile, privKeyName)
 		return
 	}
+
+	android.SetProvider(ctx, ApexKeyInfoProvider, ApexKeyInfo{
+		PublicKeyFile:  m.publicKeyFile,
+		PrivateKeyFile: m.privateKeyFile,
+	})
 }
 
 type apexKeyEntry struct {
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 3f34382..deb465d 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -60,6 +60,12 @@
 	ctx.RegisterModuleType("bpf", BpfFactory)
 }
 
+type BpfInfo struct {
+	SubDir string
+}
+
+var BpfInfoProvider = blueprint.NewProvider[BpfInfo]()
+
 var PrepareForTestWithBpf = android.FixtureRegisterWithContext(registerBpfBuildComponents)
 
 // BpfModule interface is used by the apex package to gather information from a bpf module.
@@ -230,6 +236,10 @@
 		ctx.PackageFile(installDir, obj.Base(), obj)
 	}
 
+	android.SetProvider(ctx, BpfInfoProvider, BpfInfo{
+		SubDir: bpf.SubDir(),
+	})
+
 	ctx.SetOutputFiles(bpf.objs, "")
 }
 
diff --git a/cc/afdo.go b/cc/afdo.go
index 828e494..8d8341e 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -96,8 +96,10 @@
 		flags.Local.CFlags = append([]string{"-funique-internal-linkage-names"}, flags.Local.CFlags...)
 		// Flags for Flow Sensitive AutoFDO
 		flags.Local.CFlags = append([]string{"-mllvm", "-enable-fs-discriminator=true"}, flags.Local.CFlags...)
+		flags.Local.LdFlags = append([]string{"-Wl,-mllvm,-enable-fs-discriminator=true"}, flags.Local.LdFlags...)
 		// TODO(b/266595187): Remove the following feature once it is enabled in LLVM by default.
 		flags.Local.CFlags = append([]string{"-mllvm", "-improved-fs-discriminator=true"}, flags.Local.CFlags...)
+		flags.Local.LdFlags = append([]string{"-Wl,-mllvm,-improved-fs-discriminator=true"}, flags.Local.LdFlags...)
 	}
 	if fdoProfilePath := getFdoProfilePathFromDep(ctx); fdoProfilePath != "" {
 		// The flags are prepended to allow overriding.
diff --git a/cc/cc.go b/cc/cc.go
index d31b3e2..0e70d48 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -117,6 +117,7 @@
 	IsPrebuilt             bool
 	CmakeSnapshotSupported bool
 	HasLlndkStubs          bool
+	DataPaths              []android.DataPath
 	CompilerInfo           *CompilerInfo
 	LinkerInfo             *LinkerInfo
 	SnapshotInfo           *SnapshotInfo
@@ -148,10 +149,8 @@
 	CrateName string
 	// DepFlags returns a slice of Rustc string flags
 	ExportedCrateLinkDirs []string
-	// This can be different from the one on CommonModuleInfo
-	BaseModuleName       string
-	HasNonSystemVariants bool
-	IsLlndk              bool
+	HasNonSystemVariants  bool
+	IsLlndk               bool
 	// True if the library is in the configs known NDK list.
 	IsNdk             bool
 	InVendorOrProduct bool
@@ -163,13 +162,20 @@
 	OnlyInVendorRamdisk bool
 	InRecovery          bool
 	OnlyInRecovery      bool
+	InVendor            bool
 	Installable         *bool
 	// RelativeInstallPath returns the relative install path for this module.
 	RelativeInstallPath string
 	// TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs.
 	RustApexExclude bool
 	// Bootstrap tests if this module is allowed to use non-APEX version of libraries.
-	Bootstrap bool
+	Bootstrap                       bool
+	Multilib                        string
+	ImplementationModuleNameForMake string
+	IsStubsImplementationRequired   bool
+	// Symlinks returns a list of symlinks that should be created for this module.
+	Symlinks               []string
+	APIListCoverageXMLPath android.ModuleOutPath
 }
 
 var LinkableInfoProvider = blueprint.NewProvider[*LinkableInfo]()
@@ -1576,7 +1582,7 @@
 // where the Soong name is prebuilt_foo, this returns foo (which works in Make
 // under the premise that the prebuilt module overrides its source counterpart
 // if it is exposed to Make).
-func (c *Module) ImplementationModuleNameForMake(ctx android.BaseModuleContext) string {
+func (c *Module) ImplementationModuleNameForMake() string {
 	name := c.BaseModuleName()
 	if versioned, ok := c.linker.(VersionedInterface); ok {
 		name = versioned.ImplementationModuleName(name)
@@ -2293,6 +2299,7 @@
 		IsPrebuilt:             c.IsPrebuilt(),
 		CmakeSnapshotSupported: proptools.Bool(c.Properties.Cmake_snapshot_supported),
 		HasLlndkStubs:          c.HasLlndkStubs(),
+		DataPaths:              c.DataPaths(),
 	}
 	if c.compiler != nil {
 		cflags := c.compiler.baseCompilerProps().Cflags
@@ -2365,7 +2372,7 @@
 }
 
 func CreateCommonLinkableInfo(ctx android.ModuleContext, mod VersionedLinkableInterface) *LinkableInfo {
-	return &LinkableInfo{
+	info := &LinkableInfo{
 		StaticExecutable:     mod.StaticExecutable(),
 		HasStubsVariants:     mod.HasStubsVariants(),
 		OutputFile:           mod.OutputFile(),
@@ -2376,7 +2383,6 @@
 		CcLibrary:            mod.CcLibrary(),
 		CcLibraryInterface:   mod.CcLibraryInterface(),
 		RustLibraryInterface: mod.RustLibraryInterface(),
-		BaseModuleName:       mod.BaseModuleName(),
 		IsLlndk:              mod.IsLlndk(),
 		IsNdk:                mod.IsNdk(ctx.Config()),
 		HasNonSystemVariants: mod.HasNonSystemVariants(),
@@ -2388,18 +2394,24 @@
 		OnlyInVendorRamdisk:  mod.OnlyInVendorRamdisk(),
 		InRecovery:           mod.InRecovery(),
 		OnlyInRecovery:       mod.OnlyInRecovery(),
+		InVendor:             mod.InVendor(),
 		Installable:          mod.Installable(),
 		RelativeInstallPath:  mod.RelativeInstallPath(),
 		// TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs.
-		RustApexExclude: mod.RustApexExclude(),
-		Bootstrap:       mod.Bootstrap(),
+		RustApexExclude:                 mod.RustApexExclude(),
+		Bootstrap:                       mod.Bootstrap(),
+		Multilib:                        mod.Multilib(),
+		ImplementationModuleNameForMake: mod.ImplementationModuleNameForMake(),
+		Symlinks:                        mod.Symlinks(),
 	}
-}
 
-func setOutputFilesIfNotEmpty(ctx ModuleContext, files android.Paths, tag string) {
-	if len(files) > 0 {
-		ctx.SetOutputFiles(files, tag)
+	vi := mod.VersionedInterface()
+	if vi != nil {
+		info.IsStubsImplementationRequired = vi.IsStubsImplementationRequired()
+		info.APIListCoverageXMLPath = vi.GetAPIListCoverageXMLPath()
 	}
+
+	return info
 }
 
 func (c *Module) setOutputFiles(ctx ModuleContext) {
@@ -3582,7 +3594,7 @@
 					c.sabi.Properties.ReexportedSystemIncludes, depExporterInfo.SystemIncludeDirs.Strings()...)
 			}
 
-			makeLibName := MakeLibName(ccInfo, linkableInfo, &commonInfo, linkableInfo.BaseModuleName) + libDepTag.makeSuffix
+			makeLibName := MakeLibName(ccInfo, linkableInfo, &commonInfo, commonInfo.BaseModuleName) + libDepTag.makeSuffix
 			switch {
 			case libDepTag.header():
 				c.Properties.AndroidMkHeaderLibs = append(
@@ -3609,7 +3621,8 @@
 			switch depTag {
 			case runtimeDepTag:
 				c.Properties.AndroidMkRuntimeLibs = append(
-					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ccInfo, linkableInfo, &commonInfo, linkableInfo.BaseModuleName)+libDepTag.makeSuffix)
+					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ccInfo, linkableInfo, &commonInfo,
+						commonInfo.BaseModuleName)+libDepTag.makeSuffix)
 			case objDepTag:
 				depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
 			case CrtBeginDepTag:
@@ -3771,7 +3784,7 @@
 	if ccInfo != nil {
 		// Use base module name for snapshots when exporting to Makefile.
 		if ccInfo.SnapshotInfo != nil {
-			return linkableInfo.BaseModuleName + ccInfo.SnapshotInfo.SnapshotAndroidMkSuffix
+			return commonInfo.BaseModuleName + ccInfo.SnapshotInfo.SnapshotAndroidMkSuffix
 		}
 	}
 
@@ -4033,15 +4046,6 @@
 		return ret
 	}
 
-	// Special case for modules that are configured to be installed to /data, which includes
-	// test modules. For these modules, both APEX and non-APEX variants are considered as
-	// installable. This is because even the APEX variants won't be included in the APEX, but
-	// will anyway be installed to /data/*.
-	// See b/146995717
-	if c.InstallInData() {
-		return ret
-	}
-
 	return false
 }
 
diff --git a/cc/linkable.go b/cc/linkable.go
index 337b459..f3aff15 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -83,7 +83,7 @@
 	SetSdkVersion(string)
 	SetMinSdkVersion(version string)
 	ApexSdkVersion() android.ApiLevel
-	ImplementationModuleNameForMake(ctx android.BaseModuleContext) string
+	ImplementationModuleNameForMake() string
 
 	// RustApexExclude returns ApexExclude() for Rust modules; always returns false for all non-Rust modules.
 	// TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs.
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 5586576..8ca3ca1 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -147,6 +147,8 @@
 	ctx.InstallFile(installPath, filename, txt.outputFile)
 
 	ctx.SetOutputFiles(android.Paths{txt.outputFile}, "")
+
+	etc.SetCommonPrebuiltEtcInfo(ctx, txt)
 }
 
 func getVndkFileName(m *Module) (string, error) {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index e7598e7..b704ef4 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1901,6 +1901,8 @@
 
 	ctx.SetOutputFiles(android.Paths{outputFile}, "")
 	txt.outputFile = outputFile
+
+	etc.SetCommonPrebuiltEtcInfo(ctx, txt)
 }
 
 func (txt *sanitizerLibrariesTxtModule) PrepareAndroidMKProviderInfo(config android.Config) *android.AndroidMkProviderInfo {
diff --git a/cc/stub_library.go b/cc/stub_library.go
index 9d7b5bc..21ef139 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -43,9 +43,9 @@
 }
 
 // Get target file name to be installed from this module
-func getInstalledFileName(ctx android.SingletonContext, m LinkableInterface) string {
+func getInstalledFileName(ctx android.SingletonContext, m android.ModuleProxy) string {
 	for _, ps := range android.OtherModuleProviderOrDefault(
-		ctx, m.Module(), android.InstallFilesProvider).PackagingSpecs {
+		ctx, m, android.InstallFilesProvider).PackagingSpecs {
 		if name := ps.FileName(); name != "" {
 			return name
 		}
@@ -57,18 +57,18 @@
 	// Visit all generated soong modules and store stub library file names.
 	stubLibraryMap := make(map[string]bool)
 	vendorStubLibraryMap := make(map[string]bool)
-	ctx.VisitAllModules(func(module android.Module) {
-		if m, ok := module.(VersionedLinkableInterface); ok {
-			if IsStubTarget(android.OtherModuleProviderOrDefault(ctx, m, LinkableInfoProvider)) {
-				if name := getInstalledFileName(ctx, m); name != "" {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if linkableInfo, ok := android.OtherModuleProvider(ctx, module, LinkableInfoProvider); ok {
+			if IsStubTarget(linkableInfo) {
+				if name := getInstalledFileName(ctx, module); name != "" {
 					stubLibraryMap[name] = true
-					if m.InVendor() {
+					if linkableInfo.InVendor {
 						vendorStubLibraryMap[name] = true
 					}
 				}
 			}
-			if m.CcLibraryInterface() && android.IsModulePreferred(m) {
-				if p := m.VersionedInterface().GetAPIListCoverageXMLPath().String(); p != "" {
+			if linkableInfo.CcLibraryInterface && android.IsModulePreferredProxy(ctx, module) {
+				if p := linkableInfo.APIListCoverageXMLPath.String(); p != "" {
 					s.apiListCoverageXmlPaths = append(s.apiListCoverageXmlPaths, p)
 				}
 			}
diff --git a/cc/test.go b/cc/test.go
index b3b2ae8..2c5c36e 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -418,6 +418,10 @@
 			if test.testConfig != nil {
 				ctx.InstallFile(testCases, ctx.ModuleName()+".config", test.testConfig)
 			}
+			dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+			if dynamicConfig.Valid() {
+				ctx.InstallFile(testCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path())
+			}
 			for _, extraTestConfig := range test.extraTestConfigs {
 				ctx.InstallFile(testCases, extraTestConfig.Base(), extraTestConfig)
 			}
diff --git a/ci_tests/Android.bp b/ci_tests/Android.bp
new file mode 100644
index 0000000..181ded4
--- /dev/null
+++ b/ci_tests/Android.bp
@@ -0,0 +1,21 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-ci-tests",
+    pkgPath: "android/soong/ci_tests",
+    deps: [
+        "blueprint",
+        "blueprint-proptools",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "ci_test_package_zip.go",
+    ],
+    testSrcs: [
+    ],
+    pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
+}
diff --git a/ci_tests/ci_test_package_zip.go b/ci_tests/ci_test_package_zip.go
new file mode 100644
index 0000000..d9573a3
--- /dev/null
+++ b/ci_tests/ci_test_package_zip.go
@@ -0,0 +1,237 @@
+// Copyright (C) 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ci_tests
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	pctx.Import("android/soong/android")
+	registerTestPackageZipBuildComponents(android.InitRegistrationContext)
+}
+
+func registerTestPackageZipBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("test_package", TestPackageZipFactory)
+}
+
+type testPackageZip struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties CITestPackageProperties
+
+	output android.Path
+}
+
+type CITestPackageProperties struct {
+	// test modules will be added as dependencies using the device os and the common architecture's variant.
+	Tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// test modules that will be added as dependencies based on the first supported arch variant and the device os variant
+	Device_first_tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// test modules that will be added as dependencies based on both 32bit and 64bit arch variant and the device os variant
+	Device_both_tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// test modules that will be added as dependencies based on host
+	Host_tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// git-main only test modules. Will only be added as dependencies using the device os and the common architecture's variant if exists.
+	Tests_if_exist_common proptools.Configurable[[]string] `android:"arch_variant"`
+	// git-main only test modules. Will only be added as dependencies based on both 32bit and 64bit arch variant and the device os variant if exists.
+	Tests_if_exist_device_both proptools.Configurable[[]string] `android:"arch_variant"`
+}
+
+type testPackageZipDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var testPackageZipDepTag testPackageZipDepTagType
+
+var (
+	pctx = android.NewPackageContext("android/soong/ci_tests")
+	// test_package module type should only be used for the following modules.
+	// TODO: remove "_soong" from the module names inside when eliminating the corresponding make modules
+	moduleNamesAllowed = []string{"continuous_native_tests_soong", "continuous_instrumentation_tests_soong", "platform_tests"}
+)
+
+func (p *testPackageZip) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// adding tests property deps
+	for _, t := range p.properties.Tests.GetOrDefault(ctx, nil) {
+		ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), testPackageZipDepTag, t)
+	}
+
+	// adding device_first_tests property deps
+	for _, t := range p.properties.Device_first_tests.GetOrDefault(ctx, nil) {
+		ctx.AddVariationDependencies(ctx.Config().AndroidFirstDeviceTarget.Variations(), testPackageZipDepTag, t)
+	}
+
+	// adding device_both_tests property deps
+	p.addDeviceBothDeps(ctx, false)
+
+	// adding host_tests property deps
+	for _, t := range p.properties.Host_tests.GetOrDefault(ctx, nil) {
+		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), testPackageZipDepTag, t)
+	}
+
+	// adding Tests_if_exist_* property deps
+	for _, t := range p.properties.Tests_if_exist_common.GetOrDefault(ctx, nil) {
+		if ctx.OtherModuleExists(t) {
+			ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), testPackageZipDepTag, t)
+		}
+	}
+	p.addDeviceBothDeps(ctx, true)
+}
+
+func (p *testPackageZip) addDeviceBothDeps(ctx android.BottomUpMutatorContext, checkIfExist bool) {
+	android32TargetList := android.FirstTarget(ctx.Config().Targets[android.Android], "lib32")
+	android64TargetList := android.FirstTarget(ctx.Config().Targets[android.Android], "lib64")
+	if len(android32TargetList) > 0 {
+		maybeAndroid32Target := &android32TargetList[0]
+		if checkIfExist {
+			for _, t := range p.properties.Tests_if_exist_device_both.GetOrDefault(ctx, nil) {
+				if ctx.OtherModuleExists(t) {
+					ctx.AddFarVariationDependencies(maybeAndroid32Target.Variations(), testPackageZipDepTag, t)
+				}
+			}
+		} else {
+			ctx.AddFarVariationDependencies(maybeAndroid32Target.Variations(), testPackageZipDepTag, p.properties.Device_both_tests.GetOrDefault(ctx, nil)...)
+		}
+	}
+	if len(android64TargetList) > 0 {
+		maybeAndroid64Target := &android64TargetList[0]
+		if checkIfExist {
+			for _, t := range p.properties.Tests_if_exist_device_both.GetOrDefault(ctx, nil) {
+				if ctx.OtherModuleExists(t) {
+					ctx.AddFarVariationDependencies(maybeAndroid64Target.Variations(), testPackageZipDepTag, t)
+				}
+			}
+		} else {
+			ctx.AddFarVariationDependencies(maybeAndroid64Target.Variations(), testPackageZipDepTag, p.properties.Device_both_tests.GetOrDefault(ctx, nil)...)
+		}
+	}
+}
+
+func TestPackageZipFactory() android.Module {
+	module := &testPackageZip{}
+
+	module.AddProperties(&module.properties)
+
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+
+	return module
+}
+
+func (p *testPackageZip) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if !android.InList(ctx.ModuleName(), moduleNamesAllowed) {
+		ctx.ModuleErrorf("%s is not allowed to use module type test_package")
+	}
+
+	p.output = createOutput(ctx, pctx)
+
+	ctx.SetOutputFiles(android.Paths{p.output}, "")
+
+	// dist the test output
+	if ctx.ModuleName() == "platform_tests_soong" {
+		distedName := ctx.Config().Getenv("TARGET_PRODUCT") + "-tests-" + ctx.Config().BuildId() + ".zip"
+		ctx.DistForGoalsWithFilename([]string{"droid", "platform_tests"}, p.output, distedName)
+	}
+}
+
+func createOutput(ctx android.ModuleContext, pctx android.PackageContext) android.ModuleOutPath {
+	productOut := filepath.Join(ctx.Config().OutDir(), "target", "product", ctx.Config().DeviceName())
+	stagingDir := android.PathForModuleOut(ctx, "STAGING")
+	productVariables := ctx.Config().ProductVariables()
+	arch := proptools.String(productVariables.DeviceArch)
+	secondArch := proptools.String(productVariables.DeviceSecondaryArch)
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Text("rm").Flag("-rf").Text(stagingDir.String())
+	builder.Command().Text("mkdir").Flag("-p").Output(stagingDir)
+	builder.Temporary(stagingDir)
+	ctx.VisitDirectDepsWithTag(testPackageZipDepTag, func(m android.Module) {
+		info, ok := android.OtherModuleProvider(ctx, m, android.ModuleInfoJSONProvider)
+		if !ok {
+			ctx.OtherModuleErrorf(m, "doesn't set ModuleInfoJSON provider")
+		} else if len(info) != 1 {
+			ctx.OtherModuleErrorf(m, "doesn't provide exactly one ModuleInfoJSON")
+		}
+
+		classes := info[0].GetClass()
+		if len(info[0].Class) != 1 {
+			ctx.OtherModuleErrorf(m, "doesn't have exactly one class in its ModuleInfoJSON")
+		}
+		class := strings.ToLower(classes[0])
+		if class == "apps" {
+			class = "app"
+		} else if class == "java_libraries" {
+			class = "framework"
+		}
+
+		installedFilesInfo, ok := android.OtherModuleProvider(ctx, m, android.InstallFilesProvider)
+		if !ok {
+			ctx.ModuleErrorf("Module %s doesn't set InstallFilesProvider", m.Name())
+		}
+
+		for _, installedFile := range installedFilesInfo.InstallFiles {
+			name := removeFileExtension(installedFile.Base())
+			f := strings.TrimPrefix(installedFile.String(), productOut+"/")
+			if strings.HasPrefix(f, "out") {
+				continue
+			}
+			f = strings.ReplaceAll(f, "system/", "DATA/")
+			f = strings.ReplaceAll(f, filepath.Join("testcases", name, arch), filepath.Join("DATA", class, name))
+			f = strings.ReplaceAll(f, filepath.Join("testcases", name, secondArch), filepath.Join("DATA", class, name))
+			f = strings.ReplaceAll(f, "testcases", filepath.Join("DATA", class))
+			f = strings.ReplaceAll(f, "data/", "DATA/")
+			f = strings.ReplaceAll(f, "DATA_other", "system_other")
+			f = strings.ReplaceAll(f, "system_other/DATA", "system_other/system")
+			dir := filepath.Dir(f)
+			tempOut := android.PathForModuleOut(ctx, "STAGING", f)
+			builder.Command().Text("mkdir").Flag("-p").Text(filepath.Join(stagingDir.String(), dir))
+			builder.Command().Text("cp").Flag("-Rf").Input(installedFile).Output(tempOut)
+			builder.Temporary(tempOut)
+		}
+	})
+
+	output := android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
+	builder.Command().
+		BuiltTool("soong_zip").
+		Flag("-o").Output(output).
+		Flag("-C").Text(stagingDir.String()).
+		Flag("-D").Text(stagingDir.String())
+	builder.Command().Text("rm").Flag("-rf").Text(stagingDir.String())
+	builder.Build("test_package", fmt.Sprintf("build test_package for %s", ctx.ModuleName()))
+	return output
+}
+
+func removeFileExtension(filename string) string {
+	return strings.TrimSuffix(filename, filepath.Ext(filename))
+}
+
+// The only purpose of this method is to make sure we can build the module directly
+// without adding suffix "-soong"
+func (p *testPackageZip) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{
+		android.AndroidMkEntries{
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(p.output),
+		},
+	}
+}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index e882470..1452b41 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -284,7 +284,8 @@
 		clcTargetString := "PCL[" + strings.Join(clcTarget, ":") + "]"
 
 		if systemServerClasspathJars.ContainsJar(module.Name) {
-			checkSystemServerOrder(ctx, jarIndex)
+			// TODO(b/397461231): renable this check
+			//checkSystemServerOrder(ctx, jarIndex)
 		} else {
 			// Standalone jars are loaded by separate class loaders with SYSTEMSERVERCLASSPATH as the
 			// parent.
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index bf54c09..a440c91 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -32,6 +32,7 @@
 	"path/filepath"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -90,6 +91,15 @@
 
 }
 
+type PrebuiltEtcInfo struct {
+	// Returns the base install directory, such as "etc", "usr/share".
+	BaseDir string
+	// Returns the sub install directory relative to BaseDir().
+	SubDir string
+}
+
+var PrebuiltEtcInfoProvider = blueprint.NewProvider[PrebuiltEtcInfo]()
+
 var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterPrebuiltEtcBuildComponents)
 
 type PrebuiltEtcProperties struct {
@@ -502,6 +512,15 @@
 	p.updateModuleInfoJSON(ctx)
 
 	ctx.SetOutputFiles(p.outputFilePaths.Paths(), "")
+
+	SetCommonPrebuiltEtcInfo(ctx, p)
+}
+
+func SetCommonPrebuiltEtcInfo(ctx android.ModuleContext, p PrebuiltEtcModule) {
+	android.SetProvider(ctx, PrebuiltEtcInfoProvider, PrebuiltEtcInfo{
+		BaseDir: p.BaseDir(),
+		SubDir:  p.SubDir(),
+	})
 }
 
 func (p *PrebuiltEtc) updateModuleInfoJSON(ctx android.ModuleContext) {
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index 8d7f92f..959ef37 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -15,7 +15,9 @@
 package filesystem
 
 import (
+	"cmp"
 	"fmt"
+	"slices"
 	"strings"
 	"sync/atomic"
 
@@ -212,7 +214,7 @@
 
 	allImagesZip := android.PathForModuleOut(ctx, "all_images.zip")
 	allImagesZipBuilder := android.NewRuleBuilder(pctx, ctx)
-	cmd := allImagesZipBuilder.Command().BuiltTool("soong_zip").Flag("--sort_entries")
+	cmd := allImagesZipBuilder.Command().BuiltTool("soong_zip")
 	for _, dep := range deps {
 		cmd.FlagWithArg("-e ", dep.Base())
 		cmd.FlagWithInput("-f ", dep)
@@ -247,6 +249,38 @@
 	a.distFiles(ctx)
 }
 
+// Returns a list of modules that are installed, which are collected from the dependency
+// filesystem and super_image modules.
+func (a *androidDevice) allInstalledModules(ctx android.ModuleContext) []android.Module {
+	fsInfoMap := a.getFsInfos(ctx)
+	allOwners := make(map[string][]string)
+	for _, partition := range android.SortedKeys(fsInfoMap) {
+		fsInfo := fsInfoMap[partition]
+		for _, owner := range fsInfo.Owners {
+			allOwners[owner.Name] = append(allOwners[owner.Name], owner.Variation)
+		}
+	}
+
+	ret := []android.Module{}
+	ctx.WalkDepsProxy(func(mod, _ android.ModuleProxy) bool {
+		if variations, ok := allOwners[mod.Name()]; ok && android.InList(ctx.OtherModuleSubDir(mod), variations) {
+			ret = append(ret, mod)
+		}
+		return true
+	})
+
+	// Remove duplicates
+	ret = android.FirstUniqueFunc(ret, func(a, b android.Module) bool {
+		return a.String() == b.String()
+	})
+
+	// Sort the modules by their names and variants
+	slices.SortFunc(ret, func(a, b android.Module) int {
+		return cmp.Compare(a.String(), b.String())
+	})
+	return ret
+}
+
 func (a *androidDevice) distFiles(ctx android.ModuleContext) {
 	if !ctx.Config().KatiEnabled() {
 		if proptools.Bool(a.deviceProps.Main_device) {
@@ -302,11 +336,6 @@
 		targetFilesZipCopy{a.partitionProps.Init_boot_partition_name, "INIT_BOOT/RAMDISK"},
 		targetFilesZipCopy{a.partitionProps.Vendor_boot_partition_name, "VENDOR_BOOT/RAMDISK"},
 	}
-	// TODO: Handle cases where recovery files are copied to BOOT/ or RECOVERY/
-	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=6211-6219?q=core%2FMakefile&ss=android%2Fplatform%2Fsuperproject%2Fmain
-	if ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() {
-		toCopy = append(toCopy, targetFilesZipCopy{a.partitionProps.Recovery_partition_name, "VENDOR_BOOT/RAMDISK"})
-	}
 
 	filesystemsToCopy := []targetFilesystemZipCopy{}
 	for _, zipCopy := range toCopy {
@@ -322,7 +351,7 @@
 	if a.partitionProps.Super_partition_name != nil {
 		superPartition := ctx.GetDirectDepProxyWithTag(*a.partitionProps.Super_partition_name, superPartitionDepTag)
 		if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok {
-			for _, partition := range android.SortedStringKeys(info.SubImageInfo) {
+			for _, partition := range android.SortedKeys(info.SubImageInfo) {
 				filesystemsToCopy = append(
 					filesystemsToCopy,
 					targetFilesystemZipCopy{info.SubImageInfo[partition], strings.ToUpper(partition)},
@@ -343,6 +372,12 @@
 			BuiltTool("acp").
 			Textf("-rd %s/. %s/%s", rootDirString, targetFilesDir, toCopy.destSubdir).
 			Implicit(toCopy.fsInfo.Output) // so that the staging dir is built
+		for _, extraRootDir := range toCopy.fsInfo.ExtraRootDirs {
+			builder.Command().
+				BuiltTool("acp").
+				Textf("-rd %s/. %s/%s", extraRootDir, targetFilesDir, toCopy.destSubdir).
+				Implicit(toCopy.fsInfo.Output) // so that the staging dir is built
+		}
 
 		if toCopy.destSubdir == "SYSTEM" {
 			// Create the ROOT partition in target_files.zip
diff --git a/filesystem/avb_add_hash_footer.go b/filesystem/avb_add_hash_footer.go
index f32993c..c1e03cb 100644
--- a/filesystem/avb_add_hash_footer.go
+++ b/filesystem/avb_add_hash_footer.go
@@ -149,6 +149,8 @@
 	a.installDir = android.PathForModuleInstall(ctx, "etc")
 	ctx.InstallFile(a.installDir, a.installFileName(), output)
 	a.output = output
+
+	setCommonFilesystemInfo(ctx, a)
 }
 
 func addAvbProp(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, prop avbProp) {
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 6d6c15c..effbd65 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -230,6 +230,8 @@
 		ramdiskModule := ctx.GetDirectDepWithTag(ramdisk, bootimgRamdiskDep)
 		fsInfo, _ := android.OtherModuleProvider(ctx, ramdiskModule, FilesystemProvider)
 		android.SetProvider(ctx, FilesystemProvider, fsInfo)
+	} else {
+		setCommonFilesystemInfo(ctx, b)
 	}
 
 	// Set BootimgInfo for building target_files.zip
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index f84993d..1ce6131 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -384,9 +384,17 @@
 	Json android.Path
 }
 
+type InstalledModuleInfo struct {
+	Name      string
+	Variation string
+}
+
 type FilesystemInfo struct {
 	// The built filesystem image
 	Output android.Path
+	// Returns the output file that is signed by avbtool. If this module is not signed, returns
+	// nil.
+	SignedOutputPath android.Path
 	// An additional hermetic filesystem image.
 	// e.g. this will contain inodes with pinned timestamps.
 	// This will be copied to target_files.zip
@@ -397,6 +405,9 @@
 	// to add a dependency on the Output file, as you cannot add dependencies on directories
 	// in ninja.
 	RootDir android.Path
+	// Extra root directories that are also built into the partition. Currently only used for
+	// including the recovery partition files into the vendor_boot image.
+	ExtraRootDirs android.Paths
 	// The rebased staging directory used to build the output filesystem. If consuming this, make
 	// sure to add a dependency on the Output file, as you cannot add dependencies on directories
 	// in ninja. In many cases this is the same as RootDir, only in the system partition is it
@@ -428,6 +439,8 @@
 	SelinuxFc android.Path
 
 	FilesystemConfig android.Path
+
+	Owners []InstalledModuleInfo
 }
 
 // FullInstallPathInfo contains information about the "full install" paths of all the files
@@ -587,7 +600,8 @@
 	specs := f.gatherFilteredPackagingSpecs(ctx)
 
 	var fullInstallPaths []FullInstallPathInfo
-	for _, spec := range specs {
+	for _, specRel := range android.SortedKeys(specs) {
+		spec := specs[specRel]
 		fullInstallPaths = append(fullInstallPaths, FullInstallPathInfo{
 			FullInstallPath:     spec.FullInstallPath(),
 			RequiresFullInstall: spec.RequiresFullInstall(),
@@ -612,6 +626,7 @@
 	var outputHermetic android.WritablePath
 	var buildImagePropFile android.Path
 	var buildImagePropFileDeps android.Paths
+	var extraRootDirs android.Paths
 	switch f.fsType(ctx) {
 	case ext4Type, erofsType, f2fsType:
 		buildImagePropFile, buildImagePropFileDeps = f.buildPropFile(ctx)
@@ -625,9 +640,9 @@
 		f.buildImageUsingBuildImage(ctx, hermeticBuilder, buildImageParams{rootDir, propFileHermetic, buildImagePropFileDeps, outputHermetic})
 		mapFile = f.getMapFile(ctx)
 	case compressedCpioType:
-		f.output = f.buildCpioImage(ctx, builder, rootDir, true)
+		f.output, extraRootDirs = f.buildCpioImage(ctx, builder, rootDir, true)
 	case cpioType:
-		f.output = f.buildCpioImage(ctx, builder, rootDir, false)
+		f.output, extraRootDirs = f.buildCpioImage(ctx, builder, rootDir, false)
 	default:
 		return
 	}
@@ -655,10 +670,12 @@
 	}
 
 	fsInfo := FilesystemInfo{
-		Output:                 f.output,
+		Output:                 f.OutputPath(),
+		SignedOutputPath:       f.SignedOutputPath(),
 		OutputHermetic:         outputHermetic,
 		FileListFile:           fileListFile,
 		RootDir:                rootDir,
+		ExtraRootDirs:          extraRootDirs,
 		RebasedDir:             rebasedDir,
 		MapFile:                mapFile,
 		ModuleName:             ctx.ModuleName(),
@@ -673,6 +690,7 @@
 		ErofsCompressHints: erofsCompressHints,
 		SelinuxFc:          f.selinuxFc,
 		FilesystemConfig:   f.generateFilesystemConfig(ctx, rootDir, rebasedDir),
+		Owners:             f.gatherOwners(specs),
 	}
 
 	android.SetProvider(ctx, FilesystemProvider, fsInfo)
@@ -1157,7 +1175,7 @@
 	builder *android.RuleBuilder,
 	rootDir android.OutputPath,
 	compressed bool,
-) android.Path {
+) (android.Path, android.Paths) {
 	if proptools.Bool(f.properties.Use_avb) {
 		ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+
 			"Consider adding this to bootimg module and signing the entire boot image.")
@@ -1197,7 +1215,7 @@
 	// rootDir is not deleted. Might be useful for quick inspection.
 	builder.Build("build_cpio_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
 
-	return output
+	return output, rootDirs
 }
 
 var validPartitions = []string{
@@ -1326,6 +1344,18 @@
 	return f.PackagingBase.GatherPackagingSpecsWithFilterAndModifier(ctx, f.filesystemBuilder.FilterPackagingSpec, f.filesystemBuilder.ModifyPackagingSpec)
 }
 
+func (f *filesystem) gatherOwners(specs map[string]android.PackagingSpec) []InstalledModuleInfo {
+	var owners []InstalledModuleInfo
+	for _, p := range android.SortedKeys(specs) {
+		spec := specs[p]
+		owners = append(owners, InstalledModuleInfo{
+			Name:      spec.Owner(),
+			Variation: spec.Variation(),
+		})
+	}
+	return owners
+}
+
 // Dexpreopt files are installed to system_other. Collect the packaingSpecs for the dexpreopt files
 // from this partition to export to the system_other partition later.
 func (f *filesystem) systemOtherFiles(ctx android.ModuleContext) map[string]android.PackagingSpec {
@@ -1499,3 +1529,10 @@
 		ctx.StrictRaw("SOONG_DEFINED_SYSTEM_IMAGE_PATH", f.output.String())
 	}
 }
+
+func setCommonFilesystemInfo(ctx android.ModuleContext, m Filesystem) {
+	android.SetProvider(ctx, FilesystemProvider, FilesystemInfo{
+		Output:           m.OutputPath(),
+		SignedOutputPath: m.SignedOutputPath(),
+	})
+}
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index d0888a9..1fd2e76 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -198,6 +198,8 @@
 
 	ctx.SetOutputFiles([]android.Path{output}, "")
 	l.output = output
+
+	setCommonFilesystemInfo(ctx, l)
 }
 
 // Add a rule that converts the filesystem for the given partition to the given rule builder. The
diff --git a/filesystem/raw_binary.go b/filesystem/raw_binary.go
index 707fba0..6ca155a 100644
--- a/filesystem/raw_binary.go
+++ b/filesystem/raw_binary.go
@@ -88,6 +88,8 @@
 
 	ctx.SetOutputFiles([]android.Path{outputFile}, "")
 	r.output = outputFile
+
+	setCommonFilesystemInfo(ctx, r)
 }
 
 var _ android.AndroidMkEntriesProvider = (*rawBinary)(nil)
diff --git a/filesystem/system_other.go b/filesystem/system_other.go
index 1c00dd3..5309e90 100644
--- a/filesystem/system_other.go
+++ b/filesystem/system_other.go
@@ -23,6 +23,12 @@
 	"github.com/google/blueprint/proptools"
 )
 
+var (
+	systemOtherPropFileTweaks = pctx.AndroidStaticRule("system_other_prop_file_tweaks", blueprint.RuleParams{
+		Command: `rm -rf $out && sed -e 's@^mount_point=/$$@mount_point=system_other@g' -e 's@^partition_name=system$$@partition_name=system_other@g' $in > $out`,
+	})
+)
+
 type SystemOtherImageProperties struct {
 	// The system_other image always requires a reference to the system image. The system_other
 	// partition gets built into the system partition's "b" slot in a/b partition builds. Thus, it
@@ -123,12 +129,23 @@
 	fec := ctx.Config().HostToolPath(ctx, "fec")
 	pathToolDirs := []string{filepath.Dir(fec.String())}
 
+	// In make, the exact same prop file is used for both system and system_other. However, I
+	// believe make goes through a different build_image code path that is based on the name of
+	// the output file. So it sees the output file is named system_other.img and makes some changes.
+	// We don't use that codepath, so make the changes manually to the prop file.
+	propFile := android.PathForModuleOut(ctx, "prop")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   systemOtherPropFileTweaks,
+		Input:  systemInfo.BuildImagePropFile,
+		Output: propFile,
+	})
+
 	builder = android.NewRuleBuilder(pctx, ctx)
 	builder.Command().
 		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
 		BuiltTool("build_image").
 		Text(stagingDir.String()). // input directory
-		Input(systemInfo.BuildImagePropFile).
+		Input(propFile).
 		Implicits(systemInfo.BuildImagePropFileDeps).
 		Implicit(fec).
 		Implicit(stagingDirTimestamp).
@@ -140,7 +157,7 @@
 	// Create a hermetic system_other.img with pinned timestamps
 	builder = android.NewRuleBuilder(pctx, ctx)
 	outputHermetic := android.PathForModuleOut(ctx, "for_target_files", "system_other.img")
-	outputHermeticPropFile := m.propFileForHermeticImg(ctx, builder, systemInfo.BuildImagePropFile)
+	outputHermeticPropFile := m.propFileForHermeticImg(ctx, builder, propFile)
 	builder.Command().
 		Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
 		BuiltTool("build_image").
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index e5809d3..01b453e 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -306,6 +306,8 @@
 
 	ctx.SetOutputFiles([]android.Path{output}, "")
 	v.output = output
+
+	setCommonFilesystemInfo(ctx, v)
 }
 
 // Returns the embedded shell command that prints the rollback index
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index c2721d2..e2485a1 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -377,7 +377,7 @@
 	if modName := partitions.nameForType("userdata"); modName != "" {
 		partitionProps.Userdata_partition_name = proptools.StringPtr(modName)
 	}
-	if modName := partitions.nameForType("recovery"); modName != "" {
+	if modName := partitions.nameForType("recovery"); modName != "" && !ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() {
 		partitionProps.Recovery_partition_name = proptools.StringPtr(modName)
 	}
 	if modName := partitions.nameForType("system_dlkm"); modName != "" && !android.InList("system_dlkm", superImageSubPartitions) {
diff --git a/java/aar.go b/java/aar.go
index f7c5c13..976e4fc 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -217,7 +217,7 @@
 }
 
 func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool {
-	return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault()) &&
+	return BoolDefault(a.aaptProperties.Use_resource_processor, true) &&
 		// TODO(b/331641946): remove this when ResourceProcessorBusyBox supports generating shared libraries.
 		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib") &&
 		// Use the legacy resource processor in kythe builds.
@@ -1479,12 +1479,7 @@
 	completeStaticLibsResourceJars := depset.New(depset.PREORDER, nil, transitiveStaticLibsResourceJars)
 
 	var implementationJarFile android.Path
-	var combineJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		combineJars = completeStaticLibsImplementationJars.ToList()
-	} else {
-		combineJars = append(android.Paths{classpathFile}, staticJars...)
-	}
+	combineJars := completeStaticLibsImplementationJars.ToList()
 
 	if len(combineJars) > 1 {
 		implementationJarOutputPath := android.PathForModuleOut(ctx, "combined", jarName)
@@ -1495,12 +1490,8 @@
 	}
 
 	var resourceJarFile android.Path
-	var resourceJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		resourceJars = completeStaticLibsResourceJars.ToList()
-	} else {
-		resourceJars = staticResourceJars
-	}
+	resourceJars := completeStaticLibsResourceJars.ToList()
+
 	if len(resourceJars) > 1 {
 		combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
 		TransformJarsToJar(ctx, combinedJar, "for resources", resourceJars, android.OptionalPath{},
@@ -1511,12 +1502,8 @@
 	}
 
 	// merge implementation jar with resources if necessary
-	var implementationAndResourcesJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		implementationAndResourcesJars = append(slices.Clone(resourceJars), combineJars...)
-	} else {
-		implementationAndResourcesJars = android.PathsIfNonNil(resourceJarFile, implementationJarFile)
-	}
+	implementationAndResourcesJars := append(slices.Clone(resourceJars), combineJars...)
+
 	var implementationAndResourcesJar android.Path
 	if len(implementationAndResourcesJars) > 1 {
 		combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
@@ -1531,12 +1518,7 @@
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
 	a.implementationAndResourcesJarFile = implementationAndResourcesJar.WithoutRel()
 
-	var headerJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		headerJars = completeStaticLibsHeaderJars.ToList()
-	} else {
-		headerJars = append(android.Paths{classpathFile}, staticHeaderJars...)
-	}
+	headerJars := completeStaticLibsHeaderJars.ToList()
 	if len(headerJars) > 1 {
 		headerJarFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
 		TransformJarsToJar(ctx, headerJarFile, "combine header jars", headerJars, android.OptionalPath{}, false, nil, nil)
@@ -1545,12 +1527,7 @@
 		a.headerJarFile = headerJars[0]
 	}
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		ctx.CheckbuildFile(classpathFile)
-	} else {
-		ctx.CheckbuildFile(a.headerJarFile)
-		ctx.CheckbuildFile(a.implementationJarFile)
-	}
+	ctx.CheckbuildFile(classpathFile)
 
 	javaInfo := &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(a.headerJarFile),
diff --git a/java/app.go b/java/app.go
index 89d688d..9b10bf3 100644
--- a/java/app.go
+++ b/java/app.go
@@ -72,6 +72,16 @@
 	EmbeddedJNILibs android.Paths
 
 	MergedManifestFile android.Path
+
+	Prebuilt                      bool
+	AppSet                        bool
+	Privileged                    bool
+	OutputFile                    android.Path
+	InstallApkName                string
+	JacocoReportClassesFile       android.Path
+	Certificate                   Certificate
+	PrivAppAllowlist              android.OptionalPath
+	OverriddenManifestPackageName *string
 }
 
 var AppInfoProvider = blueprint.NewProvider[*AppInfo]()
@@ -401,10 +411,12 @@
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
 		TestOnly: true,
 	})
-	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
+	appInfo := &AppInfo{
 		Updatable:     Bool(a.appProperties.Updatable),
 		TestHelperApp: true,
-	})
+	}
+	setCommonAppInfo(appInfo, a)
+	android.SetProvider(ctx, AppInfoProvider, appInfo)
 
 	moduleInfoJSON := ctx.ModuleInfoJSON()
 	moduleInfoJSON.Tags = append(moduleInfoJSON.Tags, "tests")
@@ -428,12 +440,16 @@
 			embeddedJniLibs = append(embeddedJniLibs, jni.path)
 		}
 	}
-	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
-		Updatable:          Bool(a.appProperties.Updatable),
-		TestHelperApp:      false,
-		EmbeddedJNILibs:    embeddedJniLibs,
-		MergedManifestFile: a.mergedManifest,
-	})
+	overriddenName := a.OverriddenManifestPackageName()
+	appInfo := &AppInfo{
+		Updatable:                     Bool(a.appProperties.Updatable),
+		TestHelperApp:                 false,
+		EmbeddedJNILibs:               embeddedJniLibs,
+		MergedManifestFile:            a.mergedManifest,
+		OverriddenManifestPackageName: &overriddenName,
+	}
+	setCommonAppInfo(appInfo, a)
+	android.SetProvider(ctx, AppInfoProvider, appInfo)
 
 	a.requiredModuleNames = a.getRequiredModuleNames(ctx)
 }
@@ -1627,6 +1643,10 @@
 		if a.testConfig != nil {
 			ctx.InstallFile(pathInTestCases, ctx.Module().Name()+".config", a.testConfig)
 		}
+		dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+		if dynamicConfig.Valid() {
+			ctx.InstallFile(pathInTestCases, ctx.Module().Name()+".dynamic", dynamicConfig.Path())
+		}
 		testDeps := append(a.data, a.extraTestConfigs...)
 		for _, data := range android.SortedUniquePaths(testDeps) {
 			dataPath := android.DataPath{SrcPath: data}
@@ -2044,7 +2064,7 @@
 			if _, ok := android.OtherModuleProvider(ctx, m, SdkLibraryInfoProvider); ok {
 				// Skip java_sdk_library dependencies that provide stubs, but not an implementation.
 				// This will be restricted to optional_uses_libs
-				if tag == usesLibOptTag && lib.DexJarBuildPath.PathOrNil() == nil {
+				if tag == usesLibOptTag && javaInfo.DexJarBuildPath.PathOrNil() == nil {
 					u.shouldDisableDexpreopt = true
 					return
 				}
@@ -2054,7 +2074,7 @@
 				libName = *ulib.ProvidesUsesLib
 			}
 			clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional,
-				lib.DexJarBuildPath.PathOrNil(), lib.DexJarInstallPath,
+				javaInfo.DexJarBuildPath.PathOrNil(), lib.DexJarInstallPath,
 				lib.ClassLoaderContexts)
 		} else if ctx.Config().AllowMissingDependencies() {
 			ctx.AddMissingDependencies([]string{dep})
@@ -2147,3 +2167,29 @@
 	classLoaderContexts *dexpreopt.ClassLoaderContextMap) {
 	u.verifyUsesLibraries(ctx, apk, nil, classLoaderContexts) // for APKs manifest_check does not write output file
 }
+
+// androidApp is an interface to handle all app modules (android_app, android_app_import, etc.) in
+// the same way.
+type androidApp interface {
+	android.Module
+	Privileged() bool
+	InstallApkName() string
+	OutputFile() android.Path
+	JacocoReportClassesFile() android.Path
+	Certificate() Certificate
+	BaseModuleName() string
+	PrivAppAllowlist() android.OptionalPath
+}
+
+var _ androidApp = (*AndroidApp)(nil)
+var _ androidApp = (*AndroidAppImport)(nil)
+var _ androidApp = (*AndroidTestHelperApp)(nil)
+
+func setCommonAppInfo(appInfo *AppInfo, m androidApp) {
+	appInfo.Privileged = m.Privileged()
+	appInfo.OutputFile = m.OutputFile()
+	appInfo.InstallApkName = m.InstallApkName()
+	appInfo.JacocoReportClassesFile = m.JacocoReportClassesFile()
+	appInfo.Certificate = m.Certificate()
+	appInfo.PrivAppAllowlist = m.PrivAppAllowlist()
+}
diff --git a/java/app_import.go b/java/app_import.go
index 919266f..37c673c 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -354,6 +354,12 @@
 
 func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.generateAndroidBuildActions(ctx)
+
+	appInfo := &AppInfo{
+		Prebuilt: true,
+	}
+	setCommonAppInfo(appInfo, a)
+	android.SetProvider(ctx, AppInfoProvider, appInfo)
 }
 
 func (a *AndroidAppImport) InstallApkName() string {
diff --git a/java/app_set.go b/java/app_set.go
index 7997570..2e9d314 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -192,6 +192,11 @@
 		},
 	)
 
+	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
+		AppSet:     true,
+		Privileged: as.Privileged(),
+		OutputFile: as.OutputFile(),
+	})
 }
 
 func (as *AndroidAppSet) InstallBypassMake() bool { return true }
diff --git a/java/app_test.go b/java/app_test.go
index 4f23f61..5f5f04d 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -887,15 +887,24 @@
 			},
 			appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -908,21 +917,23 @@
 			directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"},
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
 			transitiveOverlays:  nil,
 			transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			transitiveSrcJars:   []string{"out/soong/.intermediates/transitive/android_common/gen/android/R.srcjar"},
-			transitiveClasspath: []string{"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar"},
+			transitiveClasspath: []string{"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar"},
 			transitiveCombined:  nil,
 
 			sharedResources: nil,
@@ -936,9 +947,9 @@
 			},
 			sharedSrcJars: []string{"out/soong/.intermediates/shared/android_common/gen/android/R.srcjar"},
 			sharedClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine-combined/shared_transitive_shared.jar",
-				"out/soong/.intermediates/shared_transitive_static/android_common/turbine-combined/shared_transitive_static.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine/shared_transitive_shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
 			},
 			sharedCombined: []string{
 				"out/soong/.intermediates/shared/android_common/javac/shared.jar",
@@ -985,17 +996,26 @@
 			},
 			appSrcJars: nil,
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -1008,18 +1028,20 @@
 			},
 			directSrcJars: nil,
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/direct/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
@@ -1027,7 +1049,7 @@
 			transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			transitiveSrcJars:   nil,
 			transitiveClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
 			},
 			transitiveCombined: nil,
@@ -1041,12 +1063,12 @@
 			},
 			sharedSrcJars: nil,
 			sharedClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/shared/android_common/busybox/R.jar",
 				"out/soong/.intermediates/shared_transitive_static/android_common/busybox/R.jar",
 				"out/soong/.intermediates/shared_transitive_shared/android_common/busybox/R.jar",
-				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine-combined/shared_transitive_shared.jar",
-				"out/soong/.intermediates/shared_transitive_static/android_common/turbine-combined/shared_transitive_static.jar",
+				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine/shared_transitive_shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
 			},
 			sharedCombined: []string{
 				"out/soong/.intermediates/shared/android_common/javac/shared.jar",
@@ -1090,18 +1112,27 @@
 			},
 			appSrcJars: nil,
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				// R.jar has to come before direct.jar
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			dontVerifyDirect:           true,
@@ -1133,15 +1164,24 @@
 			},
 			appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -1154,17 +1194,19 @@
 			},
 			directSrcJars: nil,
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/direct/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			dontVerifyTransitive:       true,
@@ -1195,15 +1237,24 @@
 			},
 			appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -1216,14 +1267,16 @@
 			directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"},
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
@@ -1231,7 +1284,7 @@
 			transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			transitiveSrcJars:   nil,
 			transitiveClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
 			},
 			transitiveCombined: nil,
@@ -1480,7 +1533,6 @@
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
 				{"lib", "android_common"}: {
-					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 					"device/vendor/blah/overlay/lib/res/values/strings.xml",
 				},
@@ -1515,12 +1567,10 @@
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
 				{"lib", "android_common"}: {
-					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 					"device/vendor/blah/overlay/lib/res/values/strings.xml",
 				},
 				{"lib", "android_common_rro"}: {
-					"out/soong/.intermediates/lib2/android_common_rro/package-res.apk",
 					"lib/res/res/values/strings.xml",
 				},
 			},
@@ -1560,7 +1610,6 @@
 				},
 				{"bar", "android_common"}: {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
 				{"lib", "android_common"}: {
-					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 				},
 			},
@@ -3000,14 +3049,14 @@
 
 	// Verify baz, which depends on the overridden module foo, has the correct classpath javac arg.
 	javac := ctx.ModuleForTests(t, "baz", "android_common").Rule("javac")
-	fooTurbine := "out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar"
+	fooTurbine := "out/soong/.intermediates/foo/android_common/turbine/foo.jar"
 	if !strings.Contains(javac.Args["classpath"], fooTurbine) {
 		t.Errorf("baz classpath %v does not contain %q", javac.Args["classpath"], fooTurbine)
 	}
 
 	// Verify qux, which depends on the overriding module bar, has the correct classpath javac arg.
 	javac = ctx.ModuleForTests(t, "qux", "android_common").Rule("javac")
-	barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine-combined/foo.jar"
+	barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine/foo.jar"
 	if !strings.Contains(javac.Args["classpath"], barTurbine) {
 		t.Errorf("qux classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
 	}
@@ -3084,7 +3133,7 @@
 
 		// Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
 		javac := variant.Rule("javac")
-		turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
+		turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine", "foo.jar")
 		if !strings.Contains(javac.Args["classpath"], turbine) {
 			t.Errorf("classpath %q does not contain %q", javac.Args["classpath"], turbine)
 		}
diff --git a/java/base.go b/java/base.go
index 21ad73f..0833831 100644
--- a/java/base.go
+++ b/java/base.go
@@ -658,11 +658,11 @@
 	// See rank() for details.
 	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(module)
-		_, isJavaLibrary := android.OtherModuleProvider(ctx, module, JavaLibraryInfoProvider)
+		libInfo, isJavaLibrary := android.OtherModuleProvider(ctx, module, JavaLibraryInfoProvider)
 		_, isAndroidLibrary := android.OtherModuleProvider(ctx, module, AndroidLibraryInfoProvider)
 		_, isJavaAconfigLibrary := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider)
 		// Exclude java_aconfig_library modules to maintain consistency with existing behavior.
-		if (isJavaLibrary && !isJavaAconfigLibrary) || isAndroidLibrary {
+		if (isJavaLibrary && !libInfo.Prebuilt && !isJavaAconfigLibrary) || isAndroidLibrary {
 			// TODO(satayev): cover other types as well, e.g. imports
 			switch tag {
 			case bootClasspathTag, sdkLibTag, libTag, staticLibTag, java9LibTag:
@@ -1298,15 +1298,11 @@
 		}
 		j.headerJarFile = combinedHeaderJarFile
 
-		if ctx.Config().UseTransitiveJarsInClasspath() {
-			if len(localHeaderJars) > 0 {
-				ctx.CheckbuildFile(localHeaderJars...)
-			} else {
-				// There are no local sources or resources in this module, so there is nothing to checkbuild.
-				ctx.UncheckedModule()
-			}
+		if len(localHeaderJars) > 0 {
+			ctx.CheckbuildFile(localHeaderJars...)
 		} else {
-			ctx.CheckbuildFile(j.headerJarFile)
+			// There are no local sources or resources in this module, so there is nothing to checkbuild.
+			ctx.UncheckedModule()
 		}
 
 		j.outputFile = j.headerJarFile
@@ -1604,12 +1600,7 @@
 	completeStaticLibsResourceJars := depset.New(depset.PREORDER, localResourceJars, deps.transitiveStaticLibsResourceJars)
 
 	var combinedResourceJar android.Path
-	var resourceJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		resourceJars = completeStaticLibsResourceJars.ToList()
-	} else {
-		resourceJars = append(slices.Clone(localResourceJars), deps.staticResourceJars...)
-	}
+	resourceJars := completeStaticLibsResourceJars.ToList()
 	if len(resourceJars) == 1 {
 		combinedResourceJar = resourceJars[0]
 	} else if len(resourceJars) > 0 {
@@ -1630,12 +1621,7 @@
 
 	completeStaticLibsImplementationJars := depset.New(depset.PREORDER, localImplementationJars, deps.transitiveStaticLibsImplementationJars)
 
-	var jars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		jars = completeStaticLibsImplementationJars.ToList()
-	} else {
-		jars = append(slices.Clone(localImplementationJars), deps.staticJars...)
-	}
+	jars := completeStaticLibsImplementationJars.ToList()
 
 	jars = append(jars, extraDepCombinedJars...)
 
@@ -1766,7 +1752,7 @@
 		headerJarFile := android.PathForModuleOut(ctx, "javac-header", jarName)
 		convertImplementationJarToHeaderJar(ctx, j.implementationJarFile, headerJarFile)
 		j.headerJarFile = headerJarFile
-		if len(localImplementationJars) == 1 && ctx.Config().UseTransitiveJarsInClasspath() {
+		if len(localImplementationJars) == 1 {
 			localHeaderJarFile := android.PathForModuleOut(ctx, "local-javac-header", jarName)
 			convertImplementationJarToHeaderJar(ctx, localImplementationJars[0], localHeaderJarFile)
 			localHeaderJars = append(localHeaderJars, localHeaderJarFile)
@@ -1796,16 +1782,10 @@
 
 	// merge implementation jar with resources if necessary
 	var implementationAndResourcesJarsToCombine android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		resourceJars := completeStaticLibsResourceJars.ToList()
-		if len(resourceJars) > 0 {
-			implementationAndResourcesJarsToCombine = append(resourceJars, completeStaticLibsImplementationJarsToCombine.ToList()...)
-			implementationAndResourcesJarsToCombine = append(implementationAndResourcesJarsToCombine, extraDepCombinedJars...)
-		}
-	} else {
-		if combinedResourceJar != nil {
-			implementationAndResourcesJarsToCombine = android.Paths{combinedResourceJar, outputFile}
-		}
+	combinedResourceJars := completeStaticLibsResourceJars.ToList()
+	if len(combinedResourceJars) > 0 {
+		implementationAndResourcesJarsToCombine = slices.Concat(combinedResourceJars,
+			completeStaticLibsImplementationJarsToCombine.ToList(), extraDepCombinedJars)
 	}
 
 	if len(implementationAndResourcesJarsToCombine) > 0 {
@@ -1855,18 +1835,9 @@
 			}
 
 			// merge dex jar with resources if necessary
-			var dexAndResourceJarsToCombine android.Paths
-			if ctx.Config().UseTransitiveJarsInClasspath() {
-				resourceJars := completeStaticLibsResourceJars.ToList()
-				if len(resourceJars) > 0 {
-					dexAndResourceJarsToCombine = append(android.Paths{dexOutputFile}, resourceJars...)
-				}
-			} else {
-				if combinedResourceJar != nil {
-					dexAndResourceJarsToCombine = android.Paths{dexOutputFile, combinedResourceJar}
-				}
-			}
-			if len(dexAndResourceJarsToCombine) > 0 {
+			if len(combinedResourceJars) > 0 {
+				dexAndResourceJarsToCombine := append(android.Paths{dexOutputFile}, combinedResourceJars...)
+
 				combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
 				TransformJarsToJar(ctx, combinedJar, "for dex resources", dexAndResourceJarsToCombine, android.OptionalPath{},
 					false, nil, nil)
@@ -1938,18 +1909,13 @@
 
 	j.collectTransitiveSrcFiles(ctx, srcFiles)
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		if len(localImplementationJars) > 0 || len(localResourceJars) > 0 || len(localHeaderJars) > 0 {
-			ctx.CheckbuildFile(localImplementationJars...)
-			ctx.CheckbuildFile(localResourceJars...)
-			ctx.CheckbuildFile(localHeaderJars...)
-		} else {
-			// There are no local sources or resources in this module, so there is nothing to checkbuild.
-			ctx.UncheckedModule()
-		}
+	if len(localImplementationJars) > 0 || len(localResourceJars) > 0 || len(localHeaderJars) > 0 {
+		ctx.CheckbuildFile(localImplementationJars...)
+		ctx.CheckbuildFile(localResourceJars...)
+		ctx.CheckbuildFile(localHeaderJars...)
 	} else {
-		ctx.CheckbuildFile(j.implementationJarFile)
-		ctx.CheckbuildFile(j.headerJarFile)
+		// There are no local sources or resources in this module, so there is nothing to checkbuild.
+		ctx.UncheckedModule()
 	}
 
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
@@ -2116,13 +2082,8 @@
 
 	// Combine any static header libraries into classes-header.jar. If there is only
 	// one input jar this step will be skipped.
-	var jars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		depSet := depset.New(depset.PREORDER, localHeaderJars, deps.transitiveStaticLibsHeaderJars)
-		jars = depSet.ToList()
-	} else {
-		jars = append(slices.Clone(localHeaderJars), deps.staticHeaderJars...)
-	}
+	depSet := depset.New(depset.PREORDER, localHeaderJars, deps.transitiveStaticLibsHeaderJars)
+	jars := depSet.ToList()
 
 	// we cannot skip the combine step for now if there is only one jar
 	// since we have to strip META-INF/TRANSITIVE dir from turbine.jar
@@ -2634,14 +2595,12 @@
 	deps.transitiveStaticLibsImplementationJars = transitiveStaticJarsImplementationLibs
 	deps.transitiveStaticLibsResourceJars = transitiveStaticJarsResourceLibs
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		depSet := depset.New(depset.PREORDER, nil, transitiveClasspathHeaderJars)
-		deps.classpath = depSet.ToList()
-		depSet = depset.New(depset.PREORDER, nil, transitiveBootClasspathHeaderJars)
-		deps.bootClasspath = depSet.ToList()
-		depSet = depset.New(depset.PREORDER, nil, transitiveJava9ClasspathHeaderJars)
-		deps.java9Classpath = depSet.ToList()
-	}
+	depSet := depset.New(depset.PREORDER, nil, transitiveClasspathHeaderJars)
+	deps.classpath = depSet.ToList()
+	depSet = depset.New(depset.PREORDER, nil, transitiveBootClasspathHeaderJars)
+	deps.bootClasspath = depSet.ToList()
+	depSet = depset.New(depset.PREORDER, nil, transitiveJava9ClasspathHeaderJars)
+	deps.java9Classpath = depSet.ToList()
 
 	if ctx.Device() {
 		sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 7a3c21e..a09416d 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -41,6 +41,10 @@
 	ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootclasspathFragmentFactory)
 }
 
+type BootclasspathFragmentInfo struct{}
+
+var BootclasspathFragmentInfoProvider = blueprint.NewProvider[BootclasspathFragmentInfo]()
+
 // BootclasspathFragmentSdkMemberType is the member type used to add bootclasspath_fragments to
 // the SDK snapshot. It is exported for use by apex.
 var BootclasspathFragmentSdkMemberType = &bootclasspathFragmentMemberType{
@@ -557,6 +561,8 @@
 	if !ctx.IsFinalModule(ctx.Module()) {
 		b.HideFromMake()
 	}
+
+	android.SetProvider(ctx, BootclasspathFragmentInfoProvider, BootclasspathFragmentInfo{})
 }
 
 // getProfileProviderApex returns the name of the apex that provides a boot image profile, or an
diff --git a/java/device_host_converter_test.go b/java/device_host_converter_test.go
index 197bb9f..42e3b46 100644
--- a/java/device_host_converter_test.go
+++ b/java/device_host_converter_test.go
@@ -15,10 +15,11 @@
 package java
 
 import (
-	"android/soong/android"
 	"slices"
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestDeviceForHost(t *testing.T) {
@@ -54,12 +55,12 @@
 	ctx, config := testJava(t, bp)
 
 	deviceModule := ctx.ModuleForTests(t, "device_module", "android_common")
-	deviceTurbineCombined := deviceModule.Output("turbine-combined/device_module.jar")
+	deviceTurbine := deviceModule.Output("turbine/device_module.jar")
 	deviceJavac := deviceModule.Output("javac/device_module.jar")
 	deviceRes := deviceModule.Output("res/device_module.jar")
 
 	deviceImportModule := ctx.ModuleForTests(t, "device_import_module", "android_common")
-	deviceImportCombined := deviceImportModule.Output("combined/device_import_module.jar")
+	deviceImportCombined := deviceImportModule.Output("local-combined/device_import_module.jar")
 
 	hostModule := ctx.ModuleForTests(t, "host_module", config.BuildOSCommonTarget.String())
 	hostJavac := hostModule.Output("javac/host_module.jar")
@@ -69,7 +70,7 @@
 
 	// check classpath of host module with dependency on device_for_host_module
 	expectedClasspath := "-classpath " + strings.Join(android.Paths{
-		deviceTurbineCombined.Output,
+		deviceTurbine.Output,
 		deviceImportCombined.Output,
 	}.Strings(), ":")
 
@@ -137,11 +138,11 @@
 
 	hostModule := ctx.ModuleForTests(t, "host_module", config.BuildOSCommonTarget.String())
 	hostJavac := hostModule.Output("javac/host_module.jar")
-	hostJavacHeader := hostModule.Output("javac-header/host_module.jar")
+	hostJavacHeader := hostModule.Output("local-javac-header/host_module.jar")
 	hostRes := hostModule.Output("res/host_module.jar")
 
 	hostImportModule := ctx.ModuleForTests(t, "host_import_module", config.BuildOSCommonTarget.String())
-	hostImportCombined := hostImportModule.Output("combined/host_import_module.jar")
+	hostImportCombined := hostImportModule.Output("local-combined/host_import_module.jar")
 
 	deviceModule := ctx.ModuleForTests(t, "device_module", "android_common")
 	deviceJavac := deviceModule.Output("javac/device_module.jar")
diff --git a/java/dex.go b/java/dex.go
index 1a61000..311657f 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -63,6 +63,14 @@
 		// Defaults to false for apps and tests, true for libraries.
 		Proguard_compatibility *bool
 
+		// If true, R8 will not add public or protected members (fields or methods) to
+		// the API surface of the compilation unit, i.e., classes that are kept or
+		// have kept subclasses will not expose any members added by R8 for internal
+		// use. That includes renamed members if obfuscation is enabled.
+		// This should only be used for building targets that go on the bootclasspath.
+		// Defaults to false.
+		Protect_api_surface *bool
+
 		// If true, optimize for size by removing unused code.  Defaults to true for apps,
 		// false for libraries and tests.
 		Shrink *bool
@@ -170,6 +178,71 @@
 		},
 	}, []string{"outDir", "d8Flags", "zipFlags", "mergeZipsFlags"}, nil)
 
+// Include all of the args for d8r8, so that we can generate the partialcompileclean target's build using the same list.
+var d8r8Clean = pctx.AndroidStaticRule("d8r8-partialcompileclean",
+	blueprint.RuleParams{
+		Command: `rm -rf "${outDir}" "${outDict}" "${outConfig}" "${outUsage}" "${outUsageZip}" "${outUsageDir}" ` +
+			`"${resourcesOutput}" "${outR8ArtProfile}" ${builtOut}`,
+	}, "outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir", "builtOut",
+	"d8Flags", "r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile", "implicits",
+)
+
+var d8r8, d8r8RE = pctx.MultiCommandRemoteStaticRules("d8r8",
+	blueprint.RuleParams{
+		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
+			`rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` +
+			`mkdir -p $$(dirname ${outUsage}) && ` +
+			`if [ -n "$${SOONG_USE_PARTIAL_COMPILE}" ]; then ` +
+			` for f in "${outConfig}" "${outDict}" "${outUsage}" "${resourcesOutput}"; do ` +
+			`   test -n "$${f}" && test ! -f "$${f}" && mkdir -p "$$(dirname "$${f}")" && touch "$${f}" || true; ` +
+			` done && ` +
+			` $d8Template${config.D8Cmd} ${config.D8Flags} $d8Flags --output $outDir --no-dex-input-jar $in; ` +
+			`else ` +
+			` $r8Template${config.R8Cmd} ${config.R8Flags} $r8Flags -injars $in --output $outDir ` +
+			` --no-data-resources ` +
+			` -printmapping ${outDict} ` +
+			` -printconfiguration ${outConfig} ` +
+			` -printusage ${outUsage} ` +
+			` --deps-file ${out}.d && ` +
+			` touch "${outDict}" "${outConfig}" "${outUsage}"; ` +
+			`fi && ` +
+			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
+			`rm -rf ${outUsageDir} && ` +
+			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
+			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar" `,
+		CommandDeps: []string{
+			"${config.D8Cmd}",
+			"${config.R8Cmd}",
+			"${config.SoongZipCmd}",
+			"${config.MergeZipsCmd}",
+		},
+	}, map[string]*remoteexec.REParams{
+		"$d8Template": &remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "compiler": "d8"},
+			Inputs:          []string{"${config.D8Jar}"},
+			ExecStrategy:    "${config.RED8ExecStrategy}",
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+		"$r8Template": &remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
+			Inputs:          []string{"$implicits", "${config.R8Jar}"},
+			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}", "${resourcesOutput}", "${outR8ArtProfile}"},
+			ExecStrategy:    "${config.RER8ExecStrategy}",
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+		"$zipTemplate": &remoteexec.REParams{
+			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
+			OutputFiles:  []string{"$outDir/classes.dex.jar"},
+			ExecStrategy: "${config.RED8ExecStrategy}",
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+	}, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
+		"d8Flags", "r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile"}, []string{"implicits"})
+
 var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -395,6 +468,10 @@
 		r8Flags = append(r8Flags, "--force-proguard-compatibility")
 	}
 
+	if BoolDefault(opt.Protect_api_surface, false) {
+		r8Flags = append(r8Flags, "--protect-api-surface")
+	}
+
 	// Avoid unnecessary stack frame noise by only injecting source map ids for non-debug
 	// optimized or obfuscated targets.
 	if (Bool(opt.Optimize) || Bool(opt.Obfuscate)) && !debugMode {
@@ -487,6 +564,7 @@
 
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
+	cleanPhonyPath := android.PathForModuleOut(ctx, "dex", dexParams.jarName+"-partialcompileclean").OutputPath
 	outDir := android.PathForModuleOut(ctx, "dex")
 
 	zipFlags := "--ignore_missing_files"
@@ -503,7 +581,19 @@
 	}
 
 	useR8 := d.effectiveOptimizeEnabled()
+	useD8 := !useR8 || ctx.Config().PartialCompileFlags().Use_d8
+	rbeR8 := ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8")
+	rbeD8 := ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8")
+	var rule blueprint.Rule
+	var description string
 	var artProfileOutputPath *android.OutputPath
+	var implicitOutputs android.WritablePaths
+	var deps android.Paths
+	args := map[string]string{
+		"zipFlags":       zipFlags,
+		"outDir":         outDir.String(),
+		"mergeZipsFlags": mergeZipsFlags,
+	}
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
@@ -516,83 +606,89 @@
 		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
 		resourcesOutput := android.PathForModuleOut(ctx, "package-res-shrunken.apk")
 		d.resourcesOutput = android.OptionalPathForPath(resourcesOutput)
-		implicitOutputs := android.WritablePaths{
+		implicitOutputs = append(implicitOutputs, android.WritablePaths{
 			proguardDictionary,
 			proguardUsageZip,
 			proguardConfiguration,
-		}
+		}...)
+		description = "r8"
 		debugMode := android.InList("--debug", commonFlags)
 		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams, debugMode)
-		rule := r8
-		args := map[string]string{
-			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
-			"zipFlags":       zipFlags,
-			"outDict":        proguardDictionary.String(),
-			"outConfig":      proguardConfiguration.String(),
-			"outUsageDir":    proguardUsageDir.String(),
-			"outUsage":       proguardUsage.String(),
-			"outUsageZip":    proguardUsageZip.String(),
-			"outDir":         outDir.String(),
-			"mergeZipsFlags": mergeZipsFlags,
-		}
+		deps = append(deps, r8Deps...)
+		args["r8Flags"] = strings.Join(append(commonFlags, r8Flags...), " ")
 		if r8ArtProfileOutputPath != nil {
 			artProfileOutputPath = r8ArtProfileOutputPath
-			implicitOutputs = append(
-				implicitOutputs,
-				artProfileOutputPath,
-			)
 			// Add the implicit r8 Art profile output to args so that r8RE knows
 			// about this implicit output
-			args["outR8ArtProfile"] = artProfileOutputPath.String()
+			args["outR8ArtProfile"] = r8ArtProfileOutputPath.String()
 		}
-
-		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") {
-			rule = r8RE
-			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
-		}
+		args["outDict"] = proguardDictionary.String()
+		args["outConfig"] = proguardConfiguration.String()
+		args["outUsageDir"] = proguardUsageDir.String()
+		args["outUsage"] = proguardUsage.String()
+		args["outUsageZip"] = proguardUsageZip.String()
 		if d.resourcesInput.Valid() {
 			implicitOutputs = append(implicitOutputs, resourcesOutput)
 			args["resourcesOutput"] = resourcesOutput.String()
 		}
-		ctx.Build(pctx, android.BuildParams{
-			Rule:            rule,
-			Description:     "r8",
-			Output:          javalibJar,
-			ImplicitOutputs: implicitOutputs,
-			Input:           dexParams.classesJar,
-			Implicits:       r8Deps,
-			Args:            args,
-		})
-	} else {
-		implicitOutputs := android.WritablePaths{}
+
+		rule = r8
+		if rbeR8 {
+			rule = r8RE
+			args["implicits"] = strings.Join(deps.Strings(), ",")
+		}
+	}
+	if useD8 {
+		description = "d8"
 		d8Flags, d8Deps, d8ArtProfileOutputPath := d.d8Flags(ctx, dexParams)
+		deps = append(deps, d8Deps...)
+		deps = append(deps, commonDeps...)
+		args["d8Flags"] = strings.Join(append(commonFlags, d8Flags...), " ")
 		if d8ArtProfileOutputPath != nil {
 			artProfileOutputPath = d8ArtProfileOutputPath
-			implicitOutputs = append(
-				implicitOutputs,
-				artProfileOutputPath,
-			)
 		}
-		d8Deps = append(d8Deps, commonDeps...)
-		rule := d8
-		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
+		// If we are generating both d8 and r8, only use RBE when both are enabled.
+		switch {
+		case useR8 && rule == r8:
+			rule = d8r8
+			description = "d8r8"
+		case useR8 && rule == r8RE && rbeD8:
+			rule = d8r8RE
+			description = "d8r8"
+		case rbeD8:
 			rule = d8RE
+		default:
+			rule = d8
 		}
-		ctx.Build(pctx, android.BuildParams{
-			Rule:            rule,
-			Description:     "d8",
-			Output:          javalibJar,
-			Input:           dexParams.classesJar,
-			ImplicitOutputs: implicitOutputs,
-			Implicits:       d8Deps,
-			Args: map[string]string{
-				"d8Flags":        strings.Join(append(commonFlags, d8Flags...), " "),
-				"zipFlags":       zipFlags,
-				"outDir":         outDir.String(),
-				"mergeZipsFlags": mergeZipsFlags,
-			},
-		})
 	}
+	if artProfileOutputPath != nil {
+		implicitOutputs = append(
+			implicitOutputs,
+			artProfileOutputPath,
+		)
+	}
+	ctx.Build(pctx, android.BuildParams{
+		Rule:            rule,
+		Description:     description,
+		Output:          javalibJar,
+		ImplicitOutputs: implicitOutputs,
+		Input:           dexParams.classesJar,
+		Implicits:       deps,
+		Args:            args,
+	})
+	if useR8 && useD8 {
+		// Generate the rule for partial compile clean.
+		args["builtOut"] = javalibJar.String()
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        d8r8Clean,
+			Description: "d8r8Clean",
+			Output:      cleanPhonyPath,
+			Args:        args,
+			PhonyOutput: true,
+		})
+		ctx.Phony("partialcompileclean", cleanPhonyPath)
+	}
+
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
 		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", dexParams.jarName).OutputPath
 		TransformZipAlign(ctx, alignedJavalibJar, javalibJar, nil)
diff --git a/java/dex_test.go b/java/dex_test.go
index 2126e42..66d801d 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -70,16 +70,17 @@
 	appR8 := app.Rule("r8")
 	stableAppR8 := stableApp.Rule("r8")
 	corePlatformAppR8 := corePlatformApp.Rule("r8")
-	libHeader := lib.Output("turbine-combined/lib.jar").Output
-	staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
+	libHeader := lib.Output("turbine/lib.jar").Output
+	libCombinedHeader := lib.Output("turbine-combined/lib.jar").Output
+	staticLibHeader := staticLib.Output("turbine/static_lib.jar").Output
 
 	android.AssertStringDoesContain(t, "expected lib header jar in app javac classpath",
 		appJavac.Args["classpath"], libHeader.String())
 	android.AssertStringDoesContain(t, "expected static_lib header jar in app javac classpath",
 		appJavac.Args["classpath"], staticLibHeader.String())
 
-	android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
-		appR8.Args["r8Flags"], libHeader.String())
+	android.AssertStringDoesContain(t, "expected lib combined header jar in app r8 classpath",
+		appR8.Args["r8Flags"], libCombinedHeader.String())
 	android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
 		appR8.Args["r8Flags"], staticLibHeader.String())
 	android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
@@ -331,16 +332,17 @@
 	fooJavac := foo.Rule("javac")
 	fooD8 := foo.Rule("d8")
 	appD8 := app.Rule("d8")
-	libHeader := lib.Output("turbine-combined/lib.jar").Output
-	staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
+	libHeader := lib.Output("turbine/lib.jar").Output
+	libCombinedHeader := lib.Output("turbine-combined/lib.jar").Output
+	staticLibHeader := staticLib.Output("turbine/static_lib.jar").Output
 
 	android.AssertStringDoesContain(t, "expected lib header jar in foo javac classpath",
 		fooJavac.Args["classpath"], libHeader.String())
 	android.AssertStringDoesContain(t, "expected static_lib header jar in foo javac classpath",
 		fooJavac.Args["classpath"], staticLibHeader.String())
 
-	android.AssertStringDoesContain(t, "expected lib header jar in foo d8 classpath",
-		fooD8.Args["d8Flags"], libHeader.String())
+	android.AssertStringDoesContain(t, "expected lib combined header jar in foo d8 classpath",
+		fooD8.Args["d8Flags"], libCombinedHeader.String())
 	android.AssertStringDoesNotContain(t, "expected no  static_lib header jar in foo javac classpath",
 		fooD8.Args["d8Flags"], staticLibHeader.String())
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 27027f0..313d8c7 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -554,7 +554,10 @@
 				typ: dexpreoptBootJar,
 			}
 
-			ctx.AddFarVariationDependencies(ctx.Target().Variations(), tag, android.RemoveOptionalPrebuiltPrefix(selected))
+			dep := android.RemoveOptionalPrebuiltPrefix(selected)
+			if ctx.OtherModuleDependencyVariantExists(ctx.Target().Variations(), dep) {
+				ctx.AddFarVariationDependencies(ctx.Target().Variations(), tag, dep)
+			}
 		}
 	}
 }
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
index 8cbe873..735b8da 100644
--- a/java/fuzz_test.go
+++ b/java/fuzz_test.go
@@ -72,8 +72,8 @@
 	}
 
 	baz := result.ModuleForTests(t, "baz", osCommonTarget).Rule("javac").Output.String()
-	barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "javac-header", "bar.jar")
-	bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "javac-header", "baz.jar")
+	barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "local-javac-header", "bar.jar")
+	bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "local-javac-header", "baz.jar")
 
 	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barOut)
 	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazOut)
diff --git a/java/java.go b/java/java.go
index c5dee0c..b9109ee 100644
--- a/java/java.go
+++ b/java/java.go
@@ -259,7 +259,6 @@
 }
 
 type UsesLibraryDependencyInfo struct {
-	DexJarBuildPath     OptionalDexJarPath
 	DexJarInstallPath   android.Path
 	ClassLoaderContexts dexpreopt.ClassLoaderContextMap
 }
@@ -403,13 +402,6 @@
 
 	BuiltInstalled string
 
-	// ApexSystemServerDexpreoptInstalls stores the list of dexpreopt artifacts if this is a system server
-	// jar in an apex.
-	ApexSystemServerDexpreoptInstalls []DexpreopterInstall
-
-	// ApexSystemServerDexJars stores the list of dex jars if this is a system server jar in an apex.
-	ApexSystemServerDexJars android.Paths
-
 	// The config is used for two purposes:
 	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
 	//   a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py).
@@ -418,10 +410,6 @@
 	//   dexpreopt another partition).
 	ConfigPath android.WritablePath
 
-	// The path to the profile on host that dexpreopter generates. This is used as the input for
-	// dex2oat.
-	OutputProfilePathOnHost android.Path
-
 	LogtagsSrcs android.Paths
 
 	ProguardDictionary android.OptionalPath
@@ -438,14 +426,39 @@
 
 	// True if profile-guided optimization is actually enabled.
 	ProfileGuided bool
+
+	Stem string
+
+	DexJarBuildPath OptionalDexJarPath
+
+	DexpreopterInfo *DexpreopterInfo
 }
 
 var JavaInfoProvider = blueprint.NewProvider[*JavaInfo]()
 
-type JavaLibraryInfo struct{}
+type DexpreopterInfo struct {
+	// The path to the profile on host that dexpreopter generates. This is used as the input for
+	// dex2oat.
+	OutputProfilePathOnHost android.Path
+	// If the java module is to be installed into an APEX, this list contains information about the
+	// dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed
+	// outside of the APEX.
+	ApexSystemServerDexpreoptInstalls []DexpreopterInstall
+
+	// ApexSystemServerDexJars returns the list of dex jars if this is an apex system server jar.
+	ApexSystemServerDexJars android.Paths
+}
+
+type JavaLibraryInfo struct {
+	Prebuilt bool
+}
 
 var JavaLibraryInfoProvider = blueprint.NewProvider[JavaLibraryInfo]()
 
+type JavaDexImportInfo struct{}
+
+var JavaDexImportInfoProvider = blueprint.NewProvider[JavaDexImportInfo]()
+
 // SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to
 // the sysprop implementation library.
 type SyspropPublicStubInfo struct {
@@ -1127,7 +1140,9 @@
 		TopLevelTarget: j.sourceProperties.Top_level_test_target,
 	})
 
-	android.SetProvider(ctx, JavaLibraryInfoProvider, JavaLibraryInfo{})
+	android.SetProvider(ctx, JavaLibraryInfoProvider, JavaLibraryInfo{
+		Prebuilt: false,
+	})
 
 	if javaInfo != nil {
 		setExtraJavaInfo(ctx, j, javaInfo)
@@ -1137,11 +1152,8 @@
 		javaInfo.BootDexJarPath = j.bootDexJarPath
 		javaInfo.UncompressDexState = j.uncompressDexState
 		javaInfo.Active = j.active
-		javaInfo.ApexSystemServerDexpreoptInstalls = j.apexSystemServerDexpreoptInstalls
-		javaInfo.ApexSystemServerDexJars = j.apexSystemServerDexJars
 		javaInfo.BuiltInstalled = j.builtInstalled
 		javaInfo.ConfigPath = j.configPath
-		javaInfo.OutputProfilePathOnHost = j.outputProfilePathOnHost
 		javaInfo.LogtagsSrcs = j.logtagsSrcs
 		javaInfo.ProguardDictionary = j.proguardDictionary
 		javaInfo.ProguardUsageZip = j.proguardUsageZip
@@ -1910,6 +1922,10 @@
 		if j.testConfig != nil {
 			ctx.InstallFile(pathInTestCases, ctx.ModuleName()+".config", j.testConfig)
 		}
+		dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+		if dynamicConfig.Valid() {
+			ctx.InstallFile(pathInTestCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path())
+		}
 		testDeps := append(j.data, j.extraTestConfigs...)
 		for _, data := range android.SortedUniquePaths(testDeps) {
 			dataPath := android.DataPath{SrcPath: data}
@@ -3056,12 +3072,7 @@
 	// file of the module to be named jarName.
 	var outputFile android.Path
 	combinedImplementationJar := android.PathForModuleOut(ctx, "combined", jarName)
-	var implementationJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		implementationJars = completeStaticLibsImplementationJars.ToList()
-	} else {
-		implementationJars = append(slices.Clone(localJars), staticJars...)
-	}
+	implementationJars := completeStaticLibsImplementationJars.ToList()
 	TransformJarsToJar(ctx, combinedImplementationJar, "combine prebuilt implementation jars", implementationJars, android.OptionalPath{},
 		false, j.properties.Exclude_files, j.properties.Exclude_dirs)
 	outputFile = combinedImplementationJar
@@ -3084,12 +3095,7 @@
 	if reuseImplementationJarAsHeaderJar {
 		headerJar = outputFile
 	} else {
-		var headerJars android.Paths
-		if ctx.Config().UseTransitiveJarsInClasspath() {
-			headerJars = completeStaticLibsHeaderJars.ToList()
-		} else {
-			headerJars = append(slices.Clone(localJars), staticHeaderJars...)
-		}
+		headerJars := completeStaticLibsHeaderJars.ToList()
 		headerOutputFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
 		TransformJarsToJar(ctx, headerOutputFile, "combine prebuilt header jars", headerJars, android.OptionalPath{},
 			false, j.properties.Exclude_files, j.properties.Exclude_dirs)
@@ -3153,11 +3159,7 @@
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		ctx.CheckbuildFile(localJars...)
-	} else {
-		ctx.CheckbuildFile(outputFile)
-	}
+	ctx.CheckbuildFile(localJars...)
 
 	if ctx.Device() {
 		// Shared libraries deapexed from prebuilt apexes are no longer supported.
@@ -3229,6 +3231,10 @@
 	setExtraJavaInfo(ctx, j, javaInfo)
 	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 
+	android.SetProvider(ctx, JavaLibraryInfoProvider, JavaLibraryInfo{
+		Prebuilt: true,
+	})
+
 	ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, "")
 	ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, ".jar")
 
@@ -3508,6 +3514,12 @@
 		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			j.Stem()+".jar", dexOutputFile)
 	}
+
+	javaInfo := &JavaInfo{}
+	setExtraJavaInfo(ctx, j, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
+	android.SetProvider(ctx, JavaDexImportInfoProvider, JavaDexImportInfo{})
 }
 
 func (j *DexImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
@@ -3695,7 +3707,7 @@
 			}
 		}
 		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, optional,
-			dep.UsesLibraryDependencyInfo.DexJarBuildPath.PathOrNil(),
+			dep.DexJarBuildPath.PathOrNil(),
 			dep.UsesLibraryDependencyInfo.DexJarInstallPath, dep.UsesLibraryDependencyInfo.ClassLoaderContexts)
 	} else {
 		clcMap.AddContextMap(dep.UsesLibraryDependencyInfo.ClassLoaderContexts, depName)
@@ -3779,7 +3791,6 @@
 
 	if ulDep, ok := module.(UsesLibraryDependency); ok {
 		javaInfo.UsesLibraryDependencyInfo = &UsesLibraryDependencyInfo{
-			DexJarBuildPath:     ulDep.DexJarBuildPath(ctx),
 			DexJarInstallPath:   ulDep.DexJarInstallPath(),
 			ClassLoaderContexts: ulDep.ClassLoaderContexts(),
 		}
@@ -3808,4 +3819,22 @@
 			Stubs:       stubs,
 		}
 	}
+
+	if st, ok := module.(ModuleWithStem); ok {
+		javaInfo.Stem = st.Stem()
+	}
+
+	if mm, ok := module.(interface {
+		DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath
+	}); ok {
+		javaInfo.DexJarBuildPath = mm.DexJarBuildPath(ctx)
+	}
+
+	if di, ok := module.(DexpreopterInterface); ok {
+		javaInfo.DexpreopterInfo = &DexpreopterInfo{
+			OutputProfilePathOnHost:           di.OutputProfilePathOnHost(),
+			ApexSystemServerDexpreoptInstalls: di.ApexSystemServerDexpreoptInstalls(),
+			ApexSystemServerDexJars:           di.ApexSystemServerDexJars(),
+		}
+	}
 }
diff --git a/java/java_test.go b/java/java_test.go
index f097762..636a0c8 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -110,7 +110,7 @@
 	case strings.HasSuffix(name, ".jar"):
 		return name
 	default:
-		return filepath.Join("out", "soong", ".intermediates", defaultJavaDir, name, "android_common", "turbine-combined", name+".jar")
+		return filepath.Join("out", "soong", ".intermediates", defaultJavaDir, name, "android_common", "turbine", name+".jar")
 	}
 }
 
@@ -240,11 +240,6 @@
 			srcs: ["d.java"],
 		}`
 
-	frameworkTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/ext/android_common/turbine-combined/ext.jar",
-		"out/soong/.intermediates/default/java/framework/android_common/turbine-combined/framework.jar",
-	}
-
 	frameworkTurbineJars := []string{
 		"out/soong/.intermediates/default/java/ext/android_common/turbine/ext.jar",
 		"out/soong/.intermediates/default/java/framework/android_common/turbine/framework.jar",
@@ -270,43 +265,6 @@
 			preparer:       android.NullFixturePreparer,
 			fooJavacInputs: []string{"a.java"},
 			fooJavacClasspath: slices.Concat(
-				frameworkTurbineCombinedJars,
-				[]string{
-					"out/soong/.intermediates/bar/android_common/turbine-combined/bar.jar",
-					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-				},
-			),
-			fooCombinedInputs: []string{
-				"out/soong/.intermediates/foo/android_common/javac/foo.jar",
-				"out/soong/.intermediates/baz/android_common/combined/baz.jar",
-			},
-
-			fooHeaderCombinedInputs: []string{
-				"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
-				"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-			},
-
-			barJavacInputs: []string{"b.java"},
-			barJavacClasspath: slices.Concat(
-				frameworkTurbineCombinedJars,
-				[]string{
-					"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
-				},
-			),
-			barCombinedInputs: []string{
-				"out/soong/.intermediates/bar/android_common/javac/bar.jar",
-				"out/soong/.intermediates/quz/android_common/javac/quz.jar",
-			},
-			barHeaderCombinedInputs: []string{
-				"out/soong/.intermediates/bar/android_common/turbine/bar.jar",
-				"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
-			},
-		},
-		{
-			name:           "transitive classpath",
-			preparer:       PrepareForTestWithTransitiveClasspathEnabled,
-			fooJavacInputs: []string{"a.java"},
-			fooJavacClasspath: slices.Concat(
 				frameworkTurbineJars,
 				[]string{
 					"out/soong/.intermediates/bar/android_common/turbine/bar.jar",
@@ -726,11 +684,11 @@
 	javac := fooModule.Rule("javac")
 	combineJar := ctx.ModuleForTests(t, "foo", "android_common").Description("for javac")
 	barModule := ctx.ModuleForTests(t, "bar", "android_common")
-	barJar := barModule.Output("combined/bar.jar").Output
+	barJar := barModule.Output("local-combined/bar.jar").Output
 	bazModule := ctx.ModuleForTests(t, "baz", "android_common")
-	bazJar := bazModule.Output("combined/baz.jar").Output
+	bazJar := bazModule.Output("local-combined/baz.jar").Output
 	sdklibStubsJar := ctx.ModuleForTests(t, "sdklib.stubs", "android_common").
-		Output("combined/sdklib.stubs.jar").Output
+		Output("local-combined/sdklib.stubs.jar").Output
 
 	fooLibrary := fooModule.Module().(*Library)
 	assertDeepEquals(t, "foo unique sources incorrect",
@@ -858,7 +816,7 @@
 		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
 	}
 
-	barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+	barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
 	if !strings.Contains(javac.Args["classpath"], barTurbine) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
 	}
@@ -1047,7 +1005,7 @@
 		t.Errorf("bar combined resource jars %v does not contain %q", w, g)
 	}
 
-	if g, w := barResCombined.Output.String(), bar.Inputs.Strings(); !inList(g, w) {
+	if g, w := barRes.Output.String(), bar.Inputs.Strings(); !inList(g, w) {
 		t.Errorf("bar combined jars %v does not contain %q", w, g)
 	}
 
@@ -1131,7 +1089,7 @@
 
 	android.AssertPathsRelativeToTopEquals(t, "foo inputs", []string{"a.java"}, fooTurbine.Inputs)
 
-	fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
+	fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine", "foo.jar")
 	barTurbineJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
 	android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["turbineFlags"], fooHeaderJar)
 	android.AssertStringDoesContain(t, "bar javac classpath", barJavac.Args["classpath"], fooHeaderJar)
@@ -1251,16 +1209,18 @@
 
 	source := ctx.ModuleForTests(t, "source_library", "android_common")
 	sourceJar := source.Output("javac/source_library.jar")
-	sourceHeaderJar := source.Output("turbine-combined/source_library.jar")
+	sourceHeaderJar := source.Output("turbine/source_library.jar")
+	sourceCombinedHeaderJar := source.Output("turbine-combined/source_library.jar")
 	sourceJavaInfo, _ := android.OtherModuleProvider(ctx, source.Module(), JavaInfoProvider)
 
 	// The source library produces separate implementation and header jars
 	android.AssertPathsRelativeToTopEquals(t, "source library implementation jar",
 		[]string{sourceJar.Output.String()}, sourceJavaInfo.ImplementationAndResourcesJars)
 	android.AssertPathsRelativeToTopEquals(t, "source library header jar",
-		[]string{sourceHeaderJar.Output.String()}, sourceJavaInfo.HeaderJars)
+		[]string{sourceCombinedHeaderJar.Output.String()}, sourceJavaInfo.HeaderJars)
 
 	importWithNoDeps := ctx.ModuleForTests(t, "import_with_no_deps", "android_common")
+	importWithNoDepsLocalJar := importWithNoDeps.Output("local-combined/import_with_no_deps.jar")
 	importWithNoDepsJar := importWithNoDeps.Output("combined/import_with_no_deps.jar")
 	importWithNoDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithNoDeps.Module(), JavaInfoProvider)
 
@@ -1270,10 +1230,14 @@
 	android.AssertPathsRelativeToTopEquals(t, "import with no deps header jar",
 		[]string{importWithNoDepsJar.Output.String()}, importWithNoDepsJavaInfo.HeaderJars)
 	android.AssertPathsRelativeToTopEquals(t, "import with no deps combined inputs",
-		[]string{"no_deps.jar"}, importWithNoDepsJar.Inputs)
+		[]string{importWithNoDepsLocalJar.Output.String()}, importWithNoDepsJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with no deps local combined inputs",
+		[]string{"no_deps.jar"}, importWithNoDepsLocalJar.Inputs)
 
 	importWithSourceDeps := ctx.ModuleForTests(t, "import_with_source_deps", "android_common")
+	importWithSourceDepsLocalJar := importWithSourceDeps.Output("local-combined/import_with_source_deps.jar")
 	importWithSourceDepsJar := importWithSourceDeps.Output("combined/import_with_source_deps.jar")
+	importWithSourceDepsLocalHeaderJar := importWithSourceDeps.Output("local-combined/import_with_source_deps.jar")
 	importWithSourceDepsHeaderJar := importWithSourceDeps.Output("turbine-combined/import_with_source_deps.jar")
 	importWithSourceDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithSourceDeps.Module(), JavaInfoProvider)
 
@@ -1283,11 +1247,16 @@
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps header jar",
 		[]string{importWithSourceDepsHeaderJar.Output.String()}, importWithSourceDepsJavaInfo.HeaderJars)
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps combined implementation jar inputs",
-		[]string{"source_deps.jar", sourceJar.Output.String()}, importWithSourceDepsJar.Inputs)
+		[]string{importWithSourceDepsLocalJar.Output.String(), sourceJar.Output.String()}, importWithSourceDepsJar.Inputs)
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps combined header jar inputs",
-		[]string{"source_deps.jar", sourceHeaderJar.Output.String()}, importWithSourceDepsHeaderJar.Inputs)
+		[]string{importWithSourceDepsLocalHeaderJar.Output.String(), sourceHeaderJar.Output.String()}, importWithSourceDepsHeaderJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with source deps local combined implementation jar inputs",
+		[]string{"source_deps.jar"}, importWithSourceDepsLocalJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with source deps local combined header jar inputs",
+		[]string{"source_deps.jar"}, importWithSourceDepsLocalHeaderJar.Inputs)
 
 	importWithImportDeps := ctx.ModuleForTests(t, "import_with_import_deps", "android_common")
+	importWithImportDepsLocalJar := importWithImportDeps.Output("local-combined/import_with_import_deps.jar")
 	importWithImportDepsJar := importWithImportDeps.Output("combined/import_with_import_deps.jar")
 	importWithImportDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithImportDeps.Module(), JavaInfoProvider)
 
@@ -1297,7 +1266,9 @@
 	android.AssertPathsRelativeToTopEquals(t, "import with import deps header jar",
 		[]string{importWithImportDepsJar.Output.String()}, importWithImportDepsJavaInfo.HeaderJars)
 	android.AssertPathsRelativeToTopEquals(t, "import with import deps combined implementation jar inputs",
-		[]string{"import_deps.jar", importWithNoDepsJar.Output.String()}, importWithImportDepsJar.Inputs)
+		[]string{importWithImportDepsLocalJar.Output.String(), importWithNoDepsLocalJar.Output.String()}, importWithImportDepsJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with import deps local combined implementation jar inputs",
+		[]string{"import_deps.jar"}, importWithImportDepsLocalJar.Inputs)
 }
 
 var compilerFlagsTestCases = []struct {
@@ -1364,6 +1335,7 @@
 
 // TODO(jungjw): Consider making this more robust by ignoring path order.
 func checkPatchModuleFlag(t *testing.T, ctx *android.TestContext, moduleName string, expected string) {
+	t.Helper()
 	variables := ctx.ModuleForTests(t, moduleName, "android_common").VariablesForTestsRelativeToTop()
 	flags := strings.Split(variables["javacFlags"], " ")
 	got := ""
@@ -3107,7 +3079,6 @@
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		prepareForTestWithFrameworkJacocoInstrumentation,
-		PrepareForTestWithTransitiveClasspathEnabled,
 	).RunTestWithBp(t, `
 		android_app {
 			name: "foo",
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index c7b1ece..4b56cff 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -50,13 +50,6 @@
 			srcs: ["d.kt"],
 		}`
 
-	kotlinStdlibTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/turbine-combined/kotlin-stdlib.jar",
-		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/turbine-combined/kotlin-stdlib-jdk7.jar",
-		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk8/android_common/turbine-combined/kotlin-stdlib-jdk8.jar",
-		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/turbine-combined/kotlin-annotations.jar",
-	}
-
 	kotlinStdlibTurbineJars := []string{
 		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/turbine/kotlin-stdlib.jar",
 		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/turbine/kotlin-stdlib-jdk7.jar",
@@ -71,21 +64,11 @@
 		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/javac/kotlin-annotations.jar",
 	}
 
-	bootclasspathTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/stable.core.platform.api.stubs/android_common/turbine-combined/stable.core.platform.api.stubs.jar",
-		"out/soong/.intermediates/default/java/core-lambda-stubs/android_common/turbine-combined/core-lambda-stubs.jar",
-	}
-
 	bootclasspathTurbineJars := []string{
 		"out/soong/.intermediates/default/java/stable.core.platform.api.stubs/android_common/turbine/stable.core.platform.api.stubs.jar",
 		"out/soong/.intermediates/default/java/core-lambda-stubs/android_common/turbine/core-lambda-stubs.jar",
 	}
 
-	frameworkTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/ext/android_common/turbine-combined/ext.jar",
-		"out/soong/.intermediates/default/java/framework/android_common/turbine-combined/framework.jar",
-	}
-
 	frameworkTurbineJars := []string{
 		"out/soong/.intermediates/default/java/ext/android_common/turbine/ext.jar",
 		"out/soong/.intermediates/default/java/framework/android_common/turbine/framework.jar",
@@ -109,68 +92,8 @@
 		barHeaderCombinedInputs []string
 	}{
 		{
-			name:             "normal",
-			preparer:         android.NullFixturePreparer,
-			fooKotlincInputs: []string{"a.java", "b.kt"},
-			fooJavacInputs:   []string{"a.java"},
-			fooKotlincClasspath: slices.Concat(
-				bootclasspathTurbineCombinedJars,
-				frameworkTurbineCombinedJars,
-				[]string{"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar"},
-				kotlinStdlibTurbineCombinedJars,
-			),
-			fooJavacClasspath: slices.Concat(
-				[]string{"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar"},
-				frameworkTurbineCombinedJars,
-				[]string{"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar"},
-				kotlinStdlibTurbineCombinedJars,
-			),
-			fooCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/foo/android_common/kotlin/foo.jar",
-					"out/soong/.intermediates/foo/android_common/javac/foo.jar",
-					"out/soong/.intermediates/quz/android_common/combined/quz.jar",
-				},
-				kotlinStdlibJavacJars,
-			),
-			fooHeaderCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
-					"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar",
-					"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
-				},
-				kotlinStdlibTurbineCombinedJars,
-			),
-
-			barKotlincInputs: []string{"b.kt"},
-			barKotlincClasspath: slices.Concat(
-				bootclasspathTurbineCombinedJars,
-				frameworkTurbineCombinedJars,
-				[]string{
-					"out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar",
-					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-				},
-				kotlinStdlibTurbineCombinedJars,
-			),
-			barCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/bar/android_common/kotlin/bar.jar",
-					"out/soong/.intermediates/baz/android_common/combined/baz.jar",
-				},
-				kotlinStdlibJavacJars,
-				[]string{},
-			),
-			barHeaderCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/bar/android_common/kotlin_headers/bar.jar",
-					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-				},
-				kotlinStdlibTurbineCombinedJars,
-			),
-		},
-		{
 			name:             "transitive classpath",
-			preparer:         PrepareForTestWithTransitiveClasspathEnabled,
+			preparer:         android.NullFixturePreparer,
 			fooKotlincInputs: []string{"a.java", "b.kt"},
 			fooJavacInputs:   []string{"a.java"},
 			fooKotlincClasspath: slices.Concat(
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index d4d2fb5..363521a 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -43,6 +43,13 @@
 	ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory)
 }
 
+type PlatformCompatConfigInfo struct {
+	CompatConfig android.OutputPath
+	SubDir       string
+}
+
+var PlatformCompatConfigInfoProvider = blueprint.NewProvider[PlatformCompatConfigInfo]()
+
 var PrepareForTestWithPlatformCompatConfig = android.FixtureRegisterWithContext(registerPlatformCompatConfigBuildComponents)
 
 func platformCompatConfigPath(ctx android.PathContext) android.OutputPath {
@@ -124,6 +131,11 @@
 	rule.Build(configFileName, "Extract compat/compat_config.xml and install it")
 	ctx.InstallFile(p.installDirPath, p.configFile.Base(), p.configFile)
 	ctx.SetOutputFiles(android.Paths{p.configFile}, "")
+
+	android.SetProvider(ctx, PlatformCompatConfigInfoProvider, PlatformCompatConfigInfo{
+		CompatConfig: p.CompatConfig(),
+		SubDir:       p.SubDir(),
+	})
 }
 
 func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/java/ravenwood.go b/java/ravenwood.go
index 3b6c80b..c4078c5 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -267,6 +267,10 @@
 		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, r.testConfig.String())
 	}
 	moduleInfoJSON.CompatibilitySuites = []string{"general-tests", "ravenwood-tests"}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
 }
 
 func (r *ravenwoodTest) AndroidMkEntries() []android.AndroidMkEntries {
@@ -384,6 +388,10 @@
 
 	// Normal build should perform install steps
 	ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install"))
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
 }
 
 // collectTransitiveJniDeps returns all JNI dependencies, including transitive
diff --git a/java/robolectric.go b/java/robolectric.go
index 43e17f9..5dcc7dd 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -279,6 +279,10 @@
 	} else {
 		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
 	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
 }
 
 func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile android.ModuleOutPath) {
@@ -416,6 +420,10 @@
 	android.SetProvider(ctx, RobolectricRuntimesInfoProvider, RobolectricRuntimesInfo{
 		Runtimes: r.runtimes,
 	})
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
 }
 
 func (r *robolectricRuntimes) InstallInTestcases() bool { return true }
diff --git a/java/rro.go b/java/rro.go
index d9f4ff7..f7f85f0 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -34,6 +34,15 @@
 	ctx.RegisterModuleType("override_runtime_resource_overlay", OverrideRuntimeResourceOverlayModuleFactory)
 }
 
+type RuntimeResourceOverlayInfo struct {
+	OutputFile                    android.Path
+	Certificate                   Certificate
+	Theme                         string
+	OverriddenManifestPackageName string
+}
+
+var RuntimeResourceOverlayInfoProvider = blueprint.NewProvider[RuntimeResourceOverlayInfo]()
+
 type RuntimeResourceOverlay struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -207,6 +216,12 @@
 		AconfigTextFiles: aconfigTextFilePaths,
 	})
 
+	android.SetProvider(ctx, RuntimeResourceOverlayInfoProvider, RuntimeResourceOverlayInfo{
+		OutputFile:  r.OutputFile(),
+		Certificate: r.Certificate(),
+		Theme:       r.Theme(),
+	})
+
 	buildComplianceMetadata(ctx)
 }
 
diff --git a/java/sdk.go b/java/sdk.go
index 27b2434..8510959 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -358,7 +358,7 @@
 			"api_fingerprint",
 		}
 		count := 0
-		ctx.VisitAllModules(func(module android.Module) {
+		ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 			name := ctx.ModuleName(module)
 			if android.InList(name, apiTxtFileModules) {
 				cmd.Inputs(android.OutputFilesForModule(ctx, module, ""))
diff --git a/java/sdk_library.go b/java/sdk_library.go
index cf31b50..7944bb2 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -702,7 +702,7 @@
 		paths.stubsHeaderPath = lib.HeaderJars
 		paths.stubsImplPath = lib.ImplementationJars
 
-		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider).UsesLibraryDependencyInfo
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider)
 		paths.stubsDexJarPath = libDep.DexJarBuildPath
 		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath
 		return nil
@@ -718,7 +718,7 @@
 			paths.stubsImplPath = lib.ImplementationJars
 		}
 
-		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider).UsesLibraryDependencyInfo
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider)
 		paths.stubsDexJarPath = libDep.DexJarBuildPath
 		return nil
 	} else {
@@ -732,7 +732,7 @@
 			paths.stubsImplPath = lib.ImplementationJars
 		}
 
-		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider).UsesLibraryDependencyInfo
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider)
 		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath
 		return nil
 	} else {
@@ -1017,10 +1017,6 @@
 		removedApiFilePaths[kind] = removedApiFilePath
 	}
 
-	javaInfo := &JavaInfo{}
-	setExtraJavaInfo(ctx, ctx.Module(), javaInfo)
-	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
-
 	return SdkLibraryInfo{
 		EverythingStubDexJarPaths: everythingStubPaths,
 		ExportableStubDexJarPaths: exportableStubPaths,
@@ -1227,6 +1223,8 @@
 
 	// Whether if this can be used as a shared library.
 	SharedLibrary bool
+
+	Prebuilt bool
 }
 
 var SdkLibraryInfoProvider = blueprint.NewProvider[SdkLibraryInfo]()
@@ -1513,10 +1511,10 @@
 		module.dexJarFile = makeDexJarPathFromPath(module.implLibraryInfo.DexJarFile.Path())
 		module.headerJarFile = module.implLibraryInfo.HeaderJars[0]
 		module.implementationAndResourcesJar = module.implLibraryInfo.ImplementationAndResourcesJars[0]
-		module.apexSystemServerDexpreoptInstalls = module.implLibraryInfo.ApexSystemServerDexpreoptInstalls
-		module.apexSystemServerDexJars = module.implLibraryInfo.ApexSystemServerDexJars
+		module.apexSystemServerDexpreoptInstalls = module.implLibraryInfo.DexpreopterInfo.ApexSystemServerDexpreoptInstalls
+		module.apexSystemServerDexJars = module.implLibraryInfo.DexpreopterInfo.ApexSystemServerDexJars
 		module.dexpreopter.configPath = module.implLibraryInfo.ConfigPath
-		module.dexpreopter.outputProfilePathOnHost = module.implLibraryInfo.OutputProfilePathOnHost
+		module.dexpreopter.outputProfilePathOnHost = module.implLibraryInfo.DexpreopterInfo.OutputProfilePathOnHost
 
 		// Properties required for Library.AndroidMkEntries
 		module.logtagsSrcs = module.implLibraryInfo.LogtagsSrcs
@@ -1582,7 +1580,12 @@
 		setOutputFilesFromJavaInfo(ctx, module.implLibraryInfo)
 	}
 
+	javaInfo := &JavaInfo{}
+	setExtraJavaInfo(ctx, ctx.Module(), javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
 	sdkLibInfo.GeneratingLibs = generatingLibs
+	sdkLibInfo.Prebuilt = false
 	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
@@ -2236,7 +2239,12 @@
 		setOutputFilesFromJavaInfo(ctx, module.implLibraryInfo)
 	}
 
+	javaInfo := &JavaInfo{}
+	setExtraJavaInfo(ctx, ctx.Module(), javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
 	sdkLibInfo.GeneratingLibs = generatingLibs
+	sdkLibInfo.Prebuilt = true
 	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
diff --git a/java/sdk_library_internal.go b/java/sdk_library_internal.go
index 5789692..f5feabe 100644
--- a/java/sdk_library_internal.go
+++ b/java/sdk_library_internal.go
@@ -936,6 +936,8 @@
 	ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
 
 	ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "")
+
+	etc.SetCommonPrebuiltEtcInfo(ctx, module)
 }
 
 func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 2cb827d..431bbac 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -149,7 +149,7 @@
 
 	bazJavac := result.ModuleForTests(t, "baz", "android_common").Rule("javac")
 	// tests if baz is actually linked to the stubs lib
-	android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar")
+	android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.from-text.jar")
 	// ... and not to the impl lib
 	android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.jar")
 	// test if baz is not linked to the system variant of foo
@@ -157,15 +157,15 @@
 
 	bazTestJavac := result.ModuleForTests(t, "baz-test", "android_common").Rule("javac")
 	// tests if baz-test is actually linked to the test stubs lib
-	android.AssertStringDoesContain(t, "baz-test javac classpath", bazTestJavac.Args["classpath"], "foo.stubs.test.jar")
+	android.AssertStringDoesContain(t, "baz-test javac classpath", bazTestJavac.Args["classpath"], "foo.stubs.test.from-text.jar")
 
 	baz29Javac := result.ModuleForTests(t, "baz-29", "android_common").Rule("javac")
 	// tests if baz-29 is actually linked to the system 29 stubs lib
-	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/sdk_system_29_foo/android_common/combined/sdk_system_29_foo.jar")
+	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/sdk_system_29_foo/android_common/local-combined/sdk_system_29_foo.jar")
 
 	bazModule30Javac := result.ModuleForTests(t, "baz-module-30", "android_common").Rule("javac")
 	// tests if "baz-module-30" is actually linked to the module 30 stubs lib
-	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/sdk_module-lib_30_foo/android_common/combined/sdk_module-lib_30_foo.jar")
+	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/sdk_module-lib_30_foo/android_common/local-combined/sdk_module-lib_30_foo.jar")
 
 	// test if baz has exported SDK lib names foo and bar to qux
 	qux := result.ModuleForTests(t, "qux", "android_common")
@@ -422,7 +422,7 @@
 		android.AssertStringContainsEquals(t, "bad classpath for "+sdklib, sdklibCp, "/"+dep+".jar", expected)
 
 		combineJarInputs := result.ModuleForTests(t, sdklib, "android_common").Rule("combineJar").Inputs.Strings()
-		depPath := filepath.Join("out", "soong", ".intermediates", dep, "android_common", "turbine-combined", dep+".jar")
+		depPath := filepath.Join("out", "soong", ".intermediates", dep, "android_common", "turbine", dep+".jar")
 		android.AssertStringListContainsEquals(t, "bad combined inputs for "+sdklib, combineJarInputs, depPath, combined)
 	}
 	for _, expectation := range expectations {
@@ -458,7 +458,7 @@
 
 	// The bar library should depend on the stubs jar.
 	barLibrary := result.ModuleForTests(t, "bar", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath .*:out/soong/[^:]*/foo\.stubs\.from-text/foo\.stubs\.from-text\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -791,7 +791,7 @@
 
 	stubsPath := func(name string, scope *apiScope) string {
 		name = scope.stubsLibraryModuleName(name)
-		return fmt.Sprintf("out/soong/.intermediates/%[1]s/android_common/turbine-combined/%[1]s.jar", name)
+		return fmt.Sprintf("out/soong/.intermediates/%[1]s.from-text/android_common/%[1]s.from-text/%[1]s.from-text.jar", name)
 	}
 
 	// The bar library should depend on the highest (where system server is highest and public is
@@ -853,7 +853,7 @@
 		fooModule := result.ModuleForTests(t, "foo"+scope, "android_common")
 		javac := fooModule.Rule("javac")
 
-		sdklibStubsJar := result.ModuleForTests(t, "sdklib.stubs"+scope, "android_common").Output("combined/sdklib.stubs" + scope + ".jar").Output
+		sdklibStubsJar := result.ModuleForTests(t, "sdklib.stubs"+scope, "android_common").Output("local-combined/sdklib.stubs" + scope + ".jar").Output
 		android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], sdklibStubsJar.String())
 	}
 
@@ -1002,7 +1002,7 @@
 	public := result.ModuleForTests(t, "public", "android_common")
 	rule := public.Output("javac/public.jar")
 	inputs := rule.Implicits.Strings()
-	expected := "out/soong/.intermediates/prebuilt_sdklib.stubs/android_common/combined/sdklib.stubs.jar"
+	expected := "out/soong/.intermediates/prebuilt_sdklib.stubs/android_common/local-combined/sdklib.stubs.jar"
 	if !android.InList(expected, inputs) {
 		t.Errorf("expected %q to contain %q", inputs, expected)
 	}
@@ -1124,12 +1124,12 @@
 	inputs := rule.Implicits.Strings()
 	expectedInputs := []string{
 		// source
-		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs/android_common/turbine-combined/sdklib.prebuilt_preferred_using_legacy_flags.stubs.jar",
-		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system/android_common/turbine-combined/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.jar",
+		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs.from-text/android_common/sdklib.prebuilt_preferred_using_legacy_flags.stubs.from-text/sdklib.prebuilt_preferred_using_legacy_flags.stubs.from-text.jar",
+		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.from-text/android_common/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.from-text/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.from-text.jar",
 
 		// prebuilt
-		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs/android_common/combined/sdklib.source_preferred_using_legacy_flags.stubs.jar",
-		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs.system/android_common/combined/sdklib.source_preferred_using_legacy_flags.stubs.system.jar",
+		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs/android_common/local-combined/sdklib.source_preferred_using_legacy_flags.stubs.jar",
+		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs.system/android_common/local-combined/sdklib.source_preferred_using_legacy_flags.stubs.system.jar",
 	}
 	for _, expected := range expectedInputs {
 		if !android.InList(expected, inputs) {
@@ -1578,7 +1578,8 @@
 	public := result.ModuleForTests(t, "mymodule", "android_common")
 	rule := public.Output("javac/mymodule.jar")
 	inputs := rule.Implicits.Strings()
-	android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar")
+	android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs,
+		"out/soong/.intermediates/sdklib.stubs.from-text/android_common/sdklib.stubs.from-text/sdklib.stubs.from-text.jar")
 }
 
 // test that rdep gets resolved to the correct version of a java_sdk_library (source or a specific prebuilt)
@@ -1636,17 +1637,17 @@
 		{
 			desc:                   "Source library is selected using apex_contributions",
 			selectedDependencyName: "sdklib",
-			expectedStubPath:       "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar",
+			expectedStubPath:       "out/soong/.intermediates/sdklib.stubs.from-text/android_common/sdklib.stubs.from-text/sdklib.stubs.from-text.jar",
 		},
 		{
 			desc:                   "Prebuilt library v1 is selected using apex_contributions",
 			selectedDependencyName: "prebuilt_sdklib.v1",
-			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v1.stubs/android_common/combined/sdklib.stubs.jar",
+			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v1.stubs/android_common/local-combined/sdklib.stubs.jar",
 		},
 		{
 			desc:                   "Prebuilt library v2 is selected using apex_contributions",
 			selectedDependencyName: "prebuilt_sdklib.v2",
-			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v2.stubs/android_common/combined/sdklib.stubs.jar",
+			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v2.stubs/android_common/local-combined/sdklib.stubs.jar",
 		},
 	}
 
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 49983ad..6386a00 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -391,21 +391,16 @@
 
 	t.Run("basic", func(t *testing.T) {
 		t.Parallel()
-		testClasspathTestCases(t, classpathTestcases, false, false)
+		testClasspathTestCases(t, classpathTestcases, false)
 	})
 
 	t.Run("Always_use_prebuilt_sdks=true", func(t *testing.T) {
 		t.Parallel()
-		testClasspathTestCases(t, classpathTestcases, true, false)
-	})
-
-	t.Run("UseTransitiveJarsInClasspath", func(t *testing.T) {
-		t.Parallel()
-		testClasspathTestCases(t, classpathTestcases, false, true)
+		testClasspathTestCases(t, classpathTestcases, true)
 	})
 }
 
-func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks, useTransitiveJarsInClasspath bool) {
+func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks bool) {
 	for _, testcase := range classpathTestcases {
 		if testcase.forAlwaysUsePrebuiltSdks != nil && *testcase.forAlwaysUsePrebuiltSdks != alwaysUsePrebuiltSdks {
 			continue
@@ -446,10 +441,8 @@
 					switch {
 					case e == `""`, strings.HasSuffix(e, ".jar"):
 						ret[i] = e
-					case useTransitiveJarsInClasspath:
-						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine", e+".jar")
 					default:
-						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine-combined", e+".jar")
+						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine", e+".jar")
 					}
 				}
 				return ret
@@ -544,9 +537,6 @@
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				})
 			}
-			if useTransitiveJarsInClasspath {
-				preparer = PrepareForTestWithTransitiveClasspathEnabled
-			}
 
 			fixtureFactory := android.GroupFixturePreparers(
 				prepareForJavaTest,
diff --git a/java/testing.go b/java/testing.go
index 35319ae..3abbb84 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -825,5 +825,3 @@
 		config.installDir = installDir
 	})
 }
-
-var PrepareForTestWithTransitiveClasspathEnabled = android.PrepareForTestWithBuildFlag("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH", "true")
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 4f1ef9d..43fe8aa 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -86,6 +86,8 @@
 	ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath)
 
 	ctx.SetOutputFiles(android.Paths{l.outputFilePath}, "")
+
+	etc.SetCommonPrebuiltEtcInfo(ctx, l)
 }
 
 func BuildLinkerConfig(
diff --git a/python/binary.go b/python/binary.go
index 4d6e118..feac72a 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -114,6 +114,12 @@
 	android.SetProvider(ctx, PythonBinaryInfoProvider, PythonBinaryInfo{})
 
 	ctx.SetOutputFiles(android.Paths{p.installSource}, "")
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"EXECUTABLES"}
+	moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, p.androidMkSharedLibs...)
+	moduleInfoJSON.SharedLibs = append(moduleInfoJSON.SharedLibs, p.androidMkSharedLibs...)
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
 }
 
 func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
diff --git a/python/test.go b/python/test.go
index 5e70fc1..df62ab7 100644
--- a/python/test.go
+++ b/python/test.go
@@ -224,6 +224,10 @@
 			if p.testConfig != nil {
 				ctx.InstallFile(testCases, ctx.ModuleName()+".config", p.testConfig)
 			}
+			dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+			if dynamicConfig.Valid() {
+				ctx.InstallFile(testCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path())
+			}
 		}
 		// Install tests and data in arch specific subdir $PRODUCT_OUT/testcases/$module/$arch
 		testCases = testCases.Join(ctx, ctx.Target().Arch.ArchType.String())
diff --git a/rust/library.go b/rust/library.go
index 7f5861f..415785a 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -744,10 +744,16 @@
 		}
 		if library.rlib() {
 			ccExporter.RustRlibDeps = append(ccExporter.RustRlibDeps, deps.reexportedCcRlibDeps...)
+			ccExporter.RustRlibDeps = append(ccExporter.RustRlibDeps, deps.reexportedWholeCcRlibDeps...)
 		}
 		android.SetProvider(ctx, cc.FlagExporterInfoProvider, ccExporter)
 	}
 
+	if library.dylib() {
+		// reexport whole-static'd dependencies for dylibs.
+		library.flagExporter.wholeRustRlibDeps = append(library.flagExporter.wholeRustRlibDeps, deps.reexportedWholeCcRlibDeps...)
+	}
+
 	if library.shared() || library.stubs() {
 		// Optimize out relinking against shared libraries whose interface hasn't changed by
 		// depending on a table of contents file instead of the library itself.
diff --git a/rust/rust.go b/rust/rust.go
index 7a7b106..4eebda3 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -496,8 +496,9 @@
 	depLinkFlags []string
 
 	// track cc static-libs that have Rlib dependencies
-	reexportedCcRlibDeps []cc.RustRlibDep
-	ccRlibDeps           []cc.RustRlibDep
+	reexportedCcRlibDeps      []cc.RustRlibDep
+	reexportedWholeCcRlibDeps []cc.RustRlibDep
+	ccRlibDeps                []cc.RustRlibDep
 
 	// linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker
 	// Both of these are exported and propagate to dependencies.
@@ -555,6 +556,7 @@
 	staticLibObjects      []string
 	sharedLibObjects      []string
 	wholeStaticLibObjects []string
+	wholeRustRlibDeps     []cc.RustRlibDep
 }
 
 func (flagExporter *flagExporter) exportLinkDirs(dirs ...string) {
@@ -584,6 +586,7 @@
 		StaticLibObjects:      flagExporter.staticLibObjects,
 		WholeStaticLibObjects: flagExporter.wholeStaticLibObjects,
 		SharedLibPaths:        flagExporter.sharedLibObjects,
+		WholeRustRlibDeps:     flagExporter.wholeRustRlibDeps,
 	})
 }
 
@@ -600,6 +603,7 @@
 	StaticLibObjects      []string
 	WholeStaticLibObjects []string
 	SharedLibPaths        []string
+	WholeRustRlibDeps     []cc.RustRlibDep
 }
 
 var RustFlagExporterInfoProvider = blueprint.NewProvider[RustFlagExporterInfo]()
@@ -841,7 +845,7 @@
 	return shared
 }
 
-func (mod *Module) ImplementationModuleNameForMake(ctx android.BaseModuleContext) string {
+func (mod *Module) ImplementationModuleNameForMake() string {
 	name := mod.BaseModuleName()
 	if versioned, ok := mod.compiler.(cc.VersionedInterface); ok {
 		name = versioned.ImplementationModuleName(name)
@@ -1411,7 +1415,7 @@
 	if rustInfo != nil {
 		// Use base module name for snapshots when exporting to Makefile.
 		if rustInfo.SnapshotInfo != nil {
-			baseName := linkableInfo.BaseModuleName
+			baseName := commonInfo.BaseModuleName
 			return baseName + rustInfo.SnapshotInfo.SnapshotAndroidMkSuffix + rustInfo.AndroidMkSuffix
 		}
 	}
@@ -1585,8 +1589,8 @@
 				directSrcProvidersDeps = append(directSrcProvidersDeps, &dep)
 			}
 
+			exportedRustInfo, _ := android.OtherModuleProvider(ctx, dep, RustFlagExporterInfoProvider)
 			exportedInfo, _ := android.OtherModuleProvider(ctx, dep, RustFlagExporterInfoProvider)
-
 			//Append the dependencies exported objects, except for proc-macros which target a different arch/OS
 			if depTag != procMacroDepTag {
 				depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...)
@@ -1595,6 +1599,11 @@
 				depPaths.staticLibObjects = append(depPaths.staticLibObjects, exportedInfo.StaticLibObjects...)
 				depPaths.wholeStaticLibObjects = append(depPaths.wholeStaticLibObjects, exportedInfo.WholeStaticLibObjects...)
 				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
+
+				depPaths.reexportedWholeCcRlibDeps = append(depPaths.reexportedWholeCcRlibDeps, exportedRustInfo.WholeRustRlibDeps...)
+				if !mod.Rlib() {
+					depPaths.ccRlibDeps = append(depPaths.ccRlibDeps, exportedRustInfo.WholeRustRlibDeps...)
+				}
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
@@ -1656,17 +1665,26 @@
 					}
 				}
 
+				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
 				if cc.IsWholeStaticLib(depTag) {
 					// Add whole staticlibs to wholeStaticLibObjects to propagate to Rust all dependents.
 					depPaths.wholeStaticLibObjects = append(depPaths.wholeStaticLibObjects, ccLibPath.String())
+
+					// We also propagate forward whole-static'd cc staticlibs with rust_ffi_rlib dependencies
+					// We don't need to check a hypothetical exportedRustInfo.WholeRustRlibDeps because we
+					// wouldn't expect a rust_ffi_rlib to be listed in `static_libs` (Soong explicitly disallows this)
+					depPaths.reexportedWholeCcRlibDeps = append(depPaths.reexportedWholeCcRlibDeps, exportedInfo.RustRlibDeps...)
 				} else {
-					// Otherwise add to staticLibObjects, which only propagate through rlibs to their dependents.
+					// If not whole_static, add to staticLibObjects, which only propagate through rlibs to their dependents.
 					depPaths.staticLibObjects = append(depPaths.staticLibObjects, ccLibPath.String())
+
+					if mod.Rlib() {
+						// rlibs propagate their inherited rust_ffi_rlibs forward.
+						depPaths.reexportedCcRlibDeps = append(depPaths.reexportedCcRlibDeps, exportedInfo.RustRlibDeps...)
+					}
 				}
 
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
-
-				exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider)
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
@@ -1675,8 +1693,6 @@
 				if !mod.Rlib() {
 					// rlibs don't need to build the generated static library, so they don't need to track these.
 					depPaths.ccRlibDeps = append(depPaths.ccRlibDeps, exportedInfo.RustRlibDeps...)
-				} else {
-					depPaths.reexportedCcRlibDeps = append(depPaths.reexportedCcRlibDeps, exportedInfo.RustRlibDeps...)
 				}
 
 				directStaticLibDeps = append(directStaticLibDeps, linkableInfo)
@@ -1835,6 +1851,7 @@
 	depPaths.depSystemIncludePaths = android.FirstUniquePaths(depPaths.depSystemIncludePaths)
 	depPaths.depLinkFlags = android.FirstUniqueStrings(depPaths.depLinkFlags)
 	depPaths.reexportedCcRlibDeps = android.FirstUniqueFunc(depPaths.reexportedCcRlibDeps, cc.EqRustRlibDeps)
+	depPaths.reexportedWholeCcRlibDeps = android.FirstUniqueFunc(depPaths.reexportedWholeCcRlibDeps, cc.EqRustRlibDeps)
 	depPaths.ccRlibDeps = android.FirstUniqueFunc(depPaths.ccRlibDeps, cc.EqRustRlibDeps)
 
 	return depPaths
diff --git a/rust/rust_test.go b/rust/rust_test.go
index fbb9947..f634bb5 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -456,6 +456,13 @@
 		}
 
 		rust_ffi_static {
+			name: "libfoo_from_rlib_whole",
+			crate_name: "foo_from_rlib_whole",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["foo_includes"]
+		}
+
+		rust_ffi_static {
 			name: "libbuzz",
 			crate_name: "buzz",
 			srcs: ["src/lib.rs"],
@@ -469,6 +476,13 @@
 			export_include_dirs: ["buzz_includes"]
 		}
 
+		rust_ffi_static {
+			name: "libbuzz_from_rlib_whole",
+			crate_name: "buzz_from_rlib_whole",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["buzz_includes"]
+		}
+
 		cc_library_shared {
 			name: "libcc_shared",
 			srcs:["foo.c"],
@@ -489,6 +503,13 @@
 			whole_static_libs: ["libfoo_from_rlib"],
 		}
 
+		cc_library_static {
+			name: "libcc_whole_static_from_rlib",
+			srcs:["foo.c"],
+			static_libs: ["libbuzz_from_rlib_whole"],
+			whole_static_libs: ["libfoo_from_rlib_whole"],
+		}
+
 		cc_binary {
 			name: "ccBin",
 			srcs:["foo.c"],
@@ -500,6 +521,14 @@
 			srcs:["src/foo.rs"],
 			crate_name: "rs",
 			static_libs: ["libcc_static_from_rlib"],
+			whole_static_libs: ["libcc_whole_static_from_rlib"],
+		}
+
+		rust_library {
+			name: "librs2",
+			srcs:["src/foo.rs"],
+			crate_name: "rs",
+			rustlibs: ["librs"],
 		}
 
 		rust_binary {
@@ -509,7 +538,7 @@
 			rlibs: ["librs", "libbar"],
 			static_libs: ["libcc_static"],
 		}
-		`)
+	`)
 
 	libbar := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc")
 	libcc_shared_rustc := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc")
@@ -521,6 +550,10 @@
 	rustbin_genlib := ctx.ModuleForTests(t, "rsBin", "android_arm64_armv8-a").Output("generated_rust_staticlib/librustlibs.a")
 	rustbin := ctx.ModuleForTests(t, "rsBin", "android_arm64_armv8-a").Output("unstripped/rsBin")
 	librs_rlib := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a")
+	librs2_rlib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a")
+	librs_genlib := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_dylib").Output("generated_rust_staticlib/librustlibs.a")
+	librs2_genlib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_dylib").Output("generated_rust_staticlib/librustlibs.a")
+	librs2_dylib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_dylib").Output("unstripped/librs2.dylib.so")
 
 	if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") {
 		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"])
@@ -578,6 +611,10 @@
 		t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v",
 			"generated_rust_staticlib/librustlibs.a", rustbin.Args["libFlags"])
 	}
+	if !strings.Contains(librs2_dylib.Args["linkFlags"], "generated_rust_staticlib/librustlibs.a") {
+		t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v",
+			"generated_rust_staticlib/librustlibs.a", librs2_dylib.Args["libFlags"])
+	}
 
 	// Make sure that direct dependencies and indirect whole static dependencies are
 	// propagating correctly for the rlib -> cc_library_static -> rust_* generated library example.
@@ -610,6 +647,31 @@
 	if librs_rlib.Rule != nil {
 		t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"])
 	}
+	if librs2_rlib.Rule != nil {
+		t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"])
+	}
+
+	// Make sure that direct whole static dependencies are propagating correctly downstream
+	// foo_from_rlib_whole --(ws)--> libcc_whole_static_from_rlib --(ws)--> librs
+	if !strings.Contains(librs_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
+		t.Errorf("Missing direct whole_static_lib dependency libfoo_from_rlib_whole from rust dylib when writing generated Rust staticlib: %#v", librs_genlib.Args["libFlags"])
+	}
+
+	// Make sure that indirect whole static dependencies are propagating correctly downstream
+	// foo_from_rlib_whole --(ws)--> libcc_whole_static_from_rlib --(ws)--> librs --> rust_*
+	if !strings.Contains(librs2_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
+		t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib_whole from rust dylib when writing generated Rust staticlib: %#v", librs2_genlib.Args["libFlags"])
+	}
+	if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
+		t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib_whole from rust dylib in rust binary when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
+	}
+
+	// Make sure that normal static dependencies are not propagating through dylib dependencies
+	// buzz_from_rlib_whole --(s)--> libcc_whole_static_from_rlib --(ws)--> librs --> rust_*
+	if strings.Contains(librs2_genlib.Args["libFlags"], "--extern buzz_from_rlib_whole=") {
+		t.Errorf("dependency from indirect cc staticlib from direct dylib dep found in rust dylib when writing generated Rust staticlib: %#v", librs2_genlib.Args["libFlags"])
+	}
+
 }
 
 func assertString(t *testing.T, got, expected string) {
diff --git a/rust/test.go b/rust/test.go
index b658ae2..5c183bc 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -208,6 +208,10 @@
 			if test.testConfig != nil {
 				ctx.InstallFile(testCases, ctx.ModuleName()+".config", test.testConfig)
 			}
+			dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+			if dynamicConfig.Valid() {
+				ctx.InstallFile(testCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path())
+			}
 		}
 		// Install tests and data in arch specific subdir $PRODUCT_OUT/testcases/$module/$arch
 		testCases = testCases.Join(ctx, ctx.Target().Arch.ArchType.String())
diff --git a/scripts/strip.sh b/scripts/strip.sh
index 8d69f0d..5320ef6 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -101,7 +101,12 @@
 do_strip_keep_mini_debug_info_linux() {
     rm -f "${outfile}.mini_debuginfo.xz"
     local fail=
-    "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes --remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
+    if [ -z "${windows}" ]; then
+        "${CLANG_BIN}/llvm-strip" --strip-all --keep-section=.ARM.attributes --remove-section=.comment "${infile}" -o "${outfile}.tmp" || fail=true
+    else
+        # --keep-section not supported for Windows COFF.
+        fail=true
+    fi
 
     if [ -z $fail ]; then
         # create_minidebuginfo has issues with compressed debug sections. Just
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index d753d24..c0c6ff2 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -41,6 +41,14 @@
 	registerShBuildComponents(android.InitRegistrationContext)
 }
 
+type ShBinaryInfo struct {
+	SubDir     string
+	OutputFile android.Path
+	Symlinks   []string
+}
+
+var ShBinaryInfoProvider = blueprint.NewProvider[ShBinaryInfo]()
+
 func registerShBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("sh_binary", ShBinaryFactory)
 	ctx.RegisterModuleType("sh_binary_host", ShBinaryHostFactory)
@@ -314,6 +322,12 @@
 
 	s.properties.SubName = s.GetSubname(ctx)
 
+	android.SetProvider(ctx, ShBinaryInfoProvider, ShBinaryInfo{
+		SubDir:     s.SubDir(),
+		OutputFile: s.OutputFile(),
+		Symlinks:   s.Symlinks(),
+	})
+
 	ctx.SetOutputFiles(android.Paths{s.outputFilePath}, "")
 }
 
@@ -530,6 +544,28 @@
 		MkAppClass:           mkEntries.Class,
 		InstallDir:           s.installDir,
 	})
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"NATIVE_TESTS"}
+	if len(s.testProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, s.testProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+	if proptools.Bool(s.testProperties.Test_options.Unit_test) {
+		moduleInfoJSON.IsUnitTest = "true"
+		if ctx.Host() {
+			moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests")
+		}
+	}
+	moduleInfoJSON.DataDependencies = append(moduleInfoJSON.DataDependencies, s.testProperties.Data_bins...)
+	if s.testConfig != nil {
+		if _, ok := s.testConfig.(android.WritablePath); ok {
+			moduleInfoJSON.AutoTestConfig = []string{"true"}
+		}
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, s.testConfig.String())
+	}
+	moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, s.extraTestConfigs.Strings()...)
 }
 
 func addArch(archType string, paths android.Paths) []string {
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 06c5e9c..b2cfaa7 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -339,7 +339,7 @@
 
 	// Java modules linking against system API should use public stub
 	javaSystemApiClient := result.ModuleForTests(t, "java-platform", "android_common").Rule("javac")
-	syspropPlatformPublic := result.ModuleForTests(t, "sysprop-platform_public", "android_common").Description("for turbine")
+	syspropPlatformPublic := result.ModuleForTests(t, "sysprop-platform_public", "android_common").Description("turbine")
 	if g, w := javaSystemApiClient.Implicits.Strings(), syspropPlatformPublic.Output.String(); !android.InList(w, g) {
 		t.Errorf("system api client should use public stub %q, got %q", w, g)
 	}
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
index 8c3ff29..73edbf6 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -46,6 +46,7 @@
 		forceClose: make(chan bool),
 		done:       make(chan bool),
 		cancelOpen: make(chan bool),
+		running:    make(map[uint32]*Action),
 	}
 
 	go n.run()
@@ -59,6 +60,7 @@
 	forceClose chan bool
 	done       chan bool
 	cancelOpen chan bool
+	running    map[uint32]*Action
 }
 
 const NINJA_READER_CLOSE_TIMEOUT = 5 * time.Second
@@ -68,32 +70,47 @@
 	// Signal the goroutine to stop if it is blocking opening the fifo.
 	close(n.cancelOpen)
 
+	closed := false
+
 	// Ninja should already have exited or been killed, wait 5 seconds for the FIFO to be closed and any
 	// remaining messages to be processed through the NinjaReader.run goroutine.
 	timeoutCh := time.After(NINJA_READER_CLOSE_TIMEOUT)
 	select {
 	case <-n.done:
-		return
+		closed = true
 	case <-timeoutCh:
 		// Channel is not closed yet
 	}
 
-	n.status.Error(fmt.Sprintf("ninja fifo didn't finish after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
+	if !closed {
+		n.status.Error(fmt.Sprintf("ninja fifo didn't finish after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
 
-	// Force close the reader even if the FIFO didn't close.
-	close(n.forceClose)
+		// Force close the reader even if the FIFO didn't close.
+		close(n.forceClose)
 
-	// Wait again for the reader thread to acknowledge the close before giving up and assuming it isn't going
-	// to send anything else.
-	timeoutCh = time.After(NINJA_READER_CLOSE_TIMEOUT)
-	select {
-	case <-n.done:
-		return
-	case <-timeoutCh:
-		// Channel is not closed yet
+		// Wait again for the reader thread to acknowledge the close before giving up and assuming it isn't going
+		// to send anything else.
+		timeoutCh = time.After(NINJA_READER_CLOSE_TIMEOUT)
+		select {
+		case <-n.done:
+			closed = true
+		case <-timeoutCh:
+			// Channel is not closed yet
+		}
 	}
 
-	n.status.Verbose(fmt.Sprintf("ninja fifo didn't finish even after force closing after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
+	if !closed {
+		n.status.Verbose(fmt.Sprintf("ninja fifo didn't finish even after force closing after %s", NINJA_READER_CLOSE_TIMEOUT.String()))
+	}
+
+	err := fmt.Errorf("error: action cancelled when ninja exited")
+	for _, action := range n.running {
+		n.status.FinishAction(ActionResult{
+			Action: action,
+			Output: err.Error(),
+			Error:  err,
+		})
+	}
 }
 
 func (n *NinjaReader) run() {
@@ -125,8 +142,6 @@
 
 	r := bufio.NewReader(f)
 
-	running := map[uint32]*Action{}
-
 	msgChan := make(chan *ninja_frontend.Status)
 
 	// Read from the ninja fifo and decode the protobuf in a goroutine so the main NinjaReader.run goroutine
@@ -213,11 +228,11 @@
 				ChangedInputs: msg.EdgeStarted.ChangedInputs,
 			}
 			n.status.StartAction(action)
-			running[msg.EdgeStarted.GetId()] = action
+			n.running[msg.EdgeStarted.GetId()] = action
 		}
 		if msg.EdgeFinished != nil {
-			if started, ok := running[msg.EdgeFinished.GetId()]; ok {
-				delete(running, msg.EdgeFinished.GetId())
+			if started, ok := n.running[msg.EdgeFinished.GetId()]; ok {
+				delete(n.running, msg.EdgeFinished.GetId())
 
 				var err error
 				exitCode := int(msg.EdgeFinished.GetStatus())
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index 831f6d4..37537ab 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -164,7 +164,6 @@
 	directories := flags.Bool("d", false, "include directories in zip")
 	compLevel := flags.Int("L", 5, "deflate compression level (0-9)")
 	emulateJar := flags.Bool("jar", false, "modify the resultant .zip to emulate the output of 'jar'")
-	sortEntries := flags.Bool("sort_entries", false, "sort the zip entries")
 	writeIfChanged := flags.Bool("write_if_changed", false, "only update resultant .zip if it has changed")
 	ignoreMissingFiles := flags.Bool("ignore_missing_files", false, "continue if a requested file does not exist")
 	symlinks := flags.Bool("symlinks", true, "store symbolic links in zip instead of following them")
@@ -229,7 +228,6 @@
 		FileArgs:                 fileArgsBuilder.FileArgs(),
 		OutputFilePath:           *out,
 		EmulateJar:               *emulateJar,
-		SortEntries:              *sortEntries,
 		SrcJar:                   *srcJar,
 		AddDirectoryEntriesToZip: *directories,
 		CompressionLevel:         *compLevel,
diff --git a/zip/zip.go b/zip/zip.go
index e4e9585..22b7704 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -272,7 +272,6 @@
 	FileArgs                 []FileArg
 	OutputFilePath           string
 	EmulateJar               bool
-	SortEntries              bool
 	SrcJar                   bool
 	AddDirectoryEntriesToZip bool
 	CompressionLevel         int
@@ -395,7 +394,7 @@
 		}
 	}
 
-	return z.write(w, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.SortEntries, args.SrcJar, args.NumParallelJobs)
+	return z.write(w, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.SrcJar, args.NumParallelJobs)
 }
 
 // Zip creates an output zip archive from given sources.
@@ -476,6 +475,42 @@
 	return nil
 }
 
+func (z *ZipWriter) moveJavaFileBasedOnPackage(mapping *pathMapping) error {
+	src := mapping.src
+	var s os.FileInfo
+	var err error
+	if z.followSymlinks {
+		s, err = z.fs.Stat(src)
+	} else {
+		s, err = z.fs.Lstat(src)
+	}
+	if err != nil {
+		if os.IsNotExist(err) && z.ignoreMissingFiles {
+			return nil
+		}
+		return err
+	}
+	if !s.Mode().IsRegular() {
+		return nil
+	}
+	r, err := z.fs.Open(src)
+	if err != nil {
+		return err
+	}
+	// rewrite the destination using the package path if it can be determined
+	pkg, err := jar.JavaPackage(r, src)
+	err2 := r.Close()
+	if err2 != nil {
+		return err2
+	}
+	if err != nil {
+		// ignore errors for now, leaving the file at in its original location in the zip
+	} else {
+		mapping.dest = filepath.Join(filepath.Join(strings.Split(pkg, ".")...), filepath.Base(src))
+	}
+	return nil
+}
+
 func jarSort(mappings []pathMapping) {
 	sort.SliceStable(mappings, func(i int, j int) bool {
 		return jar.EntryNamesLess(mappings[i].dest, mappings[j].dest)
@@ -483,7 +518,7 @@
 }
 
 func (z *ZipWriter) write(f io.Writer, pathMappings []pathMapping, manifest string,
-	emulateJar, sortEntries, srcJar bool,
+	emulateJar, srcJar bool,
 	parallelJobs int) error {
 
 	z.errors = make(chan error)
@@ -513,16 +548,38 @@
 		return errors.New("must specify --jar when specifying a manifest via -m")
 	}
 
-	if emulateJar && sortEntries {
-		return errors.New("Cannot specify both --jar and --sort_entries (--jar implies sorting with a different algorithm)")
+	// move java source files to the correct folder based on the package statement inside of them.
+	// This is done before the entry sorting so that they're still in the right order.
+	if srcJar {
+		var javaMoveErrors []error
+		var javaMoveErrorsLock sync.Mutex
+		var wg sync.WaitGroup
+		for i := range pathMappings {
+			if filepath.Ext(pathMappings[i].src) == ".java" {
+				wg.Add(1)
+				go func() {
+					err := z.moveJavaFileBasedOnPackage(&pathMappings[i])
+					if err != nil {
+						javaMoveErrorsLock.Lock()
+						javaMoveErrors = append(javaMoveErrors, err)
+						javaMoveErrorsLock.Unlock()
+					}
+					wg.Done()
+				}()
+			}
+		}
+		wg.Wait()
+		if len(javaMoveErrors) > 0 {
+			return errors.Join(javaMoveErrors...)
+		}
 	}
+
 	if emulateJar {
 		// manifest may be empty, in which case addManifest will fill in a default
 		pathMappings = append(pathMappings, pathMapping{jar.ManifestFile, manifest, zip.Deflate})
 
 		jarSort(pathMappings)
-	}
-	if sortEntries {
+	} else {
 		sort.SliceStable(pathMappings, func(i int, j int) bool {
 			return pathMappings[i].dest < pathMappings[j].dest
 		})
@@ -536,7 +593,7 @@
 			if emulateJar && ele.dest == jar.ManifestFile {
 				err = z.addManifest(ele.dest, ele.src, ele.zipMethod)
 			} else {
-				err = z.addFile(ele.dest, ele.src, ele.zipMethod, emulateJar, srcJar)
+				err = z.addFile(ele.dest, ele.src, ele.zipMethod, emulateJar)
 			}
 			if err != nil {
 				z.errors <- err
@@ -635,7 +692,7 @@
 }
 
 // imports (possibly with compression) <src> into the zip at sub-path <dest>
-func (z *ZipWriter) addFile(dest, src string, method uint16, emulateJar, srcJar bool) error {
+func (z *ZipWriter) addFile(dest, src string, method uint16, emulateJar bool) error {
 	var fileSize int64
 	var executable bool
 
@@ -709,21 +766,6 @@
 			return err
 		}
 
-		if srcJar && filepath.Ext(src) == ".java" {
-			// rewrite the destination using the package path if it can be determined
-			pkg, err := jar.JavaPackage(r, src)
-			if err != nil {
-				// ignore errors for now, leaving the file at in its original location in the zip
-			} else {
-				dest = filepath.Join(filepath.Join(strings.Split(pkg, ".")...), filepath.Base(src))
-			}
-
-			_, err = r.Seek(0, io.SeekStart)
-			if err != nil {
-				return err
-			}
-		}
-
 		fileSize = s.Size()
 		executable = s.Mode()&0100 != 0
 
diff --git a/zip/zip_test.go b/zip/zip_test.go
index c64c3f4..8f100d8 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -159,10 +159,10 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -261,10 +261,10 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -274,10 +274,10 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
 				fh("c", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -287,11 +287,11 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
+				fh("@", fileC, zip.Deflate),
+				fh("[", fileEmpty, zip.Store),
 				fh("a/a/a", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
-				fh("@", fileC, zip.Deflate),
 				fh("foo'bar", fileC, zip.Deflate),
-				fh("[", fileEmpty, zip.Store),
 			},
 		},
 		{
@@ -463,8 +463,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("foo", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
+				fh("foo", fileA, zip.Deflate),
 			},
 		},
 		{
@@ -477,8 +477,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("prefix/foo", fileA, zip.Deflate),
 				fh("prefix/a/a/b", fileB, zip.Deflate),
+				fh("prefix/foo", fileA, zip.Deflate),
 			},
 		},
 		{
@@ -490,8 +490,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("foo", fileA, zip.Deflate),
 				fh("a/a/b", fileB, zip.Deflate),
+				fh("foo", fileA, zip.Deflate),
 			},
 		},
 		{
@@ -504,8 +504,8 @@
 			compressionLevel: 9,
 
 			files: []zip.FileHeader{
-				fh("foo/bar", fileA, zip.Deflate),
 				fh("b", fileB, zip.Deflate),
+				fh("foo/bar", fileA, zip.Deflate),
 			},
 		},
 
@@ -688,8 +688,8 @@
 
 	want := []string{
 		"foo/",
-		"foo/wrong_package.java",
 		"foo/correct_package.java",
+		"foo/wrong_package.java",
 		"no_package.java",
 		"src2/",
 		"src2/parse_error.java",