Merge "Remove more internal fields from ModuleBase." into main
diff --git a/android/Android.bp b/android/Android.bp
index 841a6af..96e8133 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -106,6 +106,7 @@
         "updatable_modules.go",
         "util.go",
         "variable.go",
+        "vintf_fragment.go",
         "visibility.go",
     ],
     testSrcs: [
@@ -148,6 +149,7 @@
         "test_suites_test.go",
         "util_test.go",
         "variable_test.go",
+        "vintf_fragment_test.go",
         "visibility_test.go",
     ],
 }
diff --git a/android/build_prop.go b/android/build_prop.go
index b127755..13d59f9 100644
--- a/android/build_prop.go
+++ b/android/build_prop.go
@@ -56,7 +56,7 @@
 }
 
 func (p *buildPropModule) propFiles(ctx ModuleContext) Paths {
-	partition := p.PartitionTag(ctx.DeviceConfig())
+	partition := p.partition(ctx.DeviceConfig())
 	if partition == "system" {
 		return ctx.Config().SystemPropFiles(ctx)
 	} else if partition == "system_ext" {
diff --git a/android/module.go b/android/module.go
index e9a36ab..b625be4 100644
--- a/android/module.go
+++ b/android/module.go
@@ -111,6 +111,7 @@
 	RequiredModuleNames(ctx ConfigAndErrorContext) []string
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
+	VintfFragmentModuleNames(ctx ConfigAndErrorContext) []string
 
 	// TransitivePackagingSpecs returns the PackagingSpecs for this module and any transitive
 	// dependencies with dependency tags for which IsInstallDepNeeded() returns true.
@@ -494,6 +495,9 @@
 
 	// The team (defined by the owner/vendor) who owns the property.
 	Team *string `android:"path"`
+
+	// vintf_fragment Modules required from this module.
+	Vintf_fragment_modules proptools.Configurable[[]string] `android:"path"`
 }
 
 type distProperties struct {
@@ -1017,6 +1021,7 @@
 	fullManifest := pv.DeviceArch != nil && pv.DeviceName != nil
 	if fullManifest {
 		addRequiredDeps(ctx)
+		addVintfFragmentDeps(ctx)
 	}
 }
 
@@ -1094,6 +1099,16 @@
 	}
 }
 
+var vintfDepTag = struct {
+	blueprint.BaseDependencyTag
+	InstallAlwaysNeededDependencyTag
+}{}
+
+func addVintfFragmentDeps(ctx BottomUpMutatorContext) {
+	mod := ctx.Module()
+	ctx.AddDependency(mod, vintfDepTag, mod.VintfFragmentModuleNames(ctx)...)
+}
+
 // AddProperties "registers" the provided props
 // each value in props MUST be a pointer to a struct
 func (m *ModuleBase) AddProperties(props ...interface{}) {
@@ -1578,6 +1593,10 @@
 	return m.base().commonProperties.Target_required
 }
 
+func (m *ModuleBase) VintfFragmentModuleNames(ctx ConfigAndErrorContext) []string {
+	return m.base().commonProperties.Vintf_fragment_modules.GetOrDefault(m.ConfigurableEvaluator(ctx), nil)
+}
+
 func (m *ModuleBase) InitRc() Paths {
 	return append(Paths{}, m.initRcPaths...)
 }
diff --git a/android/testing.go b/android/testing.go
index a3e35cb..79d0c9b 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -126,6 +126,10 @@
 	ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
 })
 
+var PrepareForTestVintfFragmentModules = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	registerVintfFragmentComponents(ctx)
+})
+
 // Test fixture preparer that will register most java build components.
 //
 // Singletons and mutators should only be added here if they are needed for a majority of java
@@ -149,6 +153,7 @@
 	PrepareForTestWithPackageModule,
 	PrepareForTestWithPrebuilts,
 	PrepareForTestWithVisibility,
+	PrepareForTestVintfFragmentModules,
 )
 
 // Prepares an integration test with all build components from the android package.
diff --git a/android/vintf_fragment.go b/android/vintf_fragment.go
new file mode 100644
index 0000000..329eac9
--- /dev/null
+++ b/android/vintf_fragment.go
@@ -0,0 +1,84 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+type vintfFragmentProperties struct {
+	// Vintf fragment XML file.
+	Src string `android:"path"`
+}
+
+type vintfFragmentModule struct {
+	ModuleBase
+
+	properties vintfFragmentProperties
+
+	installDirPath InstallPath
+	outputFilePath OutputPath
+}
+
+func init() {
+	registerVintfFragmentComponents(InitRegistrationContext)
+}
+
+func registerVintfFragmentComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("vintf_fragment", vintfLibraryFactory)
+}
+
+// vintf_fragment module processes vintf fragment file and installs under etc/vintf/manifest.
+// Vintf fragment files formerly listed in vintf_fragment property would be transformed into
+// this module type.
+func vintfLibraryFactory() Module {
+	m := &vintfFragmentModule{}
+	m.AddProperties(
+		&m.properties,
+	)
+	InitAndroidArchModule(m, DeviceSupported, MultilibFirst)
+
+	return m
+}
+
+func (m *vintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	builder := NewRuleBuilder(pctx, ctx)
+	srcVintfFragment := PathForModuleSrc(ctx, m.properties.Src)
+	processedVintfFragment := PathForModuleOut(ctx, srcVintfFragment.Base())
+
+	// Process vintf fragment source file with assemble_vintf tool
+	builder.Command().
+		Flag("VINTF_IGNORE_TARGET_FCM_VERSION=true").
+		BuiltTool("assemble_vintf").
+		FlagWithInput("-i ", srcVintfFragment).
+		FlagWithOutput("-o ", processedVintfFragment)
+
+	builder.Build("assemble_vintf", "Process vintf fragment "+processedVintfFragment.String())
+
+	m.installDirPath = PathForModuleInstall(ctx, "etc", "vintf", "manifest")
+	m.outputFilePath = processedVintfFragment.OutputPath
+
+	ctx.InstallFile(m.installDirPath, processedVintfFragment.Base(), processedVintfFragment)
+}
+
+// Make this module visible to AndroidMK so it can be referenced from modules defined from Android.mk files
+func (m *vintfFragmentModule) AndroidMkEntries() []AndroidMkEntries {
+	return []AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: OptionalPathForPath(m.outputFilePath),
+		ExtraEntries: []AndroidMkExtraEntriesFunc{
+			func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", m.installDirPath.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", m.outputFilePath.Base())
+			},
+		},
+	}}
+}
diff --git a/android/vintf_fragment_test.go b/android/vintf_fragment_test.go
new file mode 100644
index 0000000..8be534c
--- /dev/null
+++ b/android/vintf_fragment_test.go
@@ -0,0 +1,36 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestVintfManifestBuildAction(t *testing.T) {
+	bp := `
+	vintf_fragment {
+		name: "test_vintf_fragment",
+		src: "test_vintf_file",
+	}
+	`
+
+	testResult := PrepareForTestWithAndroidBuildComponents.RunTestWithBp(t, bp)
+
+	vintfFragmentBuild := testResult.TestContext.ModuleForTests("test_vintf_fragment", "android_arm64_armv8-a").Rule("assemble_vintf")
+	if !strings.Contains(vintfFragmentBuild.RuleParams.Command, "assemble_vintf") {
+		t.Errorf("Vintf_manifest build command does not process with assemble_vintf : " + vintfFragmentBuild.RuleParams.Command)
+	}
+}
diff --git a/apex/apex.go b/apex/apex.go
index fc0500a..dd1c0b5 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2370,6 +2370,7 @@
 	a.providePrebuiltInfo(ctx)
 
 	a.required = a.RequiredModuleNames(ctx)
+	a.required = append(a.required, a.VintfFragmentModuleNames(ctx)...)
 
 	a.setOutputFiles(ctx)
 }
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 65278c9..8cdfb89 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -779,7 +779,7 @@
 func (p *prebuiltCommon) DepsMutator(ctx android.BottomUpMutatorContext) {
 	if p.hasExportedDeps() {
 		// Create a dependency from the prebuilt apex (prebuilt_apex/apex_set) to the internal deapexer module
-		// The deapexer will return a provider that will be bubbled up to the rdeps of apexes (e.g. dex_bootjars)
+		// The deapexer will return a provider which will be used to determine the exported artfifacts from this prebuilt.
 		ctx.AddDependency(ctx.Module(), android.DeapexerTag, deapexerModuleName(p.Name()))
 	}
 }
diff --git a/java/base.go b/java/base.go
index 8a4c64d..070d355 100644
--- a/java/base.go
+++ b/java/base.go
@@ -875,9 +875,12 @@
 	if j.hasSrcExt(".kt") {
 		// TODO(ccross): move this to a mutator pass that can tell if generated sources contain
 		// Kotlin files
-		ctx.AddVariationDependencies(nil, kotlinStdlibTag,
-			"kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8")
-		ctx.AddVariationDependencies(nil, kotlinAnnotationsTag, "kotlin-annotations")
+		tag := staticLibTag
+		if !BoolDefault(j.properties.Static_kotlin_stdlib, true) {
+			tag = libTag
+		}
+		ctx.AddVariationDependencies(nil, tag,
+			"kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-annotations")
 	}
 
 	// Framework libraries need special handling in static coverage builds: they should not have
@@ -1211,7 +1214,6 @@
 
 	var kotlinJars android.Paths
 	var kotlinHeaderJars android.Paths
-	var kotlinExtraJars android.Paths
 
 	// Prepend extraClasspathJars to classpath so that the resource processor R.jar comes before
 	// any dependencies so that it can override any non-final R classes from dependencies with the
@@ -1290,9 +1292,6 @@
 		// Collect common .kt files for AIDEGen
 		j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, kotlinCommonSrcFiles.Strings()...)
 
-		flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
-		flags.classpath = append(flags.classpath, deps.kotlinAnnotations...)
-
 		flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...)
 		flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...)
 
@@ -1322,8 +1321,6 @@
 
 		kotlinJars = append(kotlinJars, kotlinJarPath)
 		kotlinHeaderJars = append(kotlinHeaderJars, kotlinHeaderJar)
-		kotlinExtraJars = append(kotlinExtraJars, deps.kotlinStdlib...)
-		kotlinExtraJars = append(kotlinExtraJars, deps.kotlinAnnotations...)
 	}
 
 	jars := slices.Clone(kotlinJars)
@@ -1342,9 +1339,6 @@
 			// with sharding enabled. See: b/77284273.
 		}
 		extraJars := slices.Clone(kotlinHeaderJars)
-		if BoolDefault(j.properties.Static_kotlin_stdlib, true) {
-			extraJars = append(extraJars, kotlinExtraJars...)
-		}
 		extraJars = append(extraJars, extraCombinedJars...)
 		var combinedHeaderJarFile android.Path
 		headerJarFileWithoutDepsOrJarjar, combinedHeaderJarFile =
@@ -1423,13 +1417,6 @@
 		}
 	}
 
-	// Jar kotlin classes into the final jar after javac
-	if BoolDefault(j.properties.Static_kotlin_stdlib, true) {
-		jars = append(jars, kotlinExtraJars...)
-	} else {
-		flags.dexClasspath = append(flags.dexClasspath, kotlinExtraJars...)
-	}
-
 	jars = append(jars, extraCombinedJars...)
 
 	j.srcJarArgs, j.srcJarDeps = resourcePathsToJarArgs(srcFiles), srcFiles
@@ -2343,10 +2330,6 @@
 				} else {
 					ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
 				}
-			case kotlinStdlibTag:
-				deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...)
-			case kotlinAnnotationsTag:
-				deps.kotlinAnnotations = dep.HeaderJars
 			case kotlinPluginTag:
 				deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...)
 			case syspropPublicStubDepTag:
@@ -2570,8 +2553,6 @@
 					return RenameUseInclude, "tagswitch"
 				case exportedPluginTag:
 					return RenameUseInclude, "tagswitch"
-				case kotlinStdlibTag, kotlinAnnotationsTag:
-					return RenameUseExclude, "tagswitch"
 				case kotlinPluginTag:
 					return RenameUseInclude, "tagswitch"
 				default:
diff --git a/java/builder.go b/java/builder.go
index 5d84d0b..5915235 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -156,7 +156,7 @@
 	turbine, turbineRE = pctx.RemoteStaticRules("turbine",
 		blueprint.RuleParams{
 			Command: `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} $outputFlags ` +
-				`--sources @$out.rsp  --source_jars $srcJars ` +
+				`--sources @$out.rsp ` +
 				`--javacopts ${config.CommonJdkFlags} ` +
 				`$javacFlags -source $javaVersion -target $javaVersion -- $turbineFlags && ` +
 				`(for o in $outputs; do if cmp -s $${o}.tmp $${o} ; then rm $${o}.tmp ; else mv $${o}.tmp $${o} ; fi; done )`,
@@ -170,13 +170,13 @@
 		},
 		&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
 			ExecStrategy:    "${config.RETurbineExecStrategy}",
-			Inputs:          []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
-			RSPFiles:        []string{"${out}.rsp"},
+			Inputs:          []string{"${config.TurbineJar}", "${out}.rsp", "$rbeInputs"},
+			RSPFiles:        []string{"$out.rsp", "$rspFiles"},
 			OutputFiles:     []string{"$rbeOutputs"},
 			ToolchainInputs: []string{"${config.JavaCmd}"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
-		[]string{"javacFlags", "turbineFlags", "outputFlags", "javaVersion", "outputs", "rbeOutputs", "srcJars"}, []string{"implicits"})
+		[]string{"javacFlags", "turbineFlags", "outputFlags", "javaVersion", "outputs", "rbeOutputs"}, []string{"rbeInputs", "rspFiles"})
 
 	jar, jarRE = pctx.RemoteStaticRules("jar",
 		blueprint.RuleParams{
@@ -428,53 +428,72 @@
 		})
 }
 
-func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags, dir string) (string, android.Paths) {
-	var deps android.Paths
+func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags, dir string, srcJars android.Paths) (string, android.Paths, android.Paths, android.Paths) {
+	var implicits android.Paths
+	var rbeInputs android.Paths
+	var rspFiles android.Paths
 
 	classpath := flags.classpath
 
-	var bootClasspath string
+	srcJarArgs := strings.Join(srcJars.Strings(), " ")
+	implicits = append(implicits, srcJars...)
+	const srcJarArgsLimit = 32 * 1024
+	if len(srcJarArgs) > srcJarArgsLimit {
+		srcJarRspFile := android.PathForModuleOut(ctx, "turbine", "srcjars.rsp")
+		android.WriteFileRule(ctx, srcJarRspFile, srcJarArgs)
+		srcJarArgs = "@" + srcJarRspFile.String()
+		implicits = append(implicits, srcJarRspFile)
+		rbeInputs = append(rbeInputs, srcJarRspFile)
+	} else {
+		rbeInputs = append(rbeInputs, srcJars...)
+	}
+
+	var bootClasspathFlags string
 	if flags.javaVersion.usesJavaModules() {
 		var systemModuleDeps android.Paths
-		bootClasspath, systemModuleDeps = flags.systemModules.FormTurbineSystemModulesPath(ctx.Device())
-		deps = append(deps, systemModuleDeps...)
+		bootClasspathFlags, systemModuleDeps = flags.systemModules.FormTurbineSystemModulesPath(ctx.Device())
+		implicits = append(implicits, systemModuleDeps...)
+		rbeInputs = append(rbeInputs, systemModuleDeps...)
 		classpath = append(flags.java9Classpath, classpath...)
 	} else {
-		deps = append(deps, flags.bootClasspath...)
+		implicits = append(implicits, flags.bootClasspath...)
+		rbeInputs = append(rbeInputs, flags.bootClasspath...)
 		if len(flags.bootClasspath) == 0 && ctx.Device() {
 			// explicitly specify -bootclasspath "" if the bootclasspath is empty to
 			// ensure turbine does not fall back to the default bootclasspath.
-			bootClasspath = `--bootclasspath ""`
+			bootClasspathFlags = `--bootclasspath ""`
 		} else {
-			bootClasspath = flags.bootClasspath.FormTurbineClassPath("--bootclasspath ")
+			bootClasspathFlags = flags.bootClasspath.FormTurbineClassPath("--bootclasspath ")
 		}
 	}
 
-	deps = append(deps, classpath...)
-	turbineFlags := bootClasspath + " " + classpath.FormTurbineClassPath("--classpath ")
-
-	const flagsLimit = 32 * 1024
-	if len(turbineFlags) > flagsLimit {
-		flagsRspFile := android.PathForModuleOut(ctx, dir, "turbine-flags.rsp")
-		android.WriteFileRule(ctx, flagsRspFile, turbineFlags)
-		turbineFlags = "@" + flagsRspFile.String()
-		deps = append(deps, flagsRspFile)
+	classpathFlags := classpath.FormTurbineClassPath("")
+	implicits = append(implicits, classpath...)
+	const classpathLimit = 32 * 1024
+	if len(classpathFlags) > classpathLimit {
+		classpathRspFile := android.PathForModuleOut(ctx, dir, "classpath.rsp")
+		android.WriteFileRule(ctx, classpathRspFile, classpathFlags)
+		classpathFlags = "@" + classpathRspFile.String()
+		implicits = append(implicits, classpathRspFile)
+		rspFiles = append(rspFiles, classpathRspFile)
+		rbeInputs = append(rbeInputs, classpathRspFile)
+	} else {
+		rbeInputs = append(rbeInputs, classpath...)
 	}
 
-	return turbineFlags, deps
+	turbineFlags := "--source_jars " + srcJarArgs + " " + bootClasspathFlags + " --classpath " + classpathFlags
+
+	return turbineFlags, implicits, rbeInputs, rspFiles
 }
 
 func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
 
-	turbineFlags, deps := turbineFlags(ctx, flags, "turbine")
-
-	deps = append(deps, srcJars...)
+	turbineFlags, implicits, rbeInputs, rspFiles := turbineFlags(ctx, flags, "turbine", srcJars)
 
 	rule := turbine
 	args := map[string]string{
 		"javacFlags":   flags.javacFlags,
-		"srcJars":      strings.Join(srcJars.Strings(), " "),
 		"javaVersion":  flags.javaVersion.String(),
 		"turbineFlags": turbineFlags,
 		"outputFlags":  "--output " + outputFile.String() + ".tmp",
@@ -482,15 +501,16 @@
 	}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
 		rule = turbineRE
-		args["implicits"] = strings.Join(deps.Strings(), ",")
+		args["rbeInputs"] = strings.Join(rbeInputs.Strings(), ",")
 		args["rbeOutputs"] = outputFile.String() + ".tmp"
+		args["rspFiles"] = strings.Join(rspFiles.Strings(), ",")
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        rule,
 		Description: "turbine",
 		Output:      outputFile,
 		Inputs:      srcFiles,
-		Implicits:   deps,
+		Implicits:   implicits,
 		Args:        args,
 	})
 }
@@ -499,11 +519,10 @@
 func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.WritablePath,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
 
-	turbineFlags, deps := turbineFlags(ctx, flags, "kapt")
+	turbineFlags, implicits, rbeInputs, rspFiles := turbineFlags(ctx, flags, "turbine-apt", srcJars)
 
-	deps = append(deps, srcJars...)
-
-	deps = append(deps, flags.processorPath...)
+	implicits = append(implicits, flags.processorPath...)
+	rbeInputs = append(rbeInputs, flags.processorPath...)
 	turbineFlags += " " + flags.processorPath.FormTurbineClassPath("--processorpath ")
 	turbineFlags += " --processors " + strings.Join(flags.processors, " ")
 
@@ -514,7 +533,6 @@
 	rule := turbine
 	args := map[string]string{
 		"javacFlags":   flags.javacFlags,
-		"srcJars":      strings.Join(srcJars.Strings(), " "),
 		"javaVersion":  flags.javaVersion.String(),
 		"turbineFlags": turbineFlags,
 		"outputFlags":  outputFlags,
@@ -522,8 +540,9 @@
 	}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
 		rule = turbineRE
-		args["implicits"] = strings.Join(deps.Strings(), ",")
+		args["rbeInputs"] = strings.Join(rbeInputs.Strings(), ",")
 		args["rbeOutputs"] = outputSrcJar.String() + ".tmp," + outputResJar.String() + ".tmp"
+		args["rspFiles"] = strings.Join(rspFiles.Strings(), ",")
 	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:            rule,
@@ -531,7 +550,7 @@
 		Output:          outputs[0],
 		ImplicitOutputs: outputs[1:],
 		Inputs:          srcFiles,
-		Implicits:       deps,
+		Implicits:       implicits,
 		Args:            args,
 	})
 }
diff --git a/java/java.go b/java/java.go
index 126d8f3..0d2704d 100644
--- a/java/java.go
+++ b/java/java.go
@@ -421,8 +421,6 @@
 	bootClasspathTag        = dependencyTag{name: "bootclasspath", runtimeLinked: true}
 	systemModulesTag        = dependencyTag{name: "system modules", runtimeLinked: true}
 	frameworkResTag         = dependencyTag{name: "framework-res"}
-	kotlinStdlibTag         = dependencyTag{name: "kotlin-stdlib", runtimeLinked: true}
-	kotlinAnnotationsTag    = dependencyTag{name: "kotlin-annotations", runtimeLinked: true}
 	kotlinPluginTag         = dependencyTag{name: "kotlin-plugin", toolchain: true}
 	proguardRaiseTag        = dependencyTag{name: "proguard-raise"}
 	certificateTag          = dependencyTag{name: "certificate"}
@@ -458,8 +456,6 @@
 		bootClasspathTag,
 		systemModulesTag,
 		java9LibTag,
-		kotlinStdlibTag,
-		kotlinAnnotationsTag,
 		kotlinPluginTag,
 		syspropPublicStubDepTag,
 		instrumentationForTag,
@@ -568,8 +564,6 @@
 	srcJars                 android.Paths
 	systemModules           *systemModules
 	aidlPreprocess          android.OptionalPath
-	kotlinStdlib            android.Paths
-	kotlinAnnotations       android.Paths
 	kotlinPlugins           android.Paths
 	aconfigProtoFiles       android.Paths
 
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 933fc51..844e974 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"slices"
 	"strconv"
 	"strings"
 	"testing"
@@ -23,10 +24,11 @@
 )
 
 func TestKotlin(t *testing.T) {
-	ctx, _ := testJava(t, `
+	bp := `
 		java_library {
 			name: "foo",
 			srcs: ["a.java", "b.kt"],
+			static_libs: ["quz"],
 		}
 
 		java_library {
@@ -39,85 +41,156 @@
 		java_library {
 			name: "baz",
 			srcs: ["c.java"],
+			static_libs: ["quz"],
 		}
-		`)
 
-	kotlinStdlib := ctx.ModuleForTests("kotlin-stdlib", "android_common").
-		Output("turbine-combined/kotlin-stdlib.jar").Output
-	kotlinStdlibJdk7 := ctx.ModuleForTests("kotlin-stdlib-jdk7", "android_common").
-		Output("turbine-combined/kotlin-stdlib-jdk7.jar").Output
-	kotlinStdlibJdk8 := ctx.ModuleForTests("kotlin-stdlib-jdk8", "android_common").
-		Output("turbine-combined/kotlin-stdlib-jdk8.jar").Output
-	kotlinAnnotations := ctx.ModuleForTests("kotlin-annotations", "android_common").
-		Output("turbine-combined/kotlin-annotations.jar").Output
+		java_library {
+			name: "quz",
+			srcs: ["d.kt"],
+		}`
 
-	fooKotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
-	fooJavac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
-	fooJar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
-	fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar")
-
-	fooKotlincClasses := fooKotlinc.Output
-	fooKotlincHeaderClasses := fooKotlinc.ImplicitOutput
-
-	if len(fooKotlinc.Inputs) != 2 || fooKotlinc.Inputs[0].String() != "a.java" ||
-		fooKotlinc.Inputs[1].String() != "b.kt" {
-		t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, fooKotlinc.Inputs)
+	kotlinStdlibTurbineCombinedJars := []string{
+		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/turbine-combined/kotlin-stdlib.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/turbine-combined/kotlin-stdlib-jdk7.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk8/android_common/turbine-combined/kotlin-stdlib-jdk8.jar",
+		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/turbine-combined/kotlin-annotations.jar",
 	}
 
-	if len(fooJavac.Inputs) != 1 || fooJavac.Inputs[0].String() != "a.java" {
-		t.Errorf(`foo inputs %v != ["a.java"]`, fooJavac.Inputs)
+	kotlinStdlibJavacJars := []string{
+		"out/soong/.intermediates/default/java/kotlin-stdlib/android_common/javac/kotlin-stdlib.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk7/android_common/javac/kotlin-stdlib-jdk7.jar",
+		"out/soong/.intermediates/default/java/kotlin-stdlib-jdk8/android_common/javac/kotlin-stdlib-jdk8.jar",
+		"out/soong/.intermediates/default/java/kotlin-annotations/android_common/javac/kotlin-annotations.jar",
 	}
 
-	if !strings.Contains(fooJavac.Args["classpath"], fooKotlincHeaderClasses.String()) {
-		t.Errorf("foo classpath %v does not contain %q",
-			fooJavac.Args["classpath"], fooKotlincHeaderClasses.String())
+	bootclasspathTurbineCombinedJars := []string{
+		"out/soong/.intermediates/default/java/stable.core.platform.api.stubs/android_common/turbine-combined/stable.core.platform.api.stubs.jar",
+		"out/soong/.intermediates/default/java/core-lambda-stubs/android_common/turbine-combined/core-lambda-stubs.jar",
 	}
 
-	if !inList(fooKotlincClasses.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %q",
-			fooJar.Inputs.Strings(), fooKotlincClasses.String())
+	frameworkTurbineCombinedJars := []string{
+		"out/soong/.intermediates/default/java/ext/android_common/turbine-combined/ext.jar",
+		"out/soong/.intermediates/default/java/framework/android_common/turbine-combined/framework.jar",
 	}
 
-	if !inList(kotlinStdlib.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinStdlib.String())
+	testCases := []struct {
+		name string
+
+		preparer android.FixturePreparer
+
+		fooKotlincInputs        []string
+		fooJavacInputs          []string
+		fooKotlincClasspath     []string
+		fooJavacClasspath       []string
+		fooCombinedInputs       []string
+		fooHeaderCombinedInputs []string
+
+		barKotlincInputs        []string
+		barKotlincClasspath     []string
+		barCombinedInputs       []string
+		barHeaderCombinedInputs []string
+	}{
+		{
+			name:             "normal",
+			preparer:         android.NullFixturePreparer,
+			fooKotlincInputs: []string{"a.java", "b.kt"},
+			fooJavacInputs:   []string{"a.java"},
+			fooKotlincClasspath: slices.Concat(
+				bootclasspathTurbineCombinedJars,
+				frameworkTurbineCombinedJars,
+				[]string{"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar"},
+				kotlinStdlibTurbineCombinedJars,
+			),
+			fooJavacClasspath: slices.Concat(
+				[]string{"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar"},
+				frameworkTurbineCombinedJars,
+				[]string{"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar"},
+				kotlinStdlibTurbineCombinedJars,
+			),
+			fooCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/foo/android_common/kotlin/foo.jar",
+					"out/soong/.intermediates/foo/android_common/javac/foo.jar",
+					"out/soong/.intermediates/quz/android_common/combined/quz.jar",
+				},
+				kotlinStdlibJavacJars,
+			),
+			fooHeaderCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/foo/android_common/turbine/foo.jar",
+					"out/soong/.intermediates/foo/android_common/kotlin_headers/foo.jar",
+					"out/soong/.intermediates/quz/android_common/turbine-combined/quz.jar",
+				},
+				kotlinStdlibTurbineCombinedJars,
+			),
+
+			barKotlincInputs: []string{"b.kt"},
+			barKotlincClasspath: slices.Concat(
+				bootclasspathTurbineCombinedJars,
+				frameworkTurbineCombinedJars,
+				[]string{
+					"out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar",
+					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
+				},
+				kotlinStdlibTurbineCombinedJars,
+			),
+			barCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/bar/android_common/kotlin/bar.jar",
+					"out/soong/.intermediates/baz/android_common/combined/baz.jar",
+				},
+				kotlinStdlibJavacJars,
+				[]string{},
+			),
+			barHeaderCombinedInputs: slices.Concat(
+				[]string{
+					"out/soong/.intermediates/bar/android_common/kotlin_headers/bar.jar",
+					"out/soong/.intermediates/baz/android_common/turbine-combined/baz.jar",
+				},
+				kotlinStdlibTurbineCombinedJars,
+			),
+		},
 	}
 
-	if !inList(kotlinStdlibJdk7.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinStdlibJdk7.String())
-	}
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			result := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				tt.preparer,
+			).RunTestWithBp(t, bp)
+			foo := result.ModuleForTests("foo", "android_common")
+			fooKotlinc := foo.Rule("kotlinc")
+			android.AssertPathsRelativeToTopEquals(t, "foo kotlinc inputs", tt.fooKotlincInputs, fooKotlinc.Inputs)
 
-	if !inList(kotlinStdlibJdk8.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinStdlibJdk8.String())
-	}
+			fooKotlincClasspath := android.ContentFromFileRuleForTests(t, result.TestContext, foo.Output("kotlinc/classpath.rsp"))
+			android.AssertStringPathsRelativeToTopEquals(t, "foo kotlinc classpath", result.Config, tt.fooKotlincClasspath, strings.Fields(fooKotlincClasspath))
 
-	if !inList(kotlinAnnotations.String(), fooJar.Inputs.Strings()) {
-		t.Errorf("foo jar inputs %v does not contain %v",
-			fooJar.Inputs.Strings(), kotlinAnnotations.String())
-	}
+			fooJavac := foo.Rule("javac")
+			android.AssertPathsRelativeToTopEquals(t, "foo javac inputs", tt.fooJavacInputs, fooJavac.Inputs)
 
-	if !inList(fooKotlincHeaderClasses.String(), fooHeaderJar.Inputs.Strings()) {
-		t.Errorf("foo header jar inputs %v does not contain %q",
-			fooHeaderJar.Inputs.Strings(), fooKotlincHeaderClasses.String())
-	}
+			fooJavacClasspath := fooJavac.Args["classpath"]
+			android.AssertStringPathsRelativeToTopEquals(t, "foo javac classpath", result.Config, tt.fooJavacClasspath,
+				strings.Split(strings.TrimPrefix(fooJavacClasspath, "-classpath "), ":"))
 
-	bazHeaderJar := ctx.ModuleForTests("baz", "android_common").Output("turbine-combined/baz.jar")
-	barKotlinc := ctx.ModuleForTests("bar", "android_common").Rule("kotlinc")
+			fooCombinedJar := foo.Output("combined/foo.jar")
+			android.AssertPathsRelativeToTopEquals(t, "foo combined inputs", tt.fooCombinedInputs, fooCombinedJar.Inputs)
 
-	if len(barKotlinc.Inputs) != 1 || barKotlinc.Inputs[0].String() != "b.kt" {
-		t.Errorf(`bar kotlinc inputs %v != ["b.kt"]`, barKotlinc.Inputs)
-	}
+			fooCombinedHeaderJar := foo.Output("turbine-combined/foo.jar")
+			android.AssertPathsRelativeToTopEquals(t, "foo header combined inputs", tt.fooHeaderCombinedInputs, fooCombinedHeaderJar.Inputs)
 
-	if !inList(fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
-		t.Errorf(`expected %q in bar implicits %v`,
-			fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
-	}
+			bar := result.ModuleForTests("bar", "android_common")
+			barKotlinc := bar.Rule("kotlinc")
+			android.AssertPathsRelativeToTopEquals(t, "bar kotlinc inputs", tt.barKotlincInputs, barKotlinc.Inputs)
 
-	if !inList(bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
-		t.Errorf(`expected %q in bar implicits %v`,
-			bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
+			barKotlincClasspath := android.ContentFromFileRuleForTests(t, result.TestContext, bar.Output("kotlinc/classpath.rsp"))
+			android.AssertStringPathsRelativeToTopEquals(t, "bar kotlinc classpath", result.Config, tt.barKotlincClasspath, strings.Fields(barKotlincClasspath))
+
+			barCombinedJar := bar.Output("combined/bar.jar")
+			android.AssertPathsRelativeToTopEquals(t, "bar combined inputs", tt.barCombinedInputs, barCombinedJar.Inputs)
+
+			barCombinedHeaderJar := bar.Output("turbine-combined/bar.jar")
+			android.AssertPathsRelativeToTopEquals(t, "bar header combined inputs", tt.barHeaderCombinedInputs, barCombinedHeaderJar.Inputs)
+		})
 	}
 }