Move installation of dexpreopted system server classpath libraries into apex

System server jars in apexes currently use a heuristic to determine
whether or not they are the "main" variant in order to create rules
that copy their dexpreopt artifacts to the final installed location
and their dex jars to a known location for other modules to reference.
The heuristic depends on knowing all apexes that depend on the
module, which will not be possible when the apex info mutator is
rewritten to support future incremental analysis.

Instead, export the dexpreopt artifacts and dex jars, and let the
apex decide whether or not to install them.

Bug: 372543712
Test: all soong tests pass
Change-Id: I478bc2edceca4614e08e9a695b05d091e5437ba1
diff --git a/android/defs.go b/android/defs.go
index 4dd267a..57fcc9b 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -143,3 +143,13 @@
 		return ctx.Config().RBEWrapper()
 	})
 }
+
+// CopyFileRule creates a ninja rule to copy path to outPath.
+func CopyFileRule(ctx ModuleContext, path Path, outPath OutputPath) {
+	ctx.Build(pctx, BuildParams{
+		Rule:        Cp,
+		Input:       path,
+		Output:      outPath,
+		Description: "copy " + outPath.Base(),
+	})
+}
diff --git a/apex/androidmk.go b/apex/androidmk.go
index ec5ca15..3a81ee4 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -226,11 +226,6 @@
 	required = append(required, a.required...)
 	targetRequired = append(targetRequired, a.TargetRequiredModuleNames()...)
 	hostRequired = append(hostRequired, a.HostRequiredModuleNames()...)
-	for _, fi := range a.filesInfo {
-		required = append(required, fi.requiredModuleNames...)
-		targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
-		hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
-	}
 	android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", moduleNames, a.makeModulesToInstall, required)
 	android.AndroidMkEmitAssignList(w, "LOCAL_TARGET_REQUIRED_MODULES", targetRequired)
 	android.AndroidMkEmitAssignList(w, "LOCAL_HOST_REQUIRED_MODULES", hostRequired)
@@ -261,6 +256,7 @@
 				fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
 				fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
 				fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_SYMLINKS := ", strings.Join(a.compatSymlinks.Strings(), " "))
+				fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS +=", a.extraInstalledPairs.String())
 			}
 			fmt.Fprintln(w, "LOCAL_APEX_KEY_PATH := ", a.apexKeysPath.String())
 
diff --git a/apex/apex.go b/apex/apex.go
index d98cfae..a310671 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -415,6 +415,30 @@
 	Min_sdk_version *string
 }
 
+// installPair stores a path to a built object and its install location.  It is used for holding
+// the installation location of the dexpreopt artifacts for system server jars in apexes that need
+// to be installed when the apex is installed.
+type installPair struct {
+	from android.Path
+	to   android.InstallPath
+}
+
+type installPairs []installPair
+
+// String converts a list of installPair structs to the form accepted by LOCAL_SOONG_INSTALL_PAIRS.
+func (p installPairs) String() string {
+	sb := &strings.Builder{}
+	for i, pair := range p {
+		if i != 0 {
+			sb.WriteByte(' ')
+		}
+		sb.WriteString(pair.from.String())
+		sb.WriteByte(':')
+		sb.WriteString(pair.to.String())
+	}
+	return sb.String()
+}
+
 type apexBundle struct {
 	// Inherited structs
 	android.ModuleBase
@@ -496,6 +520,12 @@
 	// Path where this APEX was installed.
 	installedFile android.InstallPath
 
+	// Extra files that are installed alongside this APEX.
+	extraInstalledFiles android.InstallPaths
+
+	// The source and install locations for extraInstalledFiles for use in LOCAL_SOONG_INSTALL_PAIRS.
+	extraInstalledPairs installPairs
+
 	// fragment for this apex for apexkeys.txt
 	apexKeysPath android.WritablePath
 
@@ -570,13 +600,15 @@
 	// Info for Android.mk Module name of `module` in AndroidMk. Note the generated AndroidMk
 	// module for apexFile is named something like <AndroidMk module name>.<apex name>[<apex
 	// suffix>]
-	androidMkModuleName       string             // becomes LOCAL_MODULE
-	class                     apexFileClass      // becomes LOCAL_MODULE_CLASS
-	moduleDir                 string             // becomes LOCAL_PATH
-	requiredModuleNames       []string           // becomes LOCAL_REQUIRED_MODULES
-	targetRequiredModuleNames []string           // becomes LOCAL_TARGET_REQUIRED_MODULES
-	hostRequiredModuleNames   []string           // becomes LOCAL_HOST_REQUIRED_MODULES
-	dataPaths                 []android.DataPath // becomes LOCAL_TEST_DATA
+	androidMkModuleName string             // becomes LOCAL_MODULE
+	class               apexFileClass      // becomes LOCAL_MODULE_CLASS
+	moduleDir           string             // becomes LOCAL_PATH
+	dataPaths           []android.DataPath // becomes LOCAL_TEST_DATA
+
+	// systemServerDexpreoptInstalls stores the list of dexpreopt artifacts for a system server jar.
+	systemServerDexpreoptInstalls []java.DexpreopterInstall
+	// systemServerDexJars stores the list of dexjars for a system server jar.
+	systemServerDexJars android.Paths
 
 	jacocoReportClassesFile android.Path     // only for javalibs and apps
 	lintInfo                *java.LintInfo   // only for javalibs and apps
@@ -892,6 +924,12 @@
 	// Add a reverse dependency to all_apex_certs singleton module.
 	// all_apex_certs will use this dependency to collect the certificate of this apex.
 	ctx.AddReverseDependency(ctx.Module(), allApexCertsDepTag, "all_apex_certs")
+
+	// TODO: When all branches contain this singleton module, make this strict
+	// TODO: Add this dependency only for mainline prebuilts and not every prebuilt module
+	if ctx.OtherModuleExists("all_apex_contributions") {
+		ctx.AddDependency(ctx.Module(), android.AcDepTag, "all_apex_contributions")
+	}
 }
 
 type allApexCertsDependencyTag struct {
@@ -1501,16 +1539,15 @@
 		af.lintInfo = lintInfo
 	}
 	af.customStem = module.Stem() + ".jar"
+	// Collect any system server dex jars and dexpreopt artifacts for installation alongside the apex.
 	// TODO: b/338641779 - Remove special casing of sdkLibrary once bcpf and sscpf depends
 	// on the implementation library
 	if sdkLib, ok := module.(*java.SdkLibrary); ok {
-		for _, install := range sdkLib.BuiltInstalledForApex() {
-			af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
-		}
+		af.systemServerDexpreoptInstalls = append(af.systemServerDexpreoptInstalls, sdkLib.ApexSystemServerDexpreoptInstalls()...)
+		af.systemServerDexJars = append(af.systemServerDexJars, sdkLib.ApexSystemServerDexJars()...)
 	} else if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
-		for _, install := range dexpreopter.DexpreoptBuiltInstalledForApex() {
-			af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
-		}
+		af.systemServerDexpreoptInstalls = append(af.systemServerDexpreoptInstalls, dexpreopter.ApexSystemServerDexpreoptInstalls()...)
+		af.systemServerDexJars = append(af.systemServerDexJars, dexpreopter.ApexSystemServerDexJars()...)
 	}
 	return af
 }
@@ -2258,6 +2295,7 @@
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 4) generate the build rules to create the APEX. This is done in builder.go.
 	a.buildManifest(ctx, vctx.provideNativeLibs, vctx.requireNativeLibs)
+	a.installApexSystemServerFiles(ctx)
 	a.buildApex(ctx)
 	a.buildApexDependencyInfo(ctx)
 	a.buildLintReports(ctx)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index a5b66c1..1382f69 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -9803,7 +9803,16 @@
 	var builder strings.Builder
 	data.Custom(&builder, apexBundle.BaseModuleName(), "TARGET_", "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n")
+	out := ctx.Config().OutDir()
+	ensureContains(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS += "+
+		filepath.Join(out, "soong/.intermediates/foo/android_common_apex10000/dexpreopt/foo/oat/arm64/javalib.odex")+
+		":"+
+		filepath.Join(out, "target/product/test_device/system/framework/oat/arm64/apex@myapex@javalib@foo.jar@classes.odex")+
+		" "+
+		filepath.Join(out, "soong/.intermediates/foo/android_common_apex10000/dexpreopt/foo/oat/arm64/javalib.vdex")+
+		":"+
+		filepath.Join(out, "target/product/test_device/system/framework/oat/arm64/apex@myapex@javalib@foo.jar@classes.vdex")+
+		"\n")
 }
 
 func TestAndroidMk_RequiredModules(t *testing.T) {
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 71a8246..d0bff62 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -320,6 +320,7 @@
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
+			`all_apex_contributions`,
 			`art-bootclasspath-fragment`,
 			`com.android.art.key`,
 			`dex2oatd`,
@@ -425,6 +426,7 @@
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
+			`all_apex_contributions`,
 			`art-bootclasspath-fragment`,
 			`com.android.art.key`,
 			`dex2oatd`,
@@ -698,6 +700,7 @@
 	})
 
 	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`myapex.key`,
 		`mybootclasspathfragment`,
diff --git a/apex/builder.go b/apex/builder.go
index b317472..31d8557 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -20,12 +20,14 @@
 	"path"
 	"path/filepath"
 	"runtime"
+	"slices"
 	"sort"
 	"strconv"
 	"strings"
 
 	"android/soong/aconfig"
 	"android/soong/android"
+	"android/soong/dexpreopt"
 	"android/soong/java"
 
 	"github.com/google/blueprint"
@@ -542,6 +544,64 @@
 	return processed
 }
 
+// installApexSystemServerFiles installs dexpreopt and dexjar files for system server classpath entries
+// provided by the apex.  They are installed here instead of in library module because there may be multiple
+// variants of the library, generally one for the "main" apex and another with a different min_sdk_version
+// for the Android Go version of the apex.  Both variants would attempt to install to the same locations,
+// and the library variants cannot determine which one should.  The apex module is better equipped to determine
+// if it is "selected".
+// This assumes that the jars produced by different min_sdk_version values are identical, which is currently
+// true but may not be true if the min_sdk_version difference between the variants spans version that changed
+// the dex format.
+func (a *apexBundle) installApexSystemServerFiles(ctx android.ModuleContext) {
+	// If performInstalls is set this module is responsible for creating the install rules.
+	performInstalls := a.GetOverriddenBy() == "" && !a.testApex && a.installable()
+	// TODO(b/234351700): Remove once ART does not have separated debug APEX, or make the selection
+	// explicit in the ART Android.bp files.
+	if ctx.Config().UseDebugArt() {
+		if ctx.ModuleName() == "com.android.art" {
+			performInstalls = false
+		}
+	} else {
+		if ctx.ModuleName() == "com.android.art.debug" {
+			performInstalls = false
+		}
+	}
+
+	psi := android.PrebuiltSelectionInfoMap{}
+	ctx.VisitDirectDeps(func(am android.Module) {
+		if info, exists := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); exists {
+			psi = info
+		}
+	})
+
+	if len(psi.GetSelectedModulesForApiDomain(ctx.ModuleName())) > 0 {
+		performInstalls = false
+	}
+
+	for _, fi := range a.filesInfo {
+		for _, install := range fi.systemServerDexpreoptInstalls {
+			var installedFile android.InstallPath
+			if performInstalls {
+				installedFile = ctx.InstallFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
+			} else {
+				// Another module created the install rules, but this module should still depend on
+				// the installed locations.
+				installedFile = install.InstallDirOnDevice.Join(ctx, install.InstallFileOnDevice)
+			}
+			a.extraInstalledFiles = append(a.extraInstalledFiles, installedFile)
+			a.extraInstalledPairs = append(a.extraInstalledPairs, installPair{install.OutputPathOnHost, installedFile})
+		}
+		if performInstalls {
+			for _, dexJar := range fi.systemServerDexJars {
+				// Copy the system server dex jar to a predefined location where dex2oat will find it.
+				android.CopyFileRule(ctx, dexJar,
+					android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir, dexJar.Base()))
+			}
+		}
+	}
+}
+
 // buildApex creates build rules to build an APEX using apexer.
 func (a *apexBundle) buildApex(ctx android.ModuleContext) {
 	suffix := imageApexSuffix
@@ -985,9 +1045,9 @@
 		a.SkipInstall()
 	}
 
+	installDeps := slices.Concat(a.compatSymlinks, a.extraInstalledFiles)
 	// Install to $OUT/soong/{target,host}/.../apex.
-	a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
-		a.compatSymlinks...)
+	a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile, installDeps...)
 
 	// installed-files.txt is dist'ed
 	a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir)
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index aaf2cb7..4fa43ba 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -15,6 +15,7 @@
 package apex
 
 import (
+	"slices"
 	"strconv"
 	"strings"
 
@@ -59,10 +60,12 @@
 	// Properties common to both prebuilt_apex and apex_set.
 	prebuiltCommonProperties *PrebuiltCommonProperties
 
-	installDir      android.InstallPath
-	installFilename string
-	installedFile   android.InstallPath
-	outputApex      android.WritablePath
+	installDir          android.InstallPath
+	installFilename     string
+	installedFile       android.InstallPath
+	extraInstalledFiles android.InstallPaths
+	extraInstalledPairs installPairs
+	outputApex          android.WritablePath
 
 	// fragment for this apex for apexkeys.txt
 	apexKeysPath android.WritablePath
@@ -70,8 +73,12 @@
 	// Installed locations of symlinks for backward compatibility.
 	compatSymlinks android.InstallPaths
 
-	hostRequired        []string
-	requiredModuleNames []string
+	// systemServerDexpreoptInstalls stores the list of dexpreopt artifacts for a system server jar.
+	systemServerDexpreoptInstalls []java.DexpreopterInstall
+
+	// systemServerDexJars stores the list of dexjars for system server jars in the prebuilt for use when
+	// dexpreopting system server jars that are later in the system server classpath.
+	systemServerDexJars android.Paths
 }
 
 type sanitizedPrebuilt interface {
@@ -188,9 +195,8 @@
 // initApexFilesForAndroidMk initializes the prebuiltCommon.requiredModuleNames field with the install only deps of the prebuilt apex
 func (p *prebuiltCommon) initApexFilesForAndroidMk(ctx android.ModuleContext) {
 	// If this apex contains a system server jar, then the dexpreopt artifacts should be added as required
-	for _, install := range p.Dexpreopter.DexpreoptBuiltInstalledForApex() {
-		p.requiredModuleNames = append(p.requiredModuleNames, install.FullModuleName())
-	}
+	p.systemServerDexpreoptInstalls = append(p.systemServerDexpreoptInstalls, p.Dexpreopter.ApexSystemServerDexpreoptInstalls()...)
+	p.systemServerDexJars = append(p.systemServerDexJars, p.Dexpreopter.ApexSystemServerDexJars()...)
 }
 
 // If this prebuilt has system server jar, create the rules to dexpreopt it and install it alongside the prebuilt apex
@@ -218,38 +224,58 @@
 	}
 }
 
-func (p *prebuiltCommon) addRequiredModules(entries *android.AndroidMkEntries) {
-	entries.AddStrings("LOCAL_REQUIRED_MODULES", p.requiredModuleNames...)
+// installApexSystemServerFiles installs dexpreopt files for system server classpath entries
+// provided by the apex.  They are installed here instead of in library module because there may be multiple
+// variants of the library, generally one for the "main" apex and another with a different min_sdk_version
+// for the Android Go version of the apex.  Both variants would attempt to install to the same locations,
+// and the library variants cannot determine which one should.  The apex module is better equipped to determine
+// if it is "selected".
+// This assumes that the jars produced by different min_sdk_version values are identical, which is currently
+// true but may not be true if the min_sdk_version difference between the variants spans version that changed
+// the dex format.
+func (p *prebuiltCommon) installApexSystemServerFiles(ctx android.ModuleContext) {
+	performInstalls := android.IsModulePreferred(ctx.Module())
+
+	for _, install := range p.systemServerDexpreoptInstalls {
+		var installedFile android.InstallPath
+		if performInstalls {
+			installedFile = ctx.InstallFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
+		} else {
+			installedFile = install.InstallDirOnDevice.Join(ctx, install.InstallFileOnDevice)
+		}
+		p.extraInstalledFiles = append(p.extraInstalledFiles, installedFile)
+		p.extraInstalledPairs = append(p.extraInstalledPairs, installPair{install.OutputPathOnHost, installedFile})
+	}
+
+	for _, dexJar := range p.systemServerDexJars {
+		// Copy the system server dex jar to a predefined location where dex2oat will find it.
+		android.CopyFileRule(ctx, dexJar,
+			android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir, dexJar.Base()))
+	}
 }
 
 func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := []android.AndroidMkEntries{
 		{
-			Class:         "ETC",
-			OutputFile:    android.OptionalPathForPath(p.outputApex),
-			Include:       "$(BUILD_PREBUILT)",
-			Host_required: p.hostRequired,
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(p.outputApex),
+			Include:    "$(BUILD_PREBUILT)",
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetString("LOCAL_MODULE_PATH", p.installDir.String())
 					entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
 					entries.SetPath("LOCAL_SOONG_INSTALLED_MODULE", p.installedFile)
-					entries.SetString("LOCAL_SOONG_INSTALL_PAIRS", p.outputApex.String()+":"+p.installedFile.String())
+					installPairs := append(installPairs{{p.outputApex, p.installedFile}}, p.extraInstalledPairs...)
+					entries.SetString("LOCAL_SOONG_INSTALL_PAIRS", installPairs.String())
 					entries.AddStrings("LOCAL_SOONG_INSTALL_SYMLINKS", p.compatSymlinks.Strings()...)
 					entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
 					entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.prebuiltCommonProperties.Overrides...)
 					entries.SetString("LOCAL_APEX_KEY_PATH", p.apexKeysPath.String())
-					p.addRequiredModules(entries)
 				},
 			},
 		},
 	}
 
-	// Add the dexpreopt artifacts to androidmk
-	for _, install := range p.Dexpreopter.DexpreoptBuiltInstalledForApex() {
-		entriesList = append(entriesList, install.ToMakeEntries())
-	}
-
 	return entriesList
 }
 
@@ -679,7 +705,9 @@
 	}
 
 	if p.installable() {
-		p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks...)
+		p.installApexSystemServerFiles(ctx)
+		installDeps := slices.Concat(p.compatSymlinks, p.extraInstalledFiles)
+		p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, installDeps...)
 		p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile)
 	}
 
@@ -690,16 +718,6 @@
 	return p.provenanceMetaDataFile
 }
 
-// prebuiltApexExtractorModule is a private module type that is only created by the prebuilt_apex
-// module. It extracts the correct apex to use and makes it available for use by apex_set.
-type prebuiltApexExtractorModule struct {
-	android.ModuleBase
-
-	properties ApexExtractorProperties
-
-	extractedApex android.WritablePath
-}
-
 // extract registers the build actions to extract an apex from .apks file
 // returns the path of the extracted apex
 func extract(ctx android.ModuleContext, apexSet android.Path, prerelease *bool) android.Path {
@@ -866,7 +884,8 @@
 
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	if a.installable() {
-		a.installedFile = ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
+		a.installApexSystemServerFiles(ctx)
+		a.installedFile = ctx.InstallFile(a.installDir, a.installFilename, a.outputApex, a.extraInstalledFiles...)
 	}
 
 	// in case that apex_set replaces source apex (using prefer: prop)
@@ -878,11 +897,3 @@
 
 	ctx.SetOutputFiles(android.Paths{a.outputApex}, "")
 }
-
-type systemExtContext struct {
-	android.ModuleContext
-}
-
-func (*systemExtContext) SystemExtSpecific() bool {
-	return true
-}
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 7dbac5f..c643a8c 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -108,6 +108,7 @@
 	})
 
 	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`myapex.key`,
 		`mysystemserverclasspathfragment`,
@@ -166,6 +167,7 @@
 	})
 
 	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`myapex.key`,
 		`mysystemserverclasspathfragment`,
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 7a39fa1..e882470 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -46,13 +46,14 @@
 
 const SystemPartition = "/system/"
 const SystemOtherPartition = "/system_other/"
+const SystemServerDexjarsDir = "system_server_dexjars"
 
 var DexpreoptRunningInSoong = false
 
 // GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
 // ModuleConfig.  The produced files and their install locations will be available through rule.Installs().
 func GenerateDexpreoptRule(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
-	global *GlobalConfig, module *ModuleConfig, productPackages android.Path, copyApexSystemServerJarDex bool) (
+	global *GlobalConfig, module *ModuleConfig, productPackages android.Path) (
 	rule *android.RuleBuilder, err error) {
 
 	defer func() {
@@ -83,7 +84,7 @@
 
 	if !dexpreoptDisabled(ctx, global, module) {
 		if valid, err := validateClassLoaderContext(module.ClassLoaderContexts); err != nil {
-			android.ReportPathErrorf(ctx, err.Error())
+			android.ReportPathErrorf(ctx, "%s", err.Error())
 		} else if valid {
 			fixClassLoaderContext(module.ClassLoaderContexts)
 
@@ -94,7 +95,7 @@
 
 			for archIdx, _ := range module.Archs {
 				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage,
-					generateDM, productPackages, copyApexSystemServerJarDex)
+					generateDM, productPackages)
 			}
 		}
 	}
@@ -231,7 +232,7 @@
 
 func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
 	global *GlobalConfig, module *ModuleConfig, rule *android.RuleBuilder, archIdx int,
-	profile android.WritablePath, appImage bool, generateDM bool, productPackages android.Path, copyApexSystemServerJarDex bool) {
+	profile android.WritablePath, appImage bool, generateDM bool, productPackages android.Path) {
 
 	arch := module.Archs[archIdx]
 
@@ -279,19 +280,6 @@
 			clcTarget = append(clcTarget, GetSystemServerDexLocation(ctx, global, lib))
 		}
 
-		if DexpreoptRunningInSoong && copyApexSystemServerJarDex {
-			// Copy the system server jar to a predefined location where dex2oat will find it.
-			dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
-			rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
-			rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost)
-		} else {
-			// For Make modules the copy rule is generated in the makefiles, not in dexpreopt.sh.
-			// This is necessary to expose the rule to Ninja, otherwise it has rules that depend on
-			// the jar (namely, dexpreopt commands for all subsequent system server jars that have
-			// this one in their class loader context), but no rule that creates it (because Ninja
-			// cannot see the rule in the generated dexpreopt.sh script).
-		}
-
 		clcHostString := "PCL[" + strings.Join(clcHost.Strings(), ":") + "]"
 		clcTargetString := "PCL[" + strings.Join(clcTarget, ":") + "]"
 
@@ -581,11 +569,11 @@
 func SystemServerDexJarHostPath(ctx android.PathContext, jar string) android.OutputPath {
 	if DexpreoptRunningInSoong {
 		// Soong module, just use the default output directory $OUT/soong.
-		return android.PathForOutput(ctx, "system_server_dexjars", jar+".jar")
+		return android.PathForOutput(ctx, SystemServerDexjarsDir, jar+".jar")
 	} else {
 		// Make module, default output directory is $OUT (passed via the "null config" created
 		// by dexpreopt_gen). Append Soong subdirectory to match Soong module paths.
-		return android.PathForOutput(ctx, "soong", "system_server_dexjars", jar+".jar")
+		return android.PathForOutput(ctx, "soong", SystemServerDexjarsDir, jar+".jar")
 	}
 }
 
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index 7512005..8033b48 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -205,9 +205,8 @@
 			panic(err)
 		}
 	}
-	cpApexSscpServerJar := false // dexpreopt_gen operates on make modules, and since sscp libraries are in soong, this should be a noop
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
-		ctx, globalSoong, global, module, android.PathForTesting(productPackagesPath), cpApexSscpServerJar)
+		ctx, globalSoong, global, module, android.PathForTesting(productPackagesPath))
 	if err != nil {
 		panic(err)
 	}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 1f188d5..f9b8880 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -104,7 +104,7 @@
 	module := testSystemModuleConfig(ctx, "test")
 	productPackages := android.PathForTesting("product_packages.txt")
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -164,7 +164,7 @@
 	for _, test := range tests {
 		global.PatternsOnSystemOther = test.patterns
 		for _, mt := range test.moduleTests {
-			rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module, productPackages, true)
+			rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module, productPackages)
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -199,7 +199,7 @@
 	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -211,15 +211,6 @@
 
 	android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
 
-	android.AssertStringListContains(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
-
-	// rule with apex sscp cp as false
-	rule, err = GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, false)
-	if err != nil {
-		t.Fatal(err)
-	}
-	android.AssertStringListDoesNotContain(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
-
 	// cleanup the global variable for test
 	DexpreoptRunningInSoong = oldDexpreoptRunningInSoong
 }
@@ -242,7 +233,7 @@
 	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -254,15 +245,6 @@
 
 	android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
 
-	android.AssertStringListContains(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
-
-	// rule with apex sscp cp as false
-	rule, err = GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, false)
-	if err != nil {
-		t.Fatal(err)
-	}
-	android.AssertStringListDoesNotContain(t, "apex sscp jar copy", rule.Outputs().Strings(), "out/soong/system_server_dexjars/service-A.jar")
-
 	// cleanup the global variable for test
 	DexpreoptRunningInSoong = oldDexpreoptRunningInSoong
 }
@@ -278,7 +260,7 @@
 	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"platform:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -302,7 +284,7 @@
 	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"system_ext:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -326,7 +308,7 @@
 	global.ApexStandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -349,7 +331,7 @@
 
 	module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages, true)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/java/androidmk.go b/java/androidmk.go
index fe3c7a2..f069e75 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -70,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.
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index f52ce5d..15e40ba 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -36,69 +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{
-		OverrideName: install.FullModuleName(),
-		Class:        "ETC",
-		OutputFile:   android.OptionalPathForPath(install.outputPathOnHost),
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
-				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
-				entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
-			},
-		},
-	}
-}
-
-func (install dexpreopterInstall) AddModuleInfoJSONForApex(ctx android.ModuleContext) {
-	moduleInfoJSON := ctx.ExtraModuleInfoJSON()
-	moduleInfoJSON.RegisterNameOverride = install.FullModuleName()
-	moduleInfoJSON.ModuleNameOverride = install.FullModuleName()
-	moduleInfoJSON.Class = []string{"ETC"}
-	moduleInfoJSON.SystemSharedLibs = []string{"none"}
+	InstallFileOnDevice string
 }
 
 type Dexpreopter struct {
@@ -128,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
@@ -286,20 +242,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 {
@@ -550,12 +492,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
@@ -588,7 +526,6 @@
 			partition = ""
 		}
 		installBase := filepath.Base(install.To)
-		arch := filepath.Base(installDir)
 		installPath := android.PathForModuleInPartitionInstall(ctx, partition, installDir)
 		isProfile := strings.HasSuffix(installBase, ".prof")
 
@@ -604,16 +541,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 {
@@ -622,6 +556,15 @@
 		}
 	}
 
+	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()
 	}
@@ -656,22 +599,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) ModuleInfoJSONForApex(ctx android.ModuleContext) {
-	for _, install := range d.builtInstalledForApex {
-		install.AddModuleInfoJSONForApex(ctx)
-	}
+func (d *dexpreopter) ApexSystemServerDexJars() android.Paths {
+	return d.apexSystemServerDexJars
 }
 
 func (d *dexpreopter) OutputProfilePathOnHost() android.Path {
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 53a0d71..bf66047 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"runtime"
-	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -309,7 +308,7 @@
 	testDex2oatToolDep(false, true, false, prebuiltDex2oatPath)
 }
 
-func TestDexpreoptBuiltInstalledForApex(t *testing.T) {
+func TestApexSystemServerDexpreoptInstalls(t *testing.T) {
 	preparers := android.GroupFixturePreparers(
 		PrepareForTestWithDexpreopt,
 		PrepareForTestWithFakeApexMutator,
@@ -329,25 +328,35 @@
 	module := ctx.ModuleForTests("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, `
@@ -361,98 +370,11 @@
 	module = ctx.ModuleForTests("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) {
diff --git a/java/java.go b/java/java.go
index 8d10e3b..9f0de46 100644
--- a/java/java.go
+++ b/java/java.go
@@ -402,7 +402,12 @@
 
 	BuiltInstalled string
 
-	BuiltInstalledForApex []dexpreopterInstall
+	// ApexSystemServerDexpreoptInstalls stores the list of dexpreopt artifacts if this is a system server
+	// jar in an apex.
+	ApexSystemServerDexpreoptInstalls []DexpreopterInstall
+
+	// ApexSystemServerDexJars stores the list of dex jars if this is a system server jar in an apex.
+	ApexSystemServerDexJars android.Paths
 
 	// The config is used for two purposes:
 	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
@@ -1129,7 +1134,8 @@
 		javaInfo.BootDexJarPath = j.bootDexJarPath
 		javaInfo.UncompressDexState = j.uncompressDexState
 		javaInfo.Active = j.active
-		javaInfo.BuiltInstalledForApex = j.builtInstalledForApex
+		javaInfo.ApexSystemServerDexpreoptInstalls = j.apexSystemServerDexpreoptInstalls
+		javaInfo.ApexSystemServerDexJars = j.apexSystemServerDexJars
 		javaInfo.BuiltInstalled = j.builtInstalled
 		javaInfo.ConfigPath = j.configPath
 		javaInfo.OutputProfilePathOnHost = j.outputProfilePathOnHost
@@ -1170,7 +1176,6 @@
 
 	if j.hideApexVariantFromMake {
 		moduleInfoJSON.Disabled = true
-		j.dexpreopter.ModuleInfoJSONForApex(ctx)
 	}
 	return moduleInfoJSON
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 05a5b49..07f0599 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1246,7 +1246,8 @@
 
 	commonToSdkLibraryAndImport
 
-	builtInstalledForApex []dexpreopterInstall
+	apexSystemServerDexpreoptInstalls []DexpreopterInstall
+	apexSystemServerDexJars           android.Paths
 }
 
 func (module *SdkLibrary) generateTestAndSystemScopesByDefault() bool {
@@ -1501,7 +1502,8 @@
 		module.dexJarFile = makeDexJarPathFromPath(module.implLibraryInfo.DexJarFile.Path())
 		module.headerJarFile = module.implLibraryInfo.HeaderJars[0]
 		module.implementationAndResourcesJar = module.implLibraryInfo.ImplementationAndResourcesJars[0]
-		module.builtInstalledForApex = module.implLibraryInfo.BuiltInstalledForApex
+		module.apexSystemServerDexpreoptInstalls = module.implLibraryInfo.ApexSystemServerDexpreoptInstalls
+		module.apexSystemServerDexJars = module.implLibraryInfo.ApexSystemServerDexJars
 		module.dexpreopter.configPath = module.implLibraryInfo.ConfigPath
 		module.dexpreopter.outputProfilePathOnHost = module.implLibraryInfo.OutputProfilePathOnHost
 
@@ -1584,8 +1586,12 @@
 	ctx.SetOutputFiles(info.GeneratedSrcjars, ".generated_srcjars")
 }
 
-func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall {
-	return module.builtInstalledForApex
+func (module *SdkLibrary) ApexSystemServerDexpreoptInstalls() []DexpreopterInstall {
+	return module.apexSystemServerDexpreoptInstalls
+}
+
+func (module *SdkLibrary) ApexSystemServerDexJars() android.Paths {
+	return module.apexSystemServerDexJars
 }
 
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {