Move dexpreopt processing from java_*_import to prebuilt_apex

dexpreopt of apex system server from prebuilts involves three soong
modules
1. prebuilt_apex / apex_set
2. an internal deapexer module created by the prebuilt apex
3. java_import/java_sdk_library

(3) acts as a shim for the deapexer to set the dexjar extracted from the
prebuilt apex. This methodolody requires a 1:1 correspondence across the
three modules

This breaks down when we have multiple versions of the same prebuilt
apex in the tree. In preparation for this, move the dexpreopt
processing from (3) to (1). Each prebuilt_apex will create the necessary
rules for dexpreopting the jars deapexed from itself. In the future,
apex_contributions will be used to pick which service-foo.{odex|.vdex} to
install depending on which prebuilt apex is selected.

Implementation details
- Embed dexpreopter in prebuiltApex structs so that this module type can
  register the dexpreopt rules. Since a single apex can have multiple
  system server jars, this also requires creating an additional scope in
  dexpreopt.go to prevent name collisions
- Add the dexpreopt modules as required in initApexFilesForAndroidMk
- Add the depreopt modules to androidMk in AndroidMkEntries. Drop the
  equivalent from java_import and java_sdk_library_import

Bug: 308790457
Test: existing soong unit tests
Test: lunch cf_x86_64_phone-next-userdebug && m out/target/product/vsoc_x86_64/system/apex/com.google.android.adservices.apex
Test: Verified that the above command installs
/out/target/product/vsoc_x86_64/system/framework/oat/x86_64/apex@com.android.adservices@javalib@service-adservices.jar@classes.{odex|vdex} and the equivalent files of service-sdksandbox

Test: presubmits

Change-Id: I01cea8956d2857fb864b415e73d3d2686d069b5e
diff --git a/apex/apex_test.go b/apex/apex_test.go
index abf6b15..1ced393 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3725,7 +3725,7 @@
 }
 
 func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) {
-	deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Rule("deapexer")
+	deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Description("deapex")
 	outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1)
 	if deapexer.Output != nil {
 		outputs = append(outputs, deapexer.Output.String())
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 42f5cd4..159e9e1 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -530,6 +530,8 @@
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
 			`com.android.art.apex.selector`,
+			`com.android.art.deapexer`,
+			`dex2oatd`,
 			`prebuilt_art-bootclasspath-fragment`,
 		})
 
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 179d90b..4c44678 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -22,6 +22,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 	"android/soong/java"
 	"android/soong/provenance"
 
@@ -50,6 +51,7 @@
 
 type prebuiltCommon struct {
 	android.ModuleBase
+	java.Dexpreopter
 	prebuilt android.Prebuilt
 
 	// Properties common to both prebuilt_apex and apex_set.
@@ -170,50 +172,39 @@
 	return proptools.BoolDefault(p.prebuiltCommonProperties.Installable, true)
 }
 
-// initApexFilesForAndroidMk initializes the prebuiltCommon.apexFilesForAndroidMk field from the
-// modules that this depends upon.
+// To satisfy java.DexpreopterInterface
+func (p *prebuiltCommon) IsInstallable() bool {
+	return p.installable()
+}
+
+// initApexFilesForAndroidMk initializes the prebuiltCommon.requiredModuleNames field with the install only deps of the prebuilt apex
 func (p *prebuiltCommon) initApexFilesForAndroidMk(ctx android.ModuleContext) {
-	// Walk the dependencies of this module looking for the java modules that it exports.
-	ctx.WalkDeps(func(child, parent android.Module) bool {
-		tag := ctx.OtherModuleDependencyTag(child)
+	// 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())
+	}
+}
 
-		name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
-		if java.IsBootclasspathFragmentContentDepTag(tag) ||
-			java.IsSystemServerClasspathFragmentContentDepTag(tag) || tag == exportedJavaLibTag {
-			// If the exported java module provides a dex jar path then add it to the list of apexFiles.
-			path := child.(interface {
-				DexJarBuildPath() java.OptionalDexJarPath
-			}).DexJarBuildPath()
-			if path.IsSet() {
-				af := apexFile{
-					module:              child,
-					moduleDir:           ctx.OtherModuleDir(child),
-					androidMkModuleName: name,
-					builtFile:           path.Path(),
-					class:               javaSharedLib,
-				}
-				if module, ok := child.(java.DexpreopterInterface); ok {
-					for _, install := range module.DexpreoptBuiltInstalledForApex() {
-						af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
-					}
-				}
-				p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, af)
-			}
-		} else if tag == exportedBootclasspathFragmentTag {
-			_, ok := child.(*java.PrebuiltBootclasspathFragmentModule)
-			if !ok {
-				ctx.PropertyErrorf("exported_bootclasspath_fragments", "%q is not a prebuilt_bootclasspath_fragment module", name)
-				return false
-			}
-			// Visit the children of the bootclasspath_fragment.
-			return true
-		} else if tag == exportedSystemserverclasspathFragmentTag {
-			// Visit the children of the systemserver_fragment.
-			return true
+// If this prebuilt has system server jar, create the rules to dexpreopt it and install it alongside the prebuilt apex
+func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext) {
+	// If this apex does not export anything, return
+	if !p.hasExportedDeps() {
+		return
+	}
+	// Use apex_name to determine the api domain of this prebuilt apex
+	apexName := p.ApexVariationName()
+	di := android.FindDeapexerProviderForModule(ctx)
+	dc := dexpreopt.GetGlobalConfig(ctx)
+	systemServerJarList := dc.AllApexSystemServerJars(ctx)
+
+	for i := 0; i < systemServerJarList.Len(); i++ {
+		sscpApex := systemServerJarList.Apex(i)
+		sscpJar := systemServerJarList.Jar(i)
+		if apexName != sscpApex {
+			continue
 		}
-
-		return false
-	})
+		p.Dexpreopter.DexpreoptPrebuiltApexSystemServerJars(ctx, sscpJar, di)
+	}
 }
 
 func (p *prebuiltCommon) addRequiredModules(entries *android.AndroidMkEntries) {
@@ -248,6 +239,11 @@
 		},
 	}
 
+	// Add the dexpreopt artifacts to androidmk
+	for _, install := range p.Dexpreopter.DexpreoptBuiltInstalledForApex() {
+		entriesList = append(entriesList, install.ToMakeEntries())
+	}
+
 	// Iterate over the apexFilesForAndroidMk list and create an AndroidMkEntries struct for each
 	// file. This provides similar behavior to that provided in apexBundle.AndroidMk() as it makes the
 	// apex specific variants of the exported java modules available for use from within make.
@@ -756,6 +752,14 @@
 	p.prebuiltApexContentsDeps(ctx)
 }
 
+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)
+		ctx.AddDependency(ctx.Module(), android.DeapexerTag, deapexerModuleName(p.BaseModuleName()))
+	}
+}
+
 var _ ApexInfoMutator = (*Prebuilt)(nil)
 
 func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
@@ -783,6 +787,9 @@
 		return
 	}
 
+	// dexpreopt any system server jars if present
+	p.dexpreoptSystemServerJars(ctx)
+
 	// Save the files that need to be made available to Make.
 	p.initApexFilesForAndroidMk(ctx)
 
@@ -999,6 +1006,9 @@
 		return
 	}
 
+	// dexpreopt any system server jars if present
+	a.dexpreoptSystemServerJars(ctx)
+
 	// Save the files that need to be made available to Make.
 	a.initApexFilesForAndroidMk(ctx)
 
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 40d0581..90fd2ca 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -272,7 +272,9 @@
 	ctx := result.TestContext
 
 	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
+		`dex2oatd`,
 		`myapex.apex.selector`,
+		`myapex.deapexer`,
 		`prebuilt_mysystemserverclasspathfragment`,
 	})
 
diff --git a/java/androidmk.go b/java/androidmk.go
index 809f9b5..cbf9abb 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -207,11 +207,7 @@
 
 func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries {
 	if prebuilt.hideApexVariantFromMake {
-		// For a library imported from a prebuilt APEX, we don't need a Make module for itself, as we
-		// don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it
-		// is preopted.
-		dexpreoptEntries := prebuilt.dexpreopter.AndroidMkEntriesForApex()
-		return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true})
+		return []android.AndroidMkEntries{}
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 8f29bb3..ef4626e 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -40,8 +40,8 @@
 	variant := ctx.ModuleForTests("foo", "android_common")
 
 	// Check dexpreopt outputs.
-	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
-		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+	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")
 	}
 
@@ -74,8 +74,8 @@
 	variant := ctx.ModuleForTests("foo", "android_common")
 
 	// Check dexpreopt outputs. They shouldn't exist.
-	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule != nil ||
-		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil {
+	if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule != nil ||
+		variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule != nil {
 		t.Errorf("dexpreopt shouldn't have run.")
 	}
 
@@ -101,8 +101,8 @@
 	variant := ctx.ModuleForTests("foo", "android_common")
 
 	// Check dexpreopt outputs.
-	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
-		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+	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")
 	}
 	// Make sure signing was skipped and aligning was done.
@@ -210,8 +210,8 @@
 	variant := ctx.ModuleForTests("foo", "android_common")
 
 	// Check dexpreopt outputs.
-	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
-		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+	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")
 	}
 
diff --git a/java/app_test.go b/java/app_test.go
index 0936b28..861c047 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -3357,7 +3357,7 @@
 	cmd := app.Rule("dexpreopt").RuleParams.Command
 	android.AssertStringDoesContain(t, "dexpreopt app cmd context", cmd, "--context-json=")
 	android.AssertStringDoesContain(t, "dexpreopt app cmd product_packages", cmd,
-		"--product-packages=out/soong/.intermediates/app/android_common/dexpreopt/product_packages.txt")
+		"--product-packages=out/soong/.intermediates/app/android_common/dexpreopt/app/product_packages.txt")
 }
 
 func TestDexpreoptBcp(t *testing.T) {
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 0f69dc3..1e289c5 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -79,18 +79,25 @@
 func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
 	return android.AndroidMkEntries{
 		Class:      "ETC",
-		SubName:    install.SubModuleName(),
 		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")
+				// Unset LOCAL_SOONG_INSTALLED_MODULE so that this does not default to the primary .apex file
+				// Without this, installation of the dexpreopt artifacts get skipped
+				entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", "")
 			},
 		},
 	}
 }
 
+type Dexpreopter struct {
+	dexpreopter
+}
+
 type dexpreopter struct {
 	dexpreoptProperties       DexpreoptProperties
 	importDexpreoptProperties ImportDexpreoptProperties
@@ -258,6 +265,17 @@
 	return defaultInstallPath
 }
 
+// DexpreoptPrebuiltApexSystemServerJars generates the dexpreopt artifacts from a jar file that has been deapexed from a prebuilt apex
+func (d *Dexpreopter) DexpreoptPrebuiltApexSystemServerJars(ctx android.ModuleContext, libraryName string, di *android.DeapexerInfo) {
+	// A single prebuilt apex can have multiple apex system jars
+	// initialize the output path for this dex jar
+	dc := dexpreopt.GetGlobalConfig(ctx)
+	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))
+	d.dexpreopt(ctx, dexJarFile)
+}
+
 func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
@@ -346,11 +364,15 @@
 
 	d.dexpreoptProperties.Dex_preopt_result.Profile_guided = profileClassListing.Valid()
 
+	// A single apex can have multiple system server jars
+	// Use the dexJar to create a unique scope for each
+	dexJarStem := strings.TrimSuffix(dexJarFile.Base(), dexJarFile.Ext())
+
 	// Full dexpreopt config, used to create dexpreopt build rules.
 	dexpreoptConfig := &dexpreopt.ModuleConfig{
 		Name:            moduleName(ctx),
 		DexLocation:     dexLocation,
-		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath,
+		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, moduleName(ctx)+".jar").OutputPath,
 		DexPath:         dexJarFile,
 		ManifestPath:    android.OptionalPathForPath(d.manifestFile),
 		UncompressedDex: d.uncompressedDex,
@@ -380,7 +402,7 @@
 		PresignedPrebuilt: d.isPresignedPrebuilt,
 	}
 
-	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config")
+	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "dexpreopt.config")
 	dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
 
 	if d.dexpreoptDisabled(ctx) {
@@ -394,7 +416,7 @@
 	// dependencies to create a per-app list, and use `rsync --checksum` to prevent the file's mtime
 	// from being changed if the contents don't change. This avoids unnecessary dexpreopt reruns.
 	productPackages := android.PathForModuleInPartitionInstall(ctx, "", "product_packages.txt")
-	appProductPackages := android.PathForModuleOut(ctx, "dexpreopt", "product_packages.txt")
+	appProductPackages := android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "product_packages.txt")
 	appProductPackagesStaging := appProductPackages.ReplaceExtension(ctx, "txt.tmp")
 	clcNames, _ := dexpreopt.ComputeClassLoaderContextDependencies(dexpreoptConfig.ClassLoaderContexts)
 	sort.Strings(clcNames) // The order needs to be deterministic.
@@ -416,7 +438,7 @@
 		Text("rsync --checksum").
 		Input(appProductPackagesStaging).
 		Output(appProductPackages)
-	productPackagesRule.Restat().Build("product_packages", "dexpreopt product_packages")
+	productPackagesRule.Restat().Build("product_packages."+dexJarStem, "dexpreopt product_packages")
 
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
 		ctx, globalSoong, global, dexpreoptConfig, appProductPackages)
@@ -425,9 +447,11 @@
 		return
 	}
 
-	dexpreoptRule.Build("dexpreopt", "dexpreopt")
+	dexpreoptRule.Build("dexpreopt"+"."+dexJarStem, "dexpreopt")
 
-	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
+	// The current ctx might be of a deapexer module created by a prebuilt apex
+	// Use the path of the dex file to determine the library name
+	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(dexJarStem)
 
 	for _, install := range dexpreoptRule.Installs() {
 		// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
@@ -452,7 +476,7 @@
 				// The installs will be handled by Make as sub-modules of the java library.
 				d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
 					name:                arch + "-" + installBase,
-					moduleName:          moduleName(ctx),
+					moduleName:          dexJarStem,
 					outputPathOnHost:    install.From,
 					installDirOnDevice:  installPath,
 					installFileOnDevice: installBase,
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index fedd564..73e33f4 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -410,7 +410,7 @@
 	verifyEntries(t,
 		"entriesList[0]",
 		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
-		"/dexpreopt/oat/arm64/javalib.odex",
+		"/dexpreopt/service-foo/oat/arm64/javalib.odex",
 		"/system/framework/oat/arm64",
 		"apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
 		entriesList[0])
@@ -418,7 +418,7 @@
 	verifyEntries(t,
 		"entriesList[1]",
 		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
-		"/dexpreopt/oat/arm64/javalib.vdex",
+		"/dexpreopt/service-foo/oat/arm64/javalib.vdex",
 		"/system/framework/oat/arm64",
 		"apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
 		entriesList[1])
@@ -459,7 +459,7 @@
 	ctx := result.TestContext
 	dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
 
-	expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/profile.prof"}
+	expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/foo/profile.prof"}
 
 	android.AssertArrayString(t, "outputs", expected, dexpreopt.AllOutputs())
 }
diff --git a/java/java_test.go b/java/java_test.go
index e21018c..2368b6c 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -618,8 +618,6 @@
 	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_library", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
 	entries = android.AndroidMkEntriesForTest(t, ctx, barModule.Module())[0]
 	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
-	entries = android.AndroidMkEntriesForTest(t, ctx, ctx.ModuleForTests("sdklib", "android_common").Module())[0]
-	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_sdk_library_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
 }
 
 func assertDeepEquals(t *testing.T, message string, expected interface{}, actual interface{}) {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 0584281..a634b17 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -2591,14 +2591,6 @@
 	}
 }
 
-func (module *SdkLibraryImport) AndroidMkEntries() []android.AndroidMkEntries {
-	// For an SDK library imported from a prebuilt APEX, we don't need a Make module for itself, as we
-	// don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it
-	// is preopted.
-	dexpreoptEntries := module.dexpreopter.AndroidMkEntriesForApex()
-	return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true})
-}
-
 var _ android.ApexModule = (*SdkLibraryImport)(nil)
 
 // Implements android.ApexModule