Fix boot jar handling when both source and prebuilt APEXes and modules
are present.

1) The boot jar to APEX mapping is maintained by the base names for
both of them. When building with prebuilt modules and APEXes, that
means we need to take care to compare them without regard to any
"prebuilt_" prefixes.

2) VisitAllModules can visit disabled modules and both source and
prebuilt modules, so they need some conditions to skip modules that
aren't applicable for boot jars.

Test: `m droid`
Test: `m droid SOONG_CONFIG_art_module_source_build=false`
  with fresh ART Module prebuilts in place
Bug: 171061220
Change-Id: Iced269d29127bc8b8f9b3171adb60a97d115628b
diff --git a/android/apex.go b/android/apex.go
index 31c62e9..4f1d41b 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -111,6 +111,19 @@
 	return false
 }
 
+// InApexByBaseName tells whether this apex variant of the module is part of the given APEX or not,
+// where the APEX is specified by its canonical base name, i.e. typically beginning with
+// "com.android.". In particular this function doesn't differentiate between source and prebuilt
+// APEXes, where the latter may have "prebuilt_" prefixes.
+func (i ApexInfo) InApexByBaseName(apex string) bool {
+	for _, a := range i.InApexes {
+		if RemoveOptionalPrebuiltPrefix(a) == apex {
+			return true
+		}
+	}
+	return false
+}
+
 // ApexTestForInfo stores the contents of APEXes for which this module is a test - although this
 // module is not part of the APEX - and thus has access to APEX internals.
 type ApexTestForInfo struct {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index ae0cbe9..39eeeb2 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4330,6 +4330,215 @@
 	})
 }
 
+func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
+	transform := func(config *dexpreopt.GlobalConfig) {
+		config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
+	}
+
+	checkBootDexJarPath := func(ctx *android.TestContext, bootDexJarPath string) {
+		s := ctx.SingletonForTests("dex_bootjars")
+		foundLibfooJar := false
+		for _, output := range s.AllOutputs() {
+			if strings.HasSuffix(output, "/libfoo.jar") {
+				foundLibfooJar = true
+				buildRule := s.Output(output)
+				actual := android.NormalizePathForTesting(buildRule.Input)
+				if actual != bootDexJarPath {
+					t.Errorf("Incorrect boot dex jar path '%s', expected '%s'", actual, bootDexJarPath)
+				}
+			}
+		}
+		if !foundLibfooJar {
+			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs")
+		}
+	}
+
+	t.Run("prebuilt only", func(t *testing.T) {
+		bp := `
+		prebuilt_apex {
+			name: "myapex",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_java_libs: ["libfoo"],
+		}
+
+		java_import {
+			name: "libfoo",
+			jars: ["libfoo.jar"],
+			apex_available: ["myapex"],
+		}
+	`
+
+		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+	})
+
+	t.Run("prebuilt with source library preferred", func(t *testing.T) {
+		bp := `
+		prebuilt_apex {
+			name: "myapex",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_java_libs: ["libfoo"],
+		}
+
+		java_import {
+			name: "libfoo",
+			jars: ["libfoo.jar"],
+			apex_available: ["myapex"],
+		}
+
+		java_library {
+			name: "libfoo",
+			srcs: ["foo/bar/MyClass.java"],
+			apex_available: ["myapex"],
+		}
+	`
+
+		// In this test the source (java_library) libfoo is active since the
+		// prebuilt (java_import) defaults to prefer:false. However the
+		// prebuilt_apex module always depends on the prebuilt, and so it doesn't
+		// find the dex boot jar in it. We either need to disable the source libfoo
+		// or make the prebuilt libfoo preferred.
+		testDexpreoptWithApexes(t, bp, "failed to find a dex jar path for module 'libfoo'", transform)
+	})
+
+	t.Run("prebuilt library preferred with source", func(t *testing.T) {
+		bp := `
+		prebuilt_apex {
+			name: "myapex",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_java_libs: ["libfoo"],
+		}
+
+		java_import {
+			name: "libfoo",
+			prefer: true,
+			jars: ["libfoo.jar"],
+			apex_available: ["myapex"],
+		}
+
+		java_library {
+			name: "libfoo",
+			srcs: ["foo/bar/MyClass.java"],
+			apex_available: ["myapex"],
+		}
+	`
+
+		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+	})
+
+	t.Run("prebuilt with source apex preferred", func(t *testing.T) {
+		bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["libfoo"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		prebuilt_apex {
+			name: "myapex",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_java_libs: ["libfoo"],
+		}
+
+		java_import {
+			name: "libfoo",
+			jars: ["libfoo.jar"],
+			apex_available: ["myapex"],
+		}
+
+		java_library {
+			name: "libfoo",
+			srcs: ["foo/bar/MyClass.java"],
+			apex_available: ["myapex"],
+		}
+	`
+
+		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		checkBootDexJarPath(ctx, ".intermediates/libfoo/android_common_apex10000/aligned/libfoo.jar")
+	})
+
+	t.Run("prebuilt preferred with source apex disabled", func(t *testing.T) {
+		bp := `
+		apex {
+			name: "myapex",
+			enabled: false,
+			key: "myapex.key",
+			java_libs: ["libfoo"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		prebuilt_apex {
+			name: "myapex",
+			arch: {
+				arm64: {
+					src: "myapex-arm64.apex",
+				},
+				arm: {
+					src: "myapex-arm.apex",
+				},
+			},
+			exported_java_libs: ["libfoo"],
+		}
+
+		java_import {
+			name: "libfoo",
+			prefer: true,
+			jars: ["libfoo.jar"],
+			apex_available: ["myapex"],
+		}
+
+		java_library {
+			name: "libfoo",
+			srcs: ["foo/bar/MyClass.java"],
+			apex_available: ["myapex"],
+		}
+	`
+
+		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+	})
+}
+
 func TestApexWithTests(t *testing.T) {
 	ctx, config := testApex(t, `
 		apex_test {
@@ -5934,10 +6143,11 @@
 		"build/make/target/product/security": nil,
 		"apex_manifest.json":                 nil,
 		"AndroidManifest.xml":                nil,
+		"system/sepolicy/apex/myapex-file_contexts":                  nil,
 		"system/sepolicy/apex/some-updatable-apex-file_contexts":     nil,
 		"system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil,
 		"system/sepolicy/apex/com.android.art.debug-file_contexts":   nil,
-		"framework/aidl/a.aidl": nil,
+		"framework/aidl/a.aidl":                                      nil,
 	}
 	cc.GatherRequiredFilesForTest(fs)
 
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 823275b..ac8107b 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -49,14 +49,36 @@
 	return true
 }
 
+// isActiveModule returns true if the given module should be considered for boot
+// jars, i.e. if it's enabled and the preferred one in case of source and
+// prebuilt alternatives.
+func isActiveModule(module android.Module) bool {
+	if !module.Enabled() {
+		return false
+	}
+	if module.IsReplacedByPrebuilt() {
+		// A source module that has been replaced by a prebuilt counterpart.
+		return false
+	}
+	if prebuilt, ok := module.(android.PrebuiltInterface); ok {
+		if p := prebuilt.Prebuilt(); p != nil {
+			return p.UsePrebuilt()
+		}
+	}
+	return true
+}
+
 func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	config := ctx.Config()
 	if config.SkipBootJarsCheck() {
 		return
 	}
 
-	// Populate a map from module name to APEX from the boot jars. If there is a problem
-	// such as duplicate modules then fail and return immediately.
+	// Populate a map from module name to APEX from the boot jars. If there is a
+	// problem such as duplicate modules then fail and return immediately. Note
+	// that both module and APEX names are tracked by base names here, so we need
+	// to be careful to remove "prebuilt_" prefixes when comparing them with
+	// actual modules and APEX bundles.
 	moduleToApex := make(map[string]string)
 	if !populateMapFromConfiguredJarList(ctx, moduleToApex, config.NonUpdatableBootJars(), "BootJars") ||
 		!populateMapFromConfiguredJarList(ctx, moduleToApex, config.UpdatableBootJars(), "UpdatableBootJars") {
@@ -69,10 +91,14 @@
 	// Scan all the modules looking for the module/apex variants corresponding to the
 	// boot jars.
 	ctx.VisitAllModules(func(module android.Module) {
-		name := ctx.ModuleName(module)
+		if !isActiveModule(module) {
+			return
+		}
+
+		name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module))
 		if apex, ok := moduleToApex[name]; ok {
 			apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
-			if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApex(apex) {
+			if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexByBaseName(apex) {
 				// The module name/apex variant should be unique in the system but double check
 				// just in case something has gone wrong.
 				if existing, ok := nameToApexVariant[name]; ok {
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 004cbbb..2dcc9e2 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -479,7 +479,7 @@
 			// A platform variant is required but this is for an apex so ignore it.
 			return -1, nil
 		}
-	} else if !android.InList(requiredApex, apexInfo.InApexes) {
+	} else if !apexInfo.InApexByBaseName(requiredApex) {
 		// An apex variant for a specific apex is required but this is the wrong apex.
 		return -1, nil
 	}
@@ -489,7 +489,7 @@
 
 	switch image.name {
 	case artBootImageName:
-		if len(apexInfo.InApexes) > 0 && allHavePrefix(apexInfo.InApexes, "com.android.art") {
+		if apexInfo.InApexByBaseName("com.android.art") || apexInfo.InApexByBaseName("com.android.art.debug") || apexInfo.InApexByBaseName("com.android.art,testing") {
 			// ok: found the jar in the ART apex
 		} else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
 			// exception (skip and continue): Jacoco platform variant for a coverage build
@@ -516,21 +516,17 @@
 	return index, jar.DexJarBuildPath()
 }
 
-func allHavePrefix(list []string, prefix string) bool {
-	for _, s := range list {
-		if s != prefix && !strings.HasPrefix(s, prefix+".") {
-			return false
-		}
-	}
-	return true
-}
-
 // buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
 func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
 	// Collect dex jar paths for the boot image modules.
 	// This logic is tested in the apex package to avoid import cycle apex <-> java.
 	bootDexJars := make(android.Paths, image.modules.Len())
+
 	ctx.VisitAllModules(func(module android.Module) {
+		if !isActiveModule(module) {
+			return
+		}
+
 		if i, j := getBootImageJar(ctx, image, module); i != -1 {
 			if existing := bootDexJars[i]; existing != nil {
 				ctx.Errorf("Multiple dex jars found for %s:%s - %s and %s",
@@ -860,6 +856,9 @@
 		// Collect `permitted_packages` for updatable boot jars.
 		var updatablePackages []string
 		ctx.VisitAllModules(func(module android.Module) {
+			if !isActiveModule(module) {
+				return
+			}
 			if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok {
 				name := ctx.ModuleName(module)
 				if i := android.IndexList(name, updatableModules); i != -1 {