Add contents property to boot_image (and prebuilt_boot_image)

Allows boot_image modules to be created for any module that contributes
to the boot class path, e.g. core-i18n from the com.android.i18n.

A boot_image module with a contents property cannot specify an
image_name, and vice versa. Only those boot_image modules with an
image_name create .art, .oat and .vdex files, either in their
associated APEX or as part of the framework "boot" image.

Bug: 177892522
Test: m nothing
Change-Id: Idfc2bcf00dd6d3ed36ac4df46fcf18e8aa7e2c92
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
index 678a4cd..8d91f5a 100644
--- a/apex/boot_image_test.go
+++ b/apex/boot_image_test.go
@@ -315,4 +315,63 @@
 	})
 }
 
+func TestBootImageContentsNoName(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootImage,
+		prepareForTestWithMyapex,
+	).RunTestWithBp(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			boot_images: [
+				"mybootimage",
+			],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+		}
+
+		boot_image {
+			name: "mybootimage",
+			contents: [
+				"foo",
+				"bar",
+			],
+			apex_available: [
+				"myapex",
+			],
+		}
+	`)
+
+	// The apex is empty because the contents of boot_image are not transitively included, yet!
+	ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{})
+
+	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+		`myapex.key`,
+		`mybootimage`,
+	})
+}
+
 // TODO(b/177892522) - add test for host apex.
diff --git a/java/boot_image.go b/java/boot_image.go
index 8594792..0fc9c6c 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -73,13 +73,13 @@
 type bootImageProperties struct {
 	// The name of the image this represents.
 	//
-	// Must be one of "art" or "boot".
+	// If specified then it must be one of "art" or "boot".
 	Image_name *string
 
 	// The contents of this boot image, could be either java_library, java_sdk_library, or boot_image.
 	//
 	// The order of this list matters as it is the order that is used in the bootclasspath.
-	Contents []string `blueprint:"mutated"`
+	Contents []string
 }
 
 type BootImageModule struct {
@@ -104,6 +104,13 @@
 }
 
 func bootImageConsistencyCheck(ctx android.EarlyModuleContext, m *BootImageModule) {
+	contents := m.properties.Contents
+	if m.properties.Image_name == nil && len(contents) == 0 {
+		ctx.ModuleErrorf(`neither of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
+	}
+	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" {
 		// Get the configuration for the art apex jars. Do not use getImageConfig(ctx) here as this is
diff --git a/java/boot_image_test.go b/java/boot_image_test.go
index a289df8..e1866de 100644
--- a/java/boot_image_test.go
+++ b/java/boot_image_test.go
@@ -101,3 +101,27 @@
 			}
 		`)
 }
+
+func TestBootImageWithoutImageNameOrContents(t *testing.T) {
+	prepareForTestWithBootImage.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\Qneither of the "image_name" and "contents" properties\E`)).
+		RunTestWithBp(t, `
+			boot_image {
+				name: "boot-image",
+			}
+		`)
+}
+
+func TestBootImageWithImageNameAndContents(t *testing.T) {
+	prepareForTestWithBootImage.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\Qboth of the "image_name" and "contents" properties\E`)).
+		RunTestWithBp(t, `
+			boot_image {
+				name: "boot-image",
+				image_name: "boot",
+				contents: ["other"],
+			}
+		`)
+}