Merge tag 'android-16.0.0_r1' of https://android.googlesource.com/platform/build/soong into HEAD

Android 16.0.0 release 1

Change-Id: Ide1ec574e3f9743d18ee70b928056bb7fe248f80
diff --git a/java/Android.bp b/java/Android.bp
index 885e682..99d9c38 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -51,6 +51,7 @@
         "gen.go",
         "generated_java_library.go",
         "genrule.go",
+        "genrule_combiner.go",
         "hiddenapi.go",
         "hiddenapi_modular.go",
         "hiddenapi_monolithic.go",
@@ -77,6 +78,7 @@
         "system_modules.go",
         "systemserver_classpath_fragment.go",
         "testing.go",
+        "tracereferences.go",
         "tradefed.go",
     ],
     testSrcs: [
@@ -95,6 +97,7 @@
         "droiddoc_test.go",
         "droidstubs_test.go",
         "fuzz_test.go",
+        "genrule_combiner_test.go",
         "genrule_test.go",
         "generated_java_library_test.go",
         "hiddenapi_singleton_test.go",
diff --git a/java/aar.go b/java/aar.go
index 7322b09..86c6bfa 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -217,13 +217,9 @@
 }
 
 func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool {
-	return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault()) &&
+	return BoolDefault(a.aaptProperties.Use_resource_processor, true) &&
 		// TODO(b/331641946): remove this when ResourceProcessorBusyBox supports generating shared libraries.
-		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib") &&
-		// Use the legacy resource processor in kythe builds.
-		// 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()
+		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib")
 }
 
 func (a *aapt) filterProduct() string {
@@ -391,8 +387,9 @@
 		versionName = proptools.NinjaEscape(versionName)
 		linkFlags = append(linkFlags, "--version-name ", versionName)
 	}
-
-	linkFlags, compileFlags = android.FilterList(linkFlags, []string{"--legacy"})
+	// Split the flags by prefix, as --png-compression-level has the "=value" suffix.
+	linkFlags, compileFlags = android.FilterListByPrefix(linkFlags,
+		[]string{"--legacy", "--png-compression-level"})
 
 	// Always set --pseudo-localize, it will be stripped out later for release
 	// builds that don't want it.
@@ -878,13 +875,15 @@
 	rroDirsDepSetBuilder := depset.NewBuilder[rroDir](depset.TOPOLOGICAL)
 	manifestsDepSetBuilder := depset.NewBuilder[android.Path](depset.TOPOLOGICAL)
 
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		depTag := ctx.OtherModuleDependencyTag(module)
 
 		var exportPackage android.Path
-		aarDep, _ := module.(AndroidLibraryDependency)
-		if aarDep != nil {
-			exportPackage = aarDep.ExportPackage()
+		var aarDep *AndroidLibraryDependencyInfo
+		javaInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider)
+		if ok && javaInfo.AndroidLibraryDependencyInfo != nil {
+			aarDep = javaInfo.AndroidLibraryDependencyInfo
+			exportPackage = aarDep.ExportPackage
 		}
 
 		switch depTag {
@@ -892,7 +891,7 @@
 			// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
 		case sdkLibTag, libTag, rroDepTag:
 			if exportPackage != nil {
-				sharedResourcesNodeDepSets = append(sharedResourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
+				sharedResourcesNodeDepSets = append(sharedResourcesNodeDepSets, aarDep.ResourcesNodeDepSet)
 				sharedLibs = append(sharedLibs, exportPackage)
 			}
 		case frameworkResTag, omniromResTag:
@@ -901,9 +900,9 @@
 			}
 		case staticLibTag:
 			if exportPackage != nil {
-				staticResourcesNodeDepSets = append(staticResourcesNodeDepSets, aarDep.ResourcesNodeDepSet())
-				rroDirsDepSetBuilder.Transitive(aarDep.RRODirsDepSet())
-				manifestsDepSetBuilder.Transitive(aarDep.ManifestsDepSet())
+				staticResourcesNodeDepSets = append(staticResourcesNodeDepSets, aarDep.ResourcesNodeDepSet)
+				rroDirsDepSetBuilder.Transitive(aarDep.RRODirsDepSet)
+				manifestsDepSetBuilder.Transitive(aarDep.ManifestsDepSet)
 			}
 		}
 
@@ -939,6 +938,18 @@
 	return staticResourcesNodes, sharedResourcesNodes, staticRRODirs, staticManifests, sharedLibs, flags
 }
 
+type AndroidLibraryInfo struct {
+	// Empty for now
+}
+
+var AndroidLibraryInfoProvider = blueprint.NewProvider[AndroidLibraryInfo]()
+
+type AARImportInfo struct {
+	// Empty for now
+}
+
+var AARImportInfoProvider = blueprint.NewProvider[AARImportInfo]()
+
 type AndroidLibrary struct {
 	Library
 	aapt
@@ -1025,7 +1036,7 @@
 		extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
 	}
 
-	a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
+	javaInfo := a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
 
 	a.aarFile = android.PathForModuleOut(ctx, ctx.ModuleName()+".aar")
 	var res android.Paths
@@ -1034,7 +1045,7 @@
 	}
 
 	prebuiltJniPackages := android.Paths{}
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		if info, ok := android.OtherModuleProvider(ctx, module, JniPackageProvider); ok {
 			prebuiltJniPackages = append(prebuiltJniPackages, info.JniPackages...)
 		}
@@ -1049,7 +1060,16 @@
 		AconfigTextFiles: aconfigTextFilePaths,
 	})
 
+	android.SetProvider(ctx, AndroidLibraryInfoProvider, AndroidLibraryInfo{})
+
+	if javaInfo != nil {
+		setExtraJavaInfo(ctx, a, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
 	a.setOutputFiles(ctx)
+
+	buildComplianceMetadata(ctx)
 }
 
 func (a *AndroidLibrary) setOutputFiles(ctx android.ModuleContext) {
@@ -1445,7 +1465,7 @@
 	var transitiveStaticLibsImplementationJars []depset.DepSet[android.Path]
 	var transitiveStaticLibsResourceJars []depset.DepSet[android.Path]
 
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			tag := ctx.OtherModuleDependencyTag(module)
 			switch tag {
@@ -1467,12 +1487,7 @@
 	completeStaticLibsResourceJars := depset.New(depset.PREORDER, nil, transitiveStaticLibsResourceJars)
 
 	var implementationJarFile android.Path
-	var combineJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		combineJars = completeStaticLibsImplementationJars.ToList()
-	} else {
-		combineJars = append(android.Paths{classpathFile}, staticJars...)
-	}
+	combineJars := completeStaticLibsImplementationJars.ToList()
 
 	if len(combineJars) > 1 {
 		implementationJarOutputPath := android.PathForModuleOut(ctx, "combined", jarName)
@@ -1483,12 +1498,8 @@
 	}
 
 	var resourceJarFile android.Path
-	var resourceJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		resourceJars = completeStaticLibsResourceJars.ToList()
-	} else {
-		resourceJars = staticResourceJars
-	}
+	resourceJars := completeStaticLibsResourceJars.ToList()
+
 	if len(resourceJars) > 1 {
 		combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName)
 		TransformJarsToJar(ctx, combinedJar, "for resources", resourceJars, android.OptionalPath{},
@@ -1499,12 +1510,8 @@
 	}
 
 	// merge implementation jar with resources if necessary
-	var implementationAndResourcesJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		implementationAndResourcesJars = append(slices.Clone(resourceJars), combineJars...)
-	} else {
-		implementationAndResourcesJars = android.PathsIfNonNil(resourceJarFile, implementationJarFile)
-	}
+	implementationAndResourcesJars := append(slices.Clone(resourceJars), combineJars...)
+
 	var implementationAndResourcesJar android.Path
 	if len(implementationAndResourcesJars) > 1 {
 		combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
@@ -1519,12 +1526,7 @@
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
 	a.implementationAndResourcesJarFile = implementationAndResourcesJar.WithoutRel()
 
-	var headerJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		headerJars = completeStaticLibsHeaderJars.ToList()
-	} else {
-		headerJars = append(android.Paths{classpathFile}, staticHeaderJars...)
-	}
+	headerJars := completeStaticLibsHeaderJars.ToList()
 	if len(headerJars) > 1 {
 		headerJarFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
 		TransformJarsToJar(ctx, headerJarFile, "combine header jars", headerJars, android.OptionalPath{}, false, nil, nil)
@@ -1533,14 +1535,9 @@
 		a.headerJarFile = headerJars[0]
 	}
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		ctx.CheckbuildFile(classpathFile)
-	} else {
-		ctx.CheckbuildFile(a.headerJarFile)
-		ctx.CheckbuildFile(a.implementationJarFile)
-	}
+	ctx.CheckbuildFile(classpathFile)
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(a.headerJarFile),
 		LocalHeaderJars:                        android.PathsIfNonNil(classpathFile),
 		TransitiveStaticLibsHeaderJars:         completeStaticLibsHeaderJars,
@@ -1553,7 +1550,9 @@
 		ImplementationJars:                     android.PathsIfNonNil(a.implementationJarFile),
 		StubsLinkType:                          Implementation,
 		// TransitiveAconfigFiles: // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
-	})
+	}
+	setExtraJavaInfo(ctx, a, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 
 	if proptools.Bool(a.properties.Extract_jni) {
 		for _, t := range ctx.MultiTargets() {
@@ -1580,8 +1579,12 @@
 		JniPackages: a.jniPackages,
 	})
 
+	android.SetProvider(ctx, AARImportInfoProvider, AARImportInfo{})
+
 	ctx.SetOutputFiles([]android.Path{a.implementationAndResourcesJarFile}, "")
 	ctx.SetOutputFiles([]android.Path{a.aarPath}, ".aar")
+
+	buildComplianceMetadata(ctx)
 }
 
 func (a *AARImport) HeaderJars() android.Paths {
@@ -1609,14 +1612,21 @@
 var _ android.ApexModule = (*AARImport)(nil)
 
 // Implements android.ApexModule
-func (a *AARImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	return a.depIsInSameApex(ctx, dep)
+func (m *AARImport) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return AARImportDepInSameApexChecker{}
+}
+
+type AARImportDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m AARImportDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	return depIsInSameApex(tag)
 }
 
 // Implements android.ApexModule
-func (a *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	return nil
+func (a *AARImport) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 var _ android.PrebuiltInterface = (*AARImport)(nil)
@@ -1646,5 +1656,5 @@
 }
 
 func (a *AARImport) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	dpInfo.Jars = append(dpInfo.Jars, a.headerJarFile.String(), a.rJar.String())
+	dpInfo.Jars = append(dpInfo.Jars, a.implementationJarFile.String(), a.rJar.String())
 }
diff --git a/java/aar_test.go b/java/aar_test.go
index aa4f0af..088ad6c 100644
--- a/java/aar_test.go
+++ b/java/aar_test.go
@@ -21,6 +21,7 @@
 )
 
 func TestAarImportProducesJniPackages(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -50,8 +51,9 @@
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			appMod := ctx.Module(tc.name, "android_common")
-			appTestMod := ctx.ModuleForTests(tc.name, "android_common")
+			appTestMod := ctx.ModuleForTests(t, tc.name, "android_common")
 
 			info, ok := android.OtherModuleProvider(ctx, appMod, JniPackageProvider)
 			if !ok {
@@ -84,6 +86,7 @@
 }
 
 func TestLibraryFlagsPackages(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 	).RunTestWithBp(t, `
@@ -114,7 +117,7 @@
 		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 
 	// android_library module depends on aconfig_declarations listed in flags_packages
 	android.AssertBoolEquals(t, "foo expected to depend on bar", true,
@@ -133,6 +136,7 @@
 }
 
 func TestAndroidLibraryOutputFilesRel(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -155,9 +159,9 @@
 		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
-	bar := result.ModuleForTests("bar", "android_common")
-	baz := result.ModuleForTests("baz", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
+	bar := result.ModuleForTests(t, "bar", "android_common")
+	baz := result.ModuleForTests(t, "baz", "android_common")
 
 	fooOutputPaths := foo.OutputFiles(result.TestContext, t, "")
 	barOutputPaths := bar.OutputFiles(result.TestContext, t, "")
diff --git a/java/android_manifest_test.go b/java/android_manifest_test.go
index 7c91884..ce227b9 100644
--- a/java/android_manifest_test.go
+++ b/java/android_manifest_test.go
@@ -21,6 +21,7 @@
 )
 
 func TestManifestMerger(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "app",
@@ -80,7 +81,7 @@
 
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, bp)
 
-	manifestMergerRule := result.ModuleForTests("app", "android_common").Rule("manifestMerger")
+	manifestMergerRule := result.ModuleForTests(t, "app", "android_common").Rule("manifestMerger")
 	android.AssertPathRelativeToTopEquals(t, "main manifest",
 		"out/soong/.intermediates/app/android_common/manifest_fixer/AndroidManifest.xml",
 		manifestMergerRule.Input)
@@ -100,6 +101,7 @@
 }
 
 func TestManifestValuesApplicationIdSetsPackageName(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_test {
 			name: "test",
@@ -127,7 +129,7 @@
 
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, bp)
 
-	manifestMergerRule := result.ModuleForTests("test", "android_common").Rule("manifestMerger")
+	manifestMergerRule := result.ModuleForTests(t, "test", "android_common").Rule("manifestMerger")
 	android.AssertStringMatches(t,
 		"manifest merger args",
 		manifestMergerRule.Args["args"],
diff --git a/java/androidmk.go b/java/androidmk.go
index eb33589..e9b1f64 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -23,13 +23,12 @@
 	"github.com/google/blueprint/proptools"
 )
 
-func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries {
-	hostDexNeeded := Bool(library.deviceProperties.Hostdex) && !library.Host()
-	if library.hideApexVariantFromMake {
-		hostDexNeeded = false
-	}
+func (library *Library) hostDexNeeded() bool {
+	return Bool(library.deviceProperties.Hostdex) && !library.Host() && !library.hideApexVariantFromMake
+}
 
-	if hostDexNeeded {
+func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries {
+	if library.hostDexNeeded() {
 		var output android.Path
 		if library.dexJarFile.IsSet() {
 			output = library.dexJarFile.Path()
@@ -71,11 +70,7 @@
 	if library.hideApexVariantFromMake {
 		// For a java library built for an APEX, we don't need a Make module for itself. Otherwise, it
 		// will conflict with the platform variant because they have the same module name in the
-		// makefile. However, we need to add its dexpreopt outputs as sub-modules, if it is preopted.
-		dexpreoptEntries := library.dexpreopter.AndroidMkEntriesForApex()
-		if len(dexpreoptEntries) > 0 {
-			entriesList = append(entriesList, dexpreoptEntries...)
-		}
+		// makefile.
 		entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true})
 	} else if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) {
 		// Platform variant.  If not available for the platform, we don't need Make module.
@@ -125,6 +120,14 @@
 					}
 				},
 			},
+			ExtraFooters: []android.AndroidMkExtraFootersFunc{
+				func(w io.Writer, name, prefix, moduleDir string) {
+					if library.apiXmlFile != nil {
+						fmt.Fprintf(w, "$(call declare-1p-target,%s,)\n", library.apiXmlFile.String())
+						fmt.Fprintf(w, "$(eval $(call copy-one-file,%s,$(TARGET_OUT_COMMON_INTERMEDIATES)/%s))\n", library.apiXmlFile.String(), library.apiXmlFile.Base())
+					}
+				},
+			},
 		})
 	}
 
@@ -555,73 +558,11 @@
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
 			func(w io.Writer, name, prefix, moduleDir string) {
-				if dstubs.apiFile != nil {
-					fmt.Fprintf(w, ".PHONY: %s %s.txt\n", dstubs.Name(), dstubs.Name())
-					fmt.Fprintf(w, "%s %s.txt: %s\n", dstubs.Name(), dstubs.Name(), dstubs.apiFile)
-				}
-				if dstubs.removedApiFile != nil {
-					fmt.Fprintf(w, ".PHONY: %s %s.txt\n", dstubs.Name(), dstubs.Name())
-					fmt.Fprintf(w, "%s %s.txt: %s\n", dstubs.Name(), dstubs.Name(), dstubs.removedApiFile)
-				}
-				if dstubs.checkCurrentApiTimestamp != nil {
-					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-current-api")
-					fmt.Fprintln(w, dstubs.Name()+"-check-current-api:",
-						dstubs.checkCurrentApiTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: checkapi")
-					fmt.Fprintln(w, "checkapi:",
-						dstubs.checkCurrentApiTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: droidcore")
-					fmt.Fprintln(w, "droidcore: checkapi")
-				}
-				if dstubs.updateCurrentApiTimestamp != nil {
-					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-update-current-api")
-					fmt.Fprintln(w, dstubs.Name()+"-update-current-api:",
-						dstubs.updateCurrentApiTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: update-api")
-					fmt.Fprintln(w, "update-api:",
-						dstubs.updateCurrentApiTimestamp.String())
-				}
-				if dstubs.checkLastReleasedApiTimestamp != nil {
-					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-last-released-api")
-					fmt.Fprintln(w, dstubs.Name()+"-check-last-released-api:",
-						dstubs.checkLastReleasedApiTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: checkapi")
-					fmt.Fprintln(w, "checkapi:",
-						dstubs.checkLastReleasedApiTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: droidcore")
-					fmt.Fprintln(w, "droidcore: checkapi")
-				}
 				if dstubs.apiLintTimestamp != nil {
-					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-api-lint")
-					fmt.Fprintln(w, dstubs.Name()+"-api-lint:",
-						dstubs.apiLintTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY: checkapi")
-					fmt.Fprintln(w, "checkapi:",
-						dstubs.Name()+"-api-lint")
-
-					fmt.Fprintln(w, ".PHONY: droidcore")
-					fmt.Fprintln(w, "droidcore: checkapi")
-
 					if dstubs.apiLintReport != nil {
-						fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", dstubs.Name()+"-api-lint",
-							dstubs.apiLintReport.String(), "apilint/"+dstubs.Name()+"-lint-report.txt")
 						fmt.Fprintf(w, "$(call declare-0p-target,%s)\n", dstubs.apiLintReport.String())
 					}
 				}
-				if dstubs.checkNullabilityWarningsTimestamp != nil {
-					fmt.Fprintln(w, ".PHONY:", dstubs.Name()+"-check-nullability-warnings")
-					fmt.Fprintln(w, dstubs.Name()+"-check-nullability-warnings:",
-						dstubs.checkNullabilityWarningsTimestamp.String())
-
-					fmt.Fprintln(w, ".PHONY:", "droidcore")
-					fmt.Fprintln(w, "droidcore: ", dstubs.Name()+"-check-nullability-warnings")
-				}
 			},
 		},
 	}}
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 1d98b18..b4b13b1 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -23,6 +23,7 @@
 )
 
 func TestRequired(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -31,7 +32,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	mod := ctx.ModuleForTests(t, "foo", "android_common").Module()
 	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 
 	expected := []string{"libfoo"}
@@ -42,6 +43,7 @@
 }
 
 func TestHostdex(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -50,7 +52,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	mod := ctx.ModuleForTests(t, "foo", "android_common").Module()
 	entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
 	if len(entriesList) != 2 {
 		t.Errorf("two entries are expected, but got %d", len(entriesList))
@@ -72,6 +74,7 @@
 }
 
 func TestHostdexRequired(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -81,7 +84,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	mod := ctx.ModuleForTests(t, "foo", "android_common").Module()
 	entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
 	if len(entriesList) != 2 {
 		t.Errorf("two entries are expected, but got %d", len(entriesList))
@@ -103,6 +106,7 @@
 }
 
 func TestHostdexSpecificRequired(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -116,7 +120,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	mod := ctx.ModuleForTests(t, "foo", "android_common").Module()
 	entriesList := android.AndroidMkEntriesForTest(t, ctx, mod)
 	if len(entriesList) != 2 {
 		t.Errorf("two entries are expected, but got %d", len(entriesList))
@@ -136,6 +140,7 @@
 }
 
 func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -153,7 +158,7 @@
 		`)
 
 	// Verify the existence of internal modules
-	result.ModuleForTests("foo-shared_library.xml", "android_common")
+	result.ModuleForTests(t, "foo-shared_library.xml", "android_common")
 
 	testCases := []struct {
 		moduleName string
@@ -163,7 +168,7 @@
 		{"foo-no_shared_library", []string{"foo-no_shared_library.impl"}},
 	}
 	for _, tc := range testCases {
-		mod := result.ModuleForTests(tc.moduleName, "android_common").Module()
+		mod := result.ModuleForTests(t, tc.moduleName, "android_common").Module()
 		entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
 		actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
 		if !reflect.DeepEqual(tc.expected, actual) {
@@ -173,6 +178,7 @@
 }
 
 func TestImportSoongDexJar(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		java_import {
 			name: "my-java-import",
@@ -191,6 +197,7 @@
 }
 
 func TestAndroidTestHelperApp_LocalDisableTestConfig(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_helper_app {
 			name: "foo",
@@ -198,7 +205,7 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_common").Module()
+	mod := ctx.ModuleForTests(t, "foo", "android_common").Module()
 	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 
 	expected := []string{"true"}
@@ -209,6 +216,7 @@
 }
 
 func TestGetOverriddenPackages(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(
 		t, `
 		android_app {
@@ -246,7 +254,7 @@
 	}
 
 	for _, expected := range expectedVariants {
-		mod := ctx.ModuleForTests(expected.name, expected.variantName).Module()
+		mod := ctx.ModuleForTests(t, expected.name, expected.variantName).Module()
 		entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
 		actual := entries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
 
@@ -255,6 +263,7 @@
 }
 
 func TestJniAsRequiredDeps(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		cc.PrepareForTestWithCcDefaultModules,
@@ -295,7 +304,7 @@
 	}
 
 	for _, tc := range testcases {
-		mod := ctx.ModuleForTests(tc.name, "android_common").Module()
+		mod := ctx.ModuleForTests(t, tc.name, "android_common").Module()
 		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
 		required := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
 		android.AssertDeepEquals(t, "unexpected required deps", tc.expected, required)
diff --git a/java/app.go b/java/app.go
index 64ecc55..a3414f6 100644
--- a/java/app.go
+++ b/java/app.go
@@ -70,6 +70,19 @@
 
 	// EmbeddedJNILibs is the list of paths to JNI libraries that were embedded in the APK.
 	EmbeddedJNILibs android.Paths
+
+	MergedManifestFile android.Path
+
+	Prebuilt                      bool
+	AppSet                        bool
+	Privileged                    bool
+	OutputFile                    android.Path
+	InstallApkName                string
+	JacocoReportClassesFile       android.Path
+	Certificate                   Certificate
+	PrivAppAllowlist              android.OptionalPath
+	OverriddenManifestPackageName *string
+	ApkCertsFile                  android.Path
 }
 
 var AppInfoProvider = blueprint.NewProvider[*AppInfo]()
@@ -399,9 +412,23 @@
 	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
 		TestOnly: true,
 	})
-	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
+	appInfo := &AppInfo{
 		Updatable:     Bool(a.appProperties.Updatable),
 		TestHelperApp: true,
+	}
+	setCommonAppInfo(appInfo, a)
+	android.SetProvider(ctx, AppInfoProvider, appInfo)
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Tags = append(moduleInfoJSON.Tags, "tests")
+	if len(a.appTestHelperAppProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, a.appTestHelperAppProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: a.appTestHelperAppProperties.Test_suites,
 	})
 }
 
@@ -418,13 +445,28 @@
 			embeddedJniLibs = append(embeddedJniLibs, jni.path)
 		}
 	}
-	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
-		Updatable:       Bool(a.appProperties.Updatable),
-		TestHelperApp:   false,
-		EmbeddedJNILibs: embeddedJniLibs,
-	})
+	overriddenName := a.OverriddenManifestPackageName()
+	appInfo := &AppInfo{
+		Updatable:                     Bool(a.appProperties.Updatable),
+		TestHelperApp:                 false,
+		EmbeddedJNILibs:               embeddedJniLibs,
+		MergedManifestFile:            a.mergedManifest,
+		OverriddenManifestPackageName: &overriddenName,
+	}
+	setCommonAppInfo(appInfo, a)
+	android.SetProvider(ctx, AppInfoProvider, appInfo)
 
 	a.requiredModuleNames = a.getRequiredModuleNames(ctx)
+
+	if a.dexer.proguardDictionary.Valid() {
+		android.SetProvider(ctx, ProguardProvider, ProguardInfo{
+			ModuleName:         ctx.ModuleName(),
+			Class:              "APPS",
+			ProguardDictionary: a.dexer.proguardDictionary.Path(),
+			ProguardUsageZip:   a.dexer.proguardUsageZip.Path(),
+			ClassesJar:         a.implementationAndResourcesJar,
+		})
+	}
 }
 
 func (a *AndroidApp) getRequiredModuleNames(ctx android.ModuleContext) []string {
@@ -487,18 +529,28 @@
 // This check is enforced for "updatable" APKs (including APK-in-APEX).
 func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) {
 	// It's enough to check direct JNI deps' sdk_version because all transitive deps from JNI deps are checked in cc.checkLinkType()
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		if !IsJniDepTag(ctx.OtherModuleDependencyTag(m)) {
 			return
 		}
-		dep, _ := m.(*cc.Module)
+		if _, ok := android.OtherModuleProvider(ctx, m, cc.CcInfoProvider); !ok {
+			panic(fmt.Errorf("jni dependency is not a cc module: %v", m))
+		}
+		commonInfo, ok := android.OtherModuleProvider(ctx, m, android.CommonModuleInfoProvider)
+		if !ok {
+			panic(fmt.Errorf("jni dependency doesn't have CommonModuleInfo provider: %v", m))
+		}
 		// The domain of cc.sdk_version is "current" and <number>
 		// We can rely on android.SdkSpec to convert it to <number> so that "current" is
 		// handled properly regardless of sdk finalization.
-		jniSdkVersion, err := android.SdkSpecFrom(ctx, dep.MinSdkVersion()).EffectiveVersion(ctx)
+		ver := ""
+		if !commonInfo.MinSdkVersion.IsPlatform {
+			ver = commonInfo.MinSdkVersion.ApiLevel.String()
+		}
+		jniSdkVersion, err := android.SdkSpecFrom(ctx, ver).EffectiveVersion(ctx)
 		if err != nil || minSdkVersion.LessThan(jniSdkVersion) {
-			ctx.OtherModuleErrorf(dep, "min_sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
-				dep.MinSdkVersion(), minSdkVersion, ctx.ModuleName())
+			ctx.OtherModuleErrorf(m, "min_sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
+				ver, minSdkVersion, ctx.ModuleName())
 			return
 		}
 
@@ -561,10 +613,10 @@
 }
 
 func getAconfigFilePaths(ctx android.ModuleContext) (aconfigTextFilePaths android.Paths) {
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(dep)
 		switch tag {
-		case staticLibTag:
+		case staticLibTag, rroDepTag:
 			if flagPackages, ok := android.OtherModuleProvider(ctx, dep, FlagsPackagesProvider); ok {
 				aconfigTextFilePaths = append(aconfigTextFilePaths, flagPackages.AconfigTextFiles...)
 			}
@@ -640,7 +692,7 @@
 	}
 
 	// Use non final ids if we are doing optimized shrinking and are using R8.
-	nonFinalIds := a.dexProperties.optimizedResourceShrinkingEnabled(ctx) && a.dexer.effectiveOptimizeEnabled()
+	nonFinalIds := a.dexProperties.optimizedResourceShrinkingEnabled(ctx) && a.dexer.effectiveOptimizeEnabled(ctx)
 
 	aconfigTextFilePaths := getAconfigFilePaths(ctx)
 
@@ -667,7 +719,7 @@
 
 func (a *AndroidApp) proguardBuildActions(ctx android.ModuleContext) {
 	var staticLibProguardFlagFiles android.Paths
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		depProguardInfo, _ := android.OtherModuleProvider(ctx, m, ProguardSpecInfoProvider)
 		staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, depProguardInfo.UnconditionallyExportedProguardFlags.ToList()...)
 		if ctx.OtherModuleDependencyTag(m) == staticLibTag {
@@ -704,7 +756,7 @@
 	return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
 }
 
-func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) (android.Path, android.Path) {
+func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) (android.Path, android.Path, *JavaInfo) {
 	a.dexpreopter.installPath = a.installPath(ctx)
 	a.dexpreopter.isApp = true
 	if a.dexProperties.Uncompress_dex == nil {
@@ -719,6 +771,7 @@
 
 	var packageResources = a.exportPackage
 
+	javaInfo := &JavaInfo{}
 	if ctx.ModuleName() != "framework-res" && ctx.ModuleName() != "omnirom-res" {
 		if a.dexProperties.resourceShrinkingEnabled(ctx) {
 			protoFile := android.PathForModuleOut(ctx, packageResources.Base()+".proto.apk")
@@ -742,7 +795,7 @@
 			extraSrcJars = android.Paths{a.aapt.aaptSrcJar}
 		}
 
-		a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
+		javaInfo = a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars, nil)
 		if a.dexProperties.resourceShrinkingEnabled(ctx) {
 			binaryResources := android.PathForModuleOut(ctx, packageResources.Base()+".binary.out.apk")
 			aapt2Convert(ctx, binaryResources, a.dexer.resourcesOutput.Path(), "binary")
@@ -750,7 +803,7 @@
 		}
 	}
 
-	return a.dexJarFile.PathOrNil(), packageResources
+	return a.dexJarFile.PathOrNil(), packageResources, javaInfo
 }
 
 func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, prebuiltJniPackages android.Paths, ctx android.ModuleContext) android.WritablePath {
@@ -960,7 +1013,7 @@
 	a.linter.resources = a.aapt.resourceFiles
 	a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()
 
-	dexJarFile, packageResources := a.dexBuildActions(ctx)
+	dexJarFile, packageResources, javaInfo := a.dexBuildActions(ctx)
 
 	// No need to check the SDK version of the JNI deps unless we embed them
 	checkNativeSdkVersion := a.shouldEmbedJnis(ctx) && !Bool(a.appProperties.Jni_uses_platform_apis)
@@ -1076,7 +1129,28 @@
 		},
 	)
 
+	if javaInfo != nil {
+		javaInfo.OutputFile = a.outputFile
+		setExtraJavaInfo(ctx, a, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
+	android.SetProvider(ctx, android.ApexBundleDepsDataProvider, android.ApexBundleDepsData{
+		FlatListPath: a.FlatListPath(),
+		Updatable:    a.Updatable(),
+	})
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"APPS"}
+	if !a.embeddedJniLibs {
+		for _, jniLib := range a.jniLibs {
+			moduleInfoJSON.ExtraRequired = append(moduleInfoJSON.ExtraRequired, jniLib.name)
+		}
+	}
+
 	a.setOutputFiles(ctx)
+
+	buildComplianceMetadata(ctx)
 }
 
 func (a *AndroidApp) setOutputFiles(ctx android.ModuleContext) {
@@ -1108,29 +1182,35 @@
 			app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
 	}
 	jniLib, prebuiltJniPackages := collectJniDeps(ctx, shouldCollectRecursiveNativeDeps,
-		checkNativeSdkVersion, func(parent, child android.Module) bool {
+		checkNativeSdkVersion, func(parent, child android.ModuleProxy) bool {
 			apkInApex := ctx.Module().(android.ApexModule).NotInPlatform()
-			childLinkable, _ := child.(cc.LinkableInterface)
-			parentLinkable, _ := parent.(cc.LinkableInterface)
-			useStubsOfDep := childLinkable.IsStubs()
-			if apkInApex && parentLinkable != nil {
+			childLinkable, _ := android.OtherModuleProvider(ctx, child, cc.LinkableInfoProvider)
+			parentIsLinkable := false
+			if android.EqualModules(ctx.Module(), parent) {
+				parentLinkable, _ := ctx.Module().(cc.LinkableInterface)
+				parentIsLinkable = parentLinkable != nil
+			} else {
+				_, parentIsLinkable = android.OtherModuleProvider(ctx, parent, cc.LinkableInfoProvider)
+			}
+			useStubsOfDep := childLinkable.IsStubs
+			if apkInApex && parentIsLinkable {
 				// APK-in-APEX
 				// If the parent is a linkable interface, use stubs if the dependency edge crosses an apex boundary.
-				useStubsOfDep = useStubsOfDep || (childLinkable.HasStubsVariants() && cc.ShouldUseStubForApex(ctx, parent, child))
+				useStubsOfDep = useStubsOfDep || (childLinkable.HasStubsVariants && cc.ShouldUseStubForApex(ctx, parent, child))
 			}
-			return !childLinkable.IsNdk(ctx.Config()) && !useStubsOfDep
+			return !childLinkable.IsNdk && !useStubsOfDep
 		})
 
 	var certificates []Certificate
 
 	var directImplementationDeps android.Paths
 	var transitiveImplementationDeps []depset.DepSet[android.Path]
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
 		if tag == certificateTag {
-			if dep, ok := module.(*AndroidAppCertificate); ok {
+			if dep, ok := android.OtherModuleProvider(ctx, module, AndroidAppCertificateInfoProvider); ok {
 				certificates = append(certificates, dep.Certificate)
 			} else {
 				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
@@ -1154,22 +1234,25 @@
 func collectJniDeps(ctx android.ModuleContext,
 	shouldCollectRecursiveNativeDeps bool,
 	checkNativeSdkVersion bool,
-	filter func(parent, child android.Module) bool) ([]jniLib, android.Paths) {
+	filter func(parent, child android.ModuleProxy) bool) ([]jniLib, android.Paths) {
 	var jniLibs []jniLib
 	var prebuiltJniPackages android.Paths
 	seenModulePaths := make(map[string]bool)
 
-	ctx.WalkDeps(func(module android.Module, parent android.Module) bool {
+	ctx.WalkDepsProxy(func(module, parent android.ModuleProxy) bool {
+		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
+			return false
+		}
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
 		if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) {
-			if dep, ok := module.(cc.LinkableInterface); ok {
+			if dep, ok := android.OtherModuleProvider(ctx, module, cc.LinkableInfoProvider); ok {
 				if filter != nil && !filter(parent, module) {
 					return false
 				}
 
-				lib := dep.OutputFile()
+				lib := dep.OutputFile
 				if lib.Valid() {
 					path := lib.Path()
 					if seenModulePaths[path.String()] {
@@ -1177,7 +1260,8 @@
 					}
 					seenModulePaths[path.String()] = true
 
-					if checkNativeSdkVersion && dep.SdkVersion() == "" {
+					commonInfo := android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider)
+					if checkNativeSdkVersion && commonInfo.SdkVersion == "" {
 						ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not",
 							otherName)
 					}
@@ -1185,11 +1269,11 @@
 					jniLibs = append(jniLibs, jniLib{
 						name:           ctx.OtherModuleName(module),
 						path:           path,
-						target:         module.Target(),
-						coverageFile:   dep.CoverageOutputFile(),
-						unstrippedFile: dep.UnstrippedOutputFile(),
-						partition:      dep.Partition(),
-						installPaths:   android.OtherModuleProviderOrDefault(ctx, dep, android.InstallFilesProvider).InstallFiles,
+						target:         commonInfo.Target,
+						coverageFile:   dep.CoverageOutputFile,
+						unstrippedFile: dep.UnstrippedOutputFile,
+						partition:      dep.Partition,
+						installPaths:   android.OtherModuleProviderOrDefault(ctx, module, android.InstallFilesProvider).InstallFiles,
 					})
 				} else if ctx.Config().AllowMissingDependencies() {
 					ctx.AddMissingDependencies([]string{otherName})
@@ -1214,10 +1298,13 @@
 }
 
 func (a *AndroidApp) WalkPayloadDeps(ctx android.BaseModuleContext, do android.PayloadDepsCallback) {
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		isExternal := !a.DepIsInSameApex(ctx, child)
-		if am, ok := child.(android.ApexModule); ok {
-			if !do(ctx, parent, am, isExternal) {
+	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+		// TODO(ccross): Should this use android.DepIsInSameApex?  Right now it is applying the android app
+		// heuristics to every transitive dependency, when it should probably be using the heuristics of the
+		// immediate parent.
+		isExternal := !a.GetDepInSameApexChecker().OutgoingDepIsInSameApex(ctx.OtherModuleDependencyTag(child))
+		if am, ok := android.OtherModuleProvider(ctx, child, android.CommonModuleInfoProvider); ok && am.IsApexModule {
+			if !do(ctx, parent, child, isExternal) {
 				return false
 			}
 		}
@@ -1231,12 +1318,12 @@
 	}
 
 	depsInfo := android.DepNameToDepInfoMap{}
-	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool {
 		depName := to.Name()
 
 		// Skip dependencies that are only available to APEXes; they are developed with updatability
 		// in mind and don't need manual approval.
-		if to.(android.ApexModule).NotAvailableForPlatform() {
+		if android.OtherModulePointerProviderOrDefault(ctx, to, android.CommonModuleInfoProvider).NotAvailableForPlatform {
 			return true
 		}
 
@@ -1246,18 +1333,9 @@
 			depsInfo[depName] = info
 		} else {
 			toMinSdkVersion := "(no version)"
-			if m, ok := to.(interface {
-				MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel
-			}); ok {
-				if v := m.MinSdkVersion(ctx); !v.IsNone() {
-					toMinSdkVersion = v.String()
-				}
-			} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
-				// TODO(b/175678607) eliminate the use of MinSdkVersion returning
-				// string
-				if v := m.MinSdkVersion(); v != "" {
-					toMinSdkVersion = v
-				}
+			if info, ok := android.OtherModuleProvider(ctx, to, android.CommonModuleInfoProvider); ok &&
+				!info.MinSdkVersion.IsPlatform && info.MinSdkVersion.ApiLevel != nil {
+				toMinSdkVersion = info.MinSdkVersion.ApiLevel.String()
 			}
 			depsInfo[depName] = android.ApexModuleDepInfo{
 				To:            depName,
@@ -1292,11 +1370,19 @@
 	return a.overridableAppProperties.Certificate.GetOrDefault(ctx, "")
 }
 
-func (a *AndroidApp) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	if IsJniDepTag(ctx.OtherModuleDependencyTag(dep)) {
+func (m *AndroidApp) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return AppDepInSameApexChecker{}
+}
+
+type AppDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m AppDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	if IsJniDepTag(tag) {
 		return true
 	}
-	return a.Library.DepIsInSameApex(ctx, dep)
+	return depIsInSameApex(tag)
 }
 
 func (a *AndroidApp) Privileged() bool {
@@ -1560,6 +1646,9 @@
 	}
 	a.generateAndroidBuildActions(ctx)
 
+	for _, c := range a.testProperties.Test_options.Tradefed_options {
+		configs = append(configs, c)
+	}
 	for _, module := range a.testProperties.Test_mainline_modules {
 		configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module})
 	}
@@ -1573,6 +1662,24 @@
 	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_common_data)...)
 	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_first_data)...)
 	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_first_prefer32_data)...)
+	a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Host_common_data)...)
+
+	// Install test deps
+	if !ctx.Config().KatiEnabled() {
+		pathInTestCases := android.PathForModuleInstall(ctx, ctx.Module().Name())
+		if a.testConfig != nil {
+			ctx.InstallFile(pathInTestCases, ctx.Module().Name()+".config", a.testConfig)
+		}
+		dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+		if dynamicConfig.Valid() {
+			ctx.InstallFile(pathInTestCases, ctx.Module().Name()+".dynamic", dynamicConfig.Path())
+		}
+		testDeps := append(a.data, a.extraTestConfigs...)
+		for _, data := range android.SortedUniquePaths(testDeps) {
+			dataPath := android.DataPath{SrcPath: data}
+			ctx.InstallTestData(pathInTestCases, []android.DataPath{dataPath})
+		}
+	}
 
 	android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
 		TestcaseRelDataFiles:    testcaseRel(a.data),
@@ -1591,6 +1698,26 @@
 		TopLevelTarget: true,
 	})
 
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Tags = append(moduleInfoJSON.Tags, "tests")
+	if a.testConfig != nil {
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, a.testConfig.String())
+	}
+	moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, a.extraTestConfigs.Strings()...)
+	if len(a.testProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, a.testProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+
+	if _, ok := testConfig.(android.WritablePath); ok {
+		moduleInfoJSON.AutoTestConfig = []string{"true"}
+	}
+	moduleInfoJSON.TestMainlineModules = append(moduleInfoJSON.TestMainlineModules, a.testProperties.Test_mainline_modules...)
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: a.testProperties.Test_suites,
+	})
 }
 
 func testcaseRel(paths android.Paths) []string {
@@ -1679,7 +1806,7 @@
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
 	module.appProperties.AlwaysPackageNativeLibs = true
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 
 	module.addHostAndDeviceProperties()
 	module.AddProperties(
@@ -1730,12 +1857,14 @@
 
 	// TODO(b/192032291): Disable by default after auditing downstream usage.
 	module.Module.dexProperties.Optimize.EnabledByDefault = true
+	module.Module.dexProperties.Optimize.Ignore_library_extends_program = proptools.BoolPtr(true)
+	module.Module.dexProperties.Optimize.Proguard_compatibility = proptools.BoolPtr(false)
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
 	module.appProperties.AlwaysPackageNativeLibs = true
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 
 	module.addHostAndDeviceProperties()
 	module.AddProperties(
@@ -1762,6 +1891,12 @@
 	Certificate *string
 }
 
+type AndroidAppCertificateInfo struct {
+	Certificate Certificate
+}
+
+var AndroidAppCertificateInfoProvider = blueprint.NewProvider[AndroidAppCertificateInfo]()
+
 // android_app_certificate modules can be referenced by the certificates property of android_app modules to select
 // the signing key.
 func AndroidAppCertificateFactory() android.Module {
@@ -1777,6 +1912,10 @@
 		Pem: android.PathForModuleSrc(ctx, cert+".x509.pem"),
 		Key: android.PathForModuleSrc(ctx, cert+".pk8"),
 	}
+
+	android.SetProvider(ctx, AndroidAppCertificateInfoProvider, AndroidAppCertificateInfo{
+		Certificate: c.Certificate,
+	})
 }
 
 type OverrideAndroidApp struct {
@@ -1931,7 +2070,7 @@
 		return clcMap
 	}
 
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		tag, isUsesLibTag := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag)
 		if !isUsesLibTag {
 			return
@@ -1939,31 +2078,35 @@
 
 		dep := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(m))
 
+		javaInfo, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider)
+		if !ok {
+			return
+		}
 		// Skip stub libraries. A dependency on the implementation library has been added earlier,
 		// so it will be added to CLC, but the stub shouldn't be. Stub libraries can be distingushed
 		// from implementation libraries by their name, which is different as it has a suffix.
-		if comp, ok := m.(SdkLibraryComponentDependency); ok {
-			if impl := comp.OptionalSdkLibraryImplementation(); impl != nil && *impl != dep {
+		if comp := javaInfo.SdkLibraryComponentDependencyInfo; comp != nil {
+			if impl := comp.OptionalSdkLibraryImplementation; impl != nil && *impl != dep {
 				return
 			}
 		}
 
-		if lib, ok := m.(UsesLibraryDependency); ok {
+		if lib := javaInfo.UsesLibraryDependencyInfo; lib != nil {
 			if _, ok := android.OtherModuleProvider(ctx, m, SdkLibraryInfoProvider); ok {
 				// Skip java_sdk_library dependencies that provide stubs, but not an implementation.
 				// This will be restricted to optional_uses_libs
-				if tag == usesLibOptTag && lib.DexJarBuildPath(ctx).PathOrNil() == nil {
+				if tag == usesLibOptTag && javaInfo.DexJarBuildPath.PathOrNil() == nil {
 					u.shouldDisableDexpreopt = true
 					return
 				}
 			}
 			libName := dep
-			if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
-				libName = *ulib.ProvidesUsesLib()
+			if ulib := javaInfo.ProvidesUsesLibInfo; ulib != nil && ulib.ProvidesUsesLib != nil {
+				libName = *ulib.ProvidesUsesLib
 			}
 			clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional,
-				lib.DexJarBuildPath(ctx).PathOrNil(), lib.DexJarInstallPath(),
-				lib.ClassLoaderContexts())
+				javaInfo.DexJarBuildPath.PathOrNil(), lib.DexJarInstallPath,
+				lib.ClassLoaderContexts)
 		} else if ctx.Config().AllowMissingDependencies() {
 			ctx.AddMissingDependencies([]string{dep})
 		} else {
@@ -2055,3 +2198,33 @@
 	classLoaderContexts *dexpreopt.ClassLoaderContextMap) {
 	u.verifyUsesLibraries(ctx, apk, nil, classLoaderContexts) // for APKs manifest_check does not write output file
 }
+
+// androidApp is an interface to handle all app modules (android_app, android_app_import, etc.) in
+// the same way.
+type androidApp interface {
+	android.Module
+	Privileged() bool
+	InstallApkName() string
+	OutputFile() android.Path
+	JacocoReportClassesFile() android.Path
+	Certificate() Certificate
+	BaseModuleName() string
+	PrivAppAllowlist() android.OptionalPath
+}
+
+var _ androidApp = (*AndroidApp)(nil)
+var _ androidApp = (*AndroidAppImport)(nil)
+var _ androidApp = (*AndroidTestHelperApp)(nil)
+
+func setCommonAppInfo(appInfo *AppInfo, m androidApp) {
+	appInfo.Privileged = m.Privileged()
+	appInfo.OutputFile = m.OutputFile()
+	appInfo.InstallApkName = m.InstallApkName()
+	appInfo.JacocoReportClassesFile = m.JacocoReportClassesFile()
+	appInfo.Certificate = m.Certificate()
+	appInfo.PrivAppAllowlist = m.PrivAppAllowlist()
+}
+
+type AppInfos []AppInfo
+
+var AppInfosProvider = blueprint.NewProvider[AppInfos]()
diff --git a/java/app_import.go b/java/app_import.go
index 8951c7d..9fb13ba 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -43,6 +43,12 @@
 		Description: "Uncompress embedded JNI libs",
 	})
 
+	stripEmbeddedJniLibsUnusedArchRule = pctx.AndroidStaticRule("strip-embedded-jni-libs-from-unused-arch", blueprint.RuleParams{
+		Command:     `${config.Zip2ZipCmd} -i $in -o $out -x 'lib/**/*.so' $extraArgs`,
+		CommandDeps: []string{"${config.Zip2ZipCmd}"},
+		Description: "Remove all JNI libs from unused architectures",
+	}, "extraArgs")
+
 	uncompressDexRule = pctx.AndroidStaticRule("uncompress-dex", blueprint.RuleParams{
 		Command: `if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` +
 			`${config.Zip2ZipCmd} -i $in -o $out -0 'classes*.dex'` +
@@ -56,6 +62,18 @@
 		CommandDeps: []string{"build/soong/scripts/check_prebuilt_presigned_apk.py", "${config.Aapt2Cmd}", "${config.ZipAlign}"},
 		Description: "Check presigned apk",
 	}, "extraArgs")
+
+	extractApkRule = pctx.AndroidStaticRule("extract-apk", blueprint.RuleParams{
+		Command:     "unzip -p $in $extract_apk > $out",
+		Description: "Extract specific sub apk",
+	}, "extract_apk")
+
+	gzipRule = pctx.AndroidStaticRule("gzip",
+		blueprint.RuleParams{
+			Command:     "prebuilts/build-tools/path/linux-x86/gzip -9 -c $in > $out",
+			CommandDeps: []string{"prebuilts/build-tools/path/linux-x86/gzip"},
+			Description: "gzip $out",
+		})
 )
 
 func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
@@ -150,10 +168,19 @@
 	// the prebuilt is Name() without "prebuilt_" prefix
 	Source_module_name *string
 
+	// Whether stripping all libraries from unused architectures.
+	Strip_unused_jni_arch *bool
+
 	// Path to the .prebuilt_info file of the prebuilt app.
 	// In case of mainline modules, the .prebuilt_info file contains the build_id that was used
 	// to generate the prebuilt.
 	Prebuilt_info *string `android:"path"`
+
+	// Path of extracted apk which is extracted from prebuilt apk. Use this extracted to import.
+	Extract_apk proptools.Configurable[string]
+
+	// Compress the output APK using gzip. Defaults to false.
+	Compress_apk proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 }
 
 func (a *AndroidAppImport) IsInstallable() bool {
@@ -278,6 +305,19 @@
 	})
 }
 
+func (a *AndroidAppImport) extractSubApk(
+	ctx android.ModuleContext, inputPath android.Path, outputPath android.WritablePath) {
+	extractApkPath := a.properties.Extract_apk.GetOrDefault(ctx, "")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   extractApkRule,
+		Input:  inputPath,
+		Output: outputPath,
+		Args: map[string]string{
+			"extract_apk": extractApkPath,
+		},
+	})
+}
+
 // Returns whether this module should have the dex file stored uncompressed in the APK.
 func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
 	if ctx.Config().UnbundledBuild() || proptools.Bool(a.properties.Preprocessed) {
@@ -292,8 +332,34 @@
 	return shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &a.dexpreopter)
 }
 
+func (a *AndroidAppImport) stripEmbeddedJniLibsUnusedArch(
+	ctx android.ModuleContext, inputPath android.Path, outputPath android.WritablePath) {
+	var wantedJniLibSlice []string
+	for _, target := range ctx.MultiTargets() {
+		supported_abis := target.Arch.Abi
+		for _, arch := range supported_abis {
+			wantedJniLibSlice = append(wantedJniLibSlice, " -X 'lib/"+arch+"/*.so'")
+		}
+	}
+	wantedJniLibString := strings.Join(wantedJniLibSlice, " ")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   stripEmbeddedJniLibsUnusedArchRule,
+		Input:  inputPath,
+		Output: outputPath,
+		Args: map[string]string{
+			"extraArgs": wantedJniLibString,
+		},
+	})
+}
+
 func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.generateAndroidBuildActions(ctx)
+
+	appInfo := &AppInfo{
+		Prebuilt: true,
+	}
+	setCommonAppInfo(appInfo, a)
+	android.SetProvider(ctx, AppInfoProvider, appInfo)
 }
 
 func (a *AndroidAppImport) InstallApkName() string {
@@ -336,10 +402,14 @@
 		ctx.ModuleErrorf("One and only one of certficate, presigned (implied by preprocessed), and default_dev_cert properties must be set")
 	}
 
-	// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
 	// TODO: LOCAL_PACKAGE_SPLITS
 
 	srcApk := a.prebuilt.SingleSourcePath(ctx)
+	if a.properties.Extract_apk.GetOrDefault(ctx, "") != "" {
+		extract_apk := android.PathForModuleOut(ctx, "extract-apk", ctx.ModuleName()+".apk")
+		a.extractSubApk(ctx, srcApk, extract_apk)
+		srcApk = extract_apk
+	}
 
 	// TODO: Install or embed JNI libraries
 
@@ -347,6 +417,13 @@
 	jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
 	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed)
 
+	// Strip all embedded JNI libs and include only required ones accordingly to the module's compile_multilib
+	if Bool(a.properties.Strip_unused_jni_arch) {
+		jnisStripped := android.PathForModuleOut(ctx, "jnis-stripped", ctx.ModuleName()+".apk")
+		a.stripEmbeddedJniLibsUnusedArch(ctx, jnisUncompressed, jnisStripped)
+		jnisUncompressed = jnisStripped
+	}
+
 	var pathFragments []string
 	relInstallPath := String(a.properties.Relative_install_path)
 
@@ -366,7 +443,9 @@
 
 	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries(ctx)
 	a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
-	if a.usesLibrary.shouldDisableDexpreopt {
+
+	// Disable Dexpreopt if Compress_apk is true. It follows the build/make/core/app_prebuilt_internal.mk
+	if a.usesLibrary.shouldDisableDexpreopt || a.properties.Compress_apk.GetOrDefault(ctx, false) {
 		a.dexpreopter.disableDexpreopt()
 	}
 
@@ -385,7 +464,13 @@
 		jnisUncompressed = dexUncompressed
 	}
 
-	apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
+	defaultApkFilename := a.BaseModuleName()
+	if a.properties.Compress_apk.GetOrDefault(ctx, false) {
+		defaultApkFilename += ".apk.gz"
+	} else {
+		defaultApkFilename += ".apk"
+	}
+	apkFilename := proptools.StringDefault(a.properties.Filename, defaultApkFilename)
 
 	// TODO: Handle EXTERNAL
 
@@ -425,7 +510,16 @@
 		a.certificate = PresignedCertificate
 	}
 
-	// TODO: Optionally compress the output apk.
+	if a.properties.Compress_apk.GetOrDefault(ctx, false) {
+		outputFile := android.PathForModuleOut(ctx, "compressed_apk", apkFilename)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        gzipRule,
+			Input:       a.outputFile,
+			Output:      outputFile,
+			Description: "Compressing " + a.outputFile.Base(),
+		})
+		a.outputFile = outputFile
+	}
 
 	if apexInfo.IsForPlatform() {
 		a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
@@ -443,6 +537,8 @@
 
 	ctx.SetOutputFiles([]android.Path{a.outputFile}, "")
 
+	buildComplianceMetadata(ctx)
+
 	// TODO: androidmk converter jni libs
 }
 
@@ -538,7 +634,15 @@
 	return Bool(a.properties.Privileged)
 }
 
-func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
+func (m *AndroidAppImport) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return AppImportDepInSameApexChecker{}
+}
+
+type AppImportDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m AppImportDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
 	// android_app_import might have extra dependencies via uses_libs property.
 	// Don't track the dependency as we don't automatically add those libraries
 	// to the classpath. It should be explicitly added to java_libs property of APEX
@@ -556,10 +660,8 @@
 var _ android.ApexModule = (*AndroidAppImport)(nil)
 
 // Implements android.ApexModule
-func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	// Do not check for prebuilts against the min_sdk_version of enclosing APEX
-	return nil
+func (m *AndroidAppImport) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
@@ -682,7 +784,29 @@
 func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.generateAndroidBuildActions(ctx)
 
+	a.updateModuleInfoJSON(ctx)
+
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: a.testProperties.Test_suites,
+	})
+}
+
+func (a *AndroidTestImport) updateModuleInfoJSON(ctx android.ModuleContext) {
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"APPS"}
+	moduleInfoJSON.CompatibilitySuites = []string{"null-suite"}
+	if len(a.testProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = a.testProperties.Test_suites
+	}
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
+	moduleInfoJSON.Tags = []string{"tests"}
+	moduleInfoJSON.RegisterNameOverride = a.BaseModuleName()
+	testConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml")
+	if testConfig.Valid() {
+		moduleInfoJSON.TestConfig = []string{testConfig.String()}
+	}
 }
 
 func (a *AndroidTestImport) InstallInTestcases() bool {
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 54a5e75..2600767 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -26,6 +26,7 @@
 )
 
 func TestAndroidAppImport(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -37,7 +38,47 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
+
+	// Check dexpreopt outputs.
+	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
+		variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule == nil {
+		t.Errorf("can't find dexpreopt outputs")
+	}
+
+	// Check cert signing flag.
+	signedApk := variant.Output("signed/foo.apk")
+	signingFlag := signedApk.Args["certificates"]
+	expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
+	if expected != signingFlag {
+		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+	}
+	rule := variant.Rule("genProvenanceMetaData")
+	android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
+	android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
+	android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
+	android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
+}
+
+func TestAndroidAppImportWithDefaults(t *testing.T) {
+	t.Parallel()
+	ctx, _ := testJava(t, `
+		android_app_import {
+			name: "foo",
+			defaults: ["foo_defaults"],
+		}
+
+		java_defaults {
+			name: "foo_defaults",
+			apk: "prebuilts/apk/app.apk",
+			certificate: "platform",
+			dex_preopt: {
+				enabled: true,
+			},
+		}
+		`)
+
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	// Check dexpreopt outputs.
 	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
@@ -60,6 +101,7 @@
 }
 
 func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -71,7 +113,7 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	// Check dexpreopt outputs. They shouldn't exist.
 	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule != nil ||
@@ -87,6 +129,7 @@
 }
 
 func TestAndroidAppImport_Presigned(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -98,7 +141,7 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	// Check dexpreopt outputs.
 	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
@@ -121,6 +164,7 @@
 }
 
 func TestAndroidAppImport_SigningLineage(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 	  android_app_import {
 			name: "foo",
@@ -137,7 +181,7 @@
 		}
 	`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	signedApk := variant.Output("signed/foo.apk")
 	// Check certificates
@@ -164,6 +208,7 @@
 }
 
 func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 	  android_app_import {
 			name: "foo",
@@ -178,7 +223,7 @@
 		}
 	`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	signedApk := variant.Output("signed/foo.apk")
 	// Check cert signing lineage flag.
@@ -196,6 +241,7 @@
 }
 
 func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -207,7 +253,7 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 
 	// Check dexpreopt outputs.
 	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil ||
@@ -231,6 +277,7 @@
 }
 
 func TestAndroidAppImport_DpiVariants(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app_import {
 			name: "foo",
@@ -302,7 +349,7 @@
 			}),
 		).RunTestWithBp(t, bp)
 
-		variant := result.ModuleForTests("foo", "android_common")
+		variant := result.ModuleForTests(t, "foo", "android_common")
 		input := variant.Output("jnis-uncompressed/foo.apk").Input.String()
 		if strings.HasSuffix(input, test.expected) {
 			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input)
@@ -317,6 +364,7 @@
 }
 
 func TestAndroidAppImport_Filename(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app_import {
 			name: "foo",
@@ -325,10 +373,25 @@
 		}
 
 		android_app_import {
+			name: "foo_compressed",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			compress_apk: true,
+		}
+
+		android_app_import {
 			name: "bar",
 			apk: "prebuilts/apk/app.apk",
 			presigned: true,
-			filename: "bar_sample.apk"
+			filename: "bar_sample.apk",
+		}
+
+		android_app_import {
+			name: "compressed_bar",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			filename: "bar_sample.apk",
+			compress_apk: true,
 		}
 		`)
 
@@ -347,16 +410,30 @@
 			expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto",
 		},
 		{
+			name:                 "foo_compressed",
+			expected:             "foo_compressed.apk.gz",
+			onDevice:             "/system/app/foo_compressed/foo_compressed.apk.gz",
+			expectedArtifactPath: "prebuilts/apk/app.apk",
+			expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/foo_compressed/provenance_metadata.textproto",
+		},
+		{
 			name:                 "bar",
 			expected:             "bar_sample.apk",
 			onDevice:             "/system/app/bar/bar_sample.apk",
 			expectedArtifactPath: "prebuilts/apk/app.apk",
 			expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/bar/provenance_metadata.textproto",
 		},
+		{
+			name:                 "compressed_bar",
+			expected:             "bar_sample.apk",
+			onDevice:             "/system/app/compressed_bar/bar_sample.apk",
+			expectedArtifactPath: "prebuilts/apk/app.apk",
+			expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/compressed_bar/provenance_metadata.textproto",
+		},
 	}
 
 	for _, test := range testCases {
-		variant := ctx.ModuleForTests(test.name, "android_common")
+		variant := ctx.ModuleForTests(t, test.name, "android_common")
 		if variant.MaybeOutput(test.expected).Rule == nil {
 			t.Errorf("can't find output named %q - all outputs: %v", test.expected, variant.AllOutputs())
 		}
@@ -380,6 +457,7 @@
 }
 
 func TestAndroidAppImport_ArchVariants(t *testing.T) {
+	t.Parallel()
 	// The test config's target arch is ARM64.
 	testCases := []struct {
 		name         string
@@ -505,9 +583,10 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx, _ := testJava(t, test.bp)
 
-			variant := ctx.ModuleForTests("foo", "android_common")
+			variant := ctx.ModuleForTests(t, "foo", "android_common")
 			if test.expected == "" {
 				if variant.Module().Enabled(android.PanickingConfigAndErrorContext(ctx)) {
 					t.Error("module should have been disabled, but wasn't")
@@ -530,6 +609,7 @@
 }
 
 func TestAndroidAppImport_SoongConfigVariables(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name         string
 		bp           string
@@ -572,6 +652,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx := android.GroupFixturePreparers(
 				prepareForJavaTest,
 				android.PrepareForTestWithSoongConfigModuleBuildComponents,
@@ -584,7 +665,7 @@
 				}),
 			).RunTestWithBp(t, test.bp).TestContext
 
-			variant := ctx.ModuleForTests("foo", "android_common")
+			variant := ctx.ModuleForTests(t, "foo", "android_common")
 			if test.expected == "" {
 				if variant.Module().Enabled(android.PanickingConfigAndErrorContext(ctx)) {
 					t.Error("module should have been disabled, but wasn't")
@@ -607,6 +688,7 @@
 }
 
 func TestAndroidAppImport_overridesDisabledAndroidApp(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -622,7 +704,7 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("prebuilt_foo", "android_common")
+	variant := ctx.ModuleForTests(t, "prebuilt_foo", "android_common")
 	a := variant.Module().(*AndroidAppImport)
 	// The prebuilt module should still be enabled and active even if the source-based counterpart
 	// is disabled.
@@ -635,6 +717,7 @@
 }
 
 func TestAndroidAppImport_relativeInstallPath(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app_import {
 			name: "no_relative_install_path",
@@ -664,28 +747,49 @@
 	}{
 		{
 			name:                "no_relative_install_path",
-			expectedInstallPath: "out/soong/target/product/test_device/system/app/no_relative_install_path/no_relative_install_path.apk",
+			expectedInstallPath: "out/target/product/test_device/system/app/no_relative_install_path/no_relative_install_path.apk",
 			errorMessage:        "Install path is not correct when relative_install_path is missing",
 		},
 		{
 			name:                "relative_install_path",
-			expectedInstallPath: "out/soong/target/product/test_device/system/app/my/path/relative_install_path/relative_install_path.apk",
+			expectedInstallPath: "out/target/product/test_device/system/app/my/path/relative_install_path/relative_install_path.apk",
 			errorMessage:        "Install path is not correct for app when relative_install_path is present",
 		},
 		{
 			name:                "privileged_relative_install_path",
-			expectedInstallPath: "out/soong/target/product/test_device/system/priv-app/my/path/privileged_relative_install_path/privileged_relative_install_path.apk",
+			expectedInstallPath: "out/target/product/test_device/system/priv-app/my/path/privileged_relative_install_path/privileged_relative_install_path.apk",
 			errorMessage:        "Install path is not correct for privileged app when relative_install_path is present",
 		},
 	}
 	for _, testCase := range testCases {
-		ctx, _ := testJava(t, bp)
-		mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*AndroidAppImport)
-		android.AssertPathRelativeToTopEquals(t, testCase.errorMessage, testCase.expectedInstallPath, mod.installPath)
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			ctx, _ := testJava(t, bp)
+			mod := ctx.ModuleForTests(t, testCase.name, "android_common").Module().(*AndroidAppImport)
+			android.AssertPathRelativeToTopEquals(t, testCase.errorMessage, testCase.expectedInstallPath, mod.installPath)
+		})
 	}
 }
 
+func TestAndroidAppImport_ExtractApk(t *testing.T) {
+	t.Parallel()
+	ctx, _ := testJava(t, `
+		android_app_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			certificate: "platform",
+			extract_apk: "extract_path/sub_app.apk"
+		}
+		`)
+
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
+	extractRuleArgs := variant.Output("extract-apk/foo.apk").BuildParams.Args
+	if extractRuleArgs["extract_apk"] != "extract_path/sub_app.apk" {
+		t.Errorf("Unexpected extract apk args: %s", extractRuleArgs["extract_apk"])
+	}
+}
 func TestAndroidTestImport(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_import {
 			name: "foo",
@@ -697,7 +801,7 @@
 		}
 		`)
 
-	test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
+	test := ctx.ModuleForTests(t, "foo", "android_common").Module().(*AndroidTestImport)
 
 	// Check android mks.
 	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
@@ -714,6 +818,7 @@
 }
 
 func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_import {
 			name: "foo",
@@ -734,16 +839,16 @@
 		}
 		`)
 
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 	jniRule := variant.Output("jnis-uncompressed/foo.apk").BuildParams.Rule.String()
 	if jniRule == android.Cp.String() {
-		t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
+		t.Errorf("Unexpected JNI uncompress rule command: %s", jniRule)
 	}
 
-	variant = ctx.ModuleForTests("foo_presigned", "android_common")
+	variant = ctx.ModuleForTests(t, "foo_presigned", "android_common")
 	jniRule = variant.Output("jnis-uncompressed/foo_presigned.apk").BuildParams.Rule.String()
 	if jniRule != android.Cp.String() {
-		t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+		t.Errorf("Unexpected JNI uncompress rule: %s", jniRule)
 	}
 	if variant.MaybeOutput("zip-aligned/foo_presigned.apk").Rule == nil {
 		t.Errorf("Presigned test apk should be aligned")
@@ -751,6 +856,7 @@
 }
 
 func TestAndroidTestImport_Preprocessed(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_test_import {
 			name: "foo",
@@ -761,10 +867,10 @@
 		`)
 
 	apkName := "foo.apk"
-	variant := ctx.ModuleForTests("foo", "android_common")
+	variant := ctx.ModuleForTests(t, "foo", "android_common")
 	jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String()
 	if jniRule != android.Cp.String() {
-		t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+		t.Errorf("Unexpected JNI uncompress rule: %s", jniRule)
 	}
 
 	// Make sure signing and aligning were skipped.
@@ -777,9 +883,11 @@
 }
 
 func TestAndroidAppImport_Preprocessed(t *testing.T) {
+	t.Parallel()
 	for _, dontUncompressPrivAppDexs := range []bool{false, true} {
 		name := fmt.Sprintf("dontUncompressPrivAppDexs:%t", dontUncompressPrivAppDexs)
 		t.Run(name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -804,10 +912,10 @@
 
 			// non-privileged app
 			apkName := "foo.apk"
-			variant := result.ModuleForTests("foo", "android_common")
+			variant := result.ModuleForTests(t, "foo", "android_common")
 			outputBuildParams := variant.Output(apkName).BuildParams
 			if outputBuildParams.Rule.String() != android.Cp.String() {
-				t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
+				t.Errorf("Unexpected prebuilt android_app_import rule: %s", outputBuildParams.Rule.String())
 			}
 
 			// Make sure compression and aligning were validated.
@@ -817,7 +925,7 @@
 
 			validationBuildParams := variant.Output("validated-prebuilt/check.stamp").BuildParams
 			if validationBuildParams.Rule.String() != checkPresignedApkRule.String() {
-				t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String())
+				t.Errorf("Unexpected validation rule: %s", validationBuildParams.Rule.String())
 			}
 
 			expectedScriptArgs := "--preprocessed"
@@ -826,10 +934,10 @@
 
 			// privileged app
 			apkName = "bar.apk"
-			variant = result.ModuleForTests("bar", "android_common")
+			variant = result.ModuleForTests(t, "bar", "android_common")
 			outputBuildParams = variant.Output(apkName).BuildParams
 			if outputBuildParams.Rule.String() != android.Cp.String() {
-				t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
+				t.Errorf("Unexpected prebuilt android_app_import rule: %s", outputBuildParams.Rule.String())
 			}
 
 			// Make sure compression and aligning were validated.
@@ -839,7 +947,7 @@
 
 			validationBuildParams = variant.Output("validated-prebuilt/check.stamp").BuildParams
 			if validationBuildParams.Rule.String() != checkPresignedApkRule.String() {
-				t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String())
+				t.Errorf("Unexpected validation rule: %s", validationBuildParams.Rule.String())
 			}
 
 			expectedScriptArgs = "--privileged"
@@ -854,6 +962,7 @@
 }
 
 func TestAndroidTestImport_UncompressDex(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name string
 		bp   string
@@ -894,7 +1003,7 @@
 			}),
 		).RunTestWithBp(t, bp)
 
-		foo := result.ModuleForTests("foo", "android_common")
+		foo := result.ModuleForTests(t, "foo", "android_common")
 		actual := foo.MaybeRule("uncompress-dex").Rule != nil
 
 		expect := !unbundled
@@ -917,6 +1026,7 @@
 				name := fmt.Sprintf("%s,unbundled:%t,dontUncompressPrivAppDexs:%t",
 					tt.name, unbundled, dontUncompressPrivAppDexs)
 				t.Run(name, func(t *testing.T) {
+					t.Parallel()
 					test(t, tt.bp, unbundled, dontUncompressPrivAppDexs)
 				})
 			}
@@ -925,6 +1035,7 @@
 }
 
 func TestAppImportMissingCertificateAllowMissingDependencies(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestWithAllowMissingDependencies,
@@ -936,7 +1047,7 @@
 			certificate: ":missing_certificate",
 		}`)
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 	fooApk := foo.Output("signed/foo.apk")
 	if fooApk.Rule != android.ErrorRule {
 		t.Fatalf("expected ErrorRule for foo.apk, got %s", fooApk.Rule.String())
diff --git a/java/app_set.go b/java/app_set.go
index 7997570..6a2c678 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -192,6 +192,12 @@
 		},
 	)
 
+	android.SetProvider(ctx, AppInfoProvider, &AppInfo{
+		AppSet:       true,
+		Privileged:   as.Privileged(),
+		OutputFile:   as.OutputFile(),
+		ApkCertsFile: as.apkcertsFile,
+	})
 }
 
 func (as *AndroidAppSet) InstallBypassMake() bool { return true }
diff --git a/java/app_set_test.go b/java/app_set_test.go
index c02b359..9b4c44b 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -24,13 +24,14 @@
 )
 
 func TestAndroidAppSet(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app_set {
 			name: "foo",
 			set: "prebuilts/apks/app.apks",
 			prerelease: true,
 		}`)
-	module := result.ModuleForTests("foo", "android_common")
+	module := result.ModuleForTests(t, "foo", "android_common")
 	const packedSplitApks = "foo.zip"
 	params := module.Output(packedSplitApks)
 	if params.Rule == nil {
@@ -65,6 +66,7 @@
 }
 
 func TestAndroidAppSet_Variants(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app_set {
 			name: "foo",
@@ -113,24 +115,24 @@
 	}
 
 	for _, test := range testCases {
-		ctx := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
-				variables.Platform_sdk_version = &test.sdkVersion
-			}),
-			android.FixtureModifyConfig(func(config android.Config) {
-				config.Targets[android.Android] = test.targets
-			}),
-		).RunTestWithBp(t, bp)
+		t.Run(test.name, func(t *testing.T) {
+			ctx := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+					variables.Platform_sdk_version = &test.sdkVersion
+				}),
+				android.FixtureModifyConfig(func(config android.Config) {
+					config.Targets[android.Android] = test.targets
+				}),
+			).RunTestWithBp(t, bp)
 
-		module := ctx.ModuleForTests("foo", "android_common")
-		const packedSplitApks = "foo.zip"
-		params := module.Output(packedSplitApks)
-		for k, v := range test.expected {
-			t.Run(test.name, func(t *testing.T) {
+			module := ctx.ModuleForTests(t, "foo", "android_common")
+			const packedSplitApks = "foo.zip"
+			params := module.Output(packedSplitApks)
+			for k, v := range test.expected {
 				android.AssertStringEquals(t, fmt.Sprintf("arg value for `%s`", k), v, params.Args[k])
-			})
-		}
+			}
+		})
 	}
 }
diff --git a/java/app_test.go b/java/app_test.go
index e9422a6..c6b22c7 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"sort"
 	"strings"
 	"testing"
@@ -41,6 +42,7 @@
 }
 
 func TestApp(t *testing.T) {
+	t.Parallel()
 	resourceFiles := []string{
 		"res/layout/layout.xml",
 		"res/values/strings.xml",
@@ -55,6 +57,7 @@
 
 	for _, moduleType := range []string{"android_app", "android_library"} {
 		t.Run(moduleType, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				prepareForJavaTest,
 				android.FixtureModifyMockFS(func(fs android.MockFS) {
@@ -69,15 +72,15 @@
 				}
 			`)
 
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
 			var expectedLinkImplicits []string
 
 			manifestFixer := foo.Output("manifest_fixer/AndroidManifest.xml")
 			expectedLinkImplicits = append(expectedLinkImplicits, manifestFixer.Output.String())
 
-			frameworkRes := result.ModuleForTests("framework-res", "android_common")
-			omniromRes := result.ModuleForTests("omnirom-res", "android_common")
+			frameworkRes := result.ModuleForTests(t, "framework-res", "android_common")
+			omniromRes := result.ModuleForTests(t,"omnirom-res", "android_common")
 			expectedLinkImplicits = append(expectedLinkImplicits,
 				frameworkRes.Output("package-res.apk").Output.String())
 			expectedLinkImplicits = append(expectedLinkImplicits,
@@ -96,13 +99,14 @@
 			expectedLinkImplicits = append(expectedLinkImplicits, list.Output.String())
 
 			// Check that the link rule uses
-			res := result.ModuleForTests("foo", "android_common").Output("package-res.apk")
+			res := result.ModuleForTests(t, "foo", "android_common").Output("package-res.apk")
 			android.AssertDeepEquals(t, "aapt2 link implicits", expectedLinkImplicits, res.Implicits.Strings())
 		})
 	}
 }
 
 func TestAppSplits(t *testing.T) {
+	t.Parallel()
 	ctx := testApp(t, `
 				android_app {
 					name: "foo",
@@ -111,7 +115,7 @@
 					sdk_version: "current"
 				}`)
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	expectedOutputs := []string{
 		"out/soong/.intermediates/foo/android_common/foo.apk",
@@ -127,6 +131,7 @@
 }
 
 func TestPlatformAPIs(t *testing.T) {
+	t.Parallel()
 	testJava(t, `
 		android_app {
 			name: "foo",
@@ -161,6 +166,7 @@
 }
 
 func TestAndroidAppLinkType(t *testing.T) {
+	t.Parallel()
 	testJava(t, `
 		android_app {
 			name: "foo",
@@ -250,6 +256,7 @@
 }
 
 func TestUpdatableApps(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name          string
 		bp            string
@@ -361,6 +368,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			errorHandler := android.FixtureExpectsNoErrors
 			if test.expectedError != "" {
 				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
@@ -375,6 +383,7 @@
 }
 
 func TestUpdatableApps_TransitiveDepsShouldSetMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	testJavaError(t, `module "bar".*: should support min_sdk_version\(29\)`, cc.GatherRequiredDepsForTest(android.Android)+`
 		android_app {
 			name: "foo",
@@ -393,6 +402,7 @@
 }
 
 func TestUpdatableApps_JniLibsShouldShouldSupportMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		android_app {
 			name: "foo",
@@ -413,6 +423,7 @@
 }
 
 func TestUpdatableApps_JniLibShouldBeBuiltAgainstMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	bp := cc.GatherRequiredDepsForTest(android.Android) + `
 		android_app {
 			name: "foo",
@@ -440,11 +451,11 @@
 
 	ctx, _ := testJavaWithFS(t, bp, fs)
 
-	inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
+	inputs := ctx.ModuleForTests(t, "libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
 	var crtbeginFound, crtendFound bool
-	expectedCrtBegin := ctx.ModuleForTests("crtbegin_so",
+	expectedCrtBegin := ctx.ModuleForTests(t, "crtbegin_so",
 		"android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output
-	expectedCrtEnd := ctx.ModuleForTests("crtend_so",
+	expectedCrtEnd := ctx.ModuleForTests(t, "crtend_so",
 		"android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output
 	implicits := []string{}
 	for _, input := range inputs {
@@ -468,6 +479,7 @@
 }
 
 func TestUpdatableApps_ErrorIfJniLibDoesntSupportMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	bp := cc.GatherRequiredDepsForTest(android.Android) + `
 		android_app {
 			name: "foo",
@@ -489,6 +501,7 @@
 }
 
 func TestUpdatableApps_ErrorIfDepMinSdkVersionIsHigher(t *testing.T) {
+	t.Parallel()
 	bp := cc.GatherRequiredDepsForTest(android.Android) + `
 		android_app {
 			name: "foo",
@@ -520,6 +533,7 @@
 }
 
 func TestUpdatableApps_ApplyDefaultUpdatableModuleVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -531,7 +545,7 @@
 			updatable: true,
 		}
 	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 	android.AssertStringDoesContain(t,
 		"com.android.foo: expected manifest fixer to set override-placeholder-version to RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION",
 		foo.BuildParams.Args["args"],
@@ -540,6 +554,7 @@
 }
 
 func TestUpdatableApps_ApplyOverrideApexManifestDefaultVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureMergeEnv(map[string]string{
@@ -554,7 +569,7 @@
 			updatable: true,
 		}
 	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 	android.AssertStringDoesContain(t,
 		"com.android.foo: expected manifest fixer to set override-placeholder-version to 1234",
 		foo.BuildParams.Args["args"],
@@ -563,6 +578,7 @@
 }
 
 func TestResourceDirs(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name      string
 		prop      string
@@ -599,12 +615,13 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				fs.AddToFixture(),
 			).RunTestWithBp(t, fmt.Sprintf(bp, testCase.prop))
 
-			module := result.ModuleForTests("foo", "android_common")
+			module := result.ModuleForTests(t, "foo", "android_common")
 			resourceList := module.MaybeOutput("aapt2/res.list")
 
 			var resources []string
@@ -620,6 +637,7 @@
 }
 
 func TestLibraryAssets(t *testing.T) {
+	t.Parallel()
 	bp := `
 			android_app {
 				name: "foo",
@@ -714,7 +732,8 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			m := ctx.ModuleForTests(test.name, "android_common")
+			t.Parallel()
+			m := ctx.ModuleForTests(t, test.name, "android_common")
 
 			// Check asset flag in aapt2 link flags
 			var aapt2link android.TestingBuildParams
@@ -749,6 +768,7 @@
 }
 
 func TestAppJavaResources(t *testing.T) {
+	t.Parallel()
 	bp := `
 			android_app {
 				name: "foo",
@@ -766,7 +786,7 @@
 
 	ctx := testApp(t, bp)
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 	fooResources := foo.Output("res/foo.jar")
 	fooDexJar := foo.Output("dex-withres/foo.jar")
 	fooDexJarAligned := foo.Output("dex-withres-aligned/foo.jar")
@@ -784,7 +804,7 @@
 		t.Errorf("expected aligned dex jar %q in foo apk inputs %q", w, g)
 	}
 
-	bar := ctx.ModuleForTests("bar", "android_common")
+	bar := ctx.ModuleForTests(t, "bar", "android_common")
 	barResources := bar.Output("res/bar.jar")
 	barApk := bar.Rule("combineApk")
 
@@ -794,6 +814,7 @@
 }
 
 func TestAndroidResourceProcessor(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                            string
 		appUsesRP                       bool
@@ -869,15 +890,24 @@
 			},
 			appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -890,21 +920,23 @@
 			directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"},
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
 			transitiveOverlays:  nil,
 			transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			transitiveSrcJars:   []string{"out/soong/.intermediates/transitive/android_common/gen/android/R.srcjar"},
-			transitiveClasspath: []string{"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar"},
+			transitiveClasspath: []string{"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar"},
 			transitiveCombined:  nil,
 
 			sharedResources: nil,
@@ -918,9 +950,9 @@
 			},
 			sharedSrcJars: []string{"out/soong/.intermediates/shared/android_common/gen/android/R.srcjar"},
 			sharedClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine-combined/shared_transitive_shared.jar",
-				"out/soong/.intermediates/shared_transitive_static/android_common/turbine-combined/shared_transitive_static.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine/shared_transitive_shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
 			},
 			sharedCombined: []string{
 				"out/soong/.intermediates/shared/android_common/javac/shared.jar",
@@ -967,17 +999,26 @@
 			},
 			appSrcJars: nil,
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -990,18 +1031,20 @@
 			},
 			directSrcJars: nil,
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/direct/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
@@ -1009,7 +1052,7 @@
 			transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			transitiveSrcJars:   nil,
 			transitiveClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
 			},
 			transitiveCombined: nil,
@@ -1023,12 +1066,12 @@
 			},
 			sharedSrcJars: nil,
 			sharedClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/shared/android_common/busybox/R.jar",
 				"out/soong/.intermediates/shared_transitive_static/android_common/busybox/R.jar",
 				"out/soong/.intermediates/shared_transitive_shared/android_common/busybox/R.jar",
-				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine-combined/shared_transitive_shared.jar",
-				"out/soong/.intermediates/shared_transitive_static/android_common/turbine-combined/shared_transitive_static.jar",
+				"out/soong/.intermediates/shared_transitive_shared/android_common/turbine/shared_transitive_shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
 			},
 			sharedCombined: []string{
 				"out/soong/.intermediates/shared/android_common/javac/shared.jar",
@@ -1072,18 +1115,27 @@
 			},
 			appSrcJars: nil,
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				// R.jar has to come before direct.jar
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
 				"out/soong/.intermediates/app/android_common/busybox/R.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			dontVerifyDirect:           true,
@@ -1115,15 +1167,24 @@
 			},
 			appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -1136,17 +1197,19 @@
 			},
 			directSrcJars: nil,
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/direct/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
 				"out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			dontVerifyTransitive:       true,
@@ -1177,15 +1240,24 @@
 			},
 			appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
 			appClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar",
-				"out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/shared/android_common/turbine/shared.jar",
+				"out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
+				"out/soong/.intermediates/direct/android_common/turbine/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 			appCombined: []string{
 				"out/soong/.intermediates/app/android_common/javac/app.jar",
-				"out/soong/.intermediates/direct/android_common/combined/direct.jar",
-				"out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar",
+				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
+				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
+				"out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
+				"out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
 			},
 
 			directResources: nil,
@@ -1198,14 +1270,16 @@
 			directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"},
 			directClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
-				"out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
+				"out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 			directCombined: []string{
 				"out/soong/.intermediates/direct/android_common/javac/direct.jar",
 				"out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
-				"out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
+				"out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
 			},
 
 			transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
@@ -1213,7 +1287,7 @@
 			transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
 			transitiveSrcJars:   nil,
 			transitiveClasspath: []string{
-				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar",
+				"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
 				"out/soong/.intermediates/transitive/android_common/busybox/R.jar",
 			},
 			transitiveCombined: nil,
@@ -1226,6 +1300,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			bp := fmt.Sprintf(`
 				android_app {
 					name: "app",
@@ -1333,7 +1408,7 @@
 			}
 
 			getAaptInfo := func(moduleName string) (aaptInfo aaptInfo) {
-				mod := result.ModuleForTests(moduleName, "android_common")
+				mod := result.ModuleForTests(t, moduleName, "android_common")
 				resourceListRule := mod.MaybeOutput("aapt2/res.list")
 				overlayListRule := mod.MaybeOutput("aapt2/overlay.list")
 				aaptRule := mod.Rule("aapt2Link")
@@ -1422,6 +1497,7 @@
 }
 
 func TestAndroidResourceOverlays(t *testing.T) {
+	t.Parallel()
 	type moduleAndVariant struct {
 		module  string
 		variant string
@@ -1460,7 +1536,6 @@
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
 				{"lib", "android_common"}: {
-					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 					"device/vendor/blah/overlay/lib/res/values/strings.xml",
 				},
@@ -1495,12 +1570,10 @@
 					"device/vendor/blah/overlay/bar/res/values/strings.xml",
 				},
 				{"lib", "android_common"}: {
-					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 					"device/vendor/blah/overlay/lib/res/values/strings.xml",
 				},
 				{"lib", "android_common_rro"}: {
-					"out/soong/.intermediates/lib2/android_common_rro/package-res.apk",
 					"lib/res/res/values/strings.xml",
 				},
 			},
@@ -1540,7 +1613,6 @@
 				},
 				{"bar", "android_common"}: {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
 				{"lib", "android_common"}: {
-					"out/soong/.intermediates/lib2/android_common/package-res.apk",
 					"lib/res/res/values/strings.xml",
 				},
 			},
@@ -1618,6 +1690,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				fs.AddToFixture(),
@@ -1649,7 +1722,7 @@
 			}
 
 			getResources := func(moduleName, variantName string) (resourceFiles, overlayFiles, rroDirs []string) {
-				module := result.ModuleForTests(moduleName, variantName)
+				module := result.ModuleForTests(t, moduleName, variantName)
 				resourceList := module.MaybeOutput("aapt2/res.list")
 				if resourceList.Rule != nil {
 					resourceFiles = resourceListToFiles(module, android.PathsRelativeToTop(resourceList.Inputs))
@@ -1708,7 +1781,7 @@
 }
 
 func checkSdkVersion(t *testing.T, result *android.TestResult, expectedSdkVersion string) {
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 	link := foo.Output("package-res.apk")
 	linkFlags := strings.Split(link.Args["flags"], " ")
 	min := android.IndexList("--min-sdk-version", linkFlags)
@@ -1727,6 +1800,7 @@
 }
 
 func TestAppSdkVersion(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                  string
 		sdkVersion            string
@@ -1794,6 +1868,7 @@
 	for _, moduleType := range []string{"android_app", "android_library"} {
 		for _, test := range testCases {
 			t.Run(moduleType+" "+test.name, func(t *testing.T) {
+				t.Parallel()
 				platformApiProp := ""
 				if test.platformApis {
 					platformApiProp = "platform_apis: true,"
@@ -1830,6 +1905,7 @@
 }
 
 func TestVendorAppSdkVersion(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                                  string
 		sdkVersion                            string
@@ -1872,6 +1948,7 @@
 		for _, sdkKind := range []string{"", "system_"} {
 			for _, test := range testCases {
 				t.Run(moduleType+" "+test.name, func(t *testing.T) {
+					t.Parallel()
 					bp := fmt.Sprintf(`%s {
 						name: "foo",
 						srcs: ["a.java"],
@@ -1903,6 +1980,7 @@
 }
 
 func TestJNIABI(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -1959,7 +2037,8 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(test.name, "android_common")
+			t.Parallel()
+			app := ctx.ModuleForTests(t, test.name, "android_common")
 			jniLibZip := app.Output("jnilibs.zip")
 			var abis []string
 			args := strings.Fields(jniLibZip.Args["jarArgs"])
@@ -1977,6 +2056,7 @@
 }
 
 func TestAppSdkVersionByPartition(t *testing.T) {
+	t.Parallel()
 	testJavaError(t, "sdk_version must have a value when the module is located at vendor or product", `
 		android_app {
 			name: "foo",
@@ -2021,6 +2101,7 @@
 }
 
 func TestJNIPackaging(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -2092,7 +2173,8 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(test.name, "android_common")
+			t.Parallel()
+			app := ctx.ModuleForTests(t, test.name, "android_common")
 			jniLibZip := app.MaybeOutput("jnilibs.zip")
 			if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
 				t.Errorf("expected jni packaged %v, got %v", w, g)
@@ -2112,6 +2194,7 @@
 }
 
 func TestJNISDK(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -2173,16 +2256,17 @@
 		{name: "app_vendor", vendorJNI: true},
 	}
 
-	platformJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_shared").
+	platformJNI := ctx.ModuleForTests(t, "libjni", "android_arm64_armv8-a_shared").
 		Output("libjni.so").Output.String()
-	sdkJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").
+	sdkJNI := ctx.ModuleForTests(t, "libjni", "android_arm64_armv8-a_sdk_shared").
 		Output("libjni.so").Output.String()
-	vendorJNI := ctx.ModuleForTests("libvendorjni", "android_vendor_arm64_armv8-a_shared").
+	vendorJNI := ctx.ModuleForTests(t, "libvendorjni", "android_vendor_arm64_armv8-a_shared").
 		Output("libvendorjni.so").Output.String()
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(test.name, "android_common")
+			t.Parallel()
+			app := ctx.ModuleForTests(t, test.name, "android_common")
 
 			jniLibZip := app.MaybeOutput("jnilibs.zip")
 			if len(jniLibZip.Implicits) != 1 {
@@ -2207,6 +2291,7 @@
 	}
 
 	t.Run("jni_uses_platform_apis_error", func(t *testing.T) {
+		t.Parallel()
 		testJavaError(t, `jni_uses_platform_apis: can only be set for modules that set sdk_version`, `
 			android_test {
 				name: "app_platform",
@@ -2217,6 +2302,7 @@
 	})
 
 	t.Run("jni_uses_sdk_apis_error", func(t *testing.T) {
+		t.Parallel()
 		testJavaError(t, `jni_uses_sdk_apis: can only be set for modules that do not set sdk_version`, `
 			android_test {
 				name: "app_sdk",
@@ -2229,6 +2315,7 @@
 }
 
 func TestCertificates(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                     string
 		bp                       string
@@ -2366,6 +2453,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -2381,7 +2469,7 @@
 				}),
 			).RunTestWithBp(t, test.bp)
 
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
 			certificate := foo.Module().(*AndroidApp).certificate
 			android.AssertPathRelativeToTopEquals(t, "certificates key", test.expectedCertificate+".pk8", certificate.Key)
@@ -2403,6 +2491,7 @@
 }
 
 func TestRequestV4SigningFlag(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name     string
 		bp       string
@@ -2447,11 +2536,12 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 			).RunTestWithBp(t, test.bp)
 
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
 			signapk := foo.Output("foo.apk")
 			signFlags := signapk.Args["flags"]
@@ -2461,6 +2551,7 @@
 }
 
 func TestPackageNameOverride(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name                string
 		bp                  string
@@ -2479,7 +2570,7 @@
 			packageNameOverride: "",
 			expected: []string{
 				"out/soong/.intermediates/foo/android_common/foo.apk",
-				"out/soong/target/product/test_device/system/app/foo/foo.apk",
+				"out/target/product/test_device/system/app/foo/foo.apk",
 			},
 		},
 		{
@@ -2495,7 +2586,7 @@
 			expected: []string{
 				// The package apk should be still be the original name for test dependencies.
 				"out/soong/.intermediates/foo/android_common/bar.apk",
-				"out/soong/target/product/test_device/system/app/bar/bar.apk",
+				"out/target/product/test_device/system/app/bar/bar.apk",
 			},
 		},
 		{
@@ -2511,13 +2602,14 @@
 			packageNameOverride: "",
 			expected: []string{
 				"out/soong/.intermediates/foo/android_common/bar.apk",
-				"out/soong/target/product/test_device/system/app/bar/bar.apk",
+				"out/target/product/test_device/system/app/bar/bar.apk",
 			},
 		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -2527,7 +2619,7 @@
 				}),
 			).RunTestWithBp(t, test.bp)
 
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
 			outSoongDir := result.Config.SoongOutDir()
 
@@ -2546,6 +2638,7 @@
 }
 
 func TestInstrumentationTargetOverridden(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "foo",
@@ -2567,7 +2660,7 @@
 		}),
 	).RunTestWithBp(t, bp)
 
-	bar := result.ModuleForTests("bar", "android_common")
+	bar := result.ModuleForTests(t, "bar", "android_common")
 	res := bar.Output("package-res.apk")
 	aapt2Flags := res.Args["flags"]
 	e := "--rename-instrumentation-target-package org.dandroid.bp"
@@ -2577,6 +2670,7 @@
 }
 
 func TestOverrideAndroidApp(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
 		t, `
 		android_app {
@@ -2654,7 +2748,7 @@
 			name:             "foo",
 			moduleName:       "foo",
 			variantName:      "android_common",
-			apkPath:          "out/soong/target/product/test_device/system/app/foo/foo.apk",
+			apkPath:          "out/target/product/test_device/system/app/foo/foo.apk",
 			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			certSigningFlags: "",
 			overrides:        []string{"qux"},
@@ -2666,7 +2760,7 @@
 			name:             "foo",
 			moduleName:       "bar",
 			variantName:      "android_common_bar",
-			apkPath:          "out/soong/target/product/test_device/system/app/bar/bar.apk",
+			apkPath:          "out/target/product/test_device/system/app/bar/bar.apk",
 			certFlag:         "cert/new_cert.x509.pem cert/new_cert.pk8",
 			certSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32",
 			overrides:        []string{"qux", "foo"},
@@ -2678,7 +2772,7 @@
 			name:             "foo",
 			moduleName:       "baz",
 			variantName:      "android_common_baz",
-			apkPath:          "out/soong/target/product/test_device/system/app/baz/baz.apk",
+			apkPath:          "out/target/product/test_device/system/app/baz/baz.apk",
 			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			certSigningFlags: "",
 			overrides:        []string{"qux", "foo"},
@@ -2690,7 +2784,7 @@
 			name:             "foo",
 			moduleName:       "baz_no_rename_resources",
 			variantName:      "android_common_baz_no_rename_resources",
-			apkPath:          "out/soong/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
+			apkPath:          "out/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
 			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			certSigningFlags: "",
 			overrides:        []string{"qux", "foo"},
@@ -2702,7 +2796,7 @@
 			name:             "foo_no_rename_resources",
 			moduleName:       "baz_base_no_rename_resources",
 			variantName:      "android_common_baz_base_no_rename_resources",
-			apkPath:          "out/soong/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
+			apkPath:          "out/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
 			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			certSigningFlags: "",
 			overrides:        []string{"qux", "foo_no_rename_resources"},
@@ -2714,7 +2808,7 @@
 			name:             "foo_no_rename_resources",
 			moduleName:       "baz_override_base_rename_resources",
 			variantName:      "android_common_baz_override_base_rename_resources",
-			apkPath:          "out/soong/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
+			apkPath:          "out/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
 			certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 			certSigningFlags: "",
 			overrides:        []string{"qux", "foo_no_rename_resources"},
@@ -2724,7 +2818,7 @@
 		},
 	}
 	for _, expected := range expectedVariants {
-		variant := result.ModuleForTests(expected.name, expected.variantName)
+		variant := result.ModuleForTests(t, expected.name, expected.variantName)
 
 		// Check the final apk name
 		variant.Output(expected.apkPath)
@@ -2759,6 +2853,7 @@
 }
 
 func TestOverrideAndroidAppOverrides(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(
 		t, `
 		android_app {
@@ -2808,7 +2903,7 @@
 		},
 	}
 	for _, expected := range expectedVariants {
-		variant := ctx.ModuleForTests(expected.name, expected.variantName)
+		variant := ctx.ModuleForTests(t, expected.name, expected.variantName)
 
 		// Check if the overrides field values are correctly aggregated.
 		mod := variant.Module().(*AndroidApp)
@@ -2817,6 +2912,7 @@
 }
 
 func TestOverrideAndroidAppWithPrebuilt(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
 		t, `
 		android_app {
@@ -2839,19 +2935,20 @@
 		`)
 
 	// An app that has an override that also has a prebuilt should not be hidden.
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 	if foo.Module().IsHideFromMake() {
 		t.Errorf("expected foo to have HideFromMake false")
 	}
 
 	// An override that also has a prebuilt should be hidden.
-	barOverride := result.ModuleForTests("foo", "android_common_bar")
+	barOverride := result.ModuleForTests(t, "foo", "android_common_bar")
 	if !barOverride.Module().IsHideFromMake() {
 		t.Errorf("expected bar override variant of foo to have HideFromMake true")
 	}
 }
 
 func TestOverrideAndroidAppStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -2891,41 +2988,42 @@
 		{
 			moduleName:  "foo",
 			variantName: "android_common",
-			apkPath:     "out/soong/target/product/test_device/system/app/foo/foo.apk",
+			apkPath:     "out/target/product/test_device/system/app/foo/foo.apk",
 		},
 		{
 			moduleName:  "foo",
 			variantName: "android_common_bar",
-			apkPath:     "out/soong/target/product/test_device/system/app/bar/bar.apk",
+			apkPath:     "out/target/product/test_device/system/app/bar/bar.apk",
 		},
 		{
 			moduleName:  "foo",
 			variantName: "android_common_baz",
-			apkPath:     "out/soong/target/product/test_device/system/app/baz_stem/baz_stem.apk",
+			apkPath:     "out/target/product/test_device/system/app/baz_stem/baz_stem.apk",
 		},
 		{
 			moduleName:  "foo2",
 			variantName: "android_common",
-			apkPath:     "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
+			apkPath:     "out/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
 		},
 		{
 			moduleName:  "foo2",
 			variantName: "android_common_bar2",
 			// Note that this may cause the duplicate output error.
-			apkPath: "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
+			apkPath: "out/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
 		},
 		{
 			moduleName:  "foo2",
 			variantName: "android_common_baz2",
-			apkPath:     "out/soong/target/product/test_device/system/app/baz2_stem/baz2_stem.apk",
+			apkPath:     "out/target/product/test_device/system/app/baz2_stem/baz2_stem.apk",
 		},
 	} {
-		variant := ctx.ModuleForTests(expected.moduleName, expected.variantName)
+		variant := ctx.ModuleForTests(t, expected.moduleName, expected.variantName)
 		variant.Output(expected.apkPath)
 	}
 }
 
 func TestOverrideAndroidAppDependency(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -2953,15 +3051,15 @@
 		`)
 
 	// Verify baz, which depends on the overridden module foo, has the correct classpath javac arg.
-	javac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
-	fooTurbine := "out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar"
+	javac := ctx.ModuleForTests(t, "baz", "android_common").Rule("javac")
+	fooTurbine := "out/soong/.intermediates/foo/android_common/turbine/foo.jar"
 	if !strings.Contains(javac.Args["classpath"], fooTurbine) {
 		t.Errorf("baz classpath %v does not contain %q", javac.Args["classpath"], fooTurbine)
 	}
 
 	// Verify qux, which depends on the overriding module bar, has the correct classpath javac arg.
-	javac = ctx.ModuleForTests("qux", "android_common").Rule("javac")
-	barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine-combined/foo.jar"
+	javac = ctx.ModuleForTests(t, "qux", "android_common").Rule("javac")
+	barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine/foo.jar"
 	if !strings.Contains(javac.Args["classpath"], barTurbine) {
 		t.Errorf("qux classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
 	}
@@ -3024,10 +3122,10 @@
 		},
 	}
 	for _, expected := range expectedVariants {
-		variant := ctx.ModuleForTests("foo_test", expected.variantName)
+		variant := ctx.ModuleForTests(t, "foo_test", expected.variantName)
 
 		// Check the final apk name
-		variant.Output("out/soong" + expected.apkPath)
+		variant.Output("out" + expected.apkPath)
 
 		// Check if the overrides field values are correctly aggregated.
 		mod := variant.Module().(*AndroidTest)
@@ -3038,7 +3136,7 @@
 
 		// Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
 		javac := variant.Rule("javac")
-		turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
+		turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine", "foo.jar")
 		if !strings.Contains(javac.Args["classpath"], turbine) {
 			t.Errorf("classpath %q does not contain %q", javac.Args["classpath"], turbine)
 		}
@@ -3115,7 +3213,7 @@
 	}
 
 	for _, test := range testCases {
-		variant := ctx.ModuleForTests(test.moduleName, test.variantName)
+		variant := ctx.ModuleForTests(t, test.moduleName, test.variantName)
 		params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml")
 
 		if len(test.expectedFlags) > 0 {
@@ -3161,6 +3259,7 @@
 }
 
 func TestStl(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
 			name: "libjni",
@@ -3203,7 +3302,8 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(test.name, "android_common")
+			t.Parallel()
+			app := ctx.ModuleForTests(t, test.name, "android_common")
 			jniLibZip := app.Output("jnilibs.zip")
 			var jnis []string
 			args := strings.Fields(jniLibZip.Args["jarArgs"])
@@ -3380,8 +3480,8 @@
 		}),
 	).RunTestWithBp(t, bp)
 
-	app := result.ModuleForTests("app", "android_common")
-	prebuilt := result.ModuleForTests("prebuilt", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
+	prebuilt := result.ModuleForTests(t, "prebuilt", "android_common")
 
 	// Test that implicit dependencies on java_sdk_library instances are passed to the manifest.
 	// These also include explicit `uses_libs`/`optional_uses_libs` entries, as they may be
@@ -3433,6 +3533,7 @@
 }
 
 func TestDexpreoptBcp(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_sdk_library {
 			name: "foo",
@@ -3475,6 +3576,7 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				prepareForJavaTest,
 				PrepareForTestWithJavaSdkLibraryFiles,
@@ -3484,7 +3586,7 @@
 				dexpreopt.FixtureSetPreoptWithUpdatableBcp(test.with),
 			).RunTestWithBp(t, bp)
 
-			app := result.ModuleForTests("app", "android_common")
+			app := result.ModuleForTests(t, "app", "android_common")
 			cmd := app.Rule("dexpreopt").RuleParams.Command
 			bcp := " -Xbootclasspath-locations:" + test.expect + " " // space at the end matters
 			android.AssertStringDoesContain(t, "dexpreopt app bcp", cmd, bcp)
@@ -3493,6 +3595,7 @@
 }
 
 func TestCodelessApp(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name   string
 		bp     string
@@ -3557,9 +3660,10 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx := testApp(t, test.bp)
 
-			foo := ctx.ModuleForTests("foo", "android_common")
+			foo := ctx.ModuleForTests(t, "foo", "android_common")
 			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
 			if strings.Contains(manifestFixerArgs, "--has-no-code") != test.noCode {
 				t.Errorf("unexpected manifest_fixer args: %q", manifestFixerArgs)
@@ -3569,6 +3673,7 @@
 }
 
 func TestUncompressDex(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name string
 		bp   string
@@ -3656,7 +3761,7 @@
 			}),
 		).RunTestWithBp(t, bp)
 
-		foo := result.ModuleForTests("foo", "android_common")
+		foo := result.ModuleForTests(t, "foo", "android_common")
 		dex := foo.Rule("r8")
 		uncompressedInDexJar := strings.Contains(dex.Args["zipFlags"], "-L 0")
 		aligned := foo.MaybeRule("zipalign").Rule != nil
@@ -3668,10 +3773,13 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
 			t.Run("platform", func(t *testing.T) {
+				t.Parallel()
 				test(t, tt.bp, tt.uncompressedPlatform, false)
 			})
 			t.Run("unbundled", func(t *testing.T) {
+				t.Parallel()
 				test(t, tt.bp, tt.uncompressedUnbundled, true)
 			})
 		})
@@ -3693,6 +3801,7 @@
 }
 
 func TestExportedProguardFlagFiles(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		android_app {
 			name: "foo",
@@ -3738,7 +3847,7 @@
 
 	`)
 
-	m := ctx.ModuleForTests("foo", "android_common")
+	m := ctx.ModuleForTests(t, "foo", "android_common")
 	r8 := m.Rule("java.r8")
 	implicits := r8.Implicits.RelativeToTop().Strings()
 	android.AssertStringListContains(t, "r8 implicits", implicits, "lib1proguard.cfg")
@@ -3754,6 +3863,7 @@
 }
 
 func TestTargetSdkVersionManifestFixer(t *testing.T) {
+	t.Parallel()
 	platform_sdk_codename := "Tiramisu"
 	platform_sdk_version := 33
 	testCases := []struct {
@@ -3806,43 +3916,47 @@
 		},
 	}
 	for _, testCase := range testCases {
-		targetSdkVersionTemplate := ""
-		if testCase.targetSdkVersionInBp != "" {
-			targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, testCase.targetSdkVersionInBp)
-		}
-		bp := fmt.Sprintf(`
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			targetSdkVersionTemplate := ""
+			if testCase.targetSdkVersionInBp != "" {
+				targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, testCase.targetSdkVersionInBp)
+			}
+			bp := fmt.Sprintf(`
 			android_app {
 				name: "foo",
 				sdk_version: "current",
 				%s
 			}
 			`, targetSdkVersionTemplate)
-		fixture := android.GroupFixturePreparers(
-			prepareForJavaTest,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				if testCase.platformSdkFinal {
-					variables.Platform_sdk_final = proptools.BoolPtr(true)
-				}
-				// explicitly set platform_sdk_codename to make the test deterministic
-				variables.Platform_sdk_codename = &platform_sdk_codename
-				variables.Platform_sdk_version = &platform_sdk_version
-				variables.Platform_version_active_codenames = []string{platform_sdk_codename}
-				// create a non-empty list if unbundledBuild==true
-				if testCase.unbundledBuild {
-					variables.Unbundled_build_apps = []string{"apex_a", "apex_b"}
-				}
-			}),
-		)
+			fixture := android.GroupFixturePreparers(
+				prepareForJavaTest,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					if testCase.platformSdkFinal {
+						variables.Platform_sdk_final = proptools.BoolPtr(true)
+					}
+					// explicitly set platform_sdk_codename to make the test deterministic
+					variables.Platform_sdk_codename = &platform_sdk_codename
+					variables.Platform_sdk_version = &platform_sdk_version
+					variables.Platform_version_active_codenames = []string{platform_sdk_codename}
+					// create a non-empty list if unbundledBuild==true
+					if testCase.unbundledBuild {
+						variables.Unbundled_build_apps = []string{"apex_a", "apex_b"}
+					}
+				}),
+			)
 
-		result := fixture.RunTestWithBp(t, bp)
-		foo := result.ModuleForTests("foo", "android_common")
+			result := fixture.RunTestWithBp(t, bp)
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
-		manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-		android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+			android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+		})
 	}
 }
 
 func TestDefaultAppTargetSdkVersionForUpdatableModules(t *testing.T) {
+	t.Parallel()
 	platform_sdk_codename := "Tiramisu"
 	platform_sdk_version := 33
 	testCases := []struct {
@@ -3898,11 +4012,13 @@
 		},
 	}
 	for _, testCase := range testCases {
-		targetSdkVersionTemplate := ""
-		if testCase.targetSdkVersionInBp != nil {
-			targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, *testCase.targetSdkVersionInBp)
-		}
-		bp := fmt.Sprintf(`
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			targetSdkVersionTemplate := ""
+			if testCase.targetSdkVersionInBp != nil {
+				targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, *testCase.targetSdkVersionInBp)
+			}
+			bp := fmt.Sprintf(`
 			android_app {
 				name: "foo",
 				sdk_version: "current",
@@ -3913,30 +4029,32 @@
 			}
 			`, targetSdkVersionTemplate, testCase.updatable, testCase.updatable) // enforce default target sdk version if app is updatable
 
-		fixture := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.PrepareForTestWithAllowMissingDependencies,
-			android.PrepareForTestWithAndroidMk,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				// explicitly set following platform variables to make the test deterministic
-				variables.Platform_sdk_final = &testCase.platform_sdk_final
-				variables.Platform_sdk_version = &platform_sdk_version
-				variables.Platform_sdk_codename = &platform_sdk_codename
-				variables.Platform_version_active_codenames = []string{platform_sdk_codename}
-				variables.Unbundled_build = proptools.BoolPtr(true)
-				variables.Unbundled_build_apps = []string{"sampleModule"}
-			}),
-		)
+			fixture := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.PrepareForTestWithAllowMissingDependencies,
+				android.PrepareForTestWithAndroidMk,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					// explicitly set following platform variables to make the test deterministic
+					variables.Platform_sdk_final = &testCase.platform_sdk_final
+					variables.Platform_sdk_version = &platform_sdk_version
+					variables.Platform_sdk_codename = &platform_sdk_codename
+					variables.Platform_version_active_codenames = []string{platform_sdk_codename}
+					variables.Unbundled_build = proptools.BoolPtr(true)
+					variables.Unbundled_build_apps = []string{"sampleModule"}
+				}),
+			)
 
-		result := fixture.RunTestWithBp(t, bp)
-		foo := result.ModuleForTests("foo", "android_common")
+			result := fixture.RunTestWithBp(t, bp)
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
-		manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-		android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+*testCase.targetSdkVersionExpected)
+			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+			android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+*testCase.targetSdkVersionExpected)
+		})
 	}
 }
 
 func TestEnforceDefaultAppTargetSdkVersionFlag(t *testing.T) {
+	t.Parallel()
 	platform_sdk_codename := "Tiramisu"
 	platform_sdk_version := 33
 	testCases := []struct {
@@ -3981,8 +4099,10 @@
 		},
 	}
 	for _, testCase := range testCases {
-		errExpected := testCase.expectedError != ""
-		bp := fmt.Sprintf(`
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			errExpected := testCase.expectedError != ""
+			bp := fmt.Sprintf(`
 			android_app {
 				name: "foo",
 				enforce_default_target_sdk_version: %t,
@@ -3993,35 +4113,37 @@
 			}
 			`, testCase.enforceDefaultTargetSdkVersion, testCase.targetSdkVersionInBp, testCase.updatable)
 
-		fixture := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.PrepareForTestWithAllowMissingDependencies,
-			android.PrepareForTestWithAndroidMk,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				// explicitly set following platform variables to make the test deterministic
-				variables.Platform_sdk_final = &testCase.platform_sdk_final
-				variables.Platform_sdk_version = &platform_sdk_version
-				variables.Platform_sdk_codename = &platform_sdk_codename
-				variables.Unbundled_build = proptools.BoolPtr(true)
-				variables.Unbundled_build_apps = []string{"sampleModule"}
-			}),
-		)
+			fixture := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.PrepareForTestWithAllowMissingDependencies,
+				android.PrepareForTestWithAndroidMk,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					// explicitly set following platform variables to make the test deterministic
+					variables.Platform_sdk_final = &testCase.platform_sdk_final
+					variables.Platform_sdk_version = &platform_sdk_version
+					variables.Platform_sdk_codename = &platform_sdk_codename
+					variables.Unbundled_build = proptools.BoolPtr(true)
+					variables.Unbundled_build_apps = []string{"sampleModule"}
+				}),
+			)
 
-		errorHandler := android.FixtureExpectsNoErrors
-		if errExpected {
-			errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
-		}
-		result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)
+			errorHandler := android.FixtureExpectsNoErrors
+			if errExpected {
+				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
+			}
+			result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)
 
-		if !errExpected {
-			foo := result.ModuleForTests("foo", "android_common")
-			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-			android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
-		}
+			if !errExpected {
+				foo := result.ModuleForTests(t, "foo", "android_common")
+				manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+				android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+			}
+		})
 	}
 }
 
 func TestEnforceDefaultAppTargetSdkVersionFlagForTests(t *testing.T) {
+	t.Parallel()
 	platform_sdk_codename := "Tiramisu"
 	platform_sdk_version := 33
 	testCases := []struct {
@@ -4054,8 +4176,10 @@
 		},
 	}
 	for _, testCase := range testCases {
-		errExpected := testCase.expectedError != ""
-		bp := fmt.Sprintf(`
+		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
+			errExpected := testCase.expectedError != ""
+			bp := fmt.Sprintf(`
 			android_test {
 				name: "foo",
 				enforce_default_target_sdk_version: %t,
@@ -4064,35 +4188,37 @@
 			}
 		`, testCase.enforceDefaultTargetSdkVersion, testCase.targetSdkVersionInBp)
 
-		fixture := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.PrepareForTestWithAllowMissingDependencies,
-			android.PrepareForTestWithAndroidMk,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				// explicitly set following platform variables to make the test deterministic
-				variables.Platform_sdk_final = &testCase.platform_sdk_final
-				variables.Platform_sdk_version = &platform_sdk_version
-				variables.Platform_sdk_codename = &platform_sdk_codename
-				variables.Unbundled_build = proptools.BoolPtr(true)
-				variables.Unbundled_build_apps = []string{"sampleModule"}
-			}),
-		)
+			fixture := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.PrepareForTestWithAllowMissingDependencies,
+				android.PrepareForTestWithAndroidMk,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					// explicitly set following platform variables to make the test deterministic
+					variables.Platform_sdk_final = &testCase.platform_sdk_final
+					variables.Platform_sdk_version = &platform_sdk_version
+					variables.Platform_sdk_codename = &platform_sdk_codename
+					variables.Unbundled_build = proptools.BoolPtr(true)
+					variables.Unbundled_build_apps = []string{"sampleModule"}
+				}),
+			)
 
-		errorHandler := android.FixtureExpectsNoErrors
-		if errExpected {
-			errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
-		}
-		result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)
+			errorHandler := android.FixtureExpectsNoErrors
+			if errExpected {
+				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
+			}
+			result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)
 
-		if !errExpected {
-			foo := result.ModuleForTests("foo", "android_common")
-			manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-			android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
-		}
+			if !errExpected {
+				foo := result.ModuleForTests(t, "foo", "android_common")
+				manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+				android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+			}
+		})
 	}
 }
 
 func TestAppMissingCertificateAllowMissingDependencies(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestWithAllowMissingDependencies,
@@ -4113,7 +4239,7 @@
 			sdk_version: "current",
 		}`)
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 	fooApk := foo.Output("foo.apk")
 	if fooApk.Rule != android.ErrorRule {
 		t.Fatalf("expected ErrorRule for foo.apk, got %s", fooApk.Rule.String())
@@ -4122,6 +4248,7 @@
 }
 
 func TestAppIncludesJniPackages(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -4184,7 +4311,8 @@
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			app := ctx.ModuleForTests(tc.name, "android_common")
+			t.Parallel()
+			app := ctx.ModuleForTests(t, tc.name, "android_common")
 
 			outputFile := "jnilibs.zip"
 			jniOutputLibZip := app.MaybeOutput(outputFile)
@@ -4208,6 +4336,7 @@
 }
 
 func TestTargetSdkVersionMtsTests(t *testing.T) {
+	t.Parallel()
 	platformSdkCodename := "Tiramisu"
 	android_test := "android_test"
 	android_test_helper_app := "android_test_helper_app"
@@ -4263,14 +4392,18 @@
 		}),
 	)
 	for _, testCase := range testCases {
-		result := fixture.RunTestWithBp(t, fmt.Sprintf(bpTemplate, testCase.moduleType, testCase.targetSdkVersionInBp, testCase.testSuites))
-		mytest := result.ModuleForTests("mytest", "android_common")
-		manifestFixerArgs := mytest.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
-		android.AssertStringDoesContain(t, testCase.desc, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+		t.Run(testCase.desc, func(t *testing.T) {
+			t.Parallel()
+			result := fixture.RunTestWithBp(t, fmt.Sprintf(bpTemplate, testCase.moduleType, testCase.targetSdkVersionInBp, testCase.testSuites))
+			mytest := result.ModuleForTests(t, "mytest", "android_common")
+			manifestFixerArgs := mytest.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+			android.AssertStringDoesContain(t, testCase.desc, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+		})
 	}
 }
 
 func TestPrivappAllowlist(t *testing.T) {
+	t.Parallel()
 	testJavaError(t, "privileged must be set in order to use privapp_allowlist", `
 		android_app {
 			name: "foo",
@@ -4296,8 +4429,8 @@
 		}
 		`,
 	)
-	app := result.ModuleForTests("foo", "android_common")
-	overrideApp := result.ModuleForTests("foo", "android_common_bar")
+	app := result.ModuleForTests(t, "foo", "android_common")
+	overrideApp := result.ModuleForTests(t, "foo", "android_common_bar")
 
 	// verify that privapp allowlist is created for override apps
 	overrideApp.Output("out/soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml")
@@ -4308,11 +4441,12 @@
 	}
 
 	// verify that permissions are copied to device
-	app.Output("out/soong/target/product/test_device/system/etc/permissions/foo.xml")
-	overrideApp.Output("out/soong/target/product/test_device/system/etc/permissions/bar.xml")
+	app.Output("out/target/product/test_device/system/etc/permissions/foo.xml")
+	overrideApp.Output("out/target/product/test_device/system/etc/permissions/bar.xml")
 }
 
 func TestPrivappAllowlistAndroidMk(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestWithAndroidMk,
@@ -4333,8 +4467,8 @@
 		}
 		`,
 	)
-	baseApp := result.ModuleForTests("foo", "android_common")
-	overrideApp := result.ModuleForTests("foo", "android_common_bar")
+	baseApp := result.ModuleForTests(t, "foo", "android_common")
+	overrideApp := result.ModuleForTests(t, "foo", "android_common_bar")
 
 	baseAndroidApp := baseApp.Module().(*AndroidApp)
 	baseEntries := android.AndroidMkEntriesForTest(t, result.TestContext, baseAndroidApp)[0]
@@ -4392,6 +4526,7 @@
 }
 
 func TestAppFlagsPackages(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureMergeMockFs(
@@ -4429,7 +4564,7 @@
 		}
 	`)
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	// android_app module depends on aconfig_declarations listed in flags_packages
 	android.AssertBoolEquals(t, "foo expected to depend on bar", true,
@@ -4456,6 +4591,7 @@
 }
 
 func TestAppFlagsPackagesPropagation(t *testing.T) {
+	t.Parallel()
 	ctx := testApp(t, `
 		aconfig_declarations {
 			name: "foo",
@@ -4513,7 +4649,7 @@
 		}
 	`)
 
-	bazApp := ctx.ModuleForTests("baz_app", "android_common")
+	bazApp := ctx.ModuleForTests(t, "baz_app", "android_common")
 
 	// android_app module depends on aconfig_declarations listed in flags_packages
 	// and that of static libs, but not libs
@@ -4533,6 +4669,7 @@
 
 // Test that dexpreopt is disabled if an optional_uses_libs exists, but does not provide an implementation.
 func TestNoDexpreoptOptionalUsesLibDoesNotHaveImpl(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_sdk_library_import {
 			name: "sdklib_noimpl",
@@ -4550,7 +4687,7 @@
 		}
 	`
 	result := prepareForJavaTest.RunTestWithBp(t, bp)
-	dexpreopt := result.ModuleForTests("app", "android_common").MaybeRule("dexpreopt").Rule
+	dexpreopt := result.ModuleForTests(t, "app", "android_common").MaybeRule("dexpreopt").Rule
 	android.AssertBoolEquals(t, "dexpreopt should be disabled if optional_uses_libs does not have an implementation", true, dexpreopt == nil)
 }
 
@@ -4592,7 +4729,77 @@
 	assertTestOnlyAndTopLevel(t, ctx, expectedTestOnly, expectedTopLevel)
 }
 
+func TestTestConfigTemplate(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+		android_test {
+			name: "android-test",
+			test_config_template: "AndroidTestTemplate.xml",
+			test_options: {
+				tradefed_options: [
+					{
+						name: "name1",
+						key: "key1",
+						value: "value1",
+					},
+					{
+						name: "name2",
+						key: "key2",
+						value: "value2",
+					},
+				],
+				test_runner_options: [
+					{
+						name: "name3",
+						key: "key3",
+						value: "value3",
+					},
+					{
+						name: "name4",
+						key: "key4",
+						value: "value4",
+					},
+				],
+			},
+		}
+	`)
+	type option struct {
+		name  string
+		key   string
+		value string
+	}
+	re := regexp.MustCompile(`<option name="(.*)" key="(.*)" value="(.*)" />`)
+	parse_options := func(optionsString string) []option {
+		lines := strings.Split(optionsString, `\n`)
+		var ret []option
+		for _, l := range lines {
+			sm := re.FindStringSubmatch(l)
+			if sm == nil {
+				continue
+			}
+			ret = append(ret, option{sm[1], sm[2], sm[3]})
+		}
+		return ret
+	}
+	rule := ctx.ModuleForTests(t, "android-test", "android_common").Rule("autogenInstrumentationTest")
+	android.AssertSameArray(t, "extraConfigs mismatch",
+		[]option{
+			{"name1", "key1", "value1"},
+			{"name2", "key2", "value2"},
+		},
+		parse_options(rule.Args["extraConfigs"]))
+	android.AssertSameArray(t, "extraTestRunnerConfigs mismatch",
+		[]option{
+			{"name3", "key3", "value3"},
+			{"name4", "key4", "value4"},
+		},
+		parse_options(rule.Args["extraTestRunnerConfigs"]))
+}
+
 func TestAppStem(t *testing.T) {
+	t.Parallel()
 	ctx := testApp(t, `
 				android_app {
 					name: "foo",
@@ -4601,7 +4808,7 @@
 					sdk_version: "current",
 				}`)
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	outputs := fmt.Sprint(foo.AllOutputs())
 	if !strings.Contains(outputs, "foo-new.apk") {
@@ -4610,6 +4817,7 @@
 }
 
 func TestAppMinSdkVersionOverride(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -4626,8 +4834,8 @@
 			min_sdk_version: "33",
 		}
 	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
-	fooOverride := result.ModuleForTests("com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer")
+	foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
+	fooOverride := result.ModuleForTests(t, "com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer")
 
 	android.AssertStringDoesContain(t,
 		"com.android.foo: expected manifest fixer to set minSdkVersion to T",
@@ -4643,6 +4851,7 @@
 }
 
 func TestNotApplyDefaultUpdatableModuleVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -4653,7 +4862,7 @@
 			min_sdk_version: "31",
 		}
 	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 	android.AssertStringDoesNotContain(t,
 		"com.android.foo: expected manifest fixer to not set override-placeholder-version",
 		foo.BuildParams.Args["args"],
@@ -4662,6 +4871,7 @@
 }
 
 func TestNotApplyOverrideApexManifestDefaultVersion(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureMergeEnv(map[string]string{
@@ -4675,7 +4885,7 @@
 			min_sdk_version: "31",
 		}
 	`)
-	foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer")
+	foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 	android.AssertStringDoesNotContain(t,
 		"com.android.foo: expected manifest fixer to not set override-placeholder-version",
 		foo.BuildParams.Args["args"],
@@ -4684,6 +4894,7 @@
 }
 
 func TestResourcesWithFlagDirectories(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureMergeMockFs(android.MockFS{
@@ -4702,7 +4913,7 @@
 			],
 		}
 	`)
-	fooModule := result.ModuleForTests("foo", "android_common")
+	fooModule := result.ModuleForTests(t, "foo", "android_common")
 	compileOutputPaths := fooModule.Rule("aapt2Compile").Outputs.Strings()
 
 	android.AssertStringListContains(
@@ -4788,17 +4999,20 @@
 		},
 	}
 	for _, tc := range testCases {
-		result := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.EnforceRROTargets = []string{"*"}
-			}),
-			android.OptionalFixturePreparer(tc.preparer),
-		).RunTestWithBp(t, bp)
-		vendorOverlayApk := result.ModuleForTests("foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("foo__test_product__auto_generated_rro_vendor.apk")
-		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, vendorOverlayApk.Rule != nil)
-		overrideVendorOverlayApk := result.ModuleForTests("override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("override_foo__test_product__auto_generated_rro_vendor.apk")
-		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.Rule != nil)
+		t.Run(tc.desc, func(t *testing.T) {
+			t.Parallel()
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.EnforceRROTargets = []string{"*"}
+				}),
+				android.OptionalFixturePreparer(tc.preparer),
+			).RunTestWithBp(t, bp)
+			vendorOverlayApk := result.ModuleForTests(t, "foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("foo__test_product__auto_generated_rro_vendor.apk")
+			android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, vendorOverlayApk.Rule != nil)
+			overrideVendorOverlayApk := result.ModuleForTests(t, "override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("override_foo__test_product__auto_generated_rro_vendor.apk")
+			android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.Rule != nil)
+		})
 	}
 }
 
@@ -4855,22 +5069,25 @@
 		},
 	}
 	for _, tc := range testCases {
-		result := android.GroupFixturePreparers(
-			PrepareForTestWithJavaDefaultModules,
-			android.PrepareForTestWithSoongConfigModuleBuildComponents,
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.EnforceRROTargets = []string{"*"}
-			}),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.DeviceResourceOverlays = []string{"device/company/test_product"}
-			}),
-			android.MockFS{
-				"res/foo.xml": nil,
-				"device/company/test_product/res/foo.xml": nil,
-			}.AddToFixture(),
-			android.OptionalFixturePreparer(tc.preparer),
-		).RunTestWithBp(t, bp)
-		overrideVendorOverlayApk := result.ModuleForTests("override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").Module().(*AutogenRuntimeResourceOverlay)
-		android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.exportPackage != nil)
+		t.Run(tc.desc, func(t *testing.T) {
+			t.Parallel()
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				android.PrepareForTestWithSoongConfigModuleBuildComponents,
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.EnforceRROTargets = []string{"*"}
+				}),
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.DeviceResourceOverlays = []string{"device/company/test_product"}
+				}),
+				android.MockFS{
+					"res/foo.xml": nil,
+					"device/company/test_product/res/foo.xml": nil,
+				}.AddToFixture(),
+				android.OptionalFixturePreparer(tc.preparer),
+			).RunTestWithBp(t, bp)
+			overrideVendorOverlayApk := result.ModuleForTests(t, "override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").Module().(*AutogenRuntimeResourceOverlay)
+			android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.exportPackage != nil)
+		})
 	}
 }
diff --git a/java/base.go b/java/base.go
index c0ac4ab..8aa0109 100644
--- a/java/base.go
+++ b/java/base.go
@@ -60,6 +60,9 @@
 	// This is most useful in the arch/multilib variants to remove non-common files
 	Exclude_srcs []string `android:"path,arch_variant"`
 
+	// list of Kotlin source files that should excluded from the list of common_srcs.
+	Exclude_common_srcs []string `android:"path,arch_variant"`
+
 	// list of directories containing Java resources
 	Java_resource_dirs []string `android:"arch_variant"`
 
@@ -87,6 +90,10 @@
 	// list of module-specific flags that will be used for kotlinc compiles
 	Kotlincflags []string `android:"arch_variant"`
 
+	// Kotlin language version to target. Currently only 1.9 and 2 are supported.
+	// See kotlinc's `-language-version` flag.
+	Kotlin_lang_version *string
+
 	// list of java libraries that will be in the classpath
 	Libs []string `android:"arch_variant"`
 
@@ -106,6 +113,10 @@
 	// if not blank, used as prefix to generate repackage rule
 	Jarjar_prefix *string
 
+	// Number of shards for jarjar. It needs to be an integer represented as a string.
+	// TODO(b/383559945) change it to int, once Configurable supports the type.
+	Jarjar_shards proptools.Configurable[string]
+
 	// If not blank, set the java version passed to javac as -source and -target
 	Java_version *string
 
@@ -362,13 +373,13 @@
 	e.initSdkLibraryComponent(module)
 }
 
-// Module/Import's DepIsInSameApex(...) delegates to this method.
+// Module/Import's OutgoingDepIsInSameApex(...) delegates to this method.
 //
-// This cannot implement DepIsInSameApex(...) directly as that leads to ambiguity with
+// This cannot implement OutgoingDepIsInSameApex(...) directly as that leads to ambiguity with
 // the one provided by ApexModuleBase.
-func (e *embeddableInModuleAndImport) depIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+func depIsInSameApex(tag blueprint.DependencyTag) bool {
 	// dependencies other than the static linkage are all considered crossing APEX boundary
-	if staticLibTag == ctx.OtherModuleDependencyTag(dep) {
+	if tag == staticLibTag {
 		return true
 	}
 	return false
@@ -618,7 +629,7 @@
 			return nil
 		}
 		if info.SdkVersion.Kind == android.SdkCorePlatform {
-			if useLegacyCorePlatformApi(ctx, android.OtherModuleProviderOrDefault(ctx, module, android.CommonModuleInfoKey).BaseModuleName) {
+			if useLegacyCorePlatformApi(ctx, android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).BaseModuleName) {
 				return fmt.Errorf("non stable SDK %v - uses legacy core platform", info.SdkVersion)
 			} else {
 				// Treat stable core platform as stable.
@@ -645,14 +656,17 @@
 
 	// Make sure this module doesn't statically link to modules with lower-ranked SDK link type.
 	// See rank() for details.
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(module)
-		switch module.(type) {
-		// TODO(satayev): cover other types as well, e.g. imports
-		case *Library, *AndroidLibrary:
+		libInfo, isJavaLibrary := android.OtherModuleProvider(ctx, module, JavaLibraryInfoProvider)
+		_, isAndroidLibrary := android.OtherModuleProvider(ctx, module, AndroidLibraryInfoProvider)
+		_, isJavaAconfigLibrary := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider)
+		// Exclude java_aconfig_library modules to maintain consistency with existing behavior.
+		if (isJavaLibrary && !libInfo.Prebuilt && !isJavaAconfigLibrary) || isAndroidLibrary {
+			// TODO(satayev): cover other types as well, e.g. imports
 			switch tag {
 			case bootClasspathTag, sdkLibTag, libTag, staticLibTag, java9LibTag:
-				j.checkSdkLinkType(ctx, module.(moduleWithSdkDep), tag.(dependencyTag))
+				j.checkSdkLinkType(ctx, module)
 			}
 		}
 	})
@@ -835,13 +849,18 @@
 }
 
 func (j *Module) AvailableFor(what string) bool {
-	if what == android.AvailableToPlatform && Bool(j.deviceProperties.Hostdex) {
+	return android.CheckAvailableForApex(what, j.ApexAvailableFor())
+}
+
+func (j *Module) ApexAvailableFor() []string {
+	list := j.ApexModuleBase.ApexAvailable()
+	if Bool(j.deviceProperties.Hostdex) {
 		// Exception: for hostdex: true libraries, the platform variant is created
 		// even if it's not marked as available to platform. In that case, the platform
 		// variant is used only for the hostdex and not installed to the device.
-		return true
+		list = append(list, android.AvailableToPlatform)
 	}
-	return j.ApexModuleBase.AvailableFor(what)
+	return android.FirstUniqueStrings(list)
 }
 
 func (j *Module) staticLibs(ctx android.BaseModuleContext) []string {
@@ -869,6 +888,10 @@
 	// Add dependency on libraries that provide additional hidden api annotations.
 	ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...)
 
+	// Add dependency on (soft) downstream libs from which to trace references during optimization.
+	traceRefs := j.dexProperties.Optimize.Trace_references_from.GetOrDefault(ctx, []string{})
+	ctx.AddVariationDependencies(nil, traceReferencesTag, traceRefs...)
+
 	// For library dependencies that are component libraries (like stubs), add the implementation
 	// as a dependency (dexpreopt needs to be against the implementation library, not stubs).
 	for _, dep := range libDeps {
@@ -922,7 +945,7 @@
 
 	if j.useCompose(ctx) {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag,
-			"androidx.compose.compiler_compiler-hosted-plugin")
+			"kotlin-compose-compiler-plugin")
 	}
 }
 
@@ -1141,7 +1164,7 @@
 	j.properties.Generated_srcjars = append(j.properties.Generated_srcjars, path)
 }
 
-func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars, extraDepCombinedJars android.Paths) {
+func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars, extraDepCombinedJars android.Paths) *JavaInfo {
 	// Auto-propagating jarjar rules
 	jarjarProviderData := j.collectJarJarRules(ctx)
 	if jarjarProviderData != nil {
@@ -1182,7 +1205,7 @@
 		flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags)
 	}
 
-	kotlinCommonSrcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Common_srcs, nil)
+	kotlinCommonSrcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Common_srcs, j.properties.Exclude_common_srcs)
 	if len(kotlinCommonSrcFiles.FilterOutByExt(".kt")) > 0 {
 		ctx.PropertyErrorf("common_srcs", "common_srcs must be .kt files")
 	}
@@ -1229,7 +1252,6 @@
 	uniqueSrcFiles = append(uniqueSrcFiles, uniqueJavaFiles...)
 	uniqueSrcFiles = append(uniqueSrcFiles, uniqueKtFiles...)
 	j.uniqueSrcFiles = uniqueSrcFiles
-	android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: uniqueSrcFiles.Strings()})
 
 	// We don't currently run annotation processors in turbine, which means we can't use turbine
 	// generated header jars when an annotation processor that generates API is enabled.  One
@@ -1265,7 +1287,7 @@
 		localHeaderJars, combinedHeaderJarFile := j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName,
 			extraCombinedJars)
 
-		combinedHeaderJarFile, jarjared := j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine")
+		combinedHeaderJarFile, jarjared := j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine", false)
 		if jarjared {
 			localHeaderJars = android.Paths{combinedHeaderJarFile}
 			transitiveStaticLibsHeaderJars = nil
@@ -1276,22 +1298,19 @@
 			transitiveStaticLibsHeaderJars = nil
 		}
 		if ctx.Failed() {
-			return
+			return nil
 		}
 		j.headerJarFile = combinedHeaderJarFile
 
-		if ctx.Config().UseTransitiveJarsInClasspath() {
-			if len(localHeaderJars) > 0 {
-				ctx.CheckbuildFile(localHeaderJars...)
-			} else {
-				// There are no local sources or resources in this module, so there is nothing to checkbuild.
-				ctx.UncheckedModule()
-			}
+		if len(localHeaderJars) > 0 {
+			ctx.CheckbuildFile(localHeaderJars...)
 		} else {
-			ctx.CheckbuildFile(j.headerJarFile)
+			// There are no local sources or resources in this module, so there is nothing to checkbuild.
+			ctx.UncheckedModule()
 		}
 
-		android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+		j.outputFile = j.headerJarFile
+		return &JavaInfo{
 			HeaderJars:                          android.PathsIfNonNil(j.headerJarFile),
 			LocalHeaderJars:                     localHeaderJars,
 			TransitiveStaticLibsHeaderJars:      depset.New(depset.PREORDER, localHeaderJars, transitiveStaticLibsHeaderJars),
@@ -1304,10 +1323,7 @@
 			StubsLinkType:                       j.stubsLinkType,
 			AconfigIntermediateCacheOutputPaths: deps.aconfigProtoFiles,
 			SdkVersion:                          j.SdkVersion(ctx),
-		})
-
-		j.outputFile = j.headerJarFile
-		return
+		}
 	}
 
 	if srcFiles.HasExt(".kt") {
@@ -1320,6 +1336,26 @@
 		kotlincFlags := j.properties.Kotlincflags
 		CheckKotlincFlags(ctx, kotlincFlags)
 
+		// Available kotlin versions can be found at
+		// https://github.com/JetBrains/kotlin/blob/master/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt#L560
+		// in the `LanguageVersion` class.
+		// For now, avoid targeting language versions directly, as we'd like to kee our source
+		// code version aligned as much as possible. Ideally, after defaulting to "2", we
+		// can remove the "1.9" option entirely, or at least make it emit a warning.
+		kotlin_default_lang_version := "1.9"
+		if build_flag_lang_version, ok := ctx.Config().GetBuildFlag("RELEASE_KOTLIN_LANG_VERSION"); ok {
+			kotlin_default_lang_version = build_flag_lang_version
+		}
+		kotlin_lang_version := proptools.StringDefault(j.properties.Kotlin_lang_version, kotlin_default_lang_version)
+		switch kotlin_lang_version {
+		case "1.9":
+			kotlincFlags = append(kotlincFlags, "-language-version 1.9")
+		case "2":
+			kotlincFlags = append(kotlincFlags, "-Xsuppress-version-warnings", "-Xconsistent-data-class-copy-visibility")
+		default:
+			ctx.PropertyErrorf("kotlin_lang_version", "Must be one of `1.9` or `2`")
+		}
+
 		// Workaround for KT-46512
 		kotlincFlags = append(kotlincFlags, "-Xsam-conversions=class")
 
@@ -1364,7 +1400,7 @@
 		kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName)
 		j.kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags)
 		if ctx.Failed() {
-			return
+			return nil
 		}
 
 		kotlinJarPath, _ := j.repackageFlagsIfNecessary(ctx, kotlinJar, jarName, "kotlinc")
@@ -1401,7 +1437,7 @@
 		shardingHeaderJars = localHeaderJars
 
 		var jarjared bool
-		j.headerJarFile, jarjared = j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine")
+		j.headerJarFile, jarjared = j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine", false)
 		if jarjared {
 			// jarjar modifies transitive static dependencies, use the combined header jar and drop the transitive
 			// static libs header jars.
@@ -1434,20 +1470,27 @@
 			// build.
 			flags = enableErrorproneFlags(flags)
 		} else if hasErrorproneableFiles && ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil {
-			// Otherwise, if the RUN_ERROR_PRONE environment variable is set, create
-			// a new jar file just for compiling with the errorprone compiler to.
-			// This is because we don't want to cause the java files to get completely
-			// rebuilt every time the state of the RUN_ERROR_PRONE variable changes.
-			// We also don't want to run this if errorprone is enabled by default for
-			// this module, or else we could have duplicated errorprone messages.
-			errorproneFlags := enableErrorproneFlags(flags)
-			errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
-			errorproneAnnoSrcJar := android.PathForModuleOut(ctx, "errorprone", "anno.srcjar")
+			if ctx.Config().RunErrorProneInline() {
+				// On CI, we're not going to toggle back/forth between errorprone and non-errorprone
+				// builds, so it's faster if we don't compile the module twice and instead always
+				// compile the module with errorprone.
+				flags = enableErrorproneFlags(flags)
+			} else {
+				// Otherwise, if the RUN_ERROR_PRONE environment variable is set, create
+				// a new jar file just for compiling with the errorprone compiler to.
+				// This is because we don't want to cause the java files to get completely
+				// rebuilt every time the state of the RUN_ERROR_PRONE variable changes.
+				// We also don't want to run this if errorprone is enabled by default for
+				// this module, or else we could have duplicated errorprone messages.
+				errorproneFlags := enableErrorproneFlags(flags)
+				errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
+				errorproneAnnoSrcJar := android.PathForModuleOut(ctx, "errorprone", "anno.srcjar")
 
-			transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneAnnoSrcJar, errorproneFlags, nil,
-				"errorprone", "errorprone")
+				transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneAnnoSrcJar, errorproneFlags, nil,
+					"errorprone", "errorprone")
 
-			extraJarDeps = append(extraJarDeps, errorprone)
+				extraJarDeps = append(extraJarDeps, errorprone)
+			}
 		}
 
 		if enableSharding {
@@ -1484,7 +1527,7 @@
 			localImplementationJars = append(localImplementationJars, classes)
 		}
 		if ctx.Failed() {
-			return
+			return nil
 		}
 	}
 
@@ -1524,7 +1567,7 @@
 		resourceJar := android.PathForModuleOut(ctx, "res", jarName)
 		TransformResourcesToJar(ctx, resourceJar, resArgs, resDeps)
 		if ctx.Failed() {
-			return
+			return nil
 		}
 		localResourceJars = append(localResourceJars, resourceJar)
 	}
@@ -1561,12 +1604,7 @@
 	completeStaticLibsResourceJars := depset.New(depset.PREORDER, localResourceJars, deps.transitiveStaticLibsResourceJars)
 
 	var combinedResourceJar android.Path
-	var resourceJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		resourceJars = completeStaticLibsResourceJars.ToList()
-	} else {
-		resourceJars = append(slices.Clone(localResourceJars), deps.staticResourceJars...)
-	}
+	resourceJars := completeStaticLibsResourceJars.ToList()
 	if len(resourceJars) == 1 {
 		combinedResourceJar = resourceJars[0]
 	} else if len(resourceJars) > 0 {
@@ -1587,12 +1625,7 @@
 
 	completeStaticLibsImplementationJars := depset.New(depset.PREORDER, localImplementationJars, deps.transitiveStaticLibsImplementationJars)
 
-	var jars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		jars = completeStaticLibsImplementationJars.ToList()
-	} else {
-		jars = append(slices.Clone(localImplementationJars), deps.staticJars...)
-	}
+	jars := completeStaticLibsImplementationJars.ToList()
 
 	jars = append(jars, extraDepCombinedJars...)
 
@@ -1628,7 +1661,7 @@
 	}
 
 	// jarjar implementation jar if necessary
-	jarjarFile, jarjarred := j.jarjarIfNecessary(ctx, outputFile, jarName, "")
+	jarjarFile, jarjarred := j.jarjarIfNecessary(ctx, outputFile, jarName, "", true)
 	if jarjarred {
 		localImplementationJars = android.Paths{jarjarFile}
 		completeStaticLibsImplementationJars = depset.New(depset.PREORDER, localImplementationJars, nil)
@@ -1637,7 +1670,7 @@
 
 	// jarjar resource jar if necessary
 	if combinedResourceJar != nil {
-		resourceJarJarFile, jarjarred := j.jarjarIfNecessary(ctx, combinedResourceJar, jarName, "resource")
+		resourceJarJarFile, jarjarred := j.jarjarIfNecessary(ctx, combinedResourceJar, jarName, "resource", false)
 		combinedResourceJar = resourceJarJarFile
 		if jarjarred {
 			localResourceJars = android.Paths{resourceJarJarFile}
@@ -1646,7 +1679,7 @@
 	}
 
 	if ctx.Failed() {
-		return
+		return nil
 	}
 
 	if j.ravenizer.enabled {
@@ -1710,7 +1743,7 @@
 		CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages)
 
 		if ctx.Failed() {
-			return
+			return nil
 		}
 	}
 
@@ -1723,7 +1756,7 @@
 		headerJarFile := android.PathForModuleOut(ctx, "javac-header", jarName)
 		convertImplementationJarToHeaderJar(ctx, j.implementationJarFile, headerJarFile)
 		j.headerJarFile = headerJarFile
-		if len(localImplementationJars) == 1 && ctx.Config().UseTransitiveJarsInClasspath() {
+		if len(localImplementationJars) == 1 {
 			localHeaderJarFile := android.PathForModuleOut(ctx, "local-javac-header", jarName)
 			convertImplementationJarToHeaderJar(ctx, localImplementationJars[0], localHeaderJarFile)
 			localHeaderJars = append(localHeaderJars, localHeaderJarFile)
@@ -1735,7 +1768,7 @@
 	// enforce syntax check to jacoco filters for any build (http://b/183622051)
 	specs := j.jacocoModuleToZipCommand(ctx)
 	if ctx.Failed() {
-		return
+		return nil
 	}
 
 	completeStaticLibsImplementationJarsToCombine := completeStaticLibsImplementationJars
@@ -1753,16 +1786,10 @@
 
 	// merge implementation jar with resources if necessary
 	var implementationAndResourcesJarsToCombine android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		resourceJars := completeStaticLibsResourceJars.ToList()
-		if len(resourceJars) > 0 {
-			implementationAndResourcesJarsToCombine = append(resourceJars, completeStaticLibsImplementationJarsToCombine.ToList()...)
-			implementationAndResourcesJarsToCombine = append(implementationAndResourcesJarsToCombine, extraDepCombinedJars...)
-		}
-	} else {
-		if combinedResourceJar != nil {
-			implementationAndResourcesJarsToCombine = android.Paths{combinedResourceJar, outputFile}
-		}
+	combinedResourceJars := completeStaticLibsResourceJars.ToList()
+	if len(combinedResourceJars) > 0 {
+		implementationAndResourcesJarsToCombine = slices.Concat(combinedResourceJars,
+			completeStaticLibsImplementationJarsToCombine.ToList(), extraDepCombinedJars)
 	}
 
 	if len(implementationAndResourcesJarsToCombine) > 0 {
@@ -1789,7 +1816,7 @@
 				classesJar:    outputFile,
 				jarName:       jarName,
 			}
-			if j.GetProfileGuided(ctx) && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting(ctx) {
+			if j.GetProfileGuided(ctx) && j.optimizeOrObfuscateEnabled(ctx) && !j.EnableProfileRewriting(ctx) {
 				ctx.PropertyErrorf("enable_profile_rewriting",
 					"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on. The attached profile should be sourced from an unoptimized/unobfuscated APK.",
 				)
@@ -1803,7 +1830,7 @@
 			}
 			dexOutputFile, dexArtProfileOutput := j.dexer.compileDex(ctx, params)
 			if ctx.Failed() {
-				return
+				return nil
 			}
 
 			// If r8/d8 provides a profile that matches the optimized dex, use that for dexpreopt.
@@ -1812,18 +1839,9 @@
 			}
 
 			// merge dex jar with resources if necessary
-			var dexAndResourceJarsToCombine android.Paths
-			if ctx.Config().UseTransitiveJarsInClasspath() {
-				resourceJars := completeStaticLibsResourceJars.ToList()
-				if len(resourceJars) > 0 {
-					dexAndResourceJarsToCombine = append(android.Paths{dexOutputFile}, resourceJars...)
-				}
-			} else {
-				if combinedResourceJar != nil {
-					dexAndResourceJarsToCombine = android.Paths{dexOutputFile, combinedResourceJar}
-				}
-			}
-			if len(dexAndResourceJarsToCombine) > 0 {
+			if len(combinedResourceJars) > 0 {
+				dexAndResourceJarsToCombine := append(android.Paths{dexOutputFile}, combinedResourceJars...)
+
 				combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
 				TransformJarsToJar(ctx, combinedJar, "for dex resources", dexAndResourceJarsToCombine, android.OptionalPath{},
 					false, nil, nil)
@@ -1862,7 +1880,7 @@
 		}
 
 		if ctx.Failed() {
-			return
+			return nil
 		}
 	}
 
@@ -1895,21 +1913,19 @@
 
 	j.collectTransitiveSrcFiles(ctx, srcFiles)
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		if len(localImplementationJars) > 0 || len(localResourceJars) > 0 || len(localHeaderJars) > 0 {
-			ctx.CheckbuildFile(localImplementationJars...)
-			ctx.CheckbuildFile(localResourceJars...)
-			ctx.CheckbuildFile(localHeaderJars...)
-		} else {
-			// There are no local sources or resources in this module, so there is nothing to checkbuild.
-			ctx.UncheckedModule()
-		}
+	if len(localImplementationJars) > 0 || len(localResourceJars) > 0 || len(localHeaderJars) > 0 {
+		ctx.CheckbuildFile(localImplementationJars...)
+		ctx.CheckbuildFile(localResourceJars...)
+		ctx.CheckbuildFile(localHeaderJars...)
 	} else {
-		ctx.CheckbuildFile(j.implementationJarFile)
-		ctx.CheckbuildFile(j.headerJarFile)
+		// There are no local sources or resources in this module, so there is nothing to checkbuild.
+		ctx.UncheckedModule()
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
+	j.outputFile = outputFile.WithoutRel()
+
+	return &JavaInfo{
 		HeaderJars:           android.PathsIfNonNil(j.headerJarFile),
 		RepackagedHeaderJars: android.PathsIfNonNil(repackagedHeaderJarFile),
 
@@ -1934,10 +1950,8 @@
 		StubsLinkType:                       j.stubsLinkType,
 		AconfigIntermediateCacheOutputPaths: j.aconfigCacheFiles,
 		SdkVersion:                          j.SdkVersion(ctx),
-	})
-
-	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
-	j.outputFile = outputFile.WithoutRel()
+		OutputFile:                          j.outputFile,
+	}
 }
 
 func (j *Module) useCompose(ctx android.BaseModuleContext) bool {
@@ -1945,7 +1959,7 @@
 }
 
 func collectDepProguardSpecInfo(ctx android.ModuleContext) (transitiveProguardFlags, transitiveUnconditionalExportedFlags []depset.DepSet[android.Path]) {
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		depProguardInfo, _ := android.OtherModuleProvider(ctx, m, ProguardSpecInfoProvider)
 		depTag := ctx.OtherModuleDependencyTag(m)
 
@@ -2041,7 +2055,9 @@
 		} else if strings.HasPrefix(flag, "-Xintellij-plugin-root") {
 			ctx.PropertyErrorf("kotlincflags",
 				"Bad flag: `%s`, only use internal compiler for consistency.", flag)
-		} else if inList(flag, config.KotlincIllegalFlags) {
+		} else if slices.ContainsFunc(config.KotlincIllegalFlags, func(f string) bool {
+			return strings.HasPrefix(flag, f)
+		}) {
 			ctx.PropertyErrorf("kotlincflags", "Flag `%s` already used by build system", flag)
 		} else if flag == "-include-runtime" {
 			ctx.PropertyErrorf("kotlincflags", "Bad flag: `%s`, do not include runtime.", flag)
@@ -2070,13 +2086,8 @@
 
 	// Combine any static header libraries into classes-header.jar. If there is only
 	// one input jar this step will be skipped.
-	var jars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		depSet := depset.New(depset.PREORDER, localHeaderJars, deps.transitiveStaticLibsHeaderJars)
-		jars = depSet.ToList()
-	} else {
-		jars = append(slices.Clone(localHeaderJars), deps.staticHeaderJars...)
-	}
+	depSet := depset.New(depset.PREORDER, localHeaderJars, deps.transitiveStaticLibsHeaderJars)
+	jars := depSet.ToList()
 
 	// we cannot skip the combine step for now if there is only one jar
 	// since we have to strip META-INF/TRANSITIVE dir from turbine.jar
@@ -2116,7 +2127,7 @@
 	directStaticLibs := android.Paths{}
 	transitiveLibs := []depset.DepSet[android.Path]{}
 	transitiveStaticLibs := []depset.DepSet[android.Path]{}
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		// don't add deps of the prebuilt version of the same library
 		if ctx.ModuleName() == android.RemoveOptionalPrebuiltPrefix(module.Name()) {
 			return
@@ -2184,6 +2195,8 @@
 func (j *Module) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
 	if j.expandJarjarRules != nil {
 		dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
+	}
+	if j.headerJarFile != nil {
 		// Add the header jar so that the rdeps can be resolved to the repackaged classes.
 		dpInfo.Jars = append(dpInfo.Jars, j.headerJarFile.String())
 	}
@@ -2202,29 +2215,33 @@
 
 func (j *Module) hasCode(ctx android.ModuleContext) bool {
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
-	return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0
+	return len(srcFiles) > 0 || len(ctx.GetDirectDepsProxyWithTag(staticLibTag)) > 0
 }
 
 // Implements android.ApexModule
-func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	return j.depIsInSameApex(ctx, dep)
+func (m *Module) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return JavaDepInSameApexChecker{}
+}
+
+type JavaDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m JavaDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	return depIsInSameApex(tag)
 }
 
 // Implements android.ApexModule
-func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
+func (j *Module) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
 	sdkVersionSpec := j.SdkVersion(ctx)
 	minSdkVersion := j.MinSdkVersion(ctx)
-	if !minSdkVersion.Specified() {
-		return fmt.Errorf("min_sdk_version is not specified")
-	}
+
 	// If the module is compiling against core (via sdk_version), skip comparison check.
 	if sdkVersionSpec.Kind == android.SdkCore {
-		return nil
+		return android.MinApiLevel
 	}
-	if minSdkVersion.GreaterThan(sdkVersion) {
-		return fmt.Errorf("newer SDK(%v)", minSdkVersion)
-	}
-	return nil
+
+	return minSdkVersion
 }
 
 func (j *Module) Stem() string {
@@ -2240,7 +2257,7 @@
 
 func (j *Module) collectTransitiveSrcFiles(ctx android.ModuleContext, mine android.Paths) {
 	var fromDeps []depset.DepSet[android.Path]
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if tag == staticLibTag {
 			if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
@@ -2359,7 +2376,7 @@
 // checkSdkLinkType make sures the given dependency doesn't have a lower SDK link type rank than
 // this module's. See the comment on rank() for details and an example.
 func (j *Module) checkSdkLinkType(
-	ctx android.ModuleContext, dep moduleWithSdkDep, tag dependencyTag) {
+	ctx android.ModuleContext, dep android.ModuleProxy) {
 	if ctx.Host() {
 		return
 	}
@@ -2368,7 +2385,12 @@
 	if stubs {
 		return
 	}
-	depLinkType, _ := dep.getSdkLinkType(ctx, ctx.OtherModuleName(dep))
+	info, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider)
+	if !ok || info.ModuleWithSdkDepInfo == nil {
+		panic(fmt.Errorf("dependency doesn't have ModuleWithSdkDepInfo: %v", dep))
+	}
+
+	depLinkType := info.ModuleWithSdkDepInfo.SdkLinkType
 
 	if myLinkType.rank() < depLinkType.rank() {
 		ctx.ModuleErrorf("compiles against %v, but dependency %q is compiling against %v. "+
@@ -2393,7 +2415,7 @@
 	var transitiveStaticJarsImplementationLibs []depset.DepSet[android.Path]
 	var transitiveStaticJarsResourceLibs []depset.DepSet[android.Path]
 
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
@@ -2427,7 +2449,7 @@
 				deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...)
 				transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 			case sdkLibTag, libTag, instrumentationForTag:
-				if _, ok := module.(*Plugin); ok {
+				if _, ok := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider); ok {
 					ctx.ModuleErrorf("a java_plugin (%s) cannot be used as a libs dependency", otherName)
 				}
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
@@ -2445,7 +2467,7 @@
 				deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...)
 				transitiveJava9ClasspathHeaderJars = append(transitiveJava9ClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars)
 			case staticLibTag:
-				if _, ok := module.(*Plugin); ok {
+				if _, ok := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider); ok {
 					ctx.ModuleErrorf("a java_plugin (%s) cannot be used as a static_libs dependency", otherName)
 				}
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
@@ -2465,40 +2487,40 @@
 				transitiveStaticJarsImplementationLibs = append(transitiveStaticJarsImplementationLibs, dep.TransitiveStaticLibsImplementationJars)
 				transitiveStaticJarsResourceLibs = append(transitiveStaticJarsResourceLibs, dep.TransitiveStaticLibsResourceJars)
 			case pluginTag:
-				if plugin, ok := module.(*Plugin); ok {
-					if plugin.pluginProperties.Processor_class != nil {
-						addPlugins(&deps, dep.ImplementationAndResourcesJars, *plugin.pluginProperties.Processor_class)
+				if plugin, ok := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider); ok {
+					if plugin.ProcessorClass != nil {
+						addPlugins(&deps, dep.ImplementationAndResourcesJars, *plugin.ProcessorClass)
 					} else {
 						addPlugins(&deps, dep.ImplementationAndResourcesJars)
 					}
 					// Turbine doesn't run annotation processors, so any module that uses an
 					// annotation processor that generates API is incompatible with the turbine
 					// optimization.
-					deps.disableTurbine = deps.disableTurbine || Bool(plugin.pluginProperties.Generates_api)
+					deps.disableTurbine = deps.disableTurbine || plugin.GeneratesApi
 				} else {
 					ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
 				}
 			case errorpronePluginTag:
-				if _, ok := module.(*Plugin); ok {
+				if _, ok := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider); ok {
 					deps.errorProneProcessorPath = append(deps.errorProneProcessorPath, dep.ImplementationAndResourcesJars...)
 				} else {
 					ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
 				}
 			case exportedPluginTag:
-				if plugin, ok := module.(*Plugin); ok {
+				if plugin, ok := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider); ok {
 					j.exportedPluginJars = append(j.exportedPluginJars, dep.ImplementationAndResourcesJars...)
-					if plugin.pluginProperties.Processor_class != nil {
-						j.exportedPluginClasses = append(j.exportedPluginClasses, *plugin.pluginProperties.Processor_class)
+					if plugin.ProcessorClass != nil {
+						j.exportedPluginClasses = append(j.exportedPluginClasses, *plugin.ProcessorClass)
 					}
 					// Turbine doesn't run annotation processors, so any module that uses an
 					// annotation processor that generates API is incompatible with the turbine
 					// optimization.
-					j.exportedDisableTurbine = Bool(plugin.pluginProperties.Generates_api)
+					j.exportedDisableTurbine = plugin.GeneratesApi
 				} else {
 					ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
 				}
 			case kotlinPluginTag:
-				if _, ok := module.(*KotlinPlugin); ok {
+				if _, ok := android.OtherModuleProvider(ctx, module, KotlinPluginInfoProvider); ok {
 					deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...)
 				} else {
 					ctx.PropertyErrorf("kotlin_plugins", "%q is not a kotlin_plugin module", otherName)
@@ -2510,21 +2532,21 @@
 					JavaInfo: dep,
 				})
 			}
-		} else if dep, ok := module.(android.SourceFileProducer); ok {
+		} else if dep, ok := android.OtherModuleProvider(ctx, module, android.SourceFilesInfoProvider); ok {
 			switch tag {
 			case sdkLibTag, libTag:
-				checkProducesJars(ctx, dep)
-				deps.classpath = append(deps.classpath, dep.Srcs()...)
-				deps.dexClasspath = append(deps.classpath, dep.Srcs()...)
+				checkProducesJars(ctx, dep, module)
+				deps.classpath = append(deps.classpath, dep.Srcs...)
+				deps.dexClasspath = append(deps.classpath, dep.Srcs...)
 				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars,
-					depset.New(depset.PREORDER, dep.Srcs(), nil))
+					depset.New(depset.PREORDER, dep.Srcs, nil))
 			case staticLibTag:
-				checkProducesJars(ctx, dep)
-				deps.classpath = append(deps.classpath, dep.Srcs()...)
-				deps.staticJars = append(deps.staticJars, dep.Srcs()...)
-				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...)
+				checkProducesJars(ctx, dep, module)
+				deps.classpath = append(deps.classpath, dep.Srcs...)
+				deps.staticJars = append(deps.staticJars, dep.Srcs...)
+				deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs...)
 
-				depHeaderJars := depset.New(depset.PREORDER, dep.Srcs(), nil)
+				depHeaderJars := depset.New(depset.PREORDER, dep.Srcs, nil)
 				transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, depHeaderJars)
 				transitiveStaticJarsHeaderLibs = append(transitiveStaticJarsHeaderLibs, depHeaderJars)
 				transitiveStaticJarsImplementationLibs = append(transitiveStaticJarsImplementationLibs, depHeaderJars)
@@ -2577,14 +2599,12 @@
 	deps.transitiveStaticLibsImplementationJars = transitiveStaticJarsImplementationLibs
 	deps.transitiveStaticLibsResourceJars = transitiveStaticJarsResourceLibs
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		depSet := depset.New(depset.PREORDER, nil, transitiveClasspathHeaderJars)
-		deps.classpath = depSet.ToList()
-		depSet = depset.New(depset.PREORDER, nil, transitiveBootClasspathHeaderJars)
-		deps.bootClasspath = depSet.ToList()
-		depSet = depset.New(depset.PREORDER, nil, transitiveJava9ClasspathHeaderJars)
-		deps.java9Classpath = depSet.ToList()
-	}
+	depSet := depset.New(depset.PREORDER, nil, transitiveClasspathHeaderJars)
+	deps.classpath = depSet.ToList()
+	depSet = depset.New(depset.PREORDER, nil, transitiveBootClasspathHeaderJars)
+	deps.bootClasspath = depSet.ToList()
+	depSet = depset.New(depset.PREORDER, nil, transitiveJava9ClasspathHeaderJars)
+	deps.java9Classpath = depSet.ToList()
 
 	if ctx.Device() {
 		sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
@@ -2625,18 +2645,11 @@
 	RenameUseExclude
 )
 
-type RenameUseElement struct {
-	DepName   string
-	RenameUse DependencyUse
-	Why       string // token for determining where in the logic the decision was made.
-}
-
 type JarJarProviderData struct {
 	// Mapping of class names: original --> renamed.  If the value is "", the class will be
 	// renamed by the next rdep that has the jarjar_prefix attribute (or this module if it has
 	// attribute). Rdeps of that module will inherit the renaming.
-	Rename    map[string]string
-	RenameUse []RenameUseElement
+	Rename map[string]string
 }
 
 func (this JarJarProviderData) GetDebugString() string {
@@ -2707,7 +2720,7 @@
 	module := ctx.Module()
 	moduleName := module.Name()
 
-	ctx.VisitDirectDeps(func(m android.Module) {
+	ctx.VisitDirectDepsProxy(func(m android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(m)
 		// This logic mirrors that in (*Module).collectDeps above.  There are several places
 		// where we explicitly return RenameUseExclude, even though it is the default, to
@@ -2715,93 +2728,89 @@
 		//
 		// Note well: there are probably cases that are getting to the unconditional return
 		// and are therefore wrong.
-		shouldIncludeRenames := func() (DependencyUse, string) {
+		shouldIncludeRenames := func() DependencyUse {
 			if moduleName == m.Name() {
-				return RenameUseInclude, "name" // If we have the same module name, include the renames.
+				return RenameUseInclude // If we have the same module name, include the renames.
 			}
 			if sc, ok := module.(android.SdkContext); ok {
 				if ctx.Device() {
 					sdkDep := decodeSdkDep(ctx, sc)
 					if !sdkDep.invalidVersion && sdkDep.useFiles {
-						return RenameUseExclude, "useFiles"
+						return RenameUseExclude
 					}
 				}
 			}
 			if IsJniDepTag(tag) || tag == certificateTag || tag == proguardRaiseTag {
-				return RenameUseExclude, "tags"
+				return RenameUseExclude
 			}
 			if _, ok := android.OtherModuleProvider(ctx, m, SdkLibraryInfoProvider); ok {
 				switch tag {
 				case sdkLibTag, libTag:
-					return RenameUseExclude, "sdklibdep" // matches collectDeps()
+					return RenameUseExclude // matches collectDeps()
 				}
-				return RenameUseInvalid, "sdklibdep" // dep is not used in collectDeps()
+				return RenameUseInvalid // dep is not used in collectDeps()
 			} else if ji, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
 				switch ji.StubsLinkType {
 				case Stubs:
-					return RenameUseExclude, "info"
+					return RenameUseExclude
 				case Implementation:
-					return RenameUseInclude, "info"
+					return RenameUseInclude
 				default:
 					//fmt.Printf("collectDirectDepsProviders: %v -> %v StubsLinkType unknown\n", module, m)
 					// Fall through to the heuristic logic.
 				}
-				switch reflect.TypeOf(m).String() {
-				case "*java.GeneratedJavaLibraryModule":
+				if _, ok := android.OtherModuleProvider(ctx, m, android.CodegenInfoProvider); ok {
 					// Probably a java_aconfig_library module.
-					// TODO: make this check better.
-					return RenameUseInclude, "reflect"
+					return RenameUseInclude
 				}
 				switch tag {
 				case bootClasspathTag:
-					return RenameUseExclude, "tagswitch"
+					return RenameUseExclude
 				case sdkLibTag, libTag, instrumentationForTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				case java9LibTag:
-					return RenameUseExclude, "tagswitch"
+					return RenameUseExclude
 				case staticLibTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				case pluginTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				case errorpronePluginTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				case exportedPluginTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				case kotlinPluginTag:
-					return RenameUseInclude, "tagswitch"
+					return RenameUseInclude
 				default:
-					return RenameUseExclude, "tagswitch"
+					return RenameUseExclude
 				}
-			} else if _, ok := m.(android.SourceFileProducer); ok {
+			} else if _, ok := android.OtherModuleProvider(ctx, m, android.SourceFilesInfoProvider); ok {
 				switch tag {
 				case sdkLibTag, libTag, staticLibTag:
-					return RenameUseInclude, "srcfile"
+					return RenameUseInclude
 				default:
-					return RenameUseExclude, "srcfile"
+					return RenameUseExclude
 				}
 			} else if _, ok := android.OtherModuleProvider(ctx, m, android.CodegenInfoProvider); ok {
-				return RenameUseInclude, "aconfig_declarations_group"
+				return RenameUseInclude
 			} else {
 				switch tag {
 				case bootClasspathTag:
-					return RenameUseExclude, "else"
+					return RenameUseExclude
 				case systemModulesTag:
-					return RenameUseInclude, "else"
+					return RenameUseInclude
 				}
 			}
 			// If we got here, choose the safer option, which may lead to a build failure, rather
 			// than runtime failures on the device.
-			return RenameUseExclude, "end"
+			return RenameUseExclude
 		}
 
 		if result == nil {
 			result = &JarJarProviderData{
-				Rename:    make(map[string]string),
-				RenameUse: make([]RenameUseElement, 0),
+				Rename: make(map[string]string),
 			}
 		}
-		how, why := shouldIncludeRenames()
-		result.RenameUse = append(result.RenameUse, RenameUseElement{DepName: m.Name(), RenameUse: how, Why: why})
+		how := shouldIncludeRenames()
 		if how != RenameUseInclude {
 			// Nothing to merge.
 			return
@@ -2906,14 +2915,18 @@
 // Get the jarjar rule text for a given provider for the fully resolved rules. Classes that map
 // to "" won't be in this list because they shouldn't be renamed yet.
 func getJarJarRuleText(provider *JarJarProviderData) string {
-	result := ""
+	result := strings.Builder{}
 	for _, orig := range android.SortedKeys(provider.Rename) {
 		renamed := provider.Rename[orig]
 		if renamed != "" {
-			result += "rule " + orig + " " + renamed + "\n"
+			result.WriteString("rule ")
+			result.WriteString(orig)
+			result.WriteString(" ")
+			result.WriteString(renamed)
+			result.WriteString("\n")
 		}
 	}
-	return result
+	return result.String()
 }
 
 // Repackage the flags if the jarjar rule txt for the flags is generated
@@ -2926,12 +2939,23 @@
 	return repackagedJarjarFile, true
 }
 
-func (j *Module) jarjarIfNecessary(ctx android.ModuleContext, infile android.Path, jarName, info string) (android.Path, bool) {
+func (j *Module) jarjarIfNecessary(ctx android.ModuleContext, infile android.Path, jarName, info string, useShards bool) (android.Path, bool) {
 	if j.expandJarjarRules == nil {
 		return infile, false
 	}
 	jarjarFile := android.PathForModuleOut(ctx, "jarjar", info, jarName)
-	TransformJarJar(ctx, jarjarFile, infile, j.expandJarjarRules)
+
+	totalShards := 1
+	if useShards {
+		totalShardsStr := j.properties.Jarjar_shards.GetOrDefault(ctx, "1")
+		ts, err := strconv.Atoi(totalShardsStr)
+		if err != nil {
+			ctx.PropertyErrorf("jarjar_shards", "jarjar_shards must be an integer represented as a string")
+			return infile, false
+		}
+		totalShards = ts
+	}
+	TransformJarJarWithShards(ctx, jarjarFile, infile, j.expandJarjarRules, totalShards)
 	return jarjarFile, true
 
 }
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 3413cf3..98fb417 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -15,6 +15,8 @@
 package java
 
 import (
+	"fmt"
+
 	"android/soong/android"
 
 	"github.com/google/blueprint"
@@ -23,36 +25,9 @@
 
 // Contains code that is common to both platform_bootclasspath and bootclasspath_fragment.
 
-func init() {
-	registerBootclasspathBuildComponents(android.InitRegistrationContext)
-}
-
-func registerBootclasspathBuildComponents(ctx android.RegistrationContext) {
-	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator)
-	})
-}
-
-// BootclasspathDepsMutator is the interface that a module must implement if it wants to add
-// dependencies onto APEX specific variants of bootclasspath fragments or bootclasspath contents.
-type BootclasspathDepsMutator interface {
-	// BootclasspathDepsMutator implementations should add dependencies using
-	// addDependencyOntoApexModulePair and addDependencyOntoApexVariants.
-	BootclasspathDepsMutator(ctx android.BottomUpMutatorContext)
-}
-
-// bootclasspathDepsMutator is called during the final deps phase after all APEX variants have
-// been created so can add dependencies onto specific APEX variants of modules.
-func bootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
-	m := ctx.Module()
-	if p, ok := m.(BootclasspathDepsMutator); ok {
-		p.BootclasspathDepsMutator(ctx)
-	}
-}
-
 // addDependencyOntoApexVariants adds dependencies onto the appropriate apex specific variants of
 // the module as specified in the ApexVariantReference list.
-func addDependencyOntoApexVariants(ctx android.BottomUpMutatorContext, propertyName string, refs []ApexVariantReference, tag blueprint.DependencyTag) {
+func addDependencyOntoApexVariants(ctx android.BottomUpMutatorContext, propertyName string, refs []ApexVariantReference, tagType bootclasspathDependencyTagType) {
 	for i, ref := range refs {
 		apex := proptools.StringDefault(ref.Apex, "platform")
 
@@ -62,7 +37,7 @@
 		}
 		name := proptools.String(ref.Module)
 
-		addDependencyOntoApexModulePair(ctx, apex, name, tag)
+		addDependencyOntoApexModulePair(ctx, apex, name, tagType)
 	}
 }
 
@@ -75,68 +50,154 @@
 // module when both source and prebuilt modules are available.
 //
 // Use gatherApexModulePairDepsWithTag to retrieve the dependencies.
-func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex string, name string, tag blueprint.DependencyTag) {
-	var variations []blueprint.Variation
-	if !android.IsConfiguredJarForPlatform(apex) {
-		// Pick the correct apex variant.
-		variations = []blueprint.Variation{
-			{Mutator: "apex", Variation: apex},
-		}
+func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex string, name string, tagType bootclasspathDependencyTagType) {
+	tag := bootclasspathDependencyTag{
+		typ: tagType,
 	}
-
 	target := ctx.Module().Target()
-	variations = append(variations, target.Variations()...)
-
-	addedDep := false
-	if ctx.OtherModuleDependencyVariantExists(variations, name) {
-		ctx.AddFarVariationDependencies(variations, tag, name)
-		addedDep = true
+	if android.IsConfiguredJarForPlatform(apex) {
+		// Platform variant, add a direct dependency.
+		ctx.AddFarVariationDependencies(target.Variations(), tag, name)
+	} else {
+		// A module in an apex.  Dependencies can't be added directly onto an apex variation, as that would
+		// require constructing a full ApexInfo configuration, which can't be predicted here.  Add a dependency
+		// on the apex instead, and annotate the dependency tag with the desired module in the apex.
+		tag.moduleInApex = name
+		ctx.AddFarVariationDependencies(target.Variations(), tag, apex)
 	}
 
-	// Add a dependency on the prebuilt module if it exists.
-	prebuiltName := android.PrebuiltNameFromSource(name)
-	if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) {
-		ctx.AddVariationDependencies(variations, tag, prebuiltName)
-		addedDep = true
-	}
-
-	// If no appropriate variant existing for this, so no dependency could be added, then it is an
-	// error, unless missing dependencies are allowed. The simplest way to handle that is to add a
-	// dependency that will not be satisfied and the default behavior will handle it.
-	if !addedDep {
-		// Add dependency on the unprefixed (i.e. source or renamed prebuilt) module which we know does
-		// not exist. The resulting error message will contain useful information about the available
-		// variants.
-		reportMissingVariationDependency(ctx, variations, name)
-
-		// Add dependency on the missing prefixed prebuilt variant too if a module with that name exists
-		// so that information about its available variants will be reported too.
-		if ctx.OtherModuleExists(prebuiltName) {
-			reportMissingVariationDependency(ctx, variations, prebuiltName)
-		}
-	}
 }
 
-// reportMissingVariationDependency intentionally adds a dependency on a missing variation in order
-// to generate an appropriate error message with information about the available variations.
-func reportMissingVariationDependency(ctx android.BottomUpMutatorContext, variations []blueprint.Variation, name string) {
-	ctx.AddFarVariationDependencies(variations, nil, name)
+// gatherFragments collects fragments that are direct dependencies of this module, as well as
+// any fragments in apexes via the dependency on the apex.  It returns a list of the fragment
+// modules and map from apex name to the fragment in that apex.
+func gatherFragments(ctx android.BaseModuleContext) ([]android.Module, map[string]android.Module) {
+	var fragments []android.Module
+
+	type fragmentInApex struct {
+		module string
+		apex   string
+	}
+
+	var fragmentsInApexes []fragmentInApex
+
+	// Find any direct dependencies, as well as a list of the modules in apexes.
+	ctx.VisitDirectDeps(func(module android.Module) {
+		t := ctx.OtherModuleDependencyTag(module)
+		if bcpTag, ok := t.(bootclasspathDependencyTag); ok && bcpTag.typ == fragment {
+			if bcpTag.moduleInApex != "" {
+				fragmentsInApexes = append(fragmentsInApexes, fragmentInApex{bcpTag.moduleInApex, ctx.OtherModuleName(module)})
+			} else {
+				fragments = append(fragments, module)
+			}
+		}
+	})
+
+	fragmentsMap := make(map[string]android.Module)
+	for _, fragmentInApex := range fragmentsInApexes {
+		var found android.Module
+		// Find a desired module in an apex.
+		ctx.WalkDeps(func(child, parent android.Module) bool {
+			t := ctx.OtherModuleDependencyTag(child)
+			if parent == ctx.Module() {
+				if bcpTag, ok := t.(bootclasspathDependencyTag); ok && bcpTag.typ == fragment && ctx.OtherModuleName(child) == fragmentInApex.apex {
+					// This is the dependency from this module to the apex, recurse into it.
+					return true
+				}
+			} else if android.IsDontReplaceSourceWithPrebuiltTag(t) {
+				return false
+			} else if t == android.PrebuiltDepTag {
+				return false
+			} else if IsBootclasspathFragmentContentDepTag(t) {
+				return false
+			} else if android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child)) == fragmentInApex.module {
+				// This is the desired module inside the apex.
+				if found != nil && child != found {
+					panic(fmt.Errorf("found two conflicting modules %q in apex %q: %s and %s",
+						fragmentInApex.module, fragmentInApex.apex, found, child))
+				}
+				found = child
+			}
+			return false
+		})
+		if found != nil {
+			if existing, exists := fragmentsMap[fragmentInApex.apex]; exists {
+				ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", fragmentInApex.apex, fragmentInApex.module, existing)
+			} else {
+				fragmentsMap[fragmentInApex.apex] = found
+				fragments = append(fragments, found)
+			}
+		} else if !ctx.Config().AllowMissingDependencies() {
+			ctx.ModuleErrorf("failed to find fragment %q in apex %q\n",
+				fragmentInApex.module, fragmentInApex.apex)
+		}
+	}
+	return fragments, fragmentsMap
 }
 
 // gatherApexModulePairDepsWithTag returns the list of dependencies with the supplied tag that was
 // added by addDependencyOntoApexModulePair.
-func gatherApexModulePairDepsWithTag(ctx android.BaseModuleContext, tag blueprint.DependencyTag) []android.Module {
+func gatherApexModulePairDepsWithTag(ctx android.BaseModuleContext, tagType bootclasspathDependencyTagType) ([]android.Module, map[android.Module]string) {
 	var modules []android.Module
-	isActiveModulePred := func(module android.Module) bool {
-		return isActiveModule(ctx, module)
+	modulesToApex := make(map[android.Module]string)
+
+	type moduleInApex struct {
+		module string
+		apex   string
 	}
-	ctx.VisitDirectDepsIf(isActiveModulePred, func(module android.Module) {
+
+	var modulesInApexes []moduleInApex
+
+	ctx.VisitDirectDeps(func(module android.Module) {
 		t := ctx.OtherModuleDependencyTag(module)
-		if t == tag {
-			modules = append(modules, module)
+		if bcpTag, ok := t.(bootclasspathDependencyTag); ok && bcpTag.typ == tagType {
+			if bcpTag.moduleInApex != "" {
+				modulesInApexes = append(modulesInApexes, moduleInApex{bcpTag.moduleInApex, ctx.OtherModuleName(module)})
+			} else {
+				modules = append(modules, module)
+			}
 		}
 	})
-	return modules
+
+	for _, moduleInApex := range modulesInApexes {
+		var found android.Module
+		ctx.WalkDeps(func(child, parent android.Module) bool {
+			t := ctx.OtherModuleDependencyTag(child)
+			if parent == ctx.Module() {
+				if bcpTag, ok := t.(bootclasspathDependencyTag); ok && bcpTag.typ == tagType && ctx.OtherModuleName(child) == moduleInApex.apex {
+					// recurse into the apex
+					return true
+				}
+			} else if tagType != fragment && android.IsFragmentInApexTag(t) {
+				return true
+			} else if android.IsDontReplaceSourceWithPrebuiltTag(t) {
+				return false
+			} else if t == android.PrebuiltDepTag {
+				return false
+			} else if IsBootclasspathFragmentContentDepTag(t) {
+				return false
+			} else if android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child)) == moduleInApex.module {
+				if found != nil && child != found {
+					panic(fmt.Errorf("found two conflicting modules %q in apex %q: %s and %s",
+						moduleInApex.module, moduleInApex.apex, found, child))
+				}
+				found = child
+			}
+			return false
+		})
+		if found != nil {
+			modules = append(modules, found)
+			if existing, exists := modulesToApex[found]; exists && existing != moduleInApex.apex {
+				ctx.ModuleErrorf("module %s is in two apexes, %s and %s", moduleInApex.module, existing, moduleInApex.apex)
+			} else {
+				modulesToApex[found] = moduleInApex.apex
+			}
+		} else if !ctx.Config().AllowMissingDependencies() {
+			ctx.ModuleErrorf("failed to find module %q in apex %q\n",
+				moduleInApex.module, moduleInApex.apex)
+		}
+	}
+	return modules, modulesToApex
 }
 
 // ApexVariantReference specifies a particular apex variant of a module.
@@ -165,7 +226,7 @@
 // addDependenciesOntoFragments adds dependencies to the fragments specified in this properties
 // structure.
 func (p *BootclasspathFragmentsDepsProperties) addDependenciesOntoFragments(ctx android.BottomUpMutatorContext) {
-	addDependencyOntoApexVariants(ctx, "fragments", p.Fragments, bootclasspathFragmentDepTag)
+	addDependencyOntoApexVariants(ctx, "fragments", p.Fragments, fragment)
 }
 
 // bootclasspathDependencyTag defines dependencies from/to bootclasspath_fragment,
@@ -174,23 +235,38 @@
 type bootclasspathDependencyTag struct {
 	blueprint.BaseDependencyTag
 
-	name string
+	typ bootclasspathDependencyTagType
+
+	// moduleInApex is set to the name of the desired module when this dependency points
+	// to the apex that the modules is contained in.
+	moduleInApex string
 }
 
+type bootclasspathDependencyTagType int
+
+const (
+	// The tag used for dependencies onto bootclasspath_fragments.
+	fragment bootclasspathDependencyTagType = iota
+	// The tag used for dependencies onto platform_bootclasspath.
+	platform
+	dexpreoptBootJar
+	artBootJar
+	platformBootJar
+	apexBootJar
+)
+
 func (t bootclasspathDependencyTag) ExcludeFromVisibilityEnforcement() {
 }
 
+func (t bootclasspathDependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
+	return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
+}
+
 // Dependencies that use the bootclasspathDependencyTag instances are only added after all the
 // visibility checking has been done so this has no functional effect. However, it does make it
 // clear that visibility is not being enforced on these tags.
 var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathDependencyTag{}
 
-// The tag used for dependencies onto bootclasspath_fragments.
-var bootclasspathFragmentDepTag = bootclasspathDependencyTag{name: "fragment"}
-
-// The tag used for dependencies onto platform_bootclasspath.
-var platformBootclasspathDepTag = bootclasspathDependencyTag{name: "platform"}
-
 // BootclasspathNestedAPIProperties defines properties related to the API provided by parts of the
 // bootclasspath that are nested within the main BootclasspathAPIProperties.
 type BootclasspathNestedAPIProperties struct {
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 375a1aa..a09416d 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -41,6 +41,10 @@
 	ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootclasspathFragmentFactory)
 }
 
+type BootclasspathFragmentInfo struct{}
+
+var BootclasspathFragmentInfoProvider = blueprint.NewProvider[BootclasspathFragmentInfo]()
+
 // BootclasspathFragmentSdkMemberType is the member type used to add bootclasspath_fragments to
 // the SDK snapshot. It is exported for use by apex.
 var BootclasspathFragmentSdkMemberType = &bootclasspathFragmentMemberType{
@@ -89,6 +93,19 @@
 // The tag used for the dependency between the bootclasspath_fragment module and its contents.
 var bootclasspathFragmentContentDepTag = bootclasspathFragmentContentDependencyTag{}
 
+type moduleInFragmentDependencyTag struct {
+	blueprint.DependencyTag
+}
+
+func (m moduleInFragmentDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+// moduleInFragmentDepTag is added alongside bootclasspathFragmentContentDependencyTag,
+// but doesn't set ReplaceSourceWithPrebuilt.  It is used to find modules in the fragment
+// by traversing from the apex to the fragment to the module, which prevents having to
+// construct a dependency on the apex variant of the fragment directly.
+var moduleInFragmentDepTag = moduleInFragmentDependencyTag{}
+
 var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContentDepTag
 var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
 var _ android.SdkMemberDependencyTag = bootclasspathFragmentContentDepTag
@@ -239,6 +256,8 @@
 	profilePathErr error
 }
 
+var _ android.ApexModule = (*BootclasspathFragmentModule)(nil)
+
 // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
 // bootclasspath fragment modules.
 type commonBootclasspathFragment interface {
@@ -290,6 +309,10 @@
 	return m
 }
 
+func (m *BootclasspathFragmentModule) UniqueApexVariations() bool {
+	return true
+}
+
 func (m *BootclasspathFragmentModule) bootclasspathFragmentPropertyCheck(ctx android.ModuleContext) {
 	contents := m.properties.Contents.GetOrDefault(ctx, nil)
 	if len(contents) == 0 {
@@ -393,11 +416,17 @@
 	return i.profileInstallPathInApex
 }
 
-func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	tag := ctx.OtherModuleDependencyTag(dep)
+func (m *BootclasspathFragmentModule) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return BootclasspathFragmentDepInSameApexChecker{}
+}
 
+type BootclasspathFragmentDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (b BootclasspathFragmentDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
 	// If the module is a default module, do not check the tag
-	if _, ok := dep.(*Defaults); ok {
+	if tag == android.DefaultsDepTag {
 		return true
 	}
 	if IsBootclasspathFragmentContentDepTag(tag) {
@@ -408,17 +437,31 @@
 		// Cross-cutting metadata dependencies are metadata.
 		return false
 	}
+	if tag == moduleInFragmentDepTag {
+		return true
+	}
 	// Dependency to the bootclasspath fragment of another apex
 	// e.g. concsrypt-bootclasspath-fragment --> art-bootclasspath-fragment
-	if tag == bootclasspathFragmentDepTag {
+	if bcpTag, ok := tag.(bootclasspathDependencyTag); ok && bcpTag.typ == fragment {
 		return false
-
 	}
-	panic(fmt.Errorf("boot_image module %q should not have a dependency on %q via tag %s", b, dep, android.PrettyPrintTag(tag)))
+	if tag == moduleInFragmentDepTag {
+		return false
+	}
+	if tag == dexpreopt.Dex2oatDepTag {
+		return false
+	}
+	if tag == android.PrebuiltDepTag {
+		return false
+	}
+	if _, ok := tag.(hiddenAPIStubsDependencyTag); ok {
+		return false
+	}
+	panic(fmt.Errorf("boot_image module should not have a dependency tag %s", android.PrettyPrintTag(tag)))
 }
 
-func (b *BootclasspathFragmentModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
-	return nil
+func (m *BootclasspathFragmentModule) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 // ComponentDepsMutator adds dependencies onto modules before any prebuilt modules without a
@@ -456,24 +499,24 @@
 		}
 	}
 
-	if !dexpreopt.IsDex2oatNeeded(ctx) {
-		return
+	if dexpreopt.IsDex2oatNeeded(ctx) {
+		// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
+		// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
+		dexpreopt.RegisterToolDeps(ctx)
 	}
 
-	// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
-	// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
-	dexpreopt.RegisterToolDeps(ctx)
-
 	// Add a dependency to `all_apex_contributions` to determine if prebuilts are active.
 	// If prebuilts are active, `contents` validation on the source bootclasspath fragment should be disabled.
 	if _, isPrebuiltModule := ctx.Module().(*PrebuiltBootclasspathFragmentModule); !isPrebuiltModule {
 		ctx.AddDependency(b, android.AcDepTag, "all_apex_contributions")
 	}
-}
 
-func (b *BootclasspathFragmentModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies on all the fragments.
 	b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx)
+
+	for _, name := range b.properties.Contents.GetOrDefault(ctx, nil) {
+		ctx.AddDependency(ctx.Module(), moduleInFragmentDepTag, name)
+	}
 }
 
 func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -496,7 +539,7 @@
 		}
 	})
 
-	fragments := gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag)
+	fragments, _ := gatherFragments(ctx)
 
 	// Perform hidden API processing.
 	hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
@@ -518,6 +561,8 @@
 	if !ctx.IsFinalModule(ctx.Module()) {
 		b.HideFromMake()
 	}
+
+	android.SetProvider(ctx, BootclasspathFragmentInfoProvider, BootclasspathFragmentInfo{})
 }
 
 // getProfileProviderApex returns the name of the apex that provides a boot image profile, or an
@@ -529,19 +574,18 @@
 	}
 
 	// Bootclasspath fragment modules that are for the platform do not produce boot related files.
-	apexInfos, _ := android.ModuleProvider(ctx, android.AllApexInfoProvider)
-	if apexInfos == nil {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	if apexInfo.IsForPlatform() {
 		return ""
 	}
 
-	for _, apexInfo := range apexInfos.ApexInfos {
-		for _, apex := range apexInfo.InApexVariants {
-			if isProfileProviderApex(ctx, apex) {
-				return apex
+	for _, config := range genBootImageConfigs(ctx) {
+		if config.profileProviderModule == b.BaseModuleName() {
+			if len(config.profileImports) > 0 {
+				return config.profileImports[0]
 			}
 		}
 	}
-
 	return ""
 }
 
@@ -605,7 +649,7 @@
 			if android.IsModulePrebuilt(ctx.Module()) {
 				// prebuilt bcpf. the validation of this will be done at the top-level apex
 				providerClasspathFragmentValidationInfoProvider(ctx, unknown)
-			} else if !disableSourceApexVariant(ctx) {
+			} else if !disableSourceApexVariant(ctx) && android.IsModulePreferred(ctx.Module()) {
 				// source bcpf, and prebuilt apex are not selected.
 				ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
 			}
@@ -1141,6 +1185,13 @@
 	android.InitPrebuiltModule(m, &[]string{"placeholder"})
 	android.InitApexModule(m)
 	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(m)
+
+	m.SetDefaultableHook(func(mctx android.DefaultableHookContext) {
+		if mctx.Config().AlwaysUsePrebuiltSdks() {
+			m.prebuilt.ForcePrefer()
+		}
+	})
 
 	return m
 }
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 3aa1258..87b853c 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -31,6 +31,7 @@
 )
 
 func TestBootclasspathFragment_UnknownImageName(t *testing.T) {
+	t.Parallel()
 	prepareForTestWithBootclasspathFragment.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
 			`\Qimage_name: unknown image name "unknown", expected "art"\E`)).
@@ -50,6 +51,7 @@
 }
 
 func TestPrebuiltBootclasspathFragment_UnknownImageName(t *testing.T) {
+	t.Parallel()
 	prepareForTestWithBootclasspathFragment.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
 			`\Qimage_name: unknown image name "unknown", expected "art"\E`)).
@@ -68,6 +70,7 @@
 }
 
 func TestBootclasspathFragmentInconsistentArtConfiguration_Platform(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		dexpreopt.FixtureSetArtBootJars("platform:foo", "apex:bar"),
@@ -99,6 +102,7 @@
 }
 
 func TestBootclasspathFragmentInconsistentArtConfiguration_ApexMixture(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		dexpreopt.FixtureSetArtBootJars("apex1:foo", "apex2:bar"),
@@ -131,6 +135,7 @@
 }
 
 func TestBootclasspathFragment_Coverage(t *testing.T) {
+	t.Parallel()
 	prepareWithBp := android.FixtureWithRootAndroidBp(`
 		bootclasspath_fragment {
 			name: "myfragment",
@@ -204,11 +209,13 @@
 	)
 
 	t.Run("without coverage", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 		checkContents(t, result, "mybootlib")
 	})
 
 	t.Run("with coverage", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			prepareForTestWithFrameworkJacocoInstrumentation,
 			preparer,
@@ -364,7 +371,7 @@
 		}
 	`)
 
-	fragment := result.ModuleForTests("myfragment", "android_common")
+	fragment := result.ModuleForTests(t, "myfragment", "android_common")
 	dependencyStubDexFlag := "--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.test_module_lib/android_common/dex/android-non-updatable.stubs.test_module_lib.jar"
 	stubFlagsCommand := fragment.Output("modular-hiddenapi/stub-flags.csv").RuleParams.Command
 	android.AssertStringDoesContain(t,
@@ -472,7 +479,7 @@
 
 	// Make sure that the signature-patterns.csv is passed all the appropriate package properties
 	// from the bootclasspath_fragment and its contents.
-	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common")
+	fragment := result.ModuleForTests(t, "mybootclasspathfragment", "android_common")
 	rule := fragment.Output("modular-hiddenapi/signature-patterns.csv")
 	expectedCommand := strings.Join([]string{
 		"--split-package newlibrary",
diff --git a/java/builder.go b/java/builder.go
index 895ddb6..dff0032 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -46,6 +46,7 @@
 				`mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
 				`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
+				`${config.FindInputDeltaCmd} --template '' --target "$out" --inputs_file "$out.rsp" && ` +
 				`${config.SoongJavacWrapper} $javaTemplate${config.JavacCmd} ` +
 				`${config.JavacHeapFlags} ${config.JavacVmFlags} ${config.CommonJdkFlags} ` +
 				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
@@ -55,8 +56,10 @@
 				`$zipTemplate${config.SoongZipCmd} -jar -o $out.tmp -C $outDir -D $outDir && ` +
 				`if ! cmp -s "$out.tmp" "$out"; then mv "$out.tmp" "$out"; fi && ` +
 				`if ! cmp -s "$annoSrcJar.tmp" "$annoSrcJar"; then mv "$annoSrcJar.tmp" "$annoSrcJar"; fi && ` +
+				`if [ -f "$out.pc_state.new" ]; then mv "$out.pc_state.new" "$out.pc_state"; fi && ` +
 				`rm -rf "$srcJarDir" "$outDir"`,
 			CommandDeps: []string{
+				"${config.FindInputDeltaCmd}",
 				"${config.JavacCmd}",
 				"${config.SoongZipCmd}",
 				"${config.ZipSyncCmd}",
@@ -165,7 +168,7 @@
 				"${config.JavaCmd}",
 			},
 			Rspfile:        "$out.rsp",
-			RspfileContent: "$in",
+			RspfileContent: "$in_newline",
 			Restat:         true,
 		},
 		&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
@@ -223,6 +226,12 @@
 		},
 		"jarArgs")
 
+	extractR8Rules = pctx.AndroidStaticRule("extractR8Rules",
+		blueprint.RuleParams{
+			Command:     `${config.ExtractR8RulesCmd} --rules-output $out --include-origin-comments $in`,
+			CommandDeps: []string{"${config.ExtractR8RulesCmd}"},
+		})
+
 	jarjar = pctx.AndroidStaticRule("jarjar",
 		blueprint.RuleParams{
 			Command: "" +
@@ -235,12 +244,12 @@
 				// for newly repackaged classes. Dropping @UnsupportedAppUsage on repackaged classes
 				// avoids adding new hiddenapis after jarjar'ing.
 				" -DremoveAndroidCompatAnnotations=true" +
-				" -jar ${config.JarjarCmd} process $rulesFile $in $out && " +
+				" -jar ${config.JarjarCmd} process $rulesFile $in $out $total_shards $shard_index && " +
 				// Turn a missing output file into a ninja error
 				`[ -e ${out} ] || (echo "Missing output file"; exit 1)`,
 			CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"},
 		},
-		"rulesFile")
+		"rulesFile", "total_shards", "shard_index")
 
 	packageCheck = pctx.AndroidStaticRule("packageCheck",
 		blueprint.RuleParams{
@@ -301,7 +310,7 @@
 
 	gatherReleasedFlaggedApisRule = pctx.AndroidStaticRule("gatherReleasedFlaggedApisRule",
 		blueprint.RuleParams{
-			Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}' ` +
+			Command: `${aconfig} dump-cache --dedup --format=protobuf ` +
 				`--out ${out} ` +
 				`${flags_path} ` +
 				`${filter_args} `,
@@ -311,8 +320,15 @@
 
 	generateMetalavaRevertAnnotationsRule = pctx.AndroidStaticRule("generateMetalavaRevertAnnotationsRule",
 		blueprint.RuleParams{
-			Command:     `${keep-flagged-apis} ${in} > ${out}`,
-			CommandDeps: []string{"${keep-flagged-apis}"},
+			Command:     `${aconfig-to-metalava-flags} ${in} > ${out}`,
+			CommandDeps: []string{"${aconfig-to-metalava-flags}"},
+		})
+
+	generateApiXMLRule = pctx.AndroidStaticRule("generateApiXMLRule",
+		blueprint.RuleParams{
+			Command:     `${config.JavaCmd} ${config.JavaVmFlags} -Xmx4g -jar ${config.MetalavaJar} jar-to-jdiff ${in} ${out}`,
+			CommandDeps: []string{"${config.JavaCmd}", "${config.MetalavaJar}"},
+			Description: "Converting API file to XML",
 		})
 )
 
@@ -323,7 +339,7 @@
 	pctx.HostBinToolVariable("aconfig", "aconfig")
 	pctx.HostBinToolVariable("ravenizer", "ravenizer")
 	pctx.HostBinToolVariable("apimapper", "apimapper")
-	pctx.HostBinToolVariable("keep-flagged-apis", "keep-flagged-apis")
+	pctx.HostBinToolVariable("aconfig-to-metalava-flags", "aconfig-to-metalava-flags")
 }
 
 type javaBuilderFlags struct {
@@ -456,9 +472,10 @@
 	const srcJarArgsLimit = 32 * 1024
 	if len(srcJarArgs) > srcJarArgsLimit {
 		srcJarRspFile := android.PathForModuleOut(ctx, "turbine", "srcjars.rsp")
-		android.WriteFileRule(ctx, srcJarRspFile, srcJarArgs)
+		android.WriteFileRule(ctx, srcJarRspFile, strings.Join(srcJars.Strings(), "\n"))
 		srcJarArgs = "@" + srcJarRspFile.String()
 		implicits = append(implicits, srcJarRspFile)
+		rspFiles = append(rspFiles, srcJarRspFile)
 		rbeInputs = append(rbeInputs, srcJarRspFile)
 	} else {
 		rbeInputs = append(rbeInputs, srcJars...)
@@ -488,7 +505,7 @@
 	const classpathLimit = 32 * 1024
 	if len(classpathFlags) > classpathLimit {
 		classpathRspFile := android.PathForModuleOut(ctx, dir, "classpath.rsp")
-		android.WriteFileRule(ctx, classpathRspFile, classpathFlags)
+		android.WriteFileRule(ctx, classpathRspFile, strings.Join(classpath.Strings(), "\n"))
 		classpathFlags = "@" + classpathRspFile.String()
 		implicits = append(implicits, classpathRspFile)
 		rspFiles = append(rspFiles, classpathRspFile)
@@ -736,6 +753,16 @@
 	})
 }
 
+func TransformJarToR8Rules(ctx android.ModuleContext, outputFile android.WritablePath,
+	jar android.Path) {
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   extractR8Rules,
+		Output: outputFile,
+		Input:  jar,
+	})
+}
+
 func convertImplementationJarToHeaderJar(ctx android.ModuleContext, implementationJarFile android.Path,
 	headerJarFile android.WritablePath) {
 	ctx.Build(pctx, android.BuildParams{
@@ -747,16 +774,58 @@
 
 func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath,
 	classesJar android.Path, rulesFile android.Path) {
+	TransformJarJarWithShards(ctx, outputFile, classesJar, rulesFile, 1)
+}
+
+func TransformJarJarWithShards(ctx android.ModuleContext, outputFile android.WritablePath,
+	classesJar android.Path, rulesFile android.Path, totalShards int) {
+
+	// If the total number of shards is 1, just run jarjar as-is, with `total_shards` = 1
+	// and `shard_index` == 0, which effectively disables sharding
+	if totalShards == 1 {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        jarjar,
+			Description: "jarjar",
+			Output:      outputFile,
+			Input:       classesJar,
+			Implicit:    rulesFile,
+			Args: map[string]string{
+				"rulesFile":    rulesFile.String(),
+				"total_shards": "1",
+				"shard_index":  "0",
+			},
+		})
+		return
+	}
+
+	// Otherwise, run multiple jarjar instances and use merge_zips to combine the output.
+	tempJars := make([]android.Path, 0)
+	totalStr := strconv.Itoa(totalShards)
+	for i := 0; i < totalShards; i++ {
+		iStr := strconv.Itoa(i)
+		tempOut := outputFile.ReplaceExtension(ctx, "-"+iStr+".jar")
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        jarjar,
+			Description: "jarjar (" + iStr + "/" + totalStr + ")",
+			Output:      tempOut,
+			Input:       classesJar,
+			Implicit:    rulesFile,
+			Args: map[string]string{
+				"rulesFile":    rulesFile.String(),
+				"total_shards": totalStr,
+				"shard_index":  iStr,
+			},
+		})
+		tempJars = append(tempJars, tempOut)
+	}
+
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        jarjar,
-		Description: "jarjar",
+		Rule:        combineJar,
+		Description: "merge jarjar shards",
 		Output:      outputFile,
-		Input:       classesJar,
-		Implicit:    rulesFile,
-		Args: map[string]string{
-			"rulesFile": rulesFile.String(),
-		},
+		Inputs:      tempJars,
 	})
+
 }
 
 func CheckJarPackages(ctx android.ModuleContext, outputFile android.WritablePath,
diff --git a/java/classpath_element.go b/java/classpath_element.go
index abbcae7..4af2770 100644
--- a/java/classpath_element.go
+++ b/java/classpath_element.go
@@ -108,33 +108,18 @@
 //
 // e.g. Given the following input:
 //
-//	libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext
-//	fragments: com.android.art:art-bootclasspath-fragment
+//		libraries: core-oj, core-libart, framework, ext
+//		fragments: art-bootclasspath-fragment
+//	    libraryToApex: core-oj: com.android.art, core-libart: com.android.art
+//	    apexNameToFragment: com.android.art: art-bootclasspath-fragment
 //
 // Then this will return:
 //
 //	ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]),
 //	ClasspathLibraryElement(framework),
 //	ClasspathLibraryElement(ext),
-func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module) ClasspathElements {
-	// Create a map from apex name to the fragment module. This makes it easy to find the fragment
-	// associated with a particular apex.
-	apexToFragment := map[string]android.Module{}
-	for _, fragment := range fragments {
-		apexInfo, ok := android.OtherModuleProvider(ctx, fragment, android.ApexInfoProvider)
-		if !ok {
-			ctx.ModuleErrorf("fragment %s is not part of an apex", fragment)
-			continue
-		}
-
-		for _, apex := range apexInfo.InApexVariants {
-			if existing, ok := apexToFragment[apex]; ok {
-				ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing)
-				continue
-			}
-			apexToFragment[apex] = fragment
-		}
-	}
+func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module,
+	libraryToApex map[android.Module]string, apexNameToFragment map[string]android.Module) ClasspathElements {
 
 	fragmentToElement := map[android.Module]*ClasspathFragmentElement{}
 	elements := []ClasspathElement{}
@@ -144,31 +129,28 @@
 	// Iterate over the libraries to construct the ClasspathElements list.
 	for _, library := range libraries {
 		var element ClasspathElement
-		if apexInfo, ok := android.OtherModuleProvider(ctx, library, android.ApexInfoProvider); ok {
-
+		if libraryApex, ok := libraryToApex[library]; ok {
 			var fragment android.Module
 
 			// Make sure that the library is in only one fragment of the classpath.
-			for _, apex := range apexInfo.InApexVariants {
-				if f, ok := apexToFragment[apex]; ok {
-					if fragment == nil {
-						// This is the first fragment so just save it away.
-						fragment = f
-					} else if f != fragment {
-						// This apex variant of the library is in a different fragment.
-						ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f)
-						// Skip over this library entirely as otherwise the resulting classpath elements would
-						// be invalid.
-						continue skipLibrary
-					}
-				} else {
-					// There is no fragment associated with the library's apex.
+			if f, ok := apexNameToFragment[libraryApex]; ok {
+				if fragment == nil {
+					// This is the first fragment so just save it away.
+					fragment = f
+				} else if f != fragment {
+					// This apex variant of the library is in a different fragment.
+					ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f)
+					// Skip over this library entirely as otherwise the resulting classpath elements would
+					// be invalid.
+					continue skipLibrary
 				}
+			} else {
+				// There is no fragment associated with the library's apex.
 			}
 
 			if fragment == nil {
 				ctx.ModuleErrorf("library %s is from apexes %s which have no corresponding fragment in %s",
-					library, apexInfo.InApexVariants, fragments)
+					library, []string{libraryApex}, fragments)
 				// Skip over this library entirely as otherwise the resulting classpath elements would
 				// be invalid.
 				continue skipLibrary
diff --git a/java/config/config.go b/java/config/config.go
index 87703d8..fdb8d78 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -159,6 +159,7 @@
 	pctx.SourcePathVariable("ResourceProcessorBusyBox", "prebuilts/bazel/common/android_tools/android_tools/all_android_tools_deploy.jar")
 
 	pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file")
+	pctx.HostBinToolVariable("FindInputDeltaCmd", "find_input_delta")
 
 	pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh")
 	pctx.SourcePathVariable("PackageCheckCmd", "build/soong/scripts/package-check.sh")
@@ -170,7 +171,9 @@
 	pctx.HostBinToolVariable("ApiCheckCmd", "apicheck")
 	pctx.HostBinToolVariable("D8Cmd", "d8")
 	pctx.HostBinToolVariable("R8Cmd", "r8")
+	pctx.HostBinToolVariable("ExtractR8RulesCmd", "extract-r8-rules")
 	pctx.HostBinToolVariable("ResourceShrinkerCmd", "resourceshrinker")
+	pctx.HostBinToolVariable("TraceReferencesCmd", "tracereferences")
 	pctx.HostBinToolVariable("HiddenAPICmd", "hiddenapi")
 	pctx.HostBinToolVariable("ExtractApksCmd", "extract_apks")
 	pctx.VariableFunc("TurbineJar", func(ctx android.PackageVarContext) string {
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index 302d021..ffb025d 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -21,6 +21,7 @@
 	KotlincIllegalFlags = []string{
 		"-no-jdk",
 		"-no-stdlib",
+		"-language-version",
 	}
 )
 
@@ -56,5 +57,5 @@
 	// platform that are not fully compatible with the kotlinc used in g3 kythe indexers.
 	// e.g. uninitialized variables are a warning in 1.*, but an error in 2.*
 	// https://github.com/JetBrains/kotlin/blob/master/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt#L748
-	pctx.StaticVariable("KotlincKytheGlobalFlags", strings.Join([]string{"-language-version 1.9"}, " "))
+	pctx.StaticVariable("KotlincKytheGlobalFlags", strings.Join([]string{}, " "))
 }
diff --git a/java/container_test.go b/java/container_test.go
index 25cfa4c..35a3020 100644
--- a/java/container_test.go
+++ b/java/container_test.go
@@ -26,6 +26,7 @@
 }
 
 func TestJavaContainersModuleProperties(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 	).RunTestWithBp(t, `
@@ -154,7 +155,7 @@
 	}
 
 	for _, c := range testcases {
-		m := result.ModuleForTests(c.moduleName, "android_common")
+		m := result.ModuleForTests(t, c.moduleName, "android_common")
 		containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider)
 		belongingContainers := containers.BelongingContainers()
 		checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers))
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index bfacea6..04def3e 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -140,7 +140,7 @@
 		d.combinedHeaderJar = d.headerJars[0]
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             d.headerJars,
 		LocalHeaderJars:                        d.headerJars,
 		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, nil, transitiveHeaderJars),
@@ -154,7 +154,9 @@
 		StubsLinkType:                          Implementation,
 		// TODO: Not sure if aconfig flags that have been moved between device and host variants
 		// make sense.
-	})
+	}
+	setExtraJavaInfo(ctx, d, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 
 }
 
diff --git a/java/device_host_converter_test.go b/java/device_host_converter_test.go
index 6ccc5c1..42e3b46 100644
--- a/java/device_host_converter_test.go
+++ b/java/device_host_converter_test.go
@@ -15,13 +15,15 @@
 package java
 
 import (
-	"android/soong/android"
 	"slices"
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestDeviceForHost(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "device_module",
@@ -52,15 +54,15 @@
 
 	ctx, config := testJava(t, bp)
 
-	deviceModule := ctx.ModuleForTests("device_module", "android_common")
-	deviceTurbineCombined := deviceModule.Output("turbine-combined/device_module.jar")
+	deviceModule := ctx.ModuleForTests(t, "device_module", "android_common")
+	deviceTurbine := deviceModule.Output("turbine/device_module.jar")
 	deviceJavac := deviceModule.Output("javac/device_module.jar")
 	deviceRes := deviceModule.Output("res/device_module.jar")
 
-	deviceImportModule := ctx.ModuleForTests("device_import_module", "android_common")
-	deviceImportCombined := deviceImportModule.Output("combined/device_import_module.jar")
+	deviceImportModule := ctx.ModuleForTests(t, "device_import_module", "android_common")
+	deviceImportCombined := deviceImportModule.Output("local-combined/device_import_module.jar")
 
-	hostModule := ctx.ModuleForTests("host_module", config.BuildOSCommonTarget.String())
+	hostModule := ctx.ModuleForTests(t, "host_module", config.BuildOSCommonTarget.String())
 	hostJavac := hostModule.Output("javac/host_module.jar")
 	hostRes := hostModule.Output("res/host_module.jar")
 	combined := hostModule.Output("combined/host_module.jar")
@@ -68,7 +70,7 @@
 
 	// check classpath of host module with dependency on device_for_host_module
 	expectedClasspath := "-classpath " + strings.Join(android.Paths{
-		deviceTurbineCombined.Output,
+		deviceTurbine.Output,
 		deviceImportCombined.Output,
 	}.Strings(), ":")
 
@@ -102,6 +104,7 @@
 }
 
 func TestHostForDevice(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library_host {
 			name: "host_module",
@@ -133,15 +136,15 @@
 
 	ctx, config := testJava(t, bp)
 
-	hostModule := ctx.ModuleForTests("host_module", config.BuildOSCommonTarget.String())
+	hostModule := ctx.ModuleForTests(t, "host_module", config.BuildOSCommonTarget.String())
 	hostJavac := hostModule.Output("javac/host_module.jar")
-	hostJavacHeader := hostModule.Output("javac-header/host_module.jar")
+	hostJavacHeader := hostModule.Output("local-javac-header/host_module.jar")
 	hostRes := hostModule.Output("res/host_module.jar")
 
-	hostImportModule := ctx.ModuleForTests("host_import_module", config.BuildOSCommonTarget.String())
-	hostImportCombined := hostImportModule.Output("combined/host_import_module.jar")
+	hostImportModule := ctx.ModuleForTests(t, "host_import_module", config.BuildOSCommonTarget.String())
+	hostImportCombined := hostImportModule.Output("local-combined/host_import_module.jar")
 
-	deviceModule := ctx.ModuleForTests("device_module", "android_common")
+	deviceModule := ctx.ModuleForTests(t, "device_module", "android_common")
 	deviceJavac := deviceModule.Output("javac/device_module.jar")
 	deviceRes := deviceModule.Output("res/device_module.jar")
 	combined := deviceModule.Output("combined/device_module.jar")
diff --git a/java/dex.go b/java/dex.go
index 2b3c931..e3058e9 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -42,13 +42,39 @@
 		// True if the module containing this has it set by default.
 		EnabledByDefault bool `blueprint:"mutated"`
 
+		// If true, then `d8` will be used on eng builds instead of `r8`, even though
+		// optimize.enabled is true.
+		D8_on_eng *bool
+
+		// Whether to allow that library classes inherit from program classes.
+		// Defaults to false.
+		Ignore_library_extends_program *bool
+
 		// Whether to continue building even if warnings are emitted.  Defaults to true.
 		Ignore_warnings *bool
 
+		// Whether runtime invisible annotations should be kept by R8. Defaults to false.
+		// This is equivalent to:
+		//   -keepattributes RuntimeInvisibleAnnotations,
+		//                   RuntimeInvisibleParameterAnnotations,
+		//                   RuntimeInvisibleTypeAnnotations
+		// This is only applicable when RELEASE_R8_ONLY_RUNTIME_VISIBLE_ANNOTATIONS is
+		// enabled and will be used to migrate away from keeping runtime invisible
+		// annotations (b/387958004).
+		Keep_runtime_invisible_annotations *bool
+
 		// If true, runs R8 in Proguard compatibility mode, otherwise runs R8 in full mode.
-		// Defaults to false for apps, true for libraries and tests.
+		// Defaults to false for apps and tests, true for libraries.
 		Proguard_compatibility *bool
 
+		// If true, R8 will not add public or protected members (fields or methods) to
+		// the API surface of the compilation unit, i.e., classes that are kept or
+		// have kept subclasses will not expose any members added by R8 for internal
+		// use. That includes renamed members if obfuscation is enabled.
+		// This should only be used for building targets that go on the bootclasspath.
+		// Defaults to false.
+		Protect_api_surface *bool
+
 		// If true, optimize for size by removing unused code.  Defaults to true for apps,
 		// false for libraries and tests.
 		Shrink *bool
@@ -81,6 +107,34 @@
 		// If true, transitive reverse dependencies of this module will have this
 		// module's proguard spec appended to their optimization action
 		Export_proguard_flags_files *bool
+
+		// Path to a file containing a list of class names that should not be compiled using R8.
+		// These classes will be compiled by D8 similar to when Optimize.Enabled is false.
+		//
+		// Example:
+		//
+		//   r8.exclude:
+		//   com.example.Foo
+		//   com.example.Bar
+		//   com.example.Bar$Baz
+		//
+		// By default all classes are compiled using R8 when Optimize.Enabled is set.
+		Exclude *string `android:"path"`
+
+		// Optional list of downstream (Java) libraries from which to trace and preserve references
+		// when optimizing. Note that this requires that the source reference does *not* have
+		// a strict lib dependency on this target; dependencies should be on intermediate targets
+		// statically linked into this target, e.g., if A references B, and we want to trace and
+		// keep references from A when optimizing B, you would create an intermediate B.impl (
+		// containing all static code), have A depend on `B.impl` via libs, and set
+		// `trace_references_from: ["A"]` on B.
+		//
+		// Also note that these are *not* inherited across targets, they must be specified at the
+		// top-level target that is optimized.
+		//
+		// TODO(b/212737576): Handle this implicitly using bottom-up deps mutation and implicit
+		// creation of a proxy `.impl` library.
+		Trace_references_from proptools.Configurable[[]string] `android:"arch_variant"`
 	}
 
 	// Keep the data uncompressed. We always need uncompressed dex for execution,
@@ -111,7 +165,12 @@
 	providesTransitiveHeaderJarsForR8
 }
 
-func (d *dexer) effectiveOptimizeEnabled() bool {
+func (d *dexer) effectiveOptimizeEnabled(ctx android.EarlyModuleContext) bool {
+	// For eng builds, if Optimize.D8_on_eng is true, then disable optimization.
+	if ctx.Config().Eng() && proptools.Bool(d.dexProperties.Optimize.D8_on_eng) {
+		return false
+	}
+	// Otherwise, use the legacy logic of a default value which can be explicitly overridden by the module.
 	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
 }
 
@@ -123,8 +182,8 @@
 	return d.resourceShrinkingEnabled(ctx) && BoolDefault(d.Optimize.Optimized_shrink_resources, ctx.Config().UseOptimizedResourceShrinkingByDefault())
 }
 
-func (d *dexer) optimizeOrObfuscateEnabled() bool {
-	return d.effectiveOptimizeEnabled() && (proptools.Bool(d.dexProperties.Optimize.Optimize) || proptools.Bool(d.dexProperties.Optimize.Obfuscate))
+func (d *dexer) optimizeOrObfuscateEnabled(ctx android.EarlyModuleContext) bool {
+	return d.effectiveOptimizeEnabled(ctx) && (proptools.Bool(d.dexProperties.Optimize.Optimize) || proptools.Bool(d.dexProperties.Optimize.Obfuscate))
 }
 
 var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
@@ -156,6 +215,71 @@
 		},
 	}, []string{"outDir", "d8Flags", "zipFlags", "mergeZipsFlags"}, nil)
 
+// Include all of the args for d8r8, so that we can generate the partialcompileclean target's build using the same list.
+var d8r8Clean = pctx.AndroidStaticRule("d8r8-partialcompileclean",
+	blueprint.RuleParams{
+		Command: `rm -rf "${outDir}" "${outDict}" "${outConfig}" "${outUsage}" "${outUsageZip}" "${outUsageDir}" ` +
+			`"${resourcesOutput}" "${outR8ArtProfile}" ${builtOut}`,
+	}, "outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir", "builtOut",
+	"d8Flags", "r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile", "implicits",
+)
+
+var d8r8, d8r8RE = pctx.MultiCommandRemoteStaticRules("d8r8",
+	blueprint.RuleParams{
+		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
+			`rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` +
+			`mkdir -p $$(dirname ${outUsage}) && ` +
+			`if [ -n "$${SOONG_USE_PARTIAL_COMPILE}" ]; then ` +
+			` for f in "${outConfig}" "${outDict}" "${outUsage}" "${resourcesOutput}"; do ` +
+			`   test -n "$${f}" && test ! -f "$${f}" && mkdir -p "$$(dirname "$${f}")" && touch "$${f}" || true; ` +
+			` done && ` +
+			` $d8Template${config.D8Cmd} ${config.D8Flags} $d8Flags --output $outDir --no-dex-input-jar $in; ` +
+			`else ` +
+			` $r8Template${config.R8Cmd} ${config.R8Flags} $r8Flags -injars $in --output $outDir ` +
+			` --no-data-resources ` +
+			` -printmapping ${outDict} ` +
+			` -printconfiguration ${outConfig} ` +
+			` -printusage ${outUsage} ` +
+			` --deps-file ${out}.d && ` +
+			` touch "${outDict}" "${outConfig}" "${outUsage}"; ` +
+			`fi && ` +
+			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
+			`rm -rf ${outUsageDir} && ` +
+			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
+			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar" `,
+		CommandDeps: []string{
+			"${config.D8Cmd}",
+			"${config.R8Cmd}",
+			"${config.SoongZipCmd}",
+			"${config.MergeZipsCmd}",
+		},
+	}, map[string]*remoteexec.REParams{
+		"$d8Template": &remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "compiler": "d8"},
+			Inputs:          []string{"${config.D8Jar}"},
+			ExecStrategy:    "${config.RED8ExecStrategy}",
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+		"$r8Template": &remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
+			Inputs:          []string{"$implicits", "${config.R8Jar}"},
+			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}", "${resourcesOutput}", "${outR8ArtProfile}"},
+			ExecStrategy:    "${config.RER8ExecStrategy}",
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+		"$zipTemplate": &remoteexec.REParams{
+			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
+			OutputFiles:  []string{"$outDir/classes.dex.jar"},
+			ExecStrategy: "${config.RED8ExecStrategy}",
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+	}, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
+		"d8Flags", "r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile"}, []string{"implicits"})
+
 var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -220,21 +344,29 @@
 		deps = append(deps, f)
 	}
 
-	var requestReleaseMode bool
+	var requestReleaseMode, requestDebugMode bool
 	requestReleaseMode, flags = android.RemoveFromList("--release", flags)
+	requestDebugMode, flags = android.RemoveFromList("--debug", flags)
 
 	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" || ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
-		flags = append(flags, "--debug")
+		requestDebugMode = true
 		requestReleaseMode = false
 	}
 
 	// Don't strip out debug information for eng builds, unless the target
 	// explicitly provided the `--release` build flag. This allows certain
 	// test targets to remain optimized as part of eng test_suites builds.
-	if requestReleaseMode {
+	if requestDebugMode {
+		flags = append(flags, "--debug")
+	} else if requestReleaseMode {
 		flags = append(flags, "--release")
 	} else if ctx.Config().Eng() {
 		flags = append(flags, "--debug")
+	} else if !d.effectiveOptimizeEnabled(ctx) && d.dexProperties.Optimize.EnabledByDefault {
+		// D8 uses --debug by default, whereas R8 uses --release by default.
+		// For targets that default to R8 usage (e.g., apps), but override this default, we still
+		// want D8 to run in release mode, preserving semantics as much as possible between the two.
+		flags = append(flags, "--release")
 	}
 
 	// Supplying the platform build flag disables various features like API modeling and desugaring.
@@ -306,7 +438,7 @@
 	// 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) {
+		ctx.VisitDirectDepsProxyWithTag(proguardRaiseTag, func(m android.ModuleProxy) {
 			if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
 				proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...)
 			}
@@ -340,11 +472,30 @@
 		android.PathForSource(ctx, "build/make/core/proguard.flags"),
 	}
 
+	if ctx.Config().UseR8GlobalCheckNotNullFlags() {
+		flagFiles = append(flagFiles, android.PathForSource(ctx,
+			"build/make/core/proguard/checknotnull.flags"))
+	}
+
 	flagFiles = append(flagFiles, d.extraProguardFlagsFiles...)
 	// TODO(ccross): static android library proguard files
 
 	flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, opt.Proguard_flags_files)...)
 
+	traceReferencesSources := android.Paths{}
+	ctx.VisitDirectDepsProxyWithTag(traceReferencesTag, func(m android.ModuleProxy) {
+		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
+			traceReferencesSources = append(traceReferencesSources, dep.ImplementationJars...)
+		}
+	})
+	if len(traceReferencesSources) > 0 {
+		traceTarget := dexParams.classesJar
+		traceLibs := android.FirstUniquePaths(append(flags.bootClasspath.Paths(), flags.dexClasspath.Paths()...))
+		traceReferencesFlags := android.PathForModuleOut(ctx, "proguard", "trace_references.flags")
+		TraceReferences(ctx, traceReferencesSources, traceTarget, traceLibs, traceReferencesFlags)
+		flagFiles = append(flagFiles, traceReferencesFlags)
+	}
+
 	flagFiles = android.FirstUniquePaths(flagFiles)
 
 	r8Flags = append(r8Flags, android.JoinWithPrefix(flagFiles.Strings(), "-include "))
@@ -356,10 +507,22 @@
 
 	r8Flags = append(r8Flags, opt.Proguard_flags...)
 
-	if BoolDefault(opt.Proguard_compatibility, true) {
+	if BoolDefault(opt.Ignore_library_extends_program, false) {
+		r8Flags = append(r8Flags, "--ignore-library-extends-program")
+	}
+
+	if BoolDefault(opt.Keep_runtime_invisible_annotations, false) {
+		r8Flags = append(r8Flags, "--keep-runtime-invisible-annotations")
+	}
+
+	if BoolDefault(opt.Proguard_compatibility, !ctx.Config().UseR8FullModeByDefault()) {
 		r8Flags = append(r8Flags, "--force-proguard-compatibility")
 	}
 
+	if BoolDefault(opt.Protect_api_surface, false) {
+		r8Flags = append(r8Flags, "--protect-api-surface")
+	}
+
 	// Avoid unnecessary stack frame noise by only injecting source map ids for non-debug
 	// optimized or obfuscated targets.
 	if (Bool(opt.Optimize) || Bool(opt.Obfuscate)) && !debugMode {
@@ -412,6 +575,15 @@
 		artProfileOutput = profileOutput
 	}
 
+	if ctx.Config().UseR8StoreStoreFenceConstructorInlining() {
+		r8Flags = append(r8Flags, "--store-store-fence-constructor-inlining")
+	}
+
+	if opt.Exclude != nil {
+		r8Flags = append(r8Flags, "--exclude", *opt.Exclude)
+		r8Deps = append(r8Deps, android.PathForModuleSrc(ctx, *opt.Exclude))
+	}
+
 	return r8Flags, r8Deps, artProfileOutput
 }
 
@@ -448,6 +620,7 @@
 
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
+	cleanPhonyPath := android.PathForModuleOut(ctx, "dex", dexParams.jarName+"-partialcompileclean").OutputPath
 	outDir := android.PathForModuleOut(ctx, "dex")
 
 	zipFlags := "--ignore_missing_files"
@@ -463,8 +636,20 @@
 		mergeZipsFlags = "-stripFile META-INF/*.kotlin_module -stripFile **/*.kotlin_builtins"
 	}
 
-	useR8 := d.effectiveOptimizeEnabled()
+	useR8 := d.effectiveOptimizeEnabled(ctx)
+	useD8 := !useR8 || ctx.Config().PartialCompileFlags().Use_d8
+	rbeR8 := ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8")
+	rbeD8 := ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8")
+	var rule blueprint.Rule
+	var description string
 	var artProfileOutputPath *android.OutputPath
+	var implicitOutputs android.WritablePaths
+	var deps android.Paths
+	args := map[string]string{
+		"zipFlags":       zipFlags,
+		"outDir":         outDir.String(),
+		"mergeZipsFlags": mergeZipsFlags,
+	}
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
@@ -477,83 +662,89 @@
 		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
 		resourcesOutput := android.PathForModuleOut(ctx, "package-res-shrunken.apk")
 		d.resourcesOutput = android.OptionalPathForPath(resourcesOutput)
-		implicitOutputs := android.WritablePaths{
+		implicitOutputs = append(implicitOutputs, android.WritablePaths{
 			proguardDictionary,
 			proguardUsageZip,
 			proguardConfiguration,
-		}
+		}...)
+		description = "r8"
 		debugMode := android.InList("--debug", commonFlags)
 		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams, debugMode)
-		rule := r8
-		args := map[string]string{
-			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
-			"zipFlags":       zipFlags,
-			"outDict":        proguardDictionary.String(),
-			"outConfig":      proguardConfiguration.String(),
-			"outUsageDir":    proguardUsageDir.String(),
-			"outUsage":       proguardUsage.String(),
-			"outUsageZip":    proguardUsageZip.String(),
-			"outDir":         outDir.String(),
-			"mergeZipsFlags": mergeZipsFlags,
-		}
+		deps = append(deps, r8Deps...)
+		args["r8Flags"] = strings.Join(append(commonFlags, r8Flags...), " ")
 		if r8ArtProfileOutputPath != nil {
 			artProfileOutputPath = r8ArtProfileOutputPath
-			implicitOutputs = append(
-				implicitOutputs,
-				artProfileOutputPath,
-			)
 			// Add the implicit r8 Art profile output to args so that r8RE knows
 			// about this implicit output
-			args["outR8ArtProfile"] = artProfileOutputPath.String()
+			args["outR8ArtProfile"] = r8ArtProfileOutputPath.String()
 		}
-
-		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") {
-			rule = r8RE
-			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
-		}
+		args["outDict"] = proguardDictionary.String()
+		args["outConfig"] = proguardConfiguration.String()
+		args["outUsageDir"] = proguardUsageDir.String()
+		args["outUsage"] = proguardUsage.String()
+		args["outUsageZip"] = proguardUsageZip.String()
 		if d.resourcesInput.Valid() {
 			implicitOutputs = append(implicitOutputs, resourcesOutput)
 			args["resourcesOutput"] = resourcesOutput.String()
 		}
-		ctx.Build(pctx, android.BuildParams{
-			Rule:            rule,
-			Description:     "r8",
-			Output:          javalibJar,
-			ImplicitOutputs: implicitOutputs,
-			Input:           dexParams.classesJar,
-			Implicits:       r8Deps,
-			Args:            args,
-		})
-	} else {
-		implicitOutputs := android.WritablePaths{}
+
+		rule = r8
+		if rbeR8 {
+			rule = r8RE
+			args["implicits"] = strings.Join(deps.Strings(), ",")
+		}
+	}
+	if useD8 {
+		description = "d8"
 		d8Flags, d8Deps, d8ArtProfileOutputPath := d.d8Flags(ctx, dexParams)
+		deps = append(deps, d8Deps...)
+		deps = append(deps, commonDeps...)
+		args["d8Flags"] = strings.Join(append(commonFlags, d8Flags...), " ")
 		if d8ArtProfileOutputPath != nil {
 			artProfileOutputPath = d8ArtProfileOutputPath
-			implicitOutputs = append(
-				implicitOutputs,
-				artProfileOutputPath,
-			)
 		}
-		d8Deps = append(d8Deps, commonDeps...)
-		rule := d8
-		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
+		// If we are generating both d8 and r8, only use RBE when both are enabled.
+		switch {
+		case useR8 && rule == r8:
+			rule = d8r8
+			description = "d8r8"
+		case useR8 && rule == r8RE && rbeD8:
+			rule = d8r8RE
+			description = "d8r8"
+		case rbeD8:
 			rule = d8RE
+		default:
+			rule = d8
 		}
-		ctx.Build(pctx, android.BuildParams{
-			Rule:            rule,
-			Description:     "d8",
-			Output:          javalibJar,
-			Input:           dexParams.classesJar,
-			ImplicitOutputs: implicitOutputs,
-			Implicits:       d8Deps,
-			Args: map[string]string{
-				"d8Flags":        strings.Join(append(commonFlags, d8Flags...), " "),
-				"zipFlags":       zipFlags,
-				"outDir":         outDir.String(),
-				"mergeZipsFlags": mergeZipsFlags,
-			},
-		})
 	}
+	if artProfileOutputPath != nil {
+		implicitOutputs = append(
+			implicitOutputs,
+			artProfileOutputPath,
+		)
+	}
+	ctx.Build(pctx, android.BuildParams{
+		Rule:            rule,
+		Description:     description,
+		Output:          javalibJar,
+		ImplicitOutputs: implicitOutputs,
+		Input:           dexParams.classesJar,
+		Implicits:       deps,
+		Args:            args,
+	})
+	if useR8 && useD8 {
+		// Generate the rule for partial compile clean.
+		args["builtOut"] = javalibJar.String()
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        d8r8Clean,
+			Description: "d8r8Clean",
+			Output:      cleanPhonyPath,
+			Args:        args,
+			PhonyOutput: true,
+		})
+		ctx.Phony("partialcompileclean", cleanPhonyPath)
+	}
+
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
 		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", dexParams.jarName).OutputPath
 		TransformZipAlign(ctx, alignedJavalibJar, javalibJar, nil)
@@ -562,3 +753,13 @@
 
 	return javalibJar, artProfileOutputPath
 }
+
+type ProguardInfo struct {
+	ModuleName         string
+	Class              string
+	ProguardDictionary android.Path
+	ProguardUsageZip   android.Path
+	ClassesJar         android.Path
+}
+
+var ProguardProvider = blueprint.NewProvider[ProguardInfo]()
diff --git a/java/dex_test.go b/java/dex_test.go
index 8bc28e6..8c1e5f7 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"strconv"
 	"testing"
 
 	"android/soong/android"
@@ -24,6 +25,7 @@
 )
 
 func TestR8(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -58,26 +60,27 @@
 		}
 	`)
 
-	app := result.ModuleForTests("app", "android_common")
-	stableApp := result.ModuleForTests("stable_app", "android_common")
-	corePlatformApp := result.ModuleForTests("core_platform_app", "android_common")
-	lib := result.ModuleForTests("lib", "android_common")
-	staticLib := result.ModuleForTests("static_lib", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
+	stableApp := result.ModuleForTests(t, "stable_app", "android_common")
+	corePlatformApp := result.ModuleForTests(t, "core_platform_app", "android_common")
+	lib := result.ModuleForTests(t, "lib", "android_common")
+	staticLib := result.ModuleForTests(t, "static_lib", "android_common")
 
 	appJavac := app.Rule("javac")
 	appR8 := app.Rule("r8")
 	stableAppR8 := stableApp.Rule("r8")
 	corePlatformAppR8 := corePlatformApp.Rule("r8")
-	libHeader := lib.Output("turbine-combined/lib.jar").Output
-	staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
+	libHeader := lib.Output("turbine/lib.jar").Output
+	libCombinedHeader := lib.Output("turbine-combined/lib.jar").Output
+	staticLibHeader := staticLib.Output("turbine/static_lib.jar").Output
 
 	android.AssertStringDoesContain(t, "expected lib header jar in app javac classpath",
 		appJavac.Args["classpath"], libHeader.String())
 	android.AssertStringDoesContain(t, "expected static_lib header jar in app javac classpath",
 		appJavac.Args["classpath"], staticLibHeader.String())
 
-	android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
-		appR8.Args["r8Flags"], libHeader.String())
+	android.AssertStringDoesContain(t, "expected lib combined header jar in app r8 classpath",
+		appR8.Args["r8Flags"], libCombinedHeader.String())
 	android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app r8 classpath",
 		appR8.Args["r8Flags"], staticLibHeader.String())
 	android.AssertStringDoesContain(t, "expected -ignorewarnings in app r8 flags",
@@ -91,6 +94,7 @@
 }
 
 func TestR8TransitiveDeps(t *testing.T) {
+	t.Parallel()
 	bp := `
 		override_android_app {
 			name: "override_app",
@@ -192,6 +196,7 @@
 
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			fixturePreparer := PrepareForTestWithJavaDefaultModules
 			if tc.unbundled {
 				fixturePreparer = android.GroupFixturePreparers(
@@ -206,14 +211,14 @@
 			result := fixturePreparer.RunTestWithBp(t, bp)
 
 			getHeaderJar := func(name string) android.Path {
-				mod := result.ModuleForTests(name, "android_common")
+				mod := result.ModuleForTests(t, name, "android_common")
 				return mod.Output("turbine-combined/" + name + ".jar").Output
 			}
 
-			appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
-			overrideAppR8 := result.ModuleForTests("app", "android_common_override_app").Rule("r8")
+			appR8 := result.ModuleForTests(t, "app", "android_common").Rule("r8")
+			overrideAppR8 := result.ModuleForTests(t, "app", "android_common_override_app").Rule("r8")
 			appHeader := getHeaderJar("app")
-			overrideAppHeader := result.ModuleForTests("app", "android_common_override_app").Output("turbine-combined/app.jar").Output
+			overrideAppHeader := result.ModuleForTests(t, "app", "android_common_override_app").Output("turbine-combined/app.jar").Output
 			libHeader := getHeaderJar("lib")
 			transitiveLibHeader := getHeaderJar("transitive_lib")
 			transitiveLib2Header := getHeaderJar("transitive_lib_2")
@@ -222,7 +227,7 @@
 			repeatedDepHeader := getHeaderJar("repeated_dep")
 			usesLibHeader := getHeaderJar("uses_lib")
 			optionalUsesLibHeader := getHeaderJar("optional_uses_lib")
-			prebuiltLibHeader := result.ModuleForTests("prebuilt_lib", "android_common").Output("combined/lib.jar").Output
+			prebuiltLibHeader := result.ModuleForTests(t, "prebuilt_lib", "android_common").Output("combined/lib.jar").Output
 
 			for _, rule := range []android.TestingBuildParams{appR8, overrideAppR8} {
 				android.AssertStringDoesNotContain(t, "expected no app header jar in app r8 classpath",
@@ -259,6 +264,7 @@
 }
 
 func TestR8Flags(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -273,7 +279,7 @@
 		}
 	`)
 
-	app := result.ModuleForTests("app", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
 	appR8 := app.Rule("r8")
 	android.AssertStringDoesContain(t, "expected -dontshrink in app r8 flags",
 		appR8.Args["r8Flags"], "-dontshrink")
@@ -288,6 +294,7 @@
 }
 
 func TestD8(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -306,29 +313,52 @@
 			name: "static_lib",
 			srcs: ["foo.java"],
 		}
+
+		android_app {
+			name: "app",
+			srcs: ["foo.java"],
+			platform_apis: true,
+			optimize: {
+				enabled: false,
+			},
+		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
-	lib := result.ModuleForTests("lib", "android_common")
-	staticLib := result.ModuleForTests("static_lib", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
+	lib := result.ModuleForTests(t, "lib", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
+	staticLib := result.ModuleForTests(t, "static_lib", "android_common")
 
 	fooJavac := foo.Rule("javac")
 	fooD8 := foo.Rule("d8")
-	libHeader := lib.Output("turbine-combined/lib.jar").Output
-	staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
+	appD8 := app.Rule("d8")
+	libHeader := lib.Output("turbine/lib.jar").Output
+	libCombinedHeader := lib.Output("turbine-combined/lib.jar").Output
+	staticLibHeader := staticLib.Output("turbine/static_lib.jar").Output
 
 	android.AssertStringDoesContain(t, "expected lib header jar in foo javac classpath",
 		fooJavac.Args["classpath"], libHeader.String())
 	android.AssertStringDoesContain(t, "expected static_lib header jar in foo javac classpath",
 		fooJavac.Args["classpath"], staticLibHeader.String())
 
-	android.AssertStringDoesContain(t, "expected lib header jar in foo d8 classpath",
-		fooD8.Args["d8Flags"], libHeader.String())
+	android.AssertStringDoesContain(t, "expected lib combined header jar in foo d8 classpath",
+		fooD8.Args["d8Flags"], libCombinedHeader.String())
 	android.AssertStringDoesNotContain(t, "expected no  static_lib header jar in foo javac classpath",
 		fooD8.Args["d8Flags"], staticLibHeader.String())
+
+	// A --release flag is added only for targets that opt out of default R8 behavior (e.g., apps).
+	// For library targets that don't use R8 by default, no --debug or --release flag should be
+	// added, instead relying on default D8 behavior (--debug).
+	android.AssertStringDoesContain(t, "expected --release in app d8 flags",
+		appD8.Args["d8Flags"], "--release")
+	android.AssertStringDoesNotContain(t, "expected no --release flag in lib d8 flags",
+		fooD8.Args["d8Flags"], "--release")
+	android.AssertStringDoesNotContain(t, "expected no --debug flag in lib d8 flags",
+		fooD8.Args["d8Flags"], "--debug")
 }
 
 func TestProguardFlagsInheritanceStatic(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -370,7 +400,7 @@
 		}
 	`)
 
-	app := result.ModuleForTests("app", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
 	appR8 := app.Rule("r8")
 	android.AssertStringDoesContain(t, "expected primary_lib's proguard flags from direct dep",
 		appR8.Args["r8Flags"], "primary.flags")
@@ -383,6 +413,7 @@
 }
 
 func TestProguardFlagsInheritance(t *testing.T) {
+	t.Parallel()
 	directDepFlagsFileName := "direct_dep.flags"
 	transitiveDepFlagsFileName := "transitive_dep.flags"
 
@@ -601,6 +632,7 @@
 	for _, topLevelModuleDef := range topLevelModules {
 		for _, tc := range testcases {
 			t.Run(topLevelModuleDef.name+"-"+tc.name, func(t *testing.T) {
+				t.Parallel()
 				result := android.GroupFixturePreparers(
 					PrepareForTestWithJavaDefaultModules,
 					android.FixtureMergeMockFs(android.MockFS{
@@ -617,7 +649,7 @@
 							tc.transitiveDepExportsFlagsFiles,
 						),
 				)
-				appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
+				appR8 := result.ModuleForTests(t, "app", "android_common").Rule("r8")
 
 				shouldHaveDepFlags := android.InList(directDepFlagsFileName, tc.expectedFlagsFiles)
 				if shouldHaveDepFlags {
@@ -642,6 +674,7 @@
 }
 
 func TestProguardFlagsInheritanceAppImport(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "app",
@@ -658,12 +691,13 @@
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, bp)
 
-	appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
+	appR8 := result.ModuleForTests(t, "app", "android_common").Rule("r8")
 	android.AssertStringDoesContain(t, "expected aarimports's proguard flags",
 		appR8.Args["r8Flags"], "proguard.txt")
 }
 
 func TestR8FlagsArtProfile(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
@@ -677,7 +711,7 @@
 		}
 	`)
 
-	app := result.ModuleForTests("app", "android_common")
+	app := result.ModuleForTests(t, "app", "android_common")
 	appR8 := app.Rule("r8")
 	android.AssertStringDoesContain(t, "expected --art-profile in app r8 flags",
 		appR8.Args["r8Flags"], "--art-profile")
@@ -696,6 +730,7 @@
 //
 // The rewritten profile should be used since the dex signatures in the checked-in profile will not match the optimized binary.
 func TestEnableProfileRewritingIsRequiredForOptimizedApps(t *testing.T) {
+	t.Parallel()
 	testJavaError(t,
 		"Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on",
 		`
@@ -715,11 +750,15 @@
 }
 
 func TestDebugReleaseFlags(t *testing.T) {
+	t.Parallel()
 	bp := `
 		android_app {
 			name: "app",
 			srcs: ["foo.java"],
 			platform_apis: true,
+			optimize: {
+				enabled: %s,
+			},
 			dxflags: ["%s"]
 		}
 	`
@@ -728,6 +767,7 @@
 		name          string
 		envVar        string
 		isEng         bool
+		useD8         bool
 		dxFlags       string
 		expectedFlags string
 	}{
@@ -757,20 +797,36 @@
 		},
 		{
 			name:          "app_eng",
+			useD8:         true,
 			isEng:         true,
 			expectedFlags: "--debug",
 		},
 		{
 			name:    "app_release_eng",
 			isEng:   true,
+			useD8:   true,
 			dxFlags: "--release",
 			// Eng mode does *not* override explicit dxflags.
 			expectedFlags: "--release",
 		},
+		{
+			name:  "app_d8",
+			useD8: true,
+			// D8 usage w/ apps should explicitly enable --release mode.
+			expectedFlags: "--release",
+		},
+		{
+			name:    "app_d8_debug",
+			useD8:   true,
+			dxFlags: "--debug",
+			// D8 usage w/ apps respects overriding dxFlags.
+			expectedFlags: "--debug",
+		},
 	}
 
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			fixturePreparer := PrepareForTestWithJavaDefaultModules
 			fixturePreparer = android.GroupFixturePreparers(
 				fixturePreparer,
@@ -788,11 +844,16 @@
 					}),
 				)
 			}
-			result := fixturePreparer.RunTestWithBp(t, fmt.Sprintf(bp, tc.dxFlags))
+			result := fixturePreparer.RunTestWithBp(t, fmt.Sprintf(bp, strconv.FormatBool(!tc.useD8), tc.dxFlags))
 
-			appR8 := result.ModuleForTests("app", "android_common").Rule("r8")
-			android.AssertStringDoesContain(t, "expected flag in R8 flags",
-				appR8.Args["r8Flags"], tc.expectedFlags)
+			dexRuleKey := "r8"
+			if tc.useD8 {
+				dexRuleKey = "d8"
+			}
+			dexFlagsKey := dexRuleKey + "Flags"
+			appDex := result.ModuleForTests(t, "app", "android_common").Rule(dexRuleKey)
+			android.AssertStringDoesContain(t, "expected flag in dex flags",
+				appDex.Args[dexFlagsKey], tc.expectedFlags)
 
 			var unexpectedFlags string
 			if tc.expectedFlags == "--debug" {
@@ -801,9 +862,52 @@
 				unexpectedFlags = "--debug"
 			}
 			if unexpectedFlags != "" {
-				android.AssertStringDoesNotContain(t, "unexpected flag in R8 flags",
-					appR8.Args["r8Flags"], unexpectedFlags)
+				android.AssertStringDoesNotContain(t, "unexpected flag in dex flags",
+					appDex.Args[dexFlagsKey], unexpectedFlags)
 			}
 		})
 	}
 }
+
+func TestTraceReferences(t *testing.T) {
+	t.Parallel()
+	bp := `
+		android_app {
+			name: "app",
+			libs: ["lib.impl"],
+			srcs: ["foo.java"],
+			platform_apis: true,
+		}
+
+		java_library {
+			name: "lib",
+			optimize: {
+				enabled: true,
+				trace_references_from: ["app"],
+			},
+			srcs: ["bar.java"],
+			static_libs: ["lib.impl"],
+			installable: true,
+		}
+
+		java_library {
+			name: "lib.impl",
+			srcs: ["baz.java"],
+		}
+	`
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, bp)
+
+	appJar := result.ModuleForTests(t, "app", "android_common").Output("combined/app.jar").Output
+	libJar := result.ModuleForTests(t, "lib", "android_common").Output("combined/lib.jar").Output
+	libTraceRefs := result.ModuleForTests(t, "lib", "android_common").Rule("traceReferences")
+	libR8 := result.ModuleForTests(t, "lib", "android_common").Rule("r8")
+
+	android.AssertStringDoesContain(t, "expected trace reference source from app jar",
+		libTraceRefs.Args["sources"], "--source "+appJar.String())
+	android.AssertStringEquals(t, "expected trace reference target into lib jar",
+		libJar.String(), libTraceRefs.Input.String())
+	android.AssertStringDoesContain(t, "expected trace reference proguard flags in lib r8 flags",
+		libR8.Args["r8Flags"], "trace_references.flags")
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 5928446..e8e1cd4 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -36,61 +36,24 @@
 	// If the java module is to be installed into an APEX, this list contains information about the
 	// dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed
 	// outside of the APEX.
-	DexpreoptBuiltInstalledForApex() []dexpreopterInstall
+	ApexSystemServerDexpreoptInstalls() []DexpreopterInstall
 
-	// The Make entries to install the dexpreopt outputs. Derived from
-	// `DexpreoptBuiltInstalledForApex`.
-	AndroidMkEntriesForApex() []android.AndroidMkEntries
+	// ApexSystemServerDexJars returns the list of dex jars if this is an apex system server jar.
+	ApexSystemServerDexJars() android.Paths
 
 	// See `dexpreopter.outputProfilePathOnHost`.
 	OutputProfilePathOnHost() android.Path
 }
 
-type dexpreopterInstall struct {
-	// A unique name to distinguish an output from others for the same java library module. Usually in
-	// the form of `<arch>-<encoded-path>.odex/vdex/art`.
-	name string
-
-	// The name of the input java module.
-	moduleName string
-
+type DexpreopterInstall struct {
 	// The path to the dexpreopt output on host.
-	outputPathOnHost android.Path
+	OutputPathOnHost android.Path
 
 	// The directory on the device for the output to install to.
-	installDirOnDevice android.InstallPath
+	InstallDirOnDevice android.InstallPath
 
 	// The basename (the last segment of the path) for the output to install as.
-	installFileOnDevice string
-}
-
-// The full module name of the output in the makefile.
-func (install *dexpreopterInstall) FullModuleName() string {
-	return install.moduleName + install.SubModuleName()
-}
-
-// The sub-module name of the output in the makefile (the name excluding the java module name).
-func (install *dexpreopterInstall) SubModuleName() string {
-	return "-dexpreopt-" + install.name
-}
-
-// Returns Make entries for installing the file.
-//
-// This function uses a value receiver rather than a pointer receiver to ensure that the object is
-// safe to use in `android.AndroidMkExtraEntriesFunc`.
-func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
-	return android.AndroidMkEntries{
-		Class:      "ETC",
-		OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE", install.FullModuleName())
-				entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
-				entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
-			},
-		},
-	}
+	InstallFileOnDevice string
 }
 
 type Dexpreopter struct {
@@ -120,8 +83,9 @@
 	classLoaderContexts dexpreopt.ClassLoaderContextMap
 
 	// See the `dexpreopt` function for details.
-	builtInstalled        string
-	builtInstalledForApex []dexpreopterInstall
+	builtInstalled                    string
+	apexSystemServerDexpreoptInstalls []DexpreopterInstall
+	apexSystemServerDexJars           android.Paths
 
 	// The config is used for two purposes:
 	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
@@ -204,27 +168,23 @@
 	}
 	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	psi := android.PrebuiltSelectionInfoMap{}
-	ctx.VisitDirectDeps(func(am android.Module) {
+	ctx.VisitDirectDepsProxy(func(am android.ModuleProxy) {
 		if prebuiltSelectionInfo, ok := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); ok {
 			psi = prebuiltSelectionInfo
 		}
 	})
 
 	// Find the apex variant for this module
-	apexVariantsWithoutTestApexes := []string{}
+	apexVariants := []string{}
 	if apexInfo.BaseApexName != "" {
-		// This is a transitive dependency of an override_apex
-		apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, apexInfo.BaseApexName)
-	} else {
-		_, variants, _ := android.ListSetDifference(apexInfo.InApexVariants, apexInfo.TestApexes)
-		apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, variants...)
+		apexVariants = append(apexVariants, apexInfo.BaseApexName)
 	}
 	if apexInfo.ApexAvailableName != "" {
-		apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, apexInfo.ApexAvailableName)
+		apexVariants = append(apexVariants, apexInfo.ApexAvailableName)
 	}
 	disableSource := false
 	// find the selected apexes
-	for _, apexVariant := range apexVariantsWithoutTestApexes {
+	for _, apexVariant := range apexVariants {
 		if len(psi.GetSelectedModulesForApiDomain(apexVariant)) > 0 {
 			// If the apex_contribution for this api domain is non-empty, disable the source variant
 			disableSource = true
@@ -279,20 +239,6 @@
 		if !isApexSystemServerJar {
 			return true
 		}
-		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
-		allApexInfos := []android.ApexInfo{}
-		if allApexInfosProvider, ok := android.ModuleProvider(ctx, android.AllApexInfoProvider); ok {
-			allApexInfos = allApexInfosProvider.ApexInfos
-		}
-		if len(allApexInfos) > 0 && !ai.MinSdkVersion.EqualTo(allApexInfos[0].MinSdkVersion) {
-			// Apex system server jars are dexpreopted and installed on to the system image.
-			// Since we can have BigAndroid and Go variants of system server jar providing apexes,
-			// and these two variants can have different min_sdk_versions, hide one of the apex variants
-			// from make to prevent collisions.
-			//
-			// Unlike cc, min_sdk_version does not have an effect on the build actions of java libraries.
-			ctx.Module().MakeUninstallable()
-		}
 	} else {
 		// Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
 		if isApexSystemServerJar {
@@ -347,7 +293,11 @@
 	d.installPath = android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexpreopt.GetSystemServerDexLocation(ctx, dc, libraryName), "/"))
 	// generate the rules for creating the .odex and .vdex files for this system server jar
 	dexJarFile := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName))
-
+	if dexJarFile == nil {
+		ctx.ModuleErrorf(
+			`Could not find library %s in prebuilt apex %s.
+Please make sure that the value of PRODUCT_APEX_(SYSTEM_SERVER|STANDALONE_SYSTEM_SERVER)_JARS is correct`, libraryName, ctx.ModuleName())
+	}
 	d.inputProfilePathOnHost = nil // reset: TODO(spandandas): Make dexpreopter stateless
 	if android.InList(libraryName, di.GetDexpreoptProfileGuidedExportedModuleNames()) {
 		// Set the profile path to guide optimization
@@ -539,12 +489,8 @@
 		Output(appProductPackages)
 	productPackagesRule.Restat().Build("product_packages."+dexJarStem, "dexpreopt product_packages")
 
-	// Prebuilts are active, do not copy the dexpreopt'd source javalib to out/soong/system_server_dexjars
-	// The javalib from the deapexed prebuilt will be copied to this location.
-	// TODO (b/331665856): Implement a principled solution for this.
-	copyApexSystemServerJarDex := !disableSourceApexVariant(ctx) && !ctx.Module().IsHideFromMake()
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
-		ctx, globalSoong, global, dexpreoptConfig, appProductPackages, copyApexSystemServerJarDex)
+		ctx, globalSoong, global, dexpreoptConfig, appProductPackages)
 	if err != nil {
 		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
 		return
@@ -577,7 +523,6 @@
 			partition = ""
 		}
 		installBase := filepath.Base(install.To)
-		arch := filepath.Base(installDir)
 		installPath := android.PathForModuleInPartitionInstall(ctx, partition, installDir)
 		isProfile := strings.HasSuffix(installBase, ".prof")
 
@@ -593,16 +538,13 @@
 				// libraries, only those in the system server classpath are handled here.
 				// Preopting of boot classpath jars in the ART APEX are handled in
 				// java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
-				// The installs will be handled by Make as sub-modules of the java library.
-				di := dexpreopterInstall{
-					name:                arch + "-" + installBase,
-					moduleName:          libName,
-					outputPathOnHost:    install.From,
-					installDirOnDevice:  installPath,
-					installFileOnDevice: installBase,
+				// The installs will be handled the apex module that includes this library.
+				di := DexpreopterInstall{
+					OutputPathOnHost:    install.From,
+					InstallDirOnDevice:  installPath,
+					InstallFileOnDevice: installBase,
 				}
-				ctx.InstallFile(di.installDirOnDevice, di.installFileOnDevice, di.outputPathOnHost)
-				d.builtInstalledForApex = append(d.builtInstalledForApex, di)
+				d.apexSystemServerDexpreoptInstalls = append(d.apexSystemServerDexpreoptInstalls, di)
 
 			}
 		} else if !d.preventInstall {
@@ -611,9 +553,22 @@
 		}
 	}
 
+	if isApexSystemServerJar {
+		// Store the dex jar location for system server jars in apexes, the apex will copy the file into
+		// a known location for dex2oat.
+		d.apexSystemServerDexJars = append(d.apexSystemServerDexJars, dexJarFile)
+	} else if isSystemServerJar && !d.preventInstall {
+		// Copy the dex jar into a known location for dex2oat for non-apex system server jars.
+		android.CopyFileRule(ctx, dexJarFile, android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir, dexJarFile.Base()))
+	}
+
 	if !isApexSystemServerJar {
 		d.builtInstalled = dexpreoptRule.Installs().String()
 	}
+
+	if isSystemServerJar {
+		checkSystemServerOrder(ctx, libName)
+	}
 }
 
 func getModuleInstallPathInfo(ctx android.ModuleContext, fullInstallPath string) (android.InstallPath, string, string) {
@@ -645,16 +600,12 @@
 	}
 }
 
-func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
-	return d.builtInstalledForApex
+func (d *dexpreopter) ApexSystemServerDexpreoptInstalls() []DexpreopterInstall {
+	return d.apexSystemServerDexpreoptInstalls
 }
 
-func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
-	var entries []android.AndroidMkEntries
-	for _, install := range d.builtInstalledForApex {
-		entries = append(entries, install.ToMakeEntries())
-	}
-	return entries
+func (d *dexpreopter) ApexSystemServerDexJars() android.Paths {
+	return d.apexSystemServerDexJars
 }
 
 func (d *dexpreopter) OutputProfilePathOnHost() android.Path {
@@ -684,3 +635,33 @@
 func (d *dexpreopter) SetRewrittenProfile(p android.Path) {
 	d.rewrittenProfile = p
 }
+
+// Check the order of jars on the system server classpath and give a warning/error if a jar precedes
+// one of its dependencies. This is not an error, but a missed optimization, as dexpreopt won't
+// have the dependency jar in the class loader context, and it won't be able to resolve any
+// references to its classes and methods.
+func checkSystemServerOrder(ctx android.ModuleContext, libName string) {
+	config := dexpreopt.GetGlobalConfig(ctx)
+	jars := config.AllSystemServerClasspathJars(ctx)
+	jarIndex := config.AllSystemServerJars(ctx).IndexOfJar(libName)
+	ctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
+		tag := ctx.OtherModuleDependencyTag(dep)
+		// Ideally this should only be walking relevant dependencies, but to maintain existing behavior
+		// for now just exclude any known irrelevant dependencies that would lead to incorrect errors.
+		if _, ok := tag.(bootclasspathDependencyTag); ok {
+			return false
+		} else if tag == traceReferencesTag {
+			// Allow ordering inversion if the dependency is purely for tracing references.
+			return false
+		}
+		depIndex := jars.IndexOfJar(dep.Name())
+		if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
+			jar := jars.Jar(jarIndex)
+			dep := jars.Jar(depIndex)
+			ctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
+				" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
+				" references from '%s' to '%s'.\n", jar, dep, jar, dep)
+		}
+		return true
+	})
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 8c60d23..2287043 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -225,7 +226,6 @@
 }
 
 var (
-	dexpreoptBootJarDepTag          = bootclasspathDependencyTag{name: "dexpreopt-boot-jar"}
 	dexBootJarsFragmentsKey         = android.NewOnceKey("dexBootJarsFragments")
 	apexContributionsMetadataDepTag = dependencyTag{name: "all_apex_contributions"}
 )
@@ -293,6 +293,9 @@
 	// Profiles imported from APEXes, in addition to the profile at the default path. Each entry must
 	// be the name of an APEX module.
 	profileImports []string
+
+	// The name of the module that provides boot image profiles, if any.
+	profileProviderModule string
 }
 
 // Target-dependent description of a boot image.
@@ -464,9 +467,6 @@
 func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
 	ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
 	ctx.RegisterModuleType("art_boot_images", artBootImagesFactory)
-	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("dex_bootjars_deps", DexpreoptBootJarsMutator)
-	})
 }
 
 func SkipDexpreoptBootJars(ctx android.PathContext) bool {
@@ -502,12 +502,6 @@
 func (dbj *dexpreoptBootJars) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Create a dependency on all_apex_contributions to determine the selected mainline module
 	ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions")
-}
-
-func DexpreoptBootJarsMutator(ctx android.BottomUpMutatorContext) {
-	if _, ok := ctx.Module().(*dexpreoptBootJars); !ok {
-		return
-	}
 
 	if dexpreopt.IsDex2oatNeeded(ctx) {
 		// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
@@ -521,7 +515,7 @@
 			continue
 		}
 		// For accessing the boot jars.
-		addDependenciesOntoBootImageModules(ctx, config.modules, dexpreoptBootJarDepTag)
+		addDependenciesOntoBootImageModules(ctx, config.modules, dexpreoptBootJar)
 		// Create a dependency on the apex selected using RELEASE_APEX_CONTRIBUTIONS_*
 		// TODO: b/308174306 - Remove the direct depedendency edge to the java_library (source/prebuilt) once all mainline modules
 		// have been flagged using RELEASE_APEX_CONTRIBUTIONS_*
@@ -534,11 +528,11 @@
 
 	if ctx.OtherModuleExists("platform-bootclasspath") {
 		// For accessing all bootclasspath fragments.
-		addDependencyOntoApexModulePair(ctx, "platform", "platform-bootclasspath", platformBootclasspathDepTag)
+		addDependencyOntoApexModulePair(ctx, "platform", "platform-bootclasspath", platform)
 	} else if ctx.OtherModuleExists("art-bootclasspath-fragment") {
 		// For accessing the ART bootclasspath fragment on a thin manifest (e.g., master-art) where
 		// platform-bootclasspath doesn't exist.
-		addDependencyOntoApexModulePair(ctx, "com.android.art", "art-bootclasspath-fragment", bootclasspathFragmentDepTag)
+		addDependencyOntoApexModulePair(ctx, "com.android.art", "art-bootclasspath-fragment", fragment)
 	}
 }
 
@@ -556,13 +550,13 @@
 			// We need to add a dep on only the apex listed in `contents` of the selected apex_contributions module
 			// This is not available in a structured format in `apex_contributions`, so this hack adds a dep on all `contents`
 			// (some modules like art.module.public.api do not have an apex variation since it is a pure stub module that does not get installed)
-			apexVariationOfSelected := append(ctx.Target().Variations(), blueprint.Variation{Mutator: "apex", Variation: apex})
-			if ctx.OtherModuleDependencyVariantExists(apexVariationOfSelected, selected) {
-				ctx.AddFarVariationDependencies(apexVariationOfSelected, dexpreoptBootJarDepTag, selected)
-			} else if ctx.OtherModuleDependencyVariantExists(apexVariationOfSelected, android.RemoveOptionalPrebuiltPrefix(selected)) {
-				// The prebuilt might have been renamed by prebuilt_rename mutator if the source module does not exist.
-				// Remove the prebuilt_ prefix.
-				ctx.AddFarVariationDependencies(apexVariationOfSelected, dexpreoptBootJarDepTag, android.RemoveOptionalPrebuiltPrefix(selected))
+			tag := bootclasspathDependencyTag{
+				typ: dexpreoptBootJar,
+			}
+
+			dep := android.RemoveOptionalPrebuiltPrefix(selected)
+			if ctx.OtherModuleDependencyVariantExists(ctx.Target().Variations(), dep) {
+				ctx.AddFarVariationDependencies(ctx.Target().Variations(), tag, dep)
 			}
 		}
 	}
@@ -571,23 +565,55 @@
 func gatherBootclasspathFragments(ctx android.ModuleContext) map[string]android.Module {
 	return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} {
 		fragments := make(map[string]android.Module)
+
+		type moduleInApexPair struct {
+			module string
+			apex   string
+		}
+
+		var modulesInApexes []moduleInApexPair
+
+		// Find the list of modules in apexes.
 		ctx.WalkDeps(func(child, parent android.Module) bool {
 			if !isActiveModule(ctx, child) {
 				return false
 			}
 			tag := ctx.OtherModuleDependencyTag(child)
-			if tag == platformBootclasspathDepTag {
-				return true
-			}
-			if tag == bootclasspathFragmentDepTag {
-				apexInfo, _ := android.OtherModuleProvider(ctx, child, android.ApexInfoProvider)
-				for _, apex := range apexInfo.InApexVariants {
-					fragments[apex] = child
+			if bcpTag, ok := tag.(bootclasspathDependencyTag); ok {
+				if bcpTag.typ == platform {
+					return true
 				}
-				return false
+				if bcpTag.typ == fragment {
+					if bcpTag.moduleInApex == "" {
+						panic(fmt.Errorf("expected fragment to be in apex"))
+					}
+					modulesInApexes = append(modulesInApexes, moduleInApexPair{bcpTag.moduleInApex, ctx.OtherModuleName(child)})
+					return true
+				}
 			}
 			return false
 		})
+
+		for _, moduleInApex := range modulesInApexes {
+			// Find a desired module in an apex.
+			ctx.WalkDeps(func(child, parent android.Module) bool {
+				t := ctx.OtherModuleDependencyTag(child)
+				if bcpTag, ok := t.(bootclasspathDependencyTag); ok {
+					if bcpTag.typ == platform {
+						return true
+					}
+					if bcpTag.typ == fragment && ctx.OtherModuleName(child) == moduleInApex.apex {
+						// This is the dependency from this module to the apex, recurse into it.
+						return true
+					}
+				} else if android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child)) == moduleInApex.module {
+					// This is the desired module inside the apex.
+					fragments[android.RemoveOptionalPrebuiltPrefix(moduleInApex.apex)] = child
+				}
+				return false
+			})
+		}
+
 		return fragments
 	}).(map[string]android.Module)
 }
@@ -649,6 +675,139 @@
 			installs: artBootImageHostInstalls,
 		},
 	)
+
+	d.buildBootZip(ctx)
+}
+
+// Build the boot.zip which contains the boot jars and their compilation output
+// We can do this only if preopt is enabled and if the product uses libart config (which sets the
+// default properties for preopting).
+// Origionally, this was only for ART Cloud.
+func (d *dexpreoptBootJars) buildBootZip(ctx android.ModuleContext) {
+	image := d.defaultBootImage
+	if image == nil || SkipDexpreoptBootJars(ctx) {
+		return
+	}
+	global := dexpreopt.GetGlobalConfig(ctx)
+	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
+	if global.DisablePreopt || global.OnlyPreoptArtBootImage {
+		return
+	}
+
+	bootclasspathDexFiles, bootclassPathLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
+	if len(bootclasspathDexFiles) == 0 {
+		return
+	}
+
+	systemServerDexjarsDir := android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir)
+
+	bootZipMetadataTmp := android.PathForModuleOut(ctx, "boot_zip", "METADATA.txt.tmp")
+	bootZipMetadata := android.PathForModuleOut(ctx, "boot_zip", "METADATA.txt")
+	newlineFile := android.PathForModuleOut(ctx, "boot_zip", "newline.txt")
+	android.WriteFileRule(ctx, newlineFile, "")
+
+	dexPreoptRootDir := filepath.Dir(filepath.Dir(bootclasspathDexFiles[0].String()))
+
+	var sb strings.Builder
+	sb.WriteString("bootclasspath = ")
+	for i, bootclasspathJar := range bootclasspathDexFiles {
+		if i > 0 {
+			sb.WriteString(":")
+		}
+		rel, err := filepath.Rel(dexPreoptRootDir, bootclasspathJar.String())
+		if err != nil {
+			ctx.ModuleErrorf("All dexpreopt jars should be under the same rootdir %q, but %q wasn't.", dexPreoptRootDir, bootclasspathJar)
+		} else {
+			sb.WriteString(rel)
+		}
+	}
+	sb.WriteString("\nbootclasspath-locations = ")
+	for i, bootclasspathLocation := range bootclassPathLocations {
+		if i > 0 {
+			sb.WriteString(":")
+		}
+		sb.WriteString(bootclasspathLocation)
+	}
+	sb.WriteString("\nboot-image = ")
+
+	// Infix can be 'art' (ART image for testing), 'boot' (primary), or 'mainline' (mainline
+	// extension). Soong creates a set of variables for Make, one or each boot image. The only
+	// reason why the ART image is exposed to Make is testing (art gtests) and benchmarking (art
+	// golem benchmarks). Install rules that use those variables are in dex_preopt_libart.mk. Here
+	// for dexpreopt purposes the infix is always 'boot' or 'mainline'.
+	dexpreoptInfix := "boot"
+	if global.PreoptWithUpdatableBcp {
+		dexpreoptInfix = "mainline"
+	}
+
+	var dexPreoptImageZipBoot android.Path
+	var dexPreoptImageZipArt android.Path
+	var dexPreoptImageZipMainline android.Path
+	for _, current := range append(d.otherImages, image) {
+		if current.name == dexpreoptInfix {
+			_, imageLocationsOnDevice := current.getAnyAndroidVariant().imageLocations()
+			for i, location := range imageLocationsOnDevice {
+				imageLocationsOnDevice[i] = strings.TrimPrefix(location, "/")
+			}
+			sb.WriteString(strings.Join(imageLocationsOnDevice, ":"))
+		}
+		switch current.name {
+		case "boot":
+			dexPreoptImageZipBoot = current.zip
+		case "art":
+			dexPreoptImageZipArt = current.zip
+		case "mainline":
+			dexPreoptImageZipMainline = current.zip
+		}
+	}
+	sb.WriteString("\nextra-args = ")
+	android.WriteFileRuleVerbatim(ctx, bootZipMetadataTmp, sb.String())
+	ctx.Build(pctx, android.BuildParams{
+		Rule: android.Cat,
+		Inputs: []android.Path{
+			bootZipMetadataTmp,
+			globalSoong.UffdGcFlag,
+			newlineFile,
+		},
+		Output: bootZipMetadata,
+	})
+
+	bootZipFirstPart := android.PathForModuleOut(ctx, "boot_zip", "boot_first_part.zip")
+	bootZip := android.PathForModuleOut(ctx, "boot_zip", "boot.zip")
+	builder := android.NewRuleBuilder(pctx, ctx)
+	cmd := builder.Command().BuiltTool("soong_zip").
+		FlagWithOutput("-o ", bootZipFirstPart).
+		FlagWithArg("-C ", filepath.Dir(filepath.Dir(bootclasspathDexFiles[0].String())))
+	for _, bootclasspathJar := range bootclasspathDexFiles {
+		cmd.FlagWithInput("-f ", bootclasspathJar)
+	}
+	for i := range global.SystemServerJars.Len() {
+		// Use "/system" path for JARs with "platform:" prefix. These JARs counterintuitively use
+		// "platform" prefix but they will be actually installed to /system partition.
+		// For the remaining system server JARs use the partition signified by the prefix.
+		// For example, prefix "system_ext:" will use "/system_ext" path.
+		dir := global.SystemServerJars.Apex(i)
+		if dir == "platform" {
+			dir = "system"
+		}
+		jar := global.SystemServerJars.Jar(i) + ".jar"
+		cmd.FlagWithArg("-e ", dir+"/framework/"+jar)
+		cmd.FlagWithInput("-f ", systemServerDexjarsDir.Join(ctx, jar))
+	}
+	cmd.Flag("-j")
+	cmd.FlagWithInput("-f ", bootZipMetadata)
+
+	builder.Command().BuiltTool("merge_zips").
+		Output(bootZip).
+		Input(bootZipFirstPart).
+		Input(dexPreoptImageZipBoot).
+		Input(dexPreoptImageZipArt).
+		Input(dexPreoptImageZipMainline)
+
+	builder.Build("boot_zip", "build boot.zip")
+
+	ctx.DistForGoal("droidcore", bootZipMetadata)
+	ctx.DistForGoal("droidcore", bootZip)
 }
 
 // GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make.
@@ -711,7 +870,8 @@
 	modules := make([]apexJarModulePair, 0, imageConfig.modules.Len())
 	for i := 0; i < imageConfig.modules.Len(); i++ {
 		found := false
-		for _, module := range gatherApexModulePairDepsWithTag(ctx, dexpreoptBootJarDepTag) {
+		dexpreoptBootJarModules, _ := gatherApexModulePairDepsWithTag(ctx, dexpreoptBootJar)
+		for _, module := range dexpreoptBootJarModules {
 			name := android.RemoveOptionalPrebuiltPrefix(module.Name())
 			if name == imageConfig.modules.Jar(i) {
 				modules = append(modules, apexJarModulePair{
@@ -794,11 +954,16 @@
 				"APEX '%[2]s' doesn't exist or is not added as a dependency of dex_bootjars",
 				pair.jarModule.Name(),
 				pair.apex)
+			return nil
 		}
 		bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, fragment, BootclasspathFragmentApexContentInfoProvider)
 		jar, err := bootclasspathFragmentInfo.DexBootJarPathForContentModule(pair.jarModule)
 		if err != nil {
-			ctx.ModuleErrorf("%s", err)
+			if ctx.Config().AllowMissingDependencies() {
+				ctx.AddMissingDependencies([]string{pair.jarModule.String()})
+			} else {
+				ctx.ModuleErrorf("%s", err)
+			}
 		}
 		return jar
 	}
@@ -957,9 +1122,16 @@
 
 func getApexNameToApexExportsInfoMap(ctx android.ModuleContext) apexNameToApexExportsInfoMap {
 	apexNameToApexExportsInfoMap := apexNameToApexExportsInfoMap{}
-	ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(am android.Module) {
-		if info, exists := android.OtherModuleProvider(ctx, am, android.ApexExportsInfoProvider); exists {
-			apexNameToApexExportsInfoMap[info.ApexName] = info
+
+	ctx.VisitDirectDeps(func(am android.Module) {
+		tag := ctx.OtherModuleDependencyTag(am)
+		if bcpTag, ok := tag.(bootclasspathDependencyTag); ok && bcpTag.typ == dexpreoptBootJar {
+			if bcpTag.moduleInApex == "" {
+				info, exists := android.OtherModuleProvider(ctx, am, android.ApexExportsInfoProvider)
+				if exists {
+					apexNameToApexExportsInfoMap[info.ApexName] = info
+				}
+			}
 		}
 	})
 	return apexNameToApexExportsInfoMap
@@ -1442,24 +1614,33 @@
 
 func (dbj *artBootImages) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Create a dependency on `dex_bootjars` to access the intermediate locations of host art boot image.
-	ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), dexpreoptBootJarDepTag, "dex_bootjars")
+	tag := bootclasspathDependencyTag{
+		typ: dexpreoptBootJar,
+	}
+	ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), tag, "dex_bootjars")
 }
 
 func (d *artBootImages) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(m android.Module) {
-		hostInstallsInfo, ok := android.OtherModuleProvider(ctx, m, artBootImageHostInfoProvider)
-		if !ok {
-			ctx.ModuleErrorf("Could not find information about the host variant of ART boot image")
-		}
-		installs := d.installFile(ctx, hostInstallsInfo.installs)
-		if len(installs) > 0 {
-			d.outputFile = android.OptionalPathForPath(installs[0])
-			// Create a phony target that can ART run-tests can depend on.
-			ctx.Phony(d.Name(), installs...)
-		} else {
-			// this might be true e.g. when building with `WITH_DEXPREOPT=false`
-			// create an empty file so that the `art_boot_images` is known to the packaging system.
-			d.outputFile = android.OptionalPathForPath(android.PathForModuleOut(ctx, "undefined_art_boot_images"))
+	ctx.VisitDirectDeps(func(m android.Module) {
+		tag := ctx.OtherModuleDependencyTag(m)
+		if bcpTag, ok := tag.(bootclasspathDependencyTag); ok && bcpTag.typ == dexpreoptBootJar {
+			if bcpTag.moduleInApex != "" {
+				panic("unhandled moduleInApex")
+			}
+			hostInstallsInfo, ok := android.OtherModuleProvider(ctx, m, artBootImageHostInfoProvider)
+			if !ok {
+				ctx.ModuleErrorf("Could not find information about the host variant of ART boot image")
+			}
+			installs := d.installFile(ctx, hostInstallsInfo.installs)
+			if len(installs) > 0 {
+				d.outputFile = android.OptionalPathForPath(installs[0])
+				// Create a phony target that can ART run-tests can depend on.
+				ctx.Phony(d.Name(), installs...)
+			} else {
+				// this might be true e.g. when building with `WITH_DEXPREOPT=false`
+				// create an empty file so that the `art_boot_images` is known to the packaging system.
+				d.outputFile = android.OptionalPathForPath(android.PathForModuleOut(ctx, "undefined_art_boot_images"))
+			}
 		}
 	})
 }
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index dc0973c..fb5c325 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -67,15 +67,16 @@
 
 		// ART boot image for testing only. Do not rely on it to make any build-time decision.
 		artCfg := bootImageConfig{
-			name:                 artBootImageName,
-			enabledIfExists:      "art-bootclasspath-fragment",
-			stem:                 bootImageStem,
-			installDir:           "apex/art_boot_images/javalib",
-			modules:              global.TestOnlyArtBootImageJars,
-			preloadedClassesFile: "art/build/boot/preloaded-classes",
-			compilerFilter:       "speed-profile",
-			singleImage:          false,
-			profileImports:       profileImports,
+			name:                  artBootImageName,
+			enabledIfExists:       "art-bootclasspath-fragment",
+			stem:                  bootImageStem,
+			installDir:            "apex/art_boot_images/javalib",
+			modules:               global.TestOnlyArtBootImageJars,
+			preloadedClassesFile:  "art/build/boot/preloaded-classes",
+			compilerFilter:        "speed-profile",
+			singleImage:           false,
+			profileImports:        profileImports,
+			profileProviderModule: "art-bootclasspath-fragment",
 		}
 
 		// Framework config for the boot image extension.
diff --git a/java/dexpreopt_config_test.go b/java/dexpreopt_config_test.go
index 44d2127..99b1f23 100644
--- a/java/dexpreopt_config_test.go
+++ b/java/dexpreopt_config_test.go
@@ -23,6 +23,7 @@
 )
 
 func TestBootImageConfig(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skipf("Skipping as boot image config test is only supported on linux not %s", runtime.GOOS)
 	}
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 33c682b..241941e 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -1217,6 +1217,7 @@
 	}
 
 	t.Run(imageConfig.name, func(t *testing.T) {
+		t.Parallel()
 		nestedCheckBootImageConfig(t, result, imageConfig, mutated, expected)
 	})
 }
@@ -1236,7 +1237,7 @@
 	android.AssertPathRelativeToTopEquals(t, "zip", expected.zip, imageConfig.zip)
 
 	if !mutated {
-		dexBootJarModule := result.ModuleForTests("dex_bootjars", "android_common")
+		dexBootJarModule := result.ModuleForTests(t, "dex_bootjars", "android_common")
 		profileInstallInfo, _ := android.OtherModuleProvider(result, dexBootJarModule.Module(), profileInstallInfoProvider)
 		assertInstallsEqual(t, "profileInstalls", expected.profileInstalls, profileInstallInfo.profileInstalls)
 		android.AssertStringEquals(t, "profileLicenseMetadataFile", expected.profileLicenseMetadataFile, profileInstallInfo.profileLicenseMetadataFile.RelativeToTop().String())
@@ -1246,6 +1247,7 @@
 	for i, variant := range imageConfig.variants {
 		expectedVariant := expected.variants[i]
 		t.Run(variant.target.Arch.ArchType.String(), func(t *testing.T) {
+			t.Parallel()
 			android.AssertDeepEquals(t, "archType", expectedVariant.archType, variant.target.Arch.ArchType)
 			android.AssertDeepEquals(t, "dexLocations", expectedVariant.dexLocations, variant.dexLocations)
 			android.AssertDeepEquals(t, "dexLocationsDeps", expectedVariant.dexLocationsDeps, variant.dexLocationsDeps)
@@ -1279,7 +1281,7 @@
 // checkDexpreoptMakeVars checks the DEXPREOPT_ prefixed make vars produced by dexpreoptBootJars
 // singleton.
 func checkDexpreoptMakeVars(t *testing.T, result *android.TestResult, expectedLicenseMetadataFile string) {
-	vars := result.MakeVarsForTesting(func(variable android.MakeVarVariable) bool {
+	vars := result.MakeVarsForTesting(t, func(variable android.MakeVarVariable) bool {
 		return strings.HasPrefix(variable.Name(), "DEXPREOPT_")
 	})
 
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 07d0595..f437da0 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"runtime"
-	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -30,6 +29,7 @@
 }
 
 func TestDexpreoptEnabled(t *testing.T) {
+	t.Parallel()
 	tests := []struct {
 		name        string
 		bp          string
@@ -219,6 +219,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			preparers := android.GroupFixturePreparers(
 				PrepareForTestWithDexpreopt,
 				PrepareForTestWithFakeApexMutator,
@@ -238,7 +239,7 @@
 				variant += "_apex1000"
 			}
 
-			dexpreopt := ctx.ModuleForTests(moduleName, variant).MaybeRule("dexpreopt")
+			dexpreopt := ctx.ModuleForTests(t, moduleName, variant).MaybeRule("dexpreopt")
 			enabled := dexpreopt.Rule != nil
 
 			if enabled != test.enabled {
@@ -258,6 +259,7 @@
 }
 
 func TestDex2oatToolDeps(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		// The host binary paths checked below are build OS dependent.
 		t.Skipf("Unsupported build OS %s", runtime.GOOS)
@@ -273,6 +275,7 @@
 		name := fmt.Sprintf("sourceEnabled:%t,prebuiltEnabled:%t,prebuiltPreferred:%t",
 			sourceEnabled, prebuiltEnabled, prebuiltPreferred)
 		t.Run(name, func(t *testing.T) {
+			t.Parallel()
 			result := preparers.RunTestWithBp(t, fmt.Sprintf(`
 					cc_binary {
 						name: "dex2oatd",
@@ -296,7 +299,7 @@
 		})
 	}
 
-	sourceDex2oatPath := "host/linux-x86/bin/dex2oatd"
+	sourceDex2oatPath := "../host/linux-x86/bin/dex2oatd"
 	prebuiltDex2oatPath := ".intermediates/prebuilt_dex2oatd/linux_glibc_x86_64/dex2oatd"
 
 	testDex2oatToolDep(true, false, false, sourceDex2oatPath)
@@ -305,7 +308,7 @@
 	testDex2oatToolDep(false, true, false, prebuiltDex2oatPath)
 }
 
-func TestDexpreoptBuiltInstalledForApex(t *testing.T) {
+func TestApexSystemServerDexpreoptInstalls(t *testing.T) {
 	preparers := android.GroupFixturePreparers(
 		PrepareForTestWithDexpreopt,
 		PrepareForTestWithFakeApexMutator,
@@ -322,28 +325,38 @@
 			sdk_version: "current",
 		}`)
 	ctx := result.TestContext
-	module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
+	module := ctx.ModuleForTests(t, "service-foo", "android_common_apex1000")
 	library := module.Module().(*Library)
 
-	installs := library.dexpreopter.DexpreoptBuiltInstalledForApex()
+	installs := library.dexpreopter.ApexSystemServerDexpreoptInstalls()
+	dexJars := library.dexpreopter.ApexSystemServerDexJars()
 
 	android.AssertIntEquals(t, "install count", 2, len(installs))
+	android.AssertIntEquals(t, "dexjar count", 1, len(dexJars))
 
-	android.AssertStringEquals(t, "installs[0] FullModuleName",
-		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
-		installs[0].FullModuleName())
+	android.AssertPathRelativeToTopEquals(t, "installs[0] OutputPathOnHost",
+		"out/soong/.intermediates/service-foo/android_common_apex1000/dexpreopt/service-foo/oat/arm64/javalib.odex",
+		installs[0].OutputPathOnHost)
 
-	android.AssertStringEquals(t, "installs[0] SubModuleName",
-		"-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
-		installs[0].SubModuleName())
+	android.AssertPathRelativeToTopEquals(t, "installs[0] InstallDirOnDevice",
+		"out/target/product/test_device/system/framework/oat/arm64",
+		installs[0].InstallDirOnDevice)
 
-	android.AssertStringEquals(t, "installs[1] FullModuleName",
-		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
-		installs[1].FullModuleName())
+	android.AssertStringEquals(t, "installs[0] InstallFileOnDevice",
+		"apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+		installs[0].InstallFileOnDevice)
 
-	android.AssertStringEquals(t, "installs[1] SubModuleName",
-		"-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
-		installs[1].SubModuleName())
+	android.AssertPathRelativeToTopEquals(t, "installs[1] OutputPathOnHost",
+		"out/soong/.intermediates/service-foo/android_common_apex1000/dexpreopt/service-foo/oat/arm64/javalib.vdex",
+		installs[1].OutputPathOnHost)
+
+	android.AssertPathRelativeToTopEquals(t, "installs[1] InstallDirOnDevice",
+		"out/target/product/test_device/system/framework/oat/arm64",
+		installs[1].InstallDirOnDevice)
+
+	android.AssertStringEquals(t, "installs[1] InstallFileOnDevice",
+		"apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+		installs[1].InstallFileOnDevice)
 
 	// Not an APEX system server jar.
 	result = preparers.RunTestWithBp(t, `
@@ -354,101 +367,14 @@
 			sdk_version: "current",
 		}`)
 	ctx = result.TestContext
-	module = ctx.ModuleForTests("foo", "android_common")
+	module = ctx.ModuleForTests(t, "foo", "android_common")
 	library = module.Module().(*Library)
 
-	installs = library.dexpreopter.DexpreoptBuiltInstalledForApex()
+	installs = library.dexpreopter.ApexSystemServerDexpreoptInstalls()
+	dexJars = library.dexpreopter.ApexSystemServerDexJars()
 
 	android.AssertIntEquals(t, "install count", 0, len(installs))
-}
-
-func filterDexpreoptEntriesList(entriesList []android.AndroidMkEntries) []android.AndroidMkEntries {
-	var results []android.AndroidMkEntries
-	for _, entries := range entriesList {
-		if strings.Contains(entries.EntryMap["LOCAL_MODULE"][0], "-dexpreopt-") {
-			results = append(results, entries)
-		}
-	}
-	return results
-}
-
-func verifyEntries(t *testing.T, message string, expectedModule string,
-	expectedPrebuiltModuleFile string, expectedModulePath string, expectedInstalledModuleStem string,
-	entries android.AndroidMkEntries) {
-	android.AssertStringEquals(t, message+" LOCAL_MODULE", expectedModule,
-		entries.EntryMap["LOCAL_MODULE"][0])
-
-	android.AssertStringEquals(t, message+" LOCAL_MODULE_CLASS", "ETC",
-		entries.EntryMap["LOCAL_MODULE_CLASS"][0])
-
-	android.AssertStringDoesContain(t, message+" LOCAL_PREBUILT_MODULE_FILE",
-		entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"][0], expectedPrebuiltModuleFile)
-
-	android.AssertStringDoesContain(t, message+" LOCAL_MODULE_PATH",
-		entries.EntryMap["LOCAL_MODULE_PATH"][0], expectedModulePath)
-
-	android.AssertStringEquals(t, message+" LOCAL_INSTALLED_MODULE_STEM",
-		expectedInstalledModuleStem, entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"][0])
-
-	android.AssertStringEquals(t, message+" LOCAL_NOT_AVAILABLE_FOR_PLATFORM",
-		"false", entries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"][0])
-}
-
-func TestAndroidMkEntriesForApex(t *testing.T) {
-	preparers := android.GroupFixturePreparers(
-		PrepareForTestWithDexpreopt,
-		PrepareForTestWithFakeApexMutator,
-		dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
-	)
-
-	// An APEX system server jar.
-	result := preparers.RunTestWithBp(t, `
-		java_library {
-			name: "service-foo",
-			installable: true,
-			srcs: ["a.java"],
-			apex_available: ["com.android.apex1"],
-			sdk_version: "current",
-		}`)
-	ctx := result.TestContext
-	module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
-
-	entriesList := android.AndroidMkEntriesForTest(t, ctx, module.Module())
-	entriesList = filterDexpreoptEntriesList(entriesList)
-
-	android.AssertIntEquals(t, "entries count", 2, len(entriesList))
-
-	verifyEntries(t,
-		"entriesList[0]",
-		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
-		"/dexpreopt/service-foo/oat/arm64/javalib.odex",
-		"/system/framework/oat/arm64",
-		"apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
-		entriesList[0])
-
-	verifyEntries(t,
-		"entriesList[1]",
-		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
-		"/dexpreopt/service-foo/oat/arm64/javalib.vdex",
-		"/system/framework/oat/arm64",
-		"apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
-		entriesList[1])
-
-	// Not an APEX system server jar.
-	result = preparers.RunTestWithBp(t, `
-		java_library {
-			name: "foo",
-			installable: true,
-			srcs: ["a.java"],
-			sdk_version: "current",
-		}`)
-	ctx = result.TestContext
-	module = ctx.ModuleForTests("foo", "android_common")
-
-	entriesList = android.AndroidMkEntriesForTest(t, ctx, module.Module())
-	entriesList = filterDexpreoptEntriesList(entriesList)
-
-	android.AssertIntEquals(t, "entries count", 0, len(entriesList))
+	android.AssertIntEquals(t, "dexjar count", 0, len(dexJars))
 }
 
 func TestGenerateProfileEvenIfDexpreoptIsDisabled(t *testing.T) {
@@ -470,7 +396,7 @@
 		}`)
 
 	ctx := result.TestContext
-	dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
+	dexpreopt := ctx.ModuleForTests(t, "foo", "android_common").MaybeRule("dexpreopt")
 
 	expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/foo/profile.prof"}
 
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 2dda72b..3faf294 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -19,6 +19,7 @@
 	"path/filepath"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -194,6 +195,9 @@
 				"them instead.")
 		}
 		return false
+	} else if ctx.Config().PartialCompileFlags().Disable_stub_validation &&
+		!ctx.Config().BuildFromTextStub() {
+		return false
 	} else if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" {
 		return true
 	} else if String(apiToCheck.Api_file) != "" {
@@ -357,7 +361,7 @@
 		deps.aidlPreprocess = sdkDep.aidl
 	}
 
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
@@ -381,9 +385,9 @@
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
 				deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...)
-			} else if dep, ok := module.(android.SourceFileProducer); ok {
-				checkProducesJars(ctx, dep)
-				deps.classpath = append(deps.classpath, dep.Srcs()...)
+			} else if dep, ok := android.OtherModuleProvider(ctx, module, android.SourceFilesInfoProvider); ok {
+				checkProducesJars(ctx, dep, module)
+				deps.classpath = append(deps.classpath, dep.Srcs...)
 			} else {
 				ctx.ModuleErrorf("depends on non-java module %q", otherName)
 			}
@@ -874,6 +878,13 @@
 	Path *string
 }
 
+type ExportedDroiddocDirInfo struct {
+	Deps android.Paths
+	Dir  android.Path
+}
+
+var ExportedDroiddocDirInfoProvider = blueprint.NewProvider[ExportedDroiddocDirInfo]()
+
 type ExportedDroiddocDir struct {
 	android.ModuleBase
 
@@ -897,6 +908,11 @@
 	path := String(d.properties.Path)
 	d.dir = android.PathForModuleSrc(ctx, path)
 	d.deps = android.PathsForModuleSrc(ctx, []string{filepath.Join(path, "**/*")})
+
+	android.SetProvider(ctx, ExportedDroiddocDirInfoProvider, ExportedDroiddocDirInfo{
+		Dir:  d.dir,
+		Deps: d.deps,
+	})
 }
 
 // Defaults
diff --git a/java/droiddoc_test.go b/java/droiddoc_test.go
index 9e1ebbe..0c977bc 100644
--- a/java/droiddoc_test.go
+++ b/java/droiddoc_test.go
@@ -23,6 +23,7 @@
 )
 
 func TestDroiddoc(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		droiddoc_exported_dir {
 		    name: "droiddoc-templates-sdk",
@@ -69,13 +70,13 @@
 			"bar-doc/a.java": nil,
 			"bar-doc/b.java": nil,
 		})
-	barStubsOutputs := ctx.ModuleForTests("bar-stubs", "android_common").OutputFiles(ctx, t, "")
+	barStubsOutputs := ctx.ModuleForTests(t, "bar-stubs", "android_common").OutputFiles(ctx, t, "")
 	if len(barStubsOutputs) != 1 {
 		t.Errorf("Expected one output from \"bar-stubs\" got %s", barStubsOutputs)
 	}
 
 	barStubsOutput := barStubsOutputs[0]
-	barDoc := ctx.ModuleForTests("bar-doc", "android_common")
+	barDoc := ctx.ModuleForTests(t, "bar-doc", "android_common")
 	javaDoc := barDoc.Rule("javadoc")
 	if g, w := android.PathsRelativeToTop(javaDoc.Implicits), android.PathRelativeToTop(barStubsOutput); !inList(w, g) {
 		t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
@@ -97,6 +98,7 @@
 }
 
 func TestDroiddocArgsAndFlagsCausesError(t *testing.T) {
+	t.Parallel()
 	testJavaError(t, "flags is set. Cannot set args", `
 		droiddoc_exported_dir {
 		    name: "droiddoc-templates-sdk",
diff --git a/java/droidstubs.go b/java/droidstubs.go
index e955949..b30c844 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -20,6 +20,7 @@
 	"regexp"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -27,6 +28,28 @@
 	"android/soong/remoteexec"
 )
 
+type StubsInfo struct {
+	ApiVersionsXml android.Path
+	AnnotationsZip android.Path
+	ApiFile        android.Path
+	RemovedApiFile android.Path
+}
+
+type DroidStubsInfo struct {
+	CurrentApiTimestamp android.Path
+	EverythingStubsInfo StubsInfo
+	ExportableStubsInfo StubsInfo
+}
+
+var DroidStubsInfoProvider = blueprint.NewProvider[DroidStubsInfo]()
+
+type StubsSrcInfo struct {
+	EverythingStubsSrcJar android.Path
+	ExportableStubsSrcJar android.Path
+}
+
+var StubsSrcInfoProvider = blueprint.NewProvider[StubsSrcInfo]()
+
 // The values allowed for Droidstubs' Api_levels_sdk_type
 var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"}
 
@@ -498,9 +521,9 @@
 }
 
 func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
-	ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
-		if t, ok := m.(*ExportedDroiddocDir); ok {
-			cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
+	ctx.VisitDirectDepsProxyWithTag(metalavaMergeAnnotationsDirTag, func(m android.ModuleProxy) {
+		if t, ok := android.OtherModuleProvider(ctx, m, ExportedDroiddocDirInfoProvider); ok {
+			cmd.FlagWithArg("--merge-qualifier-annotations ", t.Dir.String()).Implicits(t.Deps)
 		} else {
 			ctx.PropertyErrorf("merge_annotations_dirs",
 				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
@@ -509,9 +532,9 @@
 }
 
 func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
-	ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
-		if t, ok := m.(*ExportedDroiddocDir); ok {
-			cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
+	ctx.VisitDirectDepsProxyWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.ModuleProxy) {
+		if t, ok := android.OtherModuleProvider(ctx, m, ExportedDroiddocDirInfoProvider); ok {
+			cmd.FlagWithArg("--merge-inclusion-annotations ", t.Dir.String()).Implicits(t.Deps)
 		} else {
 			ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
 				"module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
@@ -525,12 +548,12 @@
 		d.apiLevelsGenerationFlags(ctx, cmd, stubsType, apiVersionsXml)
 		apiVersions = apiVersionsXml
 	} else {
-		ctx.VisitDirectDepsWithTag(metalavaAPILevelsModuleTag, func(m android.Module) {
-			if s, ok := m.(*Droidstubs); ok {
+		ctx.VisitDirectDepsProxyWithTag(metalavaAPILevelsModuleTag, func(m android.ModuleProxy) {
+			if s, ok := android.OtherModuleProvider(ctx, m, DroidStubsInfoProvider); ok {
 				if stubsType == Everything {
-					apiVersions = s.everythingArtifacts.apiVersionsXml
+					apiVersions = s.EverythingStubsInfo.ApiVersionsXml
 				} else if stubsType == Exportable {
-					apiVersions = s.exportableArtifacts.apiVersionsXml
+					apiVersions = s.ExportableStubsInfo.ApiVersionsXml
 				} else {
 					ctx.ModuleErrorf("%s stubs type does not generate api-versions.xml file", stubsType.String())
 				}
@@ -541,7 +564,15 @@
 		})
 	}
 	if apiVersions != nil {
-		cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
+		// We are migrating from a single API level to major.minor
+		// versions and PlatformSdkVersionFull is not yet set in all
+		// release configs. If it is not set, fall back on the single
+		// API level.
+		if fullSdkVersion := ctx.Config().PlatformSdkVersionFull(); len(fullSdkVersion) > 0 {
+			cmd.FlagWithArg("--current-version ", fullSdkVersion)
+		} else {
+			cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
+		}
 		cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
 		cmd.FlagWithInput("--apply-api-levels ", apiVersions)
 	}
@@ -603,18 +634,18 @@
 
 	var dirs []string
 	var extensions_dir string
-	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
-		if t, ok := m.(*ExportedDroiddocDir); ok {
-			extRegex := regexp.MustCompile(t.dir.String() + extensionsPattern)
+	ctx.VisitDirectDepsProxyWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.ModuleProxy) {
+		if t, ok := android.OtherModuleProvider(ctx, m, ExportedDroiddocDirInfoProvider); ok {
+			extRegex := regexp.MustCompile(t.Dir.String() + extensionsPattern)
 
 			// Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps;
 			// ideally this should be read from prebuiltApis.properties.Extensions_*
-			for _, dep := range t.deps {
+			for _, dep := range t.Deps {
 				// Check to see if it matches an extension first.
 				depBase := dep.Base()
 				if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil {
 					if extensions_dir == "" {
-						extensions_dir = t.dir.String() + "/extensions"
+						extensions_dir = t.Dir.String() + "/extensions"
 					}
 					cmd.Implicit(dep)
 				} else if depBase == filename {
@@ -622,23 +653,17 @@
 					cmd.Implicit(dep)
 				} else if depBase == AndroidPlusUpdatableJar && d.properties.Extensions_info_file != nil {
 					// The output api-versions.xml has been requested to include information on SDK
-					// extensions. That means it also needs to include
-					// so
-					// The module-lib and system-server directories should use `android-plus-updatable.jar`
-					// instead of `android.jar`. See AndroidPlusUpdatableJar for more information.
-					cmd.Implicit(dep)
-				} else if filename != "android.jar" && depBase == "android.jar" {
-					// Metalava implicitly searches these patterns:
-					//  prebuilts/tools/common/api-versions/android-%/android.jar
-					//  prebuilts/sdk/%/public/android.jar
-					// Add android.jar files from the api_levels_annotations_dirs directories to try
-					// to satisfy these patterns.  If Metalava can't find a match for an API level
-					// between 1 and 28 in at least one pattern it will fail.
+					// extensions, i.e. updatable Apis. That means it also needs to include the history of
+					// those updatable APIs. Usually, they would be included in the `android.jar` file but
+					// unfortunately, the `module-lib` and `system-server` cannot as it would lead to build
+					// cycles. So, the module-lib and system-server directories contain an
+					// `android-plus-updatable.jar` that should be used instead of `android.jar`. See
+					// AndroidPlusUpdatableJar for more information.
 					cmd.Implicit(dep)
 				}
 			}
 
-			dirs = append(dirs, t.dir.String())
+			dirs = append(dirs, t.Dir.String())
 		} else {
 			ctx.PropertyErrorf("api_levels_annotations_dirs",
 				"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
@@ -646,11 +671,11 @@
 	})
 
 	// Generate the list of --android-jar-pattern options. The order matters so the first one which
-	// matches will be the one that is used for a specific api level..
+	// matches will be the one that is used for a specific api level.
 	for _, sdkDir := range sdkDirs {
 		for _, dir := range dirs {
 			addPattern := func(jarFilename string) {
-				cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, jarFilename))
+				cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/{version:major.minor?}/%s/%s", dir, sdkDir, jarFilename))
 			}
 
 			if sdkDir == "module-lib" || sdkDir == "system-server" {
@@ -665,6 +690,10 @@
 
 			addPattern(filename)
 		}
+
+		if extensions_dir != "" {
+			cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/{version:extension}/%s/{module}.jar", extensions_dir, sdkDir))
+		}
 	}
 
 	if d.properties.Extensions_info_file != nil {
@@ -673,7 +702,6 @@
 		}
 		info_file := android.PathForModuleSrc(ctx, *d.properties.Extensions_info_file)
 		cmd.Implicit(info_file)
-		cmd.FlagWithArg("--sdk-extensions-root ", extensions_dir)
 		cmd.FlagWithArg("--sdk-extensions-info ", info_file.String())
 	}
 }
@@ -700,7 +728,8 @@
 }
 
 func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths,
-	srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams, configFiles android.Paths) *android.RuleBuilderCommand {
+	srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams,
+	configFiles android.Paths, apiSurface *string) *android.RuleBuilderCommand {
 	rule.Command().Text("rm -rf").Flag(homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(homeDir.String())
 
@@ -746,6 +775,8 @@
 
 	addMetalavaConfigFilesToCmd(cmd, configFiles)
 
+	addOptionalApiSurfaceToCmd(cmd, apiSurface)
+
 	return cmd
 }
 
@@ -764,6 +795,14 @@
 	cmd.FlagForEachInput("--config-file ", configFiles)
 }
 
+// addOptionalApiSurfaceToCmd adds --api-surface option is apiSurface is not `nil`.
+func addOptionalApiSurfaceToCmd(cmd *android.RuleBuilderCommand, apiSurface *string) {
+	if apiSurface != nil {
+		cmd.Flag("--api-surface")
+		cmd.Flag(*apiSurface)
+	}
+}
+
 // Pass flagged apis related flags to metalava. When aconfig_declarations property is not
 // defined for a module, simply revert all flagged apis annotations. If aconfig_declarations
 // property is defined, apply transformations and only revert the flagged apis that are not
@@ -790,19 +829,17 @@
 		}
 	}
 
-	if len(aconfigFlagsPaths) == 0 {
-		// This argument should not be added for "everything" stubs
-		cmd.Flag("--revert-annotation android.annotation.FlaggedApi")
-		return
-	}
+	// If aconfigFlagsPaths is empty then it is still important to generate the
+	// Metalava flags config file, albeit with an empty set of flags, so that all
+	// flagged APIs will be reverted.
 
-	releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String()))
-	revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String()))
+	releasedFlagsFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flags-%s.pb", stubsType.String()))
+	metalavaFlagsConfigFile := android.PathForModuleOut(ctx, fmt.Sprintf("flags-config-%s.xml", stubsType.String()))
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        gatherReleasedFlaggedApisRule,
 		Inputs:      aconfigFlagsPaths,
-		Output:      releasedFlaggedApisFile,
+		Output:      releasedFlagsFile,
 		Description: fmt.Sprintf("%s gather aconfig flags", stubsType),
 		Args: map[string]string{
 			"flags_path":  android.JoinPathsWithPrefix(aconfigFlagsPaths, "--cache "),
@@ -812,12 +849,12 @@
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        generateMetalavaRevertAnnotationsRule,
-		Input:       releasedFlaggedApisFile,
-		Output:      revertAnnotationsFile,
-		Description: fmt.Sprintf("%s revert annotations", stubsType),
+		Input:       releasedFlagsFile,
+		Output:      metalavaFlagsConfigFile,
+		Description: fmt.Sprintf("%s metalava flags config", stubsType),
 	})
 
-	cmd.FlagWithInput("@", revertAnnotationsFile)
+	cmd.FlagWithInput("--config-file ", metalavaFlagsConfigFile)
 }
 
 func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
@@ -838,7 +875,8 @@
 
 	configFiles := android.PathsForModuleSrc(ctx, d.properties.ConfigFiles)
 
-	cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig, configFiles)
+	cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig,
+		configFiles, d.properties.Api_surface)
 	cmd.Implicits(d.Javadoc.implicits)
 
 	d.stubsFlags(ctx, cmd, params.stubsDir, params.stubConfig.stubsType, params.stubConfig.checkApi)
@@ -1177,6 +1215,34 @@
 	rule.Build(fmt.Sprintf("metalava_%s", params.stubConfig.stubsType.String()), "metalava merged")
 }
 
+func (d *Droidstubs) setPhonyRules(ctx android.ModuleContext) {
+	if d.apiFile != nil {
+		ctx.Phony(d.Name(), d.apiFile)
+		ctx.Phony(fmt.Sprintf("%s.txt", d.Name()), d.apiFile)
+	}
+	if d.removedApiFile != nil {
+		ctx.Phony(d.Name(), d.removedApiFile)
+		ctx.Phony(fmt.Sprintf("%s.txt", d.Name()), d.removedApiFile)
+	}
+	if d.checkCurrentApiTimestamp != nil {
+		ctx.Phony(fmt.Sprintf("%s-check-current-api", d.Name()), d.checkCurrentApiTimestamp)
+		ctx.Phony("checkapi", d.checkCurrentApiTimestamp)
+	}
+	if d.updateCurrentApiTimestamp != nil {
+		ctx.Phony(fmt.Sprintf("%s-update-current-api", d.Name()), d.updateCurrentApiTimestamp)
+		ctx.Phony("update-api", d.updateCurrentApiTimestamp)
+	}
+	if d.checkLastReleasedApiTimestamp != nil {
+		ctx.Phony(fmt.Sprintf("%s-check-last-released-api", d.Name()), d.checkLastReleasedApiTimestamp)
+	}
+	if d.apiLintTimestamp != nil {
+		ctx.Phony(fmt.Sprintf("%s-api-lint", d.Name()), d.apiLintTimestamp)
+	}
+	if d.checkNullabilityWarningsTimestamp != nil {
+		ctx.Phony(fmt.Sprintf("%s-check-nullability-warnings", d.Name()), d.checkNullabilityWarningsTimestamp)
+	}
+}
+
 func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	deps := d.Javadoc.collectDeps(ctx)
 
@@ -1185,7 +1251,7 @@
 
 	// Add options for the other optional tasks: API-lint and check-released.
 	// We generate separate timestamp files for them.
-	doApiLint := BoolDefault(d.properties.Check_api.Api_lint.Enabled, false)
+	doApiLint := BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) && !ctx.Config().PartialCompileFlags().Disable_api_lint
 	doCheckReleased := apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released")
 
 	writeSdkValues := Bool(d.properties.Write_sdk_values)
@@ -1220,6 +1286,41 @@
 	stubCmdParams.stubsType = Exportable
 	d.exportableStubCmd(ctx, stubCmdParams)
 
+	if String(d.properties.Check_nullability_warnings) != "" {
+		if d.everythingArtifacts.nullabilityWarningsFile == nil {
+			ctx.PropertyErrorf("check_nullability_warnings",
+				"Cannot specify check_nullability_warnings unless validating nullability")
+		}
+
+		checkNullabilityWarningsPath := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
+
+		d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_nullability_warnings.timestamp")
+
+		msg := fmt.Sprintf(`\n******************************\n`+
+			`The warnings encountered during nullability annotation validation did\n`+
+			`not match the checked in file of expected warnings. The diffs are shown\n`+
+			`above. You have two options:\n`+
+			`   1. Resolve the differences by editing the nullability annotations.\n`+
+			`   2. Update the file of expected warnings by running:\n`+
+			`         cp %s %s\n`+
+			`       and submitting the updated file as part of your change.`,
+			d.everythingArtifacts.nullabilityWarningsFile, checkNullabilityWarningsPath)
+
+		rule := android.NewRuleBuilder(pctx, ctx)
+
+		rule.Command().
+			Text("(").
+			Text("diff").Input(checkNullabilityWarningsPath).Input(d.everythingArtifacts.nullabilityWarningsFile).
+			Text("&&").
+			Text("touch").Output(d.checkNullabilityWarningsTimestamp).
+			Text(") || (").
+			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+			Text("; exit 38").
+			Text(")")
+
+		rule.Build("nullabilityWarningsCheck", "nullability warnings check")
+	}
+
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
 
 		if len(d.Javadoc.properties.Out) > 0 {
@@ -1269,13 +1370,25 @@
 			`Note that DISABLE_STUB_VALIDATION=true does not bypass checkapi.\n`+
 			`******************************\n`, ctx.ModuleName())
 
-		rule.Command().
+		cmd := rule.Command().
 			Text("touch").Output(d.checkCurrentApiTimestamp).
 			Text(") || (").
 			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
 			Text("; exit 38").
 			Text(")")
 
+		if d.apiLintTimestamp != nil {
+			cmd.Validation(d.apiLintTimestamp)
+		}
+
+		if d.checkLastReleasedApiTimestamp != nil {
+			cmd.Validation(d.checkLastReleasedApiTimestamp)
+		}
+
+		if d.checkNullabilityWarningsTimestamp != nil {
+			cmd.Validation(d.checkNullabilityWarningsTimestamp)
+		}
+
 		rule.Build("metalavaCurrentApiCheck", "check current API")
 
 		d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "update_current_api.timestamp")
@@ -1305,42 +1418,49 @@
 		rule.Build("metalavaCurrentApiUpdate", "update current API")
 	}
 
-	if String(d.properties.Check_nullability_warnings) != "" {
-		if d.everythingArtifacts.nullabilityWarningsFile == nil {
-			ctx.PropertyErrorf("check_nullability_warnings",
-				"Cannot specify check_nullability_warnings unless validating nullability")
-		}
-
-		checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
-
-		d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_nullability_warnings.timestamp")
-
-		msg := fmt.Sprintf(`\n******************************\n`+
-			`The warnings encountered during nullability annotation validation did\n`+
-			`not match the checked in file of expected warnings. The diffs are shown\n`+
-			`above. You have two options:\n`+
-			`   1. Resolve the differences by editing the nullability annotations.\n`+
-			`   2. Update the file of expected warnings by running:\n`+
-			`         cp %s %s\n`+
-			`       and submitting the updated file as part of your change.`,
-			d.everythingArtifacts.nullabilityWarningsFile, checkNullabilityWarnings)
-
-		rule := android.NewRuleBuilder(pctx, ctx)
-
-		rule.Command().
-			Text("(").
-			Text("diff").Input(checkNullabilityWarnings).Input(d.everythingArtifacts.nullabilityWarningsFile).
-			Text("&&").
-			Text("touch").Output(d.checkNullabilityWarningsTimestamp).
-			Text(") || (").
-			Text("echo").Flag("-e").Flag(`"` + msg + `"`).
-			Text("; exit 38").
-			Text(")")
-
-		rule.Build("nullabilityWarningsCheck", "nullability warnings check")
+	droidInfo := DroidStubsInfo{
+		CurrentApiTimestamp: d.CurrentApiTimestamp(),
+		EverythingStubsInfo: StubsInfo{},
+		ExportableStubsInfo: StubsInfo{},
 	}
+	setDroidInfo(ctx, d, &droidInfo.EverythingStubsInfo, Everything)
+	setDroidInfo(ctx, d, &droidInfo.ExportableStubsInfo, Exportable)
+	android.SetProvider(ctx, DroidStubsInfoProvider, droidInfo)
+
+	android.SetProvider(ctx, StubsSrcInfoProvider, StubsSrcInfo{
+		EverythingStubsSrcJar: d.stubsSrcJar,
+		ExportableStubsSrcJar: d.exportableStubsSrcJar,
+	})
 
 	d.setOutputFiles(ctx)
+
+	d.setPhonyRules(ctx)
+
+	if d.apiLintTimestamp != nil {
+		if d.apiLintReport != nil {
+			ctx.DistForGoalsWithFilename(
+				[]string{fmt.Sprintf("%s-api-lint", d.Name()), "droidcore"},
+				d.apiLintReport,
+				fmt.Sprintf("apilint/%s-lint-report.txt", d.Name()),
+			)
+		}
+	}
+}
+
+func setDroidInfo(ctx android.ModuleContext, d *Droidstubs, info *StubsInfo, typ StubsType) {
+	if typ == Everything {
+		info.ApiFile = d.apiFile
+		info.RemovedApiFile = d.removedApiFile
+		info.AnnotationsZip = d.everythingArtifacts.annotationsZip
+		info.ApiVersionsXml = d.everythingArtifacts.apiVersionsXml
+	} else if typ == Exportable {
+		info.ApiFile = d.exportableApiFile
+		info.RemovedApiFile = d.exportableRemovedApiFile
+		info.AnnotationsZip = d.exportableArtifacts.annotationsZip
+		info.ApiVersionsXml = d.exportableArtifacts.apiVersionsXml
+	} else {
+		ctx.ModuleErrorf("failed to set ApiVersionsXml, stubs type not supported: %d", typ)
+	}
 }
 
 // This method sets the outputFiles property, which is used to set the
@@ -1508,6 +1628,11 @@
 		p.stubsSrcJar = outPath
 	}
 
+	android.SetProvider(ctx, StubsSrcInfoProvider, StubsSrcInfo{
+		EverythingStubsSrcJar: p.stubsSrcJar,
+		ExportableStubsSrcJar: p.stubsSrcJar,
+	})
+
 	ctx.SetOutputFiles(android.Paths{p.stubsSrcJar}, "")
 	// prebuilt droidstubs does not output "exportable" stubs.
 	// Output the "everything" stubs srcjar file if the tag is ".exportable".
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 1e8362c..1f9d223 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -27,6 +27,7 @@
 )
 
 func TestDroidstubs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		droiddoc_exported_dir {
 			name: "droiddoc-templates-sdk",
@@ -82,13 +83,13 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		manifest := m.Output("metalava.sbox.textproto")
 		sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, manifest)
 		cmdline := String(sboxProto.Commands[0].Command)
 		android.AssertStringContainsEquals(t, "api-versions generation flag", cmdline, "--generate-api-levels", c.generate_xml)
 		if c.expectedJarFilename != "" {
-			expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename
+			expected := "--android-jar-pattern ./{version:major.minor?}/public/" + c.expectedJarFilename
 			if !strings.Contains(cmdline, expected) {
 				t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, cmdline)
 			}
@@ -131,7 +132,7 @@
 			"foo-doc/a.java": nil,
 		})
 
-	m := ctx.ModuleForTests("foo-stubs", "android_common")
+	m := ctx.ModuleForTests(t, "foo-stubs", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
 	cmd := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command)
 	r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`)
@@ -139,35 +140,38 @@
 }
 
 func TestPublicDroidstubs(t *testing.T) {
+	t.Parallel()
 	patterns := getAndroidJarPatternsForDroidstubs(t, "public")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar",
 	}, patterns)
 }
 
 func TestSystemDroidstubs(t *testing.T) {
+	t.Parallel()
 	patterns := getAndroidJarPatternsForDroidstubs(t, "system")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/system/android.jar",
-		"--android-jar-pattern someotherdir/%/system/android.jar",
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar",
 	}, patterns)
 }
 
 func TestModuleLibDroidstubs(t *testing.T) {
+	t.Parallel()
 	patterns := getAndroidJarPatternsForDroidstubs(t, "module-lib")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/module-lib/android.jar",
-		"--android-jar-pattern someotherdir/%/module-lib/android.jar",
-		"--android-jar-pattern somedir/%/system/android.jar",
-		"--android-jar-pattern someotherdir/%/system/android.jar",
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/module-lib/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/module-lib/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar",
 	}, patterns)
 }
 
@@ -175,14 +179,14 @@
 	patterns := getAndroidJarPatternsForDroidstubs(t, "system-server")
 
 	android.AssertArrayString(t, "order of patterns", []string{
-		"--android-jar-pattern somedir/%/system-server/android.jar",
-		"--android-jar-pattern someotherdir/%/system-server/android.jar",
-		"--android-jar-pattern somedir/%/module-lib/android.jar",
-		"--android-jar-pattern someotherdir/%/module-lib/android.jar",
-		"--android-jar-pattern somedir/%/system/android.jar",
-		"--android-jar-pattern someotherdir/%/system/android.jar",
-		"--android-jar-pattern somedir/%/public/android.jar",
-		"--android-jar-pattern someotherdir/%/public/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/system-server/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/system-server/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/module-lib/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/module-lib/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/system/android.jar",
+		"--android-jar-pattern somedir/{version:major.minor?}/public/android.jar",
+		"--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar",
 	}, patterns)
 }
 
@@ -206,7 +210,7 @@
 			"bar-doc/a.java": nil,
 		})
 
-	m := ctx.ModuleForTests("bar-stubs", "android_common")
+	m := ctx.ModuleForTests(t, "bar-stubs", "android_common")
 	metalava := m.Rule("metalava")
 	if g, w := metalava.Inputs.Strings(), []string{"bar-doc/a.java"}; !reflect.DeepEqual(w, g) {
 		t.Errorf("Expected inputs %q, got %q", w, g)
@@ -267,7 +271,7 @@
 }
 
 func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) {
-	metalavaRule := ctx.ModuleForTests(moduleName, "android_common").Rule("metalava")
+	metalavaRule := ctx.ModuleForTests(t, moduleName, "android_common").Rule("metalava")
 	var systemJars []string
 	for _, i := range metalavaRule.Implicits {
 		systemJars = append(systemJars, i.Base())
@@ -300,10 +304,10 @@
 			"sdk/extensions/1/public/some-mainline-module-stubs.jar": nil,
 			"sdk/extensions/info.txt":                                nil,
 		})
-	m := ctx.ModuleForTests("baz-stubs", "android_common")
+	m := ctx.ModuleForTests(t, "baz-stubs", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
 	cmdline := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command)
-	android.AssertStringDoesContain(t, "sdk-extensions-root present", cmdline, "--sdk-extensions-root sdk/extensions")
+	android.AssertStringDoesContain(t, "android-jar-pattern present", cmdline, "--android-jar-pattern sdk/extensions/{version:extension}/public/{module}.jar")
 	android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt")
 }
 
@@ -328,7 +332,7 @@
 		},
 	)
 
-	ctx.ModuleForTests("foo.api.contribution", "")
+	ctx.ModuleForTests(t, "foo.api.contribution", "")
 }
 
 func TestGeneratedApiContributionVisibilityTest(t *testing.T) {
@@ -362,7 +366,7 @@
 		},
 	)
 
-	ctx.ModuleForTests("bar", "android_common")
+	ctx.ModuleForTests(t, "bar", "android_common")
 }
 
 func TestAconfigDeclarations(t *testing.T) {
@@ -404,14 +408,14 @@
 	android.AssertBoolEquals(t, "foo expected to depend on bar",
 		CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true)
 
-	m := result.ModuleForTests("foo", "android_common")
+	m := result.ModuleForTests(t, "foo", "android_common")
 	android.AssertStringDoesContain(t, "foo generates revert annotations file",
-		strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt")
+		strings.Join(m.AllOutputs(), ""), "flags-config-exportable.xml")
 
 	// revert-annotations.txt passed to exportable stubs generation metalava command
 	manifest := m.Output("metalava_exportable.sbox.textproto")
 	cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command)
-	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt")
+	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "flags-config-exportable.xml")
 
 	android.AssertStringDoesContain(t, "foo generates exportable stubs jar",
 		strings.Join(m.AllOutputs(), ""), "exportable/foo-stubs.srcjar")
@@ -454,9 +458,9 @@
 	}
 	`)
 
-	m := result.ModuleForTests("foo", "android_common")
+	m := result.ModuleForTests(t, "foo", "android_common")
 
-	rule := m.Output("released-flagged-apis-exportable.txt")
+	rule := m.Output("released-flags-exportable.pb")
 	exposeWritableApisFilter := "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'"
 	android.AssertStringEquals(t, "Filter argument expected to contain READ_WRITE permissions", exposeWritableApisFilter, rule.Args["filter_args"])
 }
diff --git a/java/fuzz.go b/java/fuzz.go
index 90f09a8..0e239f0 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -63,7 +63,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Top_level_test_target = true
 
@@ -107,23 +107,7 @@
 }
 
 func (j *JavaFuzzTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
-		j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
-	}
-	if j.fuzzPackagedModule.FuzzProperties.Device_common_corpus != nil {
-		j.fuzzPackagedModule.Corpus = append(j.fuzzPackagedModule.Corpus, android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Device_common_corpus)...)
-	}
-	if j.fuzzPackagedModule.FuzzProperties.Data != nil {
-		j.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Data)
-	}
-	if j.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
-		j.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *j.fuzzPackagedModule.FuzzProperties.Dictionary)
-	}
-	if j.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
-		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
-		android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
-		j.fuzzPackagedModule.Config = configPath
-	}
+	j.fuzzPackagedModule = cc.PackageFuzzModule(ctx, j.fuzzPackagedModule)
 
 	_, sharedDeps := cc.CollectAllSharedDependencies(ctx)
 	for _, dep := range sharedDeps {
@@ -148,6 +132,8 @@
 	}
 
 	j.Test.GenerateAndroidBuildActions(ctx)
+
+	fuzz.SetFuzzPackagedModuleInfo(ctx, &j.fuzzPackagedModule)
 }
 
 type javaFuzzPackager struct {
@@ -169,6 +155,10 @@
 		if !ok {
 			return
 		}
+		fuzzInfo, ok := android.OtherModuleProvider(ctx, module, fuzz.FuzzPackagedModuleInfoProvider)
+		if !ok {
+			return
+		}
 
 		hostOrTargetString := "target"
 		if javaFuzzModule.Target().HostCross {
@@ -195,7 +185,7 @@
 		builder := android.NewRuleBuilder(pctx, ctx)
 
 		// Package the artifacts (data, corpus, config and dictionary) into a zipfile.
-		files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder)
+		files = s.PackageArtifacts(ctx, module, &fuzzInfo, archDir, builder)
 
 		// Add .jar
 		if !javaFuzzModule.Host() {
@@ -209,7 +199,7 @@
 			files = append(files, fuzz.FileToZip{SourceFilePath: fPath})
 		}
 
-		archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaFuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
+		archDirs[archOs], ok = s.BuildZipFile(ctx, module, &fuzzInfo, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
 			return
 		}
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
index f29c913..735b8da 100644
--- a/java/fuzz_test.go
+++ b/java/fuzz_test.go
@@ -30,6 +30,7 @@
 )
 
 func TestJavaFuzz(t *testing.T) {
+	t.Parallel()
 	result := prepForJavaFuzzTest.RunTestWithBp(t, `
 		java_fuzz {
 			name: "foo",
@@ -63,16 +64,16 @@
 
 	osCommonTarget := result.Config.BuildOSCommonTarget.String()
 
-	javac := result.ModuleForTests("foo", osCommonTarget).Rule("javac")
-	combineJar := result.ModuleForTests("foo", osCommonTarget).Description("for javac")
+	javac := result.ModuleForTests(t, "foo", osCommonTarget).Rule("javac")
+	combineJar := result.ModuleForTests(t, "foo", osCommonTarget).Description("for javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
 		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
 	}
 
-	baz := result.ModuleForTests("baz", osCommonTarget).Rule("javac").Output.String()
-	barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "javac-header", "bar.jar")
-	bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "javac-header", "baz.jar")
+	baz := result.ModuleForTests(t, "baz", osCommonTarget).Rule("javac").Output.String()
+	barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "local-javac-header", "bar.jar")
+	bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "local-javac-header", "baz.jar")
 
 	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barOut)
 	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazOut)
@@ -82,7 +83,7 @@
 	}
 
 	ctx := result.TestContext
-	foo := ctx.ModuleForTests("foo", osCommonTarget).Module().(*JavaFuzzTest)
+	foo := ctx.ModuleForTests(t, "foo", osCommonTarget).Module().(*JavaFuzzTest)
 
 	expected := "lib64/libjni.so"
 	if runtime.GOOS == "darwin" {
diff --git a/java/gen.go b/java/gen.go
index 1b4f4c7..aab8418 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -26,15 +26,14 @@
 )
 
 func init() {
-	pctx.SourcePathVariable("logtagsCmd", "build/make/tools/java-event-log-tags.py")
-	pctx.SourcePathVariable("logtagsLib", "build/make/tools/event_log_tags.py")
+	pctx.HostBinToolVariable("logtagsCmd", "java-event-log-tags")
 }
 
 var (
 	logtags = pctx.AndroidStaticRule("logtags",
 		blueprint.RuleParams{
 			Command:     "$logtagsCmd -o $out $in",
-			CommandDeps: []string{"$logtagsCmd", "$logtagsLib"},
+			CommandDeps: []string{"$logtagsCmd"},
 		})
 )
 
diff --git a/java/generated_java_library_test.go b/java/generated_java_library_test.go
index a5c4be1..e5ee586 100644
--- a/java/generated_java_library_test.go
+++ b/java/generated_java_library_test.go
@@ -52,6 +52,7 @@
 }
 
 func TestGenLib(t *testing.T) {
+	t.Parallel()
 	bp := `
 				test_java_gen_lib {
 					name: "javagenlibtest",
@@ -60,6 +61,6 @@
 			`
 	result := testGenLib(t, android.FixtureExpectsNoErrors, bp)
 
-	javagenlibtest := result.ModuleForTests("javagenlibtest", "android_common").Module().(*GeneratedJavaLibraryModule)
+	javagenlibtest := result.ModuleForTests(t, "javagenlibtest", "android_common").Module().(*GeneratedJavaLibraryModule)
 	android.AssertPathsEndWith(t, "Generated_srcjars", []string{"/blah.srcjar"}, javagenlibtest.Library.properties.Generated_srcjars)
 }
diff --git a/java/genrule_combiner.go b/java/genrule_combiner.go
new file mode 100644
index 0000000..357dc2c
--- /dev/null
+++ b/java/genrule_combiner.go
@@ -0,0 +1,252 @@
+// Copyright 2019 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 java
+
+import (
+	"fmt"
+	"io"
+
+	"android/soong/android"
+	"android/soong/dexpreopt"
+
+	"github.com/google/blueprint/depset"
+	"github.com/google/blueprint/proptools"
+)
+
+type GenruleCombiner struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	genruleCombinerProperties GenruleCombinerProperties
+
+	headerJars                    android.Paths
+	implementationJars            android.Paths
+	implementationAndResourceJars android.Paths
+	resourceJars                  android.Paths
+	aconfigProtoFiles             android.Paths
+
+	srcJarArgs []string
+	srcJarDeps android.Paths
+
+	headerDirs android.Paths
+
+	combinedHeaderJar         android.Path
+	combinedImplementationJar android.Path
+}
+
+type GenruleCombinerProperties struct {
+	// List of modules whose implementation (and resources) jars will be visible to modules
+	// that depend on this module.
+	Static_libs proptools.Configurable[[]string] `android:"arch_variant"`
+
+	// List of modules whose header jars will be visible to modules that depend on this module.
+	Headers proptools.Configurable[[]string] `android:"arch_variant"`
+}
+
+// java_genrule_combiner provides the implementation and resource jars from `static_libs`, with
+// the header jars from `headers`.
+//
+// This is useful when a java_genrule is used to change the implementation of a java library
+// without requiring a change in the header jars.
+func GenruleCombinerFactory() android.Module {
+	module := &GenruleCombiner{}
+
+	module.AddProperties(&module.genruleCombinerProperties)
+	InitJavaModule(module, android.HostAndDeviceSupported)
+	return module
+}
+
+var genruleCombinerHeaderDepTag = dependencyTag{name: "genrule_combiner_header"}
+
+func (j *GenruleCombiner) DepsMutator(ctx android.BottomUpMutatorContext) {
+	ctx.AddVariationDependencies(nil, staticLibTag,
+		j.genruleCombinerProperties.Static_libs.GetOrDefault(ctx, nil)...)
+	ctx.AddVariationDependencies(nil, genruleCombinerHeaderDepTag,
+		j.genruleCombinerProperties.Headers.GetOrDefault(ctx, nil)...)
+}
+
+func (j *GenruleCombiner) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if len(j.genruleCombinerProperties.Static_libs.GetOrDefault(ctx, nil)) < 1 {
+		ctx.PropertyErrorf("static_libs", "at least one dependency is required")
+	}
+
+	if len(j.genruleCombinerProperties.Headers.GetOrDefault(ctx, nil)) < 1 {
+		ctx.PropertyErrorf("headers", "at least one dependency is required")
+	}
+
+	var transitiveHeaderJars []depset.DepSet[android.Path]
+	var transitiveImplementationJars []depset.DepSet[android.Path]
+	var transitiveResourceJars []depset.DepSet[android.Path]
+	var sdkVersion android.SdkSpec
+	var stubsLinkType StubsLinkType
+	moduleWithSdkDepInfo := &ModuleWithSdkDepInfo{}
+
+	// Collect the headers first, so that aconfig flag values for the libraries will override
+	// values from the headers (if they are different).
+	ctx.VisitDirectDepsWithTag(genruleCombinerHeaderDepTag, func(m android.Module) {
+		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
+			j.headerJars = append(j.headerJars, dep.HeaderJars...)
+
+			j.srcJarArgs = append(j.srcJarArgs, dep.SrcJarArgs...)
+			j.srcJarDeps = append(j.srcJarDeps, dep.SrcJarDeps...)
+			j.aconfigProtoFiles = append(j.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...)
+			sdkVersion = dep.SdkVersion
+			stubsLinkType = dep.StubsLinkType
+			*moduleWithSdkDepInfo = *dep.ModuleWithSdkDepInfo
+
+			transitiveHeaderJars = append(transitiveHeaderJars, dep.TransitiveStaticLibsHeaderJars)
+		} else if dep, ok := android.OtherModuleProvider(ctx, m, android.CodegenInfoProvider); ok {
+			j.aconfigProtoFiles = append(j.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...)
+		} else {
+			ctx.PropertyErrorf("headers", "module %q cannot be used as a dependency", ctx.OtherModuleName(m))
+		}
+	})
+	ctx.VisitDirectDepsWithTag(staticLibTag, func(m android.Module) {
+		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
+			j.implementationJars = append(j.implementationJars, dep.ImplementationJars...)
+			j.implementationAndResourceJars = append(j.implementationAndResourceJars, dep.ImplementationAndResourcesJars...)
+			j.resourceJars = append(j.resourceJars, dep.ResourceJars...)
+
+			transitiveImplementationJars = append(transitiveImplementationJars, dep.TransitiveStaticLibsImplementationJars)
+			transitiveResourceJars = append(transitiveResourceJars, dep.TransitiveStaticLibsResourceJars)
+			j.aconfigProtoFiles = append(j.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...)
+		} else if dep, ok := android.OtherModuleProvider(ctx, m, android.OutputFilesProvider); ok {
+			// This is provided by `java_genrule` modules.
+			j.implementationJars = append(j.implementationJars, dep.DefaultOutputFiles...)
+			j.implementationAndResourceJars = append(j.implementationAndResourceJars, dep.DefaultOutputFiles...)
+			stubsLinkType = Implementation
+		} else {
+			ctx.PropertyErrorf("static_libs", "module %q cannot be used as a dependency", ctx.OtherModuleName(m))
+		}
+	})
+
+	jarName := ctx.ModuleName() + ".jar"
+
+	if len(j.implementationAndResourceJars) > 1 {
+		outputFile := android.PathForModuleOut(ctx, "combined", jarName)
+		TransformJarsToJar(ctx, outputFile, "combine", j.implementationAndResourceJars,
+			android.OptionalPath{}, false, nil, nil)
+		j.combinedImplementationJar = outputFile
+	} else if len(j.implementationAndResourceJars) == 1 {
+		j.combinedImplementationJar = j.implementationAndResourceJars[0]
+	}
+
+	if len(j.headerJars) > 1 {
+		outputFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
+		TransformJarsToJar(ctx, outputFile, "turbine combine", j.headerJars,
+			android.OptionalPath{}, false, nil, []string{"META-INF/TRANSITIVE"})
+		j.combinedHeaderJar = outputFile
+		j.headerDirs = append(j.headerDirs, android.PathForModuleOut(ctx, "turbine-combined"))
+	} else if len(j.headerJars) == 1 {
+		j.combinedHeaderJar = j.headerJars[0]
+	}
+
+	javaInfo := &JavaInfo{
+		HeaderJars:                             android.Paths{j.combinedHeaderJar},
+		LocalHeaderJars:                        android.Paths{j.combinedHeaderJar},
+		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, android.Paths{j.combinedHeaderJar}, transitiveHeaderJars),
+		TransitiveStaticLibsImplementationJars: depset.New(depset.PREORDER, android.Paths{j.combinedImplementationJar}, transitiveImplementationJars),
+		TransitiveStaticLibsResourceJars:       depset.New(depset.PREORDER, nil, transitiveResourceJars),
+		GeneratedSrcjars:                       android.Paths{j.combinedImplementationJar},
+		ImplementationAndResourcesJars:         android.Paths{j.combinedImplementationJar},
+		ImplementationJars:                     android.Paths{j.combinedImplementationJar},
+		ModuleWithSdkDepInfo:                   moduleWithSdkDepInfo,
+		ResourceJars:                           j.resourceJars,
+		OutputFile:                             j.combinedImplementationJar,
+		SdkVersion:                             sdkVersion,
+		SrcJarArgs:                             j.srcJarArgs,
+		SrcJarDeps:                             j.srcJarDeps,
+		StubsLinkType:                          stubsLinkType,
+		AconfigIntermediateCacheOutputPaths:    j.aconfigProtoFiles,
+	}
+	setExtraJavaInfo(ctx, j, javaInfo)
+	ctx.SetOutputFiles(android.Paths{javaInfo.OutputFile}, "")
+	ctx.SetOutputFiles(android.Paths{javaInfo.OutputFile}, android.DefaultDistTag)
+	ctx.SetOutputFiles(javaInfo.ImplementationAndResourcesJars, ".jar")
+	ctx.SetOutputFiles(javaInfo.HeaderJars, ".hjar")
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
+}
+
+func (j *GenruleCombiner) GeneratedSourceFiles() android.Paths {
+	return append(android.Paths{}, j.combinedImplementationJar)
+}
+
+func (j *GenruleCombiner) GeneratedHeaderDirs() android.Paths {
+	return append(android.Paths{}, j.headerDirs...)
+}
+
+func (j *GenruleCombiner) GeneratedDeps() android.Paths {
+	return append(android.Paths{}, j.combinedImplementationJar)
+}
+
+func (j *GenruleCombiner) Srcs() android.Paths {
+	return append(android.Paths{}, j.implementationAndResourceJars...)
+}
+
+func (j *GenruleCombiner) HeaderJars() android.Paths {
+	return j.headerJars
+}
+
+func (j *GenruleCombiner) ImplementationAndResourcesJars() android.Paths {
+	return j.implementationAndResourceJars
+}
+
+func (j *GenruleCombiner) DexJarBuildPath(ctx android.ModuleErrorfContext) android.Path {
+	return nil
+}
+
+func (j *GenruleCombiner) DexJarInstallPath() android.Path {
+	return nil
+}
+
+func (j *GenruleCombiner) AidlIncludeDirs() android.Paths {
+	return nil
+}
+
+func (j *GenruleCombiner) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
+	return nil
+}
+
+func (j *GenruleCombiner) JacocoReportClassesFile() android.Path {
+	return nil
+}
+
+func (j *GenruleCombiner) AndroidMk() android.AndroidMkData {
+	return android.AndroidMkData{
+		Class:      "JAVA_LIBRARIES",
+		OutputFile: android.OptionalPathForPath(j.combinedImplementationJar),
+		// Make does not support Windows Java modules
+		Disabled: j.Os() == android.Windows,
+		Include:  "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+		Extra: []android.AndroidMkExtraFunc{
+			func(w io.Writer, outputFile android.Path) {
+				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+				fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", j.combinedHeaderJar.String())
+				fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", j.combinedImplementationJar.String())
+			},
+		},
+	}
+}
+
+// implement the following interface for IDE completion.
+var _ android.IDEInfo = (*GenruleCombiner)(nil)
+
+func (j *GenruleCombiner) IDEInfo(ctx android.BaseModuleContext, ideInfo *android.IdeInfo) {
+	ideInfo.Deps = append(ideInfo.Deps, j.genruleCombinerProperties.Static_libs.GetOrDefault(ctx, nil)...)
+	ideInfo.Libs = append(ideInfo.Libs, j.genruleCombinerProperties.Static_libs.GetOrDefault(ctx, nil)...)
+	ideInfo.Deps = append(ideInfo.Deps, j.genruleCombinerProperties.Headers.GetOrDefault(ctx, nil)...)
+	ideInfo.Libs = append(ideInfo.Libs, j.genruleCombinerProperties.Headers.GetOrDefault(ctx, nil)...)
+}
diff --git a/java/genrule_combiner_test.go b/java/genrule_combiner_test.go
new file mode 100644
index 0000000..7d024cf
--- /dev/null
+++ b/java/genrule_combiner_test.go
@@ -0,0 +1,237 @@
+// Copyright 2018 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 java
+
+import (
+	"reflect"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestJarGenruleCombinerSingle(t *testing.T) {
+	t.Parallel()
+	t.Helper()
+	ctx := prepareForJavaTest.RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+
+		java_genrule {
+			name: "gen",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["gen.jar"],
+			srcs: [":foo"],
+		}
+
+		java_genrule_combiner {
+			name: "jarcomb",
+			static_libs: ["gen"],
+			headers: ["foo"],
+		}
+
+		java_library {
+			name: "bar",
+			static_libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+
+		java_library {
+			name: "baz",
+			libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+	`).TestContext
+
+	fooMod := ctx.ModuleForTests(t, "foo", "android_common")
+	fooCombined := fooMod.Output("turbine-combined/foo.jar")
+	fooOutputFiles, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), fooMod.Module(), android.OutputFilesProvider)
+	fooHeaderJars := fooOutputFiles.TaggedOutputFiles[".hjar"]
+
+	genMod := ctx.ModuleForTests(t, "gen", "android_common")
+	gen := genMod.Output("gen.jar")
+
+	jarcombMod := ctx.ModuleForTests(t, "jarcomb", "android_common")
+	jarcombInfo, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), JavaInfoProvider)
+	jarcombOutputFiles, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), android.OutputFilesProvider)
+
+	// Confirm that jarcomb simply forwards the jarcomb implementation and the foo headers.
+	if len(jarcombOutputFiles.DefaultOutputFiles) != 1 ||
+		android.PathRelativeToTop(jarcombOutputFiles.DefaultOutputFiles[0]) != android.PathRelativeToTop(gen.Output) {
+		t.Errorf("jarcomb Implementation %v is not [%q]",
+			android.PathsRelativeToTop(jarcombOutputFiles.DefaultOutputFiles), android.PathRelativeToTop(gen.Output))
+	}
+	jarcombHeaderJars := jarcombOutputFiles.TaggedOutputFiles[".hjar"]
+	if !reflect.DeepEqual(jarcombHeaderJars, fooHeaderJars) {
+		t.Errorf("jarcomb Header jar %v is not %q",
+			jarcombHeaderJars, fooHeaderJars)
+	}
+
+	// Confirm that JavaInfoProvider agrees.
+	if len(jarcombInfo.ImplementationJars) != 1 ||
+		android.PathRelativeToTop(jarcombInfo.ImplementationJars[0]) != android.PathRelativeToTop(gen.Output) {
+		t.Errorf("jarcomb ImplementationJars %v is not [%q]",
+			android.PathsRelativeToTop(jarcombInfo.ImplementationJars), android.PathRelativeToTop(gen.Output))
+	}
+	if len(jarcombInfo.HeaderJars) != 1 ||
+		android.PathRelativeToTop(jarcombInfo.HeaderJars[0]) != android.PathRelativeToTop(fooCombined.Output) {
+		t.Errorf("jarcomb HeaderJars %v is not [%q]",
+			android.PathsRelativeToTop(jarcombInfo.HeaderJars), android.PathRelativeToTop(fooCombined.Output))
+	}
+
+	barMod := ctx.ModuleForTests(t, "bar", "android_common")
+	bar := barMod.Output("javac/bar.jar")
+	barCombined := barMod.Output("combined/bar.jar")
+
+	// Confirm that bar uses the Implementation from gen and headerJars from foo.
+	if len(barCombined.Inputs) != 2 ||
+		barCombined.Inputs[0].String() != bar.Output.String() ||
+		barCombined.Inputs[1].String() != gen.Output.String() {
+		t.Errorf("bar combined jar inputs %v is not [%q, %q]",
+			barCombined.Inputs.Strings(), bar.Output.String(), gen.Output.String())
+	}
+
+	bazMod := ctx.ModuleForTests(t, "baz", "android_common")
+	baz := bazMod.Output("javac/baz.jar")
+
+	string_in_list := func(s string, l []string) bool {
+		for _, v := range l {
+			if s == v {
+				return true
+			}
+		}
+		return false
+	}
+
+	// Confirm that baz uses the headerJars from foo.
+	bazImplicitsRel := android.PathsRelativeToTop(baz.Implicits)
+	for _, v := range android.PathsRelativeToTop(fooHeaderJars) {
+		if !string_in_list(v, bazImplicitsRel) {
+			t.Errorf("baz Implicits %v does not contain %q", bazImplicitsRel, v)
+		}
+	}
+}
+
+func TestJarGenruleCombinerMulti(t *testing.T) {
+	t.Parallel()
+	t.Helper()
+	ctx := prepareForJavaTest.RunTestWithBp(t, `
+		java_library {
+			name: "foo1",
+			srcs: ["foo1_a.java"],
+		}
+
+		java_library {
+			name: "foo2",
+			srcs: ["foo2_a.java"],
+		}
+
+		java_genrule {
+			name: "gen1",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["gen1.jar"],
+			srcs: [":foo1"],
+		}
+
+		java_genrule {
+			name: "gen2",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["gen2.jar"],
+			srcs: [":foo2"],
+		}
+
+		// Combine multiple java_genrule modules.
+		java_genrule_combiner {
+			name: "jarcomb",
+			static_libs: ["gen1", "gen2"],
+			headers: ["foo1", "foo2"],
+		}
+
+		java_library {
+			name: "bar",
+			static_libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+
+		java_library {
+			name: "baz",
+			libs: ["jarcomb"],
+			srcs: ["c.java"],
+		}
+	`).TestContext
+
+	gen1Mod := ctx.ModuleForTests(t, "gen1", "android_common")
+	gen1 := gen1Mod.Output("gen1.jar")
+	gen2Mod := ctx.ModuleForTests(t, "gen2", "android_common")
+	gen2 := gen2Mod.Output("gen2.jar")
+
+	jarcombMod := ctx.ModuleForTests(t, "jarcomb", "android_common")
+	jarcomb := jarcombMod.Output("combined/jarcomb.jar")
+	jarcombTurbine := jarcombMod.Output("turbine-combined/jarcomb.jar")
+	_ = jarcombTurbine
+	jarcombInfo, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), JavaInfoProvider)
+	_ = jarcombInfo
+	jarcombOutputFiles, _ := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), jarcombMod.Module(), android.OutputFilesProvider)
+	jarcombHeaderJars := jarcombOutputFiles.TaggedOutputFiles[".hjar"]
+
+	if len(jarcomb.Inputs) != 2 ||
+		jarcomb.Inputs[0].String() != gen1.Output.String() ||
+		jarcomb.Inputs[1].String() != gen2.Output.String() {
+		t.Errorf("jarcomb inputs %v are not [%q, %q]",
+			jarcomb.Inputs.Strings(), gen1.Output.String(), gen2.Output.String())
+	}
+
+	if len(jarcombHeaderJars) != 1 ||
+		android.PathRelativeToTop(jarcombHeaderJars[0]) != android.PathRelativeToTop(jarcombTurbine.Output) {
+		t.Errorf("jarcomb Header jars %v is not [%q]",
+			android.PathsRelativeToTop(jarcombHeaderJars), android.PathRelativeToTop(jarcombTurbine.Output))
+	}
+
+	barMod := ctx.ModuleForTests(t, "bar", "android_common")
+	bar := barMod.Output("javac/bar.jar")
+	barCombined := barMod.Output("combined/bar.jar")
+
+	// Confirm that bar uses the Implementation and Headers from jarcomb.
+	if len(barCombined.Inputs) != 2 ||
+		barCombined.Inputs[0].String() != bar.Output.String() ||
+		barCombined.Inputs[1].String() != jarcomb.Output.String() {
+		t.Errorf("bar combined jar inputs %v is not [%q, %q]",
+			barCombined.Inputs.Strings(), bar.Output.String(), jarcomb.Output.String())
+	}
+
+	bazMod := ctx.ModuleForTests(t, "baz", "android_common")
+	baz := bazMod.Output("javac/baz.jar")
+
+	string_in_list := func(s string, l []string) bool {
+		for _, v := range l {
+			if s == v {
+				return true
+			}
+		}
+		return false
+	}
+
+	// Confirm that baz uses the headerJars from foo.
+	bazImplicitsRel := android.PathsRelativeToTop(baz.Implicits)
+	for _, v := range android.PathsRelativeToTop(jarcombHeaderJars) {
+		if !string_in_list(v, bazImplicitsRel) {
+			t.Errorf("baz Implicits %v does not contain %q", bazImplicitsRel, v)
+		}
+	}
+}
diff --git a/java/genrule_test.go b/java/genrule_test.go
index 1c294b2..c112e45 100644
--- a/java/genrule_test.go
+++ b/java/genrule_test.go
@@ -31,6 +31,7 @@
 }
 
 func TestGenruleCmd(t *testing.T) {
+	t.Parallel()
 	fs := map[string][]byte{
 		"tool": nil,
 		"foo":  nil,
@@ -56,7 +57,7 @@
 		t.Fatal(errs)
 	}
 
-	gen := ctx.ModuleForTests("gen", "android_common").Output("out")
+	gen := ctx.ModuleForTests(t, "gen", "android_common").Output("out")
 	expected := []string{"foo"}
 	if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
 		t.Errorf(`want arm inputs %v, got %v`, expected, gen.Implicits.Strings())
@@ -64,6 +65,7 @@
 }
 
 func TestJarGenrules(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -91,11 +93,11 @@
 		}
 	`)
 
-	foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar")
-	jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar")
-	bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar")
-	baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
-	barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
+	foo := ctx.ModuleForTests(t, "foo", "android_common").Output("javac/foo.jar")
+	jargen := ctx.ModuleForTests(t, "jargen", "android_common").Output("jargen.jar")
+	bar := ctx.ModuleForTests(t, "bar", "android_common").Output("javac/bar.jar")
+	baz := ctx.ModuleForTests(t, "baz", "android_common").Output("javac/baz.jar")
+	barCombined := ctx.ModuleForTests(t, "bar", "android_common").Output("combined/bar.jar")
 
 	if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) {
 		t.Errorf("expected jargen inputs [%q], got %q", w, g)
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index b1a9deb..c9a1f2b 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -97,7 +97,7 @@
 	// Save the classes jars even if this is not active as they may be used by modular hidden API
 	// processing.
 	classesJars := android.Paths{classesJar}
-	ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(hiddenApiAnnotationsTag, func(dep android.ModuleProxy) {
 		if javaInfo, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
 			classesJars = append(classesJars, javaInfo.ImplementationJars...)
 		}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 7d21b7a..2c86942 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -171,11 +171,11 @@
 	// Now match the apex part of the boot image configuration.
 	requiredApex := configuredBootJars.Apex(index)
 	if android.IsConfiguredJarForPlatform(requiredApex) {
-		if len(apexInfo.InApexVariants) != 0 {
+		if apexInfo.ApexVariationName != "" {
 			// A platform variant is required but this is for an apex so ignore it.
 			return false
 		}
-	} else if !apexInfo.InApexVariant(requiredApex) {
+	} else if apexInfo.BaseApexName != requiredApex {
 		// An apex variant for a specific apex is required but this is the wrong apex.
 		return false
 	}
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index afe8b4c..147f326 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -44,6 +44,7 @@
 )
 
 func TestHiddenAPISingleton(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
@@ -56,13 +57,14 @@
 		}
 	`)
 
-	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenAPI := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
 func TestHiddenAPISingletonWithSourceAndPrebuiltPreferredButNoDex(t *testing.T) {
+	t.Parallel()
 	expectedErrorMessage := "module prebuilt_foo{os:android,arch:common} does not provide a dex jar"
 
 	android.GroupFixturePreparers(
@@ -86,6 +88,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
@@ -98,13 +101,14 @@
 	}
 	`)
 
-	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenAPI := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
 
 func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
@@ -124,7 +128,7 @@
 		}
 	`)
 
-	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenAPI := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
@@ -134,6 +138,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		FixtureConfigureBootJars("platform:foo"),
@@ -153,7 +158,7 @@
 		}
 	`)
 
-	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+	hiddenAPI := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 	prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
@@ -163,6 +168,7 @@
 }
 
 func TestHiddenAPISingletonSdks(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name             string
 		unbundledBuild   bool
@@ -213,7 +219,7 @@
 		}
 		`)
 
-			hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
+			hiddenAPI := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
 			hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
 			wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
 			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantPublicStubs)
@@ -246,6 +252,7 @@
 }
 
 func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) {
+	t.Parallel()
 
 	// The idea behind this test is to ensure that when the build is
 	// confugured with a PrebuiltHiddenApiDir that the rules for the
@@ -272,9 +279,9 @@
 	expectedCpOutput := "out/soong/hiddenapi/hiddenapi-flags.csv"
 	expectedFlagsCsv := "out/soong/hiddenapi/hiddenapi-flags.csv"
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 
-	hiddenAPI := result.SingletonForTests("hiddenapi")
+	hiddenAPI := result.SingletonForTests(t, "hiddenapi")
 	cpRule := hiddenAPI.Rule("Cp")
 	actualCpInput := cpRule.BuildParams.Input
 	actualCpOutput := cpRule.BuildParams.Output
@@ -289,6 +296,7 @@
 }
 
 func TestHiddenAPIEncoding_JavaSdkLibrary(t *testing.T) {
+	t.Parallel()
 
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
@@ -310,7 +318,7 @@
 	`)
 
 	checkDexEncoded := func(t *testing.T, name, unencodedDexJar, encodedDexJar string) {
-		moduleForTests := result.ModuleForTests(name+".impl", "android_common")
+		moduleForTests := result.ModuleForTests(t, name+".impl", "android_common")
 
 		encodeDexRule := moduleForTests.Rule("hiddenAPIEncodeDex")
 		actualUnencodedDexJar := encodeDexRule.Input
@@ -324,10 +332,7 @@
 		android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar)
 	}
 
-	// The java_library embedded with the java_sdk_library must be dex encoded.
-	t.Run("foo", func(t *testing.T) {
-		expectedUnencodedDexJar := "out/soong/.intermediates/foo.impl/android_common/aligned/foo.jar"
-		expectedEncodedDexJar := "out/soong/.intermediates/foo.impl/android_common/hiddenapi/foo.jar"
-		checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar)
-	})
+	expectedUnencodedDexJar := "out/soong/.intermediates/foo.impl/android_common/aligned/foo.jar"
+	expectedEncodedDexJar := "out/soong/.intermediates/foo.impl/android_common/hiddenapi/foo.jar"
+	checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar)
 }
diff --git a/java/jacoco_test.go b/java/jacoco_test.go
index 1882908..58a091e 100644
--- a/java/jacoco_test.go
+++ b/java/jacoco_test.go
@@ -17,6 +17,7 @@
 import "testing"
 
 func TestJacocoFilterToSpecs(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name, in, out string
 	}{
@@ -54,6 +55,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			got, err := jacocoFilterToSpec(testCase.in)
 			if err != nil {
 				t.Error(err)
@@ -66,6 +68,7 @@
 }
 
 func TestJacocoFiltersToZipCommand(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name               string
 		includes, excludes []string
@@ -96,6 +99,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			got := jacocoFiltersToZipCommand(testCase.includes, testCase.excludes)
 			if got != testCase.out {
 				t.Errorf("expected %q got %q", testCase.out, got)
diff --git a/java/jarjar_test.go b/java/jarjar_test.go
index 82bfa2b..b689761 100644
--- a/java/jarjar_test.go
+++ b/java/jarjar_test.go
@@ -22,7 +22,7 @@
 )
 
 func AssertJarJarRename(t *testing.T, result *android.TestResult, libName, original, expectedRename string) {
-	module := result.ModuleForTests(libName, "android_common")
+	module := result.ModuleForTests(t, libName, "android_common")
 
 	provider, found := android.OtherModuleProvider(result.OtherModuleProviderAdaptor(), module.Module(), JarJarProvider)
 	android.AssertBoolEquals(t, fmt.Sprintf("found provider (%s)", libName), true, found)
diff --git a/java/java.go b/java/java.go
index e1cfea5..2e14b82 100644
--- a/java/java.go
+++ b/java/java.go
@@ -64,6 +64,7 @@
 	ctx.RegisterModuleType("java_api_library", ApiLibraryFactory)
 	ctx.RegisterModuleType("java_api_contribution", ApiContributionFactory)
 	ctx.RegisterModuleType("java_api_contribution_import", ApiContributionImportFactory)
+	ctx.RegisterModuleType("java_genrule_combiner", GenruleCombinerFactory)
 
 	// This mutator registers dependencies on dex2oat for modules that should be
 	// dexpreopted. This is done late when the final variants have been
@@ -250,6 +251,36 @@
 
 var ProguardSpecInfoProvider = blueprint.NewProvider[ProguardSpecInfo]()
 
+type AndroidLibraryDependencyInfo struct {
+	ExportPackage       android.Path
+	ResourcesNodeDepSet depset.DepSet[*resourcesNode]
+	RRODirsDepSet       depset.DepSet[rroDir]
+	ManifestsDepSet     depset.DepSet[android.Path]
+}
+
+type UsesLibraryDependencyInfo struct {
+	DexJarInstallPath   android.Path
+	ClassLoaderContexts dexpreopt.ClassLoaderContextMap
+}
+
+type SdkLibraryComponentDependencyInfo struct {
+	// The name of the implementation library for the optional SDK library or nil, if there isn't one.
+	OptionalSdkLibraryImplementation *string
+}
+
+type ProvidesUsesLibInfo struct {
+	ProvidesUsesLib *string
+}
+
+type ModuleWithUsesLibraryInfo struct {
+	UsesLibrary *usesLibrary
+}
+
+type ModuleWithSdkDepInfo struct {
+	SdkLinkType sdkLinkType
+	Stubs       bool
+}
+
 // JavaInfo contains information about a java module for use by modules that depend on it.
 type JavaInfo struct {
 	// HeaderJars is a list of jars that can be passed as the javac classpath in order to link
@@ -328,10 +359,109 @@
 	AconfigIntermediateCacheOutputPaths android.Paths
 
 	SdkVersion android.SdkSpec
+
+	// output file of the module, which may be a classes jar or a dex jar
+	OutputFile android.Path
+
+	ExtraOutputFiles android.Paths
+
+	AndroidLibraryDependencyInfo *AndroidLibraryDependencyInfo
+
+	UsesLibraryDependencyInfo *UsesLibraryDependencyInfo
+
+	SdkLibraryComponentDependencyInfo *SdkLibraryComponentDependencyInfo
+
+	ProvidesUsesLibInfo *ProvidesUsesLibInfo
+
+	MissingOptionalUsesLibs []string
+
+	ModuleWithSdkDepInfo *ModuleWithSdkDepInfo
+
+	// output file containing classes.dex and resources
+	DexJarFile OptionalDexJarPath
+
+	// installed file for binary dependency
+	InstallFile android.Path
+
+	// The path to the dex jar that is in the boot class path. If this is unset then the associated
+	// module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional
+	// annotations for the <x> boot dex jar but which do not actually provide a boot dex jar
+	// themselves.
+	//
+	// This must be the path to the unencoded dex jar as the encoded dex jar indirectly depends on
+	// this file so using the encoded dex jar here would result in a cycle in the ninja rules.
+	BootDexJarPath OptionalDexJarPath
+
+	// The compressed state of the dex file being encoded. This is used to ensure that the encoded
+	// dex file has the same state.
+	UncompressDexState *bool
+
+	// True if the module containing this structure contributes to the hiddenapi information or has
+	// that information encoded within it.
+	Active bool
+
+	BuiltInstalled string
+
+	// The config is used for two purposes:
+	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
+	//   a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py).
+	//   Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself.
+	// - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally
+	//   dexpreopt another partition).
+	ConfigPath android.WritablePath
+
+	LogtagsSrcs android.Paths
+
+	ProguardDictionary android.OptionalPath
+
+	ProguardUsageZip android.OptionalPath
+
+	LinterReports android.Paths
+
+	// installed file for hostdex copy
+	HostdexInstallFile android.InstallPath
+
+	// Additional srcJars tacked in by GeneratedJavaLibraryModule
+	GeneratedSrcjars []android.Path
+
+	// True if profile-guided optimization is actually enabled.
+	ProfileGuided bool
+
+	Stem string
+
+	DexJarBuildPath OptionalDexJarPath
+
+	DexpreopterInfo *DexpreopterInfo
+
+	XrefJavaFiles   android.Paths
+	XrefKotlinFiles android.Paths
 }
 
 var JavaInfoProvider = blueprint.NewProvider[*JavaInfo]()
 
+type DexpreopterInfo struct {
+	// The path to the profile on host that dexpreopter generates. This is used as the input for
+	// dex2oat.
+	OutputProfilePathOnHost android.Path
+	// If the java module is to be installed into an APEX, this list contains information about the
+	// dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed
+	// outside of the APEX.
+	ApexSystemServerDexpreoptInstalls []DexpreopterInstall
+
+	// ApexSystemServerDexJars returns the list of dex jars if this is an apex system server jar.
+	ApexSystemServerDexJars android.Paths
+}
+
+type JavaLibraryInfo struct {
+	Prebuilt bool
+}
+
+var JavaLibraryInfoProvider = blueprint.NewProvider[JavaLibraryInfo]()
+
+type JavaDexImportInfo struct{}
+
+var JavaDexImportInfoProvider = blueprint.NewProvider[JavaDexImportInfo]()
+
 // SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to
 // the sysprop implementation library.
 type SyspropPublicStubInfo struct {
@@ -449,6 +579,7 @@
 	extraLintCheckTag       = dependencyTag{name: "extra-lint-check", toolchain: true}
 	jniLibTag               = dependencyTag{name: "jnilib", runtimeLinked: true}
 	r8LibraryJarTag         = dependencyTag{name: "r8-libraryjar", runtimeLinked: true}
+	traceReferencesTag      = dependencyTag{name: "trace-references"}
 	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
 	javaApiContributionTag  = dependencyTag{name: "java-api-contribution"}
 	aconfigDeclarationTag   = dependencyTag{name: "aconfig-declaration"}
@@ -479,11 +610,12 @@
 		kotlinPluginTag,
 		syspropPublicStubDepTag,
 		instrumentationForTag,
+		traceReferencesTag,
 	}
 )
 
 func IsLibDepTag(depTag blueprint.DependencyTag) bool {
-	return depTag == libTag || depTag == sdkLibTag
+	return depTag == libTag
 }
 
 func IsStaticLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -540,12 +672,12 @@
 		ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
 		ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
 		ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...)
-		if d.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
+		if d.effectiveOptimizeEnabled(ctx) && sdkDep.hasStandardLibs() {
 			ctx.AddVariationDependencies(nil, proguardRaiseTag,
 				config.LegacyCorePlatformBootclasspathLibraries...,
 			)
 		}
-		if d.effectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() {
+		if d.effectiveOptimizeEnabled(ctx) && sdkDep.hasFrameworkLibs() {
 			ctx.AddVariationDependencies(nil, proguardRaiseTag, config.FrameworkLibraries...)
 		}
 	}
@@ -599,11 +731,11 @@
 	transitiveStaticLibsResourceJars       []depset.DepSet[android.Path]
 }
 
-func checkProducesJars(ctx android.ModuleContext, dep android.SourceFileProducer) {
-	for _, f := range dep.Srcs() {
+func checkProducesJars(ctx android.ModuleContext, dep android.SourceFilesInfo, module android.ModuleProxy) {
+	for _, f := range dep.Srcs {
 		if f.Ext() != ".jar" {
 			ctx.ModuleErrorf("genrule %q must generate files ending with .jar to be used as a libs or static_libs dependency",
-				ctx.OtherModuleName(dep.(blueprint.Module)))
+				ctx.OtherModuleName(module))
 		}
 	}
 }
@@ -721,6 +853,8 @@
 	combinedExportedProguardFlagsFile android.Path
 
 	InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.InstallPaths)
+
+	apiXmlFile android.WritablePath
 }
 
 var _ android.ApexModule = (*Library)(nil)
@@ -1008,7 +1142,7 @@
 			j.dexpreopter.disableDexpreopt()
 		}
 	}
-	j.compile(ctx, nil, nil, nil, nil)
+	javaInfo := j.compile(ctx, nil, nil, nil, nil)
 
 	j.setInstallRules(ctx)
 
@@ -1017,7 +1151,97 @@
 		TopLevelTarget: j.sourceProperties.Top_level_test_target,
 	})
 
+	android.SetProvider(ctx, JavaLibraryInfoProvider, JavaLibraryInfo{
+		Prebuilt: false,
+	})
+
+	if javaInfo != nil {
+		setExtraJavaInfo(ctx, j, javaInfo)
+		javaInfo.ExtraOutputFiles = j.extraOutputFiles
+		javaInfo.DexJarFile = j.dexJarFile
+		javaInfo.InstallFile = j.installFile
+		javaInfo.BootDexJarPath = j.bootDexJarPath
+		javaInfo.UncompressDexState = j.uncompressDexState
+		javaInfo.Active = j.active
+		javaInfo.BuiltInstalled = j.builtInstalled
+		javaInfo.ConfigPath = j.configPath
+		javaInfo.LogtagsSrcs = j.logtagsSrcs
+		javaInfo.ProguardDictionary = j.proguardDictionary
+		javaInfo.ProguardUsageZip = j.proguardUsageZip
+		javaInfo.LinterReports = j.reports
+		javaInfo.HostdexInstallFile = j.hostdexInstallFile
+		javaInfo.GeneratedSrcjars = j.properties.Generated_srcjars
+		javaInfo.ProfileGuided = j.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided
+
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
 	setOutputFiles(ctx, j.Module)
+
+	j.javaLibraryModuleInfoJSON(ctx)
+
+	buildComplianceMetadata(ctx)
+
+	j.createApiXmlFile(ctx)
+
+	if j.dexer.proguardDictionary.Valid() {
+		android.SetProvider(ctx, ProguardProvider, ProguardInfo{
+			ModuleName:         ctx.ModuleName(),
+			Class:              "JAVA_LIBRARIES",
+			ProguardDictionary: j.dexer.proguardDictionary.Path(),
+			ProguardUsageZip:   j.dexer.proguardUsageZip.Path(),
+			ClassesJar:         j.implementationAndResourcesJar,
+		})
+	}
+}
+
+func (j *Library) javaLibraryModuleInfoJSON(ctx android.ModuleContext) *android.ModuleInfoJSON {
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Class = []string{"JAVA_LIBRARIES"}
+	if j.implementationAndResourcesJar != nil {
+		moduleInfoJSON.ClassesJar = []string{j.implementationAndResourcesJar.String()}
+	}
+	moduleInfoJSON.SystemSharedLibs = []string{"none"}
+
+	if j.hostDexNeeded() {
+		hostDexModuleInfoJSON := ctx.ExtraModuleInfoJSON()
+		hostDexModuleInfoJSON.SubName = "-hostdex"
+		hostDexModuleInfoJSON.Class = []string{"JAVA_LIBRARIES"}
+		if j.implementationAndResourcesJar != nil {
+			hostDexModuleInfoJSON.ClassesJar = []string{j.implementationAndResourcesJar.String()}
+		}
+		hostDexModuleInfoJSON.SystemSharedLibs = []string{"none"}
+		hostDexModuleInfoJSON.SupportedVariantsOverride = []string{"HOST"}
+	}
+
+	if j.hideApexVariantFromMake {
+		moduleInfoJSON.Disabled = true
+	}
+	return moduleInfoJSON
+}
+
+func buildComplianceMetadata(ctx android.ModuleContext) {
+	// Dump metadata that can not be done in android/compliance-metadata.go
+	complianceMetadataInfo := ctx.ComplianceMetadataInfo()
+	builtFiles := ctx.GetOutputFiles().DefaultOutputFiles.Strings()
+	for _, paths := range ctx.GetOutputFiles().TaggedOutputFiles {
+		builtFiles = append(builtFiles, paths.Strings()...)
+	}
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.BUILT_FILES, android.SortedUniqueStrings(builtFiles))
+
+	// Static deps
+	staticDepNames := make([]string, 0)
+	staticDepFiles := android.Paths{}
+	ctx.VisitDirectDepsWithTag(staticLibTag, func(module android.Module) {
+		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			staticDepNames = append(staticDepNames, module.Name())
+			staticDepFiles = append(staticDepFiles, dep.ImplementationJars...)
+			staticDepFiles = append(staticDepFiles, dep.HeaderJars...)
+			staticDepFiles = append(staticDepFiles, dep.ResourceJars...)
+		}
+	})
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEPS, android.SortedUniqueStrings(staticDepNames))
+	complianceMetadataInfo.SetListValue(android.ComplianceMetadataProp.STATIC_DEP_FILES, android.SortedUniqueStrings(staticDepFiles.Strings()))
 }
 
 func (j *Library) getJarInstallDir(ctx android.ModuleContext) android.InstallPath {
@@ -1073,6 +1297,28 @@
 	}
 }
 
+var apiXMLGeneratingApiSurfaces = []android.SdkKind{
+	android.SdkPublic,
+	android.SdkSystem,
+	android.SdkModule,
+	android.SdkSystemServer,
+	android.SdkTest,
+}
+
+func (j *Library) createApiXmlFile(ctx android.ModuleContext) {
+	if kind, ok := android.JavaLibraryNameToSdkKind(ctx.ModuleName()); ok && android.InList(kind, apiXMLGeneratingApiSurfaces) {
+		scopePrefix := AllApiScopes.matchingScopeFromSdkKind(kind).apiFilePrefix
+		j.apiXmlFile = android.PathForModuleOut(ctx, fmt.Sprintf("%sapi.xml", scopePrefix))
+		ctx.Build(pctx, android.BuildParams{
+			Rule: generateApiXMLRule,
+			// LOCAL_SOONG_CLASSES_JAR
+			Input:  j.implementationAndResourcesJar,
+			Output: j.apiXmlFile,
+		})
+		ctx.DistForGoal("dist_files", j.apiXmlFile)
+	}
+}
+
 const (
 	aidlIncludeDir   = "aidl"
 	javaDir          = "java"
@@ -1314,6 +1560,11 @@
 	// host test.
 	Device_first_prefer32_data []string `android:"path_device_first_prefer32"`
 
+	// Same as data, but will add dependencies on modules using the host's os variation and
+	// the common arch variation. Useful for a device test that wants to depend on a host
+	// module, for example to include a custom Tradefed test runner.
+	Host_common_data []string `android:"path_host_common"`
+
 	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
 	// explicitly.
@@ -1576,6 +1827,11 @@
 		MkInclude:            "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		MkAppClass:           "JAVA_LIBRARIES",
 	})
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	if proptools.Bool(j.testProperties.Test_options.Unit_test) {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests")
+	}
 }
 
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1607,20 +1863,21 @@
 	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_common_data)...)
 	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_first_data)...)
 	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Device_first_prefer32_data)...)
+	j.data = append(j.data, android.PathsForModuleSrc(ctx, j.testProperties.Host_common_data)...)
 
 	j.extraTestConfigs = android.PathsForModuleSrc(ctx, j.testProperties.Test_options.Extra_test_configs)
 
-	ctx.VisitDirectDepsWithTag(dataNativeBinsTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(dataNativeBinsTag, func(dep android.ModuleProxy) {
 		j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
 	})
 
-	ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(dataDeviceBinsTag, func(dep android.ModuleProxy) {
 		j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
 	})
 
 	var directImplementationDeps android.Paths
 	var transitiveImplementationDeps []depset.DepSet[android.Path]
-	ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(jniLibTag, func(dep android.ModuleProxy) {
 		sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider)
 		if sharedLibInfo.SharedLibrary != nil {
 			// Copy to an intermediate output directory to append "lib[64]" to the path,
@@ -1653,10 +1910,82 @@
 	})
 
 	j.Library.GenerateAndroidBuildActions(ctx)
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	// LOCAL_MODULE_TAGS
+	moduleInfoJSON.Tags = append(moduleInfoJSON.Tags, "tests")
+	var allTestConfigs android.Paths
+	if j.testConfig != nil {
+		allTestConfigs = append(allTestConfigs, j.testConfig)
+	}
+	allTestConfigs = append(allTestConfigs, j.extraTestConfigs...)
+	if len(allTestConfigs) > 0 {
+		moduleInfoJSON.TestConfig = allTestConfigs.Strings()
+	} else {
+		optionalConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml")
+		if optionalConfig.Valid() {
+			moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, optionalConfig.String())
+		}
+	}
+	if len(j.testProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, j.testProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+	if _, ok := j.testConfig.(android.WritablePath); ok {
+		moduleInfoJSON.AutoTestConfig = []string{"true"}
+	}
+	if proptools.Bool(j.testProperties.Test_options.Unit_test) {
+		moduleInfoJSON.IsUnitTest = "true"
+		if ctx.Host() {
+			moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests")
+		}
+	}
+	moduleInfoJSON.TestMainlineModules = append(moduleInfoJSON.TestMainlineModules, j.testProperties.Test_mainline_modules...)
+
+	// Install test deps
+	if !ctx.Config().KatiEnabled() {
+		pathInTestCases := android.PathForModuleInstall(ctx, "testcases", ctx.ModuleName())
+		if j.testConfig != nil {
+			ctx.InstallFile(pathInTestCases, ctx.ModuleName()+".config", j.testConfig)
+		}
+		dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml")
+		if dynamicConfig.Valid() {
+			ctx.InstallFile(pathInTestCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path())
+		}
+		testDeps := append(j.data, j.extraTestConfigs...)
+		for _, data := range android.SortedUniquePaths(testDeps) {
+			dataPath := android.DataPath{SrcPath: data}
+			ctx.InstallTestData(pathInTestCases, []android.DataPath{dataPath})
+		}
+		if j.outputFile != nil {
+			ctx.InstallFile(pathInTestCases, ctx.ModuleName()+".jar", j.outputFile)
+		}
+	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: j.testProperties.Test_suites,
+	})
 }
 
 func (j *TestHelperLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	j.Library.GenerateAndroidBuildActions(ctx)
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	moduleInfoJSON.Tags = append(moduleInfoJSON.Tags, "tests")
+	if len(j.testHelperLibraryProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, j.testHelperLibraryProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+	optionalConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml")
+	if optionalConfig.Valid() {
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, optionalConfig.String())
+	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: j.testHelperLibraryProperties.Test_suites,
+	})
 }
 
 func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -1746,7 +2075,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Top_level_test_target = true
 
@@ -1763,7 +2092,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
 
 	InitJavaModule(module, android.HostAndDeviceSupported)
@@ -1910,13 +2239,13 @@
 	// Set the jniLibs of this binary.
 	// These will be added to `LOCAL_REQUIRED_MODULES`, and the kati packaging system will
 	// install these alongside the java binary.
-	ctx.VisitDirectDepsWithTag(jniInstallTag, func(jni android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(jniInstallTag, func(jni android.ModuleProxy) {
 		// Use the BaseModuleName of the dependency (without any prebuilt_ prefix)
-		bmn, _ := jni.(interface{ BaseModuleName() string })
-		j.androidMkNamesOfJniLibs = append(j.androidMkNamesOfJniLibs, bmn.BaseModuleName()+":"+jni.Target().Arch.ArchType.Bitness())
+		commonInfo := android.OtherModulePointerProviderOrDefault(ctx, jni, android.CommonModuleInfoProvider)
+		j.androidMkNamesOfJniLibs = append(j.androidMkNamesOfJniLibs, commonInfo.BaseModuleName+":"+commonInfo.Target.Arch.ArchType.Bitness())
 	})
 	// Check that native libraries are not listed in `required`. Prompt users to use `jni_libs` instead.
-	ctx.VisitDirectDepsWithTag(android.RequiredDepTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(android.RequiredDepTag, func(dep android.ModuleProxy) {
 		if _, hasSharedLibraryInfo := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider); hasSharedLibraryInfo {
 			ctx.ModuleErrorf("cc_library %s is no longer supported in `required` of java_binary modules. Please use jni_libs instead.", dep.Name())
 		}
@@ -2135,7 +2464,7 @@
 
 func metalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
 	srcs android.Paths, homeDir android.WritablePath,
-	classpath android.Paths, configFiles android.Paths) *android.RuleBuilderCommand {
+	classpath android.Paths, configFiles android.Paths, apiSurface *string) *android.RuleBuilderCommand {
 	rule.Command().Text("rm -rf").Flag(homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(homeDir.String())
 
@@ -2176,6 +2505,8 @@
 
 	addMetalavaConfigFilesToCmd(cmd, configFiles)
 
+	addOptionalApiSurfaceToCmd(cmd, apiSurface)
+
 	if len(classpath) == 0 {
 		// The main purpose of the `--api-class-resolution api` option is to force metalava to ignore
 		// classes on the classpath when an API file contains missing classes. However, as this command
@@ -2215,6 +2546,7 @@
 	apiContributions := al.properties.Api_contributions
 	addValidations := !ctx.Config().IsEnvTrue("DISABLE_STUB_VALIDATION") &&
 		!ctx.Config().IsEnvTrue("WITHOUT_CHECK_API") &&
+		!ctx.Config().PartialCompileFlags().Disable_stub_validation &&
 		proptools.BoolDefault(al.properties.Enable_validation, true)
 	for _, apiContributionName := range apiContributions {
 		ctx.AddDependency(ctx.Module(), javaApiContributionTag, apiContributionName)
@@ -2259,6 +2591,17 @@
 var scopeOrderMap = AllApiScopes.MapToIndex(
 	func(s *apiScope) string { return s.name })
 
+// Add some extra entries into scopeOrderMap for some special api surface names needed by libcore,
+// external/conscrypt and external/icu and java/core-libraries.
+func init() {
+	count := len(scopeOrderMap)
+	scopeOrderMap["core"] = count + 1
+	scopeOrderMap["core-platform"] = count + 2
+	scopeOrderMap["intra-core"] = count + 3
+	scopeOrderMap["core-platform-plus-public"] = count + 4
+	scopeOrderMap["core-platform-legacy"] = count + 5
+}
+
 func (al *ApiLibrary) sortApiFilesByApiScope(ctx android.ModuleContext, srcFilesInfo []JavaApiImportInfo) []JavaApiImportInfo {
 	for _, srcFileInfo := range srcFilesInfo {
 		if srcFileInfo.ApiSurface == "" {
@@ -2307,7 +2650,7 @@
 	var bootclassPaths android.Paths
 	var staticLibs android.Paths
 	var systemModulesPaths android.Paths
-	ctx.VisitDirectDeps(func(dep android.Module) {
+	ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(dep)
 		switch tag {
 		case javaApiContributionTag:
@@ -2336,8 +2679,8 @@
 				systemModulesPaths = append(systemModulesPaths, sm.HeaderJars...)
 			}
 		case metalavaCurrentApiTimestampTag:
-			if currentApiTimestampProvider, ok := dep.(currentApiTimestampProvider); ok {
-				al.validationPaths = append(al.validationPaths, currentApiTimestampProvider.CurrentApiTimestamp())
+			if currentApiTimestampProvider, ok := android.OtherModuleProvider(ctx, dep, DroidStubsInfoProvider); ok {
+				al.validationPaths = append(al.validationPaths, currentApiTimestampProvider.CurrentApiTimestamp)
 			}
 		case aconfigDeclarationTag:
 			if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok {
@@ -2369,7 +2712,7 @@
 	combinedPaths := append(([]android.Path)(nil), systemModulesPaths...)
 	combinedPaths = append(combinedPaths, classPaths...)
 	combinedPaths = append(combinedPaths, bootclassPaths...)
-	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, combinedPaths, configFiles)
+	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, combinedPaths, configFiles, al.properties.Api_surface)
 
 	al.stubsFlags(ctx, cmd, stubsDir)
 
@@ -2433,7 +2776,7 @@
 
 	ctx.Phony(ctx.ModuleName(), al.stubsJar)
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(al.stubsJar),
 		LocalHeaderJars:                        android.PathsIfNonNil(al.stubsJar),
 		TransitiveStaticLibsHeaderJars:         depset.New(depset.PREORDER, android.PathsIfNonNil(al.stubsJar), nil),
@@ -2443,7 +2786,9 @@
 		AidlIncludeDirs:                        android.Paths{},
 		StubsLinkType:                          Stubs,
 		// No aconfig libraries on api libraries
-	})
+	}
+	setExtraJavaInfo(ctx, al, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
 }
 
 func (al *ApiLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
@@ -2711,7 +3056,7 @@
 	var staticJars android.Paths
 	var staticResourceJars android.Paths
 	var staticHeaderJars android.Paths
-	ctx.VisitDirectDeps(func(module android.Module) {
+	ctx.VisitDirectDepsProxy(func(module android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			switch tag {
@@ -2763,12 +3108,7 @@
 	// file of the module to be named jarName.
 	var outputFile android.Path
 	combinedImplementationJar := android.PathForModuleOut(ctx, "combined", jarName)
-	var implementationJars android.Paths
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		implementationJars = completeStaticLibsImplementationJars.ToList()
-	} else {
-		implementationJars = append(slices.Clone(localJars), staticJars...)
-	}
+	implementationJars := completeStaticLibsImplementationJars.ToList()
 	TransformJarsToJar(ctx, combinedImplementationJar, "combine prebuilt implementation jars", implementationJars, android.OptionalPath{},
 		false, j.properties.Exclude_files, j.properties.Exclude_dirs)
 	outputFile = combinedImplementationJar
@@ -2791,12 +3131,7 @@
 	if reuseImplementationJarAsHeaderJar {
 		headerJar = outputFile
 	} else {
-		var headerJars android.Paths
-		if ctx.Config().UseTransitiveJarsInClasspath() {
-			headerJars = completeStaticLibsHeaderJars.ToList()
-		} else {
-			headerJars = append(slices.Clone(localJars), staticHeaderJars...)
-		}
+		headerJars := completeStaticLibsHeaderJars.ToList()
 		headerOutputFile := android.PathForModuleOut(ctx, "turbine-combined", jarName)
 		TransformJarsToJar(ctx, headerOutputFile, "combine prebuilt header jars", headerJars, android.OptionalPath{},
 			false, j.properties.Exclude_files, j.properties.Exclude_dirs)
@@ -2833,6 +3168,23 @@
 		outputFile = combinedJar
 	}
 
+	proguardFlags := android.PathForModuleOut(ctx, "proguard_flags")
+	TransformJarToR8Rules(ctx, proguardFlags, outputFile)
+
+	transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx)
+	android.SetProvider(ctx, ProguardSpecInfoProvider, ProguardSpecInfo{
+		ProguardFlagsFiles: depset.New[android.Path](
+			depset.POSTORDER,
+			android.Paths{proguardFlags},
+			transitiveProguardFlags,
+		),
+		UnconditionallyExportedProguardFlags: depset.New[android.Path](
+			depset.POSTORDER,
+			nil,
+			transitiveUnconditionalExportedFlags,
+		),
+	})
+
 	// 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.
@@ -2843,11 +3195,7 @@
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 
-	if ctx.Config().UseTransitiveJarsInClasspath() {
-		ctx.CheckbuildFile(localJars...)
-	} else {
-		ctx.CheckbuildFile(outputFile)
-	}
+	ctx.CheckbuildFile(localJars...)
 
 	if ctx.Device() {
 		// Shared libraries deapexed from prebuilt apexes are no longer supported.
@@ -2901,7 +3249,7 @@
 		}
 	}
 
-	android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{
+	javaInfo := &JavaInfo{
 		HeaderJars:                             android.PathsIfNonNil(j.combinedHeaderFile),
 		LocalHeaderJars:                        android.PathsIfNonNil(j.combinedHeaderFile),
 		TransitiveLibsHeaderJarsForR8:          j.transitiveLibsHeaderJarsForR8,
@@ -2915,10 +3263,18 @@
 		AidlIncludeDirs:                        j.exportAidlIncludeDirs,
 		StubsLinkType:                          j.stubsLinkType,
 		// TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts
+	}
+	setExtraJavaInfo(ctx, j, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
+	android.SetProvider(ctx, JavaLibraryInfoProvider, JavaLibraryInfo{
+		Prebuilt: true,
 	})
 
 	ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, "")
 	ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, ".jar")
+
+	buildComplianceMetadata(ctx)
 }
 
 func (j *Import) maybeInstall(ctx android.ModuleContext, jarName string, outputFile android.Path) {
@@ -2965,26 +3321,29 @@
 var _ android.ApexModule = (*Import)(nil)
 
 // Implements android.ApexModule
-func (j *Import) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	return j.depIsInSameApex(ctx, dep)
+func (m *Import) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return JavaImportDepInSameApexChecker{}
+}
+
+type JavaImportDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m JavaImportDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	return depIsInSameApex(tag)
 }
 
 // Implements android.ApexModule
-func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
+func (j *Import) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
 	sdkVersionSpec := j.SdkVersion(ctx)
 	minSdkVersion := j.MinSdkVersion(ctx)
-	if !minSdkVersion.Specified() {
-		return fmt.Errorf("min_sdk_version is not specified")
-	}
+
 	// If the module is compiling against core (via sdk_version), skip comparison check.
 	if sdkVersionSpec.Kind == android.SdkCore {
-		return nil
+		return android.MinApiLevel
 	}
-	if minSdkVersion.GreaterThan(sdkVersion) {
-		return fmt.Errorf("newer SDK(%v)", minSdkVersion)
-	}
-	return nil
+
+	return minSdkVersion
 }
 
 // requiredFilesFromPrebuiltApexForImport returns information about the files that a java_import or
@@ -3021,19 +3380,10 @@
 
 // Add compile time check for interface implementation
 var _ android.IDEInfo = (*Import)(nil)
-var _ android.IDECustomizedModuleName = (*Import)(nil)
 
 // Collect information for opening IDE project files in java/jdeps.go.
-
 func (j *Import) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
-	dpInfo.Jars = append(dpInfo.Jars, j.combinedHeaderFile.String())
-}
-
-func (j *Import) IDECustomizedModuleName() string {
-	// TODO(b/113562217): Extract the base module name from the Import name, often the Import name
-	// has a prefix "prebuilt_". Remove the prefix explicitly if needed until we find a better
-	// solution to get the Import name.
-	return android.RemoveOptionalPrebuiltPrefix(j.Name())
+	dpInfo.Jars = append(dpInfo.Jars, j.combinedImplementationFile.String())
 }
 
 var _ android.PrebuiltInterface = (*Import)(nil)
@@ -3191,6 +3541,12 @@
 		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			j.Stem()+".jar", dexOutputFile)
 	}
+
+	javaInfo := &JavaInfo{}
+	setExtraJavaInfo(ctx, j, javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
+	android.SetProvider(ctx, JavaDexImportInfoProvider, JavaDexImportInfo{})
 }
 
 func (j *DexImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
@@ -3200,10 +3556,8 @@
 var _ android.ApexModule = (*DexImport)(nil)
 
 // Implements android.ApexModule
-func (j *DexImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	// we don't check prebuilt modules for sdk_version
-	return nil
+func (m *DexImport) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 // dex_import imports a `.jar` file containing classes.dex files.
@@ -3225,7 +3579,6 @@
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
-	android.ApexModuleBase
 }
 
 // java_defaults provides a set of properties that can be inherited by other java or android modules.
@@ -3289,6 +3642,8 @@
 		&bootclasspathFragmentProperties{},
 		&SourceOnlyBootclasspathProperties{},
 		&ravenwoodTestProperties{},
+		&AndroidAppImportProperties{},
+		&UsesLibraryProperties{},
 	)
 
 	android.InitDefaultsModule(module)
@@ -3305,10 +3660,10 @@
 func (ks *kytheExtractJavaSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var xrefTargets android.Paths
 	var xrefKotlinTargets android.Paths
-	ctx.VisitAllModules(func(module android.Module) {
-		if javaModule, ok := module.(xref); ok {
-			xrefTargets = append(xrefTargets, javaModule.XrefJavaFiles()...)
-			xrefKotlinTargets = append(xrefKotlinTargets, javaModule.XrefKotlinFiles()...)
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if javaInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
+			xrefTargets = append(xrefTargets, javaInfo.XrefJavaFiles...)
+			xrefKotlinTargets = append(xrefKotlinTargets, javaInfo.XrefKotlinFiles...)
 		}
 	})
 	// TODO(asmundak): perhaps emit a rule to output a warning if there were no xrefTargets
@@ -3326,11 +3681,11 @@
 var inList = android.InList[string]
 
 // Add class loader context (CLC) of a given dependency to the current CLC.
-func addCLCFromDep(ctx android.ModuleContext, depModule android.Module,
+func addCLCFromDep(ctx android.ModuleContext, depModule android.ModuleProxy,
 	clcMap dexpreopt.ClassLoaderContextMap) {
 
-	dep, ok := depModule.(UsesLibraryDependency)
-	if !ok {
+	dep, ok := android.OtherModuleProvider(ctx, depModule, JavaInfoProvider)
+	if !ok || dep.UsesLibraryDependencyInfo == nil {
 		return
 	}
 
@@ -3340,14 +3695,14 @@
 	if lib, ok := android.OtherModuleProvider(ctx, depModule, SdkLibraryInfoProvider); ok && lib.SharedLibrary {
 		// A shared SDK library. This should be added as a top-level CLC element.
 		sdkLib = &depName
-	} else if lib, ok := depModule.(SdkLibraryComponentDependency); ok && lib.OptionalSdkLibraryImplementation() != nil {
-		if depModule.Name() == proptools.String(lib.OptionalSdkLibraryImplementation())+".impl" {
-			sdkLib = lib.OptionalSdkLibraryImplementation()
+	} else if lib := dep.SdkLibraryComponentDependencyInfo; lib != nil && lib.OptionalSdkLibraryImplementation != nil {
+		if depModule.Name() == proptools.String(lib.OptionalSdkLibraryImplementation)+".impl" {
+			sdkLib = lib.OptionalSdkLibraryImplementation
 		}
-	} else if ulib, ok := depModule.(ProvidesUsesLib); ok {
+	} else if ulib := dep.ProvidesUsesLibInfo; ulib != nil {
 		// A non-SDK library disguised as an SDK library by the means of `provides_uses_lib`
 		// property. This should be handled in the same way as a shared SDK library.
-		sdkLib = ulib.ProvidesUsesLib()
+		sdkLib = ulib.ProvidesUsesLib
 	}
 
 	depTag := ctx.OtherModuleDependencyTag(depModule)
@@ -3379,21 +3734,22 @@
 			}
 		}
 		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, optional,
-			dep.DexJarBuildPath(ctx).PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
+			dep.DexJarBuildPath.PathOrNil(),
+			dep.UsesLibraryDependencyInfo.DexJarInstallPath, dep.UsesLibraryDependencyInfo.ClassLoaderContexts)
 	} else {
-		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
+		clcMap.AddContextMap(dep.UsesLibraryDependencyInfo.ClassLoaderContexts, depName)
 	}
 }
 
-func addMissingOptionalUsesLibsFromDep(ctx android.ModuleContext, depModule android.Module,
+func addMissingOptionalUsesLibsFromDep(ctx android.ModuleContext, depModule android.ModuleProxy,
 	usesLibrary *usesLibrary) {
 
-	dep, ok := depModule.(ModuleWithUsesLibrary)
+	dep, ok := android.OtherModuleProvider(ctx, depModule, JavaInfoProvider)
 	if !ok {
 		return
 	}
 
-	for _, lib := range dep.UsesLibrary().usesLibraryProperties.Missing_optional_uses_libs {
+	for _, lib := range dep.MissingOptionalUsesLibs {
 		if !android.InList(lib, usesLibrary.usesLibraryProperties.Missing_optional_uses_libs) {
 			usesLibrary.usesLibraryProperties.Missing_optional_uses_libs =
 				append(usesLibrary.usesLibraryProperties.Missing_optional_uses_libs, lib)
@@ -3449,3 +3805,68 @@
 func (ap *JavaApiContributionImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	ap.JavaApiContribution.GenerateAndroidBuildActions(ctx)
 }
+
+func setExtraJavaInfo(ctx android.ModuleContext, module android.Module, javaInfo *JavaInfo) {
+	if alDep, ok := module.(AndroidLibraryDependency); ok {
+		javaInfo.AndroidLibraryDependencyInfo = &AndroidLibraryDependencyInfo{
+			ExportPackage:       alDep.ExportPackage(),
+			ResourcesNodeDepSet: alDep.ResourcesNodeDepSet(),
+			RRODirsDepSet:       alDep.RRODirsDepSet(),
+			ManifestsDepSet:     alDep.ManifestsDepSet(),
+		}
+	}
+
+	if ulDep, ok := module.(UsesLibraryDependency); ok {
+		javaInfo.UsesLibraryDependencyInfo = &UsesLibraryDependencyInfo{
+			DexJarInstallPath:   ulDep.DexJarInstallPath(),
+			ClassLoaderContexts: ulDep.ClassLoaderContexts(),
+		}
+	}
+
+	if slcDep, ok := module.(SdkLibraryComponentDependency); ok {
+		javaInfo.SdkLibraryComponentDependencyInfo = &SdkLibraryComponentDependencyInfo{
+			OptionalSdkLibraryImplementation: slcDep.OptionalSdkLibraryImplementation(),
+		}
+	}
+
+	if pul, ok := module.(ProvidesUsesLib); ok {
+		javaInfo.ProvidesUsesLibInfo = &ProvidesUsesLibInfo{
+			ProvidesUsesLib: pul.ProvidesUsesLib(),
+		}
+	}
+
+	if mwul, ok := module.(ModuleWithUsesLibrary); ok {
+		javaInfo.MissingOptionalUsesLibs = mwul.UsesLibrary().usesLibraryProperties.Missing_optional_uses_libs
+	}
+
+	if mwsd, ok := module.(moduleWithSdkDep); ok {
+		linkType, stubs := mwsd.getSdkLinkType(ctx, ctx.ModuleName())
+		javaInfo.ModuleWithSdkDepInfo = &ModuleWithSdkDepInfo{
+			SdkLinkType: linkType,
+			Stubs:       stubs,
+		}
+	}
+
+	if st, ok := module.(ModuleWithStem); ok {
+		javaInfo.Stem = st.Stem()
+	}
+
+	if mm, ok := module.(interface {
+		DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath
+	}); ok {
+		javaInfo.DexJarBuildPath = mm.DexJarBuildPath(ctx)
+	}
+
+	if di, ok := module.(DexpreopterInterface); ok {
+		javaInfo.DexpreopterInfo = &DexpreopterInfo{
+			OutputProfilePathOnHost:           di.OutputProfilePathOnHost(),
+			ApexSystemServerDexpreoptInstalls: di.ApexSystemServerDexpreoptInstalls(),
+			ApexSystemServerDexJars:           di.ApexSystemServerDexJars(),
+		}
+	}
+
+	if xr, ok := module.(xref); ok {
+		javaInfo.XrefJavaFiles = xr.XrefJavaFiles()
+		javaInfo.XrefKotlinFiles = xr.XrefKotlinFiles()
+	}
+}
diff --git a/java/java_test.go b/java/java_test.go
index d415679..1ba78d4 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -110,13 +110,14 @@
 	case strings.HasSuffix(name, ".jar"):
 		return name
 	default:
-		return filepath.Join("out", "soong", ".intermediates", defaultJavaDir, name, "android_common", "turbine-combined", name+".jar")
+		return filepath.Join("out", "soong", ".intermediates", defaultJavaDir, name, "android_common", "turbine", name+".jar")
 	}
 }
 
 // Test that the PrepareForTestWithJavaDefaultModules provides all the files that it uses by
 // running it in a fixture that requires all source files to exist.
 func TestPrepareForTestWithJavaDefaultModules(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestDisallowNonExistentPaths,
@@ -124,6 +125,7 @@
 }
 
 func TestJavaLinkType(t *testing.T) {
+	t.Parallel()
 	testJava(t, `
 		java_library {
 			name: "foo",
@@ -212,6 +214,7 @@
 }
 
 func TestSimple(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -237,11 +240,6 @@
 			srcs: ["d.java"],
 		}`
 
-	frameworkTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/ext/android_common/turbine-combined/ext.jar",
-		"out/soong/.intermediates/default/java/framework/android_common/turbine-combined/framework.jar",
-	}
-
 	frameworkTurbineJars := []string{
 		"out/soong/.intermediates/default/java/ext/android_common/turbine/ext.jar",
 		"out/soong/.intermediates/default/java/framework/android_common/turbine/framework.jar",
@@ -267,43 +265,6 @@
 			preparer:       android.NullFixturePreparer,
 			fooJavacInputs: []string{"a.java"},
 			fooJavacClasspath: slices.Concat(
-				frameworkTurbineCombinedJars,
-				[]string{
-					"out/soong/.intermediates/bar/android_common/turbine-combined/bar.jar",
-					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-				},
-			),
-			fooCombinedInputs: []string{
-				"out/soong/.intermediates/foo/android_common/javac/foo.jar",
-				"out/soong/.intermediates/baz/android_common/combined/baz.jar",
-			},
-
-			fooHeaderCombinedInputs: []string{
-				"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
-				"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-			},
-
-			barJavacInputs: []string{"b.java"},
-			barJavacClasspath: slices.Concat(
-				frameworkTurbineCombinedJars,
-				[]string{
-					"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
-				},
-			),
-			barCombinedInputs: []string{
-				"out/soong/.intermediates/bar/android_common/javac/bar.jar",
-				"out/soong/.intermediates/quz/android_common/javac/quz.jar",
-			},
-			barHeaderCombinedInputs: []string{
-				"out/soong/.intermediates/bar/android_common/turbine/bar.jar",
-				"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
-			},
-		},
-		{
-			name:           "transitive classpath",
-			preparer:       PrepareForTestWithTransitiveClasspathEnabled,
-			fooJavacInputs: []string{"a.java"},
-			fooJavacClasspath: slices.Concat(
 				frameworkTurbineJars,
 				[]string{
 					"out/soong/.intermediates/bar/android_common/turbine/bar.jar",
@@ -341,11 +302,12 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				tt.preparer,
 			).RunTestWithBp(t, bp)
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 
 			fooJavac := foo.Rule("javac")
 			android.AssertPathsRelativeToTopEquals(t, "foo javac inputs", tt.fooJavacInputs, fooJavac.Inputs)
@@ -360,7 +322,7 @@
 			fooCombinedHeaderJar := foo.Output("turbine-combined/foo.jar")
 			android.AssertPathsRelativeToTopEquals(t, "foo header combined inputs", tt.fooHeaderCombinedInputs, fooCombinedHeaderJar.Inputs)
 
-			bar := result.ModuleForTests("bar", "android_common")
+			bar := result.ModuleForTests(t, "bar", "android_common")
 			barJavac := bar.Rule("javac")
 			android.AssertPathsRelativeToTopEquals(t, "bar javac inputs", tt.barJavacInputs, barJavac.Inputs)
 
@@ -378,6 +340,7 @@
 }
 
 func TestExportedPlugins(t *testing.T) {
+	t.Parallel()
 	type Result struct {
 		library        string
 		processors     string
@@ -456,6 +419,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx, _ := testJava(t, `
 				java_plugin {
 					name: "plugin",
@@ -469,11 +433,11 @@
 			`+test.extra)
 
 			for _, want := range test.results {
-				javac := ctx.ModuleForTests(want.library, "android_common").Rule("javac")
+				javac := ctx.ModuleForTests(t, want.library, "android_common").Rule("javac")
 				if javac.Args["processor"] != want.processors {
 					t.Errorf("For library %v, expected %v, found %v", want.library, want.processors, javac.Args["processor"])
 				}
-				turbine := ctx.ModuleForTests(want.library, "android_common").MaybeRule("turbine")
+				turbine := ctx.ModuleForTests(t, want.library, "android_common").MaybeRule("turbine")
 				disableTurbine := turbine.BuildParams.Rule == nil
 				if disableTurbine != want.disableTurbine {
 					t.Errorf("For library %v, expected disableTurbine %v, found %v", want.library, want.disableTurbine, disableTurbine)
@@ -484,6 +448,7 @@
 }
 
 func TestSdkVersionByPartition(t *testing.T) {
+	t.Parallel()
 	testJavaError(t, "sdk_version must have a value when the module is located at vendor or product", `
 		java_library {
 			name: "foo",
@@ -525,6 +490,7 @@
 }
 
 func TestArchSpecific(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -537,13 +503,14 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
 	if len(javac.Inputs) != 2 || javac.Inputs[0].String() != "a.java" || javac.Inputs[1].String() != "b.java" {
 		t.Errorf(`foo inputs %v != ["a.java", "b.java"]`, javac.Inputs)
 	}
 }
 
 func TestBinary(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library_host {
 			name: "foo",
@@ -567,11 +534,11 @@
 
 	buildOS := ctx.Config().BuildOS.String()
 
-	bar := ctx.ModuleForTests("bar", buildOS+"_common")
+	bar := ctx.ModuleForTests(t, "bar", buildOS+"_common")
 	barJar := bar.Output("bar.jar").Output.String()
 	barWrapperDeps := bar.Output("bar").Implicits.Strings()
 
-	libjni := ctx.ModuleForTests("libjni", buildOS+"_x86_64_shared")
+	libjni := ctx.ModuleForTests(t, "libjni", buildOS+"_x86_64_shared")
 	libjniSO := libjni.Rule("Cp").Output.String()
 
 	// Test that the install binary wrapper depends on the installed jar file
@@ -586,6 +553,7 @@
 }
 
 func TestTest(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_test_host {
 			name: "foo",
@@ -603,7 +571,7 @@
 
 	buildOS := ctx.Config().BuildOS.String()
 
-	foo := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
+	foo := ctx.ModuleForTests(t, "foo", buildOS+"_common").Module().(*TestHost)
 
 	expected := "lib64/libjni.so"
 	if runtime.GOOS == "darwin" {
@@ -617,7 +585,31 @@
 	}
 }
 
+func TestHostCommonData(t *testing.T) {
+	t.Parallel()
+	ctx, _ := testJava(t, `
+		java_library_host {
+			name: "host",
+			srcs: ["a.java"],
+		}
+
+		java_test {
+			name: "foo",
+			srcs: ["a.java"],
+			host_common_data: [":host"],
+		}
+	`)
+
+	foo := ctx.ModuleForTests(t, "foo", "android_common").Module().(*Test)
+	host := ctx.ModuleForTests(t, "host", ctx.Config().BuildOSCommonTarget.String()).Module().(*Library)
+
+	if g, w := foo.data.RelativeToTop().Strings(), []string{host.outputFile.RelativeToTop().String()}; !slices.Equal(g, w) {
+		t.Errorf("expected test data %q, got %q\n", w, g)
+	}
+}
+
 func TestHostBinaryNoJavaDebugInfoOverride(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "target_library",
@@ -638,7 +630,7 @@
 	).RunTestWithBp(t, bp)
 
 	// first, check that the -g flag is added to target modules
-	targetLibrary := result.ModuleForTests("target_library", "android_common")
+	targetLibrary := result.ModuleForTests(t, "target_library", "android_common")
 	targetJavaFlags := targetLibrary.Module().VariablesForTests()["javacFlags"]
 	if !strings.Contains(targetJavaFlags, "-g:source,lines") {
 		t.Errorf("target library javac flags %v should contain "+
@@ -647,7 +639,7 @@
 
 	// check that -g is not overridden for host modules
 	buildOS := result.Config.BuildOS.String()
-	hostBinary := result.ModuleForTests("host_binary", buildOS+"_common")
+	hostBinary := result.ModuleForTests(t, "host_binary", buildOS+"_common")
 	hostJavaFlags := hostBinary.Module().VariablesForTests()["javacFlags"]
 	if strings.Contains(hostJavaFlags, "-g:source,lines") {
 		t.Errorf("java_binary_host javac flags %v should not have "+
@@ -665,6 +657,7 @@
 var _ android.ModuleErrorfContext = (*moduleErrorfTestCtx)(nil)
 
 func TestPrebuilts(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -710,15 +703,15 @@
 		}
 		`)
 
-	fooModule := ctx.ModuleForTests("foo", "android_common")
+	fooModule := ctx.ModuleForTests(t, "foo", "android_common")
 	javac := fooModule.Rule("javac")
-	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
-	barModule := ctx.ModuleForTests("bar", "android_common")
-	barJar := barModule.Output("combined/bar.jar").Output
-	bazModule := ctx.ModuleForTests("baz", "android_common")
-	bazJar := bazModule.Output("combined/baz.jar").Output
-	sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").
-		Output("combined/sdklib.stubs.jar").Output
+	combineJar := ctx.ModuleForTests(t, "foo", "android_common").Description("for javac")
+	barModule := ctx.ModuleForTests(t, "bar", "android_common")
+	barJar := barModule.Output("local-combined/bar.jar").Output
+	bazModule := ctx.ModuleForTests(t, "baz", "android_common")
+	bazJar := bazModule.Output("local-combined/baz.jar").Output
+	sdklibStubsJar := ctx.ModuleForTests(t, "sdklib.stubs", "android_common").
+		Output("local-combined/sdklib.stubs.jar").Output
 
 	fooLibrary := fooModule.Module().(*Library)
 	assertDeepEquals(t, "foo unique sources incorrect",
@@ -750,7 +743,7 @@
 	expectedDexJar := "out/soong/.intermediates/baz/android_common/dex/baz.jar"
 	android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar)
 
-	ctx.ModuleForTests("qux", "android_common").Rule("Cp")
+	ctx.ModuleForTests(t, "qux", "android_common").Rule("Cp")
 
 	entries := android.AndroidMkEntriesForTest(t, ctx, fooModule.Module())[0]
 	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_library", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
@@ -765,6 +758,7 @@
 }
 
 func TestPrebuiltStubsSources(t *testing.T) {
+	t.Parallel()
 	test := func(t *testing.T, sourcesPath string, expectedInputs []string) {
 		ctx, _ := testJavaWithFS(t, fmt.Sprintf(`
 prebuilt_stubs_sources {
@@ -775,17 +769,19 @@
 			"stubs/sources/pkg/B.java": nil,
 		})
 
-		zipSrc := ctx.ModuleForTests("stubs-source", "android_common").Rule("zip_src")
+		zipSrc := ctx.ModuleForTests(t, "stubs-source", "android_common").Rule("zip_src")
 		if expected, actual := expectedInputs, zipSrc.Inputs.Strings(); !reflect.DeepEqual(expected, actual) {
 			t.Errorf("mismatch of inputs to soong_zip: expected %q, actual %q", expected, actual)
 		}
 	}
 
 	t.Run("empty/missing directory", func(t *testing.T) {
+		t.Parallel()
 		test(t, "empty-directory", nil)
 	})
 
 	t.Run("non-empty set of sources", func(t *testing.T) {
+		t.Parallel()
 		test(t, "stubs/sources", []string{
 			"stubs/sources/pkg/A.java",
 			"stubs/sources/pkg/B.java",
@@ -794,6 +790,7 @@
 }
 
 func TestDefaults(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_defaults {
 			name: "defaults",
@@ -835,40 +832,41 @@
 		}
 		`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
+	combineJar := ctx.ModuleForTests(t, "foo", "android_common").Description("for javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
 		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
 	}
 
-	barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+	barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
 	if !strings.Contains(javac.Args["classpath"], barTurbine) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
 	}
 
-	baz := ctx.ModuleForTests("baz", "android_common").Rule("javac").Output.String()
+	baz := ctx.ModuleForTests(t, "baz", "android_common").Rule("javac").Output.String()
 	if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
 		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
 	}
 
-	atestOptimize := ctx.ModuleForTests("atestOptimize", "android_common").MaybeRule("r8")
+	atestOptimize := ctx.ModuleForTests(t, "atestOptimize", "android_common").MaybeRule("r8")
 	if atestOptimize.Output == nil {
 		t.Errorf("atestOptimize should optimize APK")
 	}
 
-	atestNoOptimize := ctx.ModuleForTests("atestNoOptimize", "android_common").MaybeRule("d8")
+	atestNoOptimize := ctx.ModuleForTests(t, "atestNoOptimize", "android_common").MaybeRule("d8")
 	if atestNoOptimize.Output == nil {
 		t.Errorf("atestNoOptimize should not optimize APK")
 	}
 
-	atestDefault := ctx.ModuleForTests("atestDefault", "android_common").MaybeRule("d8")
+	atestDefault := ctx.ModuleForTests(t, "atestDefault", "android_common").MaybeRule("d8")
 	if atestDefault.Output == nil {
 		t.Errorf("atestDefault should not optimize APK")
 	}
 }
 
 func TestResources(t *testing.T) {
+	t.Parallel()
 	var table = []struct {
 		name  string
 		prop  string
@@ -940,6 +938,7 @@
 
 	for _, test := range table {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			ctx, _ := testJavaWithFS(t, `
 				java_library {
 					name: "foo",
@@ -958,8 +957,8 @@
 				},
 			)
 
-			foo := ctx.ModuleForTests("foo", "android_common").Output("withres/foo.jar")
-			fooRes := ctx.ModuleForTests("foo", "android_common").Output("res/foo.jar")
+			foo := ctx.ModuleForTests(t, "foo", "android_common").Output("withres/foo.jar")
+			fooRes := ctx.ModuleForTests(t, "foo", "android_common").Output("res/foo.jar")
 
 			if !inList(fooRes.Output.String(), foo.Inputs.Strings()) {
 				t.Errorf("foo combined jars %v does not contain %q",
@@ -975,6 +974,7 @@
 }
 
 func TestIncludeSrcs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -1003,8 +1003,8 @@
 	})
 
 	// Test a library with include_srcs: true
-	foo := ctx.ModuleForTests("foo", "android_common").Output("withres/foo.jar")
-	fooSrcJar := ctx.ModuleForTests("foo", "android_common").Output("foo.srcjar")
+	foo := ctx.ModuleForTests(t, "foo", "android_common").Output("withres/foo.jar")
+	fooSrcJar := ctx.ModuleForTests(t, "foo", "android_common").Output("foo.srcjar")
 
 	if g, w := fooSrcJar.Output.String(), foo.Inputs.Strings(); !inList(g, w) {
 		t.Errorf("foo combined jars %v does not contain %q", w, g)
@@ -1015,10 +1015,10 @@
 	}
 
 	// Test a library with include_srcs: true and resources
-	bar := ctx.ModuleForTests("bar", "android_common").Output("withres/bar.jar")
-	barResCombined := ctx.ModuleForTests("bar", "android_common").Output("res-combined/bar.jar")
-	barRes := ctx.ModuleForTests("bar", "android_common").Output("res/bar.jar")
-	barSrcJar := ctx.ModuleForTests("bar", "android_common").Output("bar.srcjar")
+	bar := ctx.ModuleForTests(t, "bar", "android_common").Output("withres/bar.jar")
+	barResCombined := ctx.ModuleForTests(t, "bar", "android_common").Output("res-combined/bar.jar")
+	barRes := ctx.ModuleForTests(t, "bar", "android_common").Output("res/bar.jar")
+	barSrcJar := ctx.ModuleForTests(t, "bar", "android_common").Output("bar.srcjar")
 
 	if g, w := barSrcJar.Output.String(), barResCombined.Inputs.Strings(); !inList(g, w) {
 		t.Errorf("bar combined resource jars %v does not contain %q", w, g)
@@ -1028,7 +1028,7 @@
 		t.Errorf("bar combined resource jars %v does not contain %q", w, g)
 	}
 
-	if g, w := barResCombined.Output.String(), bar.Inputs.Strings(); !inList(g, w) {
+	if g, w := barRes.Output.String(), bar.Inputs.Strings(); !inList(g, w) {
 		t.Errorf("bar combined jars %v does not contain %q", w, g)
 	}
 
@@ -1042,6 +1042,7 @@
 }
 
 func TestGeneratedSources(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -1062,8 +1063,8 @@
 		"b.java": nil,
 	})
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	genrule := ctx.ModuleForTests("gen", "").Rule("generator")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
+	genrule := ctx.ModuleForTests(t, "gen", "").Rule("generator")
 
 	if filepath.Base(genrule.Output.String()) != "gen.java" {
 		t.Fatalf(`gen output file %v is not ".../gen.java"`, genrule.Output.String())
@@ -1078,6 +1079,7 @@
 }
 
 func TestTurbine(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}})).
 		RunTestWithBp(t, `
@@ -1102,15 +1104,15 @@
 		}
 		`)
 
-	fooTurbine := result.ModuleForTests("foo", "android_common").Rule("turbine")
-	barTurbine := result.ModuleForTests("bar", "android_common").Rule("turbine")
-	barJavac := result.ModuleForTests("bar", "android_common").Rule("javac")
-	barTurbineCombined := result.ModuleForTests("bar", "android_common").Description("for turbine")
-	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
+	fooTurbine := result.ModuleForTests(t, "foo", "android_common").Rule("turbine")
+	barTurbine := result.ModuleForTests(t, "bar", "android_common").Rule("turbine")
+	barJavac := result.ModuleForTests(t, "bar", "android_common").Rule("javac")
+	barTurbineCombined := result.ModuleForTests(t, "bar", "android_common").Description("for turbine")
+	bazJavac := result.ModuleForTests(t, "baz", "android_common").Rule("javac")
 
 	android.AssertPathsRelativeToTopEquals(t, "foo inputs", []string{"a.java"}, fooTurbine.Inputs)
 
-	fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
+	fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine", "foo.jar")
 	barTurbineJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
 	android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["turbineFlags"], fooHeaderJar)
 	android.AssertStringDoesContain(t, "bar javac classpath", barJavac.Args["classpath"], fooHeaderJar)
@@ -1119,6 +1121,7 @@
 }
 
 func TestSharding(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "bar",
@@ -1129,7 +1132,7 @@
 
 	barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
 	for i := 0; i < 3; i++ {
-		barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i))
+		barJavac := ctx.ModuleForTests(t, "bar", "android_common").Description("javac" + strconv.Itoa(i))
 		if !strings.HasPrefix(barJavac.Args["classpath"], "-classpath "+barHeaderJar+":") {
 			t.Errorf("bar javac classpath %v does start with %q", barJavac.Args["classpath"], barHeaderJar)
 		}
@@ -1137,6 +1140,7 @@
 }
 
 func TestExcludeFileGroupInSrcs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1155,7 +1159,7 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "java-fg/c.java" {
 		t.Errorf(`foo inputs %v != ["java-fg/c.java"]`, javac.Inputs)
@@ -1163,6 +1167,7 @@
 }
 
 func TestJavaLibraryOutputFiles(t *testing.T) {
+	t.Parallel()
 	testJavaWithFS(t, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_library {
@@ -1180,6 +1185,7 @@
 }
 
 func TestJavaImportOutputFiles(t *testing.T) {
+	t.Parallel()
 	testJavaWithFS(t, "", map[string][]byte{
 		"libcore/Android.bp": []byte(`
 				java_import {
@@ -1196,6 +1202,7 @@
 }
 
 func TestJavaImport(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "source_library",
@@ -1223,18 +1230,20 @@
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, bp)
 
-	source := ctx.ModuleForTests("source_library", "android_common")
+	source := ctx.ModuleForTests(t, "source_library", "android_common")
 	sourceJar := source.Output("javac/source_library.jar")
-	sourceHeaderJar := source.Output("turbine-combined/source_library.jar")
+	sourceHeaderJar := source.Output("turbine/source_library.jar")
+	sourceCombinedHeaderJar := source.Output("turbine-combined/source_library.jar")
 	sourceJavaInfo, _ := android.OtherModuleProvider(ctx, source.Module(), JavaInfoProvider)
 
 	// The source library produces separate implementation and header jars
 	android.AssertPathsRelativeToTopEquals(t, "source library implementation jar",
 		[]string{sourceJar.Output.String()}, sourceJavaInfo.ImplementationAndResourcesJars)
 	android.AssertPathsRelativeToTopEquals(t, "source library header jar",
-		[]string{sourceHeaderJar.Output.String()}, sourceJavaInfo.HeaderJars)
+		[]string{sourceCombinedHeaderJar.Output.String()}, sourceJavaInfo.HeaderJars)
 
-	importWithNoDeps := ctx.ModuleForTests("import_with_no_deps", "android_common")
+	importWithNoDeps := ctx.ModuleForTests(t, "import_with_no_deps", "android_common")
+	importWithNoDepsLocalJar := importWithNoDeps.Output("local-combined/import_with_no_deps.jar")
 	importWithNoDepsJar := importWithNoDeps.Output("combined/import_with_no_deps.jar")
 	importWithNoDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithNoDeps.Module(), JavaInfoProvider)
 
@@ -1244,10 +1253,14 @@
 	android.AssertPathsRelativeToTopEquals(t, "import with no deps header jar",
 		[]string{importWithNoDepsJar.Output.String()}, importWithNoDepsJavaInfo.HeaderJars)
 	android.AssertPathsRelativeToTopEquals(t, "import with no deps combined inputs",
-		[]string{"no_deps.jar"}, importWithNoDepsJar.Inputs)
+		[]string{importWithNoDepsLocalJar.Output.String()}, importWithNoDepsJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with no deps local combined inputs",
+		[]string{"no_deps.jar"}, importWithNoDepsLocalJar.Inputs)
 
-	importWithSourceDeps := ctx.ModuleForTests("import_with_source_deps", "android_common")
+	importWithSourceDeps := ctx.ModuleForTests(t, "import_with_source_deps", "android_common")
+	importWithSourceDepsLocalJar := importWithSourceDeps.Output("local-combined/import_with_source_deps.jar")
 	importWithSourceDepsJar := importWithSourceDeps.Output("combined/import_with_source_deps.jar")
+	importWithSourceDepsLocalHeaderJar := importWithSourceDeps.Output("local-combined/import_with_source_deps.jar")
 	importWithSourceDepsHeaderJar := importWithSourceDeps.Output("turbine-combined/import_with_source_deps.jar")
 	importWithSourceDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithSourceDeps.Module(), JavaInfoProvider)
 
@@ -1257,11 +1270,16 @@
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps header jar",
 		[]string{importWithSourceDepsHeaderJar.Output.String()}, importWithSourceDepsJavaInfo.HeaderJars)
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps combined implementation jar inputs",
-		[]string{"source_deps.jar", sourceJar.Output.String()}, importWithSourceDepsJar.Inputs)
+		[]string{importWithSourceDepsLocalJar.Output.String(), sourceJar.Output.String()}, importWithSourceDepsJar.Inputs)
 	android.AssertPathsRelativeToTopEquals(t, "import with source deps combined header jar inputs",
-		[]string{"source_deps.jar", sourceHeaderJar.Output.String()}, importWithSourceDepsHeaderJar.Inputs)
+		[]string{importWithSourceDepsLocalHeaderJar.Output.String(), sourceHeaderJar.Output.String()}, importWithSourceDepsHeaderJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with source deps local combined implementation jar inputs",
+		[]string{"source_deps.jar"}, importWithSourceDepsLocalJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with source deps local combined header jar inputs",
+		[]string{"source_deps.jar"}, importWithSourceDepsLocalHeaderJar.Inputs)
 
-	importWithImportDeps := ctx.ModuleForTests("import_with_import_deps", "android_common")
+	importWithImportDeps := ctx.ModuleForTests(t, "import_with_import_deps", "android_common")
+	importWithImportDepsLocalJar := importWithImportDeps.Output("local-combined/import_with_import_deps.jar")
 	importWithImportDepsJar := importWithImportDeps.Output("combined/import_with_import_deps.jar")
 	importWithImportDepsJavaInfo, _ := android.OtherModuleProvider(ctx, importWithImportDeps.Module(), JavaInfoProvider)
 
@@ -1271,7 +1289,9 @@
 	android.AssertPathsRelativeToTopEquals(t, "import with import deps header jar",
 		[]string{importWithImportDepsJar.Output.String()}, importWithImportDepsJavaInfo.HeaderJars)
 	android.AssertPathsRelativeToTopEquals(t, "import with import deps combined implementation jar inputs",
-		[]string{"import_deps.jar", importWithNoDepsJar.Output.String()}, importWithImportDepsJar.Inputs)
+		[]string{importWithImportDepsLocalJar.Output.String(), importWithNoDepsLocalJar.Output.String()}, importWithImportDepsJar.Inputs)
+	android.AssertPathsRelativeToTopEquals(t, "import with import deps local combined implementation jar inputs",
+		[]string{"import_deps.jar"}, importWithImportDepsLocalJar.Inputs)
 }
 
 var compilerFlagsTestCases = []struct {
@@ -1323,6 +1343,7 @@
 }
 
 func TestCompilerFlags(t *testing.T) {
+	t.Parallel()
 	for _, testCase := range compilerFlagsTestCases {
 		ctx := &mockContext{result: true}
 		CheckKotlincFlags(ctx, []string{testCase.in})
@@ -1337,7 +1358,8 @@
 
 // TODO(jungjw): Consider making this more robust by ignoring path order.
 func checkPatchModuleFlag(t *testing.T, ctx *android.TestContext, moduleName string, expected string) {
-	variables := ctx.ModuleForTests(moduleName, "android_common").VariablesForTestsRelativeToTop()
+	t.Helper()
+	variables := ctx.ModuleForTests(t, moduleName, "android_common").VariablesForTestsRelativeToTop()
 	flags := strings.Split(variables["javacFlags"], " ")
 	got := ""
 	for _, flag := range flags {
@@ -1353,7 +1375,9 @@
 }
 
 func TestPatchModule(t *testing.T) {
+	t.Parallel()
 	t.Run("Java language level 8", func(t *testing.T) {
+		t.Parallel()
 		// Test with legacy javac -source 1.8 -target 1.8
 		bp := `
 			java_library {
@@ -1386,6 +1410,7 @@
 	})
 
 	t.Run("Java language level 9", func(t *testing.T) {
+		t.Parallel()
 		// Test with default javac -source 9 -target 9
 		bp := `
 			java_library {
@@ -1426,6 +1451,7 @@
 }
 
 func TestJavaLibraryWithSystemModules(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 		    name: "lib-with-source-system-modules",
@@ -1474,7 +1500,7 @@
 }
 
 func checkBootClasspathForLibWithSystemModule(t *testing.T, ctx *android.TestContext, moduleName string, expectedSuffix string) {
-	javacRule := ctx.ModuleForTests(moduleName, "android_common").Rule("javac")
+	javacRule := ctx.ModuleForTests(t, moduleName, "android_common").Rule("javac")
 	bootClasspath := javacRule.Args["bootClasspath"]
 	if strings.HasPrefix(bootClasspath, "--system ") && strings.HasSuffix(bootClasspath, expectedSuffix) {
 		t.Errorf("bootclasspath of %q must start with --system and end with %q, but was %#v.", moduleName, expectedSuffix, bootClasspath)
@@ -1482,6 +1508,7 @@
 }
 
 func TestAidlExportIncludeDirsFromImports(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1498,7 +1525,7 @@
 		}
 	`)
 
-	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	aidlCommand := ctx.ModuleForTests(t, "foo", "android_common").Rule("aidl").RuleParams.Command
 	expectedAidlFlag := "-Iaidl/bar"
 	if !strings.Contains(aidlCommand, expectedAidlFlag) {
 		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
@@ -1506,6 +1533,7 @@
 }
 
 func TestAidlFlagsArePassedToTheAidlCompiler(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1514,7 +1542,7 @@
 		}
 	`)
 
-	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	aidlCommand := ctx.ModuleForTests(t, "foo", "android_common").Rule("aidl").RuleParams.Command
 	expectedAidlFlag := "-Werror"
 	if !strings.Contains(aidlCommand, expectedAidlFlag) {
 		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
@@ -1522,6 +1550,7 @@
 }
 
 func TestAidlFlagsWithMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	fixture := android.GroupFixturePreparers(
 		prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}}))
 
@@ -1535,6 +1564,7 @@
 		{"system_current", `sdk_version: "system_current"`, "current"},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			ctx := fixture.RunTestWithBp(t, `
 				java_library {
 					name: "foo",
@@ -1542,7 +1572,7 @@
 					`+tc.sdkVersion+`
 				}
 			`)
-			aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+			aidlCommand := ctx.ModuleForTests(t, "foo", "android_common").Rule("aidl").RuleParams.Command
 			expectedAidlFlag := "--min_sdk_version=" + tc.expected
 			if !strings.Contains(aidlCommand, expectedAidlFlag) {
 				t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
@@ -1552,6 +1582,7 @@
 }
 
 func TestAidlFlagsMinSdkVersionDroidstubs(t *testing.T) {
+	t.Parallel()
 	bpTemplate := `
 	droidstubs {
 		name: "foo-stubs",
@@ -1578,13 +1609,14 @@
 	}
 	for _, tc := range testCases {
 		ctx := prepareForJavaTest.RunTestWithBp(t, fmt.Sprintf(bpTemplate, tc.sdkVersionBp))
-		aidlCmd := ctx.ModuleForTests("foo-stubs", "android_common").Rule("aidl").RuleParams.Command
+		aidlCmd := ctx.ModuleForTests(t, "foo-stubs", "android_common").Rule("aidl").RuleParams.Command
 		expected := "--min_sdk_version=" + tc.minSdkVersionExpected
 		android.AssertStringDoesContain(t, "aidl command conatins incorrect min_sdk_version for testCse: "+tc.desc, aidlCmd, expected)
 	}
 }
 
 func TestAidlEnforcePermissions(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1593,7 +1625,7 @@
 		}
 	`)
 
-	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	aidlCommand := ctx.ModuleForTests(t, "foo", "android_common").Rule("aidl").RuleParams.Command
 	expectedAidlFlag := "-Wmissing-permission-annotation -Werror"
 	if !strings.Contains(aidlCommand, expectedAidlFlag) {
 		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
@@ -1601,6 +1633,7 @@
 }
 
 func TestAidlEnforcePermissionsException(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1609,7 +1642,7 @@
 		}
 	`)
 
-	aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+	aidlCommand := ctx.ModuleForTests(t, "foo", "android_common").Rule("aidl").RuleParams.Command
 	expectedAidlFlag := "$$FLAGS -Wmissing-permission-annotation -Werror aidl/foo/IFoo.aidl"
 	if !strings.Contains(aidlCommand, expectedAidlFlag) {
 		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
@@ -1621,6 +1654,7 @@
 }
 
 func TestDataNativeBinaries(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.PrepareForTestWithAllowMissingDependencies).RunTestWithBp(t, `
@@ -1638,7 +1672,7 @@
 
 	buildOS := ctx.Config().BuildOS.String()
 
-	test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
+	test := ctx.ModuleForTests(t, "foo", buildOS+"_common").Module().(*TestHost)
 	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
 	expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64/bin:bin"}
 	actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
@@ -1646,6 +1680,7 @@
 }
 
 func TestDefaultInstallable(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_test_host {
 			name: "foo"
@@ -1653,12 +1688,13 @@
 	`)
 
 	buildOS := ctx.Config().BuildOS.String()
-	module := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
+	module := ctx.ModuleForTests(t, "foo", buildOS+"_common").Module().(*TestHost)
 	assertDeepEquals(t, "Default installable value should be true.", proptools.BoolPtr(true),
 		module.properties.Installable)
 }
 
 func TestErrorproneEnabled(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -1669,7 +1705,7 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Description("javac")
 
 	// Test that the errorprone plugins are passed to javac
 	expectedSubstring := "-Xplugin:ErrorProne"
@@ -1680,13 +1716,14 @@
 	// Modules with errorprone { enabled: true } will include errorprone checks
 	// in the main javac build rule. Only when RUN_ERROR_PRONE is true will
 	// the explicit errorprone build rule be created.
-	errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone")
+	errorprone := ctx.ModuleForTests(t, "foo", "android_common").MaybeDescription("errorprone")
 	if errorprone.RuleParams.Description != "" {
 		t.Errorf("expected errorprone build rule to not exist, but it did")
 	}
 }
 
 func TestErrorproneDisabled(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -1703,7 +1740,7 @@
 		}),
 	).RunTestWithBp(t, bp)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Description("javac")
 
 	// Test that the errorprone plugins are not passed to javac, like they would
 	// be if enabled was true.
@@ -1714,13 +1751,14 @@
 
 	// Check that no errorprone build rule is created, like there would be
 	// if enabled was unset and RUN_ERROR_PRONE was true.
-	errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone")
+	errorprone := ctx.ModuleForTests(t, "foo", "android_common").MaybeDescription("errorprone")
 	if errorprone.RuleParams.Description != "" {
 		t.Errorf("expected errorprone build rule to not exist, but it did")
 	}
 }
 
 func TestErrorproneEnabledOnlyByEnvironmentVariable(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -1734,8 +1772,8 @@
 		}),
 	).RunTestWithBp(t, bp)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
-	errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Description("javac")
+	errorprone := ctx.ModuleForTests(t, "foo", "android_common").Description("errorprone")
 
 	// Check that the errorprone plugins are not passed to javac, because they
 	// will instead be passed to the separate errorprone compilation
@@ -1751,6 +1789,7 @@
 }
 
 func TestDataDeviceBinsBuildsDeviceBinary(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		dataDeviceBinType  string
 		depCompileMultilib string
@@ -1887,6 +1926,7 @@
 
 		testName := fmt.Sprintf(`data_device_bins_%s with compile_multilib:"%s"`, tc.dataDeviceBinType, tc.depCompileMultilib)
 		t.Run(testName, func(t *testing.T) {
+			t.Parallel()
 			ctx := android.GroupFixturePreparers(PrepareForIntegrationTestWithJava).
 				ExtendWithErrorHandler(errorHandler).
 				RunTestWithBp(t, bp)
@@ -1895,7 +1935,7 @@
 			}
 
 			buildOS := ctx.Config.BuildOS.String()
-			fooVariant := ctx.ModuleForTests("foo", buildOS+"_common")
+			fooVariant := ctx.ModuleForTests(t, "foo", buildOS+"_common")
 			fooMod := fooVariant.Module().(*TestHost)
 			entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, fooMod)[0]
 
@@ -1907,7 +1947,7 @@
 
 			expectedData := []string{}
 			for _, variant := range tc.variants {
-				barVariant := ctx.ModuleForTests("bar", variant)
+				barVariant := ctx.ModuleForTests(t, "bar", variant)
 				relocated := barVariant.Output("bar")
 				expectedInput := fmt.Sprintf("out/soong/.intermediates/bar/%s/unstripped/bar", variant)
 				android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input)
@@ -1916,12 +1956,13 @@
 			}
 
 			actualData := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
-			android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", ctx.Config, expectedData, actualData)
+			android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", ctx.Config, android.SortedUniqueStrings(expectedData), android.SortedUniqueStrings(actualData))
 		})
 	}
 }
 
 func TestDeviceBinaryWrapperGeneration(t *testing.T) {
+	t.Parallel()
 	// Scenario 1: java_binary has main_class property in its bp
 	ctx, _ := testJava(t, `
 		java_binary {
@@ -1930,7 +1971,7 @@
 			main_class: "foo.bar.jb",
 		}
 	`)
-	wrapperPath := fmt.Sprint(ctx.ModuleForTests("foo", "android_common").AllOutputs())
+	wrapperPath := fmt.Sprint(ctx.ModuleForTests(t, "foo", "android_common").AllOutputs())
 	if !strings.Contains(wrapperPath, "foo.sh") {
 		t.Errorf("wrapper file foo.sh is not generated")
 	}
@@ -1945,6 +1986,7 @@
 }
 
 func TestJavaApiContributionEmptyApiFile(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureMergeEnv(
@@ -1968,6 +2010,7 @@
 }
 
 func TestJavaApiLibraryAndProviderLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2024,7 +2067,7 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		manifest := m.Output("metalava.sbox.textproto")
 		sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
 		manifestCommand := sboxProto.Commands[0].GetCommand()
@@ -2034,6 +2077,7 @@
 }
 
 func TestJavaApiLibraryAndDefaultsLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2132,7 +2176,7 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		manifest := m.Output("metalava.sbox.textproto")
 		sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
 		manifestCommand := sboxProto.Commands[0].GetCommand()
@@ -2142,6 +2186,7 @@
 }
 
 func TestJavaApiLibraryJarGeneration(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2199,7 +2244,7 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		outputs := fmt.Sprint(m.AllOutputs())
 		if !strings.Contains(outputs, c.outputJarName) {
 			t.Errorf("Module output does not contain expected jar %s", c.outputJarName)
@@ -2208,6 +2253,7 @@
 }
 
 func TestJavaApiLibraryLibsLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2284,7 +2330,7 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		javacRules := m.Rule("javac")
 		classPathArgs := javacRules.Args["classpath"]
 		for _, jarName := range c.classPathJarNames {
@@ -2296,6 +2342,7 @@
 }
 
 func TestJavaApiLibraryStaticLibsLink(t *testing.T) {
+	t.Parallel()
 	provider_bp_a := `
 	java_api_contribution {
 		name: "foo1",
@@ -2372,7 +2419,7 @@
 		},
 	}
 	for _, c := range testcases {
-		m := ctx.ModuleForTests(c.moduleName, "android_common")
+		m := ctx.ModuleForTests(t, c.moduleName, "android_common")
 		mergeZipsCommand := m.Rule("merge_zips").RuleParams.Command
 		for _, jarName := range c.staticLibJarNames {
 			if !strings.Contains(mergeZipsCommand, jarName) {
@@ -2383,6 +2430,7 @@
 }
 
 func TestTransitiveSrcFiles(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "a",
@@ -2399,13 +2447,14 @@
 			static_libs: ["b"],
 		}
 	`)
-	c := ctx.ModuleForTests("c", "android_common").Module()
+	c := ctx.ModuleForTests(t, "c", "android_common").Module()
 	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())
 }
 
 func TestTradefedOptions(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
 java_test_host {
 	name: "foo",
@@ -2421,7 +2470,7 @@
 `)
 
 	buildOS := result.Config.BuildOS.String()
-	args := result.ModuleForTests("foo", buildOS+"_common").
+	args := result.ModuleForTests(t, "foo", buildOS+"_common").
 		Output("out/soong/.intermediates/foo/" + buildOS + "_common/foo.config").Args
 	expected := proptools.NinjaAndShellEscape("<option name=\"exclude-path\" value=\"org/apache\" />")
 	if args["extraConfigs"] != expected {
@@ -2430,6 +2479,7 @@
 }
 
 func TestTestRunnerOptions(t *testing.T) {
+	t.Parallel()
 	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
 java_test_host {
 	name: "foo",
@@ -2445,7 +2495,7 @@
 `)
 
 	buildOS := result.Config.BuildOS.String()
-	args := result.ModuleForTests("foo", buildOS+"_common").
+	args := result.ModuleForTests(t, "foo", buildOS+"_common").
 		Output("out/soong/.intermediates/foo/" + buildOS + "_common/foo.config").Args
 	expected := proptools.NinjaAndShellEscape("<option name=\"test-timeout\" value=\"10m\" />\\n        ")
 	if args["extraTestRunnerConfigs"] != expected {
@@ -2454,6 +2504,7 @@
 }
 
 func TestJavaLibraryWithResourcesStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
     java_library {
         name: "foo",
@@ -2465,7 +2516,7 @@
 			"test-jar/test/resource.txt": nil,
 		})
 
-	m := ctx.ModuleForTests("foo", "android_common")
+	m := ctx.ModuleForTests(t, "foo", "android_common")
 	outputs := fmt.Sprint(m.AllOutputs())
 	if !strings.Contains(outputs, "test.jar") {
 		t.Errorf("Module output does not contain expected jar %s", "test.jar")
@@ -2473,6 +2524,7 @@
 }
 
 func TestHeadersOnly(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -2481,16 +2533,17 @@
 		}
 	`)
 
-	turbine := ctx.ModuleForTests("foo", "android_common").Rule("turbine")
+	turbine := ctx.ModuleForTests(t, "foo", "android_common").Rule("turbine")
 	if len(turbine.Inputs) != 1 || turbine.Inputs[0].String() != "a.java" {
 		t.Errorf(`foo inputs %v != ["a.java"]`, turbine.Inputs)
 	}
 
-	javac := ctx.ModuleForTests("foo", "android_common").MaybeRule("javac")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").MaybeRule("javac")
 	android.AssertDeepEquals(t, "javac rule", nil, javac.Rule)
 }
 
 func TestJavaApiContributionImport(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureMergeEnv(
@@ -2510,7 +2563,7 @@
 			api_surface: "public",
 		}
 	`)
-	m := ctx.ModuleForTests("foo", "android_common")
+	m := ctx.ModuleForTests(t, "foo", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest)
 	manifestCommand := sboxProto.Commands[0].GetCommand()
@@ -2519,6 +2572,7 @@
 }
 
 func TestJavaApiLibraryApiFilesSorting(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_api_library {
 			name: "foo",
@@ -2532,7 +2586,7 @@
 			stubs_type: "everything",
 		}
 	`)
-	m := ctx.ModuleForTests("foo", "android_common")
+	m := ctx.ModuleForTests(t, "foo", "android_common")
 	manifest := m.Output("metalava.sbox.textproto")
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, manifest)
 	manifestCommand := sboxProto.Commands[0].GetCommand()
@@ -2547,6 +2601,7 @@
 }
 
 func TestSdkLibraryProvidesSystemModulesToApiLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -2576,6 +2631,7 @@
 }
 
 func TestApiLibraryDroidstubsDependency(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -2605,9 +2661,9 @@
 	`)
 
 	currentApiTimestampPath := "api-stubs-docs-non-updatable/android_common/everything/check_current_api.timestamp"
-	foo := result.ModuleForTests("foo", "android_common").Module().(*ApiLibrary)
+	foo := result.ModuleForTests(t, "foo", "android_common").Module().(*ApiLibrary)
 	fooValidationPathsString := strings.Join(foo.validationPaths.Strings(), " ")
-	bar := result.ModuleForTests("bar", "android_common").Module().(*ApiLibrary)
+	bar := result.ModuleForTests(t, "bar", "android_common").Module().(*ApiLibrary)
 	barValidationPathsString := strings.Join(bar.validationPaths.Strings(), " ")
 	android.AssertStringDoesContain(t,
 		"Module expected to have validation",
@@ -2622,6 +2678,7 @@
 }
 
 func TestDisableFromTextStubForCoverageBuild(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -2651,6 +2708,7 @@
 }
 
 func TestMultiplePrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		// an rdep
 		java_library {
@@ -2733,8 +2791,8 @@
 		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
 		// check that rdep gets the correct variation of dep
-		foo := ctx.ModuleForTests("foo", "android_common")
-		expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_common")
+		foo := ctx.ModuleForTests(t, "foo", "android_common")
+		expectedDependency := ctx.ModuleForTests(t, tc.expectedDependencyName, "android_common")
 		android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", foo.Module().Name(), tc.expectedDependencyName), true, hasDep(ctx, foo.Module(), expectedDependency.Module()))
 
 		// check that output file of dep is always bar.jar
@@ -2749,6 +2807,7 @@
 }
 
 func TestMultiplePlatformCompatConfigPrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		// multiple variations of platform_compat_config
 		// source
@@ -2802,13 +2861,14 @@
 			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")
+		mergedGlobalConfig := ctx.SingletonForTests(t, "platform_compat_config_singleton").Output("compat_config/merged_compat_config.xml")
 		android.AssertIntEquals(t, "The merged compat config file should only have a single dependency", 1, len(mergedGlobalConfig.Implicits))
 		android.AssertStringEquals(t, "The merged compat config file is missing the appropriate platform compat config", mergedGlobalConfig.Implicits[0].String(), tc.expectedPlatformCompatConfigXml)
 	}
 }
 
 func TestApiLibraryAconfigDeclarations(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -2850,14 +2910,14 @@
 	android.AssertBoolEquals(t, "foo expected to depend on bar",
 		CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true)
 
-	m := result.ModuleForTests("foo", "android_common")
+	m := result.ModuleForTests(t, "foo", "android_common")
 	android.AssertStringDoesContain(t, "foo generates revert annotations file",
-		strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt")
+		strings.Join(m.AllOutputs(), ""), "flags-config-exportable.xml")
 
 	// revert-annotations.txt passed to exportable stubs generation metalava command
 	manifest := m.Output("metalava.sbox.textproto")
 	cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command)
-	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt")
+	android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "flags-config-exportable.xml")
 }
 
 func TestTestOnly(t *testing.T) {
@@ -2919,6 +2979,7 @@
 
 // Don't allow setting test-only on things that are always tests or never tests.
 func TestInvalidTestOnlyTargets(t *testing.T) {
+	t.Parallel()
 	testCases := []string{
 		` java_test {  name: "java-test", test_only: true, srcs: ["foo.java"],  } `,
 		` java_test_host {  name: "java-test-host", test_only: true, srcs: ["foo.java"],  } `,
@@ -2954,6 +3015,7 @@
 }
 
 func TestJavaLibHostWithStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library_host {
 			name: "foo",
@@ -2963,7 +3025,7 @@
 	`)
 
 	buildOS := ctx.Config().BuildOS.String()
-	foo := ctx.ModuleForTests("foo", buildOS+"_common")
+	foo := ctx.ModuleForTests(t, "foo", buildOS+"_common")
 
 	outputs := fmt.Sprint(foo.AllOutputs())
 	if !strings.Contains(outputs, "foo-new.jar") {
@@ -2972,6 +3034,7 @@
 }
 
 func TestJavaLibWithStem(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -2980,7 +3043,7 @@
 		}
 	`)
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	outputs := fmt.Sprint(foo.AllOutputs())
 	if !strings.Contains(outputs, "foo-new.jar") {
@@ -2989,6 +3052,7 @@
 }
 
 func TestJavaLibraryOutputFilesRel(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -3010,9 +3074,9 @@
 		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
-	bar := result.ModuleForTests("bar", "android_common")
-	baz := result.ModuleForTests("baz", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
+	bar := result.ModuleForTests(t, "bar", "android_common")
+	baz := result.ModuleForTests(t, "baz", "android_common")
 
 	fooOutputPaths := foo.OutputFiles(result.TestContext, t, "")
 	barOutputPaths := bar.OutputFiles(result.TestContext, t, "")
@@ -3034,10 +3098,10 @@
 }
 
 func TestCoverage(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		prepareForTestWithFrameworkJacocoInstrumentation,
-		PrepareForTestWithTransitiveClasspathEnabled,
 	).RunTestWithBp(t, `
 		android_app {
 			name: "foo",
@@ -3054,8 +3118,8 @@
 		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
-	androidCar := result.ModuleForTests("android.car", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
+	androidCar := result.ModuleForTests(t, "android.car", "android_common")
 
 	fooJacoco := foo.Rule("jacoco")
 	fooCombine := foo.Description("for javac")
@@ -3104,6 +3168,7 @@
 
 // Test that a dependency edge is created to the matching variant of a native library listed in `jni_libs` of java_binary
 func TestNativeRequiredDepOfJavaBinary(t *testing.T) {
+	t.Parallel()
 	findDepsOfModule := func(ctx *android.TestContext, module android.Module, depName string) []blueprint.Module {
 		var ret []blueprint.Module
 		ctx.VisitDirectDeps(module, func(dep blueprint.Module) {
@@ -3125,6 +3190,75 @@
 }
 `
 	res, _ := testJava(t, bp)
-	deps := findDepsOfModule(res, res.ModuleForTests("myjavabin", "android_common").Module(), "mynativelib")
+	deps := findDepsOfModule(res, res.ModuleForTests(t, "myjavabin", "android_common").Module(), "mynativelib")
 	android.AssertIntEquals(t, "Create a dep on the first variant", 1, len(deps))
 }
+
+func TestBootJarNotInUsesLibs(t *testing.T) {
+	t.Parallel()
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary"),
+		FixtureConfigureApexBootJars("myapex:mysdklibrary"),
+	).RunTestWithBp(t, `
+		bootclasspath_fragment {
+			name: "myfragment",
+			contents: ["mysdklibrary"],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		java_sdk_library {
+			name: "mysdklibrary",
+			srcs: ["Test.java"],
+			compile_dex: true,
+			public: {enabled: true},
+			min_sdk_version: "2",
+			permitted_packages: ["mysdklibrary"],
+			sdk_version: "current",
+		}
+
+		java_sdk_library {
+			name: "myothersdklibrary",
+			srcs: ["Test.java"],
+			compile_dex: true,
+			public: {enabled: true},
+			min_sdk_version: "2",
+			permitted_packages: ["myothersdklibrary"],
+			sdk_version: "current",
+		}
+
+		java_library {
+			name: "foo",
+			libs: [
+				"bar",
+				"mysdklibrary.stubs",
+			],
+			srcs: ["A.java"],
+		}
+
+		java_library {
+			name: "bar",
+			libs: [
+				"myothersdklibrary.stubs"
+			],
+		}
+	`)
+	ctx := result.TestContext
+	fooModule := ctx.ModuleForTests(t, "foo", "android_common")
+
+	androidMkEntries := android.AndroidMkEntriesForTest(t, ctx, fooModule.Module())[0]
+	localExportSdkLibraries := androidMkEntries.EntryMap["LOCAL_EXPORT_SDK_LIBRARIES"]
+	android.AssertStringListDoesNotContain(t,
+		"boot jar should not be included in uses libs entries",
+		localExportSdkLibraries,
+		"mysdklibrary",
+	)
+	android.AssertStringListContains(t,
+		"non boot jar is included in uses libs entries",
+		localExportSdkLibraries,
+		"myothersdklibrary",
+	)
+}
diff --git a/java/jdeps.go b/java/jdeps.go
index c2ce503..56142c8 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -37,8 +37,6 @@
 	outputPath android.Path
 }
 
-var _ android.SingletonMakeVarsProvider = (*jdepsGeneratorSingleton)(nil)
-
 const (
 	jdepsJsonFileName = "module_bp_java_deps.json"
 )
@@ -47,13 +45,13 @@
 	// (b/204397180) Generate module_bp_java_deps.json by default.
 	moduleInfos := make(map[string]android.IdeInfo)
 
-	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled(ctx) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
 			return
 		}
 
 		// Prevent including both prebuilts and matching source modules when one replaces the other.
-		if !android.IsModulePreferred(module) {
+		if !android.IsModulePreferredProxy(ctx, module) {
 			return
 		}
 
@@ -62,9 +60,11 @@
 			return
 		}
 		name := ideInfoProvider.BaseModuleName
-		ideModuleNameProvider, ok := module.(android.IDECustomizedModuleName)
-		if ok {
-			name = ideModuleNameProvider.IDECustomizedModuleName()
+		if info, ok := android.OtherModuleProvider(ctx, module, JavaLibraryInfoProvider); ok && info.Prebuilt {
+			// TODO(b/113562217): Extract the base module name from the Import name, often the Import name
+			// has a prefix "prebuilt_". Remove the prefix explicitly if needed until we find a better
+			// solution to get the Import name.
+			name = android.RemoveOptionalPrebuiltPrefix(module.Name())
 		}
 
 		dpInfo := moduleInfos[name]
@@ -72,13 +72,12 @@
 		dpInfo.Paths = []string{ctx.ModuleDir(module)}
 		moduleInfos[name] = dpInfo
 
-		mkProvider, ok := module.(android.AndroidMkDataProvider)
+		mkProvider, ok := android.OtherModuleProvider(ctx, module, android.AndroidMkDataInfoProvider)
 		if !ok {
 			return
 		}
-		data := mkProvider.AndroidMk()
-		if data.Class != "" {
-			dpInfo.Classes = append(dpInfo.Classes, data.Class)
+		if mkProvider.Class != "" {
+			dpInfo.Classes = append(dpInfo.Classes, mkProvider.Class)
 		}
 
 		if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
@@ -101,14 +100,7 @@
 		Rule:   android.Touch,
 		Output: jfpath,
 	})
-}
-
-func (j *jdepsGeneratorSingleton) MakeVars(ctx android.MakeVarsContext) {
-	if j.outputPath == nil {
-		return
-	}
-
-	ctx.DistForGoal("general-tests", j.outputPath)
+	ctx.DistForGoals([]string{"general-tests", "dist_files"}, j.outputPath)
 }
 
 func createJsonFile(moduleInfos map[string]android.IdeInfo, jfpath android.WritablePath) error {
diff --git a/java/jdeps_test.go b/java/jdeps_test.go
index 1435000..00f3839 100644
--- a/java/jdeps_test.go
+++ b/java/jdeps_test.go
@@ -22,6 +22,7 @@
 )
 
 func TestCollectJavaLibraryPropertiesAddLibsDeps(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {name: "Foo"}
@@ -31,7 +32,7 @@
 			libs: ["Foo", "Bar"],
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	for _, expected := range []string{"Foo", "Bar"} {
@@ -42,6 +43,7 @@
 }
 
 func TestCollectJavaLibraryPropertiesAddStaticLibsDeps(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {name: "Foo"}
@@ -51,7 +53,7 @@
 			static_libs: ["Foo", "Bar"],
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	for _, expected := range []string{"Foo", "Bar"} {
@@ -62,6 +64,7 @@
 }
 
 func TestCollectJavaLibraryPropertiesAddScrs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {
@@ -69,7 +72,7 @@
 			srcs: ["Foo.java", "Bar.java"],
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	expected := []string{"Foo.java", "Bar.java"}
@@ -79,6 +82,7 @@
 }
 
 func TestCollectJavaLibraryPropertiesAddAidlIncludeDirs(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {
@@ -88,7 +92,7 @@
 			},
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	expected := []string{"Foo", "Bar"}
@@ -98,6 +102,7 @@
 }
 
 func TestCollectJavaLibraryWithJarJarRules(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t,
 		`
 		java_library {
@@ -106,7 +111,7 @@
 			jarjar_rules: "jarjar_rules.txt",
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	android.AssertStringEquals(t, "IdeInfo.Srcs of repackaged library should not be empty", "foo.java", dpInfo.Srcs[0])
@@ -117,6 +122,7 @@
 }
 
 func TestCollectJavaLibraryLinkingAgainstVersionedSdk(t *testing.T) {
+	t.Parallel()
 	ctx := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		FixtureWithPrebuiltApis(map[string][]string{
@@ -129,7 +135,7 @@
 			sdk_version: "29",
 		}
 	`)
-	module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	module := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, module, android.IdeInfoProviderKey)
 
 	android.AssertStringListContains(t, "IdeInfo.Deps should contain versioned sdk module", dpInfo.Deps, "sdk_public_29_android")
@@ -165,11 +171,11 @@
 			api_surface: "public",
 		}
 	`)
-	javalib := ctx.ModuleForTests("javalib", "android_common").Module().(*Library)
+	javalib := ctx.ModuleForTests(t, "javalib", "android_common").Module().(*Library)
 	dpInfo, _ := android.OtherModuleProvider(ctx, javalib, android.IdeInfoProviderKey)
 	android.AssertStringListDoesNotContain(t, "IdeInfo.Deps should contain not contain `none`", dpInfo.Deps, "none")
 
-	javalib_stubs := ctx.ModuleForTests("javalib.stubs", "android_common").Module().(*ApiLibrary)
+	javalib_stubs := ctx.ModuleForTests(t, "javalib.stubs", "android_common").Module().(*ApiLibrary)
 	dpInfo, _ = android.OtherModuleProvider(ctx, javalib_stubs, android.IdeInfoProviderKey)
 	android.AssertStringListDoesNotContain(t, "IdeInfo.Deps should contain not contain `none`", dpInfo.Deps, "none")
 }
diff --git a/java/kotlin.go b/java/kotlin.go
index f42d163..308bb03 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -181,6 +181,7 @@
 		Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && ` +
 			`mkdir -p "$srcJarDir" "$kaptDir/sources" "$kaptDir/classes" && ` +
 			`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
+			`${config.FindInputDeltaCmd} --template '' --target "$out" --inputs_file "$out.rsp" && ` +
 			`${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` +
 			` --srcs "$out.rsp" --srcs "$srcJarDir/list"` +
 			` $commonSrcFilesArg --out "$kotlinBuildFile" && ` +
@@ -197,8 +198,10 @@
 			`$kaptProcessor ` +
 			`-Xbuild-file=$kotlinBuildFile && ` +
 			`${config.SoongZipCmd} -jar -write_if_changed -o $out -C $kaptDir/stubs -D $kaptDir/stubs && ` +
+			`if [ -f "$out.pc_state.new" ]; then mv "$out.pc_state.new" "$out.pc_state"; fi && ` +
 			`rm -rf "$srcJarDir"`,
 		CommandDeps: []string{
+			"${config.FindInputDeltaCmd}",
 			"${config.KotlincCmd}",
 			"${config.KotlinCompilerJar}",
 			"${config.KotlinKaptJar}",
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 45eac01..4b56cff 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -24,6 +24,7 @@
 )
 
 func TestKotlin(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -49,13 +50,6 @@
 			srcs: ["d.kt"],
 		}`
 
-	kotlinStdlibTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/turbine-combined/kotlin-stdlib.jar",
-		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/turbine-combined/kotlin-stdlib-jdk7.jar",
-		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk8/android_common/turbine-combined/kotlin-stdlib-jdk8.jar",
-		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/turbine-combined/kotlin-annotations.jar",
-	}
-
 	kotlinStdlibTurbineJars := []string{
 		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/turbine/kotlin-stdlib.jar",
 		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/turbine/kotlin-stdlib-jdk7.jar",
@@ -70,21 +64,11 @@
 		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/javac/kotlin-annotations.jar",
 	}
 
-	bootclasspathTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/stable.core.platform.api.stubs/android_common/turbine-combined/stable.core.platform.api.stubs.jar",
-		"out/soong/.intermediates/default/java/core-lambda-stubs/android_common/turbine-combined/core-lambda-stubs.jar",
-	}
-
 	bootclasspathTurbineJars := []string{
 		"out/soong/.intermediates/default/java/stable.core.platform.api.stubs/android_common/turbine/stable.core.platform.api.stubs.jar",
 		"out/soong/.intermediates/default/java/core-lambda-stubs/android_common/turbine/core-lambda-stubs.jar",
 	}
 
-	frameworkTurbineCombinedJars := []string{
-		"out/soong/.intermediates/default/java/ext/android_common/turbine-combined/ext.jar",
-		"out/soong/.intermediates/default/java/framework/android_common/turbine-combined/framework.jar",
-	}
-
 	frameworkTurbineJars := []string{
 		"out/soong/.intermediates/default/java/ext/android_common/turbine/ext.jar",
 		"out/soong/.intermediates/default/java/framework/android_common/turbine/framework.jar",
@@ -108,68 +92,8 @@
 		barHeaderCombinedInputs []string
 	}{
 		{
-			name:             "normal",
-			preparer:         android.NullFixturePreparer,
-			fooKotlincInputs: []string{"a.java", "b.kt"},
-			fooJavacInputs:   []string{"a.java"},
-			fooKotlincClasspath: slices.Concat(
-				bootclasspathTurbineCombinedJars,
-				frameworkTurbineCombinedJars,
-				[]string{"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar"},
-				kotlinStdlibTurbineCombinedJars,
-			),
-			fooJavacClasspath: slices.Concat(
-				[]string{"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar"},
-				frameworkTurbineCombinedJars,
-				[]string{"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar"},
-				kotlinStdlibTurbineCombinedJars,
-			),
-			fooCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/foo/android_common/kotlin/foo.jar",
-					"out/soong/.intermediates/foo/android_common/javac/foo.jar",
-					"out/soong/.intermediates/quz/android_common/combined/quz.jar",
-				},
-				kotlinStdlibJavacJars,
-			),
-			fooHeaderCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
-					"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar",
-					"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
-				},
-				kotlinStdlibTurbineCombinedJars,
-			),
-
-			barKotlincInputs: []string{"b.kt"},
-			barKotlincClasspath: slices.Concat(
-				bootclasspathTurbineCombinedJars,
-				frameworkTurbineCombinedJars,
-				[]string{
-					"out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar",
-					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-				},
-				kotlinStdlibTurbineCombinedJars,
-			),
-			barCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/bar/android_common/kotlin/bar.jar",
-					"out/soong/.intermediates/baz/android_common/combined/baz.jar",
-				},
-				kotlinStdlibJavacJars,
-				[]string{},
-			),
-			barHeaderCombinedInputs: slices.Concat(
-				[]string{
-					"out/soong/.intermediates/bar/android_common/kotlin_headers/bar.jar",
-					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
-				},
-				kotlinStdlibTurbineCombinedJars,
-			),
-		},
-		{
 			name:             "transitive classpath",
-			preparer:         PrepareForTestWithTransitiveClasspathEnabled,
+			preparer:         android.NullFixturePreparer,
 			fooKotlincInputs: []string{"a.java", "b.kt"},
 			fooJavacInputs:   []string{"a.java"},
 			fooKotlincClasspath: slices.Concat(
@@ -234,11 +158,12 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
 			result := android.GroupFixturePreparers(
 				PrepareForTestWithJavaDefaultModules,
 				tt.preparer,
 			).RunTestWithBp(t, bp)
-			foo := result.ModuleForTests("foo", "android_common")
+			foo := result.ModuleForTests(t, "foo", "android_common")
 			fooKotlinc := foo.Rule("kotlinc")
 			android.AssertPathsRelativeToTopEquals(t, "foo kotlinc inputs", tt.fooKotlincInputs, fooKotlinc.Inputs)
 
@@ -258,7 +183,7 @@
 			fooCombinedHeaderJar := foo.Output("turbine-combined/foo.jar")
 			android.AssertPathsRelativeToTopEquals(t, "foo header combined inputs", tt.fooHeaderCombinedInputs, fooCombinedHeaderJar.Inputs)
 
-			bar := result.ModuleForTests("bar", "android_common")
+			bar := result.ModuleForTests(t, "bar", "android_common")
 			barKotlinc := bar.Rule("kotlinc")
 			android.AssertPathsRelativeToTopEquals(t, "bar kotlinc inputs", tt.barKotlincInputs, barKotlinc.Inputs)
 
@@ -275,6 +200,7 @@
 }
 
 func TestKapt(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -303,18 +229,19 @@
 		}
 	`
 	t.Run("", func(t *testing.T) {
+		t.Parallel()
 		ctx, _ := testJava(t, bp)
 
 		buildOS := ctx.Config().BuildOS.String()
 
-		foo := ctx.ModuleForTests("foo", "android_common")
+		foo := ctx.ModuleForTests(t, "foo", "android_common")
 		kaptStubs := foo.Rule("kapt")
 		turbineApt := foo.Description("turbine apt")
 		kotlinc := foo.Rule("kotlinc")
 		javac := foo.Rule("javac")
 
-		bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
-		baz := ctx.ModuleForTests("baz", buildOS+"_common").Rule("javac").Output.String()
+		bar := ctx.ModuleForTests(t, "bar", buildOS+"_common").Rule("javac").Output.String()
+		baz := ctx.ModuleForTests(t, "baz", buildOS+"_common").Rule("javac").Output.String()
 
 		// Test that the kotlin and java sources are passed to kapt and kotlinc
 		if len(kaptStubs.Inputs) != 2 || kaptStubs.Inputs[0].String() != "a.java" || kaptStubs.Inputs[1].String() != "b.kt" {
@@ -384,6 +311,7 @@
 	})
 
 	t.Run("errorprone", func(t *testing.T) {
+		t.Parallel()
 		env := map[string]string{
 			"RUN_ERROR_PRONE": "true",
 		}
@@ -395,13 +323,13 @@
 
 		buildOS := result.Config.BuildOS.String()
 
-		kapt := result.ModuleForTests("foo", "android_common").Rule("kapt")
-		javac := result.ModuleForTests("foo", "android_common").Description("javac")
-		errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone")
+		kapt := result.ModuleForTests(t, "foo", "android_common").Rule("kapt")
+		javac := result.ModuleForTests(t, "foo", "android_common").Description("javac")
+		errorprone := result.ModuleForTests(t, "foo", "android_common").Description("errorprone")
 
-		bar := result.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String()
-		baz := result.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String()
-		myCheck := result.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String()
+		bar := result.ModuleForTests(t, "bar", buildOS+"_common").Description("javac").Output.String()
+		baz := result.ModuleForTests(t, "baz", buildOS+"_common").Description("javac").Output.String()
+		myCheck := result.ModuleForTests(t, "my_check", buildOS+"_common").Description("javac").Output.String()
 
 		// Test that the errorprone plugins are not passed to kapt
 		expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
@@ -434,6 +362,7 @@
 }
 
 func TestKaptEncodeFlags(t *testing.T) {
+	t.Parallel()
 	// Compares the kaptEncodeFlags against the results of the example implementation at
 	// https://kotlinlang.org/docs/reference/kapt.html#apjavac-options-encoding
 	tests := []struct {
@@ -484,6 +413,7 @@
 
 	for i, test := range tests {
 		t.Run(strconv.Itoa(i), func(t *testing.T) {
+			t.Parallel()
 			got := kaptEncodeFlags(test.in)
 			if got != test.out {
 				t.Errorf("\nwant %q\n got %q", test.out, got)
@@ -493,6 +423,7 @@
 }
 
 func TestKotlinCompose(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -501,7 +432,7 @@
 		}
 
 		kotlin_plugin {
-			name: "androidx.compose.compiler_compiler-hosted-plugin",
+			name: "kotlin-compose-compiler-plugin",
 		}
 
 		java_library {
@@ -523,9 +454,9 @@
 
 	buildOS := result.Config.BuildOS.String()
 
-	composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted-plugin", buildOS+"_common").Rule("combineJar").Output
-	withCompose := result.ModuleForTests("withcompose", "android_common")
-	noCompose := result.ModuleForTests("nocompose", "android_common")
+	composeCompiler := result.ModuleForTests(t, "kotlin-compose-compiler-plugin", buildOS+"_common").Rule("combineJar").Output
+	withCompose := result.ModuleForTests(t, "withcompose", "android_common")
+	noCompose := result.ModuleForTests(t, "nocompose", "android_common")
 
 	android.AssertStringListContains(t, "missing compose compiler dependency",
 		withCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String())
@@ -544,6 +475,7 @@
 }
 
 func TestKotlinPlugin(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 	).RunTestWithBp(t, `
@@ -570,9 +502,9 @@
 
 	buildOS := result.Config.BuildOS.String()
 
-	kotlinPlugin := result.ModuleForTests("kotlin_plugin", buildOS+"_common").Rule("combineJar").Output
-	withKotlinPlugin := result.ModuleForTests("with_kotlin_plugin", "android_common")
-	noKotlinPlugin := result.ModuleForTests("no_kotlin_plugin", "android_common")
+	kotlinPlugin := result.ModuleForTests(t, "kotlin_plugin", buildOS+"_common").Rule("combineJar").Output
+	withKotlinPlugin := result.ModuleForTests(t, "with_kotlin_plugin", "android_common")
+	noKotlinPlugin := result.ModuleForTests(t, "no_kotlin_plugin", "android_common")
 
 	android.AssertStringListContains(t, "missing plugin compiler dependency",
 		withKotlinPlugin.Rule("kotlinc").Implicits.Strings(), kotlinPlugin.String())
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 4be7d04..21895d4 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -47,8 +47,8 @@
 	"sam",
 	"saminterfacelibrary",
 	"sammanagerlibrary",
-	"services",
 	"services.core.unboosted",
+	"services.impl",
 	"Settings-core",
 	"SettingsGoogle",
 	"SettingsGoogleOverlayCoral",
diff --git a/java/lint.go b/java/lint.go
index ac90e19..dc1e51f 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -69,6 +69,11 @@
 		// If soong gets support for testonly, this flag should be replaced with that.
 		Test *bool
 
+		// Same as the regular Test property, but set by internal soong code based on if the module
+		// type is a test module type. This will act as the default value for the test property,
+		// but can be overridden by the user.
+		Test_module_type *bool `blueprint:"mutated"`
+
 		// Whether to ignore the exit code of Android lint. This is the --exit_code
 		// option. Defaults to false.
 		Suppress_exit_code *bool
@@ -257,7 +262,12 @@
 	if l.library {
 		cmd.Flag("--library")
 	}
-	if proptools.BoolDefault(l.properties.Lint.Test, false) {
+
+	test := proptools.BoolDefault(l.properties.Lint.Test_module_type, false)
+	if l.properties.Lint.Test != nil {
+		test = *l.properties.Lint.Test
+	}
+	if test {
 		cmd.Flag("--test")
 	}
 	if l.manifest != nil {
@@ -388,7 +398,7 @@
 		}
 	}
 
-	extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
+	extraLintCheckModules := ctx.GetDirectDepsProxyWithTag(extraLintCheckTag)
 	for _, extraLintCheckModule := range extraLintCheckModules {
 		if dep, ok := android.OtherModuleProvider(ctx, extraLintCheckModule, JavaInfoProvider); ok {
 			l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
@@ -413,7 +423,7 @@
 
 	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml, baseline)
 
-	ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
+	ctx.VisitDirectDepsProxyWithTag(staticLibTag, func(dep android.ModuleProxy) {
 		if info, ok := android.OtherModuleProvider(ctx, dep, LintProvider); ok {
 			depSetsBuilder.Transitive(info)
 		}
@@ -470,7 +480,7 @@
 
 	cmd := rule.Command()
 
-	cmd.Flag(`JAVA_OPTS="-Xmx3072m --add-opens java.base/java.util=ALL-UNNAMED"`).
+	cmd.Flag(`JAVA_OPTS="-Xmx4096m --add-opens java.base/java.util=ALL-UNNAMED"`).
 		FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
 		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
 		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
@@ -585,12 +595,12 @@
 	l.copyLintDependencies(ctx)
 }
 
-func findModuleOrErr(ctx android.SingletonContext, moduleName string) android.Module {
-	var res android.Module
-	ctx.VisitAllModules(func(m android.Module) {
+func findModuleOrErr(ctx android.SingletonContext, moduleName string) *android.ModuleProxy {
+	var res *android.ModuleProxy
+	ctx.VisitAllModuleProxies(func(m android.ModuleProxy) {
 		if ctx.ModuleName(m) == moduleName {
 			if res == nil {
-				res = m
+				res = &m
 			} else {
 				ctx.Errorf("lint: multiple %s modules found: %s and %s", moduleName,
 					ctx.ModuleSubDir(m), ctx.ModuleSubDir(res))
@@ -625,13 +635,13 @@
 
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.CpIfChanged,
-			Input:  android.OutputFileForModule(ctx, sdkAnnotations, ""),
+			Input:  android.OutputFileForModule(ctx, *sdkAnnotations, ""),
 			Output: copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName),
 		})
 
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.CpIfChanged,
-			Input:  android.OutputFileForModule(ctx, apiVersionsDb, ".api_versions.xml"),
+			Input:  android.OutputFileForModule(ctx, *apiVersionsDb, ".api_versions.xml"),
 			Output: copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName),
 		})
 	}
@@ -648,12 +658,13 @@
 
 	var outputs []*LintInfo
 	var dirs []string
-	ctx.VisitAllModules(func(m android.Module) {
-		if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
+	ctx.VisitAllModuleProxies(func(m android.ModuleProxy) {
+		commonInfo := android.OtherModulePointerProviderOrDefault(ctx, m, android.CommonModuleInfoProvider)
+		if ctx.Config().KatiEnabled() && !commonInfo.ExportedToMake {
 			return
 		}
 
-		if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
+		if commonInfo.IsApexModule && commonInfo.NotAvailableForPlatform {
 			apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider)
 			if apexInfo.IsForPlatform() {
 				// There are stray platform variants of modules in apexes that are not available for
@@ -694,16 +705,12 @@
 	zip(l.referenceBaselineZip, func(l *LintInfo) android.Path { return l.ReferenceBaseline })
 
 	ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
-}
 
-func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
 	if !ctx.Config().UnbundledBuild() {
 		ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
 	}
 }
 
-var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
-
 func init() {
 	android.RegisterParallelSingletonType("lint",
 		func() android.Singleton { return &lintSingleton{} })
diff --git a/java/lint_test.go b/java/lint_test.go
index afe3914..236fa63 100644
--- a/java/lint_test.go
+++ b/java/lint_test.go
@@ -22,6 +22,7 @@
 )
 
 func TestJavaLintDoesntUseBaselineImplicitly(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -37,7 +38,7 @@
 		"lint-baseline.xml": nil,
 	})
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
 	if strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml") {
@@ -46,6 +47,7 @@
 }
 
 func TestJavaLintRequiresCustomLintFileToExist(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestDisallowNonExistentPaths,
@@ -65,6 +67,7 @@
 }
 
 func TestJavaLintUsesCorrectBpConfig(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJavaWithFS(t, `
 		java_library {
 			name: "foo",
@@ -84,7 +87,7 @@
 		"mybaseline.xml": nil,
 	})
 
-	foo := ctx.ModuleForTests("foo", "android_common")
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
 
 	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
 	if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline mybaseline.xml") {
@@ -101,6 +104,7 @@
 }
 
 func TestJavaLintBypassUpdatableChecks(t *testing.T) {
+	t.Parallel()
 	testCases := []struct {
 		name  string
 		bp    string
@@ -144,6 +148,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
+			t.Parallel()
 			errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.error)
 			android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules).
 				ExtendWithErrorHandler(errorHandler).
@@ -153,6 +158,7 @@
 }
 
 func TestJavaLintStrictUpdatabilityLinting(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "foo",
@@ -187,7 +193,7 @@
 	result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()).
 		RunTestWithBp(t, bp)
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 	strictUpdatabilityCheck := foo.Output("lint_strict_updatability_check.stamp")
 	if !strings.Contains(strictUpdatabilityCheck.RuleParams.Command,
 		"--disallowed_issues NewApi") {
@@ -250,7 +256,7 @@
 		})).
 			RunTestWithBp(t, thisBp)
 
-		foo := result.ModuleForTests("foo", "android_common")
+		foo := result.ModuleForTests(t, "foo", "android_common")
 		sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, foo.Output("lint.sbox.textproto"))
 		if !strings.Contains(*sboxProto.Commands[0].Command, "/"+testCase.expected_file) {
 			t.Error("did not use full api database for case", testCase)
@@ -276,3 +282,50 @@
 		ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("Don't use --disable, --enable, or --check in the flags field, instead use the dedicated disabled_checks, warning_checks, error_checks, or fatal_checks fields")).
 		RunTestWithBp(t, bp)
 }
+
+// b/358643466
+func TestNotTestViaDefault(t *testing.T) {
+	bp := `
+		java_defaults {
+			name: "mydefaults",
+			lint: {
+				test: false,
+			},
+		}
+		android_test {
+			name: "foo",
+			srcs: [
+				"a.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "current",
+			defaults: ["mydefaults"],
+		}
+		android_test {
+			name: "foo2",
+			srcs: [
+				"a.java",
+			],
+			min_sdk_version: "29",
+			sdk_version: "current",
+		}
+	`
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, bp)
+	ctx := result.TestContext
+
+	foo := ctx.ModuleForTests(t, "foo", "android_common")
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto"))
+	command := *sboxProto.Commands[0].Command
+
+	if strings.Contains(command, "--test") {
+		t.Fatalf("Expected command to not contain --test")
+	}
+
+	foo2 := ctx.ModuleForTests(t, "foo2", "android_common")
+	sboxProto2 := android.RuleBuilderSboxProtoForTests(t, ctx, foo2.Output("lint.sbox.textproto"))
+	command2 := *sboxProto2.Commands[0].Command
+
+	if !strings.Contains(command2, "--test") {
+		t.Fatalf("Expected command to contain --test")
+	}
+}
diff --git a/java/metalava/main-config.xml b/java/metalava/main-config.xml
index c61196f..c2881ac 100644
--- a/java/metalava/main-config.xml
+++ b/java/metalava/main-config.xml
@@ -16,4 +16,20 @@
 
 <config xmlns="http://www.google.com/tools/metalava/config"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://www.google.com/tools/metalava/config ../../../../tools/metalava/metalava/src/main/resources/schemas/config.xsd"/>
+    xsi:schemaLocation="http://www.google.com/tools/metalava/config ../../../../tools/metalava/metalava/src/main/resources/schemas/config.xsd">
+  <api-surfaces>
+    <!-- These are hard coded into java_sdk_library. -->
+    <api-surface name="public"/>
+    <api-surface name="system" extends="public"/>
+    <api-surface name="module-lib" extends="system"/>
+    <api-surface name="test" extends="system"/>
+    <api-surface name="system-server" extends="public"/>
+    <!-- This is used in java/core-libraries/Android.bp. -->
+    <api-surface name="core"/>
+    <!-- These are used in libcore, external/conscrypt and external/icu. -->
+    <api-surface name="core-platform" extends="public"/>
+    <api-surface name="core-platform-legacy" extends="public"/>
+    <api-surface name="core-platform-plus-public"/>
+    <api-surface name="intra-core" extends="public"/>
+  </api-surfaces>
+</config>
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index d09a02e..155afc6 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -15,6 +15,11 @@
 package java
 
 import (
+	"maps"
+	"slices"
+
+	"github.com/google/blueprint"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 )
@@ -29,12 +34,15 @@
 
 // The tags used for the dependencies between the platform bootclasspath and any configured boot
 // jars.
-var (
-	platformBootclasspathArtBootJarDepTag  = bootclasspathDependencyTag{name: "art-boot-jar"}
-	platformBootclasspathBootJarDepTag     = bootclasspathDependencyTag{name: "platform-boot-jar"}
-	platformBootclasspathApexBootJarDepTag = bootclasspathDependencyTag{name: "apex-boot-jar"}
-	platformBootclasspathImplLibDepTag     = dependencyTag{name: "impl-lib-tag"}
-)
+type platformBootclasspathImplLibDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+func (p platformBootclasspathImplLibDepTagType) ExcludeFromVisibilityEnforcement() {}
+
+var platformBootclasspathImplLibDepTag platformBootclasspathImplLibDepTagType
+
+var _ android.ExcludeFromVisibilityEnforcementTag = platformBootclasspathImplLibDepTag
 
 type platformBootclasspathModule struct {
 	android.ModuleBase
@@ -48,6 +56,12 @@
 	// The apex:module pairs obtained from the fragments.
 	fragments []android.Module
 
+	// The map of apex to the fragments they contain.
+	apexNameToFragment map[string]android.Module
+
+	// The map of library modules in the bootclasspath to the fragments that contain them.
+	libraryToApex map[android.Module]string
+
 	// Path to the monolithic hiddenapi-flags.csv file.
 	hiddenAPIFlagsCSV android.OutputPath
 
@@ -89,26 +103,12 @@
 
 	b.hiddenAPIDepsMutator(ctx)
 
-	if !dexpreopt.IsDex2oatNeeded(ctx) {
-		return
+	if dexpreopt.IsDex2oatNeeded(ctx) {
+		// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
+		// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
+		dexpreopt.RegisterToolDeps(ctx)
 	}
 
-	// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
-	// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
-	dexpreopt.RegisterToolDeps(ctx)
-}
-
-func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpMutatorContext) {
-	if ctx.Config().DisableHiddenApiChecks() {
-		return
-	}
-
-	// Add dependencies onto the stub lib modules.
-	apiLevelToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config())
-	hiddenAPIAddStubLibDependencies(ctx, apiLevelToStubLibModules)
-}
-
-func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies on all the ART jars.
 	global := dexpreopt.GetGlobalConfig(ctx)
 	addDependenciesOntoSelectedBootImageApexes(ctx, "com.android.art")
@@ -116,13 +116,13 @@
 	var bootImageModuleNames []string
 
 	// TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly
-	addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, platformBootclasspathArtBootJarDepTag)
+	addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, artBootJar)
 	bootImageModuleNames = append(bootImageModuleNames, global.ArtApexJars.CopyOfJars()...)
 
 	// Add dependencies on all the non-updatable jars, which are on the platform or in non-updatable
 	// APEXes.
 	platformJars := b.platformJars(ctx)
-	addDependenciesOntoBootImageModules(ctx, platformJars, platformBootclasspathBootJarDepTag)
+	addDependenciesOntoBootImageModules(ctx, platformJars, platformBootJar)
 	bootImageModuleNames = append(bootImageModuleNames, platformJars.CopyOfJars()...)
 
 	// Add dependencies on all the updatable jars, except the ART jars.
@@ -133,7 +133,7 @@
 	}
 	addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...)
 	// TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly
-	addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag)
+	addDependenciesOntoBootImageModules(ctx, apexJars, apexBootJar)
 	bootImageModuleNames = append(bootImageModuleNames, apexJars.CopyOfJars()...)
 
 	// Add dependencies on all the fragments.
@@ -147,32 +147,42 @@
 	}
 }
 
-func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList, tag bootclasspathDependencyTag) {
+func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpMutatorContext) {
+	if ctx.Config().DisableHiddenApiChecks() {
+		return
+	}
+
+	// Add dependencies onto the stub lib modules.
+	apiLevelToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config())
+	hiddenAPIAddStubLibDependencies(ctx, apiLevelToStubLibModules)
+}
+
+func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList, tagType bootclasspathDependencyTagType) {
 	for i := 0; i < modules.Len(); i++ {
 		apex := modules.Apex(i)
 		name := modules.Jar(i)
 
-		addDependencyOntoApexModulePair(ctx, apex, name, tag)
+		addDependencyOntoApexModulePair(ctx, apex, name, tagType)
 	}
 }
 
 func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Gather all the dependencies from the art, platform, and apex boot jars.
-	artModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathArtBootJarDepTag)
-	platformModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathBootJarDepTag)
-	apexModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathApexBootJarDepTag)
+	artModules, artModulesToApex := gatherApexModulePairDepsWithTag(ctx, artBootJar)
+	platformModules, platformModulesToApex := gatherApexModulePairDepsWithTag(ctx, platformBootJar)
+	apexModules, apexModulesToApex := gatherApexModulePairDepsWithTag(ctx, apexBootJar)
 
 	// Concatenate them all, in order as they would appear on the bootclasspath.
-	var allModules []android.Module
-	allModules = append(allModules, artModules...)
-	allModules = append(allModules, platformModules...)
-	allModules = append(allModules, apexModules...)
+	allModules := slices.Concat(artModules, platformModules, apexModules)
 	b.configuredModules = allModules
+	b.libraryToApex = maps.Clone(artModulesToApex)
+	maps.Copy(b.libraryToApex, platformModulesToApex)
+	maps.Copy(b.libraryToApex, apexModulesToApex)
 
 	// Do not add implLibModule to allModules as the impl lib is only used to collect the
 	// transitive source files
 	var implLibModule []android.Module
-	ctx.VisitDirectDepsWithTag(implLibraryTag, func(m android.Module) {
+	ctx.VisitDirectDepsWithTag(platformBootclasspathImplLibDepTag, func(m android.Module) {
 		implLibModule = append(implLibModule, m)
 	})
 
@@ -188,7 +198,7 @@
 	TransformResourcesToJar(ctx, srcjar, jarArgs, transitiveSrcFiles)
 
 	// Gather all the fragments dependencies.
-	b.fragments = gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag)
+	b.fragments, b.apexNameToFragment = gatherFragments(ctx)
 
 	// Check the configuration of the boot modules.
 	// ART modules are checked by the art-bootclasspath-fragment.
@@ -197,7 +207,7 @@
 
 	b.generateClasspathProtoBuildActions(ctx)
 
-	bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
+	bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments, b.libraryToApex, b.apexNameToFragment)
 	buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
 
 	ctx.SetOutputFiles(android.Paths{b.hiddenAPIFlagsCSV}, "hiddenapi-flags.csv")
@@ -248,7 +258,7 @@
 		fromUpdatableApex := apexInfo.Updatable
 		if fromUpdatableApex {
 			// error: this jar is part of an updatable apex
-			ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the platform bootclasspath", ctx.OtherModuleName(m), apexInfo.InApexVariants)
+			ctx.ModuleErrorf("module %q from updatable apex %q is not allowed in the platform bootclasspath", ctx.OtherModuleName(m), apexInfo.BaseApexName)
 		} else {
 			// ok: this jar is part of the platform or a non-updatable apex
 		}
@@ -272,7 +282,11 @@
 				//  modules is complete.
 				if !ctx.Config().AlwaysUsePrebuiltSdks() {
 					// error: this jar is part of the platform
-					ctx.ModuleErrorf("module %q from platform is not allowed in the apex boot jars list", name)
+					if ctx.Config().AllowMissingDependencies() {
+						ctx.AddMissingDependencies([]string{"module_" + name + "_from_platform_is_not_allowed_in_the_apex_boot_jars_list"})
+					} else {
+						ctx.ModuleErrorf("module %q from platform is not allowed in the apex boot jars list", name)
+					}
 				}
 			} else {
 				// TODO(b/177892522): Treat this as an error.
@@ -284,7 +298,8 @@
 }
 
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
-func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule {
+func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module,
+	fragments []android.Module, libraryToApex map[android.Module]string, apexNameToFragment map[string]android.Module) bootDexJarByModule {
 	createEmptyHiddenApiFiles := func() {
 		paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
 		for _, path := range paths {
@@ -311,7 +326,7 @@
 	}
 
 	// Construct a list of ClasspathElement objects from the modules and fragments.
-	classpathElements := CreateClasspathElements(ctx, modules, fragments)
+	classpathElements := CreateClasspathElements(ctx, modules, fragments, libraryToApex, apexNameToFragment)
 
 	monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, classpathElements)
 
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 7fa6ddb..727e306 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -27,21 +27,27 @@
 )
 
 func TestPlatformBootclasspath(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		FixtureConfigureBootJars("platform:foo", "system_ext:bar"),
+		android.FixtureMergeMockFs(android.MockFS{
+			"api/current.txt": nil,
+			"api/removed.txt": nil,
+		}),
 		android.FixtureWithRootAndroidBp(`
 			platform_bootclasspath {
 				name: "platform-bootclasspath",
 			}
 
-			java_library {
+			java_sdk_library {
 				name: "bar",
 				srcs: ["a.java"],
 				system_modules: "none",
 				sdk_version: "none",
 				compile_dex: true,
 				system_ext_specific: true,
+				unsafe_ignore_missing_latest_api: true,
 			}
 		`),
 	)
@@ -76,6 +82,7 @@
 	`)
 
 	t.Run("missing", func(t *testing.T) {
+		t.Parallel()
 		preparer.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"platform-bootclasspath" depends on undefined module "foo"`)).
 			RunTest(t)
@@ -86,11 +93,12 @@
 
 	checkSrcJarInputs := func(t *testing.T, result *android.TestResult, name string, expected []string) {
 		t.Helper()
-		srcjar := result.ModuleForTests(name, "android_common").Output(name + "-transitive.srcjar")
+		srcjar := result.ModuleForTests(t, name, "android_common").Output(name + "-transitive.srcjar")
 		android.AssertStringDoesContain(t, "srcjar arg", srcjar.Args["jarArgs"], "-srcjar")
 		android.AssertArrayString(t, "srcjar inputs", expected, srcjar.Implicits.Strings())
 	}
 	t.Run("source", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addSourceBootclassPathModule,
@@ -107,6 +115,7 @@
 	})
 
 	t.Run("prebuilt", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addPrebuiltBootclassPathModule,
@@ -123,6 +132,7 @@
 	})
 
 	t.Run("source+prebuilt - source preferred", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addSourceBootclassPathModule,
@@ -140,6 +150,7 @@
 	})
 
 	t.Run("source+prebuilt - prebuilt preferred", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			addSourceBootclassPathModule,
@@ -157,6 +168,7 @@
 	})
 
 	t.Run("dex import", func(t *testing.T) {
+		t.Parallel()
 		result := android.GroupFixturePreparers(
 			preparer,
 			android.FixtureAddTextFile("deximport/Android.bp", `
@@ -179,6 +191,7 @@
 }
 
 func TestPlatformBootclasspathVariant(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -193,6 +206,7 @@
 }
 
 func TestPlatformBootclasspath_ClasspathFragmentPaths(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -204,10 +218,11 @@
 
 	p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
 	android.AssertStringEquals(t, "output filepath", "bootclasspath.pb", p.ClasspathFragmentBase.outputFilepath.Base())
-	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
 }
 
 func TestPlatformBootclasspathModule_AndroidMkEntries(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -218,6 +233,7 @@
 	)
 
 	t.Run("AndroidMkEntries", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 
 		p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
@@ -227,6 +243,7 @@
 	})
 
 	t.Run("hiddenapi-flags-entry", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 
 		p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
@@ -238,6 +255,7 @@
 	})
 
 	t.Run("classpath-fragment-entry", func(t *testing.T) {
+		t.Parallel()
 		result := preparer.RunTest(t)
 
 		want := map[string][]string{
@@ -262,6 +280,7 @@
 }
 
 func TestPlatformBootclasspath_Dist(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		FixtureConfigureBootJars("platform:foo", "platform:bar"),
@@ -305,6 +324,7 @@
 }
 
 func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -347,7 +367,7 @@
 
 	// Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that
 	// creates the index.csv file.
-	platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common")
+	platformBootclasspath := result.ModuleForTests(t, "myplatform-bootclasspath", "android_common")
 
 	var rule android.TestingBuildParams
 
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 5b145c6..d2ec8bd 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -43,6 +43,13 @@
 	ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory)
 }
 
+type PlatformCompatConfigInfo struct {
+	CompatConfig android.OutputPath
+	SubDir       string
+}
+
+var PlatformCompatConfigInfoProvider = blueprint.NewProvider[PlatformCompatConfigInfo]()
+
 var PrepareForTestWithPlatformCompatConfig = android.FixtureRegisterWithContext(registerPlatformCompatConfigBuildComponents)
 
 func platformCompatConfigPath(ctx android.PathContext) android.OutputPath {
@@ -51,6 +58,10 @@
 
 type platformCompatConfigProperties struct {
 	Src *string `android:"path"`
+
+	// If true, we include it in the "merged" XML (merged_compat_config.xml).
+	// Default is true.
+	Include_in_merged_xml *bool
 }
 
 type platformCompatConfig struct {
@@ -60,6 +71,7 @@
 	installDirPath android.InstallPath
 	configFile     android.OutputPath
 	metadataFile   android.OutputPath
+	doMerge        bool
 
 	installConfigFile android.InstallPath
 }
@@ -68,6 +80,10 @@
 	return p.metadataFile
 }
 
+func (p *platformCompatConfig) includeInMergedXml() bool {
+	return p.doMerge
+}
+
 func (p *platformCompatConfig) CompatConfig() android.OutputPath {
 	return p.configFile
 }
@@ -78,8 +94,19 @@
 
 type platformCompatConfigMetadataProvider interface {
 	compatConfigMetadata() android.Path
+
+	// Whether to include it in the "merged" XML (merged_compat_config.xml) or not.
+	includeInMergedXml() bool
 }
 
+type PlatformCompatConfigMetadataInfo struct {
+	CompatConfigMetadata android.Path
+	// Whether to include it in the "merged" XML (merged_compat_config.xml) or not.
+	IncludeInMergedXml bool
+}
+
+var PlatformCompatConfigMetadataInfoProvider = blueprint.NewProvider[PlatformCompatConfigMetadataInfo]()
+
 type PlatformCompatConfigIntf interface {
 	android.Module
 
@@ -98,6 +125,7 @@
 	metadataFileName := p.Name() + "_meta.xml"
 	p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath
 	p.metadataFile = android.PathForModuleOut(ctx, metadataFileName).OutputPath
+	p.doMerge = proptools.BoolDefault(p.properties.Include_in_merged_xml, true)
 	path := android.PathForModuleSrc(ctx, String(p.properties.Src))
 
 	rule.Command().
@@ -111,6 +139,16 @@
 	rule.Build(configFileName, "Extract compat/compat_config.xml and install it")
 	ctx.InstallFile(p.installDirPath, p.configFile.Base(), p.configFile)
 	ctx.SetOutputFiles(android.Paths{p.configFile}, "")
+
+	android.SetProvider(ctx, PlatformCompatConfigInfoProvider, PlatformCompatConfigInfo{
+		CompatConfig: p.CompatConfig(),
+		SubDir:       p.SubDir(),
+	})
+
+	android.SetProvider(ctx, PlatformCompatConfigMetadataInfoProvider, PlatformCompatConfigMetadataInfo{
+		CompatConfigMetadata: p.compatConfigMetadata(),
+		IncludeInMergedXml:   p.includeInMergedXml(),
+	})
 }
 
 func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries {
@@ -201,6 +239,10 @@
 	return module.metadataFile
 }
 
+func (module *prebuiltCompatConfigModule) includeInMergedXml() bool {
+	return true // Always include in merged.xml
+}
+
 func (module *prebuiltCompatConfigModule) BaseModuleName() string {
 	return proptools.StringDefault(module.properties.Source_module_name, module.ModuleBase.Name())
 }
@@ -209,6 +251,11 @@
 
 func (module *prebuiltCompatConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	module.metadataFile = module.prebuilt.SingleSourcePath(ctx)
+
+	android.SetProvider(ctx, PlatformCompatConfigMetadataInfoProvider, PlatformCompatConfigMetadataInfo{
+		CompatConfigMetadata: module.compatConfigMetadata(),
+		IncludeInMergedXml:   module.includeInMergedXml(),
+	})
 }
 
 // A prebuilt version of platform_compat_config that provides the metadata.
@@ -229,15 +276,18 @@
 
 	var compatConfigMetadata android.Paths
 
-	ctx.VisitAllModules(func(module android.Module) {
-		if !module.Enabled(ctx) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
 			return
 		}
-		if c, ok := module.(platformCompatConfigMetadataProvider); ok {
-			if !android.IsModulePreferred(module) {
+		if c, ok := android.OtherModuleProvider(ctx, module, PlatformCompatConfigMetadataInfoProvider); ok {
+			if !android.IsModulePreferredProxy(ctx, module) {
 				return
 			}
-			metadata := c.compatConfigMetadata()
+			if !c.IncludeInMergedXml {
+				return
+			}
+			metadata := c.CompatConfigMetadata
 			compatConfigMetadata = append(compatConfigMetadata, metadata)
 		}
 	})
@@ -258,12 +308,7 @@
 	rule.Build("merged-compat-config", "Merge compat config")
 
 	p.metadata = outputPath
-}
-
-func (p *platformCompatConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
-	if p.metadata != nil {
-		ctx.DistForGoal("droidcore", p.metadata)
-	}
+	ctx.DistForGoal("droidcore", p.metadata)
 }
 
 func platformCompatConfigSingletonFactory() android.Singleton {
diff --git a/java/platform_compat_config_test.go b/java/platform_compat_config_test.go
index 80d991c..72f81e0 100644
--- a/java/platform_compat_config_test.go
+++ b/java/platform_compat_config_test.go
@@ -21,11 +21,13 @@
 )
 
 func TestPlatformCompatConfig(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithPlatformCompatConfig,
 		android.FixtureWithRootAndroidBp(`
 			platform_compat_config {
 				name: "myconfig2",
+				include_in_merged_xml: false,
 			}
 			platform_compat_config {
 				name: "myconfig1",
@@ -38,7 +40,6 @@
 
 	CheckMergedCompatConfigInputs(t, result, "myconfig",
 		"out/soong/.intermediates/myconfig1/myconfig1_meta.xml",
-		"out/soong/.intermediates/myconfig2/myconfig2_meta.xml",
 		"out/soong/.intermediates/myconfig3/myconfig3_meta.xml",
 	)
 }
diff --git a/java/plugin.go b/java/plugin.go
index 610c9fd..3534c7b 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -16,8 +16,21 @@
 
 import (
 	"android/soong/android"
+	"github.com/google/blueprint"
 )
 
+type JavaPluginInfo struct {
+	ProcessorClass *string
+	GeneratesApi   bool
+}
+
+var JavaPluginInfoProvider = blueprint.NewProvider[JavaPluginInfo]()
+
+type KotlinPluginInfo struct {
+}
+
+var KotlinPluginInfoProvider = blueprint.NewProvider[KotlinPluginInfo]()
+
 func init() {
 	registerJavaPluginBuildComponents(android.InitRegistrationContext)
 }
@@ -65,7 +78,22 @@
 	Generates_api *bool
 }
 
+func (p *Plugin) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.Library.GenerateAndroidBuildActions(ctx)
+
+	android.SetProvider(ctx, JavaPluginInfoProvider, JavaPluginInfo{
+		ProcessorClass: p.pluginProperties.Processor_class,
+		GeneratesApi:   Bool(p.pluginProperties.Generates_api),
+	})
+}
+
 // Plugin describes a kotlin_plugin module, a host java/kotlin library that will be used by kotlinc as a compiler plugin.
 type KotlinPlugin struct {
 	Library
 }
+
+func (p *KotlinPlugin) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	p.Library.GenerateAndroidBuildActions(ctx)
+
+	android.SetProvider(ctx, KotlinPluginInfoProvider, KotlinPluginInfo{})
+}
diff --git a/java/plugin_test.go b/java/plugin_test.go
index dc29b1c..007b74a 100644
--- a/java/plugin_test.go
+++ b/java/plugin_test.go
@@ -19,6 +19,7 @@
 )
 
 func TestNoPlugin(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -26,8 +27,8 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
+	turbine := ctx.ModuleForTests(t, "foo", "android_common").MaybeRule("turbine")
 
 	if turbine.Rule == nil {
 		t.Errorf("expected turbine to be enabled")
@@ -43,6 +44,7 @@
 }
 
 func TestPlugin(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -59,14 +61,14 @@
 
 	buildOS := ctx.Config().BuildOS.String()
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
+	turbine := ctx.ModuleForTests(t, "foo", "android_common").MaybeRule("turbine")
 
 	if turbine.Rule == nil {
 		t.Errorf("expected turbine to be enabled")
 	}
 
-	bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
+	bar := ctx.ModuleForTests(t, "bar", buildOS+"_common").Rule("javac").Output.String()
 
 	if !inList(bar, javac.Implicits.Strings()) {
 		t.Errorf("foo implicits %v does not contain %q", javac.Implicits.Strings(), bar)
@@ -82,6 +84,7 @@
 }
 
 func TestPluginGeneratesApi(t *testing.T) {
+	t.Parallel()
 	ctx, _ := testJava(t, `
 		java_library {
 			name: "foo",
@@ -99,14 +102,14 @@
 
 	buildOS := ctx.Config().BuildOS.String()
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
+	javac := ctx.ModuleForTests(t, "foo", "android_common").Rule("javac")
+	turbine := ctx.ModuleForTests(t, "foo", "android_common").MaybeRule("turbine")
 
 	if turbine.Rule != nil {
 		t.Errorf("expected turbine to be disabled")
 	}
 
-	bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
+	bar := ctx.ModuleForTests(t, "bar", buildOS+"_common").Rule("javac").Output.String()
 
 	if !inList(bar, javac.Implicits.Strings()) {
 		t.Errorf("foo implicits %v does not contain %q", javac.Implicits.Strings(), bar)
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 527e479..31f149e 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -55,11 +55,6 @@
 
 	// If set to true, compile dex for java_import modules. Defaults to false.
 	Imports_compile_dex *bool
-
-	// If set to true, allow incremental platform API of the form MM.m where MM is the major release
-	// version corresponding to the API level/SDK_INT and m is an incremental release version
-	// (e.g. API changes associated with QPR). Defaults to false.
-	Allow_incremental_platform_api *bool
 }
 
 type prebuiltApis struct {
@@ -97,28 +92,28 @@
 }
 
 // parseFinalizedPrebuiltPath is like parsePrebuiltPath, but verifies the version is numeric (a finalized version).
-func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string, allowIncremental bool) (module string, version int, release int, scope string) {
+func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string) (module string, version int, release int, scope string) {
 	module, v, scope := parsePrebuiltPath(ctx, p)
-	if allowIncremental {
-		parts := strings.Split(v, ".")
-		if len(parts) != 2 {
-			ctx.ModuleErrorf("Found unexpected version '%v' for incremental prebuilts - expect MM.m format for incremental API with both major (MM) an minor (m) revision.", v)
-			return
-		}
+
+	// assume a major.minor version code
+	parts := strings.Split(v, ".")
+	if len(parts) == 2 {
 		sdk, sdk_err := strconv.Atoi(parts[0])
 		qpr, qpr_err := strconv.Atoi(parts[1])
 		if sdk_err != nil || qpr_err != nil {
-			ctx.ModuleErrorf("Unable to read version number for incremental prebuilt api '%v'", v)
+			ctx.ModuleErrorf("Unable to read major.minor version for prebuilt api '%v'", v)
 			return
 		}
 		version = sdk
 		release = qpr
 		return
 	}
+
+	// assume a legacy integer only api level
 	release = 0
 	version, err := strconv.Atoi(v)
 	if err != nil {
-		ctx.ModuleErrorf("Found finalized API files in non-numeric dir '%v'", v)
+		ctx.ModuleErrorf("Unable to read API level for prebuilt api '%v'", v)
 		return
 	}
 	return
@@ -279,12 +274,11 @@
 	}
 
 	// Create modules for all (<module>, <scope, <version>) triplets,
-	allowIncremental := proptools.BoolDefault(p.properties.Allow_incremental_platform_api, false)
 	for _, f := range apiLevelFiles {
-		module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f, allowIncremental)
-		if allowIncremental {
-			incrementalVersion := strconv.Itoa(version) + "." + strconv.Itoa(release)
-			createApiModule(mctx, PrebuiltApiModuleName(module, scope, incrementalVersion), f)
+		module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f)
+		if release != 0 {
+			majorDotMinorVersion := strconv.Itoa(version) + "." + strconv.Itoa(release)
+			createApiModule(mctx, PrebuiltApiModuleName(module, scope, majorDotMinorVersion), f)
 		} else {
 			createApiModule(mctx, PrebuiltApiModuleName(module, scope, strconv.Itoa(version)), f)
 		}
@@ -300,7 +294,7 @@
 	getLatest := func(files []string, isExtensionApiFile bool) map[string]latestApiInfo {
 		m := make(map[string]latestApiInfo)
 		for _, f := range files {
-			module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f, allowIncremental)
+			module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f)
 			if strings.HasSuffix(module, "incompatibilities") {
 				continue
 			}
diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go
index b6fb2c6..17fdae9 100644
--- a/java/prebuilt_apis_test.go
+++ b/java/prebuilt_apis_test.go
@@ -29,6 +29,7 @@
 }
 
 func TestPrebuiltApis_SystemModulesCreation(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		FixtureWithPrebuiltApis(map[string][]string{
@@ -61,6 +62,7 @@
 }
 
 func TestPrebuiltApis_WithExtensions(t *testing.T) {
+	t.Parallel()
 	runTestWithBaseExtensionLevel := func(v int) (foo_input, bar_input, baz_input string) {
 		result := android.GroupFixturePreparers(
 			prepareForJavaTest,
@@ -76,9 +78,9 @@
 				"2": {"foo", "bar"},
 			}),
 		).RunTest(t)
-		foo_input = result.ModuleForTests("foo.api.public.latest", "").Rule("generator").Implicits[0].String()
-		bar_input = result.ModuleForTests("bar.api.public.latest", "").Rule("generator").Implicits[0].String()
-		baz_input = result.ModuleForTests("baz.api.public.latest", "").Rule("generator").Implicits[0].String()
+		foo_input = result.ModuleForTests(t, "foo.api.public.latest", "").Rule("generator").Implicits[0].String()
+		bar_input = result.ModuleForTests(t, "bar.api.public.latest", "").Rule("generator").Implicits[0].String()
+		baz_input = result.ModuleForTests(t, "baz.api.public.latest", "").Rule("generator").Implicits[0].String()
 		return
 	}
 	// Extension 2 is the latest for both foo and bar, finalized after the base extension version.
@@ -100,25 +102,26 @@
 	android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_input)
 }
 
-func TestPrebuiltApis_WithIncrementalApi(t *testing.T) {
+func TestPrebuiltApis_WithMixedVersionCodes(t *testing.T) {
+	t.Parallel()
 	runTestWithIncrementalApi := func() (foo_input, bar_input, baz_input string) {
 		result := android.GroupFixturePreparers(
 			prepareForJavaTest,
-			FixtureWithPrebuiltIncrementalApis(map[string][]string{
+			FixtureWithPrebuiltApis(map[string][]string{
 				"33.0":    {"foo"},
-				"33.1":    {"foo", "bar", "baz"},
-				"33.2":    {"foo", "bar"},
+				"34":      {"foo", "bar", "baz"},
+				"34.1":    {"foo", "bar"},
 				"current": {"foo", "bar"},
 			}),
 		).RunTest(t)
-		foo_input = result.ModuleForTests("foo.api.public.latest", "").Rule("generator").Implicits[0].String()
-		bar_input = result.ModuleForTests("bar.api.public.latest", "").Rule("generator").Implicits[0].String()
-		baz_input = result.ModuleForTests("baz.api.public.latest", "").Rule("generator").Implicits[0].String()
+		foo_input = result.ModuleForTests(t, "foo.api.public.latest", "").Rule("generator").Implicits[0].String()
+		bar_input = result.ModuleForTests(t, "bar.api.public.latest", "").Rule("generator").Implicits[0].String()
+		baz_input = result.ModuleForTests(t, "baz.api.public.latest", "").Rule("generator").Implicits[0].String()
 		return
 	}
-	// 33.1 is the latest for baz, 33.2 is the latest for both foo & bar
+	// 34 is the latest for baz, 34.1 is the latest for both foo & bar
 	foo_input, bar_input, baz_input := runTestWithIncrementalApi()
-	android.AssertStringEquals(t, "Expected latest foo = api level 33.2", "prebuilts/sdk/33.2/public/api/foo.txt", foo_input)
-	android.AssertStringEquals(t, "Expected latest bar = api level 33.2", "prebuilts/sdk/33.2/public/api/bar.txt", bar_input)
-	android.AssertStringEquals(t, "Expected latest baz = api level 33.1", "prebuilts/sdk/33.1/public/api/baz.txt", baz_input)
+	android.AssertStringEquals(t, "Expected latest foo = api level 34.1", "prebuilts/sdk/34.1/public/api/foo.txt", foo_input)
+	android.AssertStringEquals(t, "Expected latest bar = api level 34.1", "prebuilts/sdk/34.1/public/api/bar.txt", bar_input)
+	android.AssertStringEquals(t, "Expected latest baz = api level 34", "prebuilts/sdk/34/public/api/baz.txt", baz_input)
 }
diff --git a/java/proto_test.go b/java/proto_test.go
index d1cb714..3fbe3e6 100644
--- a/java/proto_test.go
+++ b/java/proto_test.go
@@ -28,6 +28,7 @@
 `
 
 func TestProtoStream(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_library {
 			name: "java-stream-protos",
@@ -45,7 +46,7 @@
 		PrepareForIntegrationTestWithJava,
 	).RunTestWithBp(t, protoModules+bp)
 
-	proto0 := ctx.ModuleForTests("java-stream-protos", "android_common").Output("proto/proto0.srcjar")
+	proto0 := ctx.ModuleForTests(t, "java-stream-protos", "android_common").Output("proto/proto0.srcjar")
 
 	if cmd := proto0.RuleParams.Command; !strings.Contains(cmd, "--javastream_out=") {
 		t.Errorf("expected '--javastream_out' in %q", cmd)
diff --git a/java/ravenwood.go b/java/ravenwood.go
index 4c43a9f..a942dc6 100644
--- a/java/ravenwood.go
+++ b/java/ravenwood.go
@@ -110,13 +110,15 @@
 	module.AddProperties(&module.testProperties, &module.ravenwoodTestProperties)
 
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
 
 	module.testProperties.Test_suites = []string{
 		"general-tests",
 		"ravenwood-tests",
 	}
 	module.testProperties.Test_options.Unit_test = proptools.BoolPtr(false)
+	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Top_level_test_target = true
 
 	InitJavaModule(module, android.DeviceSupported)
 	android.InitDefaultableModule(module)
@@ -185,26 +187,24 @@
 	// All JNI libraries included in the runtime
 	var runtimeJniModuleNames map[string]bool
 
-	if utils := ctx.GetDirectDepsWithTag(ravenwoodUtilsTag)[0]; utils != nil {
-		for _, installFile := range android.OtherModuleProviderOrDefault(
-			ctx, utils, android.InstallFilesProvider).InstallFiles {
-			installDeps = append(installDeps, installFile)
-		}
-		jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
-		if ok {
-			runtimeJniModuleNames = jniDeps.names
-		}
+	utils := ctx.GetDirectDepsProxyWithTag(ravenwoodUtilsTag)[0]
+	for _, installFile := range android.OtherModuleProviderOrDefault(
+		ctx, utils, android.InstallFilesProvider).InstallFiles {
+		installDeps = append(installDeps, installFile)
+	}
+	jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
+	if ok {
+		runtimeJniModuleNames = jniDeps.names
 	}
 
-	if runtime := ctx.GetDirectDepsWithTag(ravenwoodRuntimeTag)[0]; runtime != nil {
-		for _, installFile := range android.OtherModuleProviderOrDefault(
-			ctx, runtime, android.InstallFilesProvider).InstallFiles {
-			installDeps = append(installDeps, installFile)
-		}
-		jniDeps, ok := android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
-		if ok {
-			runtimeJniModuleNames = jniDeps.names
-		}
+	runtime := ctx.GetDirectDepsProxyWithTag(ravenwoodRuntimeTag)[0]
+	for _, installFile := range android.OtherModuleProviderOrDefault(
+		ctx, runtime, android.InstallFilesProvider).InstallFiles {
+		installDeps = append(installDeps, installFile)
+	}
+	jniDeps, ok = android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
+	if ok {
+		runtimeJniModuleNames = jniDeps.names
 	}
 
 	// Also remember what JNI libs are in the runtime.
@@ -228,7 +228,7 @@
 	resApkInstallPath := installPath.Join(ctx, "ravenwood-res-apks")
 
 	copyResApk := func(tag blueprint.DependencyTag, toFileName string) {
-		if resApk := ctx.GetDirectDepsWithTag(tag); len(resApk) > 0 {
+		if resApk := ctx.GetDirectDepsProxyWithTag(tag); len(resApk) > 0 {
 			installFile := android.OutputFileForModule(ctx, resApk[0], "")
 			installResApk := ctx.InstallFile(resApkInstallPath, toFileName, installFile)
 			installDeps = append(installDeps, installResApk)
@@ -260,6 +260,19 @@
 
 	// Install our JAR with all dependencies
 	ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
+
+	moduleInfoJSON := ctx.ModuleInfoJSON()
+	if _, ok := r.testConfig.(android.WritablePath); ok {
+		moduleInfoJSON.AutoTestConfig = []string{"true"}
+	}
+	if r.testConfig != nil {
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, r.testConfig.String())
+	}
+	moduleInfoJSON.CompatibilitySuites = []string{"general-tests", "ravenwood-tests"}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
 }
 
 func (r *ravenwoodTest) AndroidMkEntries() []android.AndroidMkEntries {
@@ -345,7 +358,7 @@
 	// Install our runtime into expected location for packaging
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	for _, lib := range r.ravenwoodLibgroupProperties.Libs {
-		libModule := ctx.GetDirectDepWithTag(lib, ravenwoodLibContentTag)
+		libModule := ctx.GetDirectDepProxyWithTag(lib, ravenwoodLibContentTag)
 		if libModule == nil {
 			if ctx.Config().AllowMissingDependencies() {
 				ctx.AddMissingDependencies([]string{lib})
@@ -377,6 +390,10 @@
 
 	// Normal build should perform install steps
 	ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install"))
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
 }
 
 // collectTransitiveJniDeps returns all JNI dependencies, including transitive
diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go
index 6394a9a..d6493bc 100644
--- a/java/ravenwood_test.go
+++ b/java/ravenwood_test.go
@@ -100,9 +100,10 @@
 	`),
 )
 
-var installPathPrefix = "out/soong/host/linux-x86/testcases"
+var installPathPrefix = "out/host/linux-x86/testcases"
 
 func TestRavenwoodRuntime(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
 	}
@@ -120,7 +121,7 @@
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-utils", "android_common", "framework-rules.ravenwood")
 
 	// Verify that we've emitted artifacts in expected location
-	runtime := ctx.ModuleForTests("ravenwood-runtime", "android_common")
+	runtime := ctx.ModuleForTests(t, "ravenwood-runtime", "android_common")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/framework-minus-apex.ravenwood.jar")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/framework-services.ravenwood.jar")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni1.so")
@@ -128,11 +129,12 @@
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/ravenwood-data/app1.apk")
 	runtime.Output(installPathPrefix + "/ravenwood-runtime/fonts/Font.ttf")
-	utils := ctx.ModuleForTests("ravenwood-utils", "android_common")
+	utils := ctx.ModuleForTests(t, "ravenwood-utils", "android_common")
 	utils.Output(installPathPrefix + "/ravenwood-utils/framework-rules.ravenwood.jar")
 }
 
 func TestRavenwoodTest(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
 	}
@@ -191,7 +193,7 @@
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "ravenwood-utils")
 	CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "jni-lib")
 
-	module := ctx.ModuleForTests("ravenwood-test", "android_common")
+	module := ctx.ModuleForTests(t, "ravenwood-test", "android_common")
 	classpath := module.Rule("javac").Args["classpath"]
 
 	// Verify that we're linking against test_current
@@ -212,7 +214,7 @@
 	module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-res.apk")
 	module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-inst-res.apk")
 
-	module = ctx.ModuleForTests("ravenwood-test-empty", "android_common")
+	module = ctx.ModuleForTests(t, "ravenwood-test-empty", "android_common")
 	module.Output(installPathPrefix + "/ravenwood-test-empty/ravenwood.properties")
 
 	// ravenwood-runtime*.so are included in the runtime, so it shouldn't be emitted.
@@ -228,4 +230,15 @@
 	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/libred.so")
 	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so")
 	android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-utils/framework-rules.ravenwood.jar")
+
+	// Ensure they are listed as "test" modules for code coverage
+	expectedTestOnlyModules := []string{
+		"ravenwood-test",
+		"ravenwood-test-empty",
+	}
+	expectedTopLevelTests := []string{
+		"ravenwood-test",
+		"ravenwood-test-empty",
+	}
+	assertTestOnlyAndTopLevel(t, ctx, expectedTestOnlyModules, expectedTopLevelTests)
 }
diff --git a/java/robolectric.go b/java/robolectric.go
index 5f46267..1d204a4 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -21,6 +21,7 @@
 	"android/soong/java/config"
 	"android/soong/tradefed"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -28,6 +29,23 @@
 	RegisterRobolectricBuildComponents(android.InitRegistrationContext)
 }
 
+type RobolectricRuntimesInfo struct {
+	Runtimes []android.InstallPath
+}
+
+var RobolectricRuntimesInfoProvider = blueprint.NewProvider[RobolectricRuntimesInfo]()
+
+type roboRuntimeOnlyDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+var roboRuntimeOnlyDepTag roboRuntimeOnlyDependencyTag
+
+// Mark this tag so dependencies that use it are excluded from visibility enforcement.
+func (t roboRuntimeOnlyDependencyTag) ExcludeFromVisibilityEnforcement() {}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = roboRuntimeOnlyDepTag
+
 func RegisterRobolectricBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_robolectric_test", RobolectricTestFactory)
 	ctx.RegisterModuleType("android_robolectric_runtimes", robolectricRuntimesFactory)
@@ -41,12 +59,12 @@
 }
 
 const robolectricCurrentLib = "Robolectric_all-target"
+const clearcutJunitLib = "ClearcutJunitListenerAar"
 const robolectricPrebuiltLibPattern = "platform-robolectric-%s-prebuilt"
 
 var (
 	roboCoverageLibsTag = dependencyTag{name: "roboCoverageLibs"}
 	roboRuntimesTag     = dependencyTag{name: "roboRuntimes"}
-	roboRuntimeOnlyTag  = dependencyTag{name: "roboRuntimeOnlyTag"}
 )
 
 type robolectricProperties struct {
@@ -64,10 +82,6 @@
 		Shards *int64
 	}
 
-	// The version number of a robolectric prebuilt to use from prebuilts/misc/common/robolectric
-	// instead of the one built from source in external/robolectric-shadows.
-	Robolectric_prebuilt_version *string
-
 	// Use /external/robolectric rather than /external/robolectric-shadows as the version of robolectric
 	// to use.  /external/robolectric closely tracks github's master, and will fully replace /external/robolectric-shadows
 	Upstream *bool
@@ -106,21 +120,12 @@
 		ctx.PropertyErrorf("instrumentation_for", "missing required instrumented module")
 	}
 
-	if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" {
-		ctx.AddVariationDependencies(nil, staticLibTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v))
-	} else if !proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
-		if proptools.Bool(r.robolectricProperties.Upstream) {
-			ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib+"_upstream")
-		} else {
-			ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib)
-		}
-	}
+	ctx.AddVariationDependencies(nil, roboRuntimeOnlyDepTag, clearcutJunitLib)
 
 	if proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
-		ctx.AddVariationDependencies(nil, roboRuntimeOnlyTag, robolectricCurrentLib+"_upstream")
+		ctx.AddVariationDependencies(nil, roboRuntimeOnlyDepTag, robolectricCurrentLib)
 	} else {
-		// opting out from strict mode, robolectric_non_strict_mode_permission lib should be added
-		ctx.AddVariationDependencies(nil, staticLibTag, "robolectric_non_strict_mode_permission")
+		ctx.AddVariationDependencies(nil, staticLibTag, robolectricCurrentLib)
 	}
 
 	ctx.AddVariationDependencies(nil, staticLibTag, robolectricDefaultLibs...)
@@ -139,39 +144,54 @@
 	r.forceOSType = ctx.Config().BuildOS
 	r.forceArchType = ctx.Config().BuildArch
 
+	var extraTestRunnerOptions []tradefed.Option
+	extraTestRunnerOptions = append(extraTestRunnerOptions, tradefed.Option{Name: "java-flags", Value: "-Drobolectric=true"})
+	if proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) {
+		extraTestRunnerOptions = append(extraTestRunnerOptions, tradefed.Option{Name: "java-flags", Value: "-Drobolectric.strict.mode=true"})
+	}
+
+	var extraOptions []tradefed.Option
+	var javaHome = ctx.Config().Getenv("ANDROID_JAVA_HOME")
+	extraOptions = append(extraOptions, tradefed.Option{Name: "java-folder", Value: javaHome})
+
 	r.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
-		TestConfigProp:         r.testProperties.Test_config,
-		TestConfigTemplateProp: r.testProperties.Test_config_template,
-		TestSuites:             r.testProperties.Test_suites,
-		AutoGenConfig:          r.testProperties.Auto_gen_config,
-		DeviceTemplate:         "${RobolectricTestConfigTemplate}",
-		HostTemplate:           "${RobolectricTestConfigTemplate}",
+		TestConfigProp:          r.testProperties.Test_config,
+		TestConfigTemplateProp:  r.testProperties.Test_config_template,
+		TestSuites:              r.testProperties.Test_suites,
+		OptionsForAutogenerated: extraOptions,
+		TestRunnerOptions:       extraTestRunnerOptions,
+		AutoGenConfig:           r.testProperties.Auto_gen_config,
+		DeviceTemplate:          "${RobolectricTestConfigTemplate}",
+		HostTemplate:            "${RobolectricTestConfigTemplate}",
 	})
 	r.data = android.PathsForModuleSrc(ctx, r.testProperties.Data)
 	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_common_data)...)
 	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_first_data)...)
 	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Device_first_prefer32_data)...)
+	r.data = append(r.data, android.PathsForModuleSrc(ctx, r.testProperties.Host_common_data)...)
 
 	var ok bool
-	var instrumentedApp *AndroidApp
+	var instrumentedApp *JavaInfo
+	var appInfo *AppInfo
 
 	// TODO: this inserts paths to built files into the test, it should really be inserting the contents.
-	instrumented := ctx.GetDirectDepsWithTag(instrumentationForTag)
+	instrumented := ctx.GetDirectDepsProxyWithTag(instrumentationForTag)
 
 	if len(instrumented) == 1 {
-		instrumentedApp, ok = instrumented[0].(*AndroidApp)
+		appInfo, ok = android.OtherModuleProvider(ctx, instrumented[0], AppInfoProvider)
 		if !ok {
 			ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app")
 		}
+		instrumentedApp = android.OtherModuleProviderOrDefault(ctx, instrumented[0], JavaInfoProvider)
 	} else if !ctx.Config().AllowMissingDependencies() {
 		panic(fmt.Errorf("expected exactly 1 instrumented dependency, got %d", len(instrumented)))
 	}
 
 	var resourceApk android.Path
 	var manifest android.Path
-	if instrumentedApp != nil {
-		manifest = instrumentedApp.mergedManifestFile
-		resourceApk = instrumentedApp.outputFile
+	if appInfo != nil {
+		manifest = appInfo.MergedManifestFile
+		resourceApk = instrumentedApp.OutputFile
 	}
 
 	roboTestConfigJar := android.PathForModuleOut(ctx, "robolectric_samedir", "samedir_config.jar")
@@ -179,7 +199,7 @@
 
 	extraCombinedJars := android.Paths{roboTestConfigJar}
 
-	handleLibDeps := func(dep android.Module) {
+	handleLibDeps := func(dep android.ModuleProxy) {
 		if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) {
 			if m, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok {
 				extraCombinedJars = append(extraCombinedJars, m.ImplementationAndResourcesJars...)
@@ -187,25 +207,25 @@
 		}
 	}
 
-	for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
+	for _, dep := range ctx.GetDirectDepsProxyWithTag(libTag) {
 		handleLibDeps(dep)
 	}
-	for _, dep := range ctx.GetDirectDepsWithTag(sdkLibTag) {
+	for _, dep := range ctx.GetDirectDepsProxyWithTag(sdkLibTag) {
 		handleLibDeps(dep)
 	}
 	// handle the runtimeOnly tag for strict_mode
-	for _, dep := range ctx.GetDirectDepsWithTag(roboRuntimeOnlyTag) {
+	for _, dep := range ctx.GetDirectDepsProxyWithTag(roboRuntimeOnlyDepTag) {
 		handleLibDeps(dep)
 	}
 
-	if instrumentedApp != nil {
-		extraCombinedJars = append(extraCombinedJars, instrumentedApp.implementationAndResourcesJar)
+	if appInfo != nil {
+		extraCombinedJars = append(extraCombinedJars, instrumentedApp.ImplementationAndResourcesJars...)
 	}
 
 	r.stem = proptools.StringDefault(r.overridableProperties.Stem, ctx.ModuleName())
 	r.classLoaderContexts = r.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 	r.dexpreopter.disableDexpreopt()
-	r.compile(ctx, nil, nil, nil, extraCombinedJars)
+	javaInfo := r.compile(ctx, nil, nil, nil, extraCombinedJars)
 
 	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
 	var installDeps android.InstallPaths
@@ -227,8 +247,8 @@
 		installDeps = append(installDeps, installedResourceApk)
 	}
 
-	runtimes := ctx.GetDirectDepWithTag("robolectric-android-all-prebuilts", roboRuntimesTag)
-	for _, runtime := range runtimes.(*robolectricRuntimes).runtimes {
+	runtimes := ctx.GetDirectDepProxyWithTag("robolectric-android-all-prebuilts", roboRuntimesTag)
+	for _, runtime := range android.OtherModuleProviderOrDefault(ctx, runtimes, RobolectricRuntimesInfoProvider).Runtimes {
 		installDeps = append(installDeps, runtime)
 	}
 
@@ -242,6 +262,33 @@
 	}
 
 	r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
+
+	if javaInfo != nil {
+		setExtraJavaInfo(ctx, r, javaInfo)
+		android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+	}
+
+	moduleInfoJSON := r.javaLibraryModuleInfoJSON(ctx)
+	if _, ok := r.testConfig.(android.WritablePath); ok {
+		moduleInfoJSON.AutoTestConfig = []string{"true"}
+	}
+	if r.testConfig != nil {
+		moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, r.testConfig.String())
+	}
+	if len(r.testProperties.Test_suites) > 0 {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, r.testProperties.Test_suites...)
+	} else {
+		moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite")
+	}
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
+
+	android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+		TestOnly:       Bool(r.sourceProperties.Test_only),
+		TopLevelTarget: r.sourceProperties.Top_level_test_target,
+	})
 }
 
 func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile android.ModuleOutPath) {
@@ -292,8 +339,9 @@
 		&module.testProperties)
 
 	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
-
+	module.Module.linter.properties.Lint.Test_module_type = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Test_only = proptools.BoolPtr(true)
+	module.Module.sourceProperties.Top_level_test_target = true
 	module.testProperties.Test_suites = []string{"robolectric-tests"}
 
 	InitJavaModule(module, android.DeviceSupported)
@@ -357,7 +405,7 @@
 	}
 
 	if !ctx.Config().AlwaysUsePrebuiltSdks() && r.props.Lib != nil {
-		runtimeFromSourceModule := ctx.GetDirectDepWithTag(String(r.props.Lib), libTag)
+		runtimeFromSourceModule := ctx.GetDirectDepProxyWithTag(String(r.props.Lib), libTag)
 		if runtimeFromSourceModule == nil {
 			if ctx.Config().AllowMissingDependencies() {
 				ctx.AddMissingDependencies([]string{String(r.props.Lib)})
@@ -375,6 +423,14 @@
 		installedRuntime := ctx.InstallFile(androidAllDir, runtimeName, runtimeFromSourceJar)
 		r.runtimes = append(r.runtimes, installedRuntime)
 	}
+
+	android.SetProvider(ctx, RobolectricRuntimesInfoProvider, RobolectricRuntimesInfo{
+		Runtimes: r.runtimes,
+	})
+
+	android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{
+		TestSuites: r.TestSuites(),
+	})
 }
 
 func (r *robolectricRuntimes) InstallInTestcases() bool { return true }
diff --git a/java/robolectric_test.go b/java/robolectric_test.go
index 4775bac..cc16c6a 100644
--- a/java/robolectric_test.go
+++ b/java/robolectric_test.go
@@ -32,6 +32,11 @@
 	}
 
 	java_library {
+		name: "Robolectric_all-target",
+		srcs: ["Robo.java"]
+	}
+
+	java_library {
 		name: "mockito-robolectric-prebuilt",
 		srcs: ["Mockito.java"]
 	}
@@ -44,6 +49,12 @@
 	java_library {
 		name: "junitxml",
 		srcs: ["JUnitXml.java"]
+
+	}
+
+	java_library {
+		name: "ClearcutJunitListenerAar",
+		srcs: ["Runtime.java"]
 	}
 
 	java_library_host {
@@ -60,6 +71,7 @@
 )
 
 func TestRobolectricJniTest(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
 	}
@@ -93,6 +105,17 @@
 	CheckModuleHasDependency(t, ctx.TestContext, "robo-test", "android_common", "jni-lib1")
 
 	// Check that the .so files make it into the output.
-	module := ctx.ModuleForTests("robo-test", "android_common")
+	module := ctx.ModuleForTests(t, "robo-test", "android_common")
 	module.Output(installPathPrefix + "/robo-test/lib64/jni-lib1.so")
+
+	// Ensure they are listed as "test" modules for code coverage
+	expectedTestOnlyModules := []string{
+		"robo-test",
+	}
+
+	expectedTopLevelTests := []string{
+		"robo-test",
+	}
+	assertTestOnlyAndTopLevel(t, ctx, expectedTestOnlyModules, expectedTopLevelTests)
+
 }
diff --git a/java/rro.go b/java/rro.go
index ab4fafa..4ae8d7f 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -34,6 +34,15 @@
 	ctx.RegisterModuleType("override_runtime_resource_overlay", OverrideRuntimeResourceOverlayModuleFactory)
 }
 
+type RuntimeResourceOverlayInfo struct {
+	OutputFile                    android.Path
+	Certificate                   Certificate
+	Theme                         string
+	OverriddenManifestPackageName string
+}
+
+var RuntimeResourceOverlayInfoProvider = blueprint.NewProvider[RuntimeResourceOverlayInfo]()
+
 type RuntimeResourceOverlay struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -90,15 +99,6 @@
 	Overrides []string
 }
 
-// RuntimeResourceOverlayModule interface is used by the apex package to gather information from
-// a RuntimeResourceOverlay module.
-type RuntimeResourceOverlayModule interface {
-	android.Module
-	OutputFile() android.Path
-	Certificate() Certificate
-	Theme() string
-}
-
 // RRO's partition logic is different from the partition logic of other modules defined in soong/android/paths.go
 // The default partition for RRO is "/product" and not "/system"
 func rroPartition(ctx android.ModuleContext) string {
@@ -139,6 +139,25 @@
 	r.aapt.hasNoCode = true
 	// Do not remove resources without default values nor dedupe resource configurations with the same value
 	aaptLinkFlags := []string{"--no-resource-deduping", "--no-resource-removal"}
+
+	// Add TARGET_AAPT_CHARACTERISTICS values to AAPT link flags if they exist and --product flags were not provided.
+	hasProduct := android.PrefixInList(r.aaptProperties.Aaptflags, "--product")
+	if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 {
+		aaptLinkFlags = append(aaptLinkFlags, "--product", ctx.Config().ProductAAPTCharacteristics())
+	}
+
+	if !Bool(r.aaptProperties.Aapt_include_all_resources) {
+		// Product AAPT config
+		for _, aaptConfig := range ctx.Config().ProductAAPTConfig() {
+			aaptLinkFlags = append(aaptLinkFlags, "-c", aaptConfig)
+		}
+
+		// Product AAPT preferred config
+		if len(ctx.Config().ProductAAPTPreferredConfig()) > 0 {
+			aaptLinkFlags = append(aaptLinkFlags, "--preferred-density", ctx.Config().ProductAAPTPreferredConfig())
+		}
+	}
+
 	// Allow the override of "package name" and "overlay target package name"
 	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
 	if overridden || r.overridableProperties.Package_name != nil {
@@ -187,6 +206,16 @@
 	android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{
 		AconfigTextFiles: aconfigTextFilePaths,
 	})
+
+	android.SetProvider(ctx, RuntimeResourceOverlayInfoProvider, RuntimeResourceOverlayInfo{
+		OutputFile:  r.outputFile,
+		Certificate: r.Certificate(),
+		Theme:       r.Theme(),
+	})
+
+	ctx.SetOutputFiles([]android.Path{r.outputFile}, "")
+
+	buildComplianceMetadata(ctx)
 }
 
 func (r *RuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
@@ -216,10 +245,6 @@
 	return r.certificate
 }
 
-func (r *RuntimeResourceOverlay) OutputFile() android.Path {
-	return r.outputFile
-}
-
 func (r *RuntimeResourceOverlay) Theme() string {
 	return String(r.properties.Theme)
 }
@@ -370,10 +395,11 @@
 
 	a.aapt.buildActions(ctx,
 		aaptBuildActionOptions{
-			sdkContext:      a,
-			extraLinkFlags:  aaptLinkFlags,
-			rroDirs:         &rroDirs,
-			manifestForAapt: genManifest,
+			sdkContext:       a,
+			extraLinkFlags:   aaptLinkFlags,
+			rroDirs:          &rroDirs,
+			manifestForAapt:  genManifest,
+			aconfigTextFiles: getAconfigFilePaths(ctx),
 		},
 	)
 
@@ -390,6 +416,11 @@
 	// Install the signed apk
 	installDir := android.PathForModuleInstall(ctx, "overlay")
 	ctx.InstallFile(installDir, signed.Base(), signed)
+
+	android.SetProvider(ctx, RuntimeResourceOverlayInfoProvider, RuntimeResourceOverlayInfo{
+		OutputFile:  signed,
+		Certificate: a.certificate,
+	})
 }
 
 func (a *AutogenRuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
diff --git a/java/rro_test.go b/java/rro_test.go
index 4d58bb4..3e4fed5 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -24,6 +24,7 @@
 )
 
 func TestRuntimeResourceOverlay(t *testing.T) {
+	t.Parallel()
 	fs := android.MockFS{
 		"baz/res/res/values/strings.xml": nil,
 		"bar/res/res/values/strings.xml": nil,
@@ -66,7 +67,7 @@
 		fs.AddToFixture(),
 	).RunTestWithBp(t, bp)
 
-	m := result.ModuleForTests("foo", "android_common")
+	m := result.ModuleForTests(t, "foo", "android_common")
 
 	// Check AAPT2 link flags.
 	aapt2Flags := m.Output("package-res.apk").Args["flags"]
@@ -115,7 +116,7 @@
 	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
 
 	// A themed module has a different device location
-	m = result.ModuleForTests("foo_themed", "android_common")
+	m = result.ModuleForTests(t, "foo_themed", "android_common")
 	androidMkEntries = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
 	path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
 	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay/faza")}
@@ -129,6 +130,7 @@
 }
 
 func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
@@ -153,7 +155,7 @@
 	//
 	// RRO module with defaults
 	//
-	m := result.ModuleForTests("foo_with_defaults", "android_common")
+	m := result.ModuleForTests(t, "foo_with_defaults", "android_common")
 
 	// Check AAPT2 link flags.
 	aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ")
@@ -171,7 +173,7 @@
 	//
 	// RRO module without defaults
 	//
-	m = result.ModuleForTests("foo_barebones", "android_common")
+	m = result.ModuleForTests(t, "foo_barebones", "android_common")
 
 	// Check AAPT2 link flags.
 	aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ")
@@ -216,7 +218,7 @@
 	}{
 		{
 			variantName:       "android_common",
-			apkPath:           "out/soong/target/product/test_device/product/overlay/foo_overlay.apk",
+			apkPath:           "out/target/product/test_device/product/overlay/foo_overlay.apk",
 			overrides:         nil,
 			targetVariant:     "android_common",
 			packageFlag:       "",
@@ -224,7 +226,7 @@
 		},
 		{
 			variantName:       "android_common_bar_overlay",
-			apkPath:           "out/soong/target/product/test_device/product/overlay/bar_overlay.apk",
+			apkPath:           "out/target/product/test_device/product/overlay/bar_overlay.apk",
 			overrides:         []string{"foo_overlay"},
 			targetVariant:     "android_common_bar",
 			packageFlag:       "com.android.bar.overlay",
@@ -233,7 +235,7 @@
 		},
 	}
 	for _, expected := range expectedVariants {
-		variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
+		variant := ctx.ModuleForTests(t, "foo_overlay", expected.variantName)
 
 		// Check the final apk name
 		variant.Output(expected.apkPath)
@@ -283,28 +285,28 @@
 	}{
 		{
 			name:         "device_specific",
-			expectedPath: "out/soong/target/product/test_device/odm/overlay",
+			expectedPath: "out/target/product/test_device/odm/overlay",
 		},
 		{
 			name:         "soc_specific",
-			expectedPath: "out/soong/target/product/test_device/vendor/overlay",
+			expectedPath: "out/target/product/test_device/vendor/overlay",
 		},
 		{
 			name:         "system_ext_specific",
-			expectedPath: "out/soong/target/product/test_device/system_ext/overlay",
+			expectedPath: "out/target/product/test_device/system_ext/overlay",
 		},
 		{
 			name:         "product_specific",
-			expectedPath: "out/soong/target/product/test_device/product/overlay",
+			expectedPath: "out/target/product/test_device/product/overlay",
 		},
 		{
 			name:         "default",
-			expectedPath: "out/soong/target/product/test_device/product/overlay",
+			expectedPath: "out/target/product/test_device/product/overlay",
 		},
 	}
 	for _, testCase := range testCases {
 		ctx, _ := testJava(t, bp)
-		mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*RuntimeResourceOverlay)
+		mod := ctx.ModuleForTests(t, testCase.name, "android_common").Module().(*RuntimeResourceOverlay)
 		android.AssertPathRelativeToTopEquals(t, "Install dir is not correct for "+testCase.name, testCase.expectedPath, mod.installDir)
 	}
 }
@@ -339,7 +341,7 @@
 		}
 	`)
 
-	foo := result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests(t, "foo", "android_common")
 
 	// runtime_resource_overlay module depends on aconfig_declarations listed in flags_packages
 	android.AssertBoolEquals(t, "foo expected to depend on bar", true,
@@ -356,3 +358,21 @@
 		"--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt",
 	)
 }
+
+func TestCanBeDataOfTest(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+	).RunTestWithBp(t, `
+		runtime_resource_overlay {
+			name: "foo",
+			sdk_version: "current",
+		}
+		android_test {
+			name: "bar",
+			data: [
+				":foo",
+			],
+		}
+	`)
+	// Just test that this doesn't get errors
+}
diff --git a/java/sdk.go b/java/sdk.go
index 407fdcf..1dbea3b 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -276,7 +276,7 @@
 func createFrameworkAidl(stubsModules []string, path android.WritablePath, ctx android.SingletonContext) *android.RuleBuilder {
 	stubsJars := make([]android.Paths, len(stubsModules))
 
-	ctx.VisitAllModules(func(module android.Module) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 		// Collect dex jar paths for the modules listed above.
 		if j, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok {
 			name := ctx.ModuleName(module)
@@ -360,7 +360,7 @@
 			"api_fingerprint",
 		}
 		count := 0
-		ctx.VisitAllModules(func(module android.Module) {
+		ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 			name := ctx.ModuleName(module)
 			if android.InList(name, apiTxtFileModules) {
 				cmd.Inputs(android.OutputFilesForModule(ctx, module, ""))
@@ -383,6 +383,10 @@
 	}
 
 	rule.Build("api_fingerprint", "generate api_fingerprint.txt")
+
+	if ctx.Config().BuildOS.Linux() {
+		ctx.DistForGoals([]string{"sdk", "droidcore"}, out)
+	}
 }
 
 func sdkMakeVars(ctx android.MakeVarsContext) {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 7891776..00ba8b2 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -316,6 +316,15 @@
 	return name
 }
 
+func (scopes apiScopes) matchingScopeFromSdkKind(kind android.SdkKind) *apiScope {
+	for _, scope := range scopes {
+		if scope.kind == kind {
+			return scope
+		}
+	}
+	return nil
+}
+
 var (
 	scopeByName    = make(map[string]*apiScope)
 	allScopeNames  []string
@@ -480,6 +489,9 @@
 
 	// Extra libs used when compiling stubs for this scope.
 	Libs []string
+
+	// Name to override the api_surface that is passed down to droidstubs.
+	Api_surface *string
 }
 
 type sdkLibraryProperties struct {
@@ -690,9 +702,9 @@
 		paths.stubsHeaderPath = lib.HeaderJars
 		paths.stubsImplPath = lib.ImplementationJars
 
-		libDep := dep.(UsesLibraryDependency)
-		paths.stubsDexJarPath = libDep.DexJarBuildPath(ctx)
-		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath(ctx)
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider)
+		paths.stubsDexJarPath = libDep.DexJarBuildPath
+		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath
 		return nil
 	} else {
 		return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
@@ -706,8 +718,8 @@
 			paths.stubsImplPath = lib.ImplementationJars
 		}
 
-		libDep := dep.(UsesLibraryDependency)
-		paths.stubsDexJarPath = libDep.DexJarBuildPath(ctx)
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider)
+		paths.stubsDexJarPath = libDep.DexJarBuildPath
 		return nil
 	} else {
 		return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
@@ -720,58 +732,67 @@
 			paths.stubsImplPath = lib.ImplementationJars
 		}
 
-		libDep := dep.(UsesLibraryDependency)
-		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath(ctx)
+		libDep := android.OtherModuleProviderOrDefault(ctx, dep, JavaInfoProvider)
+		paths.exportableStubsDexJarPath = libDep.DexJarBuildPath
 		return nil
 	} else {
 		return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library")
 	}
 }
 
-func (paths *scopePaths) treatDepAsApiStubsProvider(dep android.Module, action func(provider ApiStubsProvider) error) error {
-	if apiStubsProvider, ok := dep.(ApiStubsProvider); ok {
-		err := action(apiStubsProvider)
+func (paths *scopePaths) treatDepAsApiStubsProvider(ctx android.ModuleContext, dep android.Module,
+	action func(*DroidStubsInfo, *StubsSrcInfo) error) error {
+	apiStubsProvider, ok := android.OtherModuleProvider(ctx, dep, DroidStubsInfoProvider)
+	if !ok {
+		return fmt.Errorf("expected module that provides DroidStubsInfo, e.g. droidstubs")
+	}
+
+	apiStubsSrcProvider, ok := android.OtherModuleProvider(ctx, dep, StubsSrcInfoProvider)
+	if !ok {
+		return fmt.Errorf("expected module that provides StubsSrcInfo, e.g. droidstubs")
+	}
+	return action(&apiStubsProvider, &apiStubsSrcProvider)
+}
+
+func (paths *scopePaths) treatDepAsApiStubsSrcProvider(
+	ctx android.ModuleContext, dep android.Module, action func(provider *StubsSrcInfo) error) error {
+	if apiStubsProvider, ok := android.OtherModuleProvider(ctx, dep, StubsSrcInfoProvider); ok {
+		err := action(&apiStubsProvider)
 		if err != nil {
 			return err
 		}
 		return nil
 	} else {
-		return fmt.Errorf("expected module that implements ExportableApiStubsSrcProvider, e.g. droidstubs")
+		return fmt.Errorf("expected module that provides DroidStubsInfo, e.g. droidstubs")
 	}
 }
 
-func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, action func(provider ApiStubsSrcProvider) error) error {
-	if apiStubsProvider, ok := dep.(ApiStubsSrcProvider); ok {
-		err := action(apiStubsProvider)
-		if err != nil {
-			return err
-		}
-		return nil
-	} else {
-		return fmt.Errorf("expected module that implements ApiStubsSrcProvider, e.g. droidstubs")
+func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider *DroidStubsInfo, stubsType StubsType) error {
+	var currentApiFilePathErr, removedApiFilePathErr error
+	info, err := getStubsInfoForType(provider, stubsType)
+	if err != nil {
+		return err
 	}
-}
-
-func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider, stubsType StubsType) error {
-	var annotationsZip, currentApiFilePath, removedApiFilePath android.Path
-	annotationsZip, annotationsZipErr := provider.AnnotationsZip(stubsType)
-	currentApiFilePath, currentApiFilePathErr := provider.ApiFilePath(stubsType)
-	removedApiFilePath, removedApiFilePathErr := provider.RemovedApiFilePath(stubsType)
-
-	combinedError := errors.Join(annotationsZipErr, currentApiFilePathErr, removedApiFilePathErr)
+	if info.ApiFile == nil {
+		currentApiFilePathErr = fmt.Errorf("expected module that provides ApiFile")
+	}
+	if info.RemovedApiFile == nil {
+		removedApiFilePathErr = fmt.Errorf("expected module that provides RemovedApiFile")
+	}
+	combinedError := errors.Join(currentApiFilePathErr, removedApiFilePathErr)
 
 	if combinedError == nil {
-		paths.annotationsZip = android.OptionalPathForPath(annotationsZip)
-		paths.currentApiFilePath = android.OptionalPathForPath(currentApiFilePath)
-		paths.removedApiFilePath = android.OptionalPathForPath(removedApiFilePath)
+		paths.annotationsZip = android.OptionalPathForPath(info.AnnotationsZip)
+		paths.currentApiFilePath = android.OptionalPathForPath(info.ApiFile)
+		paths.removedApiFilePath = android.OptionalPathForPath(info.RemovedApiFile)
 	}
 	return combinedError
 }
 
-func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider, stubsType StubsType) error {
-	stubsSrcJar, err := provider.StubsSrcJar(stubsType)
+func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider *StubsSrcInfo, stubsType StubsType) error {
+	path, err := getStubsSrcInfoForType(provider, stubsType)
 	if err == nil {
-		paths.stubsSrcJar = android.OptionalPathForPath(stubsSrcJar)
+		paths.stubsSrcJar = android.OptionalPathForPath(path)
 	}
 	return err
 }
@@ -781,7 +802,7 @@
 	if ctx.Config().ReleaseHiddenApiExportableStubs() {
 		stubsType = Exportable
 	}
-	return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) error {
+	return paths.treatDepAsApiStubsSrcProvider(ctx, dep, func(provider *StubsSrcInfo) error {
 		return paths.extractStubsSourceInfoFromApiStubsProviders(provider, stubsType)
 	})
 }
@@ -791,17 +812,17 @@
 	if ctx.Config().ReleaseHiddenApiExportableStubs() {
 		stubsType = Exportable
 	}
-	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) error {
-		extractApiInfoErr := paths.extractApiInfoFromApiStubsProvider(provider, stubsType)
-		extractStubsSourceInfoErr := paths.extractStubsSourceInfoFromApiStubsProviders(provider, stubsType)
+	return paths.treatDepAsApiStubsProvider(ctx, dep, func(apiStubsProvider *DroidStubsInfo, apiStubsSrcProvider *StubsSrcInfo) error {
+		extractApiInfoErr := paths.extractApiInfoFromApiStubsProvider(apiStubsProvider, stubsType)
+		extractStubsSourceInfoErr := paths.extractStubsSourceInfoFromApiStubsProviders(apiStubsSrcProvider, stubsType)
 		return errors.Join(extractApiInfoErr, extractStubsSourceInfoErr)
 	})
 }
 
-func extractOutputPaths(dep android.Module) (android.Paths, error) {
+func extractOutputPaths(ctx android.ModuleContext, dep android.Module) (android.Paths, error) {
 	var paths android.Paths
-	if sourceFileProducer, ok := dep.(android.SourceFileProducer); ok {
-		paths = sourceFileProducer.Srcs()
+	if sourceFileProducer, ok := android.OtherModuleProvider(ctx, dep, android.SourceFilesInfoProvider); ok {
+		paths = sourceFileProducer.Srcs
 		return paths, nil
 	} else {
 		return nil, fmt.Errorf("module %q does not produce source files", dep)
@@ -809,17 +830,47 @@
 }
 
 func (paths *scopePaths) extractLatestApiPath(ctx android.ModuleContext, dep android.Module) error {
-	outputPaths, err := extractOutputPaths(dep)
+	outputPaths, err := extractOutputPaths(ctx, dep)
 	paths.latestApiPaths = outputPaths
 	return err
 }
 
 func (paths *scopePaths) extractLatestRemovedApiPath(ctx android.ModuleContext, dep android.Module) error {
-	outputPaths, err := extractOutputPaths(dep)
+	outputPaths, err := extractOutputPaths(ctx, dep)
 	paths.latestRemovedApiPaths = outputPaths
 	return err
 }
 
+func getStubsInfoForType(info *DroidStubsInfo, stubsType StubsType) (ret *StubsInfo, err error) {
+	switch stubsType {
+	case Everything:
+		ret, err = &info.EverythingStubsInfo, nil
+	case Exportable:
+		ret, err = &info.ExportableStubsInfo, nil
+	default:
+		ret, err = nil, fmt.Errorf("stubs info not supported for the stub type %s", stubsType.String())
+	}
+	if ret == nil && err == nil {
+		err = fmt.Errorf("stubs info is null for the stub type %s", stubsType.String())
+	}
+	return ret, err
+}
+
+func getStubsSrcInfoForType(info *StubsSrcInfo, stubsType StubsType) (ret android.Path, err error) {
+	switch stubsType {
+	case Everything:
+		ret, err = info.EverythingStubsSrcJar, nil
+	case Exportable:
+		ret, err = info.ExportableStubsSrcJar, nil
+	default:
+		ret, err = nil, fmt.Errorf("stubs src info not supported for the stub type %s", stubsType.String())
+	}
+	if ret == nil && err == nil {
+		err = fmt.Errorf("stubs src info is null for the stub type %s", stubsType.String())
+	}
+	return ret, err
+}
+
 type commonToSdkLibraryAndImportProperties struct {
 	// Specifies whether this module can be used as an Android shared library; defaults
 	// to true.
@@ -880,6 +931,8 @@
 	RootLibraryName() string
 }
 
+var _ android.ApexModule = (*SdkLibrary)(nil)
+
 func (m *SdkLibrary) RootLibraryName() string {
 	return m.BaseModuleName()
 }
@@ -908,9 +961,9 @@
 	// This is non-empty only when api_only is false.
 	implLibraryHeaderJars android.Paths
 
-	// The reference to the implementation library created by the source module.
-	// Is nil if the source module does not exist.
-	implLibraryModule *Library
+	// The reference to the JavaInfo provided by implementation library created by
+	// the source module. Is nil if the source module does not exist.
+	implLibraryInfo *JavaInfo
 }
 
 func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImportModule) {
@@ -1170,6 +1223,8 @@
 
 	// Whether if this can be used as a shared library.
 	SharedLibrary bool
+
+	Prebuilt bool
 }
 
 var SdkLibraryInfoProvider = blueprint.NewProvider[SdkLibraryInfo]()
@@ -1200,7 +1255,8 @@
 
 	commonToSdkLibraryAndImport
 
-	builtInstalledForApex []dexpreopterInstall
+	apexSystemServerDexpreoptInstalls []DexpreopterInstall
+	apexSystemServerDexJars           android.Paths
 }
 
 func (module *SdkLibrary) generateTestAndSystemScopesByDefault() bool {
@@ -1211,16 +1267,16 @@
 
 // To satisfy the UsesLibraryDependency interface
 func (module *SdkLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath {
-	if module.implLibraryModule != nil {
-		return module.implLibraryModule.DexJarBuildPath(ctx)
+	if module.implLibraryInfo != nil {
+		return module.implLibraryInfo.DexJarFile
 	}
 	return makeUnsetDexJarPath()
 }
 
 // To satisfy the UsesLibraryDependency interface
 func (module *SdkLibrary) DexJarInstallPath() android.Path {
-	if module.implLibraryModule != nil {
-		return module.implLibraryModule.DexJarInstallPath()
+	if module.implLibraryInfo != nil {
+		return module.implLibraryInfo.InstallFile
 	}
 	return nil
 }
@@ -1281,10 +1337,10 @@
 
 func CheckMinSdkVersion(ctx android.ModuleContext, module *Library) {
 	android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.BaseModuleContext, do android.PayloadDepsCallback) {
-		ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
-			isExternal := !module.depIsInSameApex(ctx, child)
-			if am, ok := child.(android.ApexModule); ok {
-				if !do(ctx, parent, am, isExternal) {
+		ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+			isExternal := !android.IsDepInSameApex(ctx, module, child)
+			if am, ok := android.OtherModuleProvider(ctx, child, android.CommonModuleInfoProvider); ok && am.IsApexModule {
+				if !do(ctx, parent, child, isExternal) {
 					return false
 				}
 			}
@@ -1407,11 +1463,11 @@
 	// Collate the components exported by this module. All scope specific modules are exported but
 	// the impl and xml component modules are not.
 	exportedComponents := map[string]struct{}{}
-
+	var implLib android.ModuleProxy
 	// Record the paths to the header jars of the library (stubs and impl).
 	// When this java_sdk_library is depended upon from others via "libs" property,
 	// the recorded paths will be returned depending on the link type of the caller.
-	ctx.VisitDirectDeps(func(to android.Module) {
+	ctx.VisitDirectDepsProxy(func(to android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(to)
 
 		// Extract information from any of the scope specific dependencies.
@@ -1431,7 +1487,8 @@
 		if tag == implLibraryTag {
 			if dep, ok := android.OtherModuleProvider(ctx, to, JavaInfoProvider); ok {
 				module.implLibraryHeaderJars = append(module.implLibraryHeaderJars, dep.HeaderJars...)
-				module.implLibraryModule = to.(*Library)
+				module.implLibraryInfo = dep
+				implLib = to
 			}
 		}
 	})
@@ -1442,44 +1499,44 @@
 		module.hideApexVariantFromMake = true
 	}
 
-	if module.implLibraryModule != nil {
+	if module.implLibraryInfo != nil {
 		if ctx.Device() {
-			module.classesJarPaths = android.Paths{module.implLibraryModule.implementationJarFile}
-			module.bootDexJarPath = module.implLibraryModule.bootDexJarPath
-			module.uncompressDexState = module.implLibraryModule.uncompressDexState
-			module.active = module.implLibraryModule.active
+			module.classesJarPaths = module.implLibraryInfo.ImplementationJars
+			module.bootDexJarPath = module.implLibraryInfo.BootDexJarPath
+			module.uncompressDexState = module.implLibraryInfo.UncompressDexState
+			module.active = module.implLibraryInfo.Active
 		}
 
-		module.outputFile = module.implLibraryModule.outputFile
-		module.dexJarFile = makeDexJarPathFromPath(module.implLibraryModule.dexJarFile.Path())
-		module.headerJarFile = module.implLibraryModule.headerJarFile
-		module.implementationAndResourcesJar = module.implLibraryModule.implementationAndResourcesJar
-		module.builtInstalledForApex = module.implLibraryModule.builtInstalledForApex
-		module.dexpreopter.configPath = module.implLibraryModule.dexpreopter.configPath
-		module.dexpreopter.outputProfilePathOnHost = module.implLibraryModule.dexpreopter.outputProfilePathOnHost
+		module.outputFile = module.implLibraryInfo.OutputFile
+		module.dexJarFile = makeDexJarPathFromPath(module.implLibraryInfo.DexJarFile.Path())
+		module.headerJarFile = module.implLibraryInfo.HeaderJars[0]
+		module.implementationAndResourcesJar = module.implLibraryInfo.ImplementationAndResourcesJars[0]
+		module.apexSystemServerDexpreoptInstalls = module.implLibraryInfo.DexpreopterInfo.ApexSystemServerDexpreoptInstalls
+		module.apexSystemServerDexJars = module.implLibraryInfo.DexpreopterInfo.ApexSystemServerDexJars
+		module.dexpreopter.configPath = module.implLibraryInfo.ConfigPath
+		module.dexpreopter.outputProfilePathOnHost = module.implLibraryInfo.DexpreopterInfo.OutputProfilePathOnHost
 
 		// Properties required for Library.AndroidMkEntries
-		module.logtagsSrcs = module.implLibraryModule.logtagsSrcs
-		module.dexpreopter.builtInstalled = module.implLibraryModule.dexpreopter.builtInstalled
-		module.jacocoReportClassesFile = module.implLibraryModule.jacocoReportClassesFile
-		module.dexer.proguardDictionary = module.implLibraryModule.dexer.proguardDictionary
-		module.dexer.proguardUsageZip = module.implLibraryModule.dexer.proguardUsageZip
-		module.linter.reports = module.implLibraryModule.linter.reports
+		module.logtagsSrcs = module.implLibraryInfo.LogtagsSrcs
+		module.dexpreopter.builtInstalled = module.implLibraryInfo.BuiltInstalled
+		module.jacocoReportClassesFile = module.implLibraryInfo.JacocoReportClassesFile
+		module.dexer.proguardDictionary = module.implLibraryInfo.ProguardDictionary
+		module.dexer.proguardUsageZip = module.implLibraryInfo.ProguardUsageZip
+		module.linter.reports = module.implLibraryInfo.LinterReports
 
-		if lintInfo, ok := android.OtherModuleProvider(ctx, module.implLibraryModule, LintProvider); ok {
+		if lintInfo, ok := android.OtherModuleProvider(ctx, implLib, LintProvider); ok {
 			android.SetProvider(ctx, LintProvider, lintInfo)
 		}
 
 		if !module.Host() {
-			module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile
+			module.hostdexInstallFile = module.implLibraryInfo.HostdexInstallFile
 		}
 
-		if installFilesInfo, ok := android.OtherModuleProvider(ctx, module.implLibraryModule, android.InstallFilesProvider); ok {
+		if installFilesInfo, ok := android.OtherModuleProvider(ctx, implLib, android.InstallFilesProvider); ok {
 			if installFilesInfo.CheckbuildTarget != nil {
 				ctx.CheckbuildFile(installFilesInfo.CheckbuildTarget)
 			}
 		}
-		android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: module.implLibraryModule.uniqueSrcFiles.Strings()})
 	}
 
 	// Make the set of components exported by this module available for use elsewhere.
@@ -1518,17 +1575,39 @@
 		}
 	}
 
-	if module.requiresRuntimeImplementationLibrary() && module.implLibraryModule != nil {
+	if module.requiresRuntimeImplementationLibrary() && module.implLibraryInfo != nil {
 		generatingLibs = append(generatingLibs, module.implLibraryModuleName())
-		setOutputFiles(ctx, module.implLibraryModule.Module)
+		setOutputFilesFromJavaInfo(ctx, module.implLibraryInfo)
 	}
 
+	javaInfo := &JavaInfo{
+		JacocoReportClassesFile: module.jacocoReportClassesFile,
+	}
+	setExtraJavaInfo(ctx, ctx.Module(), javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
 	sdkLibInfo.GeneratingLibs = generatingLibs
+	sdkLibInfo.Prebuilt = false
 	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
-func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall {
-	return module.builtInstalledForApex
+func setOutputFilesFromJavaInfo(ctx android.ModuleContext, info *JavaInfo) {
+	ctx.SetOutputFiles(append(android.PathsIfNonNil(info.OutputFile), info.ExtraOutputFiles...), "")
+	ctx.SetOutputFiles(android.PathsIfNonNil(info.OutputFile), android.DefaultDistTag)
+	ctx.SetOutputFiles(info.ImplementationAndResourcesJars, ".jar")
+	ctx.SetOutputFiles(info.HeaderJars, ".hjar")
+	if info.ProguardDictionary.Valid() {
+		ctx.SetOutputFiles(android.Paths{info.ProguardDictionary.Path()}, ".proguard_map")
+	}
+	ctx.SetOutputFiles(info.GeneratedSrcjars, ".generated_srcjars")
+}
+
+func (module *SdkLibrary) ApexSystemServerDexpreoptInstalls() []DexpreopterInstall {
+	return module.apexSystemServerDexpreoptInstalls
+}
+
+func (module *SdkLibrary) ApexSystemServerDexJars() android.Paths {
+	return module.apexSystemServerDexJars
 }
 
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
@@ -1636,15 +1715,22 @@
 }
 
 // Implements android.ApexModule
-func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
-	depTag := mctx.OtherModuleDependencyTag(dep)
-	if depTag == xmlPermissionsFileTag {
+func (m *SdkLibrary) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return SdkLibraryDepInSameApexChecker{}
+}
+
+type SdkLibraryDepInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m SdkLibraryDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	if tag == xmlPermissionsFileTag {
 		return true
 	}
-	if dep.Name() == module.implLibraryModuleName() {
+	if tag == implLibraryTag {
 		return true
 	}
-	return module.Library.DepIsInSameApex(mctx, dep)
+	return depIsInSameApex(tag)
 }
 
 // Implements android.ApexModule
@@ -1901,10 +1987,6 @@
 
 	commonToSdkLibraryAndImport
 
-	// The reference to the xml permissions module created by the source module.
-	// Is nil if the source module does not exist.
-	xmlPermissionsFileModule *sdkLibraryXml
-
 	// Build path to the dex implementation jar obtained from the prebuilt_apex, if any.
 	dexJarFile    OptionalDexJarPath
 	dexJarFileErr error
@@ -2059,9 +2141,16 @@
 var _ android.ApexModule = (*SdkLibraryImport)(nil)
 
 // Implements android.ApexModule
-func (module *SdkLibraryImport) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
-	depTag := mctx.OtherModuleDependencyTag(dep)
-	if depTag == xmlPermissionsFileTag {
+func (m *SdkLibraryImport) GetDepInSameApexChecker() android.DepInSameApexChecker {
+	return SdkLibraryImportDepIsInSameApexChecker{}
+}
+
+type SdkLibraryImportDepIsInSameApexChecker struct {
+	android.BaseDepInSameApexChecker
+}
+
+func (m SdkLibraryImportDepIsInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool {
+	if tag == xmlPermissionsFileTag {
 		return true
 	}
 
@@ -2071,13 +2160,10 @@
 }
 
 // Implements android.ApexModule
-func (module *SdkLibraryImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	// we don't check prebuilt modules for sdk_version
-	return nil
+func (m *SdkLibraryImport) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
-// Implements android.ApexModule
 func (module *SdkLibraryImport) UniqueApexVariations() bool {
 	return module.uniqueApexVariations()
 }
@@ -2094,7 +2180,7 @@
 	module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar")
 
 	// Record the paths to the prebuilt stubs library and stubs source.
-	ctx.VisitDirectDeps(func(to android.Module) {
+	ctx.VisitDirectDepsProxy(func(to android.ModuleProxy) {
 		tag := ctx.OtherModuleDependencyTag(to)
 
 		// Extract information from any of the scope specific dependencies.
@@ -2106,17 +2192,11 @@
 			// is determined by the nature of the dependency which is determined by the tag.
 			scopeTag.extractDepInfo(ctx, to, scopePaths)
 		} else if tag == implLibraryTag {
-			if implLibrary, ok := to.(*Library); ok {
-				module.implLibraryModule = implLibrary
+			if implInfo, ok := android.OtherModuleProvider(ctx, to, JavaInfoProvider); ok {
+				module.implLibraryInfo = implInfo
 			} else {
 				ctx.ModuleErrorf("implementation library must be of type *java.Library but was %T", to)
 			}
-		} else if tag == xmlPermissionsFileTag {
-			if xmlPermissionsFileModule, ok := to.(*sdkLibraryXml); ok {
-				module.xmlPermissionsFileModule = xmlPermissionsFileModule
-			} else {
-				ctx.ModuleErrorf("xml permissions file module must be of type *sdkLibraryXml but was %T", to)
-			}
 		}
 	})
 	sdkLibInfo := module.generateCommonBuildActions(ctx)
@@ -2156,12 +2236,21 @@
 	}
 
 	module.setOutputFiles(ctx)
-	if module.implLibraryModule != nil {
+	if module.implLibraryInfo != nil {
 		generatingLibs = append(generatingLibs, module.implLibraryModuleName())
-		setOutputFiles(ctx, module.implLibraryModule.Module)
+		setOutputFilesFromJavaInfo(ctx, module.implLibraryInfo)
 	}
 
+	javaInfo := &JavaInfo{}
+	if module.implLibraryInfo != nil {
+		javaInfo.JacocoReportClassesFile = module.implLibraryInfo.JacocoReportClassesFile
+	}
+
+	setExtraJavaInfo(ctx, ctx.Module(), javaInfo)
+	android.SetProvider(ctx, JavaInfoProvider, javaInfo)
+
 	sdkLibInfo.GeneratingLibs = generatingLibs
+	sdkLibInfo.Prebuilt = true
 	android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo)
 }
 
@@ -2177,10 +2266,10 @@
 	if module.dexJarFile.IsSet() {
 		return module.dexJarFile
 	}
-	if module.implLibraryModule == nil {
+	if module.implLibraryInfo == nil {
 		return makeUnsetDexJarPath()
 	} else {
-		return module.implLibraryModule.DexJarBuildPath(ctx)
+		return module.implLibraryInfo.DexJarFile
 	}
 }
 
@@ -2196,10 +2285,10 @@
 
 // to satisfy apex.javaDependency interface
 func (module *SdkLibraryImport) JacocoReportClassesFile() android.Path {
-	if module.implLibraryModule == nil {
+	if module.implLibraryInfo == nil {
 		return nil
 	} else {
-		return module.implLibraryModule.JacocoReportClassesFile()
+		return module.implLibraryInfo.JacocoReportClassesFile
 	}
 }
 
@@ -2212,19 +2301,19 @@
 
 // to satisfy java.ApexDependency interface
 func (module *SdkLibraryImport) HeaderJars() android.Paths {
-	if module.implLibraryModule == nil {
+	if module.implLibraryInfo == nil {
 		return nil
 	} else {
-		return module.implLibraryModule.HeaderJars()
+		return module.implLibraryInfo.HeaderJars
 	}
 }
 
 // to satisfy java.ApexDependency interface
 func (module *SdkLibraryImport) ImplementationAndResourcesJars() android.Paths {
-	if module.implLibraryModule == nil {
+	if module.implLibraryInfo == nil {
 		return nil
 	} else {
-		return module.implLibraryModule.ImplementationAndResourcesJars()
+		return module.implLibraryInfo.ImplementationAndResourcesJars
 	}
 }
 
@@ -2389,8 +2478,7 @@
 	s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk
 	s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk
 
-	implLibrary := sdk.implLibraryModule
-	if implLibrary != nil && implLibrary.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided {
+	if sdk.implLibraryInfo != nil && sdk.implLibraryInfo.ProfileGuided {
 		s.DexPreoptProfileGuided = proptools.BoolPtr(true)
 	}
 }
diff --git a/java/sdk_library_internal.go b/java/sdk_library_internal.go
index 768e57a..f5feabe 100644
--- a/java/sdk_library_internal.go
+++ b/java/sdk_library_internal.go
@@ -15,12 +15,13 @@
 package java
 
 import (
-	"android/soong/android"
-	"android/soong/etc"
 	"fmt"
 	"path"
 	"strings"
 
+	"android/soong/android"
+	"android/soong/etc"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -173,6 +174,20 @@
 	mctx.CreateModule(LibraryFactory, properties...)
 }
 
+// getApiSurfaceForScope returns the api surface name to use for the apiScope. If one is specified
+// in the corresponding ApiScopeProperties.Api_surface property that is used, otherwise the name of
+// the apiScope is used.
+func (module *SdkLibrary) getApiSurfaceForScope(apiScope *apiScope) *string {
+	scopeProperties := module.scopeToProperties[apiScope]
+
+	apiSurface := scopeProperties.Api_surface
+	if apiSurface == nil {
+		apiSurface = &apiScope.name
+	}
+
+	return apiSurface
+}
+
 // Creates the [Droidstubs] module with ".stubs.source.<[apiScope.name]>" that creates stubs
 // source files from the given full source files and also updates and checks the API
 // specification files (i.e. "*-current.txt", "*-removed.txt" files).
@@ -226,7 +241,7 @@
 	props.Srcs = append(props.Srcs, module.properties.Srcs...)
 	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
 	props.Sdk_version = module.deviceProperties.Sdk_version
-	props.Api_surface = &apiScope.name
+	props.Api_surface = module.getApiSurfaceForScope(apiScope)
 	props.System_modules = module.deviceProperties.System_modules
 	props.Installable = proptools.BoolPtr(false)
 	// A droiddoc module has only one Libs property and doesn't distinguish between
@@ -778,7 +793,11 @@
 
 // from android.ApexModule
 func (module *sdkLibraryXml) AvailableFor(what string) bool {
-	return true
+	return android.CheckAvailableForApex(what, module.ApexAvailableFor())
+}
+
+func (module *sdkLibraryXml) ApexAvailableFor() []string {
+	return []string{android.AvailableToPlatform, android.AvailableToAnyApex}
 }
 
 func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -788,10 +807,8 @@
 var _ android.ApexModule = (*sdkLibraryXml)(nil)
 
 // Implements android.ApexModule
-func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
-	// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
-	return nil
+func (m *sdkLibraryXml) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 // File path to the runtime implementation library
@@ -919,6 +936,8 @@
 	ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
 
 	ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "")
+
+	etc.SetCommonPrebuiltEtcInfo(ctx, module)
 }
 
 func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 6031d72..6d27e54 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -25,6 +25,7 @@
 )
 
 func TestJavaSdkLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -114,19 +115,19 @@
 	`)
 
 	// check the existence of the internal modules
-	foo := result.ModuleForTests("foo", "android_common")
-	result.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "")
-	result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
-	result.ModuleForTests("foo.api.public.28", "")
-	result.ModuleForTests("foo.api.system.28", "")
-	result.ModuleForTests("foo.api.test.28", "")
+	foo := result.ModuleForTests(t, "foo", "android_common")
+	result.ModuleForTests(t, apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopePublic.stubsSourceModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopeTest.stubsSourceModuleName("foo"), "android_common")
+	result.ModuleForTests(t, apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "")
+	result.ModuleForTests(t, apiScopePublic.apiLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(t, "foo"+sdkXmlFileSuffix, "android_common")
+	result.ModuleForTests(t, "foo.api.public.28", "")
+	result.ModuleForTests(t, "foo.api.system.28", "")
+	result.ModuleForTests(t, "foo.api.test.28", "")
 
 	exportedComponentsInfo, _ := android.OtherModuleProvider(result, foo.Module(), android.ExportedComponentsInfoProvider)
 	expectedFooExportedComponents := []string{
@@ -146,28 +147,28 @@
 	}
 	android.AssertArrayString(t, "foo exported components", expectedFooExportedComponents, exportedComponentsInfo.Components)
 
-	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
+	bazJavac := result.ModuleForTests(t, "baz", "android_common").Rule("javac")
 	// tests if baz is actually linked to the stubs lib
-	android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar")
+	android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.from-text.jar")
 	// ... and not to the impl lib
 	android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.jar")
 	// test if baz is not linked to the system variant of foo
 	android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.jar")
 
-	bazTestJavac := result.ModuleForTests("baz-test", "android_common").Rule("javac")
+	bazTestJavac := result.ModuleForTests(t, "baz-test", "android_common").Rule("javac")
 	// tests if baz-test is actually linked to the test stubs lib
-	android.AssertStringDoesContain(t, "baz-test javac classpath", bazTestJavac.Args["classpath"], "foo.stubs.test.jar")
+	android.AssertStringDoesContain(t, "baz-test javac classpath", bazTestJavac.Args["classpath"], "foo.stubs.test.from-text.jar")
 
-	baz29Javac := result.ModuleForTests("baz-29", "android_common").Rule("javac")
+	baz29Javac := result.ModuleForTests(t, "baz-29", "android_common").Rule("javac")
 	// tests if baz-29 is actually linked to the system 29 stubs lib
-	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/sdk_system_29_foo/android_common/combined/sdk_system_29_foo.jar")
+	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/sdk_system_29_foo/android_common/local-combined/sdk_system_29_foo.jar")
 
-	bazModule30Javac := result.ModuleForTests("baz-module-30", "android_common").Rule("javac")
+	bazModule30Javac := result.ModuleForTests(t, "baz-module-30", "android_common").Rule("javac")
 	// tests if "baz-module-30" is actually linked to the module 30 stubs lib
-	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/sdk_module-lib_30_foo/android_common/combined/sdk_module-lib_30_foo.jar")
+	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/sdk_module-lib_30_foo/android_common/local-combined/sdk_module-lib_30_foo.jar")
 
 	// test if baz has exported SDK lib names foo and bar to qux
-	qux := result.ModuleForTests("qux", "android_common")
+	qux := result.ModuleForTests(t, "qux", "android_common")
 	if quxLib, ok := qux.Module().(*Library); ok {
 		requiredSdkLibs, optionalSdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
 		android.AssertDeepEquals(t, "qux exports (required)", []string{"fred", "quuz", "foo", "bar"}, requiredSdkLibs)
@@ -175,18 +176,19 @@
 	}
 
 	// test if quuz have created the api_contribution module
-	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("quuz")+".api.contribution", "")
+	result.ModuleForTests(t, apiScopePublic.stubsSourceModuleName("quuz")+".api.contribution", "")
 
-	fooImplDexJar := result.ModuleForTests("foo.impl", "android_common").Rule("d8")
+	fooImplDexJar := result.ModuleForTests(t, "foo.impl", "android_common").Rule("d8")
 	// tests if kotlinc generated files are NOT excluded from output of foo.impl.
 	android.AssertStringDoesNotContain(t, "foo.impl dex", fooImplDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
 
-	barImplDexJar := result.ModuleForTests("bar.impl", "android_common").Rule("d8")
+	barImplDexJar := result.ModuleForTests(t, "bar.impl", "android_common").Rule("d8")
 	// tests if kotlinc generated files are excluded from output of bar.impl.
 	android.AssertStringDoesContain(t, "bar.impl dex", barImplDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -218,7 +220,7 @@
 `)
 
 	// test that updatability attributes are passed on correctly
-	fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Output("fooUpdatable.xml")
+	fooUpdatable := result.ModuleForTests(t, "fooUpdatable.xml", "android_common").Output("fooUpdatable.xml")
 	fooUpdatableContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooUpdatable)
 	android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `on-bootclasspath-since="U"`)
 	android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `on-bootclasspath-before="V"`)
@@ -227,7 +229,7 @@
 
 	// double check that updatability attributes are not written if they don't exist in the bp file
 	// the permissions file for the foo library defined above
-	fooPermissions := result.ModuleForTests("foo.xml", "android_common").Output("foo.xml")
+	fooPermissions := result.ModuleForTests(t, "foo.xml", "android_common").Output("foo.xml")
 	fooPermissionsContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooPermissions)
 	android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `on-bootclasspath-since`)
 	android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `on-bootclasspath-before`)
@@ -236,6 +238,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_Validation_ValidVersion(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -263,6 +266,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_Validation_AtLeastTAttributes(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -292,6 +296,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdk(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -319,6 +324,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdkAndModuleMinSdk(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -347,6 +353,7 @@
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary_usesNewTag(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -363,13 +370,14 @@
 		}
 `)
 	// test that updatability attributes are passed on correctly
-	fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Output("foo.xml")
+	fooUpdatable := result.ModuleForTests(t, "foo.xml", "android_common").Output("foo.xml")
 	fooUpdatableContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooUpdatable)
 	android.AssertStringDoesContain(t, "foo.xml contents", fooUpdatableContents, `<apex-library`)
 	android.AssertStringDoesNotContain(t, "foo.xml contents", fooUpdatableContents, `<library`)
 }
 
 func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -409,12 +417,12 @@
 		{lib: "stub-only-static-lib", in_stub_combined: true},
 	}
 	verify := func(sdklib, dep string, cp, combined bool) {
-		sdklibCp := result.ModuleForTests(sdklib, "android_common").Rule("javac").Args["classpath"]
+		sdklibCp := result.ModuleForTests(t, sdklib, "android_common").Rule("javac").Args["classpath"]
 		expected := cp || combined // Every combined jar is also on the classpath.
 		android.AssertStringContainsEquals(t, "bad classpath for "+sdklib, sdklibCp, "/"+dep+".jar", expected)
 
-		combineJarInputs := result.ModuleForTests(sdklib, "android_common").Rule("combineJar").Inputs.Strings()
-		depPath := filepath.Join("out", "soong", ".intermediates", dep, "android_common", "turbine-combined", dep+".jar")
+		combineJarInputs := result.ModuleForTests(t, sdklib, "android_common").Rule("combineJar").Inputs.Strings()
+		depPath := filepath.Join("out", "soong", ".intermediates", dep, "android_common", "turbine", dep+".jar")
 		android.AssertStringListContainsEquals(t, "bad combined inputs for "+sdklib, combineJarInputs, depPath, combined)
 	}
 	for _, expectation := range expectations {
@@ -426,6 +434,7 @@
 }
 
 func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -448,13 +457,14 @@
 		`)
 
 	// The bar library should depend on the stubs jar.
-	barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	barLibrary := result.ModuleForTests(t, "bar", "android_common").Rule("javac")
+	if expected, actual := `^-classpath .*:out/soong/[^:]*/foo\.stubs\.from-text/foo\.stubs\.from-text\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
 
 func TestJavaSdkLibrary_AccessOutputFiles(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -478,12 +488,13 @@
 }
 
 func TestJavaSdkLibrary_AccessOutputFiles_NoAnnotations(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported module reference tag ".public.annotations.zip"`)).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported output tag ".public.annotations.zip"`)).
 		RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
@@ -503,12 +514,13 @@
 }
 
 func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported module reference tag ".system.stubs.source"`)).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported output tag ".system.stubs.source"`)).
 		RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
@@ -527,6 +539,7 @@
 }
 
 func TestJavaSdkLibrary_Deps(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -557,6 +570,7 @@
 }
 
 func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
+	t.Parallel()
 	prepareForJavaTest.RunTestWithBp(t, `
 		java_sdk_library_import {
 			name: "foo",
@@ -582,6 +596,7 @@
 }
 
 func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) {
+	t.Parallel()
 	bp := `
 		java_sdk_library_import {
 			name: "foo",
@@ -592,6 +607,7 @@
 		`
 
 	t.Run("stubs.source", func(t *testing.T) {
+		t.Parallel()
 		prepareForJavaTest.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.stubs.source"`)).
 			RunTestWithBp(t, bp+`
@@ -607,6 +623,7 @@
 	})
 
 	t.Run("api.txt", func(t *testing.T) {
+		t.Parallel()
 		prepareForJavaTest.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.api.txt"`)).
 			RunTestWithBp(t, bp+`
@@ -621,6 +638,7 @@
 	})
 
 	t.Run("removed-api.txt", func(t *testing.T) {
+		t.Parallel()
 		prepareForJavaTest.
 			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.removed-api.txt"`)).
 			RunTestWithBp(t, bp+`
@@ -636,6 +654,7 @@
 }
 
 func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
+	t.Parallel()
 	prepareForJavaTest.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": enabled api scope "system" depends on disabled scope "public"`)).
 		RunTestWithBp(t, `
@@ -656,6 +675,7 @@
 }
 
 func TestJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -674,6 +694,7 @@
 }
 
 func TestJavaSdkLibrary_ModuleLib(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -694,6 +715,7 @@
 }
 
 func TestJavaSdkLibrary_SystemServer(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -714,6 +736,7 @@
 }
 
 func TestJavaSdkLibrary_SystemServer_AccessToStubScopeLibs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -768,13 +791,13 @@
 
 	stubsPath := func(name string, scope *apiScope) string {
 		name = scope.stubsLibraryModuleName(name)
-		return fmt.Sprintf("out/soong/.intermediates/%[1]s/android_common/turbine-combined/%[1]s.jar", name)
+		return fmt.Sprintf("out/soong/.intermediates/%[1]s.from-text/android_common/%[1]s.from-text/%[1]s.from-text.jar", name)
 	}
 
 	// The bar library should depend on the highest (where system server is highest and public is
 	// lowest) API scopes provided by each of the foo-* modules. The highest API scope provided by the
 	// foo-<x> module is <x>.
-	barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
+	barLibrary := result.ModuleForTests(t, "bar", "android_common").Rule("javac")
 	stubLibraries := []string{
 		stubsPath("foo-public", apiScopePublic),
 		stubsPath("foo-system", apiScopeSystem),
@@ -788,6 +811,7 @@
 }
 
 func TestJavaSdkLibraryImport(t *testing.T) {
+	t.Parallel()
 	result := prepareForJavaTest.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -826,10 +850,10 @@
 		`)
 
 	for _, scope := range []string{"", ".system", ".test"} {
-		fooModule := result.ModuleForTests("foo"+scope, "android_common")
+		fooModule := result.ModuleForTests(t, "foo"+scope, "android_common")
 		javac := fooModule.Rule("javac")
 
-		sdklibStubsJar := result.ModuleForTests("sdklib.stubs"+scope, "android_common").Output("combined/sdklib.stubs" + scope + ".jar").Output
+		sdklibStubsJar := result.ModuleForTests(t, "sdklib.stubs"+scope, "android_common").Output("local-combined/sdklib.stubs" + scope + ".jar").Output
 		android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], sdklibStubsJar.String())
 	}
 
@@ -844,6 +868,7 @@
 }
 
 func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -968,25 +993,25 @@
 	CheckModuleDependencies(t, result.TestContext, "combined", "android_common", []string{
 		// Each use of :sdklib{...} adds a dependency onto prebuilt_sdklib.
 		`prebuilt_sdklib`,
-		`prebuilt_sdklib`,
-		`prebuilt_sdklib`,
 		`prebuilt_sdklib.stubs`,
 		`prebuilt_sdklib.stubs.source`,
 	})
 
 	// Make sure that dependencies on sdklib that resolve to one of the child libraries use the
 	// prebuilt library.
-	public := result.ModuleForTests("public", "android_common")
+	public := result.ModuleForTests(t, "public", "android_common")
 	rule := public.Output("javac/public.jar")
 	inputs := rule.Implicits.Strings()
-	expected := "out/soong/.intermediates/prebuilt_sdklib.stubs/android_common/combined/sdklib.stubs.jar"
+	expected := "out/soong/.intermediates/prebuilt_sdklib.stubs/android_common/local-combined/sdklib.stubs.jar"
 	if !android.InList(expected, inputs) {
 		t.Errorf("expected %q to contain %q", inputs, expected)
 	}
 }
 
 func TestJavaSdkLibraryImport_Preferred(t *testing.T) {
+	t.Parallel()
 	t.Run("prefer", func(t *testing.T) {
+		t.Parallel()
 		testJavaSdkLibraryImport_Preferred(t, "prefer: true,", android.NullFixturePreparer)
 	})
 }
@@ -994,6 +1019,7 @@
 // If a module is listed in `mainline_module_contributions, it should be used
 // It will supersede any other source vs prebuilt selection mechanism like `prefer` attribute
 func TestSdkLibraryImport_MetadataModuleSupersedesPreferred(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex_contributions {
 			name: "my_mainline_module_contributions",
@@ -1093,17 +1119,17 @@
 	).RunTestWithBp(t, bp)
 
 	// Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
-	public := result.ModuleForTests("public", "android_common")
+	public := result.ModuleForTests(t, "public", "android_common")
 	rule := public.Output("javac/public.jar")
 	inputs := rule.Implicits.Strings()
 	expectedInputs := []string{
 		// source
-		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs/android_common/turbine-combined/sdklib.prebuilt_preferred_using_legacy_flags.stubs.jar",
-		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system/android_common/turbine-combined/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.jar",
+		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs.from-text/android_common/sdklib.prebuilt_preferred_using_legacy_flags.stubs.from-text/sdklib.prebuilt_preferred_using_legacy_flags.stubs.from-text.jar",
+		"out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.from-text/android_common/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.from-text/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.from-text.jar",
 
 		// prebuilt
-		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs/android_common/combined/sdklib.source_preferred_using_legacy_flags.stubs.jar",
-		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs.system/android_common/combined/sdklib.source_preferred_using_legacy_flags.stubs.system.jar",
+		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs/android_common/local-combined/sdklib.source_preferred_using_legacy_flags.stubs.jar",
+		"out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs.system/android_common/local-combined/sdklib.source_preferred_using_legacy_flags.stubs.system.jar",
 	}
 	for _, expected := range expectedInputs {
 		if !android.InList(expected, inputs) {
@@ -1113,6 +1139,7 @@
 }
 
 func TestJavaSdkLibraryDist(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaBuildComponents,
 		PrepareForTestWithJavaDefaultModules,
@@ -1179,7 +1206,8 @@
 
 	for _, tt := range testCases {
 		t.Run(tt.module, func(t *testing.T) {
-			m := result.ModuleForTests(apiScopePublic.exportableStubsLibraryModuleName(tt.module), "android_common").Module().(*Library)
+			t.Parallel()
+			m := result.ModuleForTests(t, apiScopePublic.exportableStubsLibraryModuleName(tt.module), "android_common").Module().(*Library)
 			dists := m.Dists()
 			if len(dists) != 1 {
 				t.Fatalf("expected exactly 1 dist entry, got %d", len(dists))
@@ -1195,6 +1223,7 @@
 }
 
 func TestSdkLibrary_CheckMinSdkVersion(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		PrepareForTestWithJavaBuildComponents,
 		PrepareForTestWithJavaDefaultModules,
@@ -1279,6 +1308,7 @@
 }
 
 func TestJavaSdkLibrary_StubOnlyLibs_PassedToDroidstubs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1300,12 +1330,13 @@
 		`)
 
 	// The foo.stubs.source should depend on bar-lib
-	fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs)
+	fooStubsSources := result.ModuleForTests(t, "foo.stubs.source", "android_common").Module().(*Droidstubs)
 	eval := fooStubsSources.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
 	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs.GetOrDefault(eval, nil), "bar-lib")
 }
 
 func TestJavaSdkLibrary_Scope_Libs_PassedToDroidstubs(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1327,12 +1358,13 @@
 		`)
 
 	// The foo.stubs.source should depend on bar-lib
-	fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs)
+	fooStubsSources := result.ModuleForTests(t, "foo.stubs.source", "android_common").Module().(*Droidstubs)
 	eval := fooStubsSources.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
 	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs.GetOrDefault(eval, nil), "bar-lib")
 }
 
 func TestJavaSdkLibrary_ApiLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1377,12 +1409,13 @@
 	}
 
 	for _, c := range testCases {
-		m := result.ModuleForTests(c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary)
+		m := result.ModuleForTests(t, c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary)
 		android.AssertArrayString(t, "Module expected to contain api contributions", c.apiContributions, m.properties.Api_contributions)
 	}
 }
 
 func TestStaticDepStubLibrariesVisibility(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1412,6 +1445,7 @@
 }
 
 func TestSdkLibraryDependency(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1438,12 +1472,13 @@
 		}
 `)
 
-	barPermissions := result.ModuleForTests("bar.xml", "android_common").Output("bar.xml")
+	barPermissions := result.ModuleForTests(t, "bar.xml", "android_common").Output("bar.xml")
 	barContents := android.ContentFromFileRuleForTests(t, result.TestContext, barPermissions)
 	android.AssertStringDoesContain(t, "bar.xml java_sdk_xml command", barContents, `dependency="foo"`)
 }
 
 func TestSdkLibraryExportableStubsLibrary(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1480,8 +1515,8 @@
 	exportableSourceStubsLibraryModuleName := apiScopePublic.exportableSourceStubsLibraryModuleName("foo")
 
 	// Check modules generation
-	result.ModuleForTests(exportableStubsLibraryModuleName, "android_common")
-	result.ModuleForTests(exportableSourceStubsLibraryModuleName, "android_common")
+	result.ModuleForTests(t, exportableStubsLibraryModuleName, "android_common")
+	result.ModuleForTests(t, exportableSourceStubsLibraryModuleName, "android_common")
 
 	// Check static lib dependency
 	android.AssertBoolEquals(t, "exportable top level stubs library module depends on the"+
@@ -1494,6 +1529,7 @@
 // For java libraries depending on java_sdk_library(_import) via libs, assert that
 // rdep gets stubs of source if source is listed in apex_contributions and prebuilt has prefer (legacy mechanism)
 func TestStubResolutionOfJavaSdkLibraryInLibs(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex_contributions {
 			name: "my_mainline_module_contributions",
@@ -1539,14 +1575,16 @@
 
 	result := fixture.RunTestWithBp(t, bp)
 	// Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
-	public := result.ModuleForTests("mymodule", "android_common")
+	public := result.ModuleForTests(t, "mymodule", "android_common")
 	rule := public.Output("javac/mymodule.jar")
 	inputs := rule.Implicits.Strings()
-	android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar")
+	android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs,
+		"out/soong/.intermediates/sdklib.stubs.from-text/android_common/sdklib.stubs.from-text/sdklib.stubs.from-text.jar")
 }
 
 // test that rdep gets resolved to the correct version of a java_sdk_library (source or a specific prebuilt)
 func TestMultipleSdkLibraryPrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		apex_contributions {
 			name: "my_mainline_module_contributions",
@@ -1599,17 +1637,17 @@
 		{
 			desc:                   "Source library is selected using apex_contributions",
 			selectedDependencyName: "sdklib",
-			expectedStubPath:       "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar",
+			expectedStubPath:       "out/soong/.intermediates/sdklib.stubs.from-text/android_common/sdklib.stubs.from-text/sdklib.stubs.from-text.jar",
 		},
 		{
 			desc:                   "Prebuilt library v1 is selected using apex_contributions",
 			selectedDependencyName: "prebuilt_sdklib.v1",
-			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v1.stubs/android_common/combined/sdklib.stubs.jar",
+			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v1.stubs/android_common/local-combined/sdklib.stubs.jar",
 		},
 		{
 			desc:                   "Prebuilt library v2 is selected using apex_contributions",
 			selectedDependencyName: "prebuilt_sdklib.v2",
-			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v2.stubs/android_common/combined/sdklib.stubs.jar",
+			expectedStubPath:       "out/soong/.intermediates/prebuilt_sdklib.v2.stubs/android_common/local-combined/sdklib.stubs.jar",
 		},
 	}
 
@@ -1624,7 +1662,7 @@
 		result := fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
 
 		// Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
-		public := result.ModuleForTests("mymodule", "android_common")
+		public := result.ModuleForTests(t, "mymodule", "android_common")
 		rule := public.Output("javac/mymodule.jar")
 		inputs := rule.Implicits.Strings()
 		android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, tc.expectedStubPath)
@@ -1632,6 +1670,7 @@
 }
 
 func TestStubLinkType(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1668,6 +1707,7 @@
 }
 
 func TestSdkLibDirectDependency(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
@@ -1732,6 +1772,7 @@
 }
 
 func TestSdkLibDirectDependencyWithPrebuiltSdk(t *testing.T) {
+	t.Parallel()
 	android.GroupFixturePreparers(
 		prepareForJavaTest,
 		PrepareForTestWithJavaSdkLibraryFiles,
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 9bfe6a2..6386a00 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -53,6 +53,7 @@
 }
 
 func TestClasspath(t *testing.T) {
+	t.Parallel()
 	const frameworkAidl = "-I" + defaultJavaDir + "/framework/aidl"
 	var classpathTestcases = []classpathTestCase{
 		{
@@ -388,22 +389,18 @@
 		},
 	}
 
-	t.Parallel()
 	t.Run("basic", func(t *testing.T) {
 		t.Parallel()
-		testClasspathTestCases(t, classpathTestcases, false, false)
+		testClasspathTestCases(t, classpathTestcases, false)
 	})
 
 	t.Run("Always_use_prebuilt_sdks=true", func(t *testing.T) {
-		testClasspathTestCases(t, classpathTestcases, true, false)
-	})
-
-	t.Run("UseTransitiveJarsInClasspath", func(t *testing.T) {
-		testClasspathTestCases(t, classpathTestcases, false, true)
+		t.Parallel()
+		testClasspathTestCases(t, classpathTestcases, true)
 	})
 }
 
-func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks, useTransitiveJarsInClasspath bool) {
+func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks bool) {
 	for _, testcase := range classpathTestcases {
 		if testcase.forAlwaysUsePrebuiltSdks != nil && *testcase.forAlwaysUsePrebuiltSdks != alwaysUsePrebuiltSdks {
 			continue
@@ -444,10 +441,8 @@
 					switch {
 					case e == `""`, strings.HasSuffix(e, ".jar"):
 						ret[i] = e
-					case useTransitiveJarsInClasspath:
-						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine", e+".jar")
 					default:
-						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine-combined", e+".jar")
+						ret[i] = filepath.Join("out", "soong", ".intermediates", defaultJavaDir, e, "android_common", "turbine", e+".jar")
 					}
 				}
 				return ret
@@ -499,7 +494,7 @@
 			}
 
 			checkClasspath := func(t *testing.T, result *android.TestResult, isJava8 bool) {
-				foo := result.ModuleForTests("foo", variant(result))
+				foo := result.ModuleForTests(t, "foo", variant(result))
 				javac := foo.Rule("javac")
 				var deps []string
 
@@ -542,9 +537,6 @@
 					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
 				})
 			}
-			if useTransitiveJarsInClasspath {
-				preparer = PrepareForTestWithTransitiveClasspathEnabled
-			}
 
 			fixtureFactory := android.GroupFixturePreparers(
 				prepareForJavaTest,
@@ -571,12 +563,13 @@
 
 			// Test with legacy javac -source 1.8 -target 1.8
 			t.Run("Java language level 8", func(t *testing.T) {
+				t.Parallel()
 				result := fixtureFactory.RunTestWithBp(t, bpJava8)
 
 				checkClasspath(t, result, true /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant(result)).Rule("aidl")
+					aidl := result.ModuleForTests(t, "foo", variant(result)).Rule("aidl")
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
@@ -584,12 +577,13 @@
 
 			// Test with default javac -source 9 -target 9
 			t.Run("Java language level 9", func(t *testing.T) {
+				t.Parallel()
 				result := fixtureFactory.RunTestWithBp(t, bp)
 
 				checkClasspath(t, result, false /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant(result)).Rule("aidl")
+					aidl := result.ModuleForTests(t, "foo", variant(result)).Rule("aidl")
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
@@ -602,6 +596,7 @@
 
 			// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 8 -target 8
 			t.Run("REL + Java language level 8", func(t *testing.T) {
+				t.Parallel()
 				result := android.GroupFixturePreparers(
 					fixtureFactory, prepareWithPlatformVersionRel).RunTestWithBp(t, bpJava8)
 
@@ -610,6 +605,7 @@
 
 			// Test again with PLATFORM_VERSION_CODENAME=REL, javac -source 9 -target 9
 			t.Run("REL + Java language level 9", func(t *testing.T) {
+				t.Parallel()
 				result := android.GroupFixturePreparers(
 					fixtureFactory, prepareWithPlatformVersionRel).RunTestWithBp(t, bp)
 
diff --git a/java/sdk_version_test.go b/java/sdk_version_test.go
index 88351d2..03d55f7 100644
--- a/java/sdk_version_test.go
+++ b/java/sdk_version_test.go
@@ -25,6 +25,7 @@
 }
 
 func TestSystemSdkFromVendor(t *testing.T) {
+	t.Parallel()
 	fixtures := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -57,7 +58,7 @@
 			vendor: true,
 			sdk_version: "system_current",
 		}`)
-	fooModule := result.ModuleForTests("foo", "android_common")
+	fooModule := result.ModuleForTests(t, "foo", "android_common")
 	fooClasspath := fooModule.Rule("javac").Args["classpath"]
 
 	android.AssertStringDoesContain(t, "foo classpath", fooClasspath, "prebuilts/sdk/34/system/android.jar")
diff --git a/java/support_libraries.go b/java/support_libraries.go
index c483fc1..f76eb11 100644
--- a/java/support_libraries.go
+++ b/java/support_libraries.go
@@ -28,7 +28,7 @@
 func supportLibrariesMakeVarsProvider(ctx android.MakeVarsContext) {
 	var supportAars, supportJars []string
 
-	ctx.VisitAllModules(func(module android.Module) {
+	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
 		dir := ctx.ModuleDir(module)
 		switch {
 		case strings.HasPrefix(dir, "prebuilts/sdk/current/extras"),
@@ -47,11 +47,16 @@
 			return
 		}
 
-		switch module.(type) {
-		case *AndroidLibrary, *AARImport:
+		_, isAndroidLibrary := android.OtherModuleProvider(ctx, module, AndroidLibraryInfoProvider)
+		_, isAARImport := android.OtherModuleProvider(ctx, module, AARImportInfoProvider)
+		if isAndroidLibrary || isAARImport {
 			supportAars = append(supportAars, name)
-		case *Library, *Import:
-			supportJars = append(supportJars, name)
+		} else {
+			_, isJavaLibrary := android.OtherModuleProvider(ctx, module, JavaLibraryInfoProvider)
+			_, isJavaPlugin := android.OtherModuleProvider(ctx, module, JavaPluginInfoProvider)
+			if isJavaLibrary && !isJavaPlugin {
+				supportJars = append(supportJars, name)
+			}
 		}
 	})
 
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index b05b0e4..99301bc 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -51,10 +51,11 @@
 `)
 
 func TestJavaSystemModules(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForJavaTest, addSourceSystemModules).RunTest(t)
 
 	// check the existence of the source module
-	sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
+	sourceSystemModules := result.ModuleForTests(t, "system-modules", "android_common")
 	sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the source input modules.
@@ -78,10 +79,11 @@
 `)
 
 func TestJavaSystemModulesImport(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(prepareForJavaTest, addPrebuiltSystemModules).RunTest(t)
 
 	// check the existence of the renamed prebuilt module
-	prebuiltSystemModules := result.ModuleForTests("system-modules", "android_common")
+	prebuiltSystemModules := result.ModuleForTests(t, "system-modules", "android_common")
 	prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the renamed prebuilt input modules.
@@ -90,6 +92,7 @@
 }
 
 func TestJavaSystemModulesMixSourceAndPrebuilt(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForJavaTest,
 		addSourceSystemModules,
@@ -97,7 +100,7 @@
 	).RunTest(t)
 
 	// check the existence of the source module
-	sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
+	sourceSystemModules := result.ModuleForTests(t, "system-modules", "android_common")
 	sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the source input modules.
@@ -105,7 +108,7 @@
 	android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, sourceInputs.RelativeToTop().Strings())
 
 	// check the existence of the renamed prebuilt module
-	prebuiltSystemModules := result.ModuleForTests("prebuilt_system-modules", "android_common")
+	prebuiltSystemModules := result.ModuleForTests(t, "prebuilt_system-modules", "android_common")
 	prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the renamed prebuilt input modules.
@@ -114,6 +117,7 @@
 }
 
 func TestMultipleSystemModulesPrebuilts(t *testing.T) {
+	t.Parallel()
 	bp := `
 		// an rdep
 		java_library {
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 3176ad9..a60f6b8 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -58,6 +58,10 @@
 	return m
 }
 
+func (m *platformSystemServerClasspathModule) UniqueApexVariations() bool {
+	return true
+}
+
 func (p *platformSystemServerClasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) {
 	return p.classpathFragmentBase().androidMkEntries()
 }
@@ -91,8 +95,10 @@
 	properties systemServerClasspathFragmentProperties
 }
 
-func (s *SystemServerClasspathModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
-	return nil
+var _ android.ApexModule = (*SystemServerClasspathModule)(nil)
+
+func (m *SystemServerClasspathModule) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
+	return android.MinApiLevel
 }
 
 type systemServerClasspathFragmentProperties struct {
@@ -116,6 +122,10 @@
 	return m
 }
 
+func (m *SystemServerClasspathModule) UniqueApexVariations() bool {
+	return true
+}
+
 func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(s.properties.Contents.GetOrDefault(ctx, nil)) == 0 && len(s.properties.Standalone_contents.GetOrDefault(ctx, nil)) == 0 {
 		ctx.PropertyErrorf("contents", "Either contents or standalone_contents needs to be non-empty")
diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go
index ba328e7..704f5a4 100644
--- a/java/systemserver_classpath_fragment_test.go
+++ b/java/systemserver_classpath_fragment_test.go
@@ -25,6 +25,7 @@
 )
 
 func TestPlatformSystemServerClasspathVariant(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemServerClasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -39,6 +40,7 @@
 }
 
 func TestPlatformSystemServerClasspath_ClasspathFragmentPaths(t *testing.T) {
+	t.Parallel()
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemServerClasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -50,10 +52,11 @@
 
 	p := result.Module("platform-systemserverclasspath", "android_common").(*platformSystemServerClasspathModule)
 	android.AssertStringEquals(t, "output filepath", "systemserverclasspath.pb", p.ClasspathFragmentBase.outputFilepath.Base())
-	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
+	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
 }
 
 func TestPlatformSystemServerClasspathModule_AndroidMkEntries(t *testing.T) {
+	t.Parallel()
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithSystemServerClasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -97,6 +100,7 @@
 }
 
 func TestSystemServerClasspathFragmentWithoutContents(t *testing.T) {
+	t.Parallel()
 	prepareForTestWithSystemServerClasspath.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
 			`\QEither contents or standalone_contents needs to be non-empty\E`)).
diff --git a/java/testing.go b/java/testing.go
index ba33d90..a6e72cf 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -18,7 +18,6 @@
 	"fmt"
 	"reflect"
 	"regexp"
-	"sort"
 	"strings"
 	"testing"
 
@@ -237,29 +236,6 @@
 	)
 }
 
-func FixtureWithPrebuiltIncrementalApis(apiLevel2Modules map[string][]string) android.FixturePreparer {
-	mockFS := android.MockFS{}
-	path := "prebuilts/sdk/Android.bp"
-
-	bp := fmt.Sprintf(`
-			prebuilt_apis {
-				name: "sdk",
-				api_dirs: ["%s"],
-				allow_incremental_platform_api: true,
-				imports_sdk_version: "none",
-				imports_compile_dex: true,
-			}
-		`, strings.Join(android.SortedKeys(apiLevel2Modules), `", "`))
-
-	for release, modules := range apiLevel2Modules {
-		mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules))
-	}
-	return android.GroupFixturePreparers(
-		android.FixtureAddTextFile(path, bp),
-		android.FixtureMergeMockFs(mockFS),
-	)
-}
-
 func prebuiltApisFilesForModules(apiLevels []string, modules []string) map[string][]byte {
 	libs := append([]string{"android"}, modules...)
 
@@ -378,7 +354,6 @@
 	RegisterAppBuildComponents(ctx)
 	RegisterAppImportBuildComponents(ctx)
 	RegisterAppSetBuildComponents(ctx)
-	registerBootclasspathBuildComponents(ctx)
 	registerBootclasspathFragmentBuildComponents(ctx)
 	RegisterDexpreoptBootJarsComponents(ctx)
 	RegisterDocsBuildComponents(ctx)
@@ -611,19 +586,18 @@
 
 func getModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string) []string {
 	t.Helper()
-	module := ctx.ModuleForTests(name, variant).Module()
+	module := ctx.ModuleForTests(t, name, variant).Module()
 	deps := []string{}
 	ctx.VisitDirectDeps(module, func(m blueprint.Module) {
 		deps = append(deps, m.Name())
 	})
-	sort.Strings(deps)
-
-	return deps
+	return android.SortedUniqueStrings(deps)
 }
 
 // CheckModuleDependencies checks if the expected dependencies of the module are
 // identical to the actual dependencies.
 func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) {
+	t.Helper()
 	deps := getModuleDependencies(t, ctx, name, variant)
 
 	if actual := deps; !reflect.DeepEqual(expected, actual) {
@@ -643,7 +617,7 @@
 
 // CheckModuleHasDependency returns true if the module depends on the expected dependency.
 func CheckModuleHasDependencyWithTag(t *testing.T, ctx *android.TestContext, name, variant string, desiredTag blueprint.DependencyTag, expected string) bool {
-	module := ctx.ModuleForTests(name, variant).Module()
+	module := ctx.ModuleForTests(t, name, variant).Module()
 	found := false
 	ctx.VisitDirectDepsWithTags(module, func(m blueprint.Module, tag blueprint.DependencyTag) {
 		if tag == desiredTag && m.Name() == expected {
@@ -658,7 +632,7 @@
 func CheckPlatformBootclasspathModules(t *testing.T, result *android.TestResult, name string, expected []string) {
 	t.Helper()
 	platformBootclasspath := result.Module(name, "android_common").(*platformBootclasspathModule)
-	pairs := ApexNamePairsFromModules(result.TestContext, platformBootclasspath.configuredModules)
+	pairs := apexNamePairsFromModules(result.TestContext, platformBootclasspath.configuredModules, platformBootclasspath.libraryToApex)
 	android.AssertDeepEquals(t, fmt.Sprintf("%s modules", "platform-bootclasspath"), expected, pairs)
 }
 
@@ -673,23 +647,54 @@
 	android.AssertPathRelativeToTopEquals(t, "install filepath", installDir, info.ClasspathFragmentProtoInstallDir)
 }
 
-// ApexNamePairsFromModules returns the apex:module pair for the supplied modules.
-func ApexNamePairsFromModules(ctx *android.TestContext, modules []android.Module) []string {
+// CheckPlatformBootclasspathDependencies checks the dependencies of the selected module against the expected list.
+//
+// The expected list must be a list of strings of the form "<apex>:<module>", where <apex> is the
+// name of the apex, or platform is it is not part of an apex and <module> is the module name.
+func CheckPlatformBootclasspathDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) {
+	t.Helper()
+	platformBootclasspath := ctx.ModuleForTests(t, name, variant).Module().(*platformBootclasspathModule)
+	modules := []android.Module{}
+	ctx.VisitDirectDeps(platformBootclasspath, func(m blueprint.Module) {
+		modules = append(modules, m.(android.Module))
+	})
+
+	pairs := apexNamePairsFromModules(ctx, modules, platformBootclasspath.libraryToApex)
+	android.AssertDeepEquals(t, "module dependencies", expected, pairs)
+}
+
+// apexNamePairsFromModules returns the apex:module pair for the supplied modules.
+func apexNamePairsFromModules(ctx *android.TestContext, modules []android.Module, modulesToApex map[android.Module]string) []string {
 	pairs := []string{}
 	for _, module := range modules {
-		pairs = append(pairs, apexNamePairFromModule(ctx, module))
+		pairs = append(pairs, apexNamePairFromModule(ctx, module, modulesToApex))
 	}
 	return pairs
 }
 
-func apexNamePairFromModule(ctx *android.TestContext, module android.Module) string {
+// ApexFragmentPairsFromModules returns the apex:fragment pair for the supplied fragments.
+func ApexFragmentPairsFromModules(ctx *android.TestContext, fragments []android.Module, apexNameToFragment map[string]android.Module) []string {
+	pairs := []string{}
+	for _, fragment := range fragments {
+		found := false
+		for apex, apexFragment := range apexNameToFragment {
+			if apexFragment == fragment {
+				pairs = append(pairs, apex+":"+ctx.ModuleName(fragment))
+				found = true
+			}
+		}
+		if !found {
+			pairs = append(pairs, "platform:"+ctx.ModuleName(fragment))
+		}
+	}
+	return pairs
+}
+
+func apexNamePairFromModule(ctx *android.TestContext, module android.Module, modulesToApex map[android.Module]string) string {
 	name := module.Name()
-	var apex string
-	apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
-	if apexInfo.IsForPlatform() {
+	apex := modulesToApex[module]
+	if apex == "" {
 		apex = "platform"
-	} else {
-		apex = apexInfo.InApexVariants[0]
 	}
 
 	return fmt.Sprintf("%s:%s", apex, name)
@@ -700,7 +705,7 @@
 func CheckPlatformBootclasspathFragments(t *testing.T, result *android.TestResult, name string, expected []string) {
 	t.Helper()
 	platformBootclasspath := result.Module(name, "android_common").(*platformBootclasspathModule)
-	pairs := ApexNamePairsFromModules(result.TestContext, platformBootclasspath.fragments)
+	pairs := ApexFragmentPairsFromModules(result.TestContext, platformBootclasspath.fragments, platformBootclasspath.apexNameToFragment)
 	android.AssertDeepEquals(t, fmt.Sprintf("%s fragments", "platform-bootclasspath"), expected, pairs)
 }
 
@@ -723,7 +728,7 @@
 
 // Check that the merged file create by platform_compat_config_singleton has the correct inputs.
 func CheckMergedCompatConfigInputs(t *testing.T, result *android.TestResult, message string, expectedPaths ...string) {
-	sourceGlobalCompatConfig := result.SingletonForTests("platform_compat_config_singleton")
+	sourceGlobalCompatConfig := result.SingletonForTests(t, "platform_compat_config_singleton")
 	allOutputs := sourceGlobalCompatConfig.AllOutputs()
 	android.AssertIntEquals(t, message+": output len", 1, len(allOutputs))
 	output := sourceGlobalCompatConfig.Output(allOutputs[0])
@@ -801,5 +806,3 @@
 		config.installDir = installDir
 	})
 }
-
-var PrepareForTestWithTransitiveClasspathEnabled = android.PrepareForTestWithBuildFlag("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH", "true")
diff --git a/java/tracereferences.go b/java/tracereferences.go
new file mode 100644
index 0000000..342b6a6
--- /dev/null
+++ b/java/tracereferences.go
@@ -0,0 +1,54 @@
+// Copyright 2025 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 java
+
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+var traceReferences = pctx.AndroidStaticRule("traceReferences",
+	blueprint.RuleParams{
+		Command: `${config.TraceReferencesCmd} ` +
+			// Note that we suppress missing def errors, as we're only interested
+			// in the direct deps between the sources and target.
+			`--map-diagnostics:MissingDefinitionsDiagnostic error none ` +
+			`--keep-rules ` +
+			`--output ${out} ` +
+			`--target ${in} ` +
+			// `--source` and `--lib` are already prepended to each
+			// jar reference in the sources and libs joined string args.
+			`${sources} ` +
+			`${libs}`,
+		CommandDeps: []string{"${config.TraceReferencesCmd}"},
+	}, "sources", "libs")
+
+// Generates keep rules in output corresponding to any references from sources
+// (a list of jars) onto target (the referenced jar) that are not included in
+// libs (a list of external jars).
+func TraceReferences(ctx android.ModuleContext, sources android.Paths, target android.Path, libs android.Paths,
+	output android.WritablePath) {
+	ctx.Build(pctx, android.BuildParams{
+		Rule:      traceReferences,
+		Input:     target,
+		Output:    output,
+		Implicits: append(sources, libs...),
+		Args: map[string]string{
+			"sources": android.JoinWithPrefix(sources.Strings(), "--source "),
+			"libs":    android.JoinWithPrefix(libs.Strings(), "--lib "),
+		},
+	})
+}