Merge "Revert "Add a coverage suffix to avoid Ninja file regeneration."" into main
diff --git a/Android.bp b/Android.bp
index 0d1ff02..432c7fc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,7 +104,6 @@
 // Instantiate the dex_bootjars singleton module.
 dex_bootjars {
     name: "dex_bootjars",
-    no_full_install: true,
 }
 
 // Pseudo-test that's run on checkbuilds to ensure that get_clang_version can
@@ -121,20 +120,6 @@
     name: "dexpreopt_systemserver_check",
 }
 
-// buildinfo.prop contains common properties for system/build.prop, like ro.build.version.*
-// TODO(b/322090587): merge this to gen_build_prop.py script.
-buildinfo_prop {
-    name: "buildinfo.prop",
-
-    // not installable because this will be included to system/build.prop
-    installable: false,
-
-    product_config: ":product_config",
-
-    // Currently, only microdroid can refer to buildinfo.prop
-    visibility: ["//packages/modules/Virtualization/build/microdroid"],
-}
-
 // container for apex_contributions selected using build flags
 all_apex_contributions {
     name: "all_apex_contributions",
@@ -144,3 +129,41 @@
     name: "product_config",
     visibility: ["//device/google/cuttlefish/system_image"],
 }
+
+build_prop {
+    name: "system-build.prop",
+    stem: "build.prop",
+    product_config: ":product_config",
+    // Currently, only microdroid and cf system image can refer to system-build.prop
+    visibility: [
+        "//device/google/cuttlefish/system_image",
+        "//packages/modules/Virtualization/build/microdroid",
+    ],
+}
+
+build_prop {
+    name: "system_ext-build.prop",
+    stem: "build.prop",
+    system_ext_specific: true,
+    product_config: ":product_config",
+    relative_install_path: "etc", // system_ext/etc/build.prop
+    visibility: ["//visibility:private"],
+}
+
+build_prop {
+    name: "product-build.prop",
+    stem: "build.prop",
+    product_specific: true,
+    product_config: ":product_config",
+    relative_install_path: "etc", // product/etc/build.prop
+    visibility: ["//visibility:private"],
+}
+
+build_prop {
+    name: "odm-build.prop",
+    stem: "build.prop",
+    device_specific: true,
+    product_config: ":product_config",
+    relative_install_path: "etc", // odm/etc/build.prop
+    visibility: ["//visibility:private"],
+}
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index 9e3d291..9b638e7 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -220,15 +220,10 @@
 }
 
 func (module *DeclarationsModule) BuildActionProviderKeys() []blueprint.AnyProviderKey {
-	return []blueprint.AnyProviderKey{android.AconfigDeclarationsProviderKey}
-}
-
-func (module *DeclarationsModule) PackageContextPath() string {
-	return pkgPath
-}
-
-func (module *DeclarationsModule) CachedRules() []blueprint.Rule {
-	return []blueprint.Rule{aconfigRule, aconfigTextRule}
+	return []blueprint.AnyProviderKey{
+		android.AconfigDeclarationsProviderKey,
+		android.AconfigReleaseDeclarationsProviderKey,
+	}
 }
 
 var _ blueprint.Incremental = &DeclarationsModule{}
diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go
index 5483295..e89cd31 100644
--- a/aconfig/aconfig_declarations_test.go
+++ b/aconfig/aconfig_declarations_test.go
@@ -40,7 +40,7 @@
 	module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
 
 	// Check that the provider has the right contents
-	depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
+	depData, _ := android.OtherModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
 	android.AssertStringEquals(t, "package", depData.Package, "com.example.package")
 	android.AssertStringEquals(t, "container", depData.Container, "com.android.foo")
 	android.AssertBoolEquals(t, "exportable", depData.Exportable, true)
@@ -67,7 +67,7 @@
 	result := runTest(t, android.FixtureExpectsNoErrors, bp)
 
 	module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
-	depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
+	depData, _ := android.OtherModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
 	android.AssertBoolEquals(t, "exportable", depData.Exportable, false)
 }
 
@@ -205,7 +205,7 @@
 		}
 		result := fixture.RunTestWithBp(t, test.bp)
 		module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
-		depData, _ := android.SingletonModuleProvider(result, module, android.AconfigReleaseDeclarationsProviderKey)
+		depData, _ := android.OtherModuleProvider(result, module, android.AconfigReleaseDeclarationsProviderKey)
 		expectedKeys := []string{""}
 		for _, rc := range strings.Split(test.buildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"], " ") {
 			expectedKeys = append(expectedKeys, rc)
diff --git a/aconfig/aconfig_value_set_test.go b/aconfig/aconfig_value_set_test.go
index 7d18999..32c31cb 100644
--- a/aconfig/aconfig_value_set_test.go
+++ b/aconfig/aconfig_value_set_test.go
@@ -38,6 +38,6 @@
 	module := result.ModuleForTests("module_name", "").Module().(*ValueSetModule)
 
 	// Check that the provider has the right contents
-	depData, _ := android.SingletonModuleProvider(result, module, valueSetProviderKey)
+	depData, _ := android.OtherModuleProvider(result, module, valueSetProviderKey)
 	android.AssertStringEquals(t, "AvailablePackages", "blah.aconfig_values", depData.AvailablePackages["foo.package"][0].String())
 }
diff --git a/aconfig/aconfig_values_test.go b/aconfig/aconfig_values_test.go
index 526579c..ddbea57 100644
--- a/aconfig/aconfig_values_test.go
+++ b/aconfig/aconfig_values_test.go
@@ -33,7 +33,7 @@
 	module := result.ModuleForTests("module_name", "").Module().(*ValuesModule)
 
 	// Check that the provider has the right contents
-	depData, _ := android.SingletonModuleProvider(result, module, valuesProviderKey)
+	depData, _ := android.OtherModuleProvider(result, module, valuesProviderKey)
 	android.AssertStringEquals(t, "package", "foo.package", depData.Package)
 	android.AssertPathsEndWith(t, "srcs", []string{"blah.aconfig_values"}, depData.Values)
 }
diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go
index 0437c26..6ad54da 100644
--- a/aconfig/all_aconfig_declarations.go
+++ b/aconfig/all_aconfig_declarations.go
@@ -15,9 +15,10 @@
 package aconfig
 
 import (
-	"android/soong/android"
 	"fmt"
 	"slices"
+
+	"android/soong/android"
 )
 
 // A singleton module that collects all of the aconfig flags declared in the
@@ -55,7 +56,7 @@
 		var packages = make(map[string]int)
 		var cacheFiles android.Paths
 		ctx.VisitAllModules(func(module android.Module) {
-			decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigReleaseDeclarationsProviderKey)
+			decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigReleaseDeclarationsProviderKey)
 			if !ok {
 				return
 			}
diff --git a/aconfig/build_flags/all_build_flag_declarations.go b/aconfig/build_flags/all_build_flag_declarations.go
index 282c9dc..5f02912 100644
--- a/aconfig/build_flags/all_build_flag_declarations.go
+++ b/aconfig/build_flags/all_build_flag_declarations.go
@@ -38,7 +38,7 @@
 	// Find all of the build_flag_declarations modules
 	var intermediateFiles android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		decl, ok := android.SingletonModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
+		decl, ok := android.OtherModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
 		if !ok {
 			return
 		}
diff --git a/aconfig/exported_java_aconfig_library.go b/aconfig/exported_java_aconfig_library.go
index 291938f..a64cac8 100644
--- a/aconfig/exported_java_aconfig_library.go
+++ b/aconfig/exported_java_aconfig_library.go
@@ -30,7 +30,7 @@
 	// Find all of the aconfig_declarations modules
 	var cacheFiles android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
-		decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
+		decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
 		if !ok {
 			return
 		}
diff --git a/aconfig/init.go b/aconfig/init.go
index de155ab..5fa7e76 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -23,8 +23,7 @@
 )
 
 var (
-	pkgPath = "android/soong/aconfig"
-	pctx    = android.NewPackageContext(pkgPath)
+	pctx = android.NewPackageContext("android/soong/aconfig")
 
 	// For aconfig_declarations: Generate cache file
 	aconfigRule = pctx.AndroidStaticRule("aconfig",
@@ -111,6 +110,7 @@
 	pctx.HostBinToolVariable("soong_zip", "soong_zip")
 
 	gob.Register(android.AconfigDeclarationsProviderData{})
+	gob.Register(android.AconfigReleaseDeclarationsProviderData{})
 	gob.Register(android.ModuleOutPath{})
 }
 
diff --git a/aidl_library/aidl_library_test.go b/aidl_library/aidl_library_test.go
index 01eab0e..1660456 100644
--- a/aidl_library/aidl_library_test.go
+++ b/aidl_library/aidl_library_test.go
@@ -15,8 +15,9 @@
 package aidl_library
 
 import (
-	"android/soong/android"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestAidlLibrary(t *testing.T) {
@@ -46,7 +47,7 @@
 	).RunTest(t).TestContext
 
 	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
-	actualInfo, _ := android.SingletonModuleProvider(ctx, foo, AidlLibraryProvider)
+	actualInfo, _ := android.OtherModuleProvider(ctx, foo, AidlLibraryProvider)
 
 	android.AssertArrayString(
 		t,
@@ -95,7 +96,7 @@
 	).RunTest(t).TestContext
 
 	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
-	actualInfo, _ := android.SingletonModuleProvider(ctx, foo, AidlLibraryProvider)
+	actualInfo, _ := android.OtherModuleProvider(ctx, foo, AidlLibraryProvider)
 
 	android.AssertArrayString(
 		t,
diff --git a/android/Android.bp b/android/Android.bp
index ce27241..96e8133 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -39,7 +39,6 @@
         "arch_module_context.go",
         "base_module_context.go",
         "build_prop.go",
-        "buildinfo_prop.go",
         "compliance_metadata.go",
         "config.go",
         "container.go",
@@ -107,6 +106,7 @@
         "updatable_modules.go",
         "util.go",
         "variable.go",
+        "vintf_fragment.go",
         "visibility.go",
     ],
     testSrcs: [
@@ -149,6 +149,7 @@
         "test_suites_test.go",
         "util_test.go",
         "variable_test.go",
+        "vintf_fragment_test.go",
         "visibility_test.go",
     ],
 }
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index a47e80f..f0675dd 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -141,7 +141,7 @@
 }
 
 func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) {
-	info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
+	info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
 	// If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done.
 	if !ok || len(info.AconfigFiles) == 0 {
 		return
@@ -172,7 +172,7 @@
 	if len(*entries) == 0 {
 		return
 	}
-	info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
+	info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
 	if !ok || len(info.AconfigFiles) == 0 {
 		return
 	}
diff --git a/android/all_teams.go b/android/all_teams.go
index d4bf7d0..01be396 100644
--- a/android/all_teams.go
+++ b/android/all_teams.go
@@ -1,9 +1,11 @@
 package android
 
 import (
-	"android/soong/android/team_proto"
+	"path"
 	"path/filepath"
 
+	"android/soong/android/team_proto"
+
 	"google.golang.org/protobuf/proto"
 )
 
@@ -93,7 +95,7 @@
 		}
 
 		testModInfo := TestModuleInformation{}
-		if tmi, ok := SingletonModuleProvider(ctx, module, TestOnlyProviderKey); ok {
+		if tmi, ok := OtherModuleProvider(ctx, module, TestOnlyProviderKey); ok {
 			testModInfo = tmi
 		}
 
@@ -152,6 +154,11 @@
 		} else {
 			teamProperties, found = t.lookupDefaultTeam(m.bpFile)
 		}
+		// Deal with one blueprint file including another by looking up the default
+		// in the main Android.bp rather than one listed with "build = [My.bp]"
+		if !found {
+			teamProperties, found = t.lookupDefaultTeam(path.Join(path.Dir(m.bpFile), "Android.bp"))
+		}
 
 		trendy_team_id := ""
 		if found {
diff --git a/android/all_teams_test.go b/android/all_teams_test.go
index 96ed92f..fa8c048 100644
--- a/android/all_teams_test.go
+++ b/android/all_teams_test.go
@@ -264,6 +264,84 @@
 	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
 }
 
+func TestPackageLookupForIncludedBlueprintFiles(t *testing.T) {
+	t.Parallel()
+	rootBp := `
+                package { default_team: "team_top"}
+		team {
+			name: "team_top",
+			trendy_team_id: "trendy://team_top",
+		}
+                build = ["include.bp"]
+ `
+	includeBp := `
+		fake {
+			name: "IncludedModule",
+		} `
+
+	ctx := GroupFixturePreparers(
+		prepareForTestWithTeamAndFakes,
+		PrepareForTestWithPackageModule,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
+		}),
+		FixtureAddTextFile("Android.bp", rootBp),
+		FixtureAddTextFile("include.bp", includeBp),
+	).RunTest(t)
+
+	var teams *team_proto.AllTeams
+	teams = getTeamProtoOutput(t, ctx)
+
+	// map of module name -> trendy team name.
+	actualTeams := make(map[string]*string)
+	for _, teamProto := range teams.Teams {
+		actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
+	}
+	expectedTeams := map[string]*string{
+		"IncludedModule": proto.String("trendy://team_top"),
+	}
+	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
+}
+
+func TestPackageLookupForIncludedBlueprintFilesWithPackageInChildBlueprint(t *testing.T) {
+	t.Parallel()
+	rootBp := `
+		team {
+			name: "team_top",
+			trendy_team_id: "trendy://team_top",
+		}
+                build = ["include.bp"]
+ `
+	includeBp := `
+                package { default_team: "team_top"}
+		fake {
+			name: "IncludedModule",
+		} `
+
+	ctx := GroupFixturePreparers(
+		prepareForTestWithTeamAndFakes,
+		PrepareForTestWithPackageModule,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
+		}),
+		FixtureAddTextFile("Android.bp", rootBp),
+		FixtureAddTextFile("include.bp", includeBp),
+	).RunTest(t)
+
+	var teams *team_proto.AllTeams
+	teams = getTeamProtoOutput(t, ctx)
+
+	// map of module name -> trendy team name.
+	actualTeams := make(map[string]*string)
+	for _, teamProto := range teams.Teams {
+		actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
+	}
+	expectedTeams := map[string]*string{
+		"IncludedModule": proto.String("trendy://team_top"),
+	}
+	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
+}
+
 type fakeForTests struct {
 	ModuleBase
 
diff --git a/android/androidmk.go b/android/androidmk.go
index 9699ce5..081bca9 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -170,7 +170,7 @@
 }
 
 func (a *androidMkExtraEntriesContext) Provider(provider blueprint.AnyProviderKey) (any, bool) {
-	return a.ctx.moduleProvider(a.mod, provider)
+	return a.ctx.otherModuleProvider(a.mod, provider)
 }
 
 type AndroidMkExtraEntriesFunc func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries)
@@ -497,7 +497,7 @@
 	ModuleDir(module blueprint.Module) string
 	ModuleSubDir(module blueprint.Module) string
 	Config() Config
-	moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
+	otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 	ModuleType(module blueprint.Module) string
 	OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{})
 }
@@ -536,13 +536,14 @@
 	a.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
 
 	// If the install rule was generated by Soong tell Make about it.
-	if len(base.katiInstalls) > 0 {
+	info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
+	if len(info.KatiInstalls) > 0 {
 		// Assume the primary install file is last since it probably needs to depend on any other
 		// installed files.  If that is not the case we can add a method to specify the primary
 		// installed file.
-		a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", base.katiInstalls[len(base.katiInstalls)-1].to)
-		a.SetString("LOCAL_SOONG_INSTALL_PAIRS", base.katiInstalls.BuiltInstalled())
-		a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", base.katiSymlinks.InstallPaths().Paths())
+		a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", info.KatiInstalls[len(info.KatiInstalls)-1].to)
+		a.SetString("LOCAL_SOONG_INSTALL_PAIRS", info.KatiInstalls.BuiltInstalled())
+		a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", info.KatiSymlinks.InstallPaths().Paths())
 	} else {
 		// Soong may not have generated the install rule also when `no_full_install: true`.
 		// Mark this module as uninstallable in order to prevent Make from creating an
@@ -550,8 +551,8 @@
 		a.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
 	}
 
-	if len(base.testData) > 0 {
-		a.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(base.testData)...)
+	if len(info.TestData) > 0 {
+		a.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...)
 	}
 
 	if am, ok := mod.(ApexModule); ok {
@@ -633,11 +634,11 @@
 		}
 	}
 
-	if licenseMetadata, ok := SingletonModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
+	if licenseMetadata, ok := OtherModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
 		a.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
 	}
 
-	if _, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+	if _, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
 		a.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true)
 	}
 
@@ -861,6 +862,7 @@
 	}
 
 	data := provider.AndroidMk()
+
 	if data.Include == "" {
 		data.Include = "$(BUILD_PREBUILT)"
 	}
@@ -907,6 +909,7 @@
 		case "*phony.PhonyRule": // writes phony deps and acts like `.PHONY`
 		case "*selinux.selinuxContextsModule": // license properties written
 		case "*sysprop.syspropLibrary": // license properties written
+		case "*vintf.vintfCompatibilityMatrixRule": // use case like phony
 		default:
 			if !ctx.Config().IsEnvFalse("ANDROID_REQUIRE_LICENSES") {
 				return fmt.Errorf("custom make rules not allowed for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), ctx.ModuleName(mod))
@@ -918,7 +921,7 @@
 	}
 
 	if !data.Entries.disabled() {
-		if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
 			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
 		}
 	}
@@ -960,7 +963,7 @@
 	}
 
 	if len(entriesList) > 0 && !entriesList[0].disabled() {
-		if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
+		if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
 			*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
 		}
 	}
diff --git a/android/apex.go b/android/apex.go
index ecab8e3..028be57 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -280,7 +280,7 @@
 	//
 	// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
 	// "//apex_available:platform" refers to non-APEX partitions like "system.img".
-	// "com.android.gki.*" matches any APEX module name with the prefix "com.android.gki.".
+	// Prefix pattern (com.foo.*) can be used to match with any APEX name with the prefix(com.foo.).
 	// Default is ["//apex_available:platform"].
 	Apex_available []string
 
@@ -473,14 +473,12 @@
 const (
 	AvailableToPlatform = "//apex_available:platform"
 	AvailableToAnyApex  = "//apex_available:anyapex"
-	AvailableToGkiApex  = "com.android.gki.*"
 )
 
 var (
 	AvailableToRecognziedWildcards = []string{
 		AvailableToPlatform,
 		AvailableToAnyApex,
-		AvailableToGkiApex,
 	}
 )
 
@@ -494,11 +492,27 @@
 	if len(apex_available) == 0 {
 		return what == AvailableToPlatform
 	}
-	return InList(what, apex_available) ||
-		(what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available)) ||
-		(strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available)) ||
-		(what == "com.google.mainline.primary.libs") || // TODO b/248601389
-		(what == "com.google.mainline.go.primary.libs") // TODO b/248601389
+
+	// TODO b/248601389
+	if what == "com.google.mainline.primary.libs" || what == "com.google.mainline.go.primary.libs" {
+		return true
+	}
+
+	for _, apex_name := range apex_available {
+		// exact match.
+		if apex_name == what {
+			return true
+		}
+		// //apex_available:anyapex matches with any apex name, but not //apex_available:platform
+		if apex_name == AvailableToAnyApex && what != AvailableToPlatform {
+			return true
+		}
+		// prefix match.
+		if strings.HasSuffix(apex_name, ".*") && strings.HasPrefix(what, strings.TrimSuffix(apex_name, "*")) {
+			return true
+		}
+	}
+	return false
 }
 
 // Implements ApexModule
@@ -524,7 +538,20 @@
 // This function makes sure that the apex_available property is valid
 func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
 	for _, n := range m.ApexProperties.Apex_available {
-		if n == AvailableToPlatform || n == AvailableToAnyApex || n == AvailableToGkiApex {
+		if n == AvailableToPlatform || n == AvailableToAnyApex {
+			continue
+		}
+		// Prefix pattern should end with .* and has at least two components.
+		if strings.Contains(n, "*") {
+			if !strings.HasSuffix(n, ".*") {
+				mctx.PropertyErrorf("apex_available", "Wildcard should end with .* like com.foo.*")
+			}
+			if strings.Count(n, ".") < 2 {
+				mctx.PropertyErrorf("apex_available", "Wildcard requires two or more components like com.foo.*")
+			}
+			if strings.Count(n, "*") != 1 {
+				mctx.PropertyErrorf("apex_available", "Wildcard is not allowed in the middle.")
+			}
 			continue
 		}
 		if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() {
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index 8b72f8e..4cd8dda 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -119,7 +119,10 @@
 func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BaseModuleContext) {
 	addContentsToProvider := func(p *PrebuiltSelectionInfoMap, m *apexContributions) {
 		for _, content := range m.Contents() {
-			if !ctx.OtherModuleExists(content) && !ctx.Config().AllowMissingDependencies() {
+			// Verify that the module listed in contents exists in the tree
+			// Remove the prebuilt_ prefix to account for partner worksapces where the source module does not
+			// exist, and PrebuiltRenameMutator renames `prebuilt_foo` to `foo`
+			if !ctx.OtherModuleExists(content) && !ctx.OtherModuleExists(RemoveOptionalPrebuiltPrefix(content)) && !ctx.Config().AllowMissingDependencies() {
 				ctx.ModuleErrorf("%s listed in apex_contributions %s does not exist\n", content, m.Name())
 			}
 			pi := &PrebuiltSelectionInfo{
diff --git a/android/build_prop.go b/android/build_prop.go
index 45c17c3..13d59f9 100644
--- a/android/build_prop.go
+++ b/android/build_prop.go
@@ -31,16 +31,15 @@
 	// properties in prop_files.
 	Block_list []string
 
-	// Path to the input prop files. The contents of the files are directly
-	// emitted to the output
-	Prop_files []string `android:"path"`
-
 	// Files to be appended at the end of build.prop. These files are appended after
 	// post_process_props without any further checking.
 	Footer_files []string `android:"path"`
 
 	// Path to a JSON file containing product configs.
 	Product_config *string `android:"path"`
+
+	// Optional subdirectory under which this file is installed into
+	Relative_install_path *string
 }
 
 type buildPropModule struct {
@@ -56,16 +55,66 @@
 	return proptools.StringDefault(p.properties.Stem, "build.prop")
 }
 
+func (p *buildPropModule) propFiles(ctx ModuleContext) Paths {
+	partition := p.partition(ctx.DeviceConfig())
+	if partition == "system" {
+		return ctx.Config().SystemPropFiles(ctx)
+	} else if partition == "system_ext" {
+		return ctx.Config().SystemExtPropFiles(ctx)
+	} else if partition == "product" {
+		return ctx.Config().ProductPropFiles(ctx)
+	}
+	return nil
+}
+
+func shouldAddBuildThumbprint(config Config) bool {
+	knownOemProperties := []string{
+		"ro.product.brand",
+		"ro.product.name",
+		"ro.product.device",
+	}
+
+	for _, knownProp := range knownOemProperties {
+		if InList(knownProp, config.OemProperties()) {
+			return true
+		}
+	}
+	return false
+}
+
+// Can't use PartitionTag() because PartitionTag() returns the partition this module is actually
+// installed (e.g. odm module's partition tag can be either "odm" or "vendor")
+func (p *buildPropModule) partition(config DeviceConfig) string {
+	if p.SocSpecific() {
+		return "vendor"
+	} else if p.DeviceSpecific() {
+		return "odm"
+	} else if p.ProductSpecific() {
+		return "product"
+	} else if p.SystemExtSpecific() {
+		return "system_ext"
+	}
+	return "system"
+}
+
+var validPartitions = []string{
+	"system",
+	"system_ext",
+	"product",
+	"odm",
+}
+
 func (p *buildPropModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 	p.outputFilePath = PathForModuleOut(ctx, "build.prop").OutputPath
 	if !ctx.Config().KatiEnabled() {
 		WriteFileRule(ctx, p.outputFilePath, "# no build.prop if kati is disabled")
+		ctx.SetOutputFiles(Paths{p.outputFilePath}, "")
 		return
 	}
 
-	partition := p.PartitionTag(ctx.DeviceConfig())
-	if partition != "system" {
-		ctx.PropertyErrorf("partition", "unsupported partition %q: only \"system\" is supported", partition)
+	partition := p.partition(ctx.DeviceConfig())
+	if !InList(partition, validPartitions) {
+		ctx.PropertyErrorf("partition", "unsupported partition %q: only %q are supported", partition, validPartitions)
 		return
 	}
 
@@ -93,6 +142,7 @@
 	cmd.FlagWithInput("--platform-preview-sdk-fingerprint-file=", ApiFingerprintPath(ctx))
 	cmd.FlagWithInput("--product-config=", PathForModuleSrc(ctx, proptools.String(p.properties.Product_config)))
 	cmd.FlagWithArg("--partition=", partition)
+	cmd.FlagForEachInput("--prop-files=", p.propFiles(ctx))
 	cmd.FlagWithOutput("--out=", p.outputFilePath)
 
 	postProcessCmd := rule.Command().BuiltTool("post_process_props")
@@ -100,7 +150,12 @@
 		postProcessCmd.Flag("--allow-dup")
 	}
 	postProcessCmd.FlagWithArg("--sdk-version ", config.PlatformSdkVersion().String())
-	postProcessCmd.FlagWithInput("--kernel-version-file-for-uffd-gc ", PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt"))
+	if ctx.Config().EnableUffdGc() == "default" {
+		postProcessCmd.FlagWithInput("--kernel-version-file-for-uffd-gc ", PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt"))
+	} else {
+		// still need to pass an empty string to kernel-version-file-for-uffd-gc
+		postProcessCmd.FlagWithArg("--kernel-version-file-for-uffd-gc ", `""`)
+	}
 	postProcessCmd.Text(p.outputFilePath.String())
 	postProcessCmd.Flags(p.properties.Block_list)
 
@@ -108,12 +163,25 @@
 
 	rule.Build(ctx.ModuleName(), "generating build.prop")
 
-	p.installPath = PathForModuleInstall(ctx)
+	p.installPath = PathForModuleInstall(ctx, proptools.String(p.properties.Relative_install_path))
 	ctx.InstallFile(p.installPath, p.stem(), p.outputFilePath)
 
 	ctx.SetOutputFiles(Paths{p.outputFilePath}, "")
 }
 
+func (p *buildPropModule) AndroidMkEntries() []AndroidMkEntries {
+	return []AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: OptionalPathForPath(p.outputFilePath),
+		ExtraEntries: []AndroidMkExtraEntriesFunc{
+			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", p.installPath.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
+			},
+		},
+	}}
+}
+
 // build_prop module generates {partition}/build.prop file. At first common build properties are
 // printed based on Soong config variables. And then prop_files are printed as-is. Finally,
 // post_process_props tool is run to check if the result build.prop is valid or not.
diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go
deleted file mode 100644
index bba4c0d..0000000
--- a/android/buildinfo_prop.go
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2022 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-import (
-	"github.com/google/blueprint/proptools"
-)
-
-func init() {
-	ctx := InitRegistrationContext
-	ctx.RegisterModuleType("buildinfo_prop", buildinfoPropFactory)
-}
-
-type buildinfoPropProperties struct {
-	// Whether this module is directly installable to one of the partitions. Default: true.
-	Installable *bool
-
-	Product_config *string `android:"path"`
-}
-
-type buildinfoPropModule struct {
-	ModuleBase
-
-	properties buildinfoPropProperties
-
-	outputFilePath OutputPath
-	installPath    InstallPath
-}
-
-func (p *buildinfoPropModule) installable() bool {
-	return proptools.BoolDefault(p.properties.Installable, true)
-}
-
-func shouldAddBuildThumbprint(config Config) bool {
-	knownOemProperties := []string{
-		"ro.product.brand",
-		"ro.product.name",
-		"ro.product.device",
-	}
-
-	for _, knownProp := range knownOemProperties {
-		if InList(knownProp, config.OemProperties()) {
-			return true
-		}
-	}
-	return false
-}
-
-func (p *buildinfoPropModule) GenerateAndroidBuildActions(ctx ModuleContext) {
-	if ctx.ModuleName() != "buildinfo.prop" || ctx.ModuleDir() != "build/soong" {
-		ctx.ModuleErrorf("There can only be one buildinfo_prop module in build/soong")
-		return
-	}
-	p.outputFilePath = PathForModuleOut(ctx, p.Name()).OutputPath
-	ctx.SetOutputFiles(Paths{p.outputFilePath}, "")
-
-	if !ctx.Config().KatiEnabled() {
-		WriteFileRule(ctx, p.outputFilePath, "# no buildinfo.prop if kati is disabled")
-		return
-	}
-
-	rule := NewRuleBuilder(pctx, ctx)
-
-	config := ctx.Config()
-
-	cmd := rule.Command().BuiltTool("buildinfo")
-
-	cmd.FlagWithInput("--build-hostname-file=", config.BuildHostnameFile(ctx))
-	// Note: depending on BuildNumberFile will cause the build.prop file to be rebuilt
-	// every build, but that's intentional.
-	cmd.FlagWithInput("--build-number-file=", config.BuildNumberFile(ctx))
-	// Export build thumbprint only if the product has specified at least one oem fingerprint property
-	// b/17888863
-	if shouldAddBuildThumbprint(config) {
-		// In the previous make implementation, a dependency was not added on the thumbprint file
-		cmd.FlagWithArg("--build-thumbprint-file=", config.BuildThumbprintFile(ctx).String())
-	}
-	cmd.FlagWithArg("--build-username=", config.Getenv("BUILD_USERNAME"))
-	// Technically we should also have a dependency on BUILD_DATETIME_FILE,
-	// but it can be either an absolute or relative path, which is hard to turn into
-	// a Path object. So just rely on the BuildNumberFile always changing to cause
-	// us to rebuild.
-	cmd.FlagWithArg("--date-file=", ctx.Config().Getenv("BUILD_DATETIME_FILE"))
-	cmd.FlagWithInput("--platform-preview-sdk-fingerprint-file=", ApiFingerprintPath(ctx))
-	cmd.FlagWithInput("--product-config=", PathForModuleSrc(ctx, proptools.String(p.properties.Product_config)))
-	cmd.FlagWithOutput("--out=", p.outputFilePath)
-
-	rule.Build(ctx.ModuleName(), "generating buildinfo props")
-
-	if !p.installable() {
-		p.SkipInstall()
-	}
-
-	p.installPath = PathForModuleInstall(ctx)
-	ctx.InstallFile(p.installPath, p.Name(), p.outputFilePath)
-}
-
-func (p *buildinfoPropModule) AndroidMkEntries() []AndroidMkEntries {
-	return []AndroidMkEntries{{
-		Class:      "ETC",
-		OutputFile: OptionalPathForPath(p.outputFilePath),
-		ExtraEntries: []AndroidMkExtraEntriesFunc{
-			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", p.installPath.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
-				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
-			},
-		},
-	}}
-}
-
-// buildinfo_prop module generates a build.prop file, which contains a set of common
-// system/build.prop properties, such as ro.build.version.*.  Not all properties are implemented;
-// currently this module is only for microdroid.
-func buildinfoPropFactory() Module {
-	module := &buildinfoPropModule{}
-	module.AddProperties(&module.properties)
-	InitAndroidModule(module)
-	return module
-}
diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go
index 6ea6654..4c92f71 100644
--- a/android/compliance_metadata.go
+++ b/android/compliance_metadata.go
@@ -160,7 +160,7 @@
 // buildComplianceMetadataProvider starts with the ModuleContext.ComplianceMetadataInfo() and fills in more common metadata
 // for different module types without accessing their private fields but through android.Module interface
 // and public/private fields of package android. The final metadata is stored to a module's ComplianceMetadataProvider.
-func buildComplianceMetadataProvider(ctx ModuleContext, m *ModuleBase) {
+func buildComplianceMetadataProvider(ctx *moduleContext, m *ModuleBase) {
 	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
 	complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.NAME, m.Name())
 	complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.PACKAGE, ctx.ModuleDir())
@@ -186,9 +186,9 @@
 		}
 
 		var installed InstallPaths
-		installed = append(installed, m.module.FilesToInstall()...)
-		installed = append(installed, m.katiInstalls.InstallPaths()...)
-		installed = append(installed, m.katiSymlinks.InstallPaths()...)
+		installed = append(installed, ctx.installFiles...)
+		installed = append(installed, ctx.katiInstalls.InstallPaths()...)
+		installed = append(installed, ctx.katiSymlinks.InstallPaths()...)
 		installed = append(installed, m.katiInitRcInstalls.InstallPaths()...)
 		installed = append(installed, m.katiVintfInstalls.InstallPaths()...)
 		complianceMetadataInfo.SetListValue(ComplianceMetadataProp.INSTALLED_FILES, FirstUniqueStrings(installed.Strings()))
@@ -267,7 +267,7 @@
 			writerToCsv(csvWriter, metadata)
 			return
 		}
-		if provider, ok := ctx.moduleProvider(module, ComplianceMetadataProvider); ok {
+		if provider, ok := ctx.otherModuleProvider(module, ComplianceMetadataProvider); ok {
 			metadataInfo := provider.(*ComplianceMetadataInfo)
 			rowId = rowId + 1
 			metadata := []string{strconv.Itoa(rowId)}
diff --git a/android/config.go b/android/config.go
index cadc929..d6d76a4 100644
--- a/android/config.go
+++ b/android/config.go
@@ -343,9 +343,6 @@
 	// modules that aren't mixed-built for at least one variant will cause a build
 	// failure
 	ensureAllowlistIntegrity bool
-
-	// List of Api libraries that contribute to Api surfaces.
-	apiLibraries map[string]struct{}
 }
 
 type deviceConfig struct {
@@ -595,40 +592,6 @@
 	setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
 	setBuildMode(cmdArgs.DocFile, GenerateDocFile)
 
-	// TODO(b/276958307): Replace the hardcoded list to a sdk_library local prop.
-	config.apiLibraries = map[string]struct{}{
-		"android.net.ipsec.ike":             {},
-		"art.module.public.api":             {},
-		"conscrypt.module.public.api":       {},
-		"framework-adservices":              {},
-		"framework-appsearch":               {},
-		"framework-bluetooth":               {},
-		"framework-configinfrastructure":    {},
-		"framework-connectivity":            {},
-		"framework-connectivity-t":          {},
-		"framework-devicelock":              {},
-		"framework-graphics":                {},
-		"framework-healthfitness":           {},
-		"framework-location":                {},
-		"framework-media":                   {},
-		"framework-mediaprovider":           {},
-		"framework-nfc":                     {},
-		"framework-ondevicepersonalization": {},
-		"framework-pdf":                     {},
-		"framework-pdf-v":                   {},
-		"framework-permission":              {},
-		"framework-permission-s":            {},
-		"framework-scheduling":              {},
-		"framework-sdkextensions":           {},
-		"framework-statsd":                  {},
-		"framework-sdksandbox":              {},
-		"framework-tethering":               {},
-		"framework-uwb":                     {},
-		"framework-virtualization":          {},
-		"framework-wifi":                    {},
-		"i18n.module.public.api":            {},
-	}
-
 	config.productVariables.Build_from_text_stub = boolPtr(config.BuildFromTextStub())
 
 	return Config{config}, err
@@ -1979,17 +1942,6 @@
 	c.productVariables.Build_from_text_stub = boolPtr(b)
 }
 
-func (c *config) SetApiLibraries(libs []string) {
-	c.apiLibraries = make(map[string]struct{})
-	for _, lib := range libs {
-		c.apiLibraries[lib] = struct{}{}
-	}
-}
-
-func (c *config) GetApiLibraries() map[string]struct{} {
-	return c.apiLibraries
-}
-
 func (c *deviceConfig) CheckVendorSeappViolations() bool {
 	return Bool(c.config.productVariables.CheckVendorSeappViolations)
 }
@@ -2004,41 +1956,41 @@
 }
 
 var (
-	mainlineApexContributionBuildFlags = []string{
-		"RELEASE_APEX_CONTRIBUTIONS_ADBD",
-		"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES",
-		"RELEASE_APEX_CONTRIBUTIONS_APPSEARCH",
-		"RELEASE_APEX_CONTRIBUTIONS_ART",
-		"RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH",
-		"RELEASE_APEX_CONTRIBUTIONS_CAPTIVEPORTALLOGIN",
-		"RELEASE_APEX_CONTRIBUTIONS_CELLBROADCAST",
-		"RELEASE_APEX_CONTRIBUTIONS_CONFIGINFRASTRUCTURE",
-		"RELEASE_APEX_CONTRIBUTIONS_CONNECTIVITY",
-		"RELEASE_APEX_CONTRIBUTIONS_CONSCRYPT",
-		"RELEASE_APEX_CONTRIBUTIONS_CRASHRECOVERY",
-		"RELEASE_APEX_CONTRIBUTIONS_DEVICELOCK",
-		"RELEASE_APEX_CONTRIBUTIONS_DOCUMENTSUIGOOGLE",
-		"RELEASE_APEX_CONTRIBUTIONS_EXTSERVICES",
-		"RELEASE_APEX_CONTRIBUTIONS_HEALTHFITNESS",
-		"RELEASE_APEX_CONTRIBUTIONS_IPSEC",
-		"RELEASE_APEX_CONTRIBUTIONS_MEDIA",
-		"RELEASE_APEX_CONTRIBUTIONS_MEDIAPROVIDER",
-		"RELEASE_APEX_CONTRIBUTIONS_MODULE_METADATA",
-		"RELEASE_APEX_CONTRIBUTIONS_NETWORKSTACKGOOGLE",
-		"RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS",
-		"RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION",
-		"RELEASE_APEX_CONTRIBUTIONS_PERMISSION",
-		"RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS",
-		"RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING",
-		"RELEASE_APEX_CONTRIBUTIONS_RESOLV",
-		"RELEASE_APEX_CONTRIBUTIONS_SCHEDULING",
-		"RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS",
-		"RELEASE_APEX_CONTRIBUTIONS_SWCODEC",
-		"RELEASE_APEX_CONTRIBUTIONS_STATSD",
-		"RELEASE_APEX_CONTRIBUTIONS_TELEMETRY_TVP",
-		"RELEASE_APEX_CONTRIBUTIONS_TZDATA",
-		"RELEASE_APEX_CONTRIBUTIONS_UWB",
-		"RELEASE_APEX_CONTRIBUTIONS_WIFI",
+	mainlineApexContributionBuildFlagsToApexNames = map[string]string{
+		"RELEASE_APEX_CONTRIBUTIONS_ADBD":                    "com.android.adbd",
+		"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES":              "com.android.adservices",
+		"RELEASE_APEX_CONTRIBUTIONS_APPSEARCH":               "com.android.appsearch",
+		"RELEASE_APEX_CONTRIBUTIONS_ART":                     "com.android.art",
+		"RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH":               "com.android.btservices",
+		"RELEASE_APEX_CONTRIBUTIONS_CAPTIVEPORTALLOGIN":      "",
+		"RELEASE_APEX_CONTRIBUTIONS_CELLBROADCAST":           "com.android.cellbroadcast",
+		"RELEASE_APEX_CONTRIBUTIONS_CONFIGINFRASTRUCTURE":    "com.android.configinfrastructure",
+		"RELEASE_APEX_CONTRIBUTIONS_CONNECTIVITY":            "com.android.tethering",
+		"RELEASE_APEX_CONTRIBUTIONS_CONSCRYPT":               "com.android.conscrypt",
+		"RELEASE_APEX_CONTRIBUTIONS_CRASHRECOVERY":           "",
+		"RELEASE_APEX_CONTRIBUTIONS_DEVICELOCK":              "com.android.devicelock",
+		"RELEASE_APEX_CONTRIBUTIONS_DOCUMENTSUIGOOGLE":       "",
+		"RELEASE_APEX_CONTRIBUTIONS_EXTSERVICES":             "com.android.extservices",
+		"RELEASE_APEX_CONTRIBUTIONS_HEALTHFITNESS":           "com.android.healthfitness",
+		"RELEASE_APEX_CONTRIBUTIONS_IPSEC":                   "com.android.ipsec",
+		"RELEASE_APEX_CONTRIBUTIONS_MEDIA":                   "com.android.media",
+		"RELEASE_APEX_CONTRIBUTIONS_MEDIAPROVIDER":           "com.android.mediaprovider",
+		"RELEASE_APEX_CONTRIBUTIONS_MODULE_METADATA":         "",
+		"RELEASE_APEX_CONTRIBUTIONS_NETWORKSTACKGOOGLE":      "",
+		"RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS":          "com.android.neuralnetworks",
+		"RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION": "com.android.ondevicepersonalization",
+		"RELEASE_APEX_CONTRIBUTIONS_PERMISSION":              "com.android.permission",
+		"RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS":            "",
+		"RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING":   "com.android.rkpd",
+		"RELEASE_APEX_CONTRIBUTIONS_RESOLV":                  "com.android.resolv",
+		"RELEASE_APEX_CONTRIBUTIONS_SCHEDULING":              "com.android.scheduling",
+		"RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS":           "com.android.sdkext",
+		"RELEASE_APEX_CONTRIBUTIONS_SWCODEC":                 "com.android.media.swcodec",
+		"RELEASE_APEX_CONTRIBUTIONS_STATSD":                  "com.android.os.statsd",
+		"RELEASE_APEX_CONTRIBUTIONS_TELEMETRY_TVP":           "",
+		"RELEASE_APEX_CONTRIBUTIONS_TZDATA":                  "com.android.tzdata",
+		"RELEASE_APEX_CONTRIBUTIONS_UWB":                     "com.android.uwb",
+		"RELEASE_APEX_CONTRIBUTIONS_WIFI":                    "com.android.wifi",
 	}
 )
 
@@ -2046,7 +1998,7 @@
 // Each mainline module will have one entry in the list
 func (c *config) AllApexContributions() []string {
 	ret := []string{}
-	for _, f := range mainlineApexContributionBuildFlags {
+	for _, f := range SortedKeys(mainlineApexContributionBuildFlagsToApexNames) {
 		if val, exists := c.GetBuildFlag(f); exists && val != "" {
 			ret = append(ret, val)
 		}
@@ -2054,6 +2006,10 @@
 	return ret
 }
 
+func (c *config) AllMainlineApexNames() []string {
+	return SortedStringValues(mainlineApexContributionBuildFlagsToApexNames)
+}
+
 func (c *config) BuildIgnoreApexContributionContents() *bool {
 	return c.productVariables.BuildIgnoreApexContributionContents
 }
@@ -2081,3 +2037,35 @@
 
 	return Bool(c.productVariables.Eng)
 }
+
+func (c *config) SystemPropFiles(ctx PathContext) Paths {
+	return PathsForSource(ctx, c.productVariables.SystemPropFiles)
+}
+
+func (c *config) SystemExtPropFiles(ctx PathContext) Paths {
+	return PathsForSource(ctx, c.productVariables.SystemExtPropFiles)
+}
+
+func (c *config) ProductPropFiles(ctx PathContext) Paths {
+	return PathsForSource(ctx, c.productVariables.ProductPropFiles)
+}
+
+func (c *config) EnableUffdGc() string {
+	return String(c.productVariables.EnableUffdGc)
+}
+
+func (c *config) DeviceFrameworkCompatibilityMatrixFile() []string {
+	return c.productVariables.DeviceFrameworkCompatibilityMatrixFile
+}
+
+func (c *config) DeviceProductCompatibilityMatrixFile() []string {
+	return c.productVariables.DeviceProductCompatibilityMatrixFile
+}
+
+func (c *config) BoardAvbEnable() bool {
+	return Bool(c.productVariables.BoardAvbEnable)
+}
+
+func (c *config) BoardAvbSystemAddHashtreeFooterArgs() []string {
+	return c.productVariables.BoardAvbSystemAddHashtreeFooterArgs
+}
diff --git a/android/config_test.go b/android/config_test.go
index ca7c7f8..7732168 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -150,12 +150,7 @@
 
 	for _, tc := range testCases {
 		fixture := GroupFixturePreparers(
-			FixtureModifyProductVariables(func(vars FixtureProductVariables) {
-				if vars.BuildFlags == nil {
-					vars.BuildFlags = make(map[string]string)
-				}
-				vars.BuildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"] = tc.flag
-			}),
+			PrepareForTestWithBuildFlag("RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS", tc.flag),
 		)
 		actual := fixture.RunTest(t).Config.ReleaseAconfigExtraReleaseConfigs()
 		AssertArrayString(t, tc.name, tc.expected, actual)
diff --git a/android/configurable_properties.go b/android/configurable_properties.go
index dad42fa..2c794a1 100644
--- a/android/configurable_properties.go
+++ b/android/configurable_properties.go
@@ -26,3 +26,9 @@
 		resultCases,
 	)
 }
+
+func NewSimpleConfigurable[T proptools.ConfigurableElements](value T) proptools.Configurable[T] {
+	return proptools.NewConfigurable(nil, []proptools.ConfigurableCase[T]{
+		proptools.NewConfigurableCase(nil, &value),
+	})
+}
diff --git a/android/container.go b/android/container.go
index c4fdd9c..10aff4d 100644
--- a/android/container.go
+++ b/android/container.go
@@ -21,35 +21,207 @@
 	"github.com/google/blueprint"
 )
 
+// ----------------------------------------------------------------------------
+// Start of the definitions of exception functions and the lookup table.
+//
+// Functions cannot be used as a value passed in providers, because functions are not
+// hashable. As a workaround, the [exceptionHandleFuncLabel] enum values are passed using providers,
+// and the corresponding functions are called from [exceptionHandleFunctionsTable] map.
+// ----------------------------------------------------------------------------
+
+type exceptionHandleFunc func(ModuleContext, Module, Module) bool
+
 type StubsAvailableModule interface {
 	IsStubsModule() bool
 }
 
 // Returns true if the dependency module is a stubs module
-var depIsStubsModule = func(_ ModuleContext, _, dep Module) bool {
+var depIsStubsModule exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
 	if stubsModule, ok := dep.(StubsAvailableModule); ok {
 		return stubsModule.IsStubsModule()
 	}
 	return false
 }
 
+// Returns true if the dependency module belongs to any of the apexes.
+var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _, dep Module) bool {
+	depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
+	return InList(ApexContainer, depContainersInfo.belongingContainers)
+}
+
+// Returns true if the module and the dependent module belongs to common apexes.
+var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m, dep Module) bool {
+	mContainersInfo, _ := getContainerModuleInfo(mctx, m)
+	depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
+
+	return HasIntersection(mContainersInfo.ApexNames(), depContainersInfo.ApexNames())
+}
+
+// Returns true when all apexes that the module belongs to are non updatable.
+// For an apex module to be allowed to depend on a non-apex partition module,
+// all apexes that the module belong to must be non updatable.
+var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m, _ Module) bool {
+	mContainersInfo, _ := getContainerModuleInfo(mctx, m)
+
+	return !mContainersInfo.UpdatableApex()
+}
+
+// Returns true if the dependency is added via dependency tags that are not used to tag dynamic
+// dependency tags.
+var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
+	mInstallable, _ := m.(InstallableModule)
+	depTag := ctx.OtherModuleDependencyTag(dep)
+	return !InList(depTag, mInstallable.DynamicDependencyTags())
+}
+
+// Returns true if the dependency is added via dependency tags that are not used to tag static
+// or dynamic dependency tags. These dependencies do not affect the module in compile time or in
+// runtime, thus are not significant enough to raise an error.
+var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
+	mInstallable, _ := m.(InstallableModule)
+	depTag := ctx.OtherModuleDependencyTag(dep)
+	return !InList(depTag, append(mInstallable.StaticDependencyTags(), mInstallable.DynamicDependencyTags()...))
+}
+
+var globallyAllowlistedDependencies = []string{
+	// Modules that provide annotations used within the platform and apexes.
+	"aconfig-annotations-lib",
+	"framework-annotations-lib",
+	"unsupportedappusage",
+
+	// framework-res provides core resources essential for building apps and system UI.
+	// This module is implicitly added as a dependency for java modules even when the
+	// dependency specifies sdk_version.
+	"framework-res",
+}
+
+// Returns true when the dependency is globally allowlisted for inter-container dependency
+var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
+	return InList(dep.Name(), globallyAllowlistedDependencies)
+}
+
 // Labels of exception functions, which are used to determine special dependencies that allow
 // otherwise restricted inter-container dependencies
 type exceptionHandleFuncLabel int
 
 const (
 	checkStubs exceptionHandleFuncLabel = iota
+	checkApexModule
+	checkInCommonApexes
+	checkApexIsNonUpdatable
+	checkNotDynamicDepTag
+	checkNotStaticOrDynamicDepTag
+	checkGlobalAllowlistedDep
 )
 
-// Functions cannot be used as a value passed in providers, because functions are not
-// hashable. As a workaround, the exceptionHandleFunc enum values are passed using providers,
-// and the corresponding functions are called from this map.
-var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]func(ModuleContext, Module, Module) bool{
-	checkStubs: depIsStubsModule,
+// Map of [exceptionHandleFuncLabel] to the [exceptionHandleFunc]
+var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]exceptionHandleFunc{
+	checkStubs:                    depIsStubsModule,
+	checkApexModule:               depIsApexModule,
+	checkInCommonApexes:           belongsToCommonApexes,
+	checkApexIsNonUpdatable:       belongsToNonUpdatableApex,
+	checkNotDynamicDepTag:         depIsNotDynamicDepTag,
+	checkNotStaticOrDynamicDepTag: depIsNotStaticOrDynamicDepTag,
+	checkGlobalAllowlistedDep:     depIsGloballyAllowlisted,
 }
 
+// ----------------------------------------------------------------------------
+// Start of the definitions of container determination functions.
+//
+// Similar to the above section, below defines the functions used to determine
+// the container of each modules.
+// ----------------------------------------------------------------------------
+
+type containerBoundaryFunc func(mctx ModuleContext) bool
+
+var vendorContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	m, ok := mctx.Module().(ImageInterface)
+	return mctx.Module().InstallInVendor() || (ok && m.VendorVariantNeeded(mctx))
+}
+
+var systemContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	module := mctx.Module()
+
+	return !module.InstallInTestcases() &&
+		!module.InstallInData() &&
+		!module.InstallInRamdisk() &&
+		!module.InstallInVendorRamdisk() &&
+		!module.InstallInDebugRamdisk() &&
+		!module.InstallInRecovery() &&
+		!module.InstallInVendor() &&
+		!module.InstallInOdm() &&
+		!module.InstallInProduct() &&
+		determineModuleKind(module.base(), mctx.blueprintBaseModuleContext()) == platformModule
+}
+
+var productContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	m, ok := mctx.Module().(ImageInterface)
+	return mctx.Module().InstallInProduct() || (ok && m.ProductVariantNeeded(mctx))
+}
+
+var apexContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	_, ok := ModuleProvider(mctx, AllApexInfoProvider)
+	return ok
+}
+
+var ctsContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	props := mctx.Module().GetProperties()
+	for _, prop := range props {
+		val := reflect.ValueOf(prop).Elem()
+		if val.Kind() == reflect.Struct {
+			testSuites := val.FieldByName("Test_suites")
+			if testSuites.IsValid() && testSuites.Kind() == reflect.Slice && slices.Contains(testSuites.Interface().([]string), "cts") {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+type unstableInfo struct {
+	// Determines if the module contains the private APIs of the platform.
+	ContainsPlatformPrivateApis bool
+}
+
+var unstableInfoProvider = blueprint.NewProvider[unstableInfo]()
+
+func determineUnstableModule(mctx ModuleContext) bool {
+	module := mctx.Module()
+	unstableModule := module.Name() == "framework-minus-apex"
+	if installable, ok := module.(InstallableModule); ok {
+		for _, staticDepTag := range installable.StaticDependencyTags() {
+			mctx.VisitDirectDepsWithTag(staticDepTag, func(dep Module) {
+				if unstableInfo, ok := OtherModuleProvider(mctx, dep, unstableInfoProvider); ok {
+					unstableModule = unstableModule || unstableInfo.ContainsPlatformPrivateApis
+				}
+			})
+		}
+	}
+	return unstableModule
+}
+
+var unstableContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
+	return determineUnstableModule(mctx)
+}
+
+// Map of [*container] to the [containerBoundaryFunc]
+var containerBoundaryFunctionsTable = map[*container]containerBoundaryFunc{
+	VendorContainer:   vendorContainerBoundaryFunc,
+	SystemContainer:   systemContainerBoundaryFunc,
+	ProductContainer:  productContainerBoundaryFunc,
+	ApexContainer:     apexContainerBoundaryFunc,
+	CtsContainer:      ctsContainerBoundaryFunc,
+	UnstableContainer: unstableContainerBoundaryFunc,
+}
+
+// ----------------------------------------------------------------------------
+// End of the definitions of container determination functions.
+// ----------------------------------------------------------------------------
+
 type InstallableModule interface {
-	EnforceApiContainerChecks() bool
+	ContainersInfo() ContainersInfo
+	StaticDependencyTags() []blueprint.DependencyTag
+	DynamicDependencyTags() []blueprint.DependencyTag
 }
 
 type restriction struct {
@@ -77,6 +249,7 @@
 		name:       VendorVariation,
 		restricted: nil,
 	}
+
 	SystemContainer = &container{
 		name: "system",
 		restricted: []restriction{
@@ -86,10 +259,15 @@
 					"not allowed to depend on the vendor partition module, in order to support " +
 					"independent development/update cycles and to support the Generic System " +
 					"Image. Try depending on HALs, VNDK or AIDL instead.",
-				allowedExceptions: []exceptionHandleFuncLabel{},
+				allowedExceptions: []exceptionHandleFuncLabel{
+					checkStubs,
+					checkNotDynamicDepTag,
+					checkGlobalAllowlistedDep,
+				},
 			},
 		},
 	}
+
 	ProductContainer = &container{
 		name: ProductVariation,
 		restricted: []restriction{
@@ -98,24 +276,50 @@
 				errorMessage: "Module belonging to the product partition is not allowed to " +
 					"depend on the vendor partition module, as this may lead to security " +
 					"vulnerabilities. Try depending on the HALs or utilize AIDL instead.",
-				allowedExceptions: []exceptionHandleFuncLabel{},
+				allowedExceptions: []exceptionHandleFuncLabel{
+					checkStubs,
+					checkNotDynamicDepTag,
+					checkGlobalAllowlistedDep,
+				},
 			},
 		},
 	}
+
 	ApexContainer = initializeApexContainer()
-	CtsContainer  = &container{
+
+	CtsContainer = &container{
 		name: "cts",
 		restricted: []restriction{
 			{
-				dependency: SystemContainer,
-				errorMessage: "CTS module should not depend on the modules belonging to the " +
-					"system partition, including \"framework\". Depending on the system " +
-					"partition may lead to disclosure of implementation details and regression " +
-					"due to API changes across platform versions. Try depending on the stubs instead.",
-				allowedExceptions: []exceptionHandleFuncLabel{checkStubs},
+				dependency: UnstableContainer,
+				errorMessage: "CTS module should not depend on the modules that contain the " +
+					"platform implementation details, including \"framework\". Depending on these " +
+					"modules may lead to disclosure of implementation details and regression " +
+					"due to API changes across platform versions. Try depending on the stubs instead " +
+					"and ensure that the module sets an appropriate 'sdk_version'.",
+				allowedExceptions: []exceptionHandleFuncLabel{
+					checkStubs,
+					checkNotStaticOrDynamicDepTag,
+					checkGlobalAllowlistedDep,
+				},
 			},
 		},
 	}
+
+	// Container signifying that the module contains unstable platform private APIs
+	UnstableContainer = &container{
+		name:       "unstable",
+		restricted: nil,
+	}
+
+	allContainers = []*container{
+		VendorContainer,
+		SystemContainer,
+		ProductContainer,
+		ApexContainer,
+		CtsContainer,
+		UnstableContainer,
+	}
 )
 
 func initializeApexContainer() *container {
@@ -128,7 +332,14 @@
 					"modules belonging to the system partition. Either statically depend on the " +
 					"module or convert the depending module to java_sdk_library and depend on " +
 					"the stubs.",
-				allowedExceptions: []exceptionHandleFuncLabel{checkStubs},
+				allowedExceptions: []exceptionHandleFuncLabel{
+					checkStubs,
+					checkApexModule,
+					checkInCommonApexes,
+					checkApexIsNonUpdatable,
+					checkNotStaticOrDynamicDepTag,
+					checkGlobalAllowlistedDep,
+				},
 			},
 		},
 	}
@@ -139,7 +350,12 @@
 			"modules belonging to other Apex(es). Either include the depending " +
 			"module in the Apex or convert the depending module to java_sdk_library " +
 			"and depend on its stubs.",
-		allowedExceptions: []exceptionHandleFuncLabel{checkStubs},
+		allowedExceptions: []exceptionHandleFuncLabel{
+			checkStubs,
+			checkInCommonApexes,
+			checkNotStaticOrDynamicDepTag,
+			checkGlobalAllowlistedDep,
+		},
 	})
 
 	return apexContainer
@@ -155,68 +371,38 @@
 	return c.belongingContainers
 }
 
-var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]()
-
-// Determines if the module can be installed in the system partition or not.
-// Logic is identical to that of modulePartition(...) defined in paths.go
-func installInSystemPartition(ctx ModuleContext) bool {
-	module := ctx.Module()
-	return !module.InstallInTestcases() &&
-		!module.InstallInData() &&
-		!module.InstallInRamdisk() &&
-		!module.InstallInVendorRamdisk() &&
-		!module.InstallInDebugRamdisk() &&
-		!module.InstallInRecovery() &&
-		!module.InstallInVendor() &&
-		!module.InstallInOdm() &&
-		!module.InstallInProduct() &&
-		determineModuleKind(module.base(), ctx.blueprintBaseModuleContext()) == platformModule
+func (c *ContainersInfo) ApexNames() (ret []string) {
+	for _, apex := range c.belongingApexes {
+		ret = append(ret, apex.InApexModules...)
+	}
+	slices.Sort(ret)
+	return ret
 }
 
-func generateContainerInfo(ctx ModuleContext) ContainersInfo {
-	inSystem := installInSystemPartition(ctx)
-	inProduct := ctx.Module().InstallInProduct()
-	inVendor := ctx.Module().InstallInVendor()
-	inCts := false
-	inApex := false
-
-	if m, ok := ctx.Module().(ImageInterface); ok {
-		inProduct = inProduct || m.ProductVariantNeeded(ctx)
-		inVendor = inVendor || m.VendorVariantNeeded(ctx)
+// Returns true if any of the apex the module belongs to is updatable.
+func (c *ContainersInfo) UpdatableApex() bool {
+	for _, apex := range c.belongingApexes {
+		if apex.Updatable {
+			return true
+		}
 	}
+	return false
+}
 
-	props := ctx.Module().GetProperties()
-	for _, prop := range props {
-		val := reflect.ValueOf(prop).Elem()
-		if val.Kind() == reflect.Struct {
-			testSuites := val.FieldByName("Test_suites")
-			if testSuites.IsValid() && testSuites.Kind() == reflect.Slice && slices.Contains(testSuites.Interface().([]string), "cts") {
-				inCts = true
-			}
+var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]()
+
+func generateContainerInfo(ctx ModuleContext) ContainersInfo {
+	var containers []*container
+
+	for _, cnt := range allContainers {
+		if containerBoundaryFunctionsTable[cnt](ctx) {
+			containers = append(containers, cnt)
 		}
 	}
 
 	var belongingApexes []ApexInfo
 	if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
 		belongingApexes = apexInfo.ApexInfos
-		inApex = true
-	}
-
-	containers := []*container{}
-	if inSystem {
-		containers = append(containers, SystemContainer)
-	}
-	if inProduct {
-		containers = append(containers, ProductContainer)
-	}
-	if inVendor {
-		containers = append(containers, VendorContainer)
-	}
-	if inCts {
-		containers = append(containers, CtsContainer)
-	}
-	if inApex {
-		containers = append(containers, ApexContainer)
 	}
 
 	return ContainersInfo{
@@ -225,9 +411,24 @@
 	}
 }
 
+func getContainerModuleInfo(ctx ModuleContext, module Module) (ContainersInfo, bool) {
+	if ctx.Module() == module {
+		return module.ContainersInfo(), true
+	}
+
+	return OtherModuleProvider(ctx, module, ContainersInfoProvider)
+}
+
 func setContainerInfo(ctx ModuleContext) {
+	// Required to determine the unstable container. This provider is set here instead of the
+	// unstableContainerBoundaryFunc in order to prevent setting the provider multiple times.
+	SetProvider(ctx, unstableInfoProvider, unstableInfo{
+		ContainsPlatformPrivateApis: determineUnstableModule(ctx),
+	})
+
 	if _, ok := ctx.Module().(InstallableModule); ok {
 		containersInfo := generateContainerInfo(ctx)
+		ctx.Module().base().containersInfo = containersInfo
 		SetProvider(ctx, ContainersInfoProvider, containersInfo)
 	}
 }
diff --git a/android/deapexer.go b/android/deapexer.go
index 61ae64e..dcae3e4 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -181,7 +181,11 @@
 			// An err has been found. Do not visit further.
 			return
 		}
-		c, _ := OtherModuleProvider(ctx, m, DeapexerProvider)
+		c, ok := OtherModuleProvider(ctx, m, DeapexerProvider)
+		if !ok {
+			ctx.ModuleErrorf("Expected all deps with DeapexerTag to have a DeapexerProvider, but module %q did not", m.Name())
+			return
+		}
 		p := &c
 		if di != nil {
 			// If two DeapexerInfo providers have been found then check if they are
diff --git a/android/image.go b/android/image.go
index 0f03107..6e5a551 100644
--- a/android/image.go
+++ b/android/image.go
@@ -14,7 +14,7 @@
 
 package android
 
-// ImageInterface is implemented by modules that need to be split by the imageMutator.
+// ImageInterface is implemented by modules that need to be split by the imageTransitionMutator.
 type ImageInterface interface {
 	// ImageMutatorBegin is called before any other method in the ImageInterface.
 	ImageMutatorBegin(ctx BaseModuleContext)
@@ -81,18 +81,16 @@
 	DebugRamdiskVariation string = "debug_ramdisk"
 )
 
-// imageMutator creates variants for modules that implement the ImageInterface that
+// imageTransitionMutator creates variants for modules that implement the ImageInterface that
 // allow them to build differently for each partition (recovery, core, vendor, etc.).
-func imageMutator(ctx BottomUpMutatorContext) {
-	if ctx.Os() != Android {
-		return
-	}
+type imageTransitionMutator struct{}
 
-	if m, ok := ctx.Module().(ImageInterface); ok {
+func (imageTransitionMutator) Split(ctx BaseModuleContext) []string {
+	var variations []string
+
+	if m, ok := ctx.Module().(ImageInterface); ctx.Os() == Android && ok {
 		m.ImageMutatorBegin(ctx)
 
-		var variations []string
-
 		if m.CoreVariantNeeded(ctx) {
 			variations = append(variations, CoreVariation)
 		}
@@ -117,15 +115,29 @@
 
 		extraVariations := m.ExtraImageVariations(ctx)
 		variations = append(variations, extraVariations...)
+	}
 
-		if len(variations) == 0 {
-			return
-		}
+	if len(variations) == 0 {
+		variations = append(variations, "")
+	}
 
-		mod := ctx.CreateVariations(variations...)
-		for i, v := range variations {
-			mod[i].base().setImageVariation(v)
-			mod[i].(ImageInterface).SetImageVariation(ctx, v)
-		}
+	return variations
+}
+
+func (imageTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
+	return sourceVariation
+}
+
+func (imageTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
+	if _, ok := ctx.Module().(ImageInterface); ctx.Os() != Android || !ok {
+		return CoreVariation
+	}
+	return incomingVariation
+}
+
+func (imageTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
+	ctx.Module().base().setImageVariation(variation)
+	if m, ok := ctx.Module().(ImageInterface); ok {
+		m.SetImageVariation(ctx, variation)
 	}
 }
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 8056189..cd69749 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -33,7 +33,7 @@
 	}, "args")
 )
 
-func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
+func buildLicenseMetadata(ctx *moduleContext, licenseMetadataFile WritablePath) {
 	base := ctx.Module().base()
 
 	if !base.Enabled(ctx) {
@@ -52,8 +52,8 @@
 	// Only pass the last installed file to isContainerFromFileExtensions so a *.zip file in test data
 	// doesn't mark the whole module as a container.
 	var installFiles InstallPaths
-	if len(base.installFiles) > 0 {
-		installFiles = InstallPaths{base.installFiles[len(base.installFiles)-1]}
+	if len(ctx.installFiles) > 0 {
+		installFiles = InstallPaths{ctx.installFiles[len(ctx.installFiles)-1]}
 	}
 
 	isContainer := isContainerFromFileExtensions(installFiles, outputFiles)
@@ -92,7 +92,7 @@
 
 			allDepMetadataArgs = append(allDepMetadataArgs, info.LicenseMetadataPath.String()+depAnnotations)
 
-			if depInstallFiles := dep.base().installFiles; len(depInstallFiles) > 0 {
+			if depInstallFiles := OtherModuleProviderOrDefault(ctx, dep, InstallFilesProvider).InstallFiles; len(depInstallFiles) > 0 {
 				allDepOutputFiles = append(allDepOutputFiles, depInstallFiles.Paths()...)
 			} else if depOutputFiles, err := outputFilesForModule(ctx, dep, ""); err == nil {
 				depOutputFiles = PathsIfNonNil(depOutputFiles...)
@@ -162,7 +162,7 @@
 
 	// Installed files
 	args = append(args,
-		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.installFiles.Strings()), "-i "))
+		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(ctx.installFiles.Strings()), "-i "))
 
 	if isContainer {
 		args = append(args, "--is_container")
diff --git a/android/logtags.go b/android/logtags.go
index d11cccf..7929057 100644
--- a/android/logtags.go
+++ b/android/logtags.go
@@ -42,7 +42,7 @@
 		if !module.ExportedToMake() {
 			return
 		}
-		if logtagsInfo, ok := SingletonModuleProvider(ctx, module, LogtagsProviderKey); ok {
+		if logtagsInfo, ok := OtherModuleProvider(ctx, module, LogtagsProviderKey); ok {
 			allLogtags = append(allLogtags, logtagsInfo.Logtags...)
 		}
 	})
diff --git a/android/makevars.go b/android/makevars.go
index f92f458..810eb38 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -94,7 +94,7 @@
 	ModuleDir(module blueprint.Module) string
 	ModuleSubDir(module blueprint.Module) string
 	ModuleType(module blueprint.Module) string
-	moduleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool)
+	otherModuleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool)
 	BlueprintFile(module blueprint.Module) string
 
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
@@ -279,10 +279,11 @@
 		}
 
 		if m.ExportedToMake() {
-			katiInstalls = append(katiInstalls, m.base().katiInstalls...)
+			info := OtherModuleProviderOrDefault(ctx, m, InstallFilesProvider)
+			katiInstalls = append(katiInstalls, info.KatiInstalls...)
 			katiInitRcInstalls = append(katiInitRcInstalls, m.base().katiInitRcInstalls...)
 			katiVintfManifestInstalls = append(katiVintfManifestInstalls, m.base().katiVintfInstalls...)
-			katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...)
+			katiSymlinks = append(katiSymlinks, info.KatiSymlinks...)
 		}
 	})
 
diff --git a/android/module.go b/android/module.go
index 5c2b1e1..95908f8 100644
--- a/android/module.go
+++ b/android/module.go
@@ -111,15 +111,16 @@
 	RequiredModuleNames(ctx ConfigAndErrorContext) []string
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
-
-	FilesToInstall() InstallPaths
-	PackagingSpecs() []PackagingSpec
+	VintfFragmentModuleNames(ctx ConfigAndErrorContext) []string
 
 	// TransitivePackagingSpecs returns the PackagingSpecs for this module and any transitive
 	// dependencies with dependency tags for which IsInstallDepNeeded() returns true.
 	TransitivePackagingSpecs() []PackagingSpec
 
 	ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator
+
+	// Get the information about the containers this module belongs to.
+	ContainersInfo() ContainersInfo
 }
 
 // Qualified id for a module
@@ -497,6 +498,9 @@
 
 	// The team (defined by the owner/vendor) who owns the property.
 	Team *string `android:"path"`
+
+	// vintf_fragment Modules required from this module.
+	Vintf_fragment_modules proptools.Configurable[[]string] `android:"path"`
 }
 
 type distProperties struct {
@@ -832,30 +836,16 @@
 	primaryLicensesProperty applicableLicensesProperty
 
 	noAddressSanitizer   bool
-	installFiles         InstallPaths
 	installFilesDepSet   *DepSet[InstallPath]
-	checkbuildFiles      Paths
-	packagingSpecs       []PackagingSpec
 	packagingSpecsDepSet *DepSet[PackagingSpec]
-	// katiInstalls tracks the install rules that were created by Soong but are being exported
-	// to Make to convert to ninja rules so that Make can add additional dependencies.
-	katiInstalls katiInstalls
 	// katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are
 	// allowed to have duplicates across modules and variants.
 	katiInitRcInstalls katiInstalls
 	katiVintfInstalls  katiInstalls
-	katiSymlinks       katiInstalls
-	testData           []DataPath
 
 	// The files to copy to the dist as explicitly specified in the .bp file.
 	distFiles TaggedDistFiles
 
-	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
-	// Only set on the final variant of each module
-	installTarget    WritablePath
-	checkbuildTarget WritablePath
-	blueprintDir     string
-
 	hooks hooks
 
 	registerProps []interface{}
@@ -892,6 +882,10 @@
 	// complianceMetadataInfo is for different module types to dump metadata.
 	// See android.ModuleContext interface.
 	complianceMetadataInfo *ComplianceMetadataInfo
+
+	// containersInfo stores the information about the containers and the information of the
+	// apexes the module belongs to.
+	containersInfo ContainersInfo
 }
 
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
@@ -1028,6 +1022,7 @@
 	fullManifest := pv.DeviceArch != nil && pv.DeviceName != nil
 	if fullManifest {
 		addRequiredDeps(ctx)
+		addVintfFragmentDeps(ctx)
 	}
 }
 
@@ -1105,6 +1100,16 @@
 	}
 }
 
+var vintfDepTag = struct {
+	blueprint.BaseDependencyTag
+	InstallAlwaysNeededDependencyTag
+}{}
+
+func addVintfFragmentDeps(ctx BottomUpMutatorContext) {
+	mod := ctx.Module()
+	ctx.AddDependency(mod, vintfDepTag, mod.VintfFragmentModuleNames(ctx)...)
+}
+
 // AddProperties "registers" the provided props
 // each value in props MUST be a pointer to a struct
 func (m *ModuleBase) AddProperties(props ...interface{}) {
@@ -1476,14 +1481,6 @@
 	return IsInstallDepNeededTag(tag)
 }
 
-func (m *ModuleBase) FilesToInstall() InstallPaths {
-	return m.installFiles
-}
-
-func (m *ModuleBase) PackagingSpecs() []PackagingSpec {
-	return m.packagingSpecs
-}
-
 func (m *ModuleBase) TransitivePackagingSpecs() []PackagingSpec {
 	return m.packagingSpecsDepSet.ToList()
 }
@@ -1597,6 +1594,10 @@
 	return m.base().commonProperties.Target_required
 }
 
+func (m *ModuleBase) VintfFragmentModuleNames(ctx ConfigAndErrorContext) []string {
+	return m.base().commonProperties.Vintf_fragment_modules.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
+}
+
 func (m *ModuleBase) InitRc() Paths {
 	return append(Paths{}, m.initRcPaths...)
 }
@@ -1615,40 +1616,51 @@
 	m.licenseInstallMap = append(m.licenseInstallMap, installMap...)
 }
 
-func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
+func (m *ModuleBase) generateModuleTarget(ctx *moduleContext) {
 	var allInstalledFiles InstallPaths
 	var allCheckbuildFiles Paths
 	ctx.VisitAllModuleVariants(func(module Module) {
 		a := module.base()
-		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
+		var checkBuilds Paths
+		if a == m {
+			allInstalledFiles = append(allInstalledFiles, ctx.installFiles...)
+			checkBuilds = ctx.checkbuildFiles
+		} else {
+			info := OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider)
+			allInstalledFiles = append(allInstalledFiles, info.InstallFiles...)
+			checkBuilds = info.CheckbuildFiles
+		}
 		// A module's -checkbuild phony targets should
 		// not be created if the module is not exported to make.
 		// Those could depend on the build target and fail to compile
 		// for the current build target.
 		if !ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(ctx, a) {
-			allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
+			allCheckbuildFiles = append(allCheckbuildFiles, checkBuilds...)
 		}
 	})
 
 	var deps Paths
 
-	namespacePrefix := ctx.Namespace().id
-	if namespacePrefix != "" {
-		namespacePrefix = namespacePrefix + "-"
+	var namespacePrefix string
+	nameSpace := ctx.Namespace().Path
+	if nameSpace != "." {
+		namespacePrefix = strings.ReplaceAll(nameSpace, "/", ".") + "-"
 	}
 
+	var info FinalModuleBuildTargetsInfo
+
 	if len(allInstalledFiles) > 0 {
 		name := namespacePrefix + ctx.ModuleName() + "-install"
 		ctx.Phony(name, allInstalledFiles.Paths()...)
-		m.installTarget = PathForPhony(ctx, name)
-		deps = append(deps, m.installTarget)
+		info.InstallTarget = PathForPhony(ctx, name)
+		deps = append(deps, info.InstallTarget)
 	}
 
 	if len(allCheckbuildFiles) > 0 {
 		name := namespacePrefix + ctx.ModuleName() + "-checkbuild"
 		ctx.Phony(name, allCheckbuildFiles...)
-		m.checkbuildTarget = PathForPhony(ctx, name)
-		deps = append(deps, m.checkbuildTarget)
+		info.CheckbuildTarget = PathForPhony(ctx, name)
+		deps = append(deps, info.CheckbuildTarget)
 	}
 
 	if len(deps) > 0 {
@@ -1659,7 +1671,8 @@
 
 		ctx.Phony(namespacePrefix+ctx.ModuleName()+suffix, deps...)
 
-		m.blueprintDir = ctx.ModuleDir()
+		info.BlueprintDir = ctx.ModuleDir()
+		SetProvider(ctx, FinalModuleBuildTargetsProvider, info)
 	}
 }
 
@@ -1763,12 +1776,36 @@
 
 }
 
+type InstallFilesInfo struct {
+	InstallFiles    InstallPaths
+	CheckbuildFiles Paths
+	PackagingSpecs  []PackagingSpec
+	// katiInstalls tracks the install rules that were created by Soong but are being exported
+	// to Make to convert to ninja rules so that Make can add additional dependencies.
+	KatiInstalls katiInstalls
+	KatiSymlinks katiInstalls
+	TestData     []DataPath
+}
+
+var FinalModuleBuildTargetsProvider = blueprint.NewProvider[FinalModuleBuildTargetsInfo]()
+
+type FinalModuleBuildTargetsInfo struct {
+	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
+	// Only set on the final variant of each module
+	InstallTarget    WritablePath
+	CheckbuildTarget WritablePath
+	BlueprintDir     string
+}
+
+var InstallFilesProvider = blueprint.NewProvider[InstallFilesInfo]()
+
 func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
 	ctx := &moduleContext{
 		module:            m.module,
 		bp:                blueprintCtx,
 		baseModuleContext: m.baseModuleContextFactory(blueprintCtx),
 		variables:         make(map[string]string),
+		phonies:           make(map[string]Paths),
 	}
 
 	setContainerInfo(ctx)
@@ -1914,14 +1951,14 @@
 				return
 			}
 			cacheKey = &blueprint.BuildActionCacheKey{
-				Id:        ctx.bp.ModuleId(),
+				Id:        ctx.bp.ModuleCacheKey(),
 				InputHash: hash,
 			}
 		}
 
 		restored := false
 		if incrementalAnalysis && cacheKey != nil {
-			restored = ctx.bp.RestoreBuildActions(cacheKey, incrementalModule)
+			restored = ctx.bp.RestoreBuildActions(cacheKey)
 		}
 
 		if !restored {
@@ -1944,12 +1981,14 @@
 			return
 		}
 
-		m.installFiles = append(m.installFiles, ctx.installFiles...)
-		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
-		m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
-		m.katiInstalls = append(m.katiInstalls, ctx.katiInstalls...)
-		m.katiSymlinks = append(m.katiSymlinks, ctx.katiSymlinks...)
-		m.testData = append(m.testData, ctx.testData...)
+		SetProvider(ctx, InstallFilesProvider, InstallFilesInfo{
+			InstallFiles:    ctx.installFiles,
+			CheckbuildFiles: ctx.checkbuildFiles,
+			PackagingSpecs:  ctx.packagingSpecs,
+			KatiInstalls:    ctx.katiInstalls,
+			KatiSymlinks:    ctx.katiSymlinks,
+			TestData:        ctx.testData,
+		})
 	} else if ctx.Config().AllowMissingDependencies() {
 		// If the module is not enabled it will not create any build rules, nothing will call
 		// ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled
@@ -1965,15 +2004,15 @@
 		}
 	}
 
-	m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, m.installFiles, dependencyInstallFiles)
-	m.packagingSpecsDepSet = NewDepSet[PackagingSpec](TOPOLOGICAL, m.packagingSpecs, dependencyPackagingSpecs)
+	m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, ctx.installFiles, dependencyInstallFiles)
+	m.packagingSpecsDepSet = NewDepSet[PackagingSpec](TOPOLOGICAL, ctx.packagingSpecs, dependencyPackagingSpecs)
 
 	buildLicenseMetadata(ctx, m.licenseMetadataFile)
 
 	if m.moduleInfoJSON != nil {
 		var installed InstallPaths
-		installed = append(installed, m.katiInstalls.InstallPaths()...)
-		installed = append(installed, m.katiSymlinks.InstallPaths()...)
+		installed = append(installed, ctx.katiInstalls.InstallPaths()...)
+		installed = append(installed, ctx.katiSymlinks.InstallPaths()...)
 		installed = append(installed, m.katiInitRcInstalls.InstallPaths()...)
 		installed = append(installed, m.katiVintfInstalls.InstallPaths()...)
 		installedStrings := installed.Strings()
@@ -1986,7 +2025,7 @@
 		}
 
 		var data []string
-		for _, d := range m.testData {
+		for _, d := range ctx.testData {
 			data = append(data, d.ToRelativeInstallPath())
 		}
 
@@ -2022,6 +2061,11 @@
 		SetProvider(ctx, OutputFilesProvider, m.outputFiles)
 	}
 
+	if len(ctx.phonies) > 0 {
+		SetProvider(ctx, ModulePhonyProvider, ModulePhonyInfo{
+			Phonies: ctx.phonies,
+		})
+	}
 	buildComplianceMetadataProvider(ctx, m)
 }
 
@@ -2068,6 +2112,10 @@
 	return variant
 }
 
+func (m *ModuleBase) ContainersInfo() ContainersInfo {
+	return m.containersInfo
+}
+
 // Check the supplied dist structure to make sure that it is valid.
 //
 // property - the base property, e.g. dist or dists[1], which is combined with the
@@ -2519,7 +2567,7 @@
 			fromProperty = true
 		}
 	} else if cta, isCta := ctx.(*singletonContextAdaptor); isCta {
-		providerData, _ := cta.moduleProvider(module, OutputFilesProvider)
+		providerData, _ := cta.otherModuleProvider(module, OutputFilesProvider)
 		outputFiles, _ = providerData.(OutputFilesInfo)
 	} else {
 		return nil, fmt.Errorf("unsupported context %q in method outputFilesForModuleFromProvider", reflect.TypeOf(ctx))
@@ -2621,17 +2669,15 @@
 	modulesInDir := make(map[string]Paths)
 
 	ctx.VisitAllModules(func(module Module) {
-		blueprintDir := module.base().blueprintDir
-		installTarget := module.base().installTarget
-		checkbuildTarget := module.base().checkbuildTarget
+		info := OtherModuleProviderOrDefault(ctx, module, FinalModuleBuildTargetsProvider)
 
-		if checkbuildTarget != nil {
-			checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
-			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], checkbuildTarget)
+		if info.CheckbuildTarget != nil {
+			checkbuildDeps = append(checkbuildDeps, info.CheckbuildTarget)
+			modulesInDir[info.BlueprintDir] = append(modulesInDir[info.BlueprintDir], info.CheckbuildTarget)
 		}
 
-		if installTarget != nil {
-			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], installTarget)
+		if info.InstallTarget != nil {
+			modulesInDir[info.BlueprintDir] = append(modulesInDir[info.BlueprintDir], info.InstallTarget)
 		}
 	})
 
@@ -2666,7 +2712,7 @@
 	ctx.VisitAllModules(func(module Module) {
 		if module.Enabled(ctx) {
 			key := osAndCross{os: module.Target().Os, hostCross: module.Target().HostCross}
-			osDeps[key] = append(osDeps[key], module.base().checkbuildFiles...)
+			osDeps[key] = append(osDeps[key], OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider).CheckbuildFiles...)
 		}
 	})
 
diff --git a/android/module_context.go b/android/module_context.go
index 253bebd..5146782 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -231,8 +231,8 @@
 	module          Module
 	phonies         map[string]Paths
 
-	katiInstalls []katiInstall
-	katiSymlinks []katiInstall
+	katiInstalls katiInstalls
+	katiSymlinks katiInstalls
 
 	testData []DataPath
 
@@ -361,7 +361,7 @@
 }
 
 func (m *moduleContext) Phony(name string, deps ...Path) {
-	addPhony(m.config, name, deps...)
+	m.phonies[name] = append(m.phonies[name], deps...)
 }
 
 func (m *moduleContext) GetMissingDependencies() []string {
diff --git a/android/mutator.go b/android/mutator.go
index b81dd12..38fb857 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -149,7 +149,7 @@
 
 func registerArchMutator(ctx RegisterMutatorsContext) {
 	ctx.BottomUpBlueprint("os", osMutator).Parallel()
-	ctx.BottomUp("image", imageMutator).Parallel()
+	ctx.Transition("image", &imageTransitionMutator{})
 	ctx.BottomUpBlueprint("arch", archMutator).Parallel()
 }
 
diff --git a/android/paths.go b/android/paths.go
index dda48dd..d20b84a 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -237,6 +237,9 @@
 	// directory, and OutputPath.Join("foo").Rel() would return "foo".
 	Rel() string
 
+	// WithoutRel returns a new Path with no relative path, i.e. Rel() will return the same value as Base().
+	WithoutRel() Path
+
 	// RelativeToTop returns a new path relative to the top, it is provided solely for use in tests.
 	//
 	// It is guaranteed to always return the same type as it is called on, e.g. if called on an
@@ -245,13 +248,13 @@
 	// A standard build has the following structure:
 	//   ../top/
 	//          out/ - make install files go here.
-	//          out/soong - this is the soongOutDir passed to NewTestConfig()
+	//          out/soong - this is the outDir passed to NewTestConfig()
 	//          ... - the source files
 	//
 	// This function converts a path so that it appears relative to the ../top/ directory, i.e.
-	// * Make install paths, which have the pattern "soongOutDir/../<path>" are converted into the top
+	// * Make install paths, which have the pattern "outDir/../<path>" are converted into the top
 	//   relative path "out/<path>"
-	// * Soong install paths and other writable paths, which have the pattern "soongOutDir/<path>" are
+	// * Soong install paths and other writable paths, which have the pattern "outDir/soong/<path>" are
 	//   converted into the top relative path "out/soong/<path>".
 	// * Source paths are already relative to the top.
 	// * Phony paths are not relative to anything.
@@ -261,8 +264,9 @@
 }
 
 const (
-	OutDir      = "out"
-	OutSoongDir = OutDir + "/soong"
+	testOutDir         = "out"
+	testOutSoongSubDir = "/soong"
+	TestOutSoongDir    = testOutDir + testOutSoongSubDir
 )
 
 // WritablePath is a type of path that can be used as an output for build rules.
@@ -1118,8 +1122,8 @@
 	return p
 }
 
-func (p basePath) RelativeToTop() Path {
-	ensureTestOnly()
+func (p basePath) withoutRel() basePath {
+	p.rel = filepath.Base(p.path)
 	return p
 }
 
@@ -1135,6 +1139,11 @@
 	return p
 }
 
+func (p SourcePath) RelativeToTop() Path {
+	ensureTestOnly()
+	return p
+}
+
 // safePathForSource is for paths that we expect are safe -- only for use by go
 // code that is embedding ninja variables in paths
 func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
@@ -1218,11 +1227,13 @@
 // PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput,
 // the path is relative to the root of the output folder, not the out/soong folder.
 func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path {
-	p, err := validatePath(pathComponents...)
+	path, err := validatePath(pathComponents...)
 	if err != nil {
 		reportPathError(ctx, err)
 	}
-	return basePath{path: filepath.Join(ctx.Config().OutDir(), p)}
+	fullPath := filepath.Join(ctx.Config().OutDir(), path)
+	path = fullPath[len(fullPath)-len(path):]
+	return OutputPath{basePath{path, ""}, ctx.Config().OutDir(), fullPath}
 }
 
 // MaybeExistentPathForSource joins the provided path components and validates that the result
@@ -1275,6 +1286,11 @@
 	return p.path
 }
 
+func (p SourcePath) WithoutRel() Path {
+	p.basePath = p.basePath.withoutRel()
+	return p
+}
+
 // Join creates a new SourcePath with paths... joined with the current path. The
 // provided paths... may not use '..' to escape from the current path.
 func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
@@ -1325,8 +1341,8 @@
 type OutputPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.SoongOutDir()
-	soongOutDir string
+	// The base out directory for this path, either Config.SoongOutDir() or Config.OutDir()
+	outDir string
 
 	fullPath string
 }
@@ -1334,7 +1350,7 @@
 func (p OutputPath) GobEncode() ([]byte, error) {
 	w := new(bytes.Buffer)
 	encoder := gob.NewEncoder(w)
-	err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.soongOutDir), encoder.Encode(p.fullPath))
+	err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.outDir), encoder.Encode(p.fullPath))
 	if err != nil {
 		return nil, err
 	}
@@ -1345,7 +1361,7 @@
 func (p *OutputPath) GobDecode(data []byte) error {
 	r := bytes.NewBuffer(data)
 	decoder := gob.NewDecoder(r)
-	err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.soongOutDir), decoder.Decode(&p.fullPath))
+	err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.outDir), decoder.Decode(&p.fullPath))
 	if err != nil {
 		return err
 	}
@@ -1359,13 +1375,13 @@
 	return p
 }
 
-func (p OutputPath) WithoutRel() OutputPath {
-	p.basePath.rel = filepath.Base(p.basePath.path)
+func (p OutputPath) WithoutRel() Path {
+	p.basePath = p.basePath.withoutRel()
 	return p
 }
 
 func (p OutputPath) getSoongOutDir() string {
-	return p.soongOutDir
+	return p.outDir
 }
 
 func (p OutputPath) RelativeToTop() Path {
@@ -1373,8 +1389,13 @@
 }
 
 func (p OutputPath) outputPathRelativeToTop() OutputPath {
-	p.fullPath = StringPathRelativeToTop(p.soongOutDir, p.fullPath)
-	p.soongOutDir = OutSoongDir
+	p.fullPath = StringPathRelativeToTop(p.outDir, p.fullPath)
+	if strings.HasSuffix(p.outDir, testOutSoongSubDir) {
+		p.outDir = TestOutSoongDir
+	} else {
+		// Handle the PathForArbitraryOutput case
+		p.outDir = testOutDir
+	}
 	return p
 }
 
@@ -1391,6 +1412,11 @@
 	basePath
 }
 
+func (t toolDepPath) WithoutRel() Path {
+	t.basePath = t.basePath.withoutRel()
+	return t
+}
+
 func (t toolDepPath) RelativeToTop() Path {
 	ensureTestOnly()
 	return t
@@ -1420,7 +1446,7 @@
 	return OutputPath{basePath{path, ""}, ctx.Config().soongOutDir, fullPath}
 }
 
-// PathsForOutput returns Paths rooted from soongOutDir
+// PathsForOutput returns Paths rooted from outDir
 func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
 	ret := make(WritablePaths, len(paths))
 	for i, path := range paths {
@@ -1751,14 +1777,19 @@
 func (p InstallPath) RelativeToTop() Path {
 	ensureTestOnly()
 	if p.makePath {
-		p.soongOutDir = OutDir
+		p.soongOutDir = testOutDir
 	} else {
-		p.soongOutDir = OutSoongDir
+		p.soongOutDir = TestOutSoongDir
 	}
 	p.fullPath = filepath.Join(p.soongOutDir, p.path)
 	return p
 }
 
+func (p InstallPath) WithoutRel() Path {
+	p.basePath = p.basePath.withoutRel()
+	return p
+}
+
 func (p InstallPath) getSoongOutDir() string {
 	return p.soongOutDir
 }
@@ -2079,6 +2110,11 @@
 	return p
 }
 
+func (p PhonyPath) WithoutRel() Path {
+	p.basePath = p.basePath.withoutRel()
+	return p
+}
+
 func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
 	panic("Not implemented")
 }
@@ -2095,6 +2131,11 @@
 	return p
 }
 
+func (p testPath) WithoutRel() Path {
+	p.basePath = p.basePath.withoutRel()
+	return p
+}
+
 func (p testPath) String() string {
 	return p.path
 }
diff --git a/android/phony.go b/android/phony.go
index 814a9e3..f8db88d 100644
--- a/android/phony.go
+++ b/android/phony.go
@@ -26,14 +26,20 @@
 
 var phonyMapLock sync.Mutex
 
-func getPhonyMap(config Config) phonyMap {
+type ModulePhonyInfo struct {
+	Phonies map[string]Paths
+}
+
+var ModulePhonyProvider = blueprint.NewProvider[ModulePhonyInfo]()
+
+func getSingletonPhonyMap(config Config) phonyMap {
 	return config.Once(phonyMapOnceKey, func() interface{} {
 		return make(phonyMap)
 	}).(phonyMap)
 }
 
-func addPhony(config Config, name string, deps ...Path) {
-	phonyMap := getPhonyMap(config)
+func addSingletonPhony(config Config, name string, deps ...Path) {
+	phonyMap := getSingletonPhonyMap(config)
 	phonyMapLock.Lock()
 	defer phonyMapLock.Unlock()
 	phonyMap[name] = append(phonyMap[name], deps...)
@@ -47,7 +53,15 @@
 var _ SingletonMakeVarsProvider = (*phonySingleton)(nil)
 
 func (p *phonySingleton) GenerateBuildActions(ctx SingletonContext) {
-	p.phonyMap = getPhonyMap(ctx.Config())
+	p.phonyMap = getSingletonPhonyMap(ctx.Config())
+	ctx.VisitAllModules(func(m Module) {
+		if info, ok := OtherModuleProvider(ctx, m, ModulePhonyProvider); ok {
+			for k, v := range info.Phonies {
+				p.phonyMap[k] = append(p.phonyMap[k], v...)
+			}
+		}
+	})
+
 	p.phonyList = SortedKeys(p.phonyMap)
 	for _, phony := range p.phonyList {
 		p.phonyMap[phony] = SortedUniquePaths(p.phonyMap[phony])
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 51b86a5..fd5a6ea 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -61,7 +61,7 @@
 type UserSuppliedPrebuiltProperties struct {
 	// When prefer is set to true the prebuilt will be used instead of any source module with
 	// a matching name.
-	Prefer *bool `android:"arch_variant"`
+	Prefer proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 
 	// When specified this names a Soong config variable that controls the prefer property.
 	//
@@ -148,11 +148,7 @@
 }
 
 func (p *Prebuilt) ForcePrefer() {
-	p.properties.Prefer = proptools.BoolPtr(true)
-}
-
-func (p *Prebuilt) Prefer() bool {
-	return proptools.Bool(p.properties.Prefer)
+	p.properties.Prefer = NewSimpleConfigurable(true)
 }
 
 // SingleSourcePathFromSupplier invokes the supplied supplier for the current module in the
@@ -248,6 +244,8 @@
 	p.srcsPropertyName = srcsPropertyName
 }
 
+// InitPrebuiltModule is the same as InitPrebuiltModuleWithSrcSupplier, but uses the
+// provided list of strings property as the source provider.
 func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) {
 	if srcs == nil {
 		panic(fmt.Errorf("srcs must not be nil"))
@@ -260,6 +258,20 @@
 	InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs")
 }
 
+// InitConfigurablePrebuiltModule is the same as InitPrebuiltModule, but uses a
+// Configurable list of strings property instead of a regular list of strings.
+func InitConfigurablePrebuiltModule(module PrebuiltInterface, srcs *proptools.Configurable[[]string]) {
+	if srcs == nil {
+		panic(fmt.Errorf("srcs must not be nil"))
+	}
+
+	srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
+		return srcs.GetOrDefault(ctx, nil)
+	}
+
+	InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs")
+}
+
 func InitSingleSourcePrebuiltModule(module PrebuiltInterface, srcProps interface{}, srcField string) {
 	srcPropsValue := reflect.ValueOf(srcProps).Elem()
 	srcStructField, _ := srcPropsValue.Type().FieldByName(srcField)
@@ -423,15 +435,7 @@
 // The metadata will be used for source vs prebuilts selection
 func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) {
 	m := ctx.Module()
-	// If this module is a prebuilt, is enabled and has not been renamed to source then add a
-	// dependency onto the source if it is present.
-	if p := GetEmbeddedPrebuilt(m); p != nil && m.Enabled(ctx) && !p.properties.PrebuiltRenamedToSource {
-		bmn, _ := m.(baseModuleName)
-		name := bmn.BaseModuleName()
-		if ctx.OtherModuleReverseDependencyVariantExists(name) {
-			ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
-			p.properties.SourceExists = true
-		}
+	if p := GetEmbeddedPrebuilt(m); p != nil {
 		// Add a dependency from the prebuilt to the `all_apex_contributions`
 		// metadata module
 		// TODO: When all branches contain this singleton module, make this strict
@@ -439,7 +443,16 @@
 		if ctx.OtherModuleExists("all_apex_contributions") {
 			ctx.AddDependency(m, AcDepTag, "all_apex_contributions")
 		}
-
+		if m.Enabled(ctx) && !p.properties.PrebuiltRenamedToSource {
+			// If this module is a prebuilt, is enabled and has not been renamed to source then add a
+			// dependency onto the source if it is present.
+			bmn, _ := m.(baseModuleName)
+			name := bmn.BaseModuleName()
+			if ctx.OtherModuleReverseDependencyVariantExists(name) {
+				ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
+				p.properties.SourceExists = true
+			}
+		}
 	}
 }
 
@@ -668,12 +681,37 @@
 	return p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0
 }
 
+type apexVariationName interface {
+	ApexVariationName() string
+}
+
 // usePrebuilt returns true if a prebuilt should be used instead of the source module.  The prebuilt
 // will be used if it is marked "prefer" or if the source module is disabled.
 func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt Module) bool {
+	isMainlinePrebuilt := func(prebuilt Module) bool {
+		apex, ok := prebuilt.(apexVariationName)
+		if !ok {
+			return false
+		}
+		// Prebuilts of aosp apexes in prebuilts/runtime
+		// Used in minimal art branches
+		if prebuilt.base().BaseModuleName() == apex.ApexVariationName() {
+			return false
+		}
+		return InList(apex.ApexVariationName(), ctx.Config().AllMainlineApexNames())
+	}
+
 	// Use `all_apex_contributions` for source vs prebuilt selection.
 	psi := PrebuiltSelectionInfoMap{}
-	ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(am Module) {
+	var psiDepTag blueprint.DependencyTag
+	if p := GetEmbeddedPrebuilt(ctx.Module()); p != nil {
+		// This is a prebuilt module, visit all_apex_contributions to get the info
+		psiDepTag = AcDepTag
+	} else {
+		// This is a source module, visit any of its prebuilts to get the info
+		psiDepTag = PrebuiltDepTag
+	}
+	ctx.VisitDirectDepsWithTag(psiDepTag, func(am Module) {
 		psi, _ = OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
 	})
 
@@ -686,6 +724,11 @@
 		return true
 	}
 
+	// If this is a mainline prebuilt, but has not been flagged, hide it.
+	if isMainlinePrebuilt(prebuilt) {
+		return false
+	}
+
 	// If the baseModuleName could not be found in the metadata module,
 	// fall back to the existing source vs prebuilt selection.
 	// TODO: Drop the fallback mechanisms
@@ -707,7 +750,7 @@
 	}
 
 	// TODO: use p.Properties.Name and ctx.ModuleDir to override preference
-	return Bool(p.properties.Prefer)
+	return p.properties.Prefer.GetOrDefault(ctx, false)
 }
 
 func (p *Prebuilt) SourceExists() bool {
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 6e4fc0c..5e4af0b 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -574,11 +574,7 @@
 
 func TestPrebuiltErrorCannotListBothSourceAndPrebuiltInContributions(t *testing.T) {
 	selectMainlineModuleContritbutions := GroupFixturePreparers(
-		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_apex_contributions",
-			}
-		}),
+		PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_apex_contributions"),
 	)
 	testPrebuiltErrorWithFixture(t, `Found duplicate variations of the same module in apex_contributions: foo and prebuilt_foo. Please remove one of these`, `
 		source {
diff --git a/android/provider.go b/android/provider.go
index 3b9c5d2..5ded4cc 100644
--- a/android/provider.go
+++ b/android/provider.go
@@ -14,6 +14,8 @@
 var _ OtherModuleProviderContext = ModuleContext(nil)
 var _ OtherModuleProviderContext = BottomUpMutatorContext(nil)
 var _ OtherModuleProviderContext = TopDownMutatorContext(nil)
+var _ OtherModuleProviderContext = SingletonContext(nil)
+var _ OtherModuleProviderContext = (*TestContext)(nil)
 
 // OtherModuleProvider reads the provider for the given module.  If the provider has been set the value is
 // returned and the boolean is true.  If it has not been set the zero value of the provider's type  is returned
@@ -30,6 +32,11 @@
 	return value.(K), ok
 }
 
+func OtherModuleProviderOrDefault[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) K {
+	value, _ := OtherModuleProvider(ctx, module, provider)
+	return value
+}
+
 // ModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
 // TopDownMutatorContext for use in ModuleProvider.
 type ModuleProviderContext interface {
@@ -56,26 +63,6 @@
 	return value.(K), ok
 }
 
-type SingletonModuleProviderContext interface {
-	moduleProvider(blueprint.Module, blueprint.AnyProviderKey) (any, bool)
-}
-
-var _ SingletonModuleProviderContext = SingletonContext(nil)
-var _ SingletonModuleProviderContext = (*TestContext)(nil)
-
-// SingletonModuleProvider wraps blueprint.SingletonModuleProvider to provide a type-safe method to retrieve the value
-// of the given provider from a module using a SingletonContext.  If the provider has not been set the first return
-// value will be the zero value of the provider's type, and the second return value will be false.  If the provider has
-// been set the second return value will be true.
-func SingletonModuleProvider[K any](ctx SingletonModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
-	value, ok := ctx.moduleProvider(module, provider)
-	if !ok {
-		var k K
-		return k, false
-	}
-	return value.(K), ok
-}
-
 // SetProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
 // TopDownMutatorContext for use in SetProvider.
 type SetProviderContext interface {
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 8b03124..464aca4 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -554,6 +554,12 @@
 					To:   proto.String(r.sboxPathForInputRel(input)),
 				})
 			}
+			for _, input := range r.OrderOnlys() {
+				command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
+					From: proto.String(input.String()),
+					To:   proto.String(r.sboxPathForInputRel(input)),
+				})
+			}
 
 			// If using rsp files copy them and their contents into the sbox directory with
 			// the appropriate path mappings.
diff --git a/android/sbom.go b/android/sbom.go
index dd2d2fa..2a5499e 100644
--- a/android/sbom.go
+++ b/android/sbom.go
@@ -42,7 +42,9 @@
 }
 
 // sbomSingleton is used to generate build actions of generating SBOM of products.
-type sbomSingleton struct{}
+type sbomSingleton struct {
+	sbomFile OutputPath
+}
 
 func sbomSingletonFactory() Singleton {
 	return &sbomSingleton{}
@@ -77,12 +79,12 @@
 	implicits = append(implicits, installedFilesStamp)
 
 	metadataDb := PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "compliance-metadata.db")
-	sbomFile := PathForOutput(ctx, "sbom", ctx.Config().DeviceProduct(), "sbom.spdx.json")
+	this.sbomFile = PathForOutput(ctx, "sbom", ctx.Config().DeviceProduct(), "sbom.spdx.json")
 	ctx.Build(pctx, BuildParams{
 		Rule:      genSbomRule,
 		Input:     metadataDb,
 		Implicits: implicits,
-		Output:    sbomFile,
+		Output:    this.sbomFile,
 		Args: map[string]string{
 			"productOut":           filepath.Join(ctx.Config().OutDir(), "target", "product", String(prodVars.DeviceName)),
 			"soongOut":             ctx.Config().soongOutDir,
@@ -91,10 +93,19 @@
 		},
 	})
 
-	// Phony rule "soong-sbom". "m soong-sbom" to generate product SBOM in Soong.
-	ctx.Build(pctx, BuildParams{
-		Rule:   blueprint.Phony,
-		Inputs: []Path{sbomFile},
-		Output: PathForPhony(ctx, "soong-sbom"),
-	})
+	if !ctx.Config().UnbundledBuildApps() {
+		// When building SBOM of products, phony rule "sbom" is for generating product SBOM in Soong.
+		ctx.Build(pctx, BuildParams{
+			Rule:   blueprint.Phony,
+			Inputs: []Path{this.sbomFile},
+			Output: PathForPhony(ctx, "sbom"),
+		})
+	}
+}
+
+func (this *sbomSingleton) MakeVars(ctx MakeVarsContext) {
+	// When building SBOM of products
+	if !ctx.Config().UnbundledBuildApps() {
+		ctx.DistForGoalWithFilename("droid", this.sbomFile, "sbom/sbom.spdx.json")
+	}
 }
diff --git a/android/sdk.go b/android/sdk.go
index 4bcbe2e..d3f04a4 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -813,6 +813,7 @@
 
 // SdkMemberContext provides access to information common to a specific member.
 type SdkMemberContext interface {
+	ConfigAndErrorContext
 
 	// SdkModuleContext returns the module context of the sdk common os variant which is creating the
 	// snapshot.
diff --git a/android/singleton.go b/android/singleton.go
index d364384..8542bd9 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -35,7 +35,7 @@
 	// Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context.
 	ModuleVariantsFromName(referer Module, name string) []Module
 
-	moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
+	otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
 
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
 	Errorf(format string, args ...interface{})
@@ -177,7 +177,7 @@
 }
 
 func (s *singletonContextAdaptor) Phony(name string, deps ...Path) {
-	addPhony(s.Config(), name, deps...)
+	addSingletonPhony(s.Config(), name, deps...)
 }
 
 func (s *singletonContextAdaptor) SetOutDir(pctx PackageContext, value string) {
@@ -279,7 +279,7 @@
 	return result
 }
 
-func (s *singletonContextAdaptor) moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
+func (s *singletonContextAdaptor) otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
 	return s.SingletonContext.ModuleProvider(module, provider)
 }
 
diff --git a/android/test_suites.go b/android/test_suites.go
index ff75f26..936d2b6 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -47,7 +47,8 @@
 					files[testSuite] = make(map[string]InstallPaths)
 				}
 				name := ctx.ModuleName(m)
-				files[testSuite][name] = append(files[testSuite][name], tsm.FilesToInstall()...)
+				files[testSuite][name] = append(files[testSuite][name],
+					OtherModuleProviderOrDefault(ctx, tsm, InstallFilesProvider).InstallFiles...)
 			}
 		}
 	})
diff --git a/android/testing.go b/android/testing.go
index e39a1a7..79d0c9b 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -126,6 +126,10 @@
 	ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
 })
 
+var PrepareForTestVintfFragmentModules = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	registerVintfFragmentComponents(ctx)
+})
+
 // Test fixture preparer that will register most java build components.
 //
 // Singletons and mutators should only be added here if they are needed for a majority of java
@@ -149,6 +153,7 @@
 	PrepareForTestWithPackageModule,
 	PrepareForTestWithPrebuilts,
 	PrepareForTestWithVisibility,
+	PrepareForTestVintfFragmentModules,
 )
 
 // Prepares an integration test with all build components from the android package.
@@ -174,6 +179,16 @@
 	config.TestAllowNonExistentPaths = false
 })
 
+// PrepareForTestWithBuildFlag returns a FixturePreparer that sets the given flag to the given value.
+func PrepareForTestWithBuildFlag(flag, value string) FixturePreparer {
+	return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+		if variables.BuildFlags == nil {
+			variables.BuildFlags = make(map[string]string)
+		}
+		variables.BuildFlags[flag] = value
+	})
+}
+
 func NewTestArchContext(config Config) *TestContext {
 	ctx := NewTestContext(config)
 	ctx.preDeps = append(ctx.preDeps, registerArchMutator)
@@ -202,7 +217,7 @@
 	ctx.PreArchMutators(f)
 }
 
-func (ctx *TestContext) moduleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
+func (ctx *TestContext) otherModuleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
 	return ctx.Context.ModuleProvider(m, p)
 }
 
@@ -220,7 +235,7 @@
 
 func (ctx *TestContext) OtherModuleProviderAdaptor() OtherModuleProviderContext {
 	return NewOtherModuleProviderAdaptor(func(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
-		return ctx.moduleProvider(module, provider)
+		return ctx.otherModuleProvider(module, provider)
 	})
 }
 
@@ -822,15 +837,15 @@
 // containing at most one instance of the temporary build directory at the start of the path while
 // this assumes that there can be any number at any position.
 func normalizeStringRelativeToTop(config Config, s string) string {
-	// The soongOutDir usually looks something like: /tmp/testFoo2345/001
+	// The outDir usually looks something like: /tmp/testFoo2345/001
 	//
-	// Replace any usage of the soongOutDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
+	// Replace any usage of the outDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
 	// "out/soong".
 	outSoongDir := filepath.Clean(config.soongOutDir)
 	re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`)
 	s = re.ReplaceAllString(s, "out/soong")
 
-	// Replace any usage of the soongOutDir/.. with out, e.g. replace "/tmp/testFoo2345" with
+	// Replace any usage of the outDir/.. with out, e.g. replace "/tmp/testFoo2345" with
 	// "out". This must come after the previous replacement otherwise this would replace
 	// "/tmp/testFoo2345/001" with "out/001" instead of "out/soong".
 	outDir := filepath.Dir(outSoongDir)
@@ -1234,8 +1249,14 @@
 	}
 
 	if isRel {
-		// The path is in the soong out dir so indicate that in the relative path.
-		return filepath.Join("out/soong", rel)
+		if strings.HasSuffix(soongOutDir, testOutSoongSubDir) {
+			// The path is in the soong out dir so indicate that in the relative path.
+			return filepath.Join(TestOutSoongDir, rel)
+		} else {
+			// Handle the PathForArbitraryOutput case
+			return filepath.Join(testOutDir, rel)
+
+		}
 	}
 
 	// Check to see if the path is relative to the top level out dir.
diff --git a/android/variable.go b/android/variable.go
index df0e59c..c141437 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -507,6 +507,17 @@
 	OemProperties []string `json:",omitempty"`
 
 	ArtTargetIncludeDebugBuild *bool `json:",omitempty"`
+
+	SystemPropFiles    []string `json:",omitempty"`
+	SystemExtPropFiles []string `json:",omitempty"`
+	ProductPropFiles   []string `json:",omitempty"`
+
+	EnableUffdGc *string `json:",omitempty"`
+
+	BoardAvbEnable                         *bool    `json:",omitempty"`
+	BoardAvbSystemAddHashtreeFooterArgs    []string `json:",omitempty"`
+	DeviceFrameworkCompatibilityMatrixFile []string `json:",omitempty"`
+	DeviceProductCompatibilityMatrixFile   []string `json:",omitempty"`
 }
 
 type PartitionQualifiedVariablesType struct {
diff --git a/android/variable_test.go b/android/variable_test.go
index 928bca6..73dc052 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -199,9 +199,7 @@
 			ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct {
 				Foo []string
 			}{}))
-			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
-				ctx.BottomUp("variable", VariableMutator).Parallel()
-			})
+			registerVariableBuildComponents(ctx)
 		}),
 		FixtureWithRootAndroidBp(bp),
 	).RunTest(t)
@@ -210,14 +208,14 @@
 var testProductVariableDefaultsProperties = struct {
 	Product_variables struct {
 		Eng struct {
-			Foo []string
+			Foo []string `android:"arch_variant"`
 			Bar []string
-		}
-	}
+		} `android:"arch_variant"`
+	} `android:"arch_variant"`
 }{}
 
 type productVariablesDefaultsTestProperties struct {
-	Foo []string
+	Foo []string `android:"arch_variant"`
 }
 
 type productVariablesDefaultsTestProperties2 struct {
@@ -242,7 +240,7 @@
 	module := &productVariablesDefaultsTestModule{}
 	module.AddProperties(&module.properties)
 	module.variableProperties = testProductVariableDefaultsProperties
-	InitAndroidModule(module)
+	InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
 	InitDefaultableModule(module)
 	return module
 }
@@ -324,3 +322,46 @@
 		})
 	}
 }
+
+// Test a defaults module that supports more product variable properties than the target module.
+func TestProductVariablesArch(t *testing.T) {
+	bp := `
+		test {
+			name: "foo",
+			arch: {
+				arm: {
+					product_variables: {
+						eng: {
+							foo: ["arm"],
+						},
+					},
+				},
+				arm64: {
+					product_variables: {
+						eng: {
+							foo: ["arm64"],
+						},
+					},
+				},
+			},
+			foo: ["module"],
+		}
+	`
+
+	result := GroupFixturePreparers(
+		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.Eng = boolPtr(true)
+		}),
+		PrepareForTestWithArchMutator,
+		PrepareForTestWithVariables,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory)
+		}),
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	foo := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*productVariablesDefaultsTestModule)
+
+	want := []string{"module", "arm64"}
+	AssertDeepEquals(t, "foo", want, foo.properties.Foo)
+}
diff --git a/android/vintf_fragment.go b/android/vintf_fragment.go
new file mode 100644
index 0000000..329eac9
--- /dev/null
+++ b/android/vintf_fragment.go
@@ -0,0 +1,84 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+type vintfFragmentProperties struct {
+	// Vintf fragment XML file.
+	Src string `android:"path"`
+}
+
+type vintfFragmentModule struct {
+	ModuleBase
+
+	properties vintfFragmentProperties
+
+	installDirPath InstallPath
+	outputFilePath OutputPath
+}
+
+func init() {
+	registerVintfFragmentComponents(InitRegistrationContext)
+}
+
+func registerVintfFragmentComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("vintf_fragment", vintfLibraryFactory)
+}
+
+// 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.
+func vintfLibraryFactory() Module {
+	m := &vintfFragmentModule{}
+	m.AddProperties(
+		&m.properties,
+	)
+	InitAndroidArchModule(m, DeviceSupported, MultilibFirst)
+
+	return m
+}
+
+func (m *vintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	builder := NewRuleBuilder(pctx, ctx)
+	srcVintfFragment := PathForModuleSrc(ctx, m.properties.Src)
+	processedVintfFragment := PathForModuleOut(ctx, srcVintfFragment.Base())
+
+	// Process vintf fragment source file with assemble_vintf tool
+	builder.Command().
+		Flag("VINTF_IGNORE_TARGET_FCM_VERSION=true").
+		BuiltTool("assemble_vintf").
+		FlagWithInput("-i ", srcVintfFragment).
+		FlagWithOutput("-o ", processedVintfFragment)
+
+	builder.Build("assemble_vintf", "Process vintf fragment "+processedVintfFragment.String())
+
+	m.installDirPath = PathForModuleInstall(ctx, "etc", "vintf", "manifest")
+	m.outputFilePath = processedVintfFragment.OutputPath
+
+	ctx.InstallFile(m.installDirPath, processedVintfFragment.Base(), processedVintfFragment)
+}
+
+// Make this module visible to AndroidMK so it can be referenced from modules defined from Android.mk files
+func (m *vintfFragmentModule) AndroidMkEntries() []AndroidMkEntries {
+	return []AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: OptionalPathForPath(m.outputFilePath),
+		ExtraEntries: []AndroidMkExtraEntriesFunc{
+			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", m.installDirPath.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", m.outputFilePath.Base())
+			},
+		},
+	}}
+}
diff --git a/android/vintf_fragment_test.go b/android/vintf_fragment_test.go
new file mode 100644
index 0000000..8be534c
--- /dev/null
+++ b/android/vintf_fragment_test.go
@@ -0,0 +1,36 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestVintfManifestBuildAction(t *testing.T) {
+	bp := `
+	vintf_fragment {
+		name: "test_vintf_fragment",
+		src: "test_vintf_file",
+	}
+	`
+
+	testResult := PrepareForTestWithAndroidBuildComponents.RunTestWithBp(t, bp)
+
+	vintfFragmentBuild := testResult.TestContext.ModuleForTests("test_vintf_fragment", "android_arm64_armv8-a").Rule("assemble_vintf")
+	if !strings.Contains(vintfFragmentBuild.RuleParams.Command, "assemble_vintf") {
+		t.Errorf("Vintf_manifest build command does not process with assemble_vintf : " + vintfFragmentBuild.RuleParams.Command)
+	}
+}
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 373e883..a2486fd 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -46,6 +46,9 @@
 
 	outputBaseName string
 	outputFile     android.OptionalPath
+
+	// TODO(b/357908583): Temp field, remove this once we support Android Mk providers
+	installFile android.InstallPath
 }
 
 type remapProperties struct {
@@ -234,14 +237,18 @@
 
 	s.outputBaseName = name
 	s.outputFile = android.OptionalPathForPath(outputZipFile)
-	ctx.InstallFile(android.PathForModuleInstall(ctx, "sdk-repo"), name+".zip", outputZipFile)
+	installPath := android.PathForModuleInstall(ctx, "sdk-repo")
+	name = name + ".zip"
+	ctx.InstallFile(installPath, name, outputZipFile)
+	// TODO(b/357908583): Temp field, remove this once we support Android Mk providers
+	s.installFile = installPath.Join(ctx, name)
 }
 
 func (s *sdkRepoHost) AndroidMk() android.AndroidMkData {
 	return android.AndroidMkData{
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
 			fmt.Fprintln(w, ".PHONY:", name, "sdk_repo", "sdk-repo-"+name)
-			fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", strings.Join(s.FilesToInstall().Strings(), " "))
+			fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", s.installFile.String())
 
 			fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-FILE_NAME_TAG_PLACEHOLDER.zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName)
 		},
diff --git a/apex/apex.go b/apex/apex.go
index fc0500a..ec71c18 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -213,6 +213,50 @@
 
 type ApexNativeDependencies struct {
 	// List of native libraries that are embedded inside this APEX.
+	Native_shared_libs proptools.Configurable[[]string]
+
+	// List of JNI libraries that are embedded inside this APEX.
+	Jni_libs []string
+
+	// List of rust dyn libraries that are embedded inside this APEX.
+	Rust_dyn_libs []string
+
+	// List of native executables that are embedded inside this APEX.
+	Binaries proptools.Configurable[[]string]
+
+	// List of native tests that are embedded inside this APEX.
+	Tests []string
+
+	// List of filesystem images that are embedded inside this APEX bundle.
+	Filesystems []string
+
+	// List of prebuilt_etcs that are embedded inside this APEX bundle.
+	Prebuilts proptools.Configurable[[]string]
+
+	// List of native libraries to exclude from this APEX.
+	Exclude_native_shared_libs []string
+
+	// List of JNI libraries to exclude from this APEX.
+	Exclude_jni_libs []string
+
+	// List of rust dyn libraries to exclude from this APEX.
+	Exclude_rust_dyn_libs []string
+
+	// List of native executables to exclude from this APEX.
+	Exclude_binaries []string
+
+	// List of native tests to exclude from this APEX.
+	Exclude_tests []string
+
+	// List of filesystem images to exclude from this APEX bundle.
+	Exclude_filesystems []string
+
+	// List of prebuilt_etcs to exclude from this APEX bundle.
+	Exclude_prebuilts []string
+}
+
+type ResolvedApexNativeDependencies struct {
+	// List of native libraries that are embedded inside this APEX.
 	Native_shared_libs []string
 
 	// List of JNI libraries that are embedded inside this APEX.
@@ -222,8 +266,7 @@
 	Rust_dyn_libs []string
 
 	// List of native executables that are embedded inside this APEX.
-	Binaries         proptools.Configurable[[]string]
-	ResolvedBinaries []string `blueprint:"mutated"`
+	Binaries []string
 
 	// List of native tests that are embedded inside this APEX.
 	Tests []string
@@ -232,8 +275,7 @@
 	Filesystems []string
 
 	// List of prebuilt_etcs that are embedded inside this APEX bundle.
-	Prebuilts         proptools.Configurable[[]string]
-	ResolvedPrebuilts []string `blueprint:"mutated"`
+	Prebuilts []string
 
 	// List of native libraries to exclude from this APEX.
 	Exclude_native_shared_libs []string
@@ -258,14 +300,14 @@
 }
 
 // Merge combines another ApexNativeDependencies into this one
-func (a *ApexNativeDependencies) Merge(ctx android.BaseMutatorContext, b ApexNativeDependencies) {
-	a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs...)
+func (a *ResolvedApexNativeDependencies) Merge(ctx android.BaseMutatorContext, b ApexNativeDependencies) {
+	a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs.GetOrDefault(ctx, nil)...)
 	a.Jni_libs = append(a.Jni_libs, b.Jni_libs...)
 	a.Rust_dyn_libs = append(a.Rust_dyn_libs, b.Rust_dyn_libs...)
-	a.ResolvedBinaries = append(a.ResolvedBinaries, b.Binaries.GetOrDefault(ctx, nil)...)
+	a.Binaries = append(a.Binaries, b.Binaries.GetOrDefault(ctx, nil)...)
 	a.Tests = append(a.Tests, b.Tests...)
 	a.Filesystems = append(a.Filesystems, b.Filesystems...)
-	a.ResolvedPrebuilts = append(a.ResolvedPrebuilts, b.Prebuilts.GetOrDefault(ctx, nil)...)
+	a.Prebuilts = append(a.Prebuilts, b.Prebuilts.GetOrDefault(ctx, nil)...)
 
 	a.Exclude_native_shared_libs = append(a.Exclude_native_shared_libs, b.Exclude_native_shared_libs...)
 	a.Exclude_jni_libs = append(a.Exclude_jni_libs, b.Exclude_jni_libs...)
@@ -700,7 +742,7 @@
 )
 
 // TODO(jiyong): shorten this function signature
-func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) {
+func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ResolvedApexNativeDependencies, target android.Target, imageVariation string) {
 	binVariations := target.Variations()
 	libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
 	rustLibVariations := append(
@@ -718,7 +760,7 @@
 	// this module. This is required since arch variant of an APEX bundle is 'common' but it is
 	// 'arm' or 'arm64' for native shared libs.
 	ctx.AddFarVariationDependencies(binVariations, executableTag,
-		android.RemoveListFromList(nativeModules.ResolvedBinaries, nativeModules.Exclude_binaries)...)
+		android.RemoveListFromList(nativeModules.Binaries, nativeModules.Exclude_binaries)...)
 	ctx.AddFarVariationDependencies(binVariations, testTag,
 		android.RemoveListFromList(nativeModules.Tests, nativeModules.Exclude_tests)...)
 	ctx.AddFarVariationDependencies(libVariations, jniLibTag,
@@ -730,7 +772,7 @@
 	ctx.AddFarVariationDependencies(target.Variations(), fsTag,
 		android.RemoveListFromList(nativeModules.Filesystems, nativeModules.Exclude_filesystems)...)
 	ctx.AddFarVariationDependencies(target.Variations(), prebuiltTag,
-		android.RemoveListFromList(nativeModules.ResolvedPrebuilts, nativeModules.Exclude_prebuilts)...)
+		android.RemoveListFromList(nativeModules.Prebuilts, nativeModules.Exclude_prebuilts)...)
 }
 
 func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
@@ -781,7 +823,7 @@
 		}
 	}
 	for i, target := range targets {
-		var deps ApexNativeDependencies
+		var deps ResolvedApexNativeDependencies
 
 		// Add native modules targeting both ABIs. When multilib.* is omitted for
 		// native_shared_libs/jni_libs/tests, it implies multilib.both
@@ -798,7 +840,7 @@
 		if isPrimaryAbi {
 			deps.Merge(ctx, a.properties.Multilib.First)
 			deps.Merge(ctx, ApexNativeDependencies{
-				Native_shared_libs: nil,
+				Native_shared_libs: proptools.NewConfigurable[[]string](nil, nil),
 				Tests:              nil,
 				Jni_libs:           nil,
 				Binaries:           a.properties.Binaries,
@@ -1035,7 +1077,7 @@
 
 	if a.dynamic_common_lib_apex() {
 		android.SetProvider(mctx, DCLAInfoProvider, DCLAInfo{
-			ProvidedLibs: a.properties.Native_shared_libs,
+			ProvidedLibs: a.properties.Native_shared_libs.GetOrDefault(mctx, nil),
 		})
 	}
 }
@@ -1492,7 +1534,7 @@
 		imageVariation := a.getImageVariation()
 		for _, target := range ctx.MultiTargets() {
 			if target.Arch.ArchType.Multilib == "lib64" {
-				addDependenciesForNativeModules(ctx, ApexNativeDependencies{
+				addDependenciesForNativeModules(ctx, ResolvedApexNativeDependencies{
 					Native_shared_libs: []string{"libclang_rt.hwasan"},
 					Tests:              nil,
 					Jni_libs:           nil,
@@ -2370,6 +2412,7 @@
 	a.providePrebuiltInfo(ctx)
 
 	a.required = a.RequiredModuleNames(ctx)
+	a.required = append(a.required, a.VintfFragmentModuleNames(ctx)...)
 
 	a.setOutputFiles(ctx)
 }
@@ -2766,10 +2809,21 @@
 		if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) {
 			return true
 		}
+
+		// Let's give some hint for apex_available
+		hint := fmt.Sprintf("%q", apexName)
+
+		if strings.HasPrefix(apexName, "com.") && !strings.HasPrefix(apexName, "com.android.") && strings.Count(apexName, ".") >= 2 {
+			// In case of a partner APEX, prefix format might be an option.
+			components := strings.Split(apexName, ".")
+			components[len(components)-1] = "*"
+			hint += fmt.Sprintf(" or %q", strings.Join(components, "."))
+		}
+
 		ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'."+
 			"\n\nDependency path:%s\n\n"+
-			"Consider adding %q to 'apex_available' property of %q",
-			fromName, toName, ctx.GetPathString(true), apexName, toName)
+			"Consider adding %s to 'apex_available' property of %q",
+			fromName, toName, ctx.GetPathString(true), hint, toName)
 		// Visit this module's dependencies to check and report any issues with their availability.
 		return true
 	})
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index a8d89b1..f405cb2 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -88,7 +88,7 @@
 	updatableFlatLists := android.Paths{}
 	ctx.VisitAllModules(func(module android.Module) {
 		if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok {
-			apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
+			apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
 			if path := binaryInfo.FlatListPath(); path != nil {
 				if binaryInfo.Updatable() || apexInfo.Updatable {
 					updatableFlatLists = append(updatableFlatLists, path)
@@ -155,7 +155,7 @@
 	prebuiltInfos := []android.PrebuiltInfo{}
 
 	ctx.VisitAllModules(func(m android.Module) {
-		prebuiltInfo, exists := android.SingletonModuleProvider(ctx, m, android.PrebuiltInfoProvider)
+		prebuiltInfo, exists := android.OtherModuleProvider(ctx, m, android.PrebuiltInfoProvider)
 		// Use prebuiltInfoProvider to filter out non apex soong modules.
 		// Use HideFromMake to filter out the unselected variants of a specific apex.
 		if exists && !m.IsHideFromMake() {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 261d2ce..6b9944d 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -384,7 +384,7 @@
 			symlink_preferred_arch: true,
 			system_shared_libs: [],
 			stl: "none",
-			apex_available: [ "myapex", "com.android.gki.*" ],
+			apex_available: [ "myapex" ],
 		}
 
 		rust_binary {
@@ -432,14 +432,6 @@
 			apex_available: ["myapex"],
 		}
 
-		apex {
-			name: "com.android.gki.fake",
-			binaries: ["foo"],
-			key: "myapex.key",
-			file_contexts: ":myapex-file_contexts",
-			updatable: false,
-		}
-
 		cc_library_shared {
 			name: "mylib2",
 			srcs: ["mylib.cpp"],
@@ -6730,6 +6722,99 @@
 	}
 }
 
+func TestApexAvailable_PrefixMatch(t *testing.T) {
+
+	for _, tc := range []struct {
+		name          string
+		apexAvailable string
+		expectedError string
+	}{
+		{
+			name:          "prefix matches correctly",
+			apexAvailable: "com.foo.*",
+		},
+		{
+			name:          "prefix doesn't match",
+			apexAvailable: "com.bar.*",
+			expectedError: `Consider .* "com.foo\.\*"`,
+		},
+		{
+			name:          "short prefix",
+			apexAvailable: "com.*",
+			expectedError: "requires two or more components",
+		},
+		{
+			name:          "wildcard not in the end",
+			apexAvailable: "com.*.foo",
+			expectedError: "should end with .*",
+		},
+		{
+			name:          "wildcard in the middle",
+			apexAvailable: "com.foo*.*",
+			expectedError: "not allowed in the middle",
+		},
+		{
+			name:          "hint with prefix pattern",
+			apexAvailable: "//apex_available:platform",
+			expectedError: "Consider adding \"com.foo.bar\" or \"com.foo.*\"",
+		},
+	} {
+		t.Run(tc.name, func(t *testing.T) {
+			errorHandler := android.FixtureExpectsNoErrors
+			if tc.expectedError != "" {
+				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedError)
+			}
+			context := android.GroupFixturePreparers(
+				prepareForApexTest,
+				android.FixtureMergeMockFs(android.MockFS{
+					"system/sepolicy/apex/com.foo.bar-file_contexts": nil,
+				}),
+			).ExtendWithErrorHandler(errorHandler)
+
+			context.RunTestWithBp(t, `
+				apex {
+					name: "com.foo.bar",
+					key: "myapex.key",
+					native_shared_libs: ["libfoo"],
+					updatable: false,
+				}
+
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+
+				cc_library {
+					name: "libfoo",
+					stl: "none",
+					system_shared_libs: [],
+					apex_available: ["`+tc.apexAvailable+`"],
+				}`)
+		})
+	}
+	testApexError(t, `Consider adding "com.foo" to`, `
+		apex {
+			name: "com.foo", // too short for a partner apex
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libfoo",
+			stl: "none",
+			system_shared_libs: [],
+		}
+	`)
+}
+
 func TestOverrideApex(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -8993,6 +9078,30 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.capex\n")
 }
 
+func TestApexSet_ShouldRespectCompressedApexFlag(t *testing.T) {
+	for _, compressionEnabled := range []bool{true, false} {
+		t.Run(fmt.Sprintf("compressionEnabled=%v", compressionEnabled), func(t *testing.T) {
+			ctx := testApex(t, `
+				apex_set {
+					name: "com.company.android.myapex",
+					apex_name: "com.android.myapex",
+					set: "company-myapex.apks",
+				}
+			`, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.CompressedApex = proptools.BoolPtr(compressionEnabled)
+			}),
+			)
+
+			build := ctx.ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex").Output("com.company.android.myapex.apex")
+			if compressionEnabled {
+				ensureEquals(t, build.Rule.String(), "android/soong/android.Cp")
+			} else {
+				ensureEquals(t, build.Rule.String(), "android/apex.decompressApex")
+			}
+		})
+	}
+}
+
 func TestPreferredPrebuiltSharedLibDep(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -9992,9 +10101,6 @@
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.PrepareForTestWithJacocoInstrumentation,
 		java.FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 		android.FixtureMergeMockFs(fs),
 	).RunTestWithBp(t, bp)
 
@@ -11278,11 +11384,7 @@
 				fs["platform/Test.java"] = nil
 			}),
 
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions,
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions),
 		)
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
 		checkBootDexJarPath(t, ctx, "framework-foo", tc.expectedBootJar)
@@ -11421,11 +11523,7 @@
 			android.FixtureMergeMockFs(map[string][]byte{
 				"system/sepolicy/apex/com.android.foo-file_contexts": nil,
 			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions,
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions),
 		)
 		if tc.expectedError != "" {
 			preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(tc.expectedError))
@@ -11441,6 +11539,114 @@
 	}
 }
 
+// Test that product packaging installs the selected mainline module in workspaces withtout source mainline module
+func TestInstallationRulesForMultipleApexPrebuiltsWithoutSource(t *testing.T) {
+	// for a mainline module family, check that only the flagged soong module is visible to make
+	checkHideFromMake := func(t *testing.T, ctx *android.TestContext, visibleModuleNames []string, hiddenModuleNames []string) {
+		variation := func(moduleName string) string {
+			ret := "android_common_com.android.adservices"
+			if moduleName == "com.google.android.foo" {
+				ret = "android_common_com.google.android.foo_com.google.android.foo"
+			}
+			return ret
+		}
+
+		for _, visibleModuleName := range visibleModuleNames {
+			visibleModule := ctx.ModuleForTests(visibleModuleName, variation(visibleModuleName)).Module()
+			android.AssertBoolEquals(t, "Apex "+visibleModuleName+" selected using apex_contributions should be visible to make", false, visibleModule.IsHideFromMake())
+		}
+
+		for _, hiddenModuleName := range hiddenModuleNames {
+			hiddenModule := ctx.ModuleForTests(hiddenModuleName, variation(hiddenModuleName)).Module()
+			android.AssertBoolEquals(t, "Apex "+hiddenModuleName+" not selected using apex_contributions should be hidden from make", true, hiddenModule.IsHideFromMake())
+
+		}
+	}
+
+	bp := `
+		apex_key {
+			name: "com.android.adservices.key",
+			public_key: "com.android.adservices.avbpubkey",
+			private_key: "com.android.adservices.pem",
+		}
+
+		// AOSP source apex
+		apex {
+			name: "com.android.adservices",
+			key: "com.android.adservices.key",
+			updatable: false,
+		}
+
+		// Prebuilt Google APEX.
+
+		prebuilt_apex {
+			name: "com.google.android.adservices",
+			apex_name: "com.android.adservices",
+			src: "com.android.foo-arm.apex",
+		}
+
+		// Another Prebuilt Google APEX
+		prebuilt_apex {
+			name: "com.google.android.adservices.v2",
+			apex_name: "com.android.adservices",
+			src: "com.android.foo-arm.apex",
+		}
+
+		// APEX contribution modules
+
+
+		apex_contributions {
+			name: "adservices.prebuilt.contributions",
+			api_domain: "com.android.adservices",
+			contents: ["prebuilt_com.google.android.adservices"],
+		}
+
+		apex_contributions {
+			name: "adservices.prebuilt.v2.contributions",
+			api_domain: "com.android.adservices",
+			contents: ["prebuilt_com.google.android.adservices.v2"],
+		}
+	`
+
+	testCases := []struct {
+		desc                       string
+		selectedApexContributions  string
+		expectedVisibleModuleNames []string
+		expectedHiddenModuleNames  []string
+	}{
+		{
+			desc:                       "No apex contributions selected, source aosp apex should be visible, and mainline prebuilts should be hidden",
+			selectedApexContributions:  "",
+			expectedVisibleModuleNames: []string{"com.android.adservices"},
+			expectedHiddenModuleNames:  []string{"com.google.android.adservices", "com.google.android.adservices.v2"},
+		},
+		{
+			desc:                       "Prebuilt apex prebuilt_com.android.foo is selected",
+			selectedApexContributions:  "adservices.prebuilt.contributions",
+			expectedVisibleModuleNames: []string{"com.android.adservices", "com.google.android.adservices"},
+			expectedHiddenModuleNames:  []string{"com.google.android.adservices.v2"},
+		},
+		{
+			desc:                       "Prebuilt apex prebuilt_com.android.foo.v2 is selected",
+			selectedApexContributions:  "adservices.prebuilt.v2.contributions",
+			expectedVisibleModuleNames: []string{"com.android.adservices", "com.google.android.adservices.v2"},
+			expectedHiddenModuleNames:  []string{"com.google.android.adservices"},
+		},
+	}
+
+	for _, tc := range testCases {
+		preparer := android.GroupFixturePreparers(
+			android.FixtureMergeMockFs(map[string][]byte{
+				"system/sepolicy/apex/com.android.adservices-file_contexts": nil,
+			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions),
+		)
+		ctx := testApex(t, bp, preparer)
+
+		checkHideFromMake(t, ctx, tc.expectedVisibleModuleNames, tc.expectedHiddenModuleNames)
+	}
+}
+
 func TestAconfifDeclarationsValidation(t *testing.T) {
 	aconfigDeclarationLibraryString := func(moduleNames []string) (ret string) {
 		for _, moduleName := range moduleNames {
@@ -11466,9 +11672,6 @@
 		prepareForApexTest,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 	).RunTestWithBp(t, `
 		java_library {
 			name: "baz-java-lib",
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 919cb01..25131ee 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -53,11 +53,7 @@
 		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
 		java.FixtureConfigureApexBootJars("someapex:foo", "someapex:bar"),
 		prepareForTestWithArtApex,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo", "baz"),
 	).RunTestWithBp(t, `
@@ -156,7 +152,7 @@
 
 	// Check stub dex paths exported by art.
 	artFragment := result.Module("art-bootclasspath-fragment", "android_common")
-	artInfo, _ := android.SingletonModuleProvider(result, artFragment, java.HiddenAPIInfoProvider)
+	artInfo, _ := android.OtherModuleProvider(result, artFragment, java.HiddenAPIInfoProvider)
 
 	bazPublicStubs := "out/soong/.intermediates/baz.stubs.exportable/android_common/dex/baz.stubs.exportable.jar"
 	bazSystemStubs := "out/soong/.intermediates/baz.stubs.exportable.system/android_common/dex/baz.stubs.exportable.system.jar"
@@ -169,7 +165,7 @@
 
 	// Check stub dex paths exported by other.
 	otherFragment := result.Module("other-bootclasspath-fragment", "android_common")
-	otherInfo, _ := android.SingletonModuleProvider(result, otherFragment, java.HiddenAPIInfoProvider)
+	otherInfo, _ := android.OtherModuleProvider(result, otherFragment, java.HiddenAPIInfoProvider)
 
 	fooPublicStubs := "out/soong/.intermediates/foo.stubs.exportable/android_common/dex/foo.stubs.exportable.jar"
 	fooSystemStubs := "out/soong/.intermediates/foo.stubs.exportable.system/android_common/dex/foo.stubs.exportable.system.jar"
@@ -561,6 +557,7 @@
 		result := preparers.RunTestWithBp(t, fmt.Sprintf(bp, "enabled: false,"))
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
+			`all_apex_contributions`,
 			`dex2oatd`,
 			`prebuilt_art-bootclasspath-fragment`,
 			`prebuilt_com.android.art.apex.selector`,
@@ -568,6 +565,7 @@
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{
+			`all_apex_contributions`,
 			`dex2oatd`,
 			`prebuilt_bar`,
 			`prebuilt_com.android.art.deapexer`,
@@ -690,7 +688,7 @@
 	// Make sure that the fragment provides the hidden API encoded dex jars to the APEX.
 	fragment := result.Module("mybootclasspathfragment", "android_common_apex10000")
 
-	info, _ := android.SingletonModuleProvider(result, fragment, java.BootclasspathFragmentApexContentInfoProvider)
+	info, _ := android.OtherModuleProvider(result, fragment, java.BootclasspathFragmentApexContentInfoProvider)
 
 	checkFragmentExportedDexJar := func(name string, expectedDexJar string) {
 		module := result.Module(name, "android_common_apex10000")
@@ -729,11 +727,7 @@
 
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo", "quuz"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "com.android.art",
diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go
index 7a17f50..95db37d 100644
--- a/apex/dexpreopt_bootjars_test.go
+++ b/apex/dexpreopt_bootjars_test.go
@@ -399,11 +399,7 @@
 			java.FixtureConfigureBootJars("com.android.art:core-oj"),
 			PrepareForTestWithApexBuildComponents,
 			prepareForTestWithArtApex,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ART": tc.selectedArtApexContributions,
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", tc.selectedArtApexContributions),
 		).RunTestWithBp(t, bp)
 
 		dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 4a20cf0..920fc0c 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -154,7 +154,7 @@
 	).RunTest(t)
 
 	pbcp := result.Module("platform-bootclasspath", "android_common")
-	info, _ := android.SingletonModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
+	info, _ := android.OtherModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
 
 	for _, category := range java.HiddenAPIFlagFileCategories {
 		name := category.PropertyName()
@@ -236,7 +236,7 @@
 	)
 
 	pbcp := result.Module("myplatform-bootclasspath", "android_common")
-	info, _ := android.SingletonModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
+	info, _ := android.OtherModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
 
 	android.AssertArrayString(t, "stub flags", []string{"prebuilt-stub-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
 	android.AssertArrayString(t, "all flags", []string{"prebuilt-all-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
@@ -254,11 +254,7 @@
 		java.FixtureWithLastReleaseApis("foo"),
 		java.PrepareForTestWithDexpreopt,
 		dexpreopt.FixtureDisableDexpreoptBootImages(false),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "com.android.art",
@@ -429,10 +425,9 @@
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
 		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
+
 		java.FixtureWithPrebuiltApis(map[string][]string{
 			"current": {},
 			"30":      {"foo"},
@@ -935,11 +930,7 @@
 			PrepareForTestWithApexBuildComponents,
 			prepareForTestWithMyapex,
 			java.FixtureConfigureApexBootJars(tc.configuredBootJars...),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ART": "my_apex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "my_apex_contributions"),
 		)
 		if tc.errorExpected {
 			fixture = fixture.ExtendWithErrorHandler(
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index b9cc09b..9dc8a63 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -15,9 +15,6 @@
 package apex
 
 import (
-	"fmt"
-	"io"
-	"path/filepath"
 	"strconv"
 	"strings"
 
@@ -42,6 +39,11 @@
 			CommandDeps: []string{"${extract_apks}"},
 		},
 		"abis", "allow-prereleased", "sdk-version", "skip-sdk-check")
+	decompressApex = pctx.StaticRule("decompressApex", blueprint.RuleParams{
+		Command:     `rm -rf $out && ${deapexer} decompress --copy-if-uncompressed --input ${in} --output ${out}`,
+		CommandDeps: []string{"${deapexer}"},
+		Description: "decompress $out",
+	})
 )
 
 type prebuilt interface {
@@ -65,10 +67,6 @@
 	// fragment for this apex for apexkeys.txt
 	apexKeysPath android.WritablePath
 
-	// A list of apexFile objects created in prebuiltCommon.initApexFilesForAndroidMk which are used
-	// to create make modules in prebuiltCommon.AndroidMkEntries.
-	apexFilesForAndroidMk []apexFile
-
 	// Installed locations of symlinks for backward compatibility.
 	compatSymlinks android.InstallPaths
 
@@ -231,11 +229,6 @@
 }
 
 func (p *prebuiltCommon) addRequiredModules(entries *android.AndroidMkEntries) {
-	for _, fi := range p.apexFilesForAndroidMk {
-		entries.AddStrings("LOCAL_REQUIRED_MODULES", fi.requiredModuleNames...)
-		entries.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", fi.targetRequiredModuleNames...)
-		entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", fi.hostRequiredModuleNames...)
-	}
 	entries.AddStrings("LOCAL_REQUIRED_MODULES", p.requiredModuleNames...)
 }
 
@@ -267,54 +260,9 @@
 		entriesList = append(entriesList, install.ToMakeEntries())
 	}
 
-	// Iterate over the apexFilesForAndroidMk list and create an AndroidMkEntries struct for each
-	// file. This provides similar behavior to that provided in apexBundle.AndroidMk() as it makes the
-	// apex specific variants of the exported java modules available for use from within make.
-	apexName := p.BaseModuleName()
-	for _, fi := range p.apexFilesForAndroidMk {
-		entries := p.createEntriesForApexFile(fi, apexName)
-		entriesList = append(entriesList, entries)
-	}
-
 	return entriesList
 }
 
-// createEntriesForApexFile creates an AndroidMkEntries for the supplied apexFile
-func (p *prebuiltCommon) createEntriesForApexFile(fi apexFile, apexName string) android.AndroidMkEntries {
-	moduleName := fi.androidMkModuleName + "." + apexName
-	entries := android.AndroidMkEntries{
-		Class:        fi.class.nameInMake(),
-		OverrideName: moduleName,
-		OutputFile:   android.OptionalPathForPath(fi.builtFile),
-		Include:      "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", p.installDir.String())
-				entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", filepath.Join(p.installDir.String(), fi.stem()))
-				entries.SetString("LOCAL_SOONG_INSTALL_PAIRS",
-					fi.builtFile.String()+":"+filepath.Join(p.installDir.String(), fi.stem()))
-
-				// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar  Therefore
-				// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
-				// we will have foo.jar.jar
-				entries.SetString("LOCAL_MODULE_STEM", strings.TrimSuffix(fi.stem(), ".jar"))
-				entries.SetString("LOCAL_SOONG_DEX_JAR", fi.builtFile.String())
-				entries.SetString("LOCAL_DEX_PREOPT", "false")
-			},
-		},
-		ExtraFooters: []android.AndroidMkExtraFootersFunc{
-			func(w io.Writer, name, prefix, moduleDir string) {
-				// m <module_name> will build <module_name>.<apex_name> as well.
-				if fi.androidMkModuleName != moduleName {
-					fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName)
-					fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
-				}
-			},
-		},
-	}
-	return entries
-}
-
 // prebuiltApexModuleCreator defines the methods that need to be implemented by prebuilt_apex and
 // apex_set in order to create the modules needed to provide access to the prebuilt .apex file.
 type prebuiltApexModuleCreator interface {
@@ -511,7 +459,7 @@
 	// This cannot be marked as `android:"arch_variant"` because the `prebuilt_apex` is only mutated
 	// for android_common. That is so that it will have the same arch variant as, and so be compatible
 	// with, the source `apex` module type that it replaces.
-	Src  *string `android:"path"`
+	Src  proptools.Configurable[string] `android:"path,replace_instead_of_append"`
 	Arch struct {
 		Arm struct {
 			Src *string `android:"path"`
@@ -561,7 +509,7 @@
 		src = String(p.Arch.X86_64.Src)
 	}
 	if src == "" {
-		src = String(p.Src)
+		src = p.Src.GetOrDefault(ctx, "")
 	}
 
 	if src == "" {
@@ -774,7 +722,7 @@
 func (p *prebuiltCommon) DepsMutator(ctx android.BottomUpMutatorContext) {
 	if p.hasExportedDeps() {
 		// Create a dependency from the prebuilt apex (prebuilt_apex/apex_set) to the internal deapexer module
-		// The deapexer will return a provider that will be bubbled up to the rdeps of apexes (e.g. dex_bootjars)
+		// The deapexer will return a provider which will be used to determine the exported artfifacts from this prebuilt.
 		ctx.AddDependency(ctx.Module(), android.DeapexerTag, deapexerModuleName(p.Name()))
 	}
 }
@@ -1072,8 +1020,14 @@
 
 	inputApex := android.OptionalPathForModuleSrc(ctx, a.prebuiltCommonProperties.Selected_apex).Path()
 	a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
+
+	// Build the output APEX. If compression is not enabled, make sure the output is not compressed even if the input is compressed
+	buildRule := android.Cp
+	if !ctx.Config().ApexCompressionEnabled() {
+		buildRule = decompressApex
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.Cp,
+		Rule:   buildRule,
 		Input:  inputApex,
 		Output: a.outputApex,
 	})
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index f6c53b2..452a43e 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -274,6 +274,7 @@
 	ctx := result.TestContext
 
 	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`prebuilt_myapex.apex.selector`,
 		`prebuilt_myapex.deapexer`,
@@ -281,6 +282,7 @@
 	})
 
 	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
 		`prebuilt_myapex.deapexer`,
@@ -432,6 +434,7 @@
 	ctx := result.TestContext
 
 	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
 		`prebuilt_myapex.deapexer`,
diff --git a/bin/dirmods b/bin/dirmods
index a6d4de3..c8976d5 100755
--- a/bin/dirmods
+++ b/bin/dirmods
@@ -32,7 +32,10 @@
 def main():
     parser = argparse.ArgumentParser(description=__doc__)
     parser.add_argument('path')
+    parser.add_argument('--no-recurse', '-n', action='store_true',
+                        help='Do not include modules defined in subdirs of path')
     args = parser.parse_args()
+    should_recurse = not args.no_recurse
 
     d = os.path.normpath(args.path)
     # Fix absolute path to be relative to build top
@@ -43,15 +46,15 @@
             if d.startswith(base):
                 d = d[len(base):]
 
-    prefix = d + '/'
+    prefix = d + os.path.sep
 
     module_info = modinfo.ReadModuleInfo()
 
     results = set()
     for m in module_info.values():
-        for path in m.get(u'path', []):
-            if path == d or path.startswith(prefix):
-                name = m.get(u'module_name')
+        for path in m.get('path', []):
+            if path == d or (should_recurse and path.startswith(prefix)):
+                name = m.get('module_name')
                 if name:
                     results.add(name)
 
diff --git a/bin/soongdbg b/bin/soongdbg
index a73bdf9..98d31eb 100755
--- a/bin/soongdbg
+++ b/bin/soongdbg
@@ -216,7 +216,7 @@
                         help="jq query for each module metadata")
     parser.add_argument("--deptags", action="store_true",
                         help="show dependency tags (makes the graph much more complex)")
-    parser.add_argument("--tag", action="append",
+    parser.add_argument("--tag", action="append", default=[],
                         help="Limit output to these dependency tags.")
 
     group = parser.add_argument_group("output formats",
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index b72b6d3..8ecea98 100644
--- a/bloaty/bloaty.go
+++ b/bloaty/bloaty.go
@@ -88,7 +88,7 @@
 		if !m.ExportedToMake() {
 			return
 		}
-		filePaths, ok := android.SingletonModuleProvider(ctx, m, fileSizeMeasurerKey)
+		filePaths, ok := android.OtherModuleProvider(ctx, m, fileSizeMeasurerKey)
 		if !ok {
 			return
 		}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 09262e5..6445394 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -148,6 +148,10 @@
 		"-no-canonical-prefixes",
 
 		"-O2",
+		"-Wall",
+		"-Werror",
+		"-Wextra",
+
 		"-isystem bionic/libc/include",
 		"-isystem bionic/libc/kernel/uapi",
 		// The architecture doesn't matter here, but asm/types.h is included by linux/types.h.
@@ -165,7 +169,7 @@
 
 	cflags = append(cflags, bpf.properties.Cflags...)
 
-	if proptools.Bool(bpf.properties.Btf) {
+	if proptools.BoolDefault(bpf.properties.Btf, true) {
 		cflags = append(cflags, "-g")
 		if runtime.GOOS != "darwin" {
 			cflags = append(cflags, "-fdebug-prefix-map=/proc/self/cwd=")
@@ -190,7 +194,7 @@
 			},
 		})
 
-		if proptools.Bool(bpf.properties.Btf) {
+		if proptools.BoolDefault(bpf.properties.Btf, true) {
 			objStripped := android.ObjPathWithExt(ctx, "", src, "o")
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   stripRule,
diff --git a/cc/Android.bp b/cc/Android.bp
index 3bbcaa9..e68e4a3 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -73,7 +73,6 @@
         "ndk_abi.go",
         "ndk_headers.go",
         "ndk_library.go",
-        "ndk_prebuilt.go",
         "ndk_sysroot.go",
 
         "llndk_library.go",
diff --git a/cc/afdo.go b/cc/afdo.go
index 6921edf..14d105e 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -47,6 +47,10 @@
 	if ctx.Config().Eng() {
 		afdo.Properties.Afdo = false
 	}
+	// Disable for native coverage builds.
+	if ctx.DeviceConfig().NativeCoverageEnabled() {
+		afdo.Properties.Afdo = false
+	}
 }
 
 // afdoEnabled returns true for binaries and shared libraries
@@ -76,6 +80,8 @@
 	}
 
 	if afdo.Properties.Afdo || afdo.Properties.AfdoDep {
+		// Emit additional debug info for AutoFDO
+		flags.Local.CFlags = append([]string{"-fdebug-info-for-profiling"}, flags.Local.CFlags...)
 		// We use `-funique-internal-linkage-names` to associate profiles to the right internal
 		// functions. This option should be used before generating a profile. Because a profile
 		// generated for a binary without unique names doesn't work well building a binary with
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 4134653..cecaae2 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -451,10 +451,6 @@
 	})
 }
 
-func (c *ndkPrebuiltStlLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "SHARED_LIBRARIES"
-}
-
 func (p *prebuiltLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		if p.properties.Check_elf_files != nil {
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index 71e0cd8..8a7ea88 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -132,7 +132,7 @@
 
 	if ccModule.linker != nil {
 		specifiedDeps := specifiedDeps{}
-		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(specifiedDeps)
+		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx, ccModule, specifiedDeps)
 
 		p.SharedLibs = specifiedDeps.sharedLibs
 		p.SystemSharedLibs = specifiedDeps.systemSharedLibs
diff --git a/cc/builder.go b/cc/builder.go
index 367bda3..cd535c1 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -22,6 +22,7 @@
 	"fmt"
 	"path/filepath"
 	"runtime"
+	"slices"
 	"strconv"
 	"strings"
 
@@ -156,11 +157,17 @@
 		"args")
 
 	// Rule to invoke `strip` (to discard symbols and data from object files) on darwin architecture.
-	darwinStrip = pctx.AndroidStaticRule("darwinStrip",
-		blueprint.RuleParams{
-			Command:     "${config.MacStripPath} -u -r -o $out $in",
-			CommandDeps: []string{"${config.MacStripPath}"},
-		})
+	darwinStrip = func() blueprint.Rule {
+		if runtime.GOOS == "darwin" {
+			return pctx.AndroidStaticRule("darwinStrip",
+				blueprint.RuleParams{
+					Command:     "${config.MacStripPath} -u -r -o $out $in",
+					CommandDeps: []string{"${config.MacStripPath}"},
+				})
+		} else {
+			return nil
+		}
+	}()
 
 	// b/132822437: objcopy uses a file descriptor per .o file when called on .a files, which runs the system out of
 	// file descriptors on darwin.  Limit concurrent calls to 5 on darwin.
@@ -174,11 +181,17 @@
 		}
 	}()
 
-	darwinLipo = pctx.AndroidStaticRule("darwinLipo",
-		blueprint.RuleParams{
-			Command:     "${config.MacLipoPath} -create -output $out $in",
-			CommandDeps: []string{"${config.MacLipoPath}"},
-		})
+	darwinLipo = func() blueprint.Rule {
+		if runtime.GOOS == "darwin" {
+			return pctx.AndroidStaticRule("darwinLipo",
+				blueprint.RuleParams{
+					Command:     "${config.MacLipoPath} -create -output $out $in",
+					CommandDeps: []string{"${config.MacLipoPath}"},
+				})
+		} else {
+			return nil
+		}
+	}()
 
 	_ = pctx.SourcePathVariable("archiveRepackPath", "build/soong/scripts/archive_repack.sh")
 
@@ -898,6 +911,16 @@
 		"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags,
 		"crtEnd":        strings.Join(crtEnd.Strings(), " "),
 	}
+
+	// On Windows, we always generate a PDB file
+	// --strip-debug is needed to also keep COFF symbols which are needed when
+	// we patch binaries with symbol_inject.
+	if ctx.Windows() {
+		pdb := outputFile.ReplaceExtension(ctx, "pdb")
+		args["ldFlags"] = args["ldFlags"] + " -Wl,--strip-debug -Wl,--pdb=" + pdb.String() + " "
+		implicitOutputs = append(slices.Clone(implicitOutputs), pdb)
+	}
+
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
 		rule = ldRE
 		args["implicitOutputs"] = strings.Join(implicitOutputs.Strings(), ",")
diff --git a/cc/cc.go b/cc/cc.go
index 3c17be6..947dc1a 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -613,7 +613,7 @@
 	coverageOutputFilePath() android.OptionalPath
 
 	// Get the deps that have been explicitly specified in the properties.
-	linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps
+	linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps
 
 	moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON)
 }
@@ -906,19 +906,17 @@
 	logtagsPaths android.Paths
 
 	WholeRustStaticlib bool
+
+	hasAidl         bool
+	hasLex          bool
+	hasProto        bool
+	hasRenderscript bool
+	hasSysprop      bool
+	hasWinMsg       bool
+	hasYacc         bool
 }
 
 func (c *Module) AddJSONData(d *map[string]interface{}) {
-	var hasAidl, hasLex, hasProto, hasRenderscript, hasSysprop, hasWinMsg, hasYacc bool
-	if b, ok := c.compiler.(*baseCompiler); ok {
-		hasAidl = b.hasSrcExt(".aidl")
-		hasLex = b.hasSrcExt(".l") || b.hasSrcExt(".ll")
-		hasProto = b.hasSrcExt(".proto")
-		hasRenderscript = b.hasSrcExt(".rscript") || b.hasSrcExt(".fs")
-		hasSysprop = b.hasSrcExt(".sysprop")
-		hasWinMsg = b.hasSrcExt(".mc")
-		hasYacc = b.hasSrcExt(".y") || b.hasSrcExt(".yy")
-	}
 	c.AndroidModuleBase().AddJSONData(d)
 	(*d)["Cc"] = map[string]interface{}{
 		"SdkVersion":             c.SdkVersion(),
@@ -948,14 +946,14 @@
 		"IsVendorPublicLibrary":  c.IsVendorPublicLibrary(),
 		"ApexSdkVersion":         c.apexSdkVersion,
 		"TestFor":                c.TestFor(),
-		"AidlSrcs":               hasAidl,
-		"LexSrcs":                hasLex,
-		"ProtoSrcs":              hasProto,
-		"RenderscriptSrcs":       hasRenderscript,
-		"SyspropSrcs":            hasSysprop,
-		"WinMsgSrcs":             hasWinMsg,
-		"YaccSrsc":               hasYacc,
-		"OnlyCSrcs":              !(hasAidl || hasLex || hasProto || hasRenderscript || hasSysprop || hasWinMsg || hasYacc),
+		"AidlSrcs":               c.hasAidl,
+		"LexSrcs":                c.hasLex,
+		"ProtoSrcs":              c.hasProto,
+		"RenderscriptSrcs":       c.hasRenderscript,
+		"SyspropSrcs":            c.hasSysprop,
+		"WinMsgSrcs":             c.hasWinMsg,
+		"YaccSrsc":               c.hasYacc,
+		"OnlyCSrcs":              !(c.hasAidl || c.hasLex || c.hasProto || c.hasRenderscript || c.hasSysprop || c.hasWinMsg || c.hasYacc),
 		"OptimizeForSize":        c.OptimizeForSize(),
 	}
 }
@@ -1028,13 +1026,6 @@
 	return ""
 }
 
-func (c *Module) NdkPrebuiltStl() bool {
-	if _, ok := c.linker.(*ndkPrebuiltStlLinker); ok {
-		return true
-	}
-	return false
-}
-
 func (c *Module) StubDecorator() bool {
 	if _, ok := c.linker.(*stubDecorator); ok {
 		return true
@@ -1088,16 +1079,6 @@
 	return false
 }
 
-func (c *Module) IsNdkPrebuiltStl() bool {
-	if c.linker == nil {
-		return false
-	}
-	if _, ok := c.linker.(*ndkPrebuiltStlLinker); ok {
-		return true
-	}
-	return false
-}
-
 func (c *Module) RlibStd() bool {
 	panic(fmt.Errorf("RlibStd called on non-Rust module: %q", c.BaseModuleName()))
 }
@@ -2103,6 +2084,16 @@
 
 	buildComplianceMetadataInfo(ctx, c, deps)
 
+	if b, ok := c.compiler.(*baseCompiler); ok {
+		c.hasAidl = b.hasSrcExt(ctx, ".aidl")
+		c.hasLex = b.hasSrcExt(ctx, ".l") || b.hasSrcExt(ctx, ".ll")
+		c.hasProto = b.hasSrcExt(ctx, ".proto")
+		c.hasRenderscript = b.hasSrcExt(ctx, ".rscript") || b.hasSrcExt(ctx, ".fs")
+		c.hasSysprop = b.hasSrcExt(ctx, ".sysprop")
+		c.hasWinMsg = b.hasSrcExt(ctx, ".mc")
+		c.hasYacc = b.hasSrcExt(ctx, ".y") || b.hasSrcExt(ctx, ".yy")
+	}
+
 	c.setOutputFiles(ctx)
 }
 
@@ -2754,10 +2745,6 @@
 		return
 	}
 	if c, ok := to.(*Module); ok {
-		if c.NdkPrebuiltStl() {
-			// These are allowed, but they don't set sdk_version
-			return
-		}
 		if c.StubDecorator() {
 			// These aren't real libraries, but are the stub shared libraries that are included in
 			// the NDK.
@@ -3927,7 +3914,6 @@
 	headerLibrary
 	testBin // testBinary already declared
 	ndkLibrary
-	ndkPrebuiltStl
 )
 
 func (c *Module) typ() moduleType {
@@ -3966,8 +3952,6 @@
 		return sharedLibrary
 	} else if c.isNDKStubLibrary() {
 		return ndkLibrary
-	} else if c.IsNdkPrebuiltStl() {
-		return ndkPrebuiltStl
 	}
 	return unknownType
 }
diff --git a/cc/cc_test.go b/cc/cc_test.go
index ccdaae5..779e53d 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -851,7 +851,7 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	staticLibInfo, _ := android.SingletonModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
+	staticLibInfo, _ := android.OtherModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
 	actual := android.Paths(staticLibInfo.TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
 	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
 
@@ -887,7 +887,7 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	staticLibInfo, _ := android.SingletonModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
+	staticLibInfo, _ := android.OtherModuleProvider(ctx, moduleA, StaticLibraryInfoProvider)
 	actual := android.Paths(staticLibInfo.TransitiveStaticLibrariesForOrdering.ToList()).RelativeToTop()
 	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b"})
 
@@ -999,7 +999,7 @@
 	checkExportedIncludeDirs := func(module, variant string, expectedSystemDirs []string, expectedDirs ...string) {
 		t.Helper()
 		m := result.ModuleForTests(module, variant).Module()
-		f, _ := android.SingletonModuleProvider(result, m, FlagExporterInfoProvider)
+		f, _ := android.OtherModuleProvider(result, m, FlagExporterInfoProvider)
 		android.AssertPathsRelativeToTopEquals(t, "exported include dirs for "+module+"["+variant+"]",
 			expectedDirs, f.IncludeDirs)
 		android.AssertPathsRelativeToTopEquals(t, "exported include dirs for "+module+"["+variant+"]",
@@ -1027,7 +1027,7 @@
 			}
 		}
 		vendorModule := result.ModuleForTests(module, vendorVariant).Module()
-		vendorInfo, _ := android.SingletonModuleProvider(result, vendorModule, FlagExporterInfoProvider)
+		vendorInfo, _ := android.OtherModuleProvider(result, vendorModule, FlagExporterInfoProvider)
 		vendorDirs := android.Concat(vendorInfo.IncludeDirs, vendorInfo.SystemIncludeDirs)
 		android.AssertStringEquals(t, module+" has different exported include dirs for vendor variant and ABI check",
 			android.JoinPathsWithPrefix(vendorDirs, "-I"), abiCheckFlags)
@@ -2452,7 +2452,7 @@
 
 	checkIncludeDirs := func(t *testing.T, ctx *android.TestContext, module android.Module, checkers ...exportedChecker) {
 		t.Helper()
-		exported, _ := android.SingletonModuleProvider(ctx, module, FlagExporterInfoProvider)
+		exported, _ := android.OtherModuleProvider(ctx, module, FlagExporterInfoProvider)
 		name := module.Name()
 
 		for _, checker := range checkers {
@@ -2760,7 +2760,7 @@
 		"external/foo/libarm",
 		"external/foo/lib32",
 		"external/foo/libandroid_arm",
-		"defaults/cc/common/ndk_libc++_shared",
+		"defaults/cc/common/ndk_libc++_shared_include_dirs",
 	}
 
 	conly := []string{"-fPIC", "${config.CommonGlobalConlyflags}"}
@@ -3194,12 +3194,7 @@
 
 	ctx = android.GroupFixturePreparers(
 		prepareForCcTest,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			if variables.BuildFlags == nil {
-				variables.BuildFlags = make(map[string]string)
-			}
-			variables.BuildFlags["RELEASE_BOARD_API_LEVEL_FROZEN"] = "true"
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_BOARD_API_LEVEL_FROZEN", "true"),
 	).RunTestWithBp(t, bp)
 	testSdkVersionFlag("libfoo", "30")
 	testSdkVersionFlag("libbar", "29")
diff --git a/cc/cmake_module_cc.txt b/cc/cmake_module_cc.txt
index 0dc45ae..0f6e62f 100644
--- a/cc/cmake_module_cc.txt
+++ b/cc/cmake_module_cc.txt
@@ -2,10 +2,10 @@
 <<$includeDirs := getIncludeDirs .Ctx .M>>
 <<$cflags := getCflagsProperty .Ctx .M>>
 <<$deps := mapLibraries .Ctx .M (concat5
-(getLinkerProperties .M).Whole_static_libs
-(getLinkerProperties .M).Static_libs
-(getLinkerProperties .M).Shared_libs
-(getLinkerProperties .M).Header_libs
+(getWholeStaticLibsProperty .Ctx .M)
+(getStaticLibsProperty .Ctx .M)
+(getSharedLibsProperty .Ctx .M)
+(getHeaderLibsProperty .Ctx .M)
 (getExtraLibs .M)
 ) .Pprop.LibraryMapping>>
 <<$moduleType := getModuleType .M>>
diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go
index 8f3ad96..61fa46d 100644
--- a/cc/cmake_snapshot.go
+++ b/cc/cmake_snapshot.go
@@ -204,12 +204,28 @@
 			return m.compiler.baseCompilerProps()
 		},
 		"getCflagsProperty": func(ctx android.ModuleContext, m *Module) []string {
-			cflags := m.compiler.baseCompilerProps().Cflags
-			return cflags.GetOrDefault(ctx, nil)
+			prop := m.compiler.baseCompilerProps().Cflags
+			return prop.GetOrDefault(ctx, nil)
 		},
 		"getLinkerProperties": func(m *Module) BaseLinkerProperties {
 			return m.linker.baseLinkerProps()
 		},
+		"getWholeStaticLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
+			prop := m.linker.baseLinkerProps().Whole_static_libs
+			return prop.GetOrDefault(ctx, nil)
+		},
+		"getStaticLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
+			prop := m.linker.baseLinkerProps().Static_libs
+			return prop.GetOrDefault(ctx, nil)
+		},
+		"getSharedLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
+			prop := m.linker.baseLinkerProps().Shared_libs
+			return prop.GetOrDefault(ctx, nil)
+		},
+		"getHeaderLibsProperty": func(ctx android.ModuleContext, m *Module) []string {
+			prop := m.linker.baseLinkerProps().Header_libs
+			return prop.GetOrDefault(ctx, nil)
+		},
 		"getExtraLibs":   getExtraLibs,
 		"getIncludeDirs": getIncludeDirs,
 		"mapLibraries": func(ctx android.ModuleContext, m *Module, libs []string, mapping map[string]LibraryMappingProperty) []string {
@@ -476,7 +492,8 @@
 		var prebuiltsList android.Paths
 
 		ctx.VisitDirectDepsWithTag(cmakeSnapshotPrebuiltTag, func(dep android.Module) {
-			for _, file := range dep.FilesToInstall() {
+			for _, file := range android.OtherModuleProviderOrDefault(
+				ctx, dep, android.InstallFilesProvider).InstallFiles {
 				prebuiltsList = append(prebuiltsList, file)
 			}
 		})
diff --git a/cc/compdb.go b/cc/compdb.go
index da28183..b33f490 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -85,23 +85,24 @@
 	if err != nil {
 		log.Fatalf("Could not create file %s: %s", compDBFile, err)
 	}
-	defer f.Close()
+	defer func() {
+		if err := f.Close(); err != nil {
+			log.Fatalf("Could not close file %s: %s", compDBFile, err)
+		}
+	}()
 
 	v := make([]compDbEntry, 0, len(m))
-
 	for _, value := range m {
 		v = append(v, value)
 	}
-	var dat []byte
+
+	w := json.NewEncoder(f)
 	if outputCompdbDebugInfo {
-		dat, err = json.MarshalIndent(v, "", " ")
-	} else {
-		dat, err = json.Marshal(v)
+		w.SetIndent("", " ")
 	}
-	if err != nil {
-		log.Fatalf("Failed to marshal: %s", err)
+	if err := w.Encode(v); err != nil {
+		log.Fatalf("Failed to encode: %s", err)
 	}
-	f.Write(dat)
 
 	if finalLinkDir := ctx.Config().Getenv(envVariableCompdbLink); finalLinkDir != "" {
 		finalLinkPath := filepath.Join(finalLinkDir, compdbFilename)
diff --git a/cc/compiler.go b/cc/compiler.go
index ed10533..396ec88 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -37,7 +37,7 @@
 	// list of source files used to compile the C/C++ module.  May be .c, .cpp, or .S files.
 	// srcs may reference the outputs of other modules that produce source files like genrule
 	// or filegroup using the syntax ":module".
-	Srcs []string `android:"path,arch_variant"`
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	// list of source files that should not be compiled with clang-tidy.
 	Tidy_disabled_srcs []string `android:"path,arch_variant"`
@@ -47,7 +47,7 @@
 
 	// list of source files that should not be used to build the C/C++ module.
 	// This is most useful in the arch/multilib variants to remove non-common files
-	Exclude_srcs []string `android:"path,arch_variant"`
+	Exclude_srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	// list of module-specific flags that will be used for C and C++ compiles.
 	Cflags proptools.Configurable[[]string] `android:"arch_variant"`
@@ -229,7 +229,7 @@
 	} `android:"arch_variant"`
 
 	// Stores the original list of source files before being cleared by library reuse
-	OriginalSrcs []string `blueprint:"mutated"`
+	OriginalSrcs proptools.Configurable[[]string] `blueprint:"mutated"`
 
 	// Build and link with OpenMP
 	Openmp *bool `android:"arch_variant"`
@@ -300,7 +300,7 @@
 	deps.AidlLibs = append(deps.AidlLibs, compiler.Properties.Aidl.Libs...)
 
 	android.ProtoDeps(ctx, &compiler.Proto)
-	if compiler.hasSrcExt(".proto") {
+	if compiler.hasSrcExt(ctx, ".proto") {
 		deps = protoDeps(ctx, deps, &compiler.Proto, Bool(compiler.Properties.Proto.Static))
 	}
 
@@ -363,7 +363,9 @@
 	tc := ctx.toolchain()
 	modulePath := ctx.ModuleDir()
 
-	compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, compiler.Properties.Srcs, compiler.Properties.Exclude_srcs)
+	srcs := compiler.Properties.Srcs.GetOrDefault(ctx, nil)
+	exclude_srcs := compiler.Properties.Exclude_srcs.GetOrDefault(ctx, nil)
+	compiler.srcsBeforeGen = android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs)
 	compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...)
 
 	cflags := compiler.Properties.Cflags.GetOrDefault(ctx, nil)
@@ -603,11 +605,11 @@
 		flags.Global.CFlags = append(flags.Global.CFlags, "-DANDROID_STRICT")
 	}
 
-	if compiler.hasSrcExt(".proto") {
+	if compiler.hasSrcExt(ctx, ".proto") {
 		flags = protoFlags(ctx, flags, &compiler.Proto)
 	}
 
-	if compiler.hasSrcExt(".y") || compiler.hasSrcExt(".yy") {
+	if compiler.hasSrcExt(ctx, ".y") || compiler.hasSrcExt(ctx, ".yy") {
 		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "yacc", ctx.ModuleDir()).String())
 	}
@@ -617,7 +619,7 @@
 		ctx.ModuleErrorf("aidl.libs and (aidl.include_dirs or aidl.local_include_dirs) can't be set at the same time. For aidl headers, please only use aidl.libs prop")
 	}
 
-	if compiler.hasAidl(deps) {
+	if compiler.hasAidl(ctx, deps) {
 		flags.aidlFlags = append(flags.aidlFlags, compiler.Properties.Aidl.Flags...)
 		if len(compiler.Properties.Aidl.Local_include_dirs) > 0 {
 			localAidlIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Aidl.Local_include_dirs)
@@ -646,7 +648,7 @@
 		}
 		flags.aidlFlags = append(flags.aidlFlags, "--min_sdk_version="+aidlMinSdkVersion)
 
-		if compiler.hasSrcExt(".aidl") {
+		if compiler.hasSrcExt(ctx, ".aidl") {
 			flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 				"-I"+android.PathForModuleGen(ctx, "aidl").String())
 		}
@@ -656,16 +658,16 @@
 		}
 	}
 
-	if compiler.hasSrcExt(".rscript") || compiler.hasSrcExt(".fs") {
+	if compiler.hasSrcExt(ctx, ".rscript") || compiler.hasSrcExt(ctx, ".fs") {
 		flags = rsFlags(ctx, flags, &compiler.Properties)
 	}
 
-	if compiler.hasSrcExt(".sysprop") {
+	if compiler.hasSrcExt(ctx, ".sysprop") {
 		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "sysprop", "include").String())
 	}
 
-	if len(compiler.Properties.Srcs) > 0 {
+	if len(srcs) > 0 {
 		module := ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName()
 		if inList("-Wno-error", flags.Local.CFlags) || inList("-Wno-error", flags.Local.CppFlags) {
 			addToModuleList(ctx, modulesUsingWnoErrorKey, module)
@@ -706,18 +708,18 @@
 	return flags
 }
 
-func (compiler *baseCompiler) hasSrcExt(ext string) bool {
+func (compiler *baseCompiler) hasSrcExt(ctx BaseModuleContext, ext string) bool {
 	for _, src := range compiler.srcsBeforeGen {
 		if src.Ext() == ext {
 			return true
 		}
 	}
-	for _, src := range compiler.Properties.Srcs {
+	for _, src := range compiler.Properties.Srcs.GetOrDefault(ctx, nil) {
 		if filepath.Ext(src) == ext {
 			return true
 		}
 	}
-	for _, src := range compiler.Properties.OriginalSrcs {
+	for _, src := range compiler.Properties.OriginalSrcs.GetOrDefault(ctx, nil) {
 		if filepath.Ext(src) == ext {
 			return true
 		}
@@ -745,8 +747,8 @@
 	return nil
 }
 
-func (compiler *baseCompiler) hasAidl(deps PathDeps) bool {
-	return len(deps.AidlLibraryInfos) > 0 || compiler.hasSrcExt(".aidl")
+func (compiler *baseCompiler) hasAidl(ctx BaseModuleContext, deps PathDeps) bool {
+	return len(deps.AidlLibraryInfos) > 0 || compiler.hasSrcExt(ctx, ".aidl")
 }
 
 func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
@@ -796,13 +798,13 @@
 	Local_include_dirs []string `android:"arch_variant,variant_prepend"`
 
 	// list of static libraries that provide headers for this binding.
-	Static_libs []string `android:"arch_variant,variant_prepend"`
+	Static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of shared libraries that provide headers for this binding.
-	Shared_libs []string `android:"arch_variant"`
+	Shared_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// List of libraries which export include paths required for this module
-	Header_libs []string `android:"arch_variant,variant_prepend"`
+	Header_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of clang flags required to correctly interpret the headers.
 	Cflags proptools.Configurable[[]string] `android:"arch_variant"`
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 4856669..1783f49 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"os/exec"
 	"path/filepath"
+	"runtime"
 	"strings"
 	"sync"
 
@@ -28,6 +29,7 @@
 	darwinCflags = []string{
 		"-fPIC",
 		"-funwind-tables",
+		"-fno-omit-frame-pointer",
 
 		"-isysroot ${macSdkRoot}",
 		"-mmacosx-version-min=${macMinVersion}",
@@ -73,31 +75,33 @@
 )
 
 func init() {
-	pctx.VariableFunc("macSdkRoot", func(ctx android.PackageVarContext) string {
-		return getMacTools(ctx).sdkRoot
-	})
-	pctx.StaticVariable("macMinVersion", "10.14")
-	pctx.VariableFunc("MacArPath", func(ctx android.PackageVarContext) string {
-		return getMacTools(ctx).arPath
-	})
+	if runtime.GOOS == "darwin" {
+		pctx.VariableFunc("macSdkRoot", func(ctx android.PackageVarContext) string {
+			return getMacTools(ctx).sdkRoot
+		})
+		pctx.StaticVariable("macMinVersion", "10.14")
+		pctx.VariableFunc("MacArPath", func(ctx android.PackageVarContext) string {
+			return getMacTools(ctx).arPath
+		})
 
-	pctx.VariableFunc("MacLipoPath", func(ctx android.PackageVarContext) string {
-		return getMacTools(ctx).lipoPath
-	})
+		pctx.VariableFunc("MacLipoPath", func(ctx android.PackageVarContext) string {
+			return getMacTools(ctx).lipoPath
+		})
 
-	pctx.VariableFunc("MacStripPath", func(ctx android.PackageVarContext) string {
-		return getMacTools(ctx).stripPath
-	})
+		pctx.VariableFunc("MacStripPath", func(ctx android.PackageVarContext) string {
+			return getMacTools(ctx).stripPath
+		})
 
-	pctx.VariableFunc("MacToolPath", func(ctx android.PackageVarContext) string {
-		return getMacTools(ctx).toolPath
-	})
+		pctx.VariableFunc("MacToolPath", func(ctx android.PackageVarContext) string {
+			return getMacTools(ctx).toolPath
+		})
 
-	pctx.StaticVariable("DarwinCflags", strings.Join(darwinCflags, " "))
-	pctx.StaticVariable("DarwinLdflags", strings.Join(darwinLdflags, " "))
-	pctx.StaticVariable("DarwinLldflags", strings.Join(darwinLdflags, " "))
+		pctx.StaticVariable("DarwinCflags", strings.Join(darwinCflags, " "))
+		pctx.StaticVariable("DarwinLdflags", strings.Join(darwinLdflags, " "))
+		pctx.StaticVariable("DarwinLldflags", strings.Join(darwinLdflags, " "))
 
-	pctx.StaticVariable("DarwinYasmFlags", "-f macho -m amd64")
+		pctx.StaticVariable("DarwinYasmFlags", "-f macho -m amd64")
+	}
 }
 
 func MacStripPath(ctx android.PathContext) string {
diff --git a/cc/config/global.go b/cc/config/global.go
index bf2502f..0e8fff6 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -136,11 +136,6 @@
 		// displaying logs in web browsers.
 		"-fmessage-length=0",
 
-		// Disable C++17 "relaxed template template argument matching" as a workaround for
-		// our out-dated libcxx.
-		// http://b/341084395
-		"-fno-relaxed-template-template-args",
-
 		// Using simple template names reduces the size of debug builds.
 		"-gsimple-template-names",
 
@@ -149,9 +144,6 @@
 
 		// Make paths in deps files relative.
 		"-no-canonical-prefixes",
-
-		// http://b/315250603 temporarily disabled
-		"-Wno-error=format",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -181,9 +173,6 @@
 		"-Werror=sequence-point",
 		"-Werror=format-security",
 		"-nostdlibinc",
-
-		// Emit additional debug info for AutoFDO
-		"-fdebug-info-for-profiling",
 	}
 
 	commonGlobalLldflags = []string{
@@ -286,7 +275,7 @@
 		"-Wno-zero-as-null-pointer-constant",        // http://b/68236239
 		"-Wno-deprecated-anon-enum-enum-conversion", // http://b/153746485
 		"-Wno-deprecated-enum-enum-conversion",
-		"-Wno-pessimizing-move", // http://b/154270751
+		"-Wno-error=pessimizing-move", // http://b/154270751
 		// New warnings to be fixed after clang-r399163
 		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
 		// New warnings to be fixed after clang-r428724
@@ -304,13 +293,6 @@
 		"-Wno-error=invalid-offsetof",
 		"-Wno-error=thread-safety-reference-return",
 
-		// Irrelevant on Android because _we_ don't use exceptions, but causes
-		// lots of build noise because libcxx/libcxxabi do. This can probably
-		// go away when we're on a new enough libc++, but has to be global
-		// until then because it causes warnings in the _callers_, not the
-		// project itself.
-		"-Wno-deprecated-dynamic-exception-spec",
-
 		// Allow using VLA CXX extension.
 		"-Wno-vla-cxx-extension",
 	}
@@ -350,6 +332,9 @@
 
 		"-Wno-unused",
 		"-Wno-deprecated",
+
+		// http://b/315250603 temporarily disabled
+		"-Wno-error=format",
 	}
 
 	// Similar to noOverrideGlobalCflags, but applies only to third-party code
@@ -376,6 +361,7 @@
 		"-Wno-unqualified-std-cast-call",
 		"-Wno-array-parameter",
 		"-Wno-gnu-offsetof-extensions",
+		"-Wno-pessimizing-move",
 		// TODO: Enable this warning http://b/315245071
 		"-Wno-fortify-source",
 	}
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index 515cb21..ddc86c2 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -25,6 +25,8 @@
 
 		"-fPIC",
 
+		"-fno-omit-frame-pointer",
+
 		"-U_FORTIFY_SOURCE",
 		"-D_FORTIFY_SOURCE=2",
 		"-fstack-protector-strong",
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 7f22377..287967c 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -26,6 +26,8 @@
 
 		"-fPIC",
 
+		"-fno-omit-frame-pointer",
+
 		"-U_FORTIFY_SOURCE",
 		"-D_FORTIFY_SOURCE=2",
 		"-fstack-protector",
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 1e61b01..a4d43b9 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -43,6 +43,12 @@
 		"-mno-ms-bitfields",
 
 		"--sysroot ${WindowsGccRoot}/${WindowsGccTriple}",
+
+		// Windows flags to generate PDB
+		"-g",
+		"-gcodeview",
+
+		"-fno-omit-frame-pointer",
 	}
 
 	windowsIncludeFlags = []string{
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 92f2c5e..d9e221b 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -373,7 +373,7 @@
 		}
 
 		if targetFramework == fuzz.AFL {
-			fuzzBin.baseCompiler.Properties.Srcs = append(fuzzBin.baseCompiler.Properties.Srcs, ":aflpp_driver", ":afl-compiler-rt")
+			fuzzBin.baseCompiler.Properties.Srcs.AppendSimpleValue([]string{":aflpp_driver", ":afl-compiler-rt"})
 			module.fuzzer.Properties.FuzzFramework = fuzz.AFL
 		}
 	})
diff --git a/cc/image.go b/cc/image.go
index 2e52ccc..7594a08 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -465,11 +465,8 @@
 
 func squashVendorSrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
-		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
-			lib.baseCompiler.Properties.Target.Vendor.Srcs...)
-
-		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
-			lib.baseCompiler.Properties.Target.Vendor.Exclude_srcs...)
+		lib.baseCompiler.Properties.Srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Vendor.Srcs)
+		lib.baseCompiler.Properties.Exclude_srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Vendor.Exclude_srcs)
 
 		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
 			lib.baseCompiler.Properties.Target.Vendor.Exclude_generated_sources...)
@@ -482,11 +479,8 @@
 
 func squashProductSrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
-		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
-			lib.baseCompiler.Properties.Target.Product.Srcs...)
-
-		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
-			lib.baseCompiler.Properties.Target.Product.Exclude_srcs...)
+		lib.baseCompiler.Properties.Srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Product.Srcs)
+		lib.baseCompiler.Properties.Exclude_srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Product.Exclude_srcs)
 
 		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
 			lib.baseCompiler.Properties.Target.Product.Exclude_generated_sources...)
@@ -499,11 +493,8 @@
 
 func squashRecoverySrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
-		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
-			lib.baseCompiler.Properties.Target.Recovery.Srcs...)
-
-		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
-			lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs...)
+		lib.baseCompiler.Properties.Srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Recovery.Srcs)
+		lib.baseCompiler.Properties.Exclude_srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs)
 
 		lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
 			lib.baseCompiler.Properties.Target.Recovery.Exclude_generated_sources...)
@@ -512,13 +503,13 @@
 
 func squashVendorRamdiskSrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
-		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs, lib.baseCompiler.Properties.Target.Vendor_ramdisk.Exclude_srcs...)
+		lib.baseCompiler.Properties.Exclude_srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Vendor_ramdisk.Exclude_srcs)
 	}
 }
 
 func squashRamdiskSrcs(m *Module) {
 	if lib, ok := m.compiler.(*libraryDecorator); ok {
-		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs, lib.baseCompiler.Properties.Target.Ramdisk.Exclude_srcs...)
+		lib.baseCompiler.Properties.Exclude_srcs.AppendSimpleValue(lib.baseCompiler.Properties.Target.Ramdisk.Exclude_srcs)
 	}
 }
 
diff --git a/cc/library.go b/cc/library.go
index 092b177..6017848 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -142,7 +142,7 @@
 // Use `StaticProperties` or `SharedProperties`, depending on which variant is needed.
 // `StaticOrSharedProperties` exists only to avoid duplication.
 type StaticOrSharedProperties struct {
-	Srcs []string `android:"path,arch_variant"`
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	Tidy_disabled_srcs []string `android:"path,arch_variant"`
 
@@ -152,11 +152,11 @@
 
 	Cflags proptools.Configurable[[]string] `android:"arch_variant"`
 
-	Enabled            *bool    `android:"arch_variant"`
-	Whole_static_libs  []string `android:"arch_variant"`
-	Static_libs        []string `android:"arch_variant"`
-	Shared_libs        []string `android:"arch_variant"`
-	System_shared_libs []string `android:"arch_variant"`
+	Enabled            *bool                            `android:"arch_variant"`
+	Whole_static_libs  proptools.Configurable[[]string] `android:"arch_variant"`
+	Static_libs        proptools.Configurable[[]string] `android:"arch_variant"`
+	Shared_libs        proptools.Configurable[[]string] `android:"arch_variant"`
+	System_shared_libs []string                         `android:"arch_variant"`
 
 	Export_shared_lib_headers []string `android:"arch_variant"`
 	Export_static_lib_headers []string `android:"arch_variant"`
@@ -633,14 +633,17 @@
 		return objs
 	}
 
+	srcs := library.baseCompiler.Properties.Srcs.GetOrDefault(ctx, nil)
+	staticSrcs := library.StaticProperties.Static.Srcs.GetOrDefault(ctx, nil)
+	sharedSrcs := library.SharedProperties.Shared.Srcs.GetOrDefault(ctx, nil)
 	if !library.buildShared() && !library.buildStatic() {
-		if len(library.baseCompiler.Properties.Srcs) > 0 {
+		if len(srcs) > 0 {
 			ctx.PropertyErrorf("srcs", "cc_library_headers must not have any srcs")
 		}
-		if len(library.StaticProperties.Static.Srcs) > 0 {
+		if len(staticSrcs) > 0 {
 			ctx.PropertyErrorf("static.srcs", "cc_library_headers must not have any srcs")
 		}
-		if len(library.SharedProperties.Shared.Srcs) > 0 {
+		if len(sharedSrcs) > 0 {
 			ctx.PropertyErrorf("shared.srcs", "cc_library_headers must not have any srcs")
 		}
 		return Objects{}
@@ -651,8 +654,8 @@
 		for _, dir := range dirs {
 			flags.SAbiFlags = append(flags.SAbiFlags, "-I"+dir)
 		}
-		totalLength := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) +
-			len(library.SharedProperties.Shared.Srcs) + len(library.StaticProperties.Static.Srcs)
+		totalLength := len(srcs) + len(deps.GeneratedSources) +
+			len(sharedSrcs) + len(staticSrcs)
 		if totalLength > 0 {
 			flags.SAbiDump = true
 		}
@@ -662,13 +665,13 @@
 	buildFlags := flagsToBuilderFlags(flags)
 
 	if library.static() {
-		srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs)
+		srcs := android.PathsForModuleSrc(ctx, staticSrcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, srcs,
 			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_disabled_srcs),
 			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_timeout_srcs),
 			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	} else if library.shared() {
-		srcs := android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Srcs)
+		srcs := android.PathsForModuleSrc(ctx, sharedSrcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary, srcs,
 			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_disabled_srcs),
 			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_timeout_srcs),
@@ -834,9 +837,9 @@
 
 	if library.static() {
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs,
-			library.StaticProperties.Static.Whole_static_libs...)
-		deps.StaticLibs = append(deps.StaticLibs, library.StaticProperties.Static.Static_libs...)
-		deps.SharedLibs = append(deps.SharedLibs, library.StaticProperties.Static.Shared_libs...)
+			library.StaticProperties.Static.Whole_static_libs.GetOrDefault(ctx, nil)...)
+		deps.StaticLibs = append(deps.StaticLibs, library.StaticProperties.Static.Static_libs.GetOrDefault(ctx, nil)...)
+		deps.SharedLibs = append(deps.SharedLibs, library.StaticProperties.Static.Shared_libs.GetOrDefault(ctx, nil)...)
 
 		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.StaticProperties.Static.Export_shared_lib_headers...)
 		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
@@ -849,9 +852,9 @@
 		if library.baseLinker.Properties.crtPadSegment() {
 			deps.CrtEnd = append(deps.CrtEnd, ctx.toolchain().CrtPadSegmentSharedLibrary()...)
 		}
-		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...)
-		deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...)
-		deps.SharedLibs = append(deps.SharedLibs, library.SharedProperties.Shared.Shared_libs...)
+		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs.GetOrDefault(ctx, nil)...)
+		deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs.GetOrDefault(ctx, nil)...)
+		deps.SharedLibs = append(deps.SharedLibs, library.SharedProperties.Shared.Shared_libs.GetOrDefault(ctx, nil)...)
 
 		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.SharedProperties.Shared.Export_shared_lib_headers...)
 		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.SharedProperties.Shared.Export_static_lib_headers...)
@@ -897,8 +900,8 @@
 	return deps
 }
 
-func (library *libraryDecorator) linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps {
-	specifiedDeps = library.baseLinker.linkerSpecifiedDeps(specifiedDeps)
+func (library *libraryDecorator) linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
+	specifiedDeps = library.baseLinker.linkerSpecifiedDeps(ctx, module, specifiedDeps)
 	var properties StaticOrSharedProperties
 	if library.static() {
 		properties = library.StaticProperties.Static
@@ -906,7 +909,8 @@
 		properties = library.SharedProperties.Shared
 	}
 
-	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, properties.Shared_libs...)
+	eval := module.ConfigurableEvaluator(ctx)
+	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, properties.Shared_libs.GetOrDefault(eval, nil)...)
 
 	// Must distinguish nil and [] in system_shared_libs - ensure that [] in
 	// either input list doesn't come out as nil.
@@ -1651,8 +1655,8 @@
 
 	// Optionally export aidl headers.
 	if Bool(library.Properties.Aidl.Export_aidl_headers) {
-		if library.baseCompiler.hasAidl(deps) {
-			if library.baseCompiler.hasSrcExt(".aidl") {
+		if library.baseCompiler.hasAidl(ctx, deps) {
+			if library.baseCompiler.hasSrcExt(ctx, ".aidl") {
 				dir := android.PathForModuleGen(ctx, "aidl")
 				library.reexportDirs(dir)
 			}
@@ -1668,7 +1672,7 @@
 
 	// Optionally export proto headers.
 	if Bool(library.Properties.Proto.Export_proto_headers) {
-		if library.baseCompiler.hasSrcExt(".proto") {
+		if library.baseCompiler.hasSrcExt(ctx, ".proto") {
 			var includes android.Paths
 			if flags.proto.CanonicalPathFromRoot {
 				includes = append(includes, flags.proto.SubDir)
@@ -1682,7 +1686,7 @@
 	}
 
 	// If the library is sysprop_library, expose either public or internal header selectively.
-	if library.baseCompiler.hasSrcExt(".sysprop") {
+	if library.baseCompiler.hasSrcExt(ctx, ".sysprop") {
 		dir := android.PathForModuleGen(ctx, "sysprop", "include")
 		if library.Properties.Sysprop.Platform != nil {
 			isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
@@ -2076,12 +2080,12 @@
 		// include directories.
 		if len(sharedCompiler.StaticProperties.Static.Cflags.GetOrDefault(ctx, nil)) == 0 &&
 			len(sharedCompiler.SharedProperties.Shared.Cflags.GetOrDefault(ctx, nil)) == 0 &&
-			len(sharedCompiler.StaticProperties.Static.Whole_static_libs) == 0 &&
-			len(sharedCompiler.SharedProperties.Shared.Whole_static_libs) == 0 &&
-			len(sharedCompiler.StaticProperties.Static.Static_libs) == 0 &&
-			len(sharedCompiler.SharedProperties.Shared.Static_libs) == 0 &&
-			len(sharedCompiler.StaticProperties.Static.Shared_libs) == 0 &&
-			len(sharedCompiler.SharedProperties.Shared.Shared_libs) == 0 &&
+			len(sharedCompiler.StaticProperties.Static.Whole_static_libs.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Whole_static_libs.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.StaticProperties.Static.Static_libs.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Static_libs.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.StaticProperties.Static.Shared_libs.GetOrDefault(ctx, nil)) == 0 &&
+			len(sharedCompiler.SharedProperties.Shared.Shared_libs.GetOrDefault(ctx, nil)) == 0 &&
 			// Compare System_shared_libs properties with nil because empty lists are
 			// semantically significant for them.
 			sharedCompiler.StaticProperties.Static.System_shared_libs == nil &&
@@ -2091,7 +2095,7 @@
 			ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, reuseObjTag, ctx.ModuleName())
 			sharedCompiler.baseCompiler.Properties.OriginalSrcs =
 				sharedCompiler.baseCompiler.Properties.Srcs
-			sharedCompiler.baseCompiler.Properties.Srcs = nil
+			sharedCompiler.baseCompiler.Properties.Srcs = proptools.NewConfigurable[[]string](nil, nil)
 			sharedCompiler.baseCompiler.Properties.Generated_sources = nil
 		}
 
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index e8a9827..053c460 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -543,7 +543,7 @@
 	p.ExportedFlags = exportedInfo.Flags
 	if ccModule.linker != nil {
 		specifiedDeps := specifiedDeps{}
-		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(specifiedDeps)
+		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx, ccModule, specifiedDeps)
 
 		if lib := ccModule.library; lib != nil {
 			if !lib.hasStubsVariants() {
diff --git a/cc/linker.go b/cc/linker.go
index d2974c2..0056817 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -37,16 +37,16 @@
 	// in their entirety.  For static library modules, all of the .o files from the intermediate
 	// directory of the dependency will be linked into this modules .a file.  For a shared library,
 	// the dependency's .a file will be linked into this module using -Wl,--whole-archive.
-	Whole_static_libs []string `android:"arch_variant,variant_prepend"`
+	Whole_static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of modules that should be statically linked into this module.
-	Static_libs []string `android:"arch_variant,variant_prepend"`
+	Static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of modules that should be dynamically linked into this module.
-	Shared_libs []string `android:"arch_variant"`
+	Shared_libs proptools.Configurable[[]string] `android:"arch_variant"`
 
 	// list of modules that should only provide headers for this module.
-	Header_libs []string `android:"arch_variant,variant_prepend"`
+	Header_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of module-specific flags that will be used for all link steps
 	Ldflags []string `android:"arch_variant"`
@@ -296,10 +296,10 @@
 }
 
 func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...)
-	deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs...)
-	deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Static_libs...)
-	deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Shared_libs...)
+	deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs.GetOrDefault(ctx, nil)...)
+	deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs.GetOrDefault(ctx, nil)...)
+	deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Static_libs.GetOrDefault(ctx, nil)...)
+	deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Shared_libs.GetOrDefault(ctx, nil)...)
 	deps.RuntimeLibs = append(deps.RuntimeLibs, linker.Properties.Runtime_libs...)
 
 	deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders, linker.Properties.Export_header_lib_headers...)
@@ -645,8 +645,9 @@
 	panic(fmt.Errorf("baseLinker doesn't know how to link"))
 }
 
-func (linker *baseLinker) linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps {
-	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, linker.Properties.Shared_libs...)
+func (linker *baseLinker) linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
+	eval := module.ConfigurableEvaluator(ctx)
+	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, linker.Properties.Shared_libs.GetOrDefault(eval, nil)...)
 
 	// Must distinguish nil and [] in system_shared_libs - ensure that [] in
 	// either input list doesn't come out as nil.
diff --git a/cc/makevars.go b/cc/makevars.go
index cd13965..c9352a4 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -140,7 +140,6 @@
 	ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(asanCflags, " "))
 
 	ctx.Strict("HWADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(hwasanCflags, " "))
-	ctx.Strict("HWADDRESS_SANITIZER_GLOBAL_OPTIONS", strings.Join(hwasanGlobalOptions, ","))
 
 	ctx.Strict("CFI_EXTRA_CFLAGS", strings.Join(cfiCflags, " "))
 	ctx.Strict("CFI_EXTRA_ASFLAGS", strings.Join(cfiAsflags, " "))
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index 5beeab1..8202cc0 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -46,7 +46,7 @@
 
 		if m, ok := module.(*Module); ok {
 			if installer, ok := m.installer.(*stubDecorator); ok {
-				if canDumpAbi(ctx.Config()) {
+				if canDumpAbi(ctx.Config(), ctx.ModuleDir(module)) {
 					depPaths = append(depPaths, installer.abiDumpPath)
 				}
 			}
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 57a3b3a..e002931 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
@@ -44,7 +45,7 @@
 }
 
 // Returns the NDK base include path for use with sdk_version current. Usable with -I.
-func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath {
+func getCurrentIncludePath(ctx android.PathContext) android.OutputPath {
 	return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
 }
 
@@ -73,6 +74,13 @@
 
 	// Path to the NOTICE file associated with the headers.
 	License *string `android:"path"`
+
+	// Set to true if the headers installed by this module should skip
+	// verification. This step ensures that each header is self-contained (can
+	// be #included alone) and is valid C. This should not be disabled except in
+	// rare cases. Outside bionic and external, if you're using this option
+	// you've probably made a mistake.
+	Skip_verification *bool
 }
 
 type headerModule struct {
@@ -309,6 +317,13 @@
 
 	// Path to the NOTICE file associated with the headers.
 	License *string
+
+	// Set to true if the headers installed by this module should skip
+	// verification. This step ensures that each header is self-contained (can
+	// be #included alone) and is valid C. This should not be disabled except in
+	// rare cases. Outside bionic and external, if you're using this option
+	// you've probably made a mistake.
+	Skip_verification *bool
 }
 
 type preprocessedHeadersModule struct {
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index b822e5c..3e35ef5 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -321,10 +321,19 @@
 }
 
 // Feature flag.
-func canDumpAbi(config android.Config) bool {
+func canDumpAbi(config android.Config, moduleDir string) bool {
 	if runtime.GOOS == "darwin" {
 		return false
 	}
+	if strings.HasPrefix(moduleDir, "bionic/") {
+		// Bionic has enough uncommon implementation details like ifuncs and asm
+		// code that the ABI tracking here has a ton of false positives. That's
+		// causing pretty extreme friction for development there, so disabling
+		// it until the workflow can be improved.
+		//
+		// http://b/358653811
+		return false
+	}
 	// http://b/156513478
 	return config.ReleaseNdkAbiMonitored()
 }
@@ -460,7 +469,7 @@
 	nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
 	objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
 	c.versionScriptPath = nativeAbiResult.versionScript
-	if canDumpAbi(ctx.Config()) {
+	if canDumpAbi(ctx.Config(), ctx.ModuleDir()) {
 		c.dumpAbi(ctx, nativeAbiResult.symbolList)
 		if canDiffAbi(ctx.Config()) {
 			c.diffAbi(ctx)
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
deleted file mode 100644
index f503982..0000000
--- a/cc/ndk_prebuilt.go
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cc
-
-import (
-	"strings"
-
-	"android/soong/android"
-)
-
-func init() {
-	android.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
-	android.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
-}
-
-// NDK prebuilt libraries.
-//
-// These differ from regular prebuilts in that they aren't stripped and usually aren't installed
-// either (with the exception of the shared STLs, which are installed to the app's directory rather
-// than to the system image).
-
-type ndkPrebuiltStlLinker struct {
-	*libraryDecorator
-}
-
-func (ndk *ndkPrebuiltStlLinker) linkerProps() []interface{} {
-	return append(ndk.libraryDecorator.linkerProps(), &ndk.Properties, &ndk.flagExporter.Properties)
-}
-
-func (*ndkPrebuiltStlLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	// NDK libraries can't have any dependencies
-	return deps
-}
-
-func (*ndkPrebuiltStlLinker) availableFor(what string) bool {
-	// ndk prebuilt objects are available to everywhere
-	return true
-}
-
-// ndk_prebuilt_shared_stl exports a precompiled ndk shared standard template
-// library (stl) library for linking operation. The soong's module name format
-// is ndk_<NAME>.so where the library is located under
-// ./prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/$(HOST_ARCH)/<NAME>.so.
-func NdkPrebuiltSharedStlFactory() android.Module {
-	module, library := NewLibrary(android.DeviceSupported)
-	library.BuildOnlyShared()
-	module.compiler = nil
-	module.linker = &ndkPrebuiltStlLinker{
-		libraryDecorator: library,
-	}
-	module.installer = nil
-	module.Properties.Sdk_version = StringPtr("minimum")
-	module.Properties.AlwaysSdk = true
-	module.stl.Properties.Stl = StringPtr("none")
-	return module.Init()
-}
-
-// ndk_prebuilt_static_stl exports a precompiled ndk static standard template
-// library (stl) library for linking operation. The soong's module name format
-// is ndk_<NAME>.a where the library is located under
-// ./prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/$(HOST_ARCH)/<NAME>.a.
-func NdkPrebuiltStaticStlFactory() android.Module {
-	module, library := NewLibrary(android.DeviceSupported)
-	library.BuildOnlyStatic()
-	module.compiler = nil
-	module.linker = &ndkPrebuiltStlLinker{
-		libraryDecorator: library,
-	}
-	module.installer = nil
-	module.Properties.Sdk_version = StringPtr("minimum")
-	module.Properties.HideFromMake = true
-	module.Properties.AlwaysSdk = true
-	module.Properties.Sdk_version = StringPtr("current")
-	module.stl.Properties.Stl = StringPtr("none")
-	return module.Init()
-}
-
-const (
-	libDir = "current/sources/cxx-stl/llvm-libc++/libs"
-)
-
-func getNdkStlLibDir(ctx android.ModuleContext) android.SourcePath {
-	return android.PathForSource(ctx, ctx.ModuleDir(), libDir).Join(ctx, ctx.Arch().Abi[0])
-}
-
-func (ndk *ndkPrebuiltStlLinker) link(ctx ModuleContext, flags Flags,
-	deps PathDeps, objs Objects) android.Path {
-	// A null build step, but it sets up the output path.
-	if !strings.HasPrefix(ctx.ModuleName(), "ndk_lib") {
-		ctx.ModuleErrorf("NDK prebuilt libraries must have an ndk_lib prefixed name")
-	}
-
-	ndk.libraryDecorator.flagExporter.exportIncludesAsSystem(ctx)
-
-	libName := strings.TrimPrefix(ctx.ModuleName(), "ndk_")
-	libExt := flags.Toolchain.ShlibSuffix()
-	if ndk.static() {
-		libExt = staticLibraryExtension
-	}
-
-	libDir := getNdkStlLibDir(ctx)
-	lib := libDir.Join(ctx, libName+libExt)
-
-	ndk.libraryDecorator.flagExporter.setProvider(ctx)
-
-	if ndk.static() {
-		depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(lib).Build()
-		android.SetProvider(ctx, StaticLibraryInfoProvider, StaticLibraryInfo{
-			StaticLibrary: lib,
-
-			TransitiveStaticLibrariesForOrdering: depSet,
-		})
-	} else {
-		android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
-			SharedLibrary: lib,
-			Target:        ctx.Target(),
-		})
-	}
-
-	return lib
-}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 3c48f68..f571523 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -54,7 +54,22 @@
 
 import (
 	"android/soong/android"
+	"fmt"
+	"path/filepath"
 	"strings"
+
+	"github.com/google/blueprint"
+)
+
+var (
+	verifyCCompat = pctx.AndroidStaticRule("verifyCCompat",
+		blueprint.RuleParams{
+			Command:     "$ccCmd -x c -fsyntax-only $flags $in && touch $out",
+			CommandDeps: []string{"$ccCmd"},
+		},
+		"ccCmd",
+		"flags",
+	)
 )
 
 func init() {
@@ -103,6 +118,45 @@
 	return android.PathForOutput(ctx, "ndk_abi_headers.txt")
 }
 
+func verifyNdkHeaderIsCCompatible(ctx android.SingletonContext,
+	src android.Path, dest android.Path) android.Path {
+	sysrootInclude := getCurrentIncludePath(ctx)
+	baseOutputDir := android.PathForOutput(ctx, "c-compat-verification")
+	installRelPath, err := filepath.Rel(sysrootInclude.String(), dest.String())
+	if err != nil {
+		ctx.Errorf("filepath.Rel(%q, %q) failed: %s", dest, sysrootInclude, err)
+	}
+	output := baseOutputDir.Join(ctx, installRelPath)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        verifyCCompat,
+		Description: fmt.Sprintf("Verifying C compatibility of %s", src),
+		Output:      output,
+		Input:       dest,
+		// Ensures that all the headers in the sysroot are already installed
+		// before testing any of the headers for C compatibility, and also that
+		// the check will be re-run whenever the sysroot changes. This is
+		// necessary because many of the NDK headers depend on other NDK
+		// headers, but we don't have explicit dependency tracking for that.
+		Implicits: []android.Path{getNdkHeadersTimestampFile(ctx)},
+		Args: map[string]string{
+			"ccCmd": "${config.ClangBin}/clang",
+			"flags": fmt.Sprintf(
+				// Ideally we'd check each ABI, multiple API levels,
+				// fortify/non-fortify, and a handful of other variations. It's
+				// a lot more difficult to do that though, and would eat up more
+				// build time. All the problems we've seen so far that this
+				// check would catch have been in arch-generic and
+				// minSdkVersion-generic code in frameworks though, so this is a
+				// good place to start.
+				"-target aarch64-linux-android%d --sysroot %s",
+				android.FutureApiLevel.FinalOrFutureInt(),
+				getNdkSysrootBase(ctx).String(),
+			),
+		},
+	})
+	return output
+}
+
 func NdkSingleton() android.Singleton {
 	return &ndkSingleton{}
 }
@@ -143,10 +197,17 @@
 
 type ndkSingleton struct{}
 
+type srcDestPair struct {
+	src  android.Path
+	dest android.Path
+}
+
 func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var staticLibInstallPaths android.Paths
 	var headerSrcPaths android.Paths
 	var headerInstallPaths android.Paths
+	var headersToVerify []srcDestPair
+	var headerCCompatVerificationTimestampPaths android.Paths
 	var installPaths android.Paths
 	var licensePaths android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
@@ -157,6 +218,14 @@
 		if m, ok := module.(*headerModule); ok {
 			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
 			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
+			if !Bool(m.properties.Skip_verification) {
+				for i, installPath := range m.installPaths {
+					headersToVerify = append(headersToVerify, srcDestPair{
+						src:  m.srcPaths[i],
+						dest: installPath,
+					})
+				}
+			}
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
@@ -164,6 +233,10 @@
 		if m, ok := module.(*versionedHeaderModule); ok {
 			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
 			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
+			// Verification intentionally not done for headers that go through
+			// versioner. It'd be nice to have, but the only user is bionic, and
+			// that one module would also need to use skip_verification, so it
+			// wouldn't help at all.
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
@@ -171,6 +244,14 @@
 		if m, ok := module.(*preprocessedHeadersModule); ok {
 			headerSrcPaths = append(headerSrcPaths, m.srcPaths...)
 			headerInstallPaths = append(headerInstallPaths, m.installPaths...)
+			if !Bool(m.properties.Skip_verification) {
+				for i, installPath := range m.installPaths {
+					headersToVerify = append(headersToVerify, srcDestPair{
+						src:  m.srcPaths[i],
+						dest: installPath,
+					})
+				}
+			}
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
@@ -223,6 +304,12 @@
 		Implicits: headerInstallPaths,
 	})
 
+	for _, srcDestPair := range headersToVerify {
+		headerCCompatVerificationTimestampPaths = append(
+			headerCCompatVerificationTimestampPaths,
+			verifyNdkHeaderIsCCompatible(ctx, srcDestPair.src, srcDestPair.dest))
+	}
+
 	writeNdkAbiSrcFilter(ctx, headerSrcPaths, getNdkABIHeadersFile(ctx))
 
 	fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
@@ -235,6 +322,6 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:      android.Touch,
 		Output:    getNdkFullTimestampFile(ctx),
-		Implicits: fullDepPaths,
+		Implicits: append(fullDepPaths, headerCCompatVerificationTimestampPaths...),
 	})
 }
diff --git a/cc/object.go b/cc/object.go
index 8b23295..a4f4c84 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -19,6 +19,8 @@
 	"strings"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
 )
 
 //
@@ -50,13 +52,13 @@
 
 type ObjectLinkerProperties struct {
 	// list of static library modules that should only provide headers for this module.
-	Static_libs []string `android:"arch_variant,variant_prepend"`
+	Static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of shared library modules should only provide headers for this module.
-	Shared_libs []string `android:"arch_variant,variant_prepend"`
+	Shared_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of modules that should only provide headers for this module.
-	Header_libs []string `android:"arch_variant,variant_prepend"`
+	Header_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
 
 	// list of default libraries that will provide headers for this module.  If unset, generally
 	// defaults to libc, libm, and libdl.  Set to [] to prevent using headers from the defaults.
@@ -116,9 +118,9 @@
 func (*objectLinker) linkerInit(ctx BaseModuleContext) {}
 
 func (object *objectLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	deps.HeaderLibs = append(deps.HeaderLibs, object.Properties.Header_libs...)
-	deps.SharedLibs = append(deps.SharedLibs, object.Properties.Shared_libs...)
-	deps.StaticLibs = append(deps.StaticLibs, object.Properties.Static_libs...)
+	deps.HeaderLibs = append(deps.HeaderLibs, object.Properties.Header_libs.GetOrDefault(ctx, nil)...)
+	deps.SharedLibs = append(deps.SharedLibs, object.Properties.Shared_libs.GetOrDefault(ctx, nil)...)
+	deps.StaticLibs = append(deps.StaticLibs, object.Properties.Static_libs.GetOrDefault(ctx, nil)...)
 	deps.ObjFiles = append(deps.ObjFiles, object.Properties.Objs...)
 
 	deps.SystemSharedLibs = object.Properties.System_shared_libs
@@ -201,8 +203,9 @@
 	return outputFile
 }
 
-func (object *objectLinker) linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps {
-	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, object.Properties.Shared_libs...)
+func (object *objectLinker) linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps {
+	eval := module.ConfigurableEvaluator(ctx)
+	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, object.Properties.Shared_libs.GetOrDefault(eval, nil)...)
 
 	// Must distinguish nil and [] in system_shared_libs - ensure that [] in
 	// either input list doesn't come out as nil.
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index e023a32..fb151d8 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -48,7 +48,7 @@
 	Source_module_name *string
 
 	// a prebuilt library or binary. Can reference a genrule module that generates an executable file.
-	Srcs []string `android:"path,arch_variant"`
+	Srcs proptools.Configurable[[]string] `android:"path,arch_variant"`
 
 	Sanitized Sanitized `android:"arch_variant"`
 
@@ -75,10 +75,6 @@
 	return &p.Prebuilt
 }
 
-func (p *prebuiltLinker) PrebuiltSrcs() []string {
-	return p.properties.Srcs
-}
-
 type prebuiltLibraryInterface interface {
 	libraryInterface
 	prebuiltLinkerInterface
@@ -226,14 +222,14 @@
 
 func (p *prebuiltLibraryLinker) prebuiltSrcs(ctx android.BaseModuleContext) []string {
 	sanitize := ctx.Module().(*Module).sanitize
-	srcs := p.properties.Srcs
+	srcs := p.properties.Srcs.GetOrDefault(ctx, nil)
 	srcs = append(srcs, srcsForSanitizer(sanitize, p.properties.Sanitized)...)
 	if p.static() {
-		srcs = append(srcs, p.libraryDecorator.StaticProperties.Static.Srcs...)
+		srcs = append(srcs, p.libraryDecorator.StaticProperties.Static.Srcs.GetOrDefault(ctx, nil)...)
 		srcs = append(srcs, srcsForSanitizer(sanitize, p.libraryDecorator.StaticProperties.Static.Sanitized)...)
 	}
 	if p.shared() {
-		srcs = append(srcs, p.libraryDecorator.SharedProperties.Shared.Srcs...)
+		srcs = append(srcs, p.libraryDecorator.SharedProperties.Shared.Srcs.GetOrDefault(ctx, nil)...)
 		srcs = append(srcs, srcsForSanitizer(sanitize, p.libraryDecorator.SharedProperties.Shared.Sanitized)...)
 	}
 	return srcs
@@ -248,7 +244,7 @@
 }
 
 func (p *prebuiltLibraryLinker) disablePrebuilt() {
-	p.properties.Srcs = nil
+	p.properties.Srcs = proptools.NewConfigurable[[]string](nil, nil)
 	p.properties.Sanitized.None.Srcs = nil
 	p.properties.Sanitized.Address.Srcs = nil
 	p.properties.Sanitized.Hwaddress.Srcs = nil
@@ -416,7 +412,7 @@
 func (p *prebuiltBinaryLinker) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 	// TODO(ccross): verify shared library dependencies
-	if len(p.properties.Srcs) > 0 {
+	if len(p.properties.Srcs.GetOrDefault(ctx, nil)) > 0 {
 		fileName := p.getStem(ctx) + flags.Toolchain.ExecutableSuffix()
 		in := p.Prebuilt.SingleSourcePath(ctx)
 		outputFile := android.PathForModuleOut(ctx, fileName)
@@ -500,7 +496,7 @@
 
 	module.AddProperties(&prebuilt.properties)
 
-	android.InitPrebuiltModule(module, &prebuilt.properties.Srcs)
+	android.InitConfigurablePrebuiltModule(module, &prebuilt.properties.Srcs)
 	return module, binary
 }
 
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 86e6af9..acbbabc 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -476,11 +476,7 @@
 			android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 				android.RegisterApexContributionsBuildComponents(ctx)
 			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		)
 		ctx := testPrebuilt(t, fmt.Sprintf(bp, tc.selectedDependencyName), map[string][]byte{
 			"libbar.so": nil,
@@ -574,11 +570,7 @@
 			android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 				android.RegisterApexContributionsBuildComponents(ctx)
 			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		)
 		if tc.expectedErr != "" {
 			preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedErr))
@@ -638,11 +630,7 @@
 		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 			android.RegisterApexContributionsBuildComponents(ctx)
 		}),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 	)
 	ctx := testPrebuilt(t, bp, map[string][]byte{
 		"libbar.so": nil,
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 64a313b..7b0652c 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -79,8 +79,6 @@
 
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
 		"-fno-sanitize-recover=integer,undefined"}
-	hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
-		"export_memory_stats=0", "max_malloc_fill_size=131072", "malloc_fill_byte=0"}
 	memtagStackCommonFlags = []string{"-march=armv8-a+memtag"}
 	memtagStackLlvmFlags   = []string{"-dom-tree-reachability-max-bbs-to-explore=128"}
 
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 44f38e1..80facd8 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -47,7 +47,7 @@
 `))
 
 type providerInterface interface {
-	android.SingletonModuleProviderContext
+	android.OtherModuleProviderContext
 }
 
 // expectSharedLinkDep verifies that the from module links against the to module as a
@@ -55,7 +55,7 @@
 func expectSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)
+	toInfo, _ := android.OtherModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)
 
 	if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); !android.InList(w, g) {
 		t.Errorf("%s should link against %s, expected %q, got %q",
@@ -68,7 +68,7 @@
 func expectNoSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)
+	toInfo, _ := android.OtherModuleProvider(ctx, to.Module(), SharedLibraryInfoProvider)
 
 	if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); android.InList(w, g) {
 		t.Errorf("%s should not link against %s, expected %q, got %q",
@@ -81,7 +81,7 @@
 func expectStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)
+	toInfo, _ := android.OtherModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)
 
 	if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); !android.InList(w, g) {
 		t.Errorf("%s should link against %s, expected %q, got %q",
@@ -95,7 +95,7 @@
 func expectNoStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
 	t.Helper()
 	fromLink := from.Description("link")
-	toInfo, _ := android.SingletonModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)
+	toInfo, _ := android.OtherModuleProvider(ctx, to.Module(), StaticLibraryInfoProvider)
 
 	if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); android.InList(w, g) {
 		t.Errorf("%s should not link against %s, expected %q, got %q",
diff --git a/cc/stl.go b/cc/stl.go
index de2066f..8c4ef0b 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -177,7 +177,7 @@
 		} else {
 			deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl, "ndk_libc++abi")
 		}
-		deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
+		deps.StaticLibs = append(deps.StaticLibs, "libunwind")
 	default:
 		panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
 	}
diff --git a/cc/stub_library.go b/cc/stub_library.go
index 47c6cb9..f15a604 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -39,8 +39,9 @@
 }
 
 // Get target file name to be installed from this module
-func getInstalledFileName(m *Module) string {
-	for _, ps := range m.PackagingSpecs() {
+func getInstalledFileName(ctx android.SingletonContext, m *Module) string {
+	for _, ps := range android.OtherModuleProviderOrDefault(
+		ctx, m.Module(), android.InstallFilesProvider).PackagingSpecs {
 		if name := ps.FileName(); name != "" {
 			return name
 		}
@@ -53,7 +54,7 @@
 	ctx.VisitAllModules(func(module android.Module) {
 		if m, ok := module.(*Module); ok {
 			if IsStubTarget(m) {
-				if name := getInstalledFileName(m); name != "" {
+				if name := getInstalledFileName(ctx, m); name != "" {
 					s.stubLibraryMap[name] = true
 					if m.InVendor() {
 						s.stubVendorLibraryMap[name] = true
diff --git a/cc/testing.go b/cc/testing.go
index 02f9924..ed567af 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -38,8 +38,6 @@
 	ctx.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory)
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("cc_genrule", GenRuleFactory)
-	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
-	ctx.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
 	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
 	ctx.RegisterModuleType("ndk_headers", NdkHeadersFactory)
 }
@@ -312,6 +310,25 @@
 			],
 		}
 		cc_library {
+			name: "ndk_libc++_shared",
+			export_include_dirs: ["ndk_libc++_shared_include_dirs"],
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			product_available: true,
+			recovery_available: true,
+			host_supported: false,
+			sdk_version: "minimum",
+			double_loadable: true,
+			apex_available: [
+				"//apex_available:platform",
+				"//apex_available:anyapex",
+			],
+		}
+		cc_library {
 			name: "libc++demangle",
 			no_libcrt: true,
 			nocrt: true,
@@ -397,13 +414,6 @@
 			name: "libprotobuf-cpp-lite",
 		}
 
-		cc_library {
-			name: "ndk_libunwind",
-			sdk_version: "minimum",
-			stl: "none",
-			system_shared_libs: [],
-		}
-
 		ndk_library {
 			name: "libc",
 			first_version: "minimum",
@@ -422,11 +432,6 @@
 			symbol_file: "libdl.map.txt",
 		}
 
-		ndk_prebuilt_shared_stl {
-			name: "ndk_libc++_shared",
-			export_include_dirs: ["ndk_libc++_shared"],
-		}
-
 		cc_library_static {
 			name: "libgoogle-benchmark",
 			sdk_version: "current",
@@ -557,13 +562,6 @@
 
 		RegisterLlndkLibraryTxtType(ctx)
 	}),
-
-	// Additional files needed in tests that disallow non-existent source files.
-	// This includes files that are needed by all, or at least most, instances of a cc module type.
-	android.MockFS{
-		// Needed for ndk_prebuilt_(shared|static)_stl.
-		"defaults/cc/common/current/sources/cxx-stl/llvm-libc++/libs": nil,
-	}.AddToFixture(),
 )
 
 // Preparer that will define default cc modules, e.g. standard prebuilt modules.
@@ -572,17 +570,17 @@
 
 	// Additional files needed in tests that disallow non-existent source.
 	android.MockFS{
-		"defaults/cc/common/libc.map.txt":                nil,
-		"defaults/cc/common/libdl.map.txt":               nil,
-		"defaults/cc/common/libft2.map.txt":              nil,
-		"defaults/cc/common/libm.map.txt":                nil,
-		"defaults/cc/common/ndk_libc++_shared":           nil,
-		"defaults/cc/common/crtbegin_so.c":               nil,
-		"defaults/cc/common/crtbegin.c":                  nil,
-		"defaults/cc/common/crtend_so.c":                 nil,
-		"defaults/cc/common/crtend.c":                    nil,
-		"defaults/cc/common/crtbrand.c":                  nil,
-		"external/compiler-rt/lib/cfi/cfi_blocklist.txt": nil,
+		"defaults/cc/common/libc.map.txt":                   nil,
+		"defaults/cc/common/libdl.map.txt":                  nil,
+		"defaults/cc/common/libft2.map.txt":                 nil,
+		"defaults/cc/common/libm.map.txt":                   nil,
+		"defaults/cc/common/ndk_libc++_shared_include_dirs": nil,
+		"defaults/cc/common/crtbegin_so.c":                  nil,
+		"defaults/cc/common/crtbegin.c":                     nil,
+		"defaults/cc/common/crtend_so.c":                    nil,
+		"defaults/cc/common/crtend.c":                       nil,
+		"defaults/cc/common/crtbrand.c":                     nil,
+		"external/compiler-rt/lib/cfi/cfi_blocklist.txt":    nil,
 
 		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm64.a": nil,
 		"defaults/cc/common/libclang_rt.ubsan_minimal.android_arm.a":   nil,
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 2dc8c21..8c0d111 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"os"
-	"path/filepath"
 	"testing"
 
 	"android/soong/android"
@@ -147,8 +146,8 @@
 
 	output := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img")
 
-	stampFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/staging_dir.stamp")
-	fileListFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/file_list.txt")
+	stampFile := "out/target/product/test_device/obj/PACKAGING/system_intermediates/staging_dir.stamp"
+	fileListFile := "out/target/product/test_device/obj/PACKAGING/system_intermediates/file_list.txt"
 	android.AssertStringListContains(t, "deps of filesystem must include the staging dir stamp file", output.Implicits.Strings(), stampFile)
 	android.AssertStringListContains(t, "deps of filesystem must include the staging dir file list", output.Implicits.Strings(), fileListFile)
 }
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 69d922d..a8fd368 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -61,7 +61,8 @@
 
 	deps := s.gatherFilteredPackagingSpecs(ctx)
 	ctx.WalkDeps(func(child, parent android.Module) bool {
-		for _, ps := range child.PackagingSpecs() {
+		for _, ps := range android.OtherModuleProviderOrDefault(
+			ctx, child, android.InstallFilesProvider).PackagingSpecs {
 			if _, ok := deps[ps.RelPathInPackage()]; ok {
 				modulesInPackageByModule[child] = true
 				modulesInPackageByName[child.Name()] = true
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 2557922..9195643 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -147,6 +147,21 @@
 
 	// Enable restat to update the output only if the output is changed
 	Write_if_changed *bool
+
+	// When set to true, an additional $(build_number_file) label will be available
+	// to use in the cmd. This will be the location of a text file containing the
+	// build number. The dependency on this file will be "order-only", meaning that
+	// the genrule will not rerun when only this file changes, to avoid rerunning
+	// the genrule every build, because the build number changes every build.
+	// This also means that you should not attempt to consume the build number from
+	// the result of this genrule in another build rule. If you do, the build number
+	// in the second build rule will be stale when the second build rule rebuilds
+	// but this genrule does not. Only certain allowlisted modules are allowed to
+	// use this property, usages of the build number should be kept to the absolute
+	// minimum. Particularly no modules on the system image may include the build
+	// number. Prefer using libbuildversion via the use_version_lib property on
+	// cc modules.
+	Uses_order_only_build_number_file *bool
 }
 
 type Module struct {
@@ -228,6 +243,15 @@
 	}
 }
 
+// This allowlist should be kept to the bare minimum, it's
+// intended for things that existed before the build number
+// was tightly controlled. Prefer using libbuildversion
+// via the use_version_lib property of cc modules.
+var genrule_build_number_allowlist = map[string]bool{
+	"build/soong/tests:gen":                   true,
+	"tools/tradefederation/core:tradefed_zip": true,
+}
+
 // generateCommonBuildActions contains build action generation logic
 // common to both the mixed build case and the legacy case of genrule processing.
 // To fully support genrule in mixed builds, the contents of this function should
@@ -470,6 +494,11 @@
 				return strings.Join(proptools.ShellEscapeList(sandboxOuts), " "), nil
 			case "genDir":
 				return proptools.ShellEscape(cmd.PathForOutput(task.genDir)), nil
+			case "build_number_file":
+				if !proptools.Bool(g.properties.Uses_order_only_build_number_file) {
+					return reportError("to use the $(build_number_file) label, you must set uses_order_only_build_number_file: true")
+				}
+				return proptools.ShellEscape(cmd.PathForInput(ctx.Config().BuildNumberFile(ctx))), nil
 			default:
 				if strings.HasPrefix(name, "location ") {
 					label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@@ -516,6 +545,12 @@
 		cmd.Implicits(task.in)
 		cmd.ImplicitTools(tools)
 		cmd.ImplicitPackagedTools(packagedTools)
+		if proptools.Bool(g.properties.Uses_order_only_build_number_file) {
+			if _, ok := genrule_build_number_allowlist[ctx.ModuleDir()+":"+ctx.ModuleName()]; !ok {
+				ctx.ModuleErrorf("Only allowlisted modules may use uses_order_only_build_number_file: true")
+			}
+			cmd.OrderOnly(ctx.Config().BuildNumberFile(ctx))
+		}
 
 		// Create the rule to run the genrule command inside sbox.
 		rule.Build(name, desc)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 444aedb..9278f15 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -19,6 +19,7 @@
 	"os"
 	"regexp"
 	"strconv"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -1192,6 +1193,68 @@
 	}
 }
 
+func TestGenruleUsesOrderOnlyBuildNumberFile(t *testing.T) {
+	testCases := []struct {
+		name            string
+		bp              string
+		fs              android.MockFS
+		expectedError   string
+		expectedCommand string
+	}{
+		{
+			name: "not allowed when not in allowlist",
+			fs: android.MockFS{
+				"foo/Android.bp": []byte(`
+genrule {
+	name: "gen",
+	uses_order_only_build_number_file: true,
+	cmd: "cp $(build_number_file) $(out)",
+	out: ["out.txt"],
+}
+`),
+			},
+			expectedError: `Only allowlisted modules may use uses_order_only_build_number_file: true`,
+		},
+		{
+			name: "normal",
+			fs: android.MockFS{
+				"build/soong/tests/Android.bp": []byte(`
+genrule {
+	name: "gen",
+	uses_order_only_build_number_file: true,
+	cmd: "cp $(build_number_file) $(out)",
+	out: ["out.txt"],
+}
+`),
+			},
+			expectedCommand: `cp BUILD_NUMBER_FILE __SBOX_SANDBOX_DIR__/out/out.txt`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			fixtures := android.GroupFixturePreparers(
+				prepareForGenRuleTest,
+				android.PrepareForTestWithVisibility,
+				android.FixtureMergeMockFs(tc.fs),
+				android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) {
+					config.TestProductVariables.BuildNumberFile = proptools.StringPtr("build_number.txt")
+				}),
+			)
+			if tc.expectedError != "" {
+				fixtures = fixtures.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(tc.expectedError))
+			}
+			result := fixtures.RunTest(t)
+
+			if tc.expectedError == "" {
+				tc.expectedCommand = strings.ReplaceAll(tc.expectedCommand, "BUILD_NUMBER_FILE", result.Config.SoongOutDir()+"/build_number.txt")
+				gen := result.Module("gen", "").(*Module)
+				android.AssertStringEquals(t, "raw commands", tc.expectedCommand, gen.rawCommands[0])
+			}
+		})
+	}
+}
+
 type testTool struct {
 	android.ModuleBase
 	outputFile android.Path
diff --git a/java/aar.go b/java/aar.go
index b69b7c2..e6ad502 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -76,7 +76,7 @@
 	// list of directories relative to the Blueprints file containing
 	// Android resources.  Defaults to ["res"] if a directory called res exists.
 	// Set to [] to disable the default.
-	Resource_dirs []string
+	Resource_dirs []string `android:"path"`
 
 	// list of zip files containing Android resources.
 	Resource_zips []string `android:"path"`
@@ -166,7 +166,11 @@
 func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool {
 	return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault()) &&
 		// TODO(b/331641946): remove this when ResourceProcessorBusyBox supports generating shared libraries.
-		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib")
+		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib") &&
+		// Use the legacy resource processor in kythe builds.
+		// The legacy resource processor creates an R.srcjar, which kythe can use for generating crossrefs.
+		// TODO(b/354854007): Re-enable BusyBox in kythe builds
+		!ctx.Config().EmitXrefRules()
 }
 
 func (a *aapt) filterProduct() string {
@@ -991,17 +995,17 @@
 
 	properties AARImportProperties
 
-	headerJarFile                      android.WritablePath
-	implementationJarFile              android.WritablePath
-	implementationAndResourcesJarFile  android.WritablePath
-	proguardFlags                      android.WritablePath
-	exportPackage                      android.WritablePath
+	headerJarFile                      android.Path
+	implementationJarFile              android.Path
+	implementationAndResourcesJarFile  android.Path
+	proguardFlags                      android.Path
+	exportPackage                      android.Path
 	transitiveAaptResourcePackagesFile android.Path
-	extraAaptPackagesFile              android.WritablePath
+	extraAaptPackagesFile              android.Path
 	manifest                           android.Path
-	assetsPackage                      android.WritablePath
-	rTxt                               android.WritablePath
-	rJar                               android.WritablePath
+	assetsPackage                      android.Path
+	rTxt                               android.Path
+	rJar                               android.Path
 
 	resourcesNodesDepSet *android.DepSet[*resourcesNode]
 	manifestsDepSet      *android.DepSet[android.Path]
@@ -1150,8 +1154,9 @@
 
 	if Bool(a.properties.Jetifier) {
 		inputFile := a.aarPath
-		a.aarPath = android.PathForModuleOut(ctx, "jetifier", aarName)
-		TransformJetifier(ctx, a.aarPath.(android.WritablePath), inputFile)
+		jetifierPath := android.PathForModuleOut(ctx, "jetifier", aarName)
+		TransformJetifier(ctx, jetifierPath, inputFile)
+		a.aarPath = jetifierPath
 	}
 
 	jarName := ctx.ModuleName() + ".jar"
@@ -1166,14 +1171,14 @@
 		a.manifest = extractedManifest
 	}
 
-	a.rTxt = extractedAARDir.Join(ctx, "R.txt")
-	a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip")
-	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
+	rTxt := extractedAARDir.Join(ctx, "R.txt")
+	assetsPackage := android.PathForModuleOut(ctx, "assets.zip")
+	proguardFlags := extractedAARDir.Join(ctx, "proguard.txt")
 	transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx)
 	android.SetProvider(ctx, ProguardSpecInfoProvider, ProguardSpecInfo{
 		ProguardFlagsFiles: android.NewDepSet[android.Path](
 			android.POSTORDER,
-			android.Paths{a.proguardFlags},
+			android.Paths{proguardFlags},
 			transitiveProguardFlags,
 		),
 		UnconditionallyExportedProguardFlags: android.NewDepSet[android.Path](
@@ -1186,15 +1191,19 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        unzipAAR,
 		Input:       a.aarPath,
-		Outputs:     android.WritablePaths{classpathFile, a.proguardFlags, extractedManifest, a.assetsPackage, a.rTxt},
+		Outputs:     android.WritablePaths{classpathFile, proguardFlags, extractedManifest, assetsPackage, rTxt},
 		Description: "unzip AAR",
 		Args: map[string]string{
 			"outDir":             extractedAARDir.String(),
 			"combinedClassesJar": classpathFile.String(),
-			"assetsPackage":      a.assetsPackage.String(),
+			"assetsPackage":      assetsPackage.String(),
 		},
 	})
 
+	a.proguardFlags = proguardFlags
+	a.assetsPackage = assetsPackage
+	a.rTxt = rTxt
+
 	// Always set --pseudo-localize, it will be stripped out later for release
 	// builds that don't want it.
 	compileFlags := []string{"--pseudo-localize"}
@@ -1202,10 +1211,10 @@
 	flata := compiledResDir.Join(ctx, "gen_res.flata")
 	aapt2CompileZip(ctx, flata, a.aarPath, "res", compileFlags)
 
-	a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk")
+	exportPackage := android.PathForModuleOut(ctx, "package-res.apk")
 	proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
 	aaptRTxt := android.PathForModuleOut(ctx, "R.txt")
-	a.extraAaptPackagesFile = android.PathForModuleOut(ctx, "extra_packages")
+	extraAaptPackagesFile := android.PathForModuleOut(ctx, "extra_packages")
 
 	var linkDeps android.Paths
 
@@ -1241,13 +1250,16 @@
 	}
 
 	transitiveAssets := android.ReverseSliceInPlace(staticDeps.assets())
-	aapt2Link(ctx, a.exportPackage, nil, proguardOptionsFile, aaptRTxt,
+	aapt2Link(ctx, exportPackage, nil, proguardOptionsFile, aaptRTxt,
 		linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil, nil)
+	a.exportPackage = exportPackage
 
-	a.rJar = android.PathForModuleOut(ctx, "busybox/R.jar")
-	resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true, nil, false)
+	rJar := android.PathForModuleOut(ctx, "busybox/R.jar")
+	resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, rJar, nil, true, nil, false)
+	a.rJar = rJar
 
-	aapt2ExtractExtraPackages(ctx, a.extraAaptPackagesFile, a.rJar)
+	aapt2ExtractExtraPackages(ctx, extraAaptPackagesFile, a.rJar)
+	a.extraAaptPackagesFile = extraAaptPackagesFile
 
 	resourcesNodesDepSetBuilder := android.NewDepSetBuilder[*resourcesNode](android.TOPOLOGICAL)
 	resourcesNodesDepSetBuilder.Direct(&resourcesNode{
@@ -1295,11 +1307,12 @@
 		addMissingOptionalUsesLibsFromDep(ctx, module, &a.usesLibrary)
 	})
 
-	var implementationJarFile android.OutputPath
+	var implementationJarFile android.Path
 	if len(staticJars) > 0 {
 		combineJars := append(android.Paths{classpathFile}, staticJars...)
-		implementationJarFile = android.PathForModuleOut(ctx, "combined", jarName).OutputPath
-		TransformJarsToJar(ctx, implementationJarFile, "combine", combineJars, android.OptionalPath{}, false, nil, nil)
+		combinedImplementationJar := android.PathForModuleOut(ctx, "combined", jarName).OutputPath
+		TransformJarsToJar(ctx, combinedImplementationJar, "combine", combineJars, android.OptionalPath{}, false, nil, nil)
+		implementationJarFile = combinedImplementationJar
 	} else {
 		implementationJarFile = classpathFile
 	}
@@ -1318,7 +1331,7 @@
 	implementationAndResourcesJar := implementationJarFile
 	if resourceJarFile != nil {
 		jars := android.Paths{resourceJarFile, implementationAndResourcesJar}
-		combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath
+		combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
 		TransformJarsToJar(ctx, combinedJar, "for resources", jars, android.OptionalPath{},
 			false, nil, nil)
 		implementationAndResourcesJar = combinedJar
@@ -1330,13 +1343,14 @@
 
 	if len(staticHeaderJars) > 0 {
 		combineJars := append(android.Paths{classpathFile}, staticHeaderJars...)
-		a.headerJarFile = android.PathForModuleOut(ctx, "turbine-combined", jarName)
-		TransformJarsToJar(ctx, a.headerJarFile, "combine header jars", combineJars, android.OptionalPath{}, false, nil, nil)
+		headerJarFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
+		TransformJarsToJar(ctx, headerJarFile, "combine header jars", combineJars, android.OptionalPath{}, false, nil, nil)
+		a.headerJarFile = headerJarFile
 	} else {
 		a.headerJarFile = classpathFile
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(a.headerJarFile),
 		ResourceJars:                   android.PathsIfNonNil(resourceJarFile),
 		TransitiveLibsHeaderJars:       a.transitiveLibsHeaderJars,
@@ -1436,3 +1450,7 @@
 	InitJavaModuleMultiTargets(module, android.DeviceSupported)
 	return module
 }
+
+func (a *AARImport) IDEInfo(dpInfo *android.IdeInfo) {
+	dpInfo.Jars = append(dpInfo.Jars, a.headerJarFile.String(), a.rJar.String())
+}
diff --git a/java/aar_test.go b/java/aar_test.go
index ebad310..877e2c7 100644
--- a/java/aar_test.go
+++ b/java/aar_test.go
@@ -53,7 +53,7 @@
 			appMod := ctx.Module(tc.name, "android_common")
 			appTestMod := ctx.ModuleForTests(tc.name, "android_common")
 
-			info, ok := android.SingletonModuleProvider(ctx, appMod, JniPackageProvider)
+			info, ok := android.OtherModuleProvider(ctx, appMod, JniPackageProvider)
 			if !ok {
 				t.Errorf("expected android_library_import to have JniPackageProvider")
 			}
diff --git a/java/app.go b/java/app.go
index 19dc8d5..1ebf658 100644
--- a/java/app.go
+++ b/java/app.go
@@ -1109,7 +1109,7 @@
 						coverageFile:   dep.CoverageOutputFile(),
 						unstrippedFile: dep.UnstrippedOutputFile(),
 						partition:      dep.Partition(),
-						installPaths:   dep.FilesToInstall(),
+						installPaths:   android.OtherModuleProviderOrDefault(ctx, dep, android.InstallFilesProvider).InstallFiles,
 					})
 				} else if ctx.Config().AllowMissingDependencies() {
 					ctx.AddMissingDependencies([]string{otherName})
diff --git a/java/app_set.go b/java/app_set.go
index 33d3ade..7997570 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -35,7 +35,7 @@
 
 type AndroidAppSetProperties struct {
 	// APK Set path
-	Set *string
+	Set *string `android:"path"`
 
 	// Specifies that this app should be installed to the priv-app directory,
 	// where the system will grant it additional privileges not available to
diff --git a/java/app_set_test.go b/java/app_set_test.go
index 10bc5de..c02b359 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -56,7 +56,7 @@
 	mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0]
 	actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
 	expectedInstallFile := []string{
-		strings.Replace(params.ImplicitOutputs[0].String(), android.OutSoongDir, result.Config.SoongOutDir(), 1),
+		strings.Replace(params.ImplicitOutputs[0].String(), android.TestOutSoongDir, result.Config.SoongOutDir(), 1),
 	}
 	if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
 		t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',",
diff --git a/java/base.go b/java/base.go
index 02df147..4ab82c5 100644
--- a/java/base.go
+++ b/java/base.go
@@ -222,6 +222,13 @@
 	// the stubs via libs, but should be set to true when the module depends on
 	// the stubs via static libs.
 	Is_stubs_module *bool
+
+	// If true, enable the "Ravenizer" tool on the output jar.
+	// "Ravenizer" is a tool for Ravenwood tests, but it can also be enabled on other kinds
+	// of java targets.
+	Ravenizer struct {
+		Enabled *bool
+	}
 }
 
 // Properties that are specific to device modules. Host module factories should not add these when
@@ -558,13 +565,23 @@
 	// List of soong module dependencies required to compile the current module.
 	// This information is printed out to `Dependencies` field in module_bp_java_deps.json
 	compileDepNames []string
+
+	ravenizer struct {
+		enabled bool
+	}
 }
 
 var _ android.InstallableModule = (*Module)(nil)
 
 // To satisfy the InstallableModule interface
-func (j *Module) EnforceApiContainerChecks() bool {
-	return true
+func (j *Module) StaticDependencyTags() []blueprint.DependencyTag {
+	return []blueprint.DependencyTag{staticLibTag}
+}
+
+// To satisfy the InstallableModule interface
+func (j *Module) DynamicDependencyTags() []blueprint.DependencyTag {
+	return []blueprint.DependencyTag{libTag, sdkLibTag, bootClasspathTag, systemModulesTag,
+		instrumentationForTag, java9LibTag}
 }
 
 // Overrides android.ModuleBase.InstallInProduct()
@@ -875,9 +892,12 @@
 	if j.hasSrcExt(".kt") {
 		// TODO(ccross): move this to a mutator pass that can tell if generated sources contain
 		// Kotlin files
-		ctx.AddVariationDependencies(nil, kotlinStdlibTag,
-			"kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8")
-		ctx.AddVariationDependencies(nil, kotlinAnnotationsTag, "kotlin-annotations")
+		tag := staticLibTag
+		if !BoolDefault(j.properties.Static_kotlin_stdlib, true) {
+			tag = libTag
+		}
+		ctx.AddVariationDependencies(nil, tag,
+			"kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-annotations")
 	}
 
 	// Framework libraries need special handling in static coverage builds: they should not have
@@ -1113,7 +1133,6 @@
 }
 
 func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars android.Paths) {
-
 	// Auto-propagating jarjar rules
 	jarjarProviderData := j.collectJarJarRules(ctx)
 	if jarjarProviderData != nil {
@@ -1130,6 +1149,10 @@
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
+	if re := proptools.Bool(j.properties.Ravenizer.Enabled); re {
+		j.ravenizer.enabled = re
+	}
+
 	deps := j.collectDeps(ctx)
 	flags := j.collectBuilderFlags(ctx, deps)
 
@@ -1228,14 +1251,17 @@
 			ctx.ModuleErrorf("headers_only is enabled but Turbine is disabled.")
 		}
 
-		_, j.headerJarFile, _ =
-			j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName,
-				extraCombinedJars)
+		_, combinedHeaderJarFile := j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName,
+			extraCombinedJars)
+
+		combinedHeaderJarFile = j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine")
+		combinedHeaderJarFile = j.repackageFlagsIfNecessary(ctx, combinedHeaderJarFile, jarName, "repackage-turbine")
 		if ctx.Failed() {
 			return
 		}
+		j.headerJarFile = combinedHeaderJarFile
 
-		android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
+		android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
 			HeaderJars:                          android.PathsIfNonNil(j.headerJarFile),
 			TransitiveLibsHeaderJars:            j.transitiveLibsHeaderJars,
 			TransitiveStaticLibsHeaderJars:      j.transitiveStaticLibsHeaderJars,
@@ -1286,9 +1312,6 @@
 		// Collect common .kt files for AIDEGen
 		j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, kotlinCommonSrcFiles.Strings()...)
 
-		flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
-		flags.classpath = append(flags.classpath, deps.kotlinAnnotations...)
-
 		flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...)
 		flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...)
 
@@ -1311,24 +1334,13 @@
 			return
 		}
 
-		kotlinJarPath := j.repackageFlagsIfNecessary(ctx, kotlinJar.OutputPath, jarName, "kotlinc")
+		kotlinJarPath := j.repackageFlagsIfNecessary(ctx, kotlinJar, jarName, "kotlinc")
 
 		// Make javac rule depend on the kotlinc rule
 		flags.classpath = append(classpath{kotlinHeaderJar}, flags.classpath...)
 
 		kotlinJars = append(kotlinJars, kotlinJarPath)
 		kotlinHeaderJars = append(kotlinHeaderJars, kotlinHeaderJar)
-
-		// Jar kotlin classes into the final jar after javac
-		if BoolDefault(j.properties.Static_kotlin_stdlib, true) {
-			kotlinJars = append(kotlinJars, deps.kotlinStdlib...)
-			kotlinJars = append(kotlinJars, deps.kotlinAnnotations...)
-			kotlinHeaderJars = append(kotlinHeaderJars, deps.kotlinStdlib...)
-			kotlinHeaderJars = append(kotlinHeaderJars, deps.kotlinAnnotations...)
-		} else {
-			flags.dexClasspath = append(flags.dexClasspath, deps.kotlinStdlib...)
-			flags.dexClasspath = append(flags.dexClasspath, deps.kotlinAnnotations...)
-		}
 	}
 
 	jars := slices.Clone(kotlinJars)
@@ -1346,12 +1358,14 @@
 			// allow for the use of annotation processors that do function correctly
 			// with sharding enabled. See: b/77284273.
 		}
-		extraJars := append(slices.Clone(kotlinHeaderJars), extraCombinedJars...)
-		headerJarFileWithoutDepsOrJarjar, j.headerJarFile, j.repackagedHeaderJarFile =
+		extraJars := slices.Clone(kotlinHeaderJars)
+		extraJars = append(extraJars, extraCombinedJars...)
+		var combinedHeaderJarFile android.Path
+		headerJarFileWithoutDepsOrJarjar, combinedHeaderJarFile =
 			j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, extraJars)
-		if ctx.Failed() {
-			return
-		}
+
+		j.headerJarFile = j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine")
+		j.repackagedHeaderJarFile = j.repackageFlagsIfNecessary(ctx, j.headerJarFile, jarName, "turbine")
 	}
 	if len(uniqueJavaFiles) > 0 || len(srcJars) > 0 {
 		hasErrorproneableFiles := false
@@ -1513,7 +1527,7 @@
 
 	// Combine the classes built from sources, any manifests, and any static libraries into
 	// classes.jar. If there is only one input jar this step will be skipped.
-	var outputFile android.OutputPath
+	var outputFile android.Path
 
 	if len(jars) == 1 && !manifest.Valid() {
 		// Optimization: skip the combine step as there is nothing to do
@@ -1529,49 +1543,48 @@
 		// to the copy rules.
 		stub, _ := moduleStubLinkType(ctx.ModuleName())
 
-		// Transform the single path to the jar into an OutputPath as that is required by the following
-		// code.
-		if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok && !stub {
-			// The path contains an embedded OutputPath so reuse that.
-			outputFile = moduleOutPath.OutputPath
-		} else if outputPath, ok := jars[0].(android.OutputPath); ok && !stub {
-			// The path is an OutputPath so reuse it directly.
-			outputFile = outputPath
-		} else {
-			// The file is not in the out directory so create an OutputPath into which it can be copied
-			// and which the following code can use to refer to it.
+		if stub {
 			combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   android.Cp,
 				Input:  jars[0],
 				Output: combinedJar,
 			})
-			outputFile = combinedJar.OutputPath
+			outputFile = combinedJar
+		} else {
+			outputFile = jars[0]
 		}
 	} else {
 		combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
 		TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest,
 			false, nil, nil)
-		outputFile = combinedJar.OutputPath
+		outputFile = combinedJar
 	}
 
 	// jarjar implementation jar if necessary
-	if j.expandJarjarRules != nil {
-		// Transform classes.jar into classes-jarjar.jar
-		jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName).OutputPath
-		TransformJarJar(ctx, jarjarFile, outputFile, j.expandJarjarRules)
-		outputFile = jarjarFile
+	jarjarFile := j.jarjarIfNecessary(ctx, outputFile, jarName, "")
+	outputFile = jarjarFile
 
-		// jarjar resource jar if necessary
-		if j.resourceJar != nil {
-			resourceJarJarFile := android.PathForModuleOut(ctx, "res-jarjar", jarName)
-			TransformJarJar(ctx, resourceJarJarFile, j.resourceJar, j.expandJarjarRules)
-			j.resourceJar = resourceJarJarFile
-		}
+	// jarjar resource jar if necessary
+	if j.resourceJar != nil {
+		resourceJarJarFile := j.jarjarIfNecessary(ctx, j.resourceJar, jarName, "resource")
+		j.resourceJar = resourceJarJarFile
+	}
 
-		if ctx.Failed() {
-			return
-		}
+	if ctx.Failed() {
+		return
+	}
+
+	if j.ravenizer.enabled {
+		ravenizerInput := outputFile
+		ravenizerOutput := android.PathForModuleOut(ctx, "ravenizer", jarName)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        ravenizer,
+			Description: "ravenizer",
+			Input:       ravenizerInput,
+			Output:      ravenizerOutput,
+		})
+		outputFile = ravenizerOutput
 	}
 
 	// Check package restrictions if necessary.
@@ -1583,15 +1596,16 @@
 		// will check that the jar only contains the permitted packages. The new location will become
 		// the output file of this module.
 		inputFile := outputFile
-		outputFile = android.PathForModuleOut(ctx, "package-check", jarName).OutputPath
+		packageCheckOutputFile := android.PathForModuleOut(ctx, "package-check", jarName)
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.Cp,
 			Input:  inputFile,
-			Output: outputFile,
+			Output: packageCheckOutputFile,
 			// Make sure that any dependency on the output file will cause ninja to run the package check
 			// rule.
 			Validation: pkgckFile,
 		})
+		outputFile = packageCheckOutputFile
 
 		// Check packages and create a timestamp file when complete.
 		CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages)
@@ -1626,7 +1640,7 @@
 	implementationAndResourcesJar := outputFile
 	if j.resourceJar != nil {
 		jars := android.Paths{j.resourceJar, implementationAndResourcesJar}
-		combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath
+		combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
 		TransformJarsToJar(ctx, combinedJar, "for resources", jars, manifest,
 			false, nil, nil)
 		implementationAndResourcesJar = combinedJar
@@ -1653,7 +1667,7 @@
 					android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
 			}
 			// Dex compilation
-			var dexOutputFile android.OutputPath
+			var dexOutputFile android.Path
 			params := &compileDexParams{
 				flags:         flags,
 				sdkVersion:    j.SdkVersion(ctx),
@@ -1680,17 +1694,17 @@
 
 			// If r8/d8 provides a profile that matches the optimized dex, use that for dexpreopt.
 			if dexArtProfileOutput != nil {
-				j.dexpreopter.SetRewrittenProfile(*dexArtProfileOutput)
+				j.dexpreopter.SetRewrittenProfile(dexArtProfileOutput)
 			}
 
 			// merge dex jar with resources if necessary
 			if j.resourceJar != nil {
 				jars := android.Paths{dexOutputFile, j.resourceJar}
-				combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath
+				combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
 				TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
 					false, nil, nil)
 				if *j.dexProperties.Uncompress_dex {
-					combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath
+					combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName)
 					TransformZipAlign(ctx, combinedAlignedJar, combinedJar, nil)
 					dexOutputFile = combinedAlignedJar
 				} else {
@@ -1760,7 +1774,7 @@
 
 	ctx.CheckbuildFile(outputFile)
 
-	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
 		HeaderJars:                          android.PathsIfNonNil(j.headerJarFile),
 		RepackagedHeaderJars:                android.PathsIfNonNil(j.repackagedHeaderJarFile),
 		TransitiveLibsHeaderJars:            j.transitiveLibsHeaderJars,
@@ -1850,7 +1864,7 @@
 }
 
 func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int,
-	srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath {
+	srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.Path {
 
 	kzipName := pathtools.ReplaceExtension(jarName, "kzip")
 	annoSrcJar := android.PathForModuleOut(ctx, "javac", "anno.srcjar")
@@ -1860,7 +1874,7 @@
 		jarName += strconv.Itoa(idx)
 	}
 
-	classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath
+	classes := android.PathForModuleOut(ctx, "javac", jarName)
 	TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, annoSrcJar, flags, extraJarDeps)
 
 	if ctx.Config().EmitXrefRules() && ctx.Module() == ctx.PrimaryModule() {
@@ -1903,16 +1917,13 @@
 
 func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths,
 	deps deps, flags javaBuilderFlags, jarName string,
-	extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar android.Path) {
+	extraJars android.Paths) (headerJar android.Path, combinedHeaderJar android.Path) {
 
 	var jars android.Paths
 	if len(srcFiles) > 0 || len(srcJars) > 0 {
 		// Compile java sources into turbine.jar.
 		turbineJar := android.PathForModuleOut(ctx, "turbine", jarName)
 		TransformJavaToHeaderClasses(ctx, turbineJar, srcFiles, srcJars, flags)
-		if ctx.Failed() {
-			return nil, nil, nil
-		}
 		jars = append(jars, turbineJar)
 		headerJar = turbineJar
 	}
@@ -1925,40 +1936,18 @@
 
 	// 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
-	combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName)
-	TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{},
+	combinedHeaderJarOutputPath := android.PathForModuleOut(ctx, "turbine-combined", jarName)
+	TransformJarsToJar(ctx, combinedHeaderJarOutputPath, "for turbine", jars, android.OptionalPath{},
 		false, nil, []string{"META-INF/TRANSITIVE"})
-	jarjarAndDepsHeaderJar = combinedJar
 
-	if j.expandJarjarRules != nil {
-		// Transform classes.jar into classes-jarjar.jar
-		jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName)
-		TransformJarJar(ctx, jarjarFile, jarjarAndDepsHeaderJar, j.expandJarjarRules)
-		jarjarAndDepsHeaderJar = jarjarFile
-		if ctx.Failed() {
-			return nil, nil, nil
-		}
-	}
-
-	if j.repackageJarjarRules != nil {
-		repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-turbine-jarjar", jarName)
-		TransformJarJar(ctx, repackagedJarjarFile, jarjarAndDepsHeaderJar, j.repackageJarjarRules)
-		jarjarAndDepsRepackagedHeaderJar = repackagedJarjarFile
-		if ctx.Failed() {
-			return nil, nil, nil
-		}
-	} else {
-		jarjarAndDepsRepackagedHeaderJar = jarjarAndDepsHeaderJar
-	}
-
-	return headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar
+	return headerJar, combinedHeaderJarOutputPath
 }
 
 func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
-	classesJar android.Path, jarName string, specs string) android.OutputPath {
+	classesJar android.Path, jarName string, specs string) android.Path {
 
 	jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco-report-classes", jarName)
-	instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName).OutputPath
+	instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName)
 
 	jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs)
 
@@ -1993,23 +1982,24 @@
 			return
 		}
 
-		dep, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
-		tag := ctx.OtherModuleDependencyTag(module)
-		_, isUsesLibDep := tag.(usesLibraryDependencyTag)
-		if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep {
-			directLibs = append(directLibs, dep.HeaderJars...)
-		} else if tag == staticLibTag {
-			directStaticLibs = append(directStaticLibs, dep.HeaderJars...)
-		} else {
-			// Don't propagate transitive libs for other kinds of dependencies.
-			return
-		}
+		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			tag := ctx.OtherModuleDependencyTag(module)
+			_, isUsesLibDep := tag.(usesLibraryDependencyTag)
+			if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep {
+				directLibs = append(directLibs, dep.HeaderJars...)
+			} else if tag == staticLibTag {
+				directStaticLibs = append(directStaticLibs, dep.HeaderJars...)
+			} else {
+				// Don't propagate transitive libs for other kinds of dependencies.
+				return
+			}
 
-		if dep.TransitiveLibsHeaderJars != nil {
-			transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars)
-		}
-		if dep.TransitiveStaticLibsHeaderJars != nil {
-			transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars)
+			if dep.TransitiveLibsHeaderJars != nil {
+				transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars)
+			}
+			if dep.TransitiveStaticLibsHeaderJars != nil {
+				transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars)
+			}
 		}
 	})
 	j.transitiveLibsHeaderJars = android.NewDepSet(android.POSTORDER, directLibs, transitiveLibs)
@@ -2056,16 +2046,20 @@
 
 // Collect information for opening IDE project files in java/jdeps.go.
 func (j *Module) IDEInfo(dpInfo *android.IdeInfo) {
-	dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...)
-	dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
-	dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...)
-	dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...)
+	// jarjar rules will repackage the sources. To prevent misleading results, IdeInfo should contain the
+	// repackaged jar instead of the input sources.
 	if j.expandJarjarRules != nil {
 		dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
+		dpInfo.Jars = append(dpInfo.Jars, j.headerJarFile.String())
+	} else {
+		dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...)
+		dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...)
+		dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...)
 	}
+	dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...)
+	dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...)
 	dpInfo.Static_libs = append(dpInfo.Static_libs, j.properties.Static_libs...)
 	dpInfo.Libs = append(dpInfo.Libs, j.properties.Libs...)
-	dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...)
 }
 
 func (j *Module) CompilerDeps() []string {
@@ -2115,9 +2109,10 @@
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if tag == staticLibTag {
-			depInfo, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
-			if depInfo.TransitiveSrcFiles != nil {
-				fromDeps = append(fromDeps, depInfo.TransitiveSrcFiles)
+			if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+				if depInfo.TransitiveSrcFiles != nil {
+					fromDeps = append(fromDeps, depInfo.TransitiveSrcFiles)
+				}
 			}
 		}
 	})
@@ -2257,6 +2252,11 @@
 			deps.classpath = append(deps.classpath, sdkDep.jars...)
 			deps.dexClasspath = append(deps.dexClasspath, sdkDep.jars...)
 			deps.aidlPreprocess = sdkDep.aidl
+			// Add the sdk module dependency to `compileDepNames`.
+			// This ensures that the dependency is reported in `module_bp_java_deps.json`
+			// TODO (b/358608607): Move this to decodeSdkDep
+			sdkSpec := android.SdkContext(j).SdkVersion(ctx)
+			j.compileDepNames = append(j.compileDepNames, fmt.Sprintf("sdk_%s_%s_android", sdkSpec.Kind.String(), sdkSpec.ApiLevel.String()))
 		} else {
 			deps.aidlPreprocess = sdkDep.aidl
 		}
@@ -2362,10 +2362,6 @@
 				} else {
 					ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
 				}
-			case kotlinStdlibTag:
-				deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...)
-			case kotlinAnnotationsTag:
-				deps.kotlinAnnotations = dep.HeaderJars
 			case kotlinPluginTag:
 				deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...)
 			case syspropPublicStubDepTag:
@@ -2397,16 +2393,24 @@
 			case bootClasspathTag:
 				// If a system modules dependency has been added to the bootclasspath
 				// then add its libs to the bootclasspath.
-				sm := module.(SystemModulesProvider)
-				deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...)
+				if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok {
+					depHeaderJars := sm.HeaderJars
+					deps.bootClasspath = append(deps.bootClasspath, depHeaderJars...)
+				} else {
+					ctx.PropertyErrorf("boot classpath dependency %q does not provide SystemModulesProvider",
+						ctx.OtherModuleName(module))
+				}
 
 			case systemModulesTag:
 				if deps.systemModules != nil {
 					panic("Found two system module dependencies")
 				}
-				sm := module.(SystemModulesProvider)
-				outputDir, outputDeps := sm.OutputDirAndDeps()
-				deps.systemModules = &systemModules{outputDir, outputDeps}
+				if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok {
+					deps.systemModules = &systemModules{sm.OutputDir, sm.OutputDirDeps}
+				} else {
+					ctx.PropertyErrorf("system modules dependency %q does not provide SystemModulesProvider",
+						ctx.OtherModuleName(module))
+				}
 
 			case instrumentationForTag:
 				ctx.PropertyErrorf("instrumentation_for", "dependency %q of type %q does not provide JavaInfo so is unsuitable for use with this property", ctx.OtherModuleName(module), ctx.OtherModuleType(module))
@@ -2581,8 +2585,6 @@
 					return RenameUseInclude, "tagswitch"
 				case exportedPluginTag:
 					return RenameUseInclude, "tagswitch"
-				case kotlinStdlibTag, kotlinAnnotationsTag:
-					return RenameUseExclude, "tagswitch"
 				case kotlinPluginTag:
 					return RenameUseInclude, "tagswitch"
 				default:
@@ -2733,15 +2735,25 @@
 }
 
 // Repackage the flags if the jarjar rule txt for the flags is generated
-func (j *Module) repackageFlagsIfNecessary(ctx android.ModuleContext, infile android.WritablePath, jarName, info string) android.WritablePath {
+func (j *Module) repackageFlagsIfNecessary(ctx android.ModuleContext, infile android.Path, jarName, info string) android.Path {
 	if j.repackageJarjarRules == nil {
 		return infile
 	}
-	repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", info+jarName)
+	repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", info, jarName)
 	TransformJarJar(ctx, repackagedJarjarFile, infile, j.repackageJarjarRules)
 	return repackagedJarjarFile
 }
 
+func (j *Module) jarjarIfNecessary(ctx android.ModuleContext, infile android.Path, jarName, info string) android.Path {
+	if j.expandJarjarRules == nil {
+		return infile
+	}
+	jarjarFile := android.PathForModuleOut(ctx, "jarjar", info, jarName)
+	TransformJarJar(ctx, jarjarFile, infile, j.expandJarjarRules)
+	return jarjarFile
+
+}
+
 func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) {
 	deps.processorPath = append(deps.processorPath, pluginJars...)
 	deps.processorClasses = append(deps.processorClasses, pluginClasses...)
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 77ddf5c..029f6f6 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -196,7 +196,7 @@
 type BootclasspathNestedAPIProperties struct {
 	// java_library or preferably, java_sdk_library modules providing stub classes that define the
 	// APIs provided by this bootclasspath_fragment.
-	Stub_libs []string
+	Stub_libs proptools.Configurable[[]string]
 }
 
 // BootclasspathAPIProperties defines properties for defining the API provided by parts of the
@@ -229,11 +229,11 @@
 
 // apiScopeToStubLibs calculates the stub library modules for each relevant *HiddenAPIScope from the
 // Stub_libs properties.
-func (p BootclasspathAPIProperties) apiScopeToStubLibs() map[*HiddenAPIScope][]string {
+func (p BootclasspathAPIProperties) apiScopeToStubLibs(ctx android.BaseModuleContext) map[*HiddenAPIScope][]string {
 	m := map[*HiddenAPIScope][]string{}
 	for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
-		m[apiScope] = p.Api.Stub_libs
+		m[apiScope] = p.Api.Stub_libs.GetOrDefault(ctx, nil)
 	}
-	m[CorePlatformHiddenAPIScope] = p.Core_platform_api.Stub_libs
+	m[CorePlatformHiddenAPIScope] = p.Core_platform_api.Stub_libs.GetOrDefault(ctx, nil)
 	return m
 }
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 16209b7..bce507a 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -445,7 +445,7 @@
 func (b *BootclasspathFragmentModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies onto all the modules that provide the API stubs for classes on this
 	// bootclasspath fragment.
-	hiddenAPIAddStubLibDependencies(ctx, b.properties.apiScopeToStubLibs())
+	hiddenAPIAddStubLibDependencies(ctx, b.properties.apiScopeToStubLibs(ctx))
 
 	for _, additionalStubModule := range b.properties.Additional_stubs {
 		for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
@@ -933,8 +933,8 @@
 	b.Filtered_flags_path = android.OptionalPathForPath(hiddenAPIInfo.FilteredFlagsPath)
 
 	// Copy stub_libs properties.
-	b.Stub_libs = module.properties.Api.Stub_libs
-	b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs
+	b.Stub_libs = module.properties.Api.Stub_libs.GetOrDefault(mctx, nil)
+	b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs.GetOrDefault(mctx, nil)
 
 	// Copy fragment properties.
 	b.Fragments = module.properties.Fragments
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 8bc0a7e..60f1a50 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -222,11 +222,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"),
 		FixtureConfigureApexBootJars("someapex:mysdklibrary"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		bootclasspath_fragment {
 			name: "myfragment",
@@ -277,7 +273,7 @@
 	`)
 
 	fragment := result.Module("myfragment", "android_common")
-	info, _ := android.SingletonModuleProvider(result, fragment, HiddenAPIInfoProvider)
+	info, _ := android.OtherModuleProvider(result, fragment, HiddenAPIInfoProvider)
 
 	stubsJar := "out/soong/.intermediates/mystublib/android_common/dex/mystublib.jar"
 
@@ -461,7 +457,7 @@
 
 	// Make sure that the library exports hidden API properties for use by the bootclasspath_fragment.
 	library := result.Module("mynewlibrary", "android_common")
-	info, _ := android.SingletonModuleProvider(result, library, hiddenAPIPropertyInfoProvider)
+	info, _ := android.OtherModuleProvider(result, library, hiddenAPIPropertyInfoProvider)
 	android.AssertArrayString(t, "split packages", []string{"sdklibrary", "newlibrary"}, info.SplitPackages)
 	android.AssertArrayString(t, "package prefixes", []string{"newlibrary.all.mine"}, info.PackagePrefixes)
 	android.AssertArrayString(t, "single packages", []string{"newlibrary.mine"}, info.SinglePackages)
diff --git a/java/builder.go b/java/builder.go
index 5d84d0b..49207e5 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -156,7 +156,7 @@
 	turbine, turbineRE = pctx.RemoteStaticRules("turbine",
 		blueprint.RuleParams{
 			Command: `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} $outputFlags ` +
-				`--sources @$out.rsp  --source_jars $srcJars ` +
+				`--sources @$out.rsp ` +
 				`--javacopts ${config.CommonJdkFlags} ` +
 				`$javacFlags -source $javaVersion -target $javaVersion -- $turbineFlags && ` +
 				`(for o in $outputs; do if cmp -s $${o}.tmp $${o} ; then rm $${o}.tmp ; else mv $${o}.tmp $${o} ; fi; done )`,
@@ -170,13 +170,13 @@
 		},
 		&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
 			ExecStrategy:    "${config.RETurbineExecStrategy}",
-			Inputs:          []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
-			RSPFiles:        []string{"${out}.rsp"},
+			Inputs:          []string{"${config.TurbineJar}", "${out}.rsp", "$rbeInputs"},
+			RSPFiles:        []string{"$out.rsp", "$rspFiles"},
 			OutputFiles:     []string{"$rbeOutputs"},
 			ToolchainInputs: []string{"${config.JavaCmd}"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
-		[]string{"javacFlags", "turbineFlags", "outputFlags", "javaVersion", "outputs", "rbeOutputs", "srcJars"}, []string{"implicits"})
+		[]string{"javacFlags", "turbineFlags", "outputFlags", "javaVersion", "outputs", "rbeOutputs"}, []string{"rbeInputs", "rspFiles"})
 
 	jar, jarRE = pctx.RemoteStaticRules("jar",
 		blueprint.RuleParams{
@@ -258,6 +258,13 @@
 		},
 	)
 
+	ravenizer = pctx.AndroidStaticRule("ravenizer",
+		blueprint.RuleParams{
+			Command:     "rm -f $out && ${ravenizer} --in-jar $in --out-jar $out",
+			CommandDeps: []string{"${ravenizer}"},
+		},
+	)
+
 	zipalign = pctx.AndroidStaticRule("zipalign",
 		blueprint.RuleParams{
 			Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " +
@@ -307,6 +314,7 @@
 	pctx.Import("android/soong/java/config")
 
 	pctx.HostBinToolVariable("aconfig", "aconfig")
+	pctx.HostBinToolVariable("ravenizer", "ravenizer")
 	pctx.HostBinToolVariable("keep-flagged-apis", "keep-flagged-apis")
 }
 
@@ -428,53 +436,72 @@
 		})
 }
 
-func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags, dir string) (string, android.Paths) {
-	var deps android.Paths
+func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags, dir string, srcJars android.Paths) (string, android.Paths, android.Paths, android.Paths) {
+	var implicits android.Paths
+	var rbeInputs android.Paths
+	var rspFiles android.Paths
 
 	classpath := flags.classpath
 
-	var bootClasspath string
+	srcJarArgs := strings.Join(srcJars.Strings(), " ")
+	implicits = append(implicits, srcJars...)
+	const srcJarArgsLimit = 32 * 1024
+	if len(srcJarArgs) > srcJarArgsLimit {
+		srcJarRspFile := android.PathForModuleOut(ctx, "turbine", "srcjars.rsp")
+		android.WriteFileRule(ctx, srcJarRspFile, srcJarArgs)
+		srcJarArgs = "@" + srcJarRspFile.String()
+		implicits = append(implicits, srcJarRspFile)
+		rbeInputs = append(rbeInputs, srcJarRspFile)
+	} else {
+		rbeInputs = append(rbeInputs, srcJars...)
+	}
+
+	var bootClasspathFlags string
 	if flags.javaVersion.usesJavaModules() {
 		var systemModuleDeps android.Paths
-		bootClasspath, systemModuleDeps = flags.systemModules.FormTurbineSystemModulesPath(ctx.Device())
-		deps = append(deps, systemModuleDeps...)
+		bootClasspathFlags, systemModuleDeps = flags.systemModules.FormTurbineSystemModulesPath(ctx.Device())
+		implicits = append(implicits, systemModuleDeps...)
+		rbeInputs = append(rbeInputs, systemModuleDeps...)
 		classpath = append(flags.java9Classpath, classpath...)
 	} else {
-		deps = append(deps, flags.bootClasspath...)
+		implicits = append(implicits, flags.bootClasspath...)
+		rbeInputs = append(rbeInputs, flags.bootClasspath...)
 		if len(flags.bootClasspath) == 0 && ctx.Device() {
 			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
 			// ensure turbine does not fall back to the default bootclasspath.
-			bootClasspath = `--bootclasspath ""`
+			bootClasspathFlags = `--bootclasspath ""`
 		} else {
-			bootClasspath = flags.bootClasspath.FormTurbineClassPath("--bootclasspath ")
+			bootClasspathFlags = flags.bootClasspath.FormTurbineClassPath("--bootclasspath ")
 		}
 	}
 
-	deps = append(deps, classpath...)
-	turbineFlags := bootClasspath + " " + classpath.FormTurbineClassPath("--classpath ")
-
-	const flagsLimit = 32 * 1024
-	if len(turbineFlags) > flagsLimit {
-		flagsRspFile := android.PathForModuleOut(ctx, dir, "turbine-flags.rsp")
-		android.WriteFileRule(ctx, flagsRspFile, turbineFlags)
-		turbineFlags = "@" + flagsRspFile.String()
-		deps = append(deps, flagsRspFile)
+	classpathFlags := classpath.FormTurbineClassPath("")
+	implicits = append(implicits, classpath...)
+	const classpathLimit = 32 * 1024
+	if len(classpathFlags) > classpathLimit {
+		classpathRspFile := android.PathForModuleOut(ctx, dir, "classpath.rsp")
+		android.WriteFileRule(ctx, classpathRspFile, classpathFlags)
+		classpathFlags = "@" + classpathRspFile.String()
+		implicits = append(implicits, classpathRspFile)
+		rspFiles = append(rspFiles, classpathRspFile)
+		rbeInputs = append(rbeInputs, classpathRspFile)
+	} else {
+		rbeInputs = append(rbeInputs, classpath...)
 	}
 
-	return turbineFlags, deps
+	turbineFlags := "--source_jars " + srcJarArgs + " " + bootClasspathFlags + " --classpath " + classpathFlags
+
+	return turbineFlags, implicits, rbeInputs, rspFiles
 }
 
 func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
 
-	turbineFlags, deps := turbineFlags(ctx, flags, "turbine")
-
-	deps = append(deps, srcJars...)
+	turbineFlags, implicits, rbeInputs, rspFiles := turbineFlags(ctx, flags, "turbine", srcJars)
 
 	rule := turbine
 	args := map[string]string{
 		"javacFlags":   flags.javacFlags,
-		"srcJars":      strings.Join(srcJars.Strings(), " "),
 		"javaVersion":  flags.javaVersion.String(),
 		"turbineFlags": turbineFlags,
 		"outputFlags":  "--output " + outputFile.String() + ".tmp",
@@ -482,15 +509,16 @@
 	}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
 		rule = turbineRE
-		args["implicits"] = strings.Join(deps.Strings(), ",")
+		args["rbeInputs"] = strings.Join(rbeInputs.Strings(), ",")
 		args["rbeOutputs"] = outputFile.String() + ".tmp"
+		args["rspFiles"] = strings.Join(rspFiles.Strings(), ",")
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
 		Description: "turbine",
 		Output:      outputFile,
 		Inputs:      srcFiles,
-		Implicits:   deps,
+		Implicits:   implicits,
 		Args:        args,
 	})
 }
@@ -499,11 +527,10 @@
 func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.WritablePath,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
 
-	turbineFlags, deps := turbineFlags(ctx, flags, "kapt")
+	turbineFlags, implicits, rbeInputs, rspFiles := turbineFlags(ctx, flags, "turbine-apt", srcJars)
 
-	deps = append(deps, srcJars...)
-
-	deps = append(deps, flags.processorPath...)
+	implicits = append(implicits, flags.processorPath...)
+	rbeInputs = append(rbeInputs, flags.processorPath...)
 	turbineFlags += " " + flags.processorPath.FormTurbineClassPath("--processorpath ")
 	turbineFlags += " --processors " + strings.Join(flags.processors, " ")
 
@@ -514,7 +541,6 @@
 	rule := turbine
 	args := map[string]string{
 		"javacFlags":   flags.javacFlags,
-		"srcJars":      strings.Join(srcJars.Strings(), " "),
 		"javaVersion":  flags.javaVersion.String(),
 		"turbineFlags": turbineFlags,
 		"outputFlags":  outputFlags,
@@ -522,8 +548,9 @@
 	}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
 		rule = turbineRE
-		args["implicits"] = strings.Join(deps.Strings(), ",")
+		args["rbeInputs"] = strings.Join(rbeInputs.Strings(), ",")
 		args["rbeOutputs"] = outputSrcJar.String() + ".tmp," + outputResJar.String() + ".tmp"
+		args["rspFiles"] = strings.Join(rspFiles.Strings(), ",")
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:            rule,
@@ -531,7 +558,7 @@
 		Output:          outputs[0],
 		ImplicitOutputs: outputs[1:],
 		Inputs:          srcFiles,
-		Implicits:       deps,
+		Implicits:       implicits,
 		Args:            args,
 	})
 }
@@ -746,6 +773,16 @@
 	})
 }
 
+func TransformRavenizer(ctx android.ModuleContext, outputFile android.WritablePath,
+	inputFile android.Path) {
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        ravenizer,
+		Description: "ravenizer",
+		Output:      outputFile,
+		Input:       inputFile,
+	})
+}
+
 func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) {
 	android.WriteFileRule(ctx, outputFile, "Main-Class: "+mainClass+"\n")
 }
diff --git a/java/code_metadata_test.go b/java/code_metadata_test.go
index 99b1f52..9dc9a22 100644
--- a/java/code_metadata_test.go
+++ b/java/code_metadata_test.go
@@ -29,7 +29,7 @@
 	module := result.ModuleForTests("module-name", "")
 
 	// Check that the provider has the right contents
-	data, _ := android.SingletonModuleProvider(result, module.Module(), soongTesting.CodeMetadataProviderKey)
+	data, _ := android.OtherModuleProvider(result, module.Module(), soongTesting.CodeMetadataProviderKey)
 	if !strings.HasSuffix(
 		data.IntermediatePath.String(), "/intermediateCodeMetadata.pb",
 	) {
diff --git a/java/config/config.go b/java/config/config.go
index 66e857c..a50c1b4 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -47,7 +47,7 @@
 		"services",
 		"android.car",
 		"android.car7",
-		"android.car.builtin",
+		"android.car.builtin.impl",
 		"conscrypt",
 		"core-icu4j",
 		"core-oj",
diff --git a/java/container_test.go b/java/container_test.go
index 3441855..25cfa4c 100644
--- a/java/container_test.go
+++ b/java/container_test.go
@@ -65,6 +65,18 @@
 				"general-tests",
 			],
 		}
+		java_library {
+			name: "bar",
+			static_libs: [
+				"framework-minus-apex",
+			],
+		}
+		java_library {
+			name: "baz",
+			static_libs: [
+				"bar",
+			],
+		}
 	`)
 
 	testcases := []struct {
@@ -73,6 +85,7 @@
 		isVendorContainer  bool
 		isProductContainer bool
 		isCts              bool
+		isUnstable         bool
 	}{
 		{
 			moduleName:         "foo",
@@ -80,6 +93,7 @@
 			isVendorContainer:  false,
 			isProductContainer: false,
 			isCts:              false,
+			isUnstable:         false,
 		},
 		{
 			moduleName:         "foo_vendor",
@@ -87,6 +101,7 @@
 			isVendorContainer:  true,
 			isProductContainer: false,
 			isCts:              false,
+			isUnstable:         false,
 		},
 		{
 			moduleName:         "foo_soc_specific",
@@ -94,6 +109,7 @@
 			isVendorContainer:  true,
 			isProductContainer: false,
 			isCts:              false,
+			isUnstable:         false,
 		},
 		{
 			moduleName:         "foo_product_specific",
@@ -101,6 +117,7 @@
 			isVendorContainer:  false,
 			isProductContainer: true,
 			isCts:              false,
+			isUnstable:         false,
 		},
 		{
 			moduleName:         "foo_cts_test",
@@ -108,6 +125,7 @@
 			isVendorContainer:  false,
 			isProductContainer: false,
 			isCts:              true,
+			isUnstable:         false,
 		},
 		{
 			moduleName:         "foo_non_cts_test",
@@ -115,6 +133,23 @@
 			isVendorContainer:  false,
 			isProductContainer: false,
 			isCts:              false,
+			isUnstable:         false,
+		},
+		{
+			moduleName:         "bar",
+			isSystemContainer:  true,
+			isVendorContainer:  false,
+			isProductContainer: false,
+			isCts:              false,
+			isUnstable:         true,
+		},
+		{
+			moduleName:         "baz",
+			isSystemContainer:  true,
+			isVendorContainer:  false,
+			isProductContainer: false,
+			isCts:              false,
+			isUnstable:         true,
 		},
 	}
 
@@ -125,5 +160,7 @@
 		checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers))
 		checkContainerMatch(t, c.moduleName, "vendor", c.isVendorContainer, android.InList(android.VendorContainer, belongingContainers))
 		checkContainerMatch(t, c.moduleName, "product", c.isProductContainer, android.InList(android.ProductContainer, belongingContainers))
+		checkContainerMatch(t, c.moduleName, "cts", c.isCts, android.InList(android.CtsContainer, belongingContainers))
+		checkContainerMatch(t, c.moduleName, "unstable", c.isUnstable, android.InList(android.UnstableContainer, belongingContainers))
 	}
 }
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index ab72e8b..cee7a19 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -41,7 +41,7 @@
 }
 
 java_library {
-    name: "core.current.stubs.from-source",
+    name: "core.current.stubs",
     defaults: [
         "core.current.stubs.defaults",
     ],
@@ -52,8 +52,12 @@
     ],
 }
 
+// Used for bootstrapping ART system modules
 java_api_library {
     name: "core.current.stubs.from-text",
+    defaults: [
+        "core.current.stubs.defaults",
+    ],
     api_surface: "core",
     api_contributions: [
         "art.module.public.api.stubs.source.api.contribution",
@@ -68,27 +72,7 @@
 }
 
 java_library {
-    name: "core.current.stubs",
-    defaults: [
-        "core.current.stubs.defaults",
-    ],
-    static_libs: [
-        "core.current.stubs.from-source",
-    ],
-    product_variables: {
-        build_from_text_stub: {
-            static_libs: [
-                "core.current.stubs.from-text",
-            ],
-            exclude_static_libs: [
-                "core.current.stubs.from-source",
-            ],
-        },
-    },
-}
-
-java_library {
-    name: "core.current.stubs.exportable.from-source",
+    name: "core.current.stubs.exportable",
     defaults: [
         "core.current.stubs.defaults",
     ],
@@ -103,16 +87,6 @@
     },
 }
 
-java_library {
-    name: "core.current.stubs.exportable",
-    defaults: [
-        "core.current.stubs.defaults",
-    ],
-    static_libs: [
-        "core.current.stubs.exportable.from-source",
-    ],
-}
-
 // Distributed with the SDK for turning into system modules to compile apps
 // against.
 //
@@ -201,26 +175,6 @@
         "core.module_lib.stubs.defaults",
     ],
     static_libs: [
-        "core.module_lib.stubs.from-source",
-    ],
-    product_variables: {
-        build_from_text_stub: {
-            static_libs: [
-                "core.module_lib.stubs.from-text",
-            ],
-            exclude_static_libs: [
-                "core.module_lib.stubs.from-source",
-            ],
-        },
-    },
-}
-
-java_library {
-    name: "core.module_lib.stubs.from-source",
-    defaults: [
-        "core.module_lib.stubs.defaults",
-    ],
-    static_libs: [
         "art.module.public.api.stubs.module_lib",
 
         // Replace the following with the module-lib correspondence when Conscrypt or i18N module
@@ -231,27 +185,6 @@
     ],
 }
 
-java_api_library {
-    name: "core.module_lib.stubs.from-text",
-    api_surface: "module-lib",
-    api_contributions: [
-        "art.module.public.api.stubs.source.api.contribution",
-        "art.module.public.api.stubs.source.system.api.contribution",
-        "art.module.public.api.stubs.source.module_lib.api.contribution",
-
-        // Add the module-lib correspondence when Conscrypt or i18N module
-        // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides
-        // @SystemApi(MODULE_LIBRARIES).
-        "conscrypt.module.public.api.stubs.source.api.contribution",
-        "i18n.module.public.api.stubs.source.api.contribution",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    visibility: ["//visibility:private"],
-    stubs_type: "everything",
-}
-
 // Produces a dist file that is used by the
 // prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk
 // directory.
@@ -311,7 +244,7 @@
 // API annotations are available to the dex tools that enable enforcement of runtime
 // accessibility. b/119068555
 java_library {
-    name: "legacy.core.platform.api.stubs.from-source",
+    name: "legacy.core.platform.api.stubs",
     visibility: core_platform_visibility,
     defaults: [
         "core.platform.api.stubs.defaults",
@@ -324,7 +257,7 @@
 }
 
 java_library {
-    name: "legacy.core.platform.api.stubs.exportable.from-source",
+    name: "legacy.core.platform.api.stubs.exportable",
     visibility: core_platform_visibility,
     defaults: [
         "core.platform.api.stubs.defaults",
@@ -348,53 +281,6 @@
     ],
 }
 
-java_api_library {
-    name: "legacy.core.platform.api.stubs.from-text",
-    api_surface: "core_platform",
-    defaults: [
-        "android_core_platform_stubs_current_contributions",
-    ],
-    api_contributions: [
-        "legacy.i18n.module.platform.api.stubs.source.api.contribution",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    stubs_type: "everything",
-}
-
-java_library {
-    name: "legacy.core.platform.api.stubs",
-    visibility: core_platform_visibility,
-    defaults: [
-        "core.platform.api.stubs.defaults",
-    ],
-    static_libs: [
-        "legacy.core.platform.api.stubs.from-source",
-    ],
-    product_variables: {
-        build_from_text_stub: {
-            static_libs: [
-                "legacy.core.platform.api.stubs.from-text",
-            ],
-            exclude_static_libs: [
-                "legacy.core.platform.api.stubs.from-source",
-            ],
-        },
-    },
-}
-
-java_library {
-    name: "legacy.core.platform.api.stubs.exportable",
-    visibility: core_platform_visibility,
-    defaults: [
-        "core.platform.api.stubs.defaults",
-    ],
-    static_libs: [
-        "legacy.core.platform.api.stubs.exportable.from-source",
-    ],
-}
-
 java_defaults {
     name: "core.platform.api.stubs.defaults",
     hostdex: true,
@@ -424,7 +310,7 @@
 }
 
 java_library {
-    name: "stable.core.platform.api.stubs.from-source",
+    name: "stable.core.platform.api.stubs",
     visibility: core_platform_visibility,
     defaults: [
         "core.platform.api.stubs.defaults",
@@ -437,42 +323,6 @@
     ],
 }
 
-java_api_library {
-    name: "stable.core.platform.api.stubs.from-text",
-    api_surface: "core_platform",
-    defaults: [
-        "android_core_platform_stubs_current_contributions",
-    ],
-    api_contributions: [
-        "stable.i18n.module.platform.api.stubs.source.api.contribution",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    stubs_type: "everything",
-}
-
-java_library {
-    name: "stable.core.platform.api.stubs",
-    visibility: core_platform_visibility,
-    defaults: [
-        "core.platform.api.stubs.defaults",
-    ],
-    static_libs: [
-        "stable.core.platform.api.stubs.from-source",
-    ],
-    product_variables: {
-        build_from_text_stub: {
-            static_libs: [
-                "stable.core.platform.api.stubs.from-text",
-            ],
-            exclude_static_libs: [
-                "stable.core.platform.api.stubs.from-source",
-            ],
-        },
-    },
-}
-
 // Same as stable.core.platform.api.stubs, but android annotations are
 // stripped. This is used by the Java toolchain, while the annotated stub is to
 // be used by Kotlin one.
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 3f8735c..63b69d0 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -130,7 +130,7 @@
 		d.combinedHeaderJar = d.headerJars[0]
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
 		HeaderJars:                     d.headerJars,
 		ImplementationAndResourcesJars: d.implementationAndResourceJars,
 		ImplementationJars:             d.implementationJars,
@@ -188,3 +188,11 @@
 		},
 	}
 }
+
+// implement the following interface for IDE completion.
+var _ android.IDEInfo = (*DeviceHostConverter)(nil)
+
+func (d *DeviceHostConverter) IDEInfo(ideInfo *android.IdeInfo) {
+	ideInfo.Deps = append(ideInfo.Deps, d.properties.Libs...)
+	ideInfo.Libs = append(ideInfo.Libs, d.properties.Libs...)
+}
diff --git a/java/dex.go b/java/dex.go
index 7bb6925..6c739a2 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -289,14 +289,18 @@
 	// - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version.
 	// - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version.
 	// See b/20667396
-	var proguardRaiseDeps classpath
-	ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) {
-		dep, _ := android.OtherModuleProvider(ctx, m, JavaInfoProvider)
-		proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...)
-	})
+	// TODO(b/360905238): Remove SdkSystemServer exception after resolving missing class references.
+	if !dexParams.sdkVersion.Stable() || dexParams.sdkVersion.Kind == android.SdkSystemServer {
+		var proguardRaiseDeps classpath
+		ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) {
+			if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
+				proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...)
+			}
+		})
+		r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
+		r8Deps = append(r8Deps, proguardRaiseDeps...)
+	}
 
-	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
-	r8Deps = append(r8Deps, proguardRaiseDeps...)
 	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
 	r8Deps = append(r8Deps, flags.bootClasspath...)
 	r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
@@ -428,7 +432,7 @@
 }
 
 // Return the compiled dex jar and (optional) profile _after_ r8 optimization
-func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.OutputPath, *android.OutputPath) {
+func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.Path, android.Path) {
 
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 7949244..a38642a 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -359,7 +359,7 @@
 	d.dexpreopt(ctx, libraryName, dexJarFile)
 }
 
-func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.WritablePath) {
+func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.Path) {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
 	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
@@ -616,10 +616,8 @@
 	return installPath, relDir, installBase
 }
 
-// RuleBuilder.Install() adds output-to-install copy pairs to a list for Make. To share this
-// information with PackagingSpec in soong, call PackageFile for them.
-// The install path and the target install partition of the module must be the same.
-func packageFile(ctx android.ModuleContext, install android.RuleBuilderInstall) {
+// installFile will install the file if `install` path and the target install partition are the same.
+func installFile(ctx android.ModuleContext, install android.RuleBuilderInstall) {
 	installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
 	// Empty name means the install partition is not for the target image.
 	// For the system image, files for "apex" and "system_other" are skipped here.
@@ -628,7 +626,7 @@
 	// TODO(b/320196894): Files for "system_other" are skipped because soong creates the system
 	// image only for now.
 	if name != "" {
-		ctx.PackageFile(installPath.Join(ctx, relDir), name, install.From)
+		ctx.InstallFile(installPath.Join(ctx, relDir), name, install.From)
 	}
 }
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index defa82c..a81ab83 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -491,6 +491,11 @@
 	// Build path to a config file that Soong writes for Make (to be used in makefiles that install
 	// the default boot image).
 	dexpreoptConfigForMake android.WritablePath
+
+	// Build path to the boot framework profile.
+	// This is used as the `OutputFile` in `AndroidMkEntries`.
+	// A non-nil value ensures that this singleton module does not get skipped in AndroidMkEntries processing.
+	bootFrameworkProfile android.WritablePath
 }
 
 func (dbj *dexpreoptBootJars) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -603,7 +608,8 @@
 		installs := generateBootImage(ctx, config)
 		profileInstalls = append(profileInstalls, installs...)
 		if config == d.defaultBootImage {
-			_, installs := bootFrameworkProfileRule(ctx, config)
+			bootProfile, installs := bootFrameworkProfileRule(ctx, config)
+			d.bootFrameworkProfile = bootProfile
 			profileInstalls = append(profileInstalls, installs...)
 		}
 	}
@@ -613,7 +619,7 @@
 			profileLicenseMetadataFile: android.OptionalPathForPath(ctx.LicenseMetadataFile()),
 		})
 		for _, install := range profileInstalls {
-			packageFile(ctx, install)
+			installFile(ctx, install)
 		}
 	}
 }
@@ -939,24 +945,21 @@
 	}
 
 	for _, install := range image.installs {
-		packageFile(ctx, install)
+		installFile(ctx, install)
 	}
 
 	for _, install := range image.vdexInstalls {
-		if image.target.Arch.ArchType.Name != ctx.DeviceConfig().DeviceArch() {
-			// Note that the vdex files are identical between architectures. If the target image is
-			// not for the primary architecture create symlinks to share the vdex of the primary
-			// architecture with the other architectures.
-			//
-			// Assuming that the install path has the architecture name with it, replace the
-			// architecture name with the primary architecture name to find the source vdex file.
-			installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
-			if name != "" {
-				srcRelDir := strings.Replace(relDir, image.target.Arch.ArchType.Name, ctx.DeviceConfig().DeviceArch(), 1)
-				ctx.InstallSymlink(installPath.Join(ctx, relDir), name, installPath.Join(ctx, srcRelDir, name))
-			}
-		} else {
-			packageFile(ctx, install)
+		installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
+		if name == "" {
+			continue
+		}
+		// Note that the vdex files are identical between architectures. Copy the vdex to a no arch directory
+		// and create symlinks for both the primary and secondary arches.
+		ctx.InstallSymlink(installPath.Join(ctx, relDir), name, installPath.Join(ctx, "framework", name))
+		if image.target.Arch.ArchType.Name == ctx.DeviceConfig().DeviceArch() {
+			// Copy the vdex from the primary arch to the no-arch directory
+			// e.g. /system/framework/$bootjar.vdex
+			ctx.InstallFile(installPath.Join(ctx, "framework"), name, install.From)
 		}
 	}
 }
@@ -1234,7 +1237,7 @@
 
 	profile := bootImageProfileRuleCommon(ctx, image.name, image.dexPathsDeps.Paths(), image.getAnyAndroidVariant().dexLocationsDeps)
 
-	if image == defaultBootImageConfig(ctx) {
+	if image == defaultBootImageConfig(ctx) && profile != nil {
 		rule := android.NewRuleBuilder(pctx, ctx)
 		rule.Install(profile, "/system/etc/boot-image.prof")
 		return profile, rule.Installs()
@@ -1338,7 +1341,7 @@
 
 	image := d.defaultBootImage
 	if image != nil {
-		if profileInstallInfo, ok := android.SingletonModuleProvider(ctx, d, profileInstallInfoProvider); ok {
+		if profileInstallInfo, ok := android.OtherModuleProvider(ctx, d, profileInstallInfoProvider); ok {
 			ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", profileInstallInfo.profileInstalls.String())
 			if profileInstallInfo.profileLicenseMetadataFile.Valid() {
 				ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", profileInstallInfo.profileLicenseMetadataFile.String())
@@ -1380,3 +1383,12 @@
 		ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(getImageNames(), " "))
 	}
 }
+
+// Add one of the outputs in `OutputFile`
+// This ensures that this singleton module does not get skipped when writing out/soong/Android-*.mk
+func (d *dexpreoptBootJars) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(d.bootFrameworkProfile),
+	}}
+}
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 104829f..37c54b9 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -1237,7 +1237,7 @@
 
 	if !mutated {
 		dexBootJarModule := result.ModuleForTests("dex_bootjars", "android_common")
-		profileInstallInfo, _ := android.SingletonModuleProvider(result, dexBootJarModule.Module(), profileInstallInfoProvider)
+		profileInstallInfo, _ := android.OtherModuleProvider(result, dexBootJarModule.Module(), profileInstallInfoProvider)
 		assertInstallsEqual(t, "profileInstalls", expected.profileInstalls, profileInstallInfo.profileInstalls)
 		android.AssertStringEquals(t, "profileLicenseMetadataFile", expected.profileLicenseMetadataFile, profileInstallInfo.profileLicenseMetadataFile.RelativeToTop().String())
 	}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 730f236..f81c5ba 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -365,10 +365,10 @@
 		case bootClasspathTag:
 			if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 				deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars...)
-			} else if sm, ok := module.(SystemModulesProvider); ok {
+			} else if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok {
 				// A system modules dependency has been added to the bootclasspath
 				// so add its libs to the bootclasspath.
-				deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...)
+				deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars...)
 			} else {
 				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 			}
@@ -396,9 +396,12 @@
 			if deps.systemModules != nil {
 				panic("Found two system module dependencies")
 			}
-			sm := module.(SystemModulesProvider)
-			outputDir, outputDeps := sm.OutputDirAndDeps()
-			deps.systemModules = &systemModules{outputDir, outputDeps}
+			if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok {
+				deps.systemModules = &systemModules{sm.OutputDir, sm.OutputDirDeps}
+			} else {
+				ctx.PropertyErrorf("boot classpath dependency %q does not provide SystemModulesProvider",
+					ctx.OtherModuleName(module))
+			}
 		case aconfigDeclarationTag:
 			if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey); ok {
 				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPath)
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 6a14f36..1e8362c 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -421,11 +421,9 @@
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
 			variables.ExportRuntimeApis = proptools.BoolPtr(true)
 		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 		android.FixtureMergeMockFs(map[string][]byte{
 			"a/A.java":      nil,
 			"a/current.txt": nil,
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 5441a3b..b1a9deb 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -98,8 +98,9 @@
 	// processing.
 	classesJars := android.Paths{classesJar}
 	ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) {
-		javaInfo, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
-		classesJars = append(classesJars, javaInfo.ImplementationJars...)
+		if javaInfo, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+			classesJars = append(classesJars, javaInfo.ImplementationJars...)
+		}
 	})
 	h.classesJarPaths = classesJars
 
@@ -151,7 +152,7 @@
 //
 // Otherwise, it creates a copy of the supplied dex file into which it has encoded the hiddenapi
 // flags and returns this instead of the supplied dex jar.
-func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android.OutputPath) android.OutputPath {
+func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android.Path) android.Path {
 
 	if !h.active {
 		return dexJar
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 6229797..afe8b4c 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -203,10 +203,8 @@
 				FixtureConfigureBootJars("platform:foo"),
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
-					variables.BuildFlags = map[string]string{
-						"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-					}
 				}),
+				android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 			).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
diff --git a/java/java.go b/java/java.go
index b320732..46344c8 100644
--- a/java/java.go
+++ b/java/java.go
@@ -315,14 +315,14 @@
 	AconfigIntermediateCacheOutputPaths android.Paths
 }
 
-var JavaInfoProvider = blueprint.NewProvider[JavaInfo]()
+var JavaInfoProvider = blueprint.NewProvider[*JavaInfo]()
 
 // SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to
 // the sysprop implementation library.
 type SyspropPublicStubInfo struct {
 	// JavaInfo is the JavaInfoProvider of the sysprop public stub library that corresponds to
 	// the sysprop implementation library.
-	JavaInfo JavaInfo
+	JavaInfo *JavaInfo
 }
 
 var SyspropPublicStubInfoProvider = blueprint.NewProvider[SyspropPublicStubInfo]()
@@ -421,8 +421,6 @@
 	bootClasspathTag        = dependencyTag{name: "bootclasspath", runtimeLinked: true}
 	systemModulesTag        = dependencyTag{name: "system modules", runtimeLinked: true}
 	frameworkResTag         = dependencyTag{name: "framework-res"}
-	kotlinStdlibTag         = dependencyTag{name: "kotlin-stdlib", runtimeLinked: true}
-	kotlinAnnotationsTag    = dependencyTag{name: "kotlin-annotations", runtimeLinked: true}
 	kotlinPluginTag         = dependencyTag{name: "kotlin-plugin", toolchain: true}
 	proguardRaiseTag        = dependencyTag{name: "proguard-raise"}
 	certificateTag          = dependencyTag{name: "certificate"}
@@ -432,7 +430,6 @@
 	r8LibraryJarTag         = dependencyTag{name: "r8-libraryjar", runtimeLinked: true}
 	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
 	javaApiContributionTag  = dependencyTag{name: "java-api-contribution"}
-	depApiSrcsTag           = dependencyTag{name: "dep-api-srcs"}
 	aconfigDeclarationTag   = dependencyTag{name: "aconfig-declaration"}
 	jniInstallTag           = dependencyTag{name: "jni install", runtimeLinked: true, installable: true}
 	binaryInstallTag        = dependencyTag{name: "binary install", runtimeLinked: true, installable: true}
@@ -459,8 +456,6 @@
 		bootClasspathTag,
 		systemModulesTag,
 		java9LibTag,
-		kotlinStdlibTag,
-		kotlinAnnotationsTag,
 		kotlinPluginTag,
 		syspropPublicStubDepTag,
 		instrumentationForTag,
@@ -569,8 +564,6 @@
 	srcJars                 android.Paths
 	systemModules           *systemModules
 	aidlPreprocess          android.OptionalPath
-	kotlinStdlib            android.Paths
-	kotlinAnnotations       android.Paths
 	kotlinPlugins           android.Paths
 	aconfigProtoFiles       android.Paths
 
@@ -1871,10 +1864,12 @@
 	if ctx.Arch().ArchType == android.Common {
 		j.deps(ctx)
 	}
-	if ctx.Arch().ArchType != android.Common {
-		// These dependencies ensure the host installation rules will install the jar file and
-		// the jni libraries when the wrapper is installed.
+	// These dependencies ensure the installation rules will install the jar file when the
+	// wrapper is installed, and the jni libraries on host when the wrapper is installed.
+	if ctx.Arch().ArchType != android.Common && ctx.Os().Class == android.Host {
 		ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
+	}
+	if ctx.Arch().ArchType != android.Common {
 		ctx.AddVariationDependencies(
 			[]blueprint.Variation{{Mutator: "arch", Variation: android.CommonArch.String()}},
 			binaryInstallTag, ctx.ModuleName())
@@ -2005,12 +2000,6 @@
 	// merge zipped after metalava invocation
 	Static_libs []string
 
-	// Java Api library to provide the full API surface stub jar file.
-	// If this property is set, the stub jar of this module is created by
-	// extracting the compiled class files provided by the
-	// full_api_surface_stub module.
-	Full_api_surface_stub *string
-
 	// Version of previously released API file for compatibility check.
 	Previous_api *string `android:"path"`
 
@@ -2043,6 +2032,15 @@
 	// List of hard coded filegroups containing Metalava config files that are passed to every
 	// Metalava invocation that this module performs. See addMetalavaConfigFilesToCmd.
 	ConfigFiles []string `android:"path" blueprint:"mutated"`
+
+	// If not blank, set to the version of the sdk to compile against.
+	// Defaults to an empty string, which compiles the module against the private platform APIs.
+	// Values are of one of the following forms:
+	// 1) numerical API level, "current", "none", or "core_platform"
+	// 2) An SDK kind with an API level: "<sdk kind>_<API level>"
+	// See build/soong/android/sdk_version.go for the complete and up to date list of SDK kinds.
+	// If the SDK kind is empty, it will be set to public.
+	Sdk_version *string
 }
 
 func ApiLibraryFactory() android.Module {
@@ -2141,40 +2139,6 @@
 	}
 }
 
-// This method extracts the stub class files from the stub jar file provided
-// from full_api_surface_stub module instead of compiling the srcjar generated from invoking metalava.
-// This method is used because metalava can generate compilable from-text stubs only when
-// the codebase encompasses all classes listed in the input API text file, and a class can extend
-// a class that is not within the same API domain.
-func (al *ApiLibrary) extractApiSrcs(ctx android.ModuleContext, rule *android.RuleBuilder, stubsDir android.OptionalPath, fullApiSurfaceStubJar android.Path) {
-	classFilesList := android.PathForModuleOut(ctx, "metalava", "classes.txt")
-	unzippedSrcJarDir := android.PathForModuleOut(ctx, "metalava", "unzipDir")
-
-	rule.Command().
-		BuiltTool("list_files").
-		Text(stubsDir.String()).
-		FlagWithOutput("--out ", classFilesList).
-		FlagWithArg("--extensions ", ".java").
-		FlagWithArg("--root ", unzippedSrcJarDir.String()).
-		Flag("--classes")
-
-	rule.Command().
-		Text("unzip").
-		Flag("-q").
-		Input(fullApiSurfaceStubJar).
-		FlagWithArg("-d ", unzippedSrcJarDir.String())
-
-	rule.Command().
-		BuiltTool("soong_zip").
-		Flag("-jar").
-		Flag("-write_if_changed").
-		Flag("-ignore_missing_files").
-		Flag("-quiet").
-		FlagWithArg("-C ", unzippedSrcJarDir.String()).
-		FlagWithInput("-l ", classFilesList).
-		FlagWithOutput("-o ", al.stubsJarWithoutStaticLibs)
-}
-
 func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
 	apiContributions := al.properties.Api_contributions
 	addValidations := !ctx.Config().IsEnvTrue("DISABLE_STUB_VALIDATION") &&
@@ -2201,14 +2165,18 @@
 			}
 		}
 	}
+	if ctx.Device() {
+		sdkDep := decodeSdkDep(ctx, android.SdkContext(al))
+		if sdkDep.useModule {
+			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+			ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...)
+			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
+
+		}
+	}
 	ctx.AddVariationDependencies(nil, libTag, al.properties.Libs...)
 	ctx.AddVariationDependencies(nil, staticLibTag, al.properties.Static_libs...)
-	if al.properties.Full_api_surface_stub != nil {
-		ctx.AddVariationDependencies(nil, depApiSrcsTag, String(al.properties.Full_api_surface_stub))
-	}
-	if al.properties.System_modules != nil {
-		ctx.AddVariationDependencies(nil, systemModulesTag, String(al.properties.System_modules))
-	}
+
 	for _, aconfigDeclarationsName := range al.properties.Aconfig_declarations {
 		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationsName)
 	}
@@ -2264,8 +2232,8 @@
 
 	var srcFilesInfo []JavaApiImportInfo
 	var classPaths android.Paths
+	var bootclassPaths android.Paths
 	var staticLibs android.Paths
-	var depApiSrcsStubsJar android.Path
 	var systemModulesPaths android.Paths
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		tag := ctx.OtherModuleDependencyTag(dep)
@@ -2277,17 +2245,21 @@
 			}
 			srcFilesInfo = append(srcFilesInfo, provider)
 		case libTag:
-			provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
-			classPaths = append(classPaths, provider.HeaderJars...)
+			if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+				classPaths = append(classPaths, provider.HeaderJars...)
+			}
+		case bootClasspathTag:
+			if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+				bootclassPaths = append(bootclassPaths, provider.HeaderJars...)
+			}
 		case staticLibTag:
-			provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
-			staticLibs = append(staticLibs, provider.HeaderJars...)
-		case depApiSrcsTag:
-			provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
-			depApiSrcsStubsJar = provider.HeaderJars[0]
+			if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+				staticLibs = append(staticLibs, provider.HeaderJars...)
+			}
 		case systemModulesTag:
-			module := dep.(SystemModulesProvider)
-			systemModulesPaths = append(systemModulesPaths, module.HeaderJars()...)
+			if sm, ok := android.OtherModuleProvider(ctx, dep, SystemModulesProvider); ok {
+				systemModulesPaths = append(systemModulesPaths, sm.HeaderJars...)
+			}
 		case metalavaCurrentApiTimestampTag:
 			if currentApiTimestampProvider, ok := dep.(currentApiTimestampProvider); ok {
 				al.validationPaths = append(al.validationPaths, currentApiTimestampProvider.CurrentApiTimestamp())
@@ -2319,7 +2291,10 @@
 
 	configFiles := android.PathsForModuleSrc(ctx, al.properties.ConfigFiles)
 
-	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, systemModulesPaths, configFiles)
+	combinedPaths := append(([]android.Path)(nil), systemModulesPaths...)
+	combinedPaths = append(combinedPaths, classPaths...)
+	combinedPaths = append(combinedPaths, bootclassPaths...)
+	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, combinedPaths, configFiles)
 
 	al.stubsFlags(ctx, cmd, stubsDir)
 
@@ -2337,9 +2312,6 @@
 	al.stubsJarWithoutStaticLibs = android.PathForModuleOut(ctx, "metalava", "stubs.jar")
 	al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName()))
 
-	if depApiSrcsStubsJar != nil {
-		al.extractApiSrcs(ctx, rule, stubsDir, depApiSrcsStubsJar)
-	}
 	rule.Command().
 		BuiltTool("soong_zip").
 		Flag("-write_if_changed").
@@ -2350,19 +2322,18 @@
 
 	rule.Build("metalava", "metalava merged text")
 
-	if depApiSrcsStubsJar == nil {
-		var flags javaBuilderFlags
-		flags.javaVersion = getStubsJavaVersion()
-		flags.javacFlags = strings.Join(al.properties.Javacflags, " ")
-		flags.classpath = classpath(classPaths)
-		flags.bootClasspath = classpath(systemModulesPaths)
-
-		annoSrcJar := android.PathForModuleOut(ctx, ctx.ModuleName(), "anno.srcjar")
-
-		TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{},
-			android.Paths{al.stubsSrcJar}, annoSrcJar, flags, android.Paths{})
+	javacFlags := javaBuilderFlags{
+		javaVersion:   getStubsJavaVersion(),
+		javacFlags:    strings.Join(al.properties.Javacflags, " "),
+		classpath:     classpath(classPaths),
+		bootClasspath: classpath(append(systemModulesPaths, bootclassPaths...)),
 	}
 
+	annoSrcJar := android.PathForModuleOut(ctx, ctx.ModuleName(), "anno.srcjar")
+
+	TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{},
+		android.Paths{al.stubsSrcJar}, annoSrcJar, javacFlags, android.Paths{})
+
 	builder := android.NewRuleBuilder(pctx, ctx)
 	builder.Command().
 		BuiltTool("merge_zips").
@@ -2373,7 +2344,7 @@
 
 	// compile stubs to .dex for hiddenapi processing
 	dexParams := &compileDexParams{
-		flags:         javaBuilderFlags{},
+		flags:         javacFlags,
 		sdkVersion:    al.SdkVersion(ctx),
 		minSdkVersion: al.MinSdkVersion(ctx),
 		classesJar:    al.stubsJar,
@@ -2387,7 +2358,7 @@
 
 	ctx.Phony(ctx.ModuleName(), al.stubsJar)
 
-	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(al.stubsJar),
 		ImplementationAndResourcesJars: android.PathsIfNonNil(al.stubsJar),
 		ImplementationJars:             android.PathsIfNonNil(al.stubsJar),
@@ -2409,14 +2380,28 @@
 	return nil
 }
 
-// java_api_library constitutes the sdk, and does not build against one
+// Most java_api_library constitues the sdk, but there are some java_api_library that
+// does not contribute to the api surface. Such modules are allowed to set sdk_version
+// other than "none"
 func (al *ApiLibrary) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
-	return android.SdkSpecNone
+	return android.SdkSpecFrom(ctx, proptools.String(al.properties.Sdk_version))
 }
 
 // java_api_library is always at "current". Return FutureApiLevel
 func (al *ApiLibrary) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
-	return android.FutureApiLevel
+	return al.SdkVersion(ctx).ApiLevel
+}
+
+func (al *ApiLibrary) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel {
+	return al.SdkVersion(ctx).ApiLevel
+}
+
+func (al *ApiLibrary) SystemModules() string {
+	return proptools.String(al.properties.System_modules)
+}
+
+func (al *ApiLibrary) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
+	return al.SdkVersion(ctx).ApiLevel
 }
 
 func (al *ApiLibrary) IDEInfo(i *android.IdeInfo) {
@@ -2434,9 +2419,6 @@
 	if al.properties.System_modules != nil {
 		ret = append(ret, proptools.String(al.properties.System_modules))
 	}
-	if al.properties.Full_api_surface_stub != nil {
-		ret = append(ret, proptools.String(al.properties.Full_api_surface_stub))
-	}
 	// Other non java_library dependencies like java_api_contribution are ignored for now.
 	return ret
 }
@@ -2444,6 +2426,7 @@
 // implement the following interfaces for hiddenapi processing
 var _ hiddenAPIModule = (*ApiLibrary)(nil)
 var _ UsesLibraryDependency = (*ApiLibrary)(nil)
+var _ android.SdkContext = (*ApiLibrary)(nil)
 
 // implement the following interface for IDE completion.
 var _ android.IDEInfo = (*ApiLibrary)(nil)
@@ -2653,6 +2636,7 @@
 
 	j.collectTransitiveHeaderJars(ctx)
 	var staticJars android.Paths
+	var staticResourceJars android.Paths
 	var staticHeaderJars android.Paths
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -2663,7 +2647,8 @@
 				flags.dexClasspath = append(flags.dexClasspath, dep.HeaderJars...)
 			case staticLibTag:
 				flags.classpath = append(flags.classpath, dep.HeaderJars...)
-				staticJars = append(staticJars, dep.ImplementationAndResourcesJars...)
+				staticJars = append(staticJars, dep.ImplementationJars...)
+				staticResourceJars = append(staticResourceJars, dep.ResourceJars...)
 				staticHeaderJars = append(staticHeaderJars, dep.HeaderJars...)
 			case bootClasspathTag:
 				flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars...)
@@ -2683,43 +2668,67 @@
 
 	// Always pass the input jars to TransformJarsToJar, even if there is only a single jar, we need the output
 	// file of the module to be named jarName.
-	outputFile := android.PathForModuleOut(ctx, "combined", jarName)
+	var outputFile android.Path
+	combinedImplementationJar := android.PathForModuleOut(ctx, "combined", jarName)
 	implementationJars := append(slices.Clone(jars), staticJars...)
-	TransformJarsToJar(ctx, outputFile, "combine prebuilt implementation jars", implementationJars, android.OptionalPath{},
+	TransformJarsToJar(ctx, combinedImplementationJar, "combine prebuilt implementation jars", implementationJars, android.OptionalPath{},
 		false, j.properties.Exclude_files, j.properties.Exclude_dirs)
+	outputFile = combinedImplementationJar
 
 	// If no dependencies have separate header jars then there is no need to create a separate
 	// header jar for this module.
 	reuseImplementationJarAsHeaderJar := slices.Equal(staticJars, staticHeaderJars)
 
-	var headerOutputFile android.ModuleOutPath
+	var resourceJarFile android.Path
+	if len(staticResourceJars) > 1 {
+		combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
+		TransformJarsToJar(ctx, combinedJar, "for resources", staticResourceJars, android.OptionalPath{},
+			false, nil, nil)
+		resourceJarFile = combinedJar
+	} else if len(staticResourceJars) == 1 {
+		resourceJarFile = staticResourceJars[0]
+	}
+
+	var headerJar android.Path
 	if reuseImplementationJarAsHeaderJar {
-		headerOutputFile = outputFile
+		headerJar = outputFile
 	} else {
 		headerJars := append(slices.Clone(jars), staticHeaderJars...)
-		headerOutputFile = android.PathForModuleOut(ctx, "turbine-combined", jarName)
+		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)
+		headerJar = headerOutputFile
 	}
 
 	if Bool(j.properties.Jetifier) {
-		inputFile := outputFile
-		outputFile = android.PathForModuleOut(ctx, "jetifier", jarName)
-		TransformJetifier(ctx, outputFile, inputFile)
+		jetifierOutputFile := android.PathForModuleOut(ctx, "jetifier", jarName)
+		TransformJetifier(ctx, jetifierOutputFile, outputFile)
+		outputFile = jetifierOutputFile
 
 		if !reuseImplementationJarAsHeaderJar {
-			headerInputFile := headerOutputFile
-			headerOutputFile = android.PathForModuleOut(ctx, "jetifier-headers", jarName)
-			TransformJetifier(ctx, headerOutputFile, headerInputFile)
+			jetifierHeaderJar := android.PathForModuleOut(ctx, "jetifier-headers", jarName)
+			TransformJetifier(ctx, jetifierHeaderJar, headerJar)
+			headerJar = jetifierHeaderJar
 		} else {
-			headerOutputFile = outputFile
+			headerJar = outputFile
 		}
 	}
 
+	implementationJarFile := outputFile
+
+	// merge implementation jar with resources if necessary
+	if resourceJarFile != nil {
+		jars := android.Paths{resourceJarFile, outputFile}
+		combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
+		TransformJarsToJar(ctx, combinedJar, "for resources", jars, android.OptionalPath{},
+			false, nil, nil)
+		outputFile = combinedJar
+	}
+
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource.
 	// Also strip the relative path from the header output file so that the reuseImplementationJarAsHeaderJar check
 	// in a module that depends on this module considers them equal.
-	j.combinedHeaderFile = headerOutputFile.WithoutRel()
+	j.combinedHeaderFile = headerJar.WithoutRel()
 	j.combinedImplementationFile = outputFile.WithoutRel()
 
 	j.maybeInstall(ctx, jarName, outputFile)
@@ -2779,7 +2788,7 @@
 			setUncompressDex(ctx, &j.dexpreopter, &j.dexer)
 			j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 
-			var dexOutputFile android.OutputPath
+			var dexOutputFile android.Path
 			dexParams := &compileDexParams{
 				flags:         flags,
 				sdkVersion:    j.SdkVersion(ctx),
@@ -2804,12 +2813,13 @@
 		}
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
+	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(j.combinedHeaderFile),
 		TransitiveLibsHeaderJars:       j.transitiveLibsHeaderJars,
 		TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
 		ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedImplementationFile),
-		ImplementationJars:             android.PathsIfNonNil(j.combinedImplementationFile),
+		ImplementationJars:             android.PathsIfNonNil(implementationJarFile.WithoutRel()),
+		ResourceJars:                   android.PathsIfNonNil(resourceJarFile),
 		AidlIncludeDirs:                j.exportAidlIncludeDirs,
 		StubsLinkType:                  j.stubsLinkType,
 		// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
@@ -2924,7 +2934,7 @@
 // Collect information for opening IDE project files in java/jdeps.go.
 
 func (j *Import) IDEInfo(dpInfo *android.IdeInfo) {
-	dpInfo.Jars = append(dpInfo.Jars, j.PrebuiltSrcs()...)
+	dpInfo.Jars = append(dpInfo.Jars, j.combinedHeaderFile.String())
 }
 
 func (j *Import) IDECustomizedModuleName() string {
diff --git a/java/java_test.go b/java/java_test.go
index 33079f3..477a0b3 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1100,7 +1100,7 @@
 	source := ctx.ModuleForTests("source_library", "android_common")
 	sourceJar := source.Output("javac/source_library.jar")
 	sourceHeaderJar := source.Output("turbine-combined/source_library.jar")
-	sourceJavaInfo, _ := android.SingletonModuleProvider(ctx, source.Module(), JavaInfoProvider)
+	sourceJavaInfo, _ := android.OtherModuleProvider(ctx, source.Module(), JavaInfoProvider)
 
 	// The source library produces separate implementation and header jars
 	android.AssertPathsRelativeToTopEquals(t, "source library implementation jar",
@@ -1110,7 +1110,7 @@
 
 	importWithNoDeps := ctx.ModuleForTests("import_with_no_deps", "android_common")
 	importWithNoDepsJar := importWithNoDeps.Output("combined/import_with_no_deps.jar")
-	importWithNoDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithNoDeps.Module(), JavaInfoProvider)
+	importWithNoDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithNoDeps.Module(), JavaInfoProvider)
 
 	// An import with no deps produces a single jar used as both the header and implementation jar.
 	android.AssertPathsRelativeToTopEquals(t, "import with no deps implementation jar",
@@ -1123,7 +1123,7 @@
 	importWithSourceDeps := ctx.ModuleForTests("import_with_source_deps", "android_common")
 	importWithSourceDepsJar := importWithSourceDeps.Output("combined/import_with_source_deps.jar")
 	importWithSourceDepsHeaderJar := importWithSourceDeps.Output("turbine-combined/import_with_source_deps.jar")
-	importWithSourceDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithSourceDeps.Module(), JavaInfoProvider)
+	importWithSourceDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithSourceDeps.Module(), JavaInfoProvider)
 
 	// An import with source deps produces separate header and implementation jars.
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps implementation jar",
@@ -1137,7 +1137,7 @@
 
 	importWithImportDeps := ctx.ModuleForTests("import_with_import_deps", "android_common")
 	importWithImportDepsJar := importWithImportDeps.Output("combined/import_with_import_deps.jar")
-	importWithImportDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithImportDeps.Module(), JavaInfoProvider)
+	importWithImportDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithImportDeps.Module(), JavaInfoProvider)
 
 	// An import with only import deps produces a single jar used as both the header and implementation jar.
 	android.AssertPathsRelativeToTopEquals(t, "import with import deps implementation jar",
@@ -1342,12 +1342,12 @@
 		}
 		`)
 
-	checkBootClasspathForSystemModule(t, ctx, "lib-with-source-system-modules", "/source-jar.jar")
+	checkBootClasspathForLibWithSystemModule(t, ctx, "lib-with-source-system-modules", "/source-jar.jar")
 
-	checkBootClasspathForSystemModule(t, ctx, "lib-with-prebuilt-system-modules", "/prebuilt-jar.jar")
+	checkBootClasspathForLibWithSystemModule(t, ctx, "lib-with-prebuilt-system-modules", "/prebuilt-jar.jar")
 }
 
-func checkBootClasspathForSystemModule(t *testing.T, ctx *android.TestContext, moduleName string, expectedSuffix string) {
+func checkBootClasspathForLibWithSystemModule(t *testing.T, ctx *android.TestContext, moduleName string, expectedSuffix string) {
 	javacRule := ctx.ModuleForTests(moduleName, "android_common").Rule("javac")
 	bootClasspath := javacRule.Args["bootClasspath"]
 	if strings.HasPrefix(bootClasspath, "--system ") && strings.HasSuffix(bootClasspath, expectedSuffix) {
@@ -2256,61 +2256,6 @@
 	}
 }
 
-func TestJavaApiLibraryFullApiSurfaceStub(t *testing.T) {
-	provider_bp_a := `
-	java_api_contribution {
-		name: "foo1",
-		api_file: "current.txt",
-		api_surface: "public",
-	}
-	`
-	provider_bp_b := `
-	java_api_contribution {
-		name: "foo2",
-		api_file: "current.txt",
-		api_surface: "public",
-	}
-	`
-	lib_bp_a := `
-	java_api_library {
-		name: "lib1",
-		api_surface: "public",
-		api_contributions: ["foo1", "foo2"],
-		stubs_type: "everything",
-	}
-	`
-
-	ctx := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		android.FixtureMergeMockFs(
-			map[string][]byte{
-				"a/Android.bp": []byte(provider_bp_a),
-				"b/Android.bp": []byte(provider_bp_b),
-				"c/Android.bp": []byte(lib_bp_a),
-			},
-		),
-		android.FixtureMergeEnv(
-			map[string]string{
-				"DISABLE_STUB_VALIDATION": "true",
-			},
-		),
-	).RunTestWithBp(t, `
-		java_api_library {
-			name: "bar1",
-			api_surface: "public",
-			api_contributions: ["foo1"],
-			full_api_surface_stub: "lib1",
-			stubs_type: "everything",
-		}
-	`)
-
-	m := ctx.ModuleForTests("bar1", "android_common")
-	manifest := m.Output("metalava.sbox.textproto")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
-	manifestCommand := sboxProto.Commands[0].GetCommand()
-	android.AssertStringDoesContain(t, "Command expected to contain full_api_surface_stub output jar", manifestCommand, "lib1.jar")
-}
-
 func TestTransitiveSrcFiles(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_library {
@@ -2329,7 +2274,7 @@
 		}
 	`)
 	c := ctx.ModuleForTests("c", "android_common").Module()
-	javaInfo, _ := android.SingletonModuleProvider(ctx, c, JavaInfoProvider)
+	javaInfo, _ := android.OtherModuleProvider(ctx, c, JavaInfoProvider)
 	transitiveSrcFiles := android.Paths(javaInfo.TransitiveSrcFiles.ToList())
 	android.AssertArrayString(t, "unexpected jar deps", []string{"b.java", "c.java"}, transitiveSrcFiles.Strings())
 }
@@ -2511,9 +2456,6 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 		android.FixtureMergeMockFs(
 			map[string][]byte{
 				"A.java": nil,
@@ -2534,12 +2476,8 @@
 			system_modules: "baz",
 		}
 	`)
-	m := result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common")
-	manifest := m.Output("metalava.sbox.textproto")
-	sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest)
-	manifestCommand := sboxProto.Commands[0].GetCommand()
-	classPathFlag := "--classpath __SBOX_SANDBOX_DIR__/out/soong/.intermediates/bar/android_common/turbine-combined/bar.jar"
-	android.AssertStringDoesContain(t, "command expected to contain classpath flag", manifestCommand, classPathFlag)
+
+	checkBootClasspathForLibWithSystemModule(t, result.TestContext, apiScopePublic.apiLibraryModuleName("foo"), "/bar.jar")
 }
 
 func TestApiLibraryDroidstubsDependency(t *testing.T) {
@@ -2547,9 +2485,6 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 		android.FixtureMergeMockFs(
 			map[string][]byte{
 				"A.java": nil,
@@ -2598,7 +2533,6 @@
 		PrepareForTestWithJacocoInstrumentation,
 		FixtureWithLastReleaseApis("foo"),
 		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
 			config.SetBuildFromTextStub(true)
 		}),
 		android.FixtureModifyEnv(func(env map[string]string) {
@@ -2700,11 +2634,7 @@
 	for _, tc := range testCases {
 		ctx := android.GroupFixturePreparers(
 			prepareForJavaTest,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
 		// check that rdep gets the correct variation of dep
@@ -2774,11 +2704,7 @@
 		ctx := android.GroupFixturePreparers(
 			prepareForJavaTest,
 			PrepareForTestWithPlatformCompatConfig,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
 		mergedGlobalConfig := ctx.SingletonForTests("platform_compat_config_singleton").Output("compat_config/merged_compat_config.xml")
diff --git a/java/jdeps.go b/java/jdeps.go
index 3400263..e856b37 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -89,7 +89,7 @@
 			dpInfo.Classes = append(dpInfo.Classes, data.Class)
 		}
 
-		if dep, ok := android.SingletonModuleProvider(ctx, module, JavaInfoProvider); ok {
+		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			dpInfo.Installed_paths = append(dpInfo.Installed_paths, dep.ImplementationJars.Strings()...)
 		}
 		dpInfo.Classes = android.FirstUniqueStrings(dpInfo.Classes)
diff --git a/java/jdeps_test.go b/java/jdeps_test.go
index e180224..ff54da9 100644
--- a/java/jdeps_test.go
+++ b/java/jdeps_test.go
@@ -91,15 +91,42 @@
 	}
 }
 
-func TestCollectJavaLibraryPropertiesAddJarjarRules(t *testing.T) {
-	expected := "Jarjar_rules.txt"
-	module := LibraryFactory().(*Library)
-	module.expandJarjarRules = android.PathForTesting(expected)
+func TestCollectJavaLibraryWithJarJarRules(t *testing.T) {
+	ctx, _ := testJava(t,
+		`
+		java_library {
+			name: "javalib",
+			srcs: ["foo.java"],
+			jarjar_rules: "jarjar_rules.txt",
+		}
+	`)
+	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
 	dpInfo := &android.IdeInfo{}
 
 	module.IDEInfo(dpInfo)
-
-	if dpInfo.Jarjar_rules[0] != expected {
-		t.Errorf("Library.IDEInfo() Jarjar_rules = %v, want %v", dpInfo.Jarjar_rules[0], expected)
+	android.AssertBoolEquals(t, "IdeInfo.Srcs of repackaged library should be empty", true, len(dpInfo.Srcs) == 0)
+	android.AssertStringEquals(t, "IdeInfo.Jar_rules of repackaged library should not be empty", "jarjar_rules.txt", dpInfo.Jarjar_rules[0])
+	if !android.SubstringInList(dpInfo.Jars, "soong/.intermediates/javalib/android_common/jarjar/turbine/javalib.jar") {
+		t.Errorf("IdeInfo.Jars of repackaged library should contain the output of jarjar-ing. All outputs: %v\n", dpInfo.Jars)
 	}
 }
+
+func TestCollectJavaLibraryLinkingAgainstVersionedSdk(t *testing.T) {
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		FixtureWithPrebuiltApis(map[string][]string{
+			"29": {},
+		})).RunTestWithBp(t,
+		`
+		java_library {
+			name: "javalib",
+			srcs: ["foo.java"],
+			sdk_version: "29",
+		}
+	`)
+	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	dpInfo := &android.IdeInfo{}
+
+	module.IDEInfo(dpInfo)
+	android.AssertStringListContains(t, "IdeInfo.Deps should contain versioned sdk module", dpInfo.Deps, "sdk_public_29_android")
+}
diff --git a/java/kotlin.go b/java/kotlin.go
index aa2db0e..c28bc3f 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -101,6 +101,10 @@
 		commonSrcFilesArg = "--common_srcs " + commonSrcsList.String()
 	}
 
+	classpathRspFile := android.PathForModuleOut(ctx, "kotlinc", "classpath.rsp")
+	android.WriteFileRule(ctx, classpathRspFile, strings.Join(flags.kotlincClasspath.Strings(), " "))
+	deps = append(deps, classpathRspFile)
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:           kotlinc,
 		Description:    "kotlinc",
@@ -109,7 +113,7 @@
 		Inputs:         srcFiles,
 		Implicits:      deps,
 		Args: map[string]string{
-			"classpath":         flags.kotlincClasspath.FormJavaClassPath(""),
+			"classpath":         classpathRspFile.String(),
 			"kotlincFlags":      flags.kotlincFlags,
 			"commonSrcFilesArg": commonSrcFilesArg,
 			"srcJars":           strings.Join(srcJars.Strings(), " "),
@@ -205,6 +209,10 @@
 	kotlinName := filepath.Join(ctx.ModuleDir(), ctx.ModuleSubDir(), ctx.ModuleName())
 	kotlinName = strings.ReplaceAll(kotlinName, "/", "__")
 
+	classpathRspFile := android.PathForModuleOut(ctx, "kapt", "classpath.rsp")
+	android.WriteFileRule(ctx, classpathRspFile, strings.Join(flags.kotlincClasspath.Strings(), "\n"))
+	deps = append(deps, classpathRspFile)
+
 	// First run kapt to generate .java stubs from .kt files
 	kaptStubsJar := android.PathForModuleOut(ctx, "kapt", "stubs.jar")
 	ctx.Build(pctx, android.BuildParams{
@@ -214,7 +222,7 @@
 		Inputs:      srcFiles,
 		Implicits:   deps,
 		Args: map[string]string{
-			"classpath":         flags.kotlincClasspath.FormJavaClassPath(""),
+			"classpath":         classpathRspFile.String(),
 			"kotlincFlags":      flags.kotlincFlags,
 			"commonSrcFilesArg": commonSrcFilesArg,
 			"srcJars":           strings.Join(srcJars.Strings(), " "),
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 933fc51..844e974 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"slices"
 	"strconv"
 	"strings"
 	"testing"
@@ -23,10 +24,11 @@
 )
 
 func TestKotlin(t *testing.T) {
-	ctx, _ := testJava(t, `
+	bp := `
 		java_library {
 			name: "foo",
 			srcs: ["a.java", "b.kt"],
+			static_libs: ["quz"],
 		}
 
 		java_library {
@@ -39,85 +41,156 @@
 		java_library {
 			name: "baz",
 			srcs: ["c.java"],
+			static_libs: ["quz"],
 		}
-		`)
 
-	kotlinStdlib := ctx.ModuleForTests("kotlin-stdlib", "android_common").
-		Output("turbine-combined/kotlin-stdlib.jar").Output
-	kotlinStdlibJdk7 := ctx.ModuleForTests("kotlin-stdlib-jdk7", "android_common").
-		Output("turbine-combined/kotlin-stdlib-jdk7.jar").Output
-	kotlinStdlibJdk8 := ctx.ModuleForTests("kotlin-stdlib-jdk8", "android_common").
-		Output("turbine-combined/kotlin-stdlib-jdk8.jar").Output
-	kotlinAnnotations := ctx.ModuleForTests("kotlin-annotations", "android_common").
-		Output("turbine-combined/kotlin-annotations.jar").Output
+		java_library {
+			name: "quz",
+			srcs: ["d.kt"],
+		}`
 
-	fooKotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
-	fooJavac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	fooJar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
-	fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar")
-
-	fooKotlincClasses := fooKotlinc.Output
-	fooKotlincHeaderClasses := fooKotlinc.ImplicitOutput
-
-	if len(fooKotlinc.Inputs) != 2 || fooKotlinc.Inputs[0].String() != "a.java" ||
-		fooKotlinc.Inputs[1].String() != "b.kt" {
-		t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, fooKotlinc.Inputs)
+	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",
 	}
 
-	if len(fooJavac.Inputs) != 1 || fooJavac.Inputs[0].String() != "a.java" {
-		t.Errorf(`foo inputs %v != ["a.java"]`, fooJavac.Inputs)
+	kotlinStdlibJavacJars := []string{
+		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/javac/kotlin-stdlib.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/javac/kotlin-stdlib-jdk7.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk8/android_common/javac/kotlin-stdlib-jdk8.jar",
+		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/javac/kotlin-annotations.jar",
 	}
 
-	if !strings.Contains(fooJavac.Args["classpath"], fooKotlincHeaderClasses.String()) {
-		t.Errorf("foo classpath %v does not contain %q",
-			fooJavac.Args["classpath"], fooKotlincHeaderClasses.String())
+	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",
 	}
 
-	if !inList(fooKotlincClasses.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %q",
-			fooJar.Inputs.Strings(), fooKotlincClasses.String())
+	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",
 	}
 
-	if !inList(kotlinStdlib.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinStdlib.String())
+	testCases := []struct {
+		name string
+
+		preparer android.FixturePreparer
+
+		fooKotlincInputs        []string
+		fooJavacInputs          []string
+		fooKotlincClasspath     []string
+		fooJavacClasspath       []string
+		fooCombinedInputs       []string
+		fooHeaderCombinedInputs []string
+
+		barKotlincInputs        []string
+		barKotlincClasspath     []string
+		barCombinedInputs       []string
+		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,
+			),
+		},
 	}
 
-	if !inList(kotlinStdlibJdk7.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinStdlibJdk7.String())
-	}
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				tt.preparer,
+			).RunTestWithBp(t, bp)
+			foo := result.ModuleForTests("foo", "android_common")
+			fooKotlinc := foo.Rule("kotlinc")
+			android.AssertPathsRelativeToTopEquals(t, "foo kotlinc inputs", tt.fooKotlincInputs, fooKotlinc.Inputs)
 
-	if !inList(kotlinStdlibJdk8.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinStdlibJdk8.String())
-	}
+			fooKotlincClasspath := android.ContentFromFileRuleForTests(t, result.TestContext, foo.Output("kotlinc/classpath.rsp"))
+			android.AssertStringPathsRelativeToTopEquals(t, "foo kotlinc classpath", result.Config, tt.fooKotlincClasspath, strings.Fields(fooKotlincClasspath))
 
-	if !inList(kotlinAnnotations.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinAnnotations.String())
-	}
+			fooJavac := foo.Rule("javac")
+			android.AssertPathsRelativeToTopEquals(t, "foo javac inputs", tt.fooJavacInputs, fooJavac.Inputs)
 
-	if !inList(fooKotlincHeaderClasses.String(), fooHeaderJar.Inputs.Strings()) {
-		t.Errorf("foo header jar inputs %v does not contain %q",
-			fooHeaderJar.Inputs.Strings(), fooKotlincHeaderClasses.String())
-	}
+			fooJavacClasspath := fooJavac.Args["classpath"]
+			android.AssertStringPathsRelativeToTopEquals(t, "foo javac classpath", result.Config, tt.fooJavacClasspath,
+				strings.Split(strings.TrimPrefix(fooJavacClasspath, "-classpath "), ":"))
 
-	bazHeaderJar := ctx.ModuleForTests("baz", "android_common").Output("turbine-combined/baz.jar")
-	barKotlinc := ctx.ModuleForTests("bar", "android_common").Rule("kotlinc")
+			fooCombinedJar := foo.Output("combined/foo.jar")
+			android.AssertPathsRelativeToTopEquals(t, "foo combined inputs", tt.fooCombinedInputs, fooCombinedJar.Inputs)
 
-	if len(barKotlinc.Inputs) != 1 || barKotlinc.Inputs[0].String() != "b.kt" {
-		t.Errorf(`bar kotlinc inputs %v != ["b.kt"]`, barKotlinc.Inputs)
-	}
+			fooCombinedHeaderJar := foo.Output("turbine-combined/foo.jar")
+			android.AssertPathsRelativeToTopEquals(t, "foo header combined inputs", tt.fooHeaderCombinedInputs, fooCombinedHeaderJar.Inputs)
 
-	if !inList(fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
-		t.Errorf(`expected %q in bar implicits %v`,
-			fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
-	}
+			bar := result.ModuleForTests("bar", "android_common")
+			barKotlinc := bar.Rule("kotlinc")
+			android.AssertPathsRelativeToTopEquals(t, "bar kotlinc inputs", tt.barKotlincInputs, barKotlinc.Inputs)
 
-	if !inList(bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
-		t.Errorf(`expected %q in bar implicits %v`,
-			bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
+			barKotlincClasspath := android.ContentFromFileRuleForTests(t, result.TestContext, bar.Output("kotlinc/classpath.rsp"))
+			android.AssertStringPathsRelativeToTopEquals(t, "bar kotlinc classpath", result.Config, tt.barKotlincClasspath, strings.Fields(barKotlincClasspath))
+
+			barCombinedJar := bar.Output("combined/bar.jar")
+			android.AssertPathsRelativeToTopEquals(t, "bar combined inputs", tt.barCombinedInputs, barCombinedJar.Inputs)
+
+			barCombinedHeaderJar := bar.Output("turbine-combined/bar.jar")
+			android.AssertPathsRelativeToTopEquals(t, "bar header combined inputs", tt.barHeaderCombinedInputs, barCombinedHeaderJar.Inputs)
+		})
 	}
 }
 
diff --git a/java/lint.go b/java/lint.go
index 2eea07d..6782adc 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -650,7 +650,7 @@
 		}
 
 		if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
-			apexInfo, _ := android.SingletonModuleProvider(ctx, m, android.ApexInfoProvider)
+			apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider)
 			if apexInfo.IsForPlatform() {
 				// There are stray platform variants of modules in apexes that are not available for
 				// the platform, and they sometimes can't be built.  Don't depend on them.
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 38553a6..d794e51 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -168,9 +168,10 @@
 
 	var transitiveSrcFiles android.Paths
 	for _, module := range allModules {
-		depInfo, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
-		if depInfo.TransitiveSrcFiles != nil {
-			transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...)
+		if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			if depInfo.TransitiveSrcFiles != nil {
+				transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...)
+			}
 		}
 	}
 	jarArgs := resourcePathsToJarArgs(transitiveSrcFiles)
diff --git a/java/ravenwood.go b/java/ravenwood.go
index 84c285c..bb136cf 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -143,6 +143,9 @@
 		HostTemplate:           "${RavenwoodTestConfigTemplate}",
 	})
 
+	// Always enable Ravenizer for ravenwood tests.
+	r.Library.ravenizer.enabled = true
+
 	r.Library.GenerateAndroidBuildActions(ctx)
 
 	// Start by depending on all files installed by dependencies
@@ -152,7 +155,8 @@
 	var runtimeJniModuleNames map[string]bool
 
 	if utils := ctx.GetDirectDepsWithTag(ravenwoodUtilsTag)[0]; utils != nil {
-		for _, installFile := range utils.FilesToInstall() {
+		for _, installFile := range android.OtherModuleProviderOrDefault(
+			ctx, utils, android.InstallFilesProvider).InstallFiles {
 			installDeps = append(installDeps, installFile)
 		}
 		jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
@@ -162,7 +166,8 @@
 	}
 
 	if runtime := ctx.GetDirectDepsWithTag(ravenwoodRuntimeTag)[0]; runtime != nil {
-		for _, installFile := range runtime.FilesToInstall() {
+		for _, installFile := range android.OtherModuleProviderOrDefault(
+			ctx, runtime, android.InstallFilesProvider).InstallFiles {
 			installDeps = append(installDeps, installFile)
 		}
 		jniDeps, ok := android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
@@ -191,7 +196,8 @@
 
 	resApkInstallPath := installPath.Join(ctx, "ravenwood-res-apks")
 	if resApk := ctx.GetDirectDepsWithTag(ravenwoodTestResourceApkTag); len(resApk) > 0 {
-		for _, installFile := range resApk[0].FilesToInstall() {
+		for _, installFile := range android.OtherModuleProviderOrDefault(
+			ctx, resApk[0], android.InstallFilesProvider).InstallFiles {
 			installResApk := ctx.InstallFile(resApkInstallPath, "ravenwood-res.apk", installFile)
 			installDeps = append(installDeps, installResApk)
 		}
@@ -285,6 +291,14 @@
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
 		libModule := ctx.GetDirectDepWithTag(lib, ravenwoodLibContentTag)
+		if libModule == nil {
+			if ctx.Config().AllowMissingDependencies() {
+				ctx.AddMissingDependencies([]string{lib})
+			} else {
+				ctx.PropertyErrorf("lib", "missing dependency %q", lib)
+			}
+			continue
+		}
 		libJar := android.OutputFileForModule(ctx, libModule, "")
 		ctx.InstallFile(installPath, lib+".jar", libJar)
 	}
diff --git a/java/robolectric.go b/java/robolectric.go
index 4cad5b1..26f4b71 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -214,12 +214,13 @@
 	}
 
 	handleLibDeps := func(dep android.Module, runtimeOnly bool) {
-		m, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
 		if !runtimeOnly {
 			r.libs = append(r.libs, ctx.OtherModuleName(dep))
 		}
 		if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) {
-			combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars...)
+			if m, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
+				combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars...)
+			}
 		}
 	}
 
diff --git a/java/sdk.go b/java/sdk.go
index 4ef4ee2..dd198ac 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -278,7 +278,7 @@
 
 	ctx.VisitAllModules(func(module android.Module) {
 		// Collect dex jar paths for the modules listed above.
-		if j, ok := android.SingletonModuleProvider(ctx, module, JavaInfoProvider); ok {
+		if j, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			name := ctx.ModuleName(module)
 			if i := android.IndexList(name, stubsModules); i != -1 {
 				stubsJars[i] = j.HeaderJars
diff --git a/java/sdk_library.go b/java/sdk_library.go
index c5f7a1c..4f95a99 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -20,7 +20,6 @@
 	"path"
 	"path/filepath"
 	"reflect"
-	"regexp"
 	"sort"
 	"strings"
 	"sync"
@@ -427,22 +426,10 @@
 		apiScopeModuleLib,
 		apiScopeSystemServer,
 	}
-	apiLibraryAdditionalProperties = map[string]struct {
-		FullApiSurfaceStubLib     string
-		AdditionalApiContribution string
-	}{
-		"legacy.i18n.module.platform.api": {
-			FullApiSurfaceStubLib:     "legacy.core.platform.api.stubs",
-			AdditionalApiContribution: "i18n.module.public.api.stubs.source.api.contribution",
-		},
-		"stable.i18n.module.platform.api": {
-			FullApiSurfaceStubLib:     "stable.core.platform.api.stubs",
-			AdditionalApiContribution: "i18n.module.public.api.stubs.source.api.contribution",
-		},
-		"conscrypt.module.platform.api": {
-			FullApiSurfaceStubLib:     "stable.core.platform.api.stubs",
-			AdditionalApiContribution: "conscrypt.module.public.api.stubs.source.api.contribution",
-		},
+	apiLibraryAdditionalProperties = map[string]string{
+		"legacy.i18n.module.platform.api": "i18n.module.public.api.stubs.source.api.contribution",
+		"stable.i18n.module.platform.api": "i18n.module.public.api.stubs.source.api.contribution",
+		"conscrypt.module.platform.api":   "conscrypt.module.public.api.stubs.source.api.contribution",
 	}
 )
 
@@ -650,13 +637,6 @@
 		Legacy_errors_allowed *bool
 	}
 
-	// Determines if the module contributes to any api surfaces.
-	// This property should be set to true only if the module is listed under
-	// frameworks-base-api.bootclasspath in frameworks/base/api/Android.bp.
-	// Otherwise, this property should be set to false.
-	// Defaults to false.
-	Contribute_to_android_api *bool
-
 	// a list of aconfig_declarations module names that the stubs generated in this module
 	// depend on.
 	Aconfig_declarations []string
@@ -1065,28 +1045,6 @@
 	annotationsComponentName = "annotations.zip"
 )
 
-// A regular expression to match tags that reference a specific stubs component.
-//
-// It will only match if given a valid scope and a valid component. It is verfy strict
-// to ensure it does not accidentally match a similar looking tag that should be processed
-// by the embedded Library.
-var tagSplitter = func() *regexp.Regexp {
-	// Given a list of literal string items returns a regular expression that will
-	// match any one of the items.
-	choice := func(items ...string) string {
-		return `\Q` + strings.Join(items, `\E|\Q`) + `\E`
-	}
-
-	// Regular expression to match one of the scopes.
-	scopesRegexp := choice(allScopeNames...)
-
-	// Regular expression to match one of the components.
-	componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName, annotationsComponentName)
-
-	// Regular expression to match any combination of one scope and one component.
-	return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp))
-}()
-
 func (module *commonToSdkLibraryAndImport) setOutputFiles(ctx android.ModuleContext) {
 	if module.doctagPaths != nil {
 		ctx.SetOutputFiles(module.doctagPaths, ".doctags")
@@ -1751,30 +1709,13 @@
 	return latestPrebuiltApiModuleName(module.distStem()+"-incompatibilities", apiScope)
 }
 
-func (module *SdkLibrary) contributesToApiSurface(c android.Config) bool {
-	_, exists := c.GetApiLibraries()[module.Name()]
-	return exists
-}
-
-// The listed modules are the special java_sdk_libraries where apiScope.kind do not match the
-// api surface that the module contribute to. For example, the public droidstubs and java_library
-// do not contribute to the public api surface, but contributes to the core platform api surface.
-// This method returns the full api surface stub lib that
-// the generated java_api_library should depend on.
-func (module *SdkLibrary) alternativeFullApiSurfaceStubLib() string {
-	if val, ok := apiLibraryAdditionalProperties[module.Name()]; ok {
-		return val.FullApiSurfaceStubLib
-	}
-	return ""
-}
-
 // The listed modules' stubs contents do not match the corresponding txt files,
 // but require additional api contributions to generate the full stubs.
 // This method returns the name of the additional api contribution module
 // for corresponding sdk_library modules.
 func (module *SdkLibrary) apiLibraryAdditionalApiContribution() string {
 	if val, ok := apiLibraryAdditionalProperties[module.Name()]; ok {
-		return val.AdditionalApiContribution
+		return val
 	}
 	return ""
 }
@@ -2069,17 +2010,18 @@
 	mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx)
 }
 
-func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope, alternativeFullApiSurfaceStub string) {
+func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
 	props := struct {
-		Name                  *string
-		Visibility            []string
-		Api_contributions     []string
-		Libs                  []string
-		Static_libs           []string
-		Full_api_surface_stub *string
-		System_modules        *string
-		Enable_validation     *bool
-		Stubs_type            *string
+		Name              *string
+		Visibility        []string
+		Api_contributions []string
+		Libs              []string
+		Static_libs       []string
+		System_modules    *string
+		Enable_validation *bool
+		Stubs_type        *string
+		Sdk_version       *string
+		Previous_api      *string
 	}{}
 
 	props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope))
@@ -2103,34 +2045,29 @@
 	}
 
 	props.Api_contributions = apiContributions
-	props.Libs = module.properties.Libs
+
+	// Ensure that stub-annotations is added to the classpath before any other libs
+	props.Libs = []string{"stub-annotations"}
+	props.Libs = append(props.Libs, module.properties.Libs...)
+	props.Libs = append(props.Libs, module.properties.Static_libs...)
 	props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...)
 	props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...)
-	props.Libs = append(props.Libs, "stub-annotations")
 	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
-	props.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName())
-	if alternativeFullApiSurfaceStub != "" {
-		props.Full_api_surface_stub = proptools.StringPtr(alternativeFullApiSurfaceStub)
-	}
-
-	// android_module_lib_stubs_current.from-text only comprises api contributions from art, conscrypt and i18n.
-	// Thus, replace with android_module_lib_stubs_current_full.from-text, which comprises every api domains.
-	if apiScope.kind == android.SdkModule {
-		props.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + "_full.from-text")
-	}
-
-	// java_sdk_library modules that set sdk_version as none does not depend on other api
-	// domains. Therefore, java_api_library created from such modules should not depend on
-	// full_api_surface_stubs but create and compile stubs by the java_api_library module
-	// itself.
-	if module.SdkVersion(mctx).Kind == android.SdkNone {
-		props.Full_api_surface_stub = nil
-	}
 
 	props.System_modules = module.deviceProperties.System_modules
 	props.Enable_validation = proptools.BoolPtr(true)
 	props.Stubs_type = proptools.StringPtr("everything")
 
+	if module.deviceProperties.Sdk_version != nil {
+		props.Sdk_version = module.deviceProperties.Sdk_version
+	}
+
+	if module.compareAgainstLatestApi(apiScope) {
+		// check against the latest released API
+		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
+		props.Previous_api = latestApiFilegroupName
+	}
+
 	mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
 }
 
@@ -2161,7 +2098,7 @@
 }
 
 func (module *SdkLibrary) createTopLevelStubsLibrary(
-	mctx android.DefaultableHookContext, apiScope *apiScope, contributesToApiSurface bool) {
+	mctx android.DefaultableHookContext, apiScope *apiScope) {
 
 	// Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false
 	doDist := !mctx.Config().ReleaseHiddenApiExportableStubs()
@@ -2170,7 +2107,7 @@
 
 	// Add the stub compiling java_library/java_api_library as static lib based on build config
 	staticLib := module.sourceStubsLibraryModuleName(apiScope)
-	if mctx.Config().BuildFromTextStub() && contributesToApiSurface {
+	if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
 		staticLib = module.apiLibraryModuleName(apiScope)
 	}
 	props.Static_libs = append(props.Static_libs, staticLib)
@@ -2213,8 +2150,8 @@
 	return module.uniqueApexVariations()
 }
 
-func (module *SdkLibrary) ContributeToApi() bool {
-	return proptools.BoolDefault(module.sdkLibraryProperties.Contribute_to_android_api, false)
+func (module *SdkLibrary) ModuleBuildFromTextStubs() bool {
+	return proptools.BoolDefault(module.sdkLibraryProperties.Build_from_text_stub, true)
 }
 
 // Creates the xml file that publicizes the runtime library
@@ -2390,16 +2327,10 @@
 		module.createStubsLibrary(mctx, scope)
 		module.createExportableStubsLibrary(mctx, scope)
 
-		alternativeFullApiSurfaceStubLib := ""
-		if scope == apiScopePublic {
-			alternativeFullApiSurfaceStubLib = module.alternativeFullApiSurfaceStubLib()
+		if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
+			module.createApiLibrary(mctx, scope)
 		}
-		contributesToApiSurface := module.contributesToApiSurface(mctx.Config()) || alternativeFullApiSurfaceStubLib != ""
-		if contributesToApiSurface {
-			module.createApiLibrary(mctx, scope, alternativeFullApiSurfaceStubLib)
-		}
-
-		module.createTopLevelStubsLibrary(mctx, scope, contributesToApiSurface)
+		module.createTopLevelStubsLibrary(mctx, scope)
 		module.createTopLevelExportableStubsLibrary(mctx, scope)
 	}
 
@@ -3671,3 +3602,19 @@
 		propertySet.AddProperty("doctag_files", dests)
 	}
 }
+
+// TODO(b/358613520): This can be removed when modules are no longer allowed to depend on the top-level library.
+func (s *SdkLibrary) IDEInfo(dpInfo *android.IdeInfo) {
+	s.Library.IDEInfo(dpInfo)
+	if s.implLibraryModule != nil {
+		dpInfo.Deps = append(dpInfo.Deps, s.implLibraryModule.Name())
+	} else {
+		// This java_sdk_library does not have an implementation (it sets `api_only` to true).
+		// Examples of this are `art.module.intra.core.api` (IntraCore api surface).
+		// Return the "public" stubs for these.
+		stubPaths := s.findClosestScopePath(apiScopePublic)
+		if len(stubPaths.stubsHeaderPath) > 0 {
+			dpInfo.Jars = append(dpInfo.Jars, stubPaths.stubsHeaderPath[0].String())
+		}
+	}
+}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index a8a1494..52d4af0 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -35,14 +35,7 @@
 			"29": {"foo"},
 			"30": {"bar", "barney", "baz", "betty", "foo", "fred", "quuz", "wilma"},
 		}),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		droiddoc_exported_dir {
 			name: "droiddoc-templates-sdk",
@@ -137,7 +130,7 @@
 	result.ModuleForTests("foo.api.system.28", "")
 	result.ModuleForTests("foo.api.test.28", "")
 
-	exportedComponentsInfo, _ := android.SingletonModuleProvider(result, foo.Module(), android.ExportedComponentsInfoProvider)
+	exportedComponentsInfo, _ := android.OtherModuleProvider(result, foo.Module(), android.ExportedComponentsInfoProvider)
 	expectedFooExportedComponents := []string{
 		"foo-removed.api.combined.public.latest",
 		"foo-removed.api.combined.system.latest",
@@ -540,11 +533,7 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
@@ -923,6 +912,7 @@
 	}
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`prebuilt_sdklib.stubs`,
 		`prebuilt_sdklib.stubs.source.test`,
@@ -936,11 +926,7 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
@@ -989,11 +975,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
 		preparer,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib",
@@ -1185,11 +1167,7 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib.source_preferred_using_legacy_flags", "sdklib.prebuilt_preferred_using_legacy_flags"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_mainline_module_contributions"),
 	).RunTestWithBp(t, bp)
 
 	// Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
@@ -1371,11 +1349,7 @@
 			"sdklib_group_foo",
 			"sdklib_owner_foo",
 			"foo"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "sdklib_no_group",
@@ -1588,9 +1562,6 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
@@ -1609,36 +1580,30 @@
 	`)
 
 	testCases := []struct {
-		scope              *apiScope
-		apiContributions   []string
-		fullApiSurfaceStub string
+		scope            *apiScope
+		apiContributions []string
 	}{
 		{
-			scope:              apiScopePublic,
-			apiContributions:   []string{"foo.stubs.source.api.contribution"},
-			fullApiSurfaceStub: "android_stubs_current",
+			scope:            apiScopePublic,
+			apiContributions: []string{"foo.stubs.source.api.contribution"},
 		},
 		{
-			scope:              apiScopeSystem,
-			apiContributions:   []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
-			fullApiSurfaceStub: "android_system_stubs_current",
+			scope:            apiScopeSystem,
+			apiContributions: []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
 		},
 		{
-			scope:              apiScopeTest,
-			apiContributions:   []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
-			fullApiSurfaceStub: "android_test_stubs_current",
+			scope:            apiScopeTest,
+			apiContributions: []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
 		},
 		{
-			scope:              apiScopeModuleLib,
-			apiContributions:   []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
-			fullApiSurfaceStub: "android_module_lib_stubs_current_full.from-text",
+			scope:            apiScopeModuleLib,
+			apiContributions: []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
 		},
 	}
 
 	for _, c := range testCases {
 		m := result.ModuleForTests(c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary)
 		android.AssertArrayString(t, "Module expected to contain api contributions", c.apiContributions, m.properties.Api_contributions)
-		android.AssertStringEquals(t, "Module expected to contain full api surface api library", c.fullApiSurfaceStub, *m.properties.Full_api_surface_stub)
 	}
 }
 
@@ -1708,9 +1673,6 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.SetApiLibraries([]string{"foo"})
-		}),
 	).RunTestWithBp(t, `
 		aconfig_declarations {
 			name: "bar",
@@ -1799,12 +1761,8 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				// We can use any of the apex contribution build flags from build/soong/android/config.go#mainlineApexContributionBuildFlags here
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
-			}
-		}),
+		// We can use any of the apex contribution build flags from build/soong/android/config.go#mainlineApexContributionBuildFlags here
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_mainline_module_contributions"),
 	)
 
 	result := fixture.RunTestWithBp(t, bp)
@@ -1887,11 +1845,7 @@
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("sdklib", "sdklib.v1", "sdklib.v2"),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_mainline_module_contributions"),
 	)
 
 	for _, tc := range testCases {
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 9e8ba6e..2dac27a 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -388,7 +388,9 @@
 		},
 	}
 
+	t.Parallel()
 	t.Run("basic", func(t *testing.T) {
+		t.Parallel()
 		testClasspathTestCases(t, classpathTestcases, false)
 	})
 
@@ -404,6 +406,7 @@
 		}
 
 		t.Run(testcase.name, func(t *testing.T) {
+			t.Parallel()
 			moduleType := "java_library"
 			if testcase.moduleType != "" {
 				moduleType = testcase.moduleType
diff --git a/java/system_modules.go b/java/system_modules.go
index 8e2d5d8..5b00079 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -120,14 +120,16 @@
 	return module
 }
 
-type SystemModulesProvider interface {
-	HeaderJars() android.Paths
-	OutputDirAndDeps() (android.Path, android.Paths)
+type SystemModulesProviderInfo struct {
+	// The aggregated header jars from all jars specified in the libs property.
+	// Used when system module is added as a dependency to bootclasspath.
+	HeaderJars android.Paths
+
+	OutputDir     android.Path
+	OutputDirDeps android.Paths
 }
 
-var _ SystemModulesProvider = (*SystemModules)(nil)
-
-var _ SystemModulesProvider = (*systemModulesImport)(nil)
+var SystemModulesProvider = blueprint.NewProvider[*SystemModulesProviderInfo]()
 
 type SystemModules struct {
 	android.ModuleBase
@@ -135,9 +137,6 @@
 
 	properties SystemModulesProperties
 
-	// The aggregated header jars from all jars specified in the libs property.
-	// Used when system module is added as a dependency to bootclasspath.
-	headerJars android.Paths
 	outputDir  android.Path
 	outputDeps android.Paths
 }
@@ -147,28 +146,22 @@
 	Libs []string
 }
 
-func (system *SystemModules) HeaderJars() android.Paths {
-	return system.headerJars
-}
-
-func (system *SystemModules) OutputDirAndDeps() (android.Path, android.Paths) {
-	if system.outputDir == nil || len(system.outputDeps) == 0 {
-		panic("Missing directory for system module dependency")
-	}
-	return system.outputDir, system.outputDeps
-}
-
 func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	var jars android.Paths
 
 	ctx.VisitDirectDepsWithTag(systemModulesLibsTag, func(module android.Module) {
-		dep, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
-		jars = append(jars, dep.HeaderJars...)
+		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			jars = append(jars, dep.HeaderJars...)
+		}
 	})
 
-	system.headerJars = jars
-
 	system.outputDir, system.outputDeps = TransformJarsToSystemModules(ctx, jars)
+
+	android.SetProvider(ctx, SystemModulesProvider, &SystemModulesProviderInfo{
+		HeaderJars:    jars,
+		OutputDir:     system.outputDir,
+		OutputDirDeps: system.outputDeps,
+	})
 }
 
 // ComponentDepsMutator is called before prebuilt modules without a corresponding source module are
@@ -310,3 +303,11 @@
 		propertySet.AddPropertyWithTag("libs", p.Libs, ctx.SnapshotBuilder().SdkMemberReferencePropertyTag(true))
 	}
 }
+
+// implement the following interface for IDE completion.
+var _ android.IDEInfo = (*SystemModules)(nil)
+
+func (s *SystemModules) IDEInfo(ideInfo *android.IdeInfo) {
+	ideInfo.Deps = append(ideInfo.Deps, s.properties.Libs...)
+	ideInfo.Libs = append(ideInfo.Libs, s.properties.Libs...)
+}
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index 336dd21..b05b0e4 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -25,7 +25,7 @@
 	paths := []string{}
 	for _, moduleName := range moduleNames {
 		module := result.Module(moduleName, "android_common")
-		info, _ := android.SingletonModuleProvider(result, module, JavaInfoProvider)
+		info, _ := android.OtherModuleProvider(result, module, JavaInfoProvider)
 		paths = append(paths, info.HeaderJars.RelativeToTop().Strings()...)
 	}
 	return paths
@@ -182,11 +182,7 @@
 	for _, tc := range testCases {
 		res := android.GroupFixturePreparers(
 			prepareForJavaTest,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"),
 		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
 		// check that rdep gets the correct variation of system_modules
diff --git a/java/test_spec_test.go b/java/test_spec_test.go
index 4144dad..f0a5fdb 100644
--- a/java/test_spec_test.go
+++ b/java/test_spec_test.go
@@ -32,7 +32,7 @@
 	module := result.ModuleForTests("module-name", "")
 
 	// Check that the provider has the right contents
-	data, _ := android.SingletonModuleProvider(result, module.Module(), soongTesting.TestSpecProviderKey)
+	data, _ := android.OtherModuleProvider(result, module.Module(), soongTesting.TestSpecProviderKey)
 	if !strings.HasSuffix(
 		data.IntermediatePath.String(), "/intermediateTestSpecMetadata.pb",
 	) {
diff --git a/java/testing.go b/java/testing.go
index 7a42e4c..0e85022 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -486,21 +486,17 @@
 	}
 
 	extraApiLibraryModules := map[string]droidstubsStruct{
-		"android_stubs_current.from-text":                  publicDroidstubs,
-		"android_system_stubs_current.from-text":           systemDroidstubs,
-		"android_test_stubs_current.from-text":             testDroidstubs,
-		"android_module_lib_stubs_current.from-text":       moduleLibDroidstubs,
-		"android_module_lib_stubs_current_full.from-text":  moduleLibDroidstubs,
-		"android_system_server_stubs_current.from-text":    systemServerDroidstubs,
-		"core.current.stubs.from-text":                     publicDroidstubs,
-		"legacy.core.platform.api.stubs.from-text":         publicDroidstubs,
-		"stable.core.platform.api.stubs.from-text":         publicDroidstubs,
-		"core-lambda-stubs.from-text":                      publicDroidstubs,
-		"android-non-updatable.stubs.from-text":            publicDroidstubs,
-		"android-non-updatable.stubs.system.from-text":     systemDroidstubs,
-		"android-non-updatable.stubs.test.from-text":       testDroidstubs,
-		"android-non-updatable.stubs.module_lib.from-text": moduleLibDroidstubs,
-		"android-non-updatable.stubs.test_module_lib":      moduleLibDroidstubs,
+		"android_stubs_current.from-text":                 publicDroidstubs,
+		"android_system_stubs_current.from-text":          systemDroidstubs,
+		"android_test_stubs_current.from-text":            testDroidstubs,
+		"android_module_lib_stubs_current.from-text":      moduleLibDroidstubs,
+		"android_module_lib_stubs_current_full.from-text": moduleLibDroidstubs,
+		"android_system_server_stubs_current.from-text":   systemServerDroidstubs,
+		"core.current.stubs.from-text":                    publicDroidstubs,
+		"legacy.core.platform.api.stubs.from-text":        publicDroidstubs,
+		"stable.core.platform.api.stubs.from-text":        publicDroidstubs,
+		"core-lambda-stubs.from-text":                     publicDroidstubs,
+		"android-non-updatable.stubs.test_module_lib":     moduleLibDroidstubs,
 	}
 
 	for _, droidstubs := range droidstubsStructs {
@@ -529,6 +525,8 @@
 			name: "%s",
 			api_contributions: ["%s"],
 			stubs_type: "everything",
+			sdk_version: "none",
+			system_modules: "none",
 		}
         `, libName, droidstubs.name+".api.contribution")
 	}
@@ -544,6 +542,16 @@
 			},
 			compile_dex: true,
 		}
+		java_library {
+			name: "framework-minus-apex",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "stable-core-platform-api-stubs-system-modules",
+			aidl: {
+				export_include_dirs: ["framework/aidl"],
+			},
+			compile_dex: true,
+		}
 
 		android_app {
 			name: "framework-res",
@@ -634,7 +642,7 @@
 func CheckClasspathFragmentProtoContentInfoProvider(t *testing.T, result *android.TestResult, generated bool, contents, outputFilename, installDir string) {
 	t.Helper()
 	p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
-	info, _ := android.SingletonModuleProvider(result, p, ClasspathFragmentProtoContentInfoProvider)
+	info, _ := android.OtherModuleProvider(result, p, ClasspathFragmentProtoContentInfoProvider)
 
 	android.AssertBoolEquals(t, "classpath proto generated", generated, info.ClasspathFragmentProtoGenerated)
 	android.AssertStringEquals(t, "classpath proto contents", contents, info.ClasspathFragmentProtoContents.String())
@@ -654,7 +662,7 @@
 func apexNamePairFromModule(ctx *android.TestContext, module android.Module) string {
 	name := module.Name()
 	var apex string
-	apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
+	apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
 	if apexInfo.IsForPlatform() {
 		apex = "platform"
 	} else {
diff --git a/kernel/prebuilt_kernel_modules_test.go b/kernel/prebuilt_kernel_modules_test.go
index 90b9886..7b81869 100644
--- a/kernel/prebuilt_kernel_modules_test.go
+++ b/kernel/prebuilt_kernel_modules_test.go
@@ -49,7 +49,8 @@
 	}
 
 	var actual []string
-	for _, ps := range ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().PackagingSpecs() {
+	for _, ps := range android.OtherModuleProviderOrDefault(
+		ctx, ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module(), android.InstallFilesProvider).PackagingSpecs {
 		actual = append(actual, ps.RelPathInPackage())
 	}
 	actual = android.SortedUniqueStrings(actual)
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 87c0814..533ec62 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -105,7 +105,8 @@
 	var provideLibs []string
 	for _, m := range provideModules {
 		if c, ok := m.(*cc.Module); ok && (cc.IsStubTarget(c) || c.HasLlndkStubs()) {
-			for _, ps := range c.PackagingSpecs() {
+			for _, ps := range android.OtherModuleProviderOrDefault(
+				ctx, c, android.InstallFilesProvider).PackagingSpecs {
 				provideLibs = append(provideLibs, ps.FileName())
 			}
 		}
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 78ab771..2e408b7 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -72,6 +72,7 @@
 	"add_soong_config_namespace":           &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
 	"add_soong_config_var_value":           &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
 	soongConfigAssign:                      &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+	"soong_config_set_bool":                &simpleCallParser{name: baseName + ".soong_config_set_bool", returnType: starlarkTypeVoid, addGlobals: true},
 	soongConfigAppend:                      &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
 	"soong_config_get":                     &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
 	"add-to-product-copy-files-if-exists":  &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList},
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 0c4d213..c295c40 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -853,6 +853,7 @@
 $(call add_soong_config_namespace,snsconfig)
 $(call add_soong_config_var_value,snsconfig,imagetype,odm_image)
 $(call soong_config_set, snsconfig, foo, foo_value)
+$(call soong_config_set_bool, snsconfig, bar, true)
 $(call soong_config_append, snsconfig, bar, bar_value)
 PRODUCT_COPY_FILES := $(call copy-files,$(wildcard foo*.mk),etc)
 PRODUCT_COPY_FILES := $(call product-copy-files-by-pattern,from/%,to/%,a b c)
@@ -880,6 +881,7 @@
   rblf.soong_config_namespace(g, "snsconfig")
   rblf.soong_config_set(g, "snsconfig", "imagetype", "odm_image")
   rblf.soong_config_set(g, "snsconfig", "foo", "foo_value")
+  rblf.soong_config_set_bool(g, "snsconfig", "bar", "true")
   rblf.soong_config_append(g, "snsconfig", "bar", "bar_value")
   cfg["PRODUCT_COPY_FILES"] = rblf.copy_files(rblf.expand_wildcard("foo*.mk"), "etc")
   cfg["PRODUCT_COPY_FILES"] = rblf.product_copy_files_by_pattern("from/%", "to/%", "a b c")
diff --git a/rust/bindgen.go b/rust/bindgen.go
index d412ea1..31aa137 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -364,7 +364,7 @@
 		ClangProperties:    cc.RustBindgenClangProperties{},
 	}
 
-	module := NewSourceProviderModule(hod, bindgen, false, true)
+	module := NewSourceProviderModule(hod, bindgen, false, false)
 
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
 		type stub_props struct {
@@ -393,8 +393,8 @@
 		deps.StaticLibs = append(deps.StaticLibs, String(b.Properties.Static_inline_library))
 	}
 
-	deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...)
-	deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs...)
-	deps.HeaderLibs = append(deps.HeaderLibs, b.ClangProperties.Header_libs...)
+	deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs.GetOrDefault(ctx, nil)...)
+	deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs.GetOrDefault(ctx, nil)...)
+	deps.HeaderLibs = append(deps.HeaderLibs, b.ClangProperties.Header_libs.GetOrDefault(ctx, nil)...)
 	return deps
 }
diff --git a/rust/config/darwin_host.go b/rust/config/darwin_host.go
index a4bc187..df8c6ac 100644
--- a/rust/config/darwin_host.go
+++ b/rust/config/darwin_host.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"runtime"
 	"strings"
 
 	"android/soong/android"
@@ -35,13 +36,15 @@
 	registerToolchainFactory(android.Darwin, android.Arm64, darwinArm64ToolchainFactory)
 	registerToolchainFactory(android.Darwin, android.X86_64, darwinX8664ToolchainFactory)
 
-	pctx.StaticVariable("DarwinToolchainRustFlags", strings.Join(DarwinRustFlags, " "))
-	pctx.StaticVariable("DarwinToolchainLinkFlags", strings.Join(DarwinRustLinkFlags, " "))
+	if runtime.GOOS == "darwin" {
+		pctx.StaticVariable("DarwinToolchainRustFlags", strings.Join(DarwinRustFlags, " "))
+		pctx.StaticVariable("DarwinToolchainLinkFlags", strings.Join(DarwinRustLinkFlags, " "))
 
-	pctx.StaticVariable("DarwinToolchainArm64RustFlags", strings.Join(darwinArm64Rustflags, " "))
-	pctx.StaticVariable("DarwinToolchainArm64LinkFlags", strings.Join(darwinArm64Linkflags, " "))
-	pctx.StaticVariable("DarwinToolchainX8664RustFlags", strings.Join(darwinX8664Rustflags, " "))
-	pctx.StaticVariable("DarwinToolchainX8664LinkFlags", strings.Join(darwinX8664Linkflags, " "))
+		pctx.StaticVariable("DarwinToolchainArm64RustFlags", strings.Join(darwinArm64Rustflags, " "))
+		pctx.StaticVariable("DarwinToolchainArm64LinkFlags", strings.Join(darwinArm64Linkflags, " "))
+		pctx.StaticVariable("DarwinToolchainX8664RustFlags", strings.Join(darwinX8664Rustflags, " "))
+		pctx.StaticVariable("DarwinToolchainX8664LinkFlags", strings.Join(darwinX8664Linkflags, " "))
+	}
 
 }
 
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 91aa195..3d81b83 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -306,12 +306,6 @@
 }
 
 python_binary_host {
-    name: "buildinfo",
-    main: "buildinfo.py",
-    srcs: ["buildinfo.py"],
-}
-
-python_binary_host {
     name: "extra_install_zips_file_list",
     main: "extra_install_zips_file_list.py",
     srcs: ["extra_install_zips_file_list.py"],
diff --git a/scripts/buildinfo.py b/scripts/buildinfo.py
deleted file mode 100755
index 8a24b63..0000000
--- a/scripts/buildinfo.py
+++ /dev/null
@@ -1,191 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2024 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-"""A tool for generating buildinfo.prop"""
-
-import argparse
-import contextlib
-import json
-import os
-import subprocess
-
-TEST_KEY_DIR = "build/make/target/product/security"
-
-def get_build_variant(product_config):
-  if product_config["Eng"]:
-    return "eng"
-  elif product_config["Debuggable"]:
-    return "userdebug"
-  else:
-    return "user"
-
-def get_build_flavor(product_config):
-  build_flavor = product_config["DeviceProduct"] + "-" + get_build_variant(product_config)
-  if "address" in product_config.get("SanitizeDevice", []) and "_asan" not in build_flavor:
-    build_flavor += "_asan"
-  return build_flavor
-
-def get_build_keys(product_config):
-  default_cert = product_config.get("DefaultAppCertificate", "")
-  if default_cert == "" or default_cert == os.path.join(TEST_KEY_DIR, "testKey"):
-    return "test-keys"
-  return "dev-keys"
-
-def parse_args():
-  """Parse commandline arguments."""
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--build-hostname-file', required=True, type=argparse.FileType('r')),
-  parser.add_argument('--build-number-file', required=True, type=argparse.FileType('r'))
-  parser.add_argument('--build-thumbprint-file', type=argparse.FileType('r'))
-  parser.add_argument('--build-username', required=True)
-  parser.add_argument('--date-file', required=True, type=argparse.FileType('r'))
-  parser.add_argument('--platform-preview-sdk-fingerprint-file',
-                      required=True,
-                      type=argparse.FileType('r'))
-  parser.add_argument('--product-config', required=True, type=argparse.FileType('r'))
-  parser.add_argument('--out', required=True, type=argparse.FileType('w'))
-
-  option = parser.parse_args()
-
-  product_config = json.load(option.product_config)
-  build_flags = product_config["BuildFlags"]
-
-  option.build_flavor = get_build_flavor(product_config)
-  option.build_keys = get_build_keys(product_config)
-  option.build_id = product_config["BuildId"]
-  option.build_type = product_config["BuildType"]
-  option.build_variant = get_build_variant(product_config)
-  option.build_version_tags = product_config["BuildVersionTags"]
-  option.cpu_abis = product_config["DeviceAbi"]
-  option.default_locale = None
-  if len(product_config.get("ProductLocales", [])) > 0:
-    option.default_locale = product_config["ProductLocales"][0]
-  option.default_wifi_channels = product_config.get("ProductDefaultWifiChannels", [])
-  option.device = product_config["DeviceName"]
-  option.display_build_number = product_config["DisplayBuildNumber"]
-  option.platform_base_os = product_config["Platform_base_os"]
-  option.platform_display_version = product_config["Platform_display_version_name"]
-  option.platform_min_supported_target_sdk_version = build_flags["RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION"]
-  option.platform_preview_sdk_version = product_config["Platform_preview_sdk_version"]
-  option.platform_sdk_version = product_config["Platform_sdk_version"]
-  option.platform_security_patch = product_config["Platform_security_patch"]
-  option.platform_version = product_config["Platform_version_name"]
-  option.platform_version_codename = product_config["Platform_sdk_codename"]
-  option.platform_version_all_codenames = product_config["Platform_version_active_codenames"]
-  option.platform_version_known_codenames = product_config["Platform_version_known_codenames"]
-  option.platform_version_last_stable = product_config["Platform_version_last_stable"]
-  option.product = product_config["DeviceProduct"]
-  option.use_vbmeta_digest_in_fingerprint = product_config["BoardUseVbmetaDigestInFingerprint"]
-
-  return option
-
-def main():
-  option = parse_args()
-
-  build_hostname = option.build_hostname_file.read().strip()
-  build_number = option.build_number_file.read().strip()
-  build_version_tags_list = option.build_version_tags
-  if option.build_type == "debug":
-    build_version_tags_list.append("debug")
-  build_version_tags_list.append(option.build_keys)
-  build_version_tags = ",".join(sorted(set(build_version_tags_list)))
-
-  raw_date = option.date_file.read().strip()
-  date = subprocess.check_output(["date", "-d", f"@{raw_date}"], text=True).strip()
-  date_utc = subprocess.check_output(["date", "-d", f"@{raw_date}", "+%s"], text=True).strip()
-
-  # build_desc is human readable strings that describe this build. This has the same info as the
-  # build fingerprint.
-  # e.g. "aosp_cf_x86_64_phone-userdebug VanillaIceCream MAIN eng.20240319.143939 test-keys"
-  build_desc = f"{option.product}-{option.build_variant} {option.platform_version} " \
-               f"{option.build_id} {build_number} {build_version_tags}"
-
-  platform_preview_sdk_fingerprint = option.platform_preview_sdk_fingerprint_file.read().strip()
-
-  with contextlib.redirect_stdout(option.out):
-    print("# begin build properties")
-    print("# autogenerated by buildinfo.py")
-
-    # The ro.build.id will be set dynamically by init, by appending the unique vbmeta digest.
-    if option.use_vbmeta_digest_in_fingerprint:
-      print(f"ro.build.legacy.id={option.build_id}")
-    else:
-      print(f"ro.build.id?={option.build_id}")
-
-    # ro.build.display.id is shown under Settings -> About Phone
-    if option.build_variant == "user":
-      # User builds should show:
-      # release build number or branch.buld_number non-release builds
-
-      # Dev. branches should have DISPLAY_BUILD_NUMBER set
-      if option.display_build_number:
-        print(f"ro.build.display.id?={option.build_id}.{build_number} {option.build_keys}")
-      else:
-        print(f"ro.build.display.id?={option.build_id} {option.build_keys}")
-    else:
-      # Non-user builds should show detailed build information (See build desc above)
-      print(f"ro.build.display.id?={build_desc}")
-    print(f"ro.build.version.incremental={build_number}")
-    print(f"ro.build.version.sdk={option.platform_sdk_version}")
-    print(f"ro.build.version.preview_sdk={option.platform_preview_sdk_version}")
-    print(f"ro.build.version.preview_sdk_fingerprint={platform_preview_sdk_fingerprint}")
-    print(f"ro.build.version.codename={option.platform_version_codename}")
-    print(f"ro.build.version.all_codenames={','.join(option.platform_version_all_codenames)}")
-    print(f"ro.build.version.known_codenames={option.platform_version_known_codenames}")
-    print(f"ro.build.version.release={option.platform_version_last_stable}")
-    print(f"ro.build.version.release_or_codename={option.platform_version}")
-    print(f"ro.build.version.release_or_preview_display={option.platform_display_version}")
-    print(f"ro.build.version.security_patch={option.platform_security_patch}")
-    print(f"ro.build.version.base_os={option.platform_base_os}")
-    print(f"ro.build.version.min_supported_target_sdk={option.platform_min_supported_target_sdk_version}")
-    print(f"ro.build.date={date}")
-    print(f"ro.build.date.utc={date_utc}")
-    print(f"ro.build.type={option.build_variant}")
-    print(f"ro.build.user={option.build_username}")
-    print(f"ro.build.host={build_hostname}")
-    # TODO: Remove any tag-related optional property declarations once the goals
-    # from go/arc-android-sigprop-changes have been achieved.
-    print(f"ro.build.tags?={build_version_tags}")
-    # ro.build.flavor are used only by the test harness to distinguish builds.
-    # Only add _asan for a sanitized build if it isn't already a part of the
-    # flavor (via a dedicated lunch config for example).
-    print(f"ro.build.flavor={option.build_flavor}")
-
-    # These values are deprecated, use "ro.product.cpu.abilist"
-    # instead (see below).
-    print(f"# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,")
-    print(f"# use ro.product.cpu.abilist instead.")
-    print(f"ro.product.cpu.abi={option.cpu_abis[0]}")
-    if len(option.cpu_abis) > 1:
-      print(f"ro.product.cpu.abi2={option.cpu_abis[1]}")
-
-    if option.default_locale:
-      print(f"ro.product.locale={option.default_locale}")
-    print(f"ro.wifi.channels={' '.join(option.default_wifi_channels)}")
-
-    print(f"# ro.build.product is obsolete; use ro.product.device")
-    print(f"ro.build.product={option.device}")
-
-    print(f"# Do not try to parse description or thumbprint")
-    print(f"ro.build.description?={build_desc}")
-    if option.build_thumbprint_file:
-      build_thumbprint = option.build_thumbprint_file.read().strip()
-      print(f"ro.build.thumbprint={build_thumbprint}")
-
-    print(f"# end build properties")
-
-if __name__ == "__main__":
-  main()
diff --git a/scripts/gen-kotlin-build-file.py b/scripts/gen-kotlin-build-file.py
index 99afdca..8b7876f 100644
--- a/scripts/gen-kotlin-build-file.py
+++ b/scripts/gen-kotlin-build-file.py
@@ -37,7 +37,7 @@
   parser.add_argument('--out', dest='out',
                       help='file to which the module.xml contents will be written.')
   parser.add_argument('--classpath', dest='classpath', action='append', default=[],
-                      help='classpath to pass to kotlinc.')
+                      help='file containing classpath to pass to kotlinc.')
   parser.add_argument('--name', dest='name',
                       help='name of the module.')
   parser.add_argument('--out_dir', dest='out_dir',
@@ -65,8 +65,8 @@
     f.write('  <module name="%s" type="java-production" outputDir="%s">\n' % (args.name, args.out_dir or ''))
 
     # Print classpath entries
-    for c in args.classpath:
-      for entry in c.split(':'):
+    for classpath_rsp_file in args.classpath:
+      for entry in NinjaRspFileReader(classpath_rsp_file):
         path = os.path.abspath(entry)
         f.write('    <classpath path="%s"/>\n' % path)
 
diff --git a/scripts/gen_build_prop.py b/scripts/gen_build_prop.py
index 9ea56cb..c08a3fd 100644
--- a/scripts/gen_build_prop.py
+++ b/scripts/gen_build_prop.py
@@ -129,16 +129,16 @@
     print(f"ro.product.{partition}.name={config['DeviceProduct']}")
 
   if partition != "system":
-    if config["ModelForAttestation"]:
-        print(f"ro.product.model_for_attestation={config['ModelForAttestation']}")
-    if config["BrandForAttestation"]:
-        print(f"ro.product.brand_for_attestation={config['BrandForAttestation']}")
-    if config["NameForAttestation"]:
-        print(f"ro.product.name_for_attestation={config['NameForAttestation']}")
-    if config["DeviceForAttestation"]:
-        print(f"ro.product.device_for_attestation={config['DeviceForAttestation']}")
-    if config["ManufacturerForAttestation"]:
-        print(f"ro.product.manufacturer_for_attestation={config['ManufacturerForAttestation']}")
+    if config["ProductModelForAttestation"]:
+        print(f"ro.product.model_for_attestation={config['ProductModelForAttestation']}")
+    if config["ProductBrandForAttestation"]:
+        print(f"ro.product.brand_for_attestation={config['ProductBrandForAttestation']}")
+    if config["ProductNameForAttestation"]:
+        print(f"ro.product.name_for_attestation={config['ProductNameForAttestation']}")
+    if config["ProductDeviceForAttestation"]:
+        print(f"ro.product.device_for_attestation={config['ProductDeviceForAttestation']}")
+    if config["ProductManufacturerForAttestation"]:
+        print(f"ro.product.manufacturer_for_attestation={config['ProductManufacturerForAttestation']}")
 
   if config["ZygoteForce64"]:
     if partition == "vendor":
@@ -237,7 +237,7 @@
 
   print(f"# Do not try to parse description or thumbprint")
   print(f"ro.build.description?={config['BuildDesc']}")
-  if "build_thumbprint" in config:
+  if "BuildThumbprint" in config:
     print(f"ro.build.thumbprint={config['BuildThumbprint']}")
 
   print(f"# end build properties")
@@ -279,7 +279,7 @@
   config = args.config
 
   # Add the product-defined properties to the build properties.
-  if config["PropertySplitEnabled"] or config["VendorImageFileSystemType"]:
+  if not config["PropertySplitEnabled"] or not config["VendorImageFileSystemType"]:
     if "PRODUCT_PROPERTY_OVERRIDES" in config:
       props += config["PRODUCT_PROPERTY_OVERRIDES"]
 
@@ -311,6 +311,7 @@
   props.append("ro.postinstall.fstab.prefix=/system")
 
   enable_target_debugging = True
+  enable_dalvik_lock_contention_logging = True
   if config["BuildVariant"] == "user" or config["BuildVariant"] == "userdebug":
     # Target is secure in user builds.
     props.append("ro.secure=1")
@@ -320,6 +321,12 @@
       # Disable debugging in plain user builds.
       props.append("ro.adb.secure=1")
       enable_target_debugging = False
+      enable_dalvik_lock_contention_logging = False
+    else:
+      # Disable debugging in userdebug builds if PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG
+      # is set.
+      if config["ProductNotDebuggableInUserdebug"]:
+        enable_target_debugging = False
 
     # Disallow mock locations by default for user builds
     props.append("ro.allow.mock.location=0")
@@ -331,10 +338,11 @@
     # Allow mock locations by default for non user builds
     props.append("ro.allow.mock.location=1")
 
-  if enable_target_debugging:
+  if enable_dalvik_lock_contention_logging:
     # Enable Dalvik lock contention logging.
     props.append("dalvik.vm.lockprof.threshold=500")
 
+  if enable_target_debugging:
     # Target is more debuggable and adbd is on by default
     props.append("ro.debuggable=1")
   else:
@@ -416,7 +424,7 @@
   # This must not be defined for the non-GRF devices.
   # The values of the GRF properties will be verified by post_process_props.py
   if config["BoardShippingApiLevel"]:
-    props.append(f"ro.board.first_api_level={config['ProductShippingApiLevel']}")
+    props.append(f"ro.board.first_api_level={config['BoardShippingApiLevel']}")
 
   # Build system set BOARD_API_LEVEL to show the api level of the vendor API surface.
   # This must not be altered outside of build system.
@@ -464,6 +472,8 @@
   # Add the 16K developer args if it is defined for the product.
   props.append(f"ro.product.build.16k_page.enabled={'true' if config['Product16KDeveloperOption'] else 'false'}")
 
+  props.append(f"ro.product.page_size={16384 if config['TargetBoots16K'] else 4096}")
+
   props.append(f"ro.build.characteristics={config['AAPTCharacteristics']}")
 
   if "AbOtaUpdater" in config and config["AbOtaUpdater"]:
@@ -475,6 +485,9 @@
   if config["NoBionicPageSizeMacro"]:
     props.append(f"ro.product.build.no_bionic_page_size_macro=true")
 
+  # This is a temporary system property that controls the ART module. The plan is
+  # to remove it by Aug 2025, at which time Mainline updates of the ART module
+  # will ignore it as well.
   # If the value is "default", it will be mangled by post_process_props.py.
   props.append(f"ro.dalvik.vm.enable_uffd_gc={config['EnableUffdGc']}")
 
@@ -500,6 +513,15 @@
 
   build_prop(args, gen_build_info=True, gen_common_build_props=True, variables=variables)
 
+def build_system_ext_prop(args):
+  config = args.config
+
+  # Order matters here. When there are duplicates, the last one wins.
+  # TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter
+  variables = ["PRODUCT_SYSTEM_EXT_PROPERTIES"]
+
+  build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
+
 '''
 def build_vendor_prop(args):
   config = args.config
@@ -517,6 +539,7 @@
     ]
 
   build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
+'''
 
 def build_product_prop(args):
   config = args.config
@@ -527,8 +550,32 @@
     "ADDITIONAL_PRODUCT_PROPERTIES",
     "PRODUCT_PRODUCT_PROPERTIES",
   ]
+
+  gen_common_build_props = True
+
+  # Skip common /product properties generation if device released before R and
+  # has no product partition. This is the first part of the check.
+  if config["Shipping_api_level"] and int(config["Shipping_api_level"]) < 30:
+    gen_common_build_props = False
+
+  # The second part of the check - always generate common properties for the
+  # devices with product partition regardless of shipping level.
+  if config["UsesProductImage"]:
+    gen_common_build_props = True
+
   build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
-'''
+
+  if config["OemProperties"]:
+    print("####################################")
+    print("# PRODUCT_OEM_PROPERTIES")
+    print("####################################")
+
+    for prop in config["OemProperties"]:
+      print(f"import /oem/oem.prop {prop}")
+
+def build_odm_prop(args):
+  variables = ["ADDITIONAL_ODM_PROPERTIES", "PRODUCT_ODM_PROPERTIES"]
+  build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
 
 def build_prop(args, gen_build_info, gen_common_build_props, variables):
   config = args.config
@@ -550,16 +597,19 @@
   args = parse_args()
 
   with contextlib.redirect_stdout(args.out):
-    if args.partition == "system":
-      build_system_prop(args)
-      '''
-    elif args.partition == "vendor":
-      build_vendor_prop(args)
-    elif args.partition == "product":
-      build_product_prop(args)
-      '''
-    else:
-      sys.exit(f"not supported partition {args.partition}")
+    match args.partition:
+      case "system":
+        build_system_prop(args)
+      case "system_ext":
+        build_system_ext_prop(args)
+      case "odm":
+        build_odm_prop(args)
+      case "product":
+        build_product_prop(args)
+      # case "vendor":  # NOT IMPLEMENTED
+      #  build_vendor_prop(args)
+      case _:
+        sys.exit(f"not supported partition {args.partition}")
 
 if __name__ == "__main__":
   main()
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 7048a15..7ee548f 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -276,11 +276,7 @@
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
 
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 		// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
 		// file creation.
 		java.FixtureConfigureBootJars("platform:foo"),
@@ -799,11 +795,7 @@
 		// Add a platform_bootclasspath that depends on the fragment.
 		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
 
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 
 		android.MockFS{
 			"my-blocked.txt":                   nil,
@@ -1053,11 +1045,7 @@
 			variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
 		}),
 
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 
 		android.FixtureWithRootAndroidBp(`
 			sdk {
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 0a5483b..0fb5c70 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -45,11 +45,7 @@
 	java.PrepareForTestWithJavaDefaultModules,
 	java.PrepareForTestWithJavaSdkLibraryFiles,
 	java.FixtureWithLastReleaseApis("myjavalib"),
-	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.BuildFlags = map[string]string{
-			"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-		}
-	}),
+	android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 )
 
 // Contains tests for SDK members provided by the java package.
@@ -666,11 +662,7 @@
 			"1": {"myjavalib"},
 			"2": {"myjavalib"},
 		}),
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
@@ -1313,11 +1305,7 @@
 func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJavaSdkLibrary,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.BuildFlags = map[string]string{
-				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-			}
-		}),
+		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 4894210..057b370 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -457,11 +457,7 @@
 			android.FixtureMergeEnv(map[string]string{
 				"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
 			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-				}
-			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 		).RunTest(t)
 
 		CheckSnapshot(t, result, "mysdk", "",
@@ -573,11 +569,9 @@
 				"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
 			}),
 			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.BuildFlags = map[string]string{
-					"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
-				}
 				variables.Platform_version_active_codenames = []string{"UpsideDownCake", "Tiramisu", "S-V2"}
 			}),
+			android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
 		).RunTest(t)
 
 		CheckSnapshot(t, result, "mysdk", "",
diff --git a/sdk/update.go b/sdk/update.go
index 198c8d4..a4b1967 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -1989,6 +1989,14 @@
 	return m.builder.targetBuildRelease.EarlierThan(buildReleaseT)
 }
 
+func (m *memberContext) Config() android.Config {
+	return m.sdkMemberContext.Config()
+}
+
+func (m *memberContext) OtherModulePropertyErrorf(module android.Module, property string, fmt string, args ...interface{}) {
+	m.sdkMemberContext.OtherModulePropertyErrorf(module, property, fmt, args)
+}
+
 var _ android.SdkMemberContext = (*memberContext)(nil)
 
 func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule *bpModule) {
diff --git a/snapshot/Android.bp b/snapshot/Android.bp
index c384f8a..ae1869a 100644
--- a/snapshot/Android.bp
+++ b/snapshot/Android.bp
@@ -14,12 +14,8 @@
     // Source file name convention is to include _snapshot as a
     // file suffix for files that are generating snapshots.
     srcs: [
-        "host_snapshot.go",
         "snapshot_base.go",
         "util.go",
     ],
-    testSrcs: [
-        "host_test.go",
-    ],
     pluginFor: ["soong_build"],
 }
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
deleted file mode 100644
index 1ecab7d..0000000
--- a/snapshot/host_snapshot.go
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package snapshot
-
-import (
-	"encoding/json"
-	"fmt"
-	"path/filepath"
-	"sort"
-	"strings"
-
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
-
-	"android/soong/android"
-)
-
-//
-// The host_snapshot module creates a snapshot of the modules defined in
-// the deps property.  The modules within the deps property (host tools)
-// are ones that return a valid path via HostToolPath() of the
-// HostToolProvider.  The created snapshot contains the binaries and any
-// transitive PackagingSpecs of the included host tools, along with a JSON
-// meta file.
-//
-// The snapshot is installed into a source tree via
-// development/vendor_snapshot/update.py, the included modules are
-// provided as preferred prebuilts.
-//
-// To determine which tools to include in the host snapshot see
-// host_fake_snapshot.go.
-
-func init() {
-	registerHostBuildComponents(android.InitRegistrationContext)
-}
-
-func registerHostBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("host_snapshot", hostSnapshotFactory)
-}
-
-// Relative installation path
-type RelativeInstallPath interface {
-	RelativeInstallPath() string
-}
-
-type hostSnapshot struct {
-	android.ModuleBase
-	android.PackagingBase
-
-	outputFile android.OutputPath
-	installDir android.InstallPath
-}
-
-type ProcMacro interface {
-	ProcMacro() bool
-	CrateName() string
-}
-
-func hostSnapshotFactory() android.Module {
-	module := &hostSnapshot{}
-	initHostToolsModule(module)
-	return module
-}
-func initHostToolsModule(module *hostSnapshot) {
-	android.InitPackageModule(module)
-	android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon)
-}
-
-var dependencyTag = struct {
-	blueprint.BaseDependencyTag
-	android.InstallAlwaysNeededDependencyTag
-	android.PackagingItemAlwaysDepTag
-}{}
-
-func (f *hostSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
-	f.AddDeps(ctx, dependencyTag)
-}
-func (f *hostSnapshot) installFileName() string {
-	return f.Name() + ".zip"
-}
-
-// Create zipfile with JSON description, notice files... for dependent modules
-func (f *hostSnapshot) CreateMetaData(ctx android.ModuleContext, fileName string) android.OutputPath {
-	var jsonData []SnapshotJsonFlags
-	var metaPaths android.Paths
-
-	installedNotices := make(map[string]bool)
-	metaZipFile := android.PathForModuleOut(ctx, fileName).OutputPath
-
-	// Create JSON file based on the direct dependencies
-	ctx.VisitDirectDeps(func(dep android.Module) {
-		desc := hostJsonDesc(ctx, dep)
-		if desc != nil {
-			jsonData = append(jsonData, *desc)
-		}
-		for _, notice := range dep.EffectiveLicenseFiles() {
-			if _, ok := installedNotices[notice.String()]; !ok {
-				installedNotices[notice.String()] = true
-				noticeOut := android.PathForModuleOut(ctx, "NOTICE_FILES", notice.String()).OutputPath
-				CopyFileToOutputPathRule(pctx, ctx, notice, noticeOut)
-				metaPaths = append(metaPaths, noticeOut)
-			}
-		}
-	})
-	// Sort notice paths and json data for repeatble build
-	sort.Slice(jsonData, func(i, j int) bool {
-		return (jsonData[i].ModuleName < jsonData[j].ModuleName)
-	})
-	sort.Slice(metaPaths, func(i, j int) bool {
-		return (metaPaths[i].String() < metaPaths[j].String())
-	})
-
-	marsh, err := json.Marshal(jsonData)
-	if err != nil {
-		ctx.ModuleErrorf("host snapshot json marshal failure: %#v", err)
-		return android.OutputPath{}
-	}
-
-	jsonZipFile := android.PathForModuleOut(ctx, "host_snapshot.json").OutputPath
-	metaPaths = append(metaPaths, jsonZipFile)
-	rspFile := android.PathForModuleOut(ctx, "host_snapshot.rsp").OutputPath
-	android.WriteFileRule(ctx, jsonZipFile, string(marsh))
-
-	builder := android.NewRuleBuilder(pctx, ctx)
-
-	builder.Command().
-		BuiltTool("soong_zip").
-		FlagWithArg("-C ", android.PathForModuleOut(ctx).OutputPath.String()).
-		FlagWithOutput("-o ", metaZipFile).
-		FlagWithRspFileInputList("-r ", rspFile, metaPaths)
-	builder.Build("zip_meta", fmt.Sprintf("zipping meta data for %s", ctx.ModuleName()))
-
-	return metaZipFile
-}
-
-// Create the host tool zip file
-func (f *hostSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Create a zip file for the binaries, and a zip of the meta data, then merge zips
-	depsZipFile := android.PathForModuleOut(ctx, f.Name()+"_deps.zip").OutputPath
-	modsZipFile := android.PathForModuleOut(ctx, f.Name()+"_mods.zip").OutputPath
-	f.outputFile = android.PathForModuleOut(ctx, f.installFileName()).OutputPath
-
-	f.installDir = android.PathForModuleInstall(ctx)
-
-	f.CopyDepsToZip(ctx, f.GatherPackagingSpecs(ctx), depsZipFile)
-
-	builder := android.NewRuleBuilder(pctx, ctx)
-	builder.Command().
-		BuiltTool("zip2zip").
-		FlagWithInput("-i ", depsZipFile).
-		FlagWithOutput("-o ", modsZipFile).
-		Text("**/*:" + proptools.ShellEscape(f.installDir.String()))
-
-	metaZipFile := f.CreateMetaData(ctx, f.Name()+"_meta.zip")
-
-	builder.Command().
-		BuiltTool("merge_zips").
-		Output(f.outputFile).
-		Input(metaZipFile).
-		Input(modsZipFile)
-
-	builder.Build("manifest", fmt.Sprintf("Adding manifest %s", f.installFileName()))
-	ctx.InstallFile(f.installDir, f.installFileName(), f.outputFile)
-
-}
-
-// Implements android.AndroidMkEntriesProvider
-func (f *hostSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(f.outputFile),
-		DistFiles:  android.MakeDefaultDistFiles(f.outputFile),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", f.installDir.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
-			},
-		},
-	}}
-}
-
-// Get host tools path and relative install string helpers
-func hostToolPath(m android.Module) android.OptionalPath {
-	if provider, ok := m.(android.HostToolProvider); ok {
-		return provider.HostToolPath()
-	}
-	return android.OptionalPath{}
-
-}
-func hostRelativePathString(m android.Module) string {
-	var outString string
-	if rel, ok := m.(RelativeInstallPath); ok {
-		outString = rel.RelativeInstallPath()
-	}
-	return outString
-}
-
-// Create JSON description for given module, only create descriptions for binary modules
-// and rust_proc_macro modules which provide a valid HostToolPath
-func hostJsonDesc(ctx android.ConfigAndErrorContext, m android.Module) *SnapshotJsonFlags {
-	path := hostToolPath(m)
-	relPath := hostRelativePathString(m)
-	procMacro := false
-	moduleStem := filepath.Base(path.String())
-	crateName := ""
-
-	if pm, ok := m.(ProcMacro); ok && pm.ProcMacro() {
-		procMacro = pm.ProcMacro()
-		moduleStem = strings.TrimSuffix(moduleStem, filepath.Ext(moduleStem))
-		crateName = pm.CrateName()
-	}
-
-	if path.Valid() && path.String() != "" {
-		props := &SnapshotJsonFlags{
-			ModuleStemName:      moduleStem,
-			Filename:            path.String(),
-			Required:            append(m.HostRequiredModuleNames(), m.RequiredModuleNames(ctx)...),
-			RelativeInstallPath: relPath,
-			RustProcMacro:       procMacro,
-			CrateName:           crateName,
-		}
-		props.InitBaseSnapshotProps(m)
-		return props
-	}
-	return nil
-}
diff --git a/snapshot/host_test.go b/snapshot/host_test.go
deleted file mode 100644
index c68fdaf..0000000
--- a/snapshot/host_test.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package snapshot
-
-import (
-	"path/filepath"
-	"testing"
-
-	"android/soong/android"
-)
-
-// host_snapshot and host-fake-snapshot test functions
-
-type hostTestModule struct {
-	android.ModuleBase
-	props struct {
-		Deps []string
-	}
-}
-
-func hostTestBinOut(bin string) string {
-	return filepath.Join("out", "bin", bin)
-}
-
-func (c *hostTestModule) HostToolPath() android.OptionalPath {
-	return (android.OptionalPathForPath(android.PathForTesting(hostTestBinOut(c.Name()))))
-}
-
-func hostTestModuleFactory() android.Module {
-	m := &hostTestModule{}
-	m.AddProperties(&m.props)
-	android.InitAndroidArchModule(m, android.HostSupported, android.MultilibFirst)
-	return m
-}
-func (m *hostTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	builtFile := android.PathForModuleOut(ctx, m.Name())
-	dir := ctx.Target().Arch.ArchType.Multilib
-	installDir := android.PathForModuleInstall(ctx, dir)
-	ctx.InstallFile(installDir, m.Name(), builtFile)
-}
-
-// Common blueprint used for testing
-var hostTestBp = `
-		license_kind {
-			name: "test_notice",
-			conditions: ["notice"],
-		}
-		license {
-			name: "host_test_license",
-			visibility: ["//visibility:public"],
-			license_kinds: [
-				"test_notice"
-			],
-			license_text: [
-				"NOTICE",
-			],
-		}
-		component {
-			name: "foo",
-			deps: ["bar"],
-		}
-		component {
-			name: "bar",
-			licenses: ["host_test_license"],
-		}
-		`
-
-var hostTestModBp = `
-		host_snapshot {
-			name: "test-host-snapshot",
-			deps: [
-				"foo",
-			],
-		}
-		`
-
-var prepareForHostTest = android.GroupFixturePreparers(
-	android.PrepareForTestWithAndroidBuildComponents,
-	android.PrepareForTestWithLicenses,
-	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		ctx.RegisterModuleType("component", hostTestModuleFactory)
-	}),
-)
-
-// Prepare for host_snapshot test
-var prepareForHostModTest = android.GroupFixturePreparers(
-	prepareForHostTest,
-	android.FixtureWithRootAndroidBp(hostTestBp+hostTestModBp),
-	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		registerHostBuildComponents(ctx)
-	}),
-)
-
-// Prepare for fake host snapshot test disabled
-var prepareForFakeHostTest = android.GroupFixturePreparers(
-	prepareForHostTest,
-	android.FixtureWithRootAndroidBp(hostTestBp),
-)
-
-// Validate that a hostSnapshot object is created containing zip files and JSON file
-// content of zip file is not validated as this is done by PackagingSpecs
-func TestHostSnapshot(t *testing.T) {
-	result := prepareForHostModTest.RunTest(t)
-	t.Helper()
-	ctx := result.TestContext.ModuleForTests("test-host-snapshot", result.Config.BuildOS.String()+"_common")
-	mod := ctx.Module().(*hostSnapshot)
-	if ctx.MaybeOutput("host_snapshot.json").Rule == nil {
-		t.Error("Manifest file not found")
-	}
-	zips := []string{"_deps.zip", "_mods.zip", ".zip"}
-
-	for _, zip := range zips {
-		zFile := mod.Name() + zip
-		if ctx.MaybeOutput(zFile).Rule == nil {
-			t.Error("Zip file ", zFile, "not found")
-		}
-
-	}
-}
diff --git a/testing/all_code_metadata.go b/testing/all_code_metadata.go
index 12aa7b5..e89b281 100644
--- a/testing/all_code_metadata.go
+++ b/testing/all_code_metadata.go
@@ -21,7 +21,7 @@
 
 	ctx.VisitAllModules(
 		func(module android.Module) {
-			if metadata, ok := android.SingletonModuleProvider(ctx, module, CodeMetadataProviderKey); ok {
+			if metadata, ok := android.OtherModuleProvider(ctx, module, CodeMetadataProviderKey); ok {
 				intermediateMetadataPaths = append(intermediateMetadataPaths, metadata.IntermediatePath)
 			}
 		},
diff --git a/testing/all_test_specs.go b/testing/all_test_specs.go
index b035435..68f24d1 100644
--- a/testing/all_test_specs.go
+++ b/testing/all_test_specs.go
@@ -21,7 +21,7 @@
 	var intermediateMetadataPaths android.Paths
 
 	ctx.VisitAllModules(func(module android.Module) {
-		if metadata, ok := android.SingletonModuleProvider(ctx, module, TestSpecProviderKey); ok {
+		if metadata, ok := android.OtherModuleProvider(ctx, module, TestSpecProviderKey); ok {
 			intermediateMetadataPaths = append(intermediateMetadataPaths, metadata.IntermediatePath)
 		}
 	})
diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh
index 794003d..0471853 100755
--- a/tests/sbom_test.sh
+++ b/tests/sbom_test.sh
@@ -76,8 +76,8 @@
   mkdir -p $sbom_test
   cp $product_out/*.img $sbom_test
 
-  # m sbom soong-sbom
-  run_soong "${out_dir}" "sbom soong-sbom"
+  # m sbom
+  run_soong "${out_dir}" "sbom"
 
   # Generate installed file list from .img files in PRODUCT_OUT
   dump_erofs=$out_dir/host/linux-x86/bin/dump.erofs
@@ -118,7 +118,6 @@
   for f in $EROFS_IMAGES; do
     partition_name=$(basename $f | cut -d. -f1)
     file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
-    files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
     files_in_soong_spdx_file="${sbom_test}/soong-sbom-${partition_name}-files-in-spdx.txt"
     rm "$file_list_file" > /dev/null 2>&1 || true
     all_dirs="/"
@@ -147,34 +146,22 @@
     done
     sort -n -o "$file_list_file" "$file_list_file"
 
-    # Diff the file list from image and file list in SBOM created by Make
-    grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' > "$files_in_spdx_file"
-    if [ "$partition_name" = "system" ]; then
-      # system partition is mounted to /, so include FileName starts with /root/ too.
-      grep "FileName: /root/" $product_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_spdx_file"
-    fi
-    sort -n -o "$files_in_spdx_file" "$files_in_spdx_file"
-
-    echo ============ Diffing files in $f and SBOM
-    diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name" ""
-
     # Diff the file list from image and file list in SBOM created by Soong
     grep "FileName: /${partition_name}/" $soong_sbom_out/sbom.spdx | sed 's/^FileName: //' > "$files_in_soong_spdx_file"
-        if [ "$partition_name" = "system" ]; then
-          # system partition is mounted to /, so include FileName starts with /root/ too.
-          grep "FileName: /root/" $soong_sbom_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_soong_spdx_file"
-        fi
-        sort -n -o "$files_in_soong_spdx_file" "$files_in_soong_spdx_file"
+    if [ "$partition_name" = "system" ]; then
+      # system partition is mounted to /, so include FileName starts with /root/ too.
+      grep "FileName: /root/" $soong_sbom_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_soong_spdx_file"
+    fi
+    sort -n -o "$files_in_soong_spdx_file" "$files_in_soong_spdx_file"
 
-        echo ============ Diffing files in $f and SBOM created by Soong
-        diff_files "$file_list_file" "$files_in_soong_spdx_file" "$partition_name" ""
+    echo ============ Diffing files in $f and SBOM created by Soong
+    diff_files "$file_list_file" "$files_in_soong_spdx_file" "$partition_name" ""
   done
 
   RAMDISK_IMAGES="$product_out/ramdisk.img"
   for f in $RAMDISK_IMAGES; do
     partition_name=$(basename $f | cut -d. -f1)
     file_list_file="${sbom_test}/sbom-${partition_name}-files.txt"
-    files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt"
     files_in_soong_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-soong-spdx.txt"
     # lz4 decompress $f to stdout
     # cpio list all entries like ls -l
@@ -183,18 +170,12 @@
     # sed remove partition name from entry names
     $lz4 -c -d $f | cpio -tv 2>/dev/null | grep '^[-l]' | awk -F ' ' '{print $9}' | sed "s:^:/$partition_name/:" | sort -n > "$file_list_file"
 
-    grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_spdx_file"
-
     grep "FileName: /${partition_name}/" $soong_sbom_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_soong_spdx_file"
 
-    echo ============ Diffing files in $f and SBOM
-    diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name" ""
-
     echo ============ Diffing files in $f and SBOM created by Soong
     diff_files "$file_list_file" "$files_in_soong_spdx_file" "$partition_name" ""
   done
 
-  verify_package_verification_code "$product_out/sbom.spdx"
   verify_package_verification_code "$soong_sbom_out/sbom.spdx"
 
   verify_packages_licenses "$soong_sbom_out/sbom.spdx"
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index f9622d3..ef18131 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -6,6 +6,7 @@
 	"encoding/json"
 	"fmt"
 	"io"
+	"slices"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -174,6 +175,20 @@
 		return false
 	}
 
+	var extra_derived_suites []string
+	// Ensure all suites listed are also in base.
+	for _, s := range m.tradefedProperties.Test_suites {
+		if !slices.Contains(m.provider.TestSuites, s) {
+			extra_derived_suites = append(extra_derived_suites, s)
+		}
+	}
+	if len(extra_derived_suites) != 0 {
+		ctx.ModuleErrorf("Suites: [%s] listed but do not exist in base module: %s",
+			strings.Join(extra_derived_suites, ", "),
+			*m.tradefedProperties.Base)
+		return false
+	}
+
 	return true
 }
 
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index 97179f5..1510a03 100644
--- a/tradefed_modules/test_module_config_test.go
+++ b/tradefed_modules/test_module_config_test.go
@@ -40,6 +40,7 @@
 			name: "base",
 			sdk_version: "current",
                         data: [":HelperApp", "data/testfile"],
+                        test_suites: ["general-tests"],
 		}
 
                 test_module_config {
@@ -160,7 +161,7 @@
 		java.PrepareForTestWithJavaDefaultModules,
 		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
 	).ExtendWithErrorHandler(
-		android.FixtureExpectsOneErrorPattern("'base' module used as base but it is not a 'android_test' module.")).
+		android.FixtureExpectsAtLeastOneErrorMatchingPattern("'base' module used as base but it is not a 'android_test' module.")).
 		RunTestWithBp(t, badBp)
 }
 
@@ -193,6 +194,7 @@
 			name: "base",
 			sdk_version: "current",
                         srcs: ["a.java"],
+                        test_suites: ["general-tests"],
 		}
 
                 test_module_config {
@@ -218,6 +220,7 @@
 			sdk_version: "current",
                         srcs: ["a.java"],
                         data: [":HelperApp", "data/testfile"],
+                        test_suites: ["general-tests"],
 		}
 
                 android_test_helper_app {
@@ -362,6 +365,31 @@
 		RunTestWithBp(t, badBp)
 }
 
+func TestModuleConfigNonMatchingTestSuitesGiveErrors(t *testing.T) {
+	badBp := `
+		java_test_host {
+			name: "base",
+                        srcs: ["a.java"],
+                        test_suites: ["general-tests", "some-compat"],
+		}
+
+                test_module_config_host {
+                        name: "derived_test",
+                        base: "base",
+                        exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+                        include_annotations: ["android.platform.test.annotations.LargeTest"],
+                        test_suites: ["device-tests", "random-suite"],
+                }`
+
+	android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+	).ExtendWithErrorHandler(
+		// Use \\ to escape bracket so it isn't used as [] set for regex.
+		android.FixtureExpectsAtLeastOneErrorMatchingPattern("Suites: \\[device-tests, random-suite] listed but do not exist in base module")).
+		RunTestWithBp(t, badBp)
+}
+
 func TestTestOnlyProvider(t *testing.T) {
 	t.Parallel()
 	ctx := android.GroupFixturePreparers(
@@ -389,6 +417,7 @@
 			name: "base",
 			sdk_version: "current",
                         data: ["data/testfile"],
+                        test_suites: ["general-tests"],
 		}
 
 		java_test_host {
diff --git a/ui/build/config.go b/ui/build/config.go
index 631b76f..b8fcb6b 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -1341,8 +1341,7 @@
 }
 
 func (c *configImpl) rbeTmpDir() string {
-	buildTmpDir := shared.TempDirForOutDir(c.SoongOutDir())
-	return filepath.Join(buildTmpDir, "rbe")
+	return filepath.Join(c.SoongOutDir(), "rbe")
 }
 
 func (c *configImpl) rbeCacheDir() string {
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index b5e74b4..8ca62d3 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -229,6 +229,7 @@
 			"BUILD_BROKEN_INCORRECT_PARTITION_IMAGES",
 			"SOONG_USE_N2",
 			"RUST_BACKTRACE",
+			"RUST_LOG",
 		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
 	}
 
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 8fa147f..0a0f956 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -65,7 +65,7 @@
 		"RBE_platform":         "container-image=" + remoteexec.DefaultImage,
 	}
 	if config.StartRBE() {
-		name, err := config.rbeSockAddr(absPath(ctx, config.TempDir()))
+		name, err := config.rbeSockAddr(absPath(ctx, config.rbeTmpDir()))
 		if err != nil {
 			ctx.Fatalf("Error retrieving socket address: %v", err)
 			return nil
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index 5c3fec1..d9ca854 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -48,7 +48,10 @@
 	}
 )
 
-const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail"
+const (
+	nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail"
+	abfsSrcDir = "/src"
+)
 
 var sandboxConfig struct {
 	once sync.Once
@@ -145,10 +148,50 @@
 	return sandboxConfig.working
 }
 
+func (c *Cmd) srcDirArg() string {
+	if !c.config.UseABFS() {
+		return sandboxConfig.srcDir
+	}
+
+	return sandboxConfig.srcDir + ":" + abfsSrcDir
+}
+
+func (c *Cmd) outDirArg() string {
+	if !c.config.UseABFS() {
+		return sandboxConfig.outDir
+	}
+
+	return sandboxConfig.outDir + ":" + filepath.Join(abfsSrcDir, sandboxConfig.outDir)
+}
+
+// When configured to use ABFS, we need to allow the creation of the /src
+// directory. Therefore, we cannot mount the root "/" directory as read-only.
+// Instead, we individually mount the children of "/" as RO.
+func (c *Cmd) readMountArgs() []string {
+	if !c.config.UseABFS() {
+		// For now, just map everything. Make most things readonly.
+		return []string{"-R", "/"}
+	}
+
+	entries, err := os.ReadDir("/")
+	if err != nil {
+		// If we can't read "/", just use the default non-ABFS behavior.
+		return []string{"-R", "/"}
+	}
+
+	args := make([]string, 0, 2*len(entries))
+	for _, ent := range entries {
+		args = append(args, "-R", "/"+ent.Name())
+	}
+
+	return args
+}
+
 func (c *Cmd) wrapSandbox() {
 	wd, _ := os.Getwd()
 
-	sandboxArgs := []string{
+	var sandboxArgs []string
+	sandboxArgs = append(sandboxArgs,
 		// The executable to run
 		"-x", c.Path,
 
@@ -180,18 +223,21 @@
 		"--rlimit_cpu", "soft",
 		"--rlimit_fsize", "soft",
 		"--rlimit_nofile", "soft",
+	)
 
-		// For now, just map everything. Make most things readonly.
-		"-R", "/",
+	sandboxArgs = append(sandboxArgs,
+		c.readMountArgs()...
+	)
 
+	sandboxArgs = append(sandboxArgs,
 		// Mount a writable tmp dir
 		"-B", "/tmp",
 
 		// Mount source
-		c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
+		c.config.sandboxConfig.SrcDirMountFlag(), c.srcDirArg(),
 
 		//Mount out dir as read-write
-		"-B", sandboxConfig.outDir,
+		"-B", c.outDirArg(),
 
 		// Disable newcgroup for now, since it may require newer kernels
 		// TODO: try out cgroups
@@ -199,7 +245,7 @@
 
 		// Only log important warnings / errors
 		"-q",
-	}
+	)
 	if c.config.UseABFS() {
 		sandboxArgs = append(sandboxArgs, "-B", "{ABFS_DIR}")
 	}