Add exported_bootclasspath_fragments to prebuilt_apex/apex_set

This is needed to allow a prebuilt_bootclasspath_fragment to be used
interchangeably with a bootclasspath_fragment in the
platform_bootclasspath module.

The platform_bootclasspath module depends on APEX specific variants of
bootclasspath_fragment modules. That works because the
bootclasspath_fragment modules are part of an apex and so have an APEX
specific variant which the platform_bootclasspath can specify.

Using a prebuilt_bootclasspath_fragment in place of a
bootclasspath_fragment requires that the prebuilt also has an APEX
specific variant.

Specifying exported_bootclasspath_fragments on a prebuilt_apex/apex_set
will cause it to create an APEX variant for the named module whcih will
allow it to be selected by the platform_bootclasspath module.

Bug: 186034565
Bug: 177892522
Test: m nothing
Change-Id: I7ddacc6498ec3a4a9f26c5f78b7f9a033e494d78
diff --git a/apex/deapexer.go b/apex/deapexer.go
index 1db13f9..9bc5720 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -44,9 +44,13 @@
 // module.`
 type DeapexerProperties struct {
 	// List of java libraries that are embedded inside this prebuilt APEX bundle and for which this
-	// APEX bundle will provide dex implementation jars for use by dexpreopt and boot jars package
-	// check.
+	// APEX bundle will create an APEX variant and provide dex implementation jars for use by
+	// dexpreopt and boot jars package check.
 	Exported_java_libs []string
+
+	// List of bootclasspath fragments inside this prebuiltd APEX bundle and for which this APEX
+	// bundle will create an APEX variant.
+	Exported_bootclasspath_fragments []string
 }
 
 type SelectedApexProperties struct {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index a9d24a7..7830f95 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -97,10 +97,17 @@
 func (p *prebuiltCommon) deapexerDeps(ctx android.BottomUpMutatorContext) {
 	// Add dependencies onto the java modules that represent the java libraries that are provided by
 	// and exported from this prebuilt apex.
-	for _, lib := range p.deapexerProperties.Exported_java_libs {
-		dep := prebuiltApexExportedModuleName(ctx, lib)
+	for _, exported := range p.deapexerProperties.Exported_java_libs {
+		dep := prebuiltApexExportedModuleName(ctx, exported)
 		ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), exportedJavaLibTag, dep)
 	}
+
+	// Add dependencies onto the bootclasspath fragment modules that are exported from this prebuilt
+	// apex.
+	for _, exported := range p.deapexerProperties.Exported_bootclasspath_fragments {
+		dep := prebuiltApexExportedModuleName(ctx, exported)
+		ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), exportedBootclasspathFragmentTag, dep)
+	}
 }
 
 // apexInfoMutator marks any modules for which this apex exports a file as requiring an apex
@@ -137,18 +144,19 @@
 	var dependencies []android.ApexModule
 	mctx.VisitDirectDeps(func(m android.Module) {
 		tag := mctx.OtherModuleDependencyTag(m)
-		if tag == exportedJavaLibTag {
+		if exportedTag, ok := tag.(exportedDependencyTag); ok {
+			propertyName := exportedTag.name
 			depName := mctx.OtherModuleName(m)
 
 			// It is an error if the other module is not a prebuilt.
 			if _, ok := m.(android.PrebuiltInterface); !ok {
-				mctx.PropertyErrorf("exported_java_libs", "%q is not a prebuilt module", depName)
+				mctx.PropertyErrorf(propertyName, "%q is not a prebuilt module", depName)
 				return
 			}
 
 			// It is an error if the other module is not an ApexModule.
 			if _, ok := m.(android.ApexModule); !ok {
-				mctx.PropertyErrorf("exported_java_libs", "%q is not usable within an apex", depName)
+				mctx.PropertyErrorf(propertyName, "%q is not usable within an apex", depName)
 				return
 			}
 
@@ -451,7 +459,8 @@
 func (t exportedDependencyTag) ExcludeFromVisibilityEnforcement() {}
 
 var (
-	exportedJavaLibTag = exportedDependencyTag{name: "exported_java_lib"}
+	exportedJavaLibTag               = exportedDependencyTag{name: "exported_java_libs"}
+	exportedBootclasspathFragmentTag = exportedDependencyTag{name: "exported_bootclasspath_fragments"}
 )
 
 func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
diff --git a/java/boot_image.go b/java/boot_image.go
index d0862a9..6f50c27 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -124,8 +124,22 @@
 	if m.properties.Image_name != nil && len(contents) != 0 {
 		ctx.ModuleErrorf(`both of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
 	}
+
 	imageName := proptools.String(m.properties.Image_name)
 	if imageName == "art" {
+		// TODO(b/177892522): Prebuilts (versioned or not) should not use the image_name property.
+		if m.MemberName() != "" {
+			// The module is a versioned prebuilt so ignore it. This is done for a couple of reasons:
+			// 1. There is no way to use this at the moment so ignoring it is safe.
+			// 2. Attempting to initialize the contents property from the configuration will end up having
+			//    the versioned prebuilt depending on the unversioned prebuilt. That will cause problems
+			//    as the unversioned prebuilt could end up with an APEX variant created for the source
+			//    APEX which will prevent it from having an APEX variant for the prebuilt APEX which in
+			//    turn will prevent it from accessing the dex implementation jar from that which will
+			//    break hidden API processing, amongst others.
+			return
+		}
+
 		// Get the configuration for the art apex jars. Do not use getImageConfig(ctx) here as this is
 		// too early in the Soong processing for that to work.
 		global := dexpreopt.GetGlobalConfig(ctx)
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 0ce4351..ef4d7cd 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -24,6 +24,7 @@
 func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
+		java.PrepareForTestWithJavaDefaultModules,
 		prepareForSdkTestWithApex,
 
 		// Some additional files needed for the art apex.
@@ -32,6 +33,20 @@
 			"com.android.art.pem":                                nil,
 			"system/sepolicy/apex/com.android.art-file_contexts": nil,
 		}),
+
+		// platform_bootclasspath that depends on the fragment.
+		android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+				fragments: [
+					{
+						apex: "com.android.art",
+						module: "mybootclasspathfragment",
+					},
+				],
+			}
+		`),
+
 		java.FixtureConfigureBootJars("com.android.art:mybootlib"),
 		android.FixtureWithRootAndroidBp(`
 			sdk {
@@ -72,6 +87,23 @@
 		`),
 	).RunTest(t)
 
+	// A preparer to add a prebuilt apex to the test fixture.
+	prepareWithPrebuiltApex := android.GroupFixturePreparers(
+		android.FixtureAddTextFile("prebuilts/apex/Android.bp", `
+				prebuilt_apex {
+					name: "com.android.art",
+					src: "art.apex",
+					exported_java_libs: [
+						"mybootlib",
+					],
+					exported_bootclasspath_fragments: [
+						"mybootclasspathfragment",
+					],
+				}
+			`),
+		android.FixtureAddFile("prebuilts/apex/art.apex", nil),
+	)
+
 	CheckSnapshot(t, result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
@@ -121,19 +153,9 @@
 		checkAllCopyRules(`
 .intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
 `),
-		snapshotTestPreparer(checkSnapshotPreferredWithSource, android.GroupFixturePreparers(
-			android.FixtureAddTextFile("prebuilts/apex/Android.bp", `
-				prebuilt_apex {
-					name: "com.android.art",
-					src: "art.apex",
-					exported_java_libs: [
-						"mybootlib",
-					],
-				}
-			`),
-			android.FixtureAddFile("prebuilts/apex/art.apex", nil),
-		),
-		),
+		snapshotTestPreparer(checkSnapshotWithoutSource, prepareWithPrebuiltApex),
+		snapshotTestPreparer(checkSnapshotWithSourcePreferred, prepareWithPrebuiltApex),
+		snapshotTestPreparer(checkSnapshotPreferredWithSource, prepareWithPrebuiltApex),
 	)
 }
 
diff --git a/sdk/testing.go b/sdk/testing.go
index bf59aed..f4e85c0 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -254,6 +254,7 @@
 	snapshotPreparer := android.GroupFixturePreparers(sourcePreparers, fs.AddToFixture())
 
 	var runSnapshotTestWithCheckers = func(t *testing.T, testConfig snapshotTest, extraPreparer android.FixturePreparer) {
+		t.Helper()
 		customization := snapshotBuildInfo.snapshotTestCustomization(testConfig)
 		customizedPreparers := android.GroupFixturePreparers(customization.preparers...)