Merge "Add libc_openbsd to mixed builds deny list."
diff --git a/android/androidmk.go b/android/androidmk.go
index 618e4be..590eceb 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -550,7 +550,9 @@
 		if !amod.InRamdisk() && !amod.InVendorRamdisk() {
 			a.AddPaths("LOCAL_FULL_INIT_RC", amod.initRcPaths)
 		}
-		a.AddStrings("LOCAL_VINTF_FRAGMENTS", amod.commonProperties.Vintf_fragments...)
+		if len(amod.vintfFragmentsPaths) > 0 {
+			a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", amod.vintfFragmentsPaths)
+		}
 		a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(amod.commonProperties.Proprietary))
 		if Bool(amod.commonProperties.Vendor) || Bool(amod.commonProperties.Soc_specific) {
 			a.SetString("LOCAL_VENDOR_MODULE", "true")
diff --git a/android/arch.go b/android/arch.go
index 20fcc95..9f93752 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -617,7 +617,7 @@
 	}
 
 	// only the primary arch in the ramdisk / vendor_ramdisk / recovery partition
-	if os == Android && (module.InstallInRecovery() || module.InstallInRamdisk() || module.InstallInVendorRamdisk()) {
+	if os == Android && (module.InstallInRecovery() || module.InstallInRamdisk() || module.InstallInVendorRamdisk() || module.InstallInDebugRamdisk()) {
 		osTargets = []Target{osTargets[0]}
 	}
 
diff --git a/android/image.go b/android/image.go
index 1a1a423..bdb9be0 100644
--- a/android/image.go
+++ b/android/image.go
@@ -30,6 +30,11 @@
 	// vendor ramdisk partition).
 	VendorRamdiskVariantNeeded(ctx BaseModuleContext) bool
 
+	// DebugRamdiskVariantNeeded should return true if the module needs a debug ramdisk variant (installed on the
+	// debug ramdisk partition: $(PRODUCT_OUT)/debug_ramdisk/first_stage_ramdisk if BOARD_USES_RECOVERY_AS_ROOT is
+	// true, $(PRODUCT_OUT)/debug_ramdisk otherise).
+	DebugRamdiskVariantNeeded(ctx BaseModuleContext) bool
+
 	// RecoveryVariantNeeded should return true if the module needs a recovery variant (installed on the
 	// recovery partition).
 	RecoveryVariantNeeded(ctx BaseModuleContext) bool
@@ -60,6 +65,9 @@
 
 	// VendorRamdiskVariation means a module to be installed to vendor ramdisk image.
 	VendorRamdiskVariation string = "vendor_ramdisk"
+
+	// DebugRamdiskVariation means a module to be installed to debug ramdisk image.
+	DebugRamdiskVariation string = "debug_ramdisk"
 )
 
 // imageMutator creates variants for modules that implement the ImageInterface that
@@ -83,6 +91,9 @@
 		if m.VendorRamdiskVariantNeeded(ctx) {
 			variations = append(variations, VendorRamdiskVariation)
 		}
+		if m.DebugRamdiskVariantNeeded(ctx) {
+			variations = append(variations, DebugRamdiskVariation)
+		}
 		if m.RecoveryVariantNeeded(ctx) {
 			variations = append(variations, RecoveryVariation)
 		}
diff --git a/android/module.go b/android/module.go
index 9f923e2..942e071 100644
--- a/android/module.go
+++ b/android/module.go
@@ -393,6 +393,7 @@
 	InstallInSanitizerDir() bool
 	InstallInRamdisk() bool
 	InstallInVendorRamdisk() bool
+	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -450,6 +451,7 @@
 	InstallInSanitizerDir() bool
 	InstallInRamdisk() bool
 	InstallInVendorRamdisk() bool
+	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -753,6 +755,9 @@
 	// Whether this module is installed to vendor ramdisk
 	Vendor_ramdisk *bool
 
+	// Whether this module is installed to debug ramdisk
+	Debug_ramdisk *bool
+
 	// Whether this module is built for non-native architectures (also known as native bridge binary)
 	Native_bridge_supported *bool `android:"arch_variant"`
 
@@ -1540,6 +1545,10 @@
 	return Bool(m.commonProperties.Vendor_ramdisk)
 }
 
+func (m *ModuleBase) InstallInDebugRamdisk() bool {
+	return Bool(m.commonProperties.Debug_ramdisk)
+}
+
 func (m *ModuleBase) InstallInRecovery() bool {
 	return Bool(m.commonProperties.Recovery)
 }
@@ -1593,6 +1602,10 @@
 	return m.base().commonProperties.ImageVariation == VendorRamdiskVariation
 }
 
+func (m *ModuleBase) InDebugRamdisk() bool {
+	return m.base().commonProperties.ImageVariation == DebugRamdiskVariation
+}
+
 func (m *ModuleBase) InRecovery() bool {
 	return m.base().commonProperties.ImageVariation == RecoveryVariation
 }
@@ -2548,6 +2561,10 @@
 	return m.module.InstallInVendorRamdisk()
 }
 
+func (m *moduleContext) InstallInDebugRamdisk() bool {
+	return m.module.InstallInDebugRamdisk()
+}
+
 func (m *moduleContext) InstallInRecovery() bool {
 	return m.module.InstallInRecovery()
 }
diff --git a/android/paths.go b/android/paths.go
index 93c5684..026cb87 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -106,6 +106,7 @@
 	InstallInSanitizerDir() bool
 	InstallInRamdisk() bool
 	InstallInVendorRamdisk() bool
+	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallBypassMake() bool
@@ -1689,6 +1690,16 @@
 			if !ctx.InstallInRoot() {
 				partition += "/system"
 			}
+		} else if ctx.InstallInDebugRamdisk() {
+			// The module is only available after switching root into
+			// /first_stage_ramdisk. To expose the module before switching root
+			// on a device without a dedicated recovery partition, install the
+			// recovery variant.
+			if ctx.DeviceConfig().BoardUsesRecoveryAsBoot() {
+				partition = "debug_ramdisk/first_stage_ramdisk"
+			} else {
+				partition = "debug_ramdisk"
+			}
 		} else if ctx.InstallInRecovery() {
 			if ctx.InstallInRoot() {
 				partition = "recovery/root"
@@ -1859,6 +1870,7 @@
 	inSanitizerDir  bool
 	inRamdisk       bool
 	inVendorRamdisk bool
+	inDebugRamdisk  bool
 	inRecovery      bool
 	inRoot          bool
 	forceOS         *OsType
@@ -1891,6 +1903,10 @@
 	return m.inVendorRamdisk
 }
 
+func (m testModuleInstallPathContext) InstallInDebugRamdisk() bool {
+	return m.inDebugRamdisk
+}
+
 func (m testModuleInstallPathContext) InstallInRecovery() bool {
 	return m.inRecovery
 }
diff --git a/android/paths_test.go b/android/paths_test.go
index 6ec75b4..cb9138b 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -395,6 +395,19 @@
 			partitionDir: "target/product/test_device/vendor_ramdisk",
 		},
 		{
+			name: "debug_ramdisk binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inDebugRamdisk: true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/debug_ramdisk/my_test",
+			partitionDir: "target/product/test_device/debug_ramdisk",
+		},
+		{
 			name: "system native test binary",
 			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
@@ -733,6 +746,19 @@
 			out:          "target/product/test_device/vendor_ramdisk/first_stage_ramdisk/my_test",
 			partitionDir: "target/product/test_device/vendor_ramdisk/first_stage_ramdisk",
 		},
+		{
+			name: "debug_ramdisk binary",
+			ctx: &testModuleInstallPathContext{
+				baseModuleContext: baseModuleContext{
+					os:     deviceTarget.Os,
+					target: deviceTarget,
+				},
+				inDebugRamdisk: true,
+			},
+			in:           []string{"my_test"},
+			out:          "target/product/test_device/debug_ramdisk/first_stage_ramdisk/my_test",
+			partitionDir: "target/product/test_device/debug_ramdisk/first_stage_ramdisk",
+		},
 	}
 
 	for _, tc := range testCases {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 9fc701d..06d3b54 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -284,7 +284,7 @@
 					// To install companion files (init_rc, vintf_fragments)
 					// Copy some common properties of apexBundle to apex_manifest
 					commonProperties := []string{
-						"LOCAL_FULL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
+						"LOCAL_FULL_INIT_RC", "LOCAL_FULL_VINTF_FRAGMENTS",
 					}
 					for _, name := range commonProperties {
 						if value, ok := apexAndroidMkData.Entries.EntryMap[name]; ok {
@@ -394,7 +394,7 @@
 				// Because apex writes .mk with Custom(), we need to write manually some common properties
 				// which are available via data.Entries
 				commonProperties := []string{
-					"LOCAL_FULL_INIT_RC", "LOCAL_VINTF_FRAGMENTS",
+					"LOCAL_FULL_INIT_RC", "LOCAL_FULL_VINTF_FRAGMENTS",
 					"LOCAL_PROPRIETARY_MODULE", "LOCAL_VENDOR_MODULE", "LOCAL_ODM_MODULE", "LOCAL_PRODUCT_MODULE", "LOCAL_SYSTEM_EXT_MODULE",
 					"LOCAL_MODULE_OWNER",
 				}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index a7ae6f0..c2378fc 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -2755,7 +2755,7 @@
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_VINTF_FRAGMENTS := fragment.xml\n")
+	ensureContains(t, androidMk, "LOCAL_FULL_VINTF_FRAGMENTS := fragment.xml\n")
 	ensureContains(t, androidMk, "LOCAL_FULL_INIT_RC := init.rc\n")
 }
 
diff --git a/apex/testing.go b/apex/testing.go
index 926125f..69bd73e 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -25,5 +25,7 @@
 		// Needed by apex.
 		"system/core/rootdir/etc/public.libraries.android.txt": nil,
 		"build/soong/scripts/gen_ndk_backedby_apex.sh":         nil,
+		// Needed by prebuilt_apex.
+		"build/soong/scripts/unpack-prebuilt-apex.sh": nil,
 	}.AddToFixture(),
 )
diff --git a/cc/genrule.go b/cc/genrule.go
index ca4fda7..82d7205 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -75,6 +75,10 @@
 	return Bool(g.Vendor_ramdisk_available)
 }
 
+func (g *GenruleExtraProperties) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	// If the build is using a snapshot, the recovery variant under AOSP directories
 	// is not needed.
diff --git a/cc/image.go b/cc/image.go
index c1e5dfe..bf662c6 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -601,6 +601,10 @@
 	return c.Properties.VendorRamdiskVariantNeeded
 }
 
+func (c *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return c.Properties.RecoveryVariantNeeded
 }
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index c12ad79..885a0ce 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -308,6 +308,10 @@
 	return false
 }
 
+func (s *snapshot) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (s *snapshot) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 6e502b7..3204e70 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -84,6 +84,13 @@
 	// the recovery variant instead.
 	Vendor_ramdisk_available *bool
 
+	// Make this module available when building for debug ramdisk.
+	// On device without a dedicated recovery partition, the module is only
+	// available after switching root into
+	// /first_stage_ramdisk. To expose the module before switching root, install
+	// the recovery variant instead.
+	Debug_ramdisk_available *bool
+
 	// Make this module available when building for recovery.
 	Recovery_available *bool
 
@@ -159,6 +166,18 @@
 	return p.inVendorRamdisk()
 }
 
+func (p *PrebuiltEtc) inDebugRamdisk() bool {
+	return p.ModuleBase.InDebugRamdisk() || p.ModuleBase.InstallInDebugRamdisk()
+}
+
+func (p *PrebuiltEtc) onlyInDebugRamdisk() bool {
+	return p.ModuleBase.InstallInDebugRamdisk()
+}
+
+func (p *PrebuiltEtc) InstallInDebugRamdisk() bool {
+	return p.inDebugRamdisk()
+}
+
 func (p *PrebuiltEtc) inRecovery() bool {
 	return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
 }
@@ -177,7 +196,7 @@
 
 func (p *PrebuiltEtc) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return !p.ModuleBase.InstallInRecovery() && !p.ModuleBase.InstallInRamdisk() &&
-		!p.ModuleBase.InstallInVendorRamdisk()
+		!p.ModuleBase.InstallInVendorRamdisk() && !p.ModuleBase.InstallInDebugRamdisk()
 }
 
 func (p *PrebuiltEtc) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -188,6 +207,10 @@
 	return proptools.Bool(p.properties.Vendor_ramdisk_available) || p.ModuleBase.InstallInVendorRamdisk()
 }
 
+func (p *PrebuiltEtc) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return proptools.Bool(p.properties.Debug_ramdisk_available) || p.ModuleBase.InstallInDebugRamdisk()
+}
+
 func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return proptools.Bool(p.properties.Recovery_available) || p.ModuleBase.InstallInRecovery()
 }
@@ -313,6 +336,9 @@
 	if p.inVendorRamdisk() && !p.onlyInVendorRamdisk() {
 		nameSuffix = ".vendor_ramdisk"
 	}
+	if p.inDebugRamdisk() && !p.onlyInDebugRamdisk() {
+		nameSuffix = ".debug_ramdisk"
+	}
 	if p.inRecovery() && !p.onlyInRecovery() {
 		nameSuffix = ".recovery"
 	}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b426b20..77dae75 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -626,6 +626,7 @@
 func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool            { return false }
 func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool         { return false }
 func (x noopImageInterface) VendorRamdiskVariantNeeded(android.BaseModuleContext) bool   { return false }
+func (x noopImageInterface) DebugRamdiskVariantNeeded(android.BaseModuleContext) bool    { return false }
 func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool        { return false }
 func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
 func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
diff --git a/java/boot_image.go b/java/boot_image.go
index 192b16b..d0862a9 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 	"strings"
 
 	"android/soong/android"
@@ -219,7 +220,9 @@
 	for _, name := range b.properties.Contents {
 		// A bootclasspath_fragment must depend only on other source modules, while the
 		// prebuilt_bootclasspath_fragment must only depend on other prebuilt modules.
-		if !isSourceModule {
+		//
+		// TODO(b/177892522) - avoid special handling of jacocoagent.
+		if !isSourceModule && name != "jacocoagent" {
 			name = android.PrebuiltNameFromSource(name)
 		}
 		ctx.AddDependency(module, bootclasspathFragmentContentDepTag, name)
@@ -319,19 +322,58 @@
 type bootImageSdkMemberProperties struct {
 	android.SdkMemberPropertiesBase
 
+	// The image name
 	Image_name *string
+
+	// Contents of the bootclasspath fragment
+	Contents []string
+
+	// Flag files by *hiddenAPIFlagFileCategory
+	Flag_files_by_category map[*hiddenAPIFlagFileCategory]android.Paths
 }
 
 func (b *bootImageSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
 	module := variant.(*BootImageModule)
 
 	b.Image_name = module.properties.Image_name
+	if b.Image_name == nil {
+		// Only one of image_name or contents can be specified. However, if image_name is set then the
+		// contents property is updated to match the configuration used to create the corresponding
+		// boot image. Therefore, contents property is only copied if the image name is not specified.
+		b.Contents = module.properties.Contents
+	}
+
+	// Get the flag file information from the module.
+	mctx := ctx.SdkModuleContext()
+	flagFileInfo := mctx.OtherModuleProvider(module, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo)
+	b.Flag_files_by_category = flagFileInfo.categoryToPaths
 }
 
 func (b *bootImageSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
 	if b.Image_name != nil {
 		propertySet.AddProperty("image_name", *b.Image_name)
 	}
+
+	if len(b.Contents) > 0 {
+		propertySet.AddPropertyWithTag("contents", b.Contents, ctx.SnapshotBuilder().SdkMemberReferencePropertyTag(true))
+	}
+
+	builder := ctx.SnapshotBuilder()
+	if b.Flag_files_by_category != nil {
+		hiddenAPISet := propertySet.AddPropertySet("hidden_api")
+		for _, category := range hiddenAPIFlagFileCategories {
+			paths := b.Flag_files_by_category[category]
+			if len(paths) > 0 {
+				dests := []string{}
+				for _, p := range paths {
+					dest := filepath.Join("hiddenapi", p.Base())
+					builder.CopyToSnapshot(p, dest)
+					dests = append(dests, dest)
+				}
+				hiddenAPISet.AddProperty(category.propertyName, dests)
+			}
+		}
+	}
 }
 
 var _ android.SdkMemberType = (*bootImageMemberType)(nil)
diff --git a/java/testing.go b/java/testing.go
index aee0710..08a71b8 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -200,6 +200,9 @@
 		}),
 		dexpreopt.FixtureSetBootJars(bootJars...),
 		dexpreopt.FixtureSetArtBootJars(artBootJars...),
+
+		// Add a fake dex2oatd module.
+		dexpreopt.PrepareForTestWithFakeDex2oatd,
 	)
 }
 
@@ -212,6 +215,9 @@
 			variables.UpdatableBootJars = android.CreateTestConfiguredJarList(bootJars)
 		}),
 		dexpreopt.FixtureSetUpdatableBootJars(bootJars...),
+
+		// Add a fake dex2oatd module.
+		dexpreopt.PrepareForTestWithFakeDex2oatd,
 	)
 }
 
diff --git a/rust/image.go b/rust/image.go
index 7eb49d9..900842e 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -97,6 +97,10 @@
 	return mod.InRamdisk()
 }
 
+func (mod *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
 	return mod.InRecovery()
 }
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 10f86e8..0ce4351 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -18,20 +18,146 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/java"
 )
 
-func TestSnapshotWithBootclasspathFragment(t *testing.T) {
+func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		prepareForSdkTestWithApex,
+
+		// Some additional files needed for the art apex.
+		android.FixtureMergeMockFs(android.MockFS{
+			"com.android.art.avbpubkey":                          nil,
+			"com.android.art.pem":                                nil,
+			"system/sepolicy/apex/com.android.art-file_contexts": nil,
+		}),
+		java.FixtureConfigureBootJars("com.android.art:mybootlib"),
+		android.FixtureWithRootAndroidBp(`
+			sdk {
+				name: "mysdk",
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+				java_boot_libs: ["mybootlib"],
+			}
+
+			apex {
+				name: "com.android.art",
+				key: "com.android.art.key",
+				bootclasspath_fragments: [
+					"mybootclasspathfragment",
+				],
+				updatable: false,
+			}
+
+			bootclasspath_fragment {
+				name: "mybootclasspathfragment",
+				image_name: "art",
+				apex_available: ["com.android.art"],
+			}
+
+			apex_key {
+				name: "com.android.art.key",
+				public_key: "com.android.art.avbpubkey",
+				private_key: "com.android.art.pem",
+			}
+
+			java_library {
+				name: "mybootlib",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+				compile_dex: true,
+				apex_available: ["com.android.art"],
+			}
+		`),
+	).RunTest(t)
+
+	CheckSnapshot(t, result, "mysdk", "",
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_bootclasspath_fragment {
+    name: "mybootclasspathfragment",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["com.android.art"],
+    image_name: "art",
+}
+
+java_import {
+    name: "mybootlib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["com.android.art"],
+    jars: ["java/mybootlib.jar"],
+}
+`),
+		checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_bootclasspath_fragment {
+    name: "mysdk_mybootclasspathfragment@current",
+    sdk_member_name: "mybootclasspathfragment",
+    visibility: ["//visibility:public"],
+    apex_available: ["com.android.art"],
+    image_name: "art",
+}
+
+java_import {
+    name: "mysdk_mybootlib@current",
+    sdk_member_name: "mybootlib",
+    visibility: ["//visibility:public"],
+    apex_available: ["com.android.art"],
+    jars: ["java/mybootlib.jar"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    visibility: ["//visibility:public"],
+    bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"],
+    java_boot_libs: ["mysdk_mybootlib@current"],
+}
+`),
+		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),
+		),
+		),
+	)
+}
+
+func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureWithRootAndroidBp(`
 			sdk {
 				name: "mysdk",
 				bootclasspath_fragments: ["mybootclasspathfragment"],
+				java_boot_libs: ["mybootlib"],
 			}
 
 			bootclasspath_fragment {
 				name: "mybootclasspathfragment",
-				image_name: "art",
+				contents: ["mybootlib"],
+			}
+
+			java_library {
+				name: "mybootlib",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+				compile_dex: true,
 			}
 		`),
 	).RunTest(t)
@@ -45,7 +171,15 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
-    image_name: "art",
+    contents: ["mybootlib"],
+}
+
+java_import {
+    name: "mybootlib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/mybootlib.jar"],
 }
 `),
 		checkVersionedAndroidBpContents(`
@@ -56,16 +190,27 @@
     sdk_member_name: "mybootclasspathfragment",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
-    image_name: "art",
+    contents: ["mysdk_mybootlib@current"],
+}
+
+java_import {
+    name: "mysdk_mybootlib@current",
+    sdk_member_name: "mybootlib",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/mybootlib.jar"],
 }
 
 sdk_snapshot {
     name: "mysdk@current",
     visibility: ["//visibility:public"],
     bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"],
+    java_boot_libs: ["mysdk_mybootlib@current"],
 }
 `),
-		checkAllCopyRules(""))
+		checkAllCopyRules(`
+.intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
+`))
 }
 
 // Test that bootclasspath_fragment works with sdk.
@@ -103,3 +248,108 @@
 	`),
 	).RunTest(t)
 }
+
+func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.MockFS{
+			"my-blocked.txt":                   nil,
+			"my-max-target-o-low-priority.txt": nil,
+			"my-max-target-p.txt":              nil,
+			"my-max-target-q.txt":              nil,
+			"my-max-target-r-low-priority.txt": nil,
+			"my-removed.txt":                   nil,
+			"my-unsupported-packages.txt":      nil,
+			"my-unsupported.txt":               nil,
+		}.AddToFixture(),
+		android.FixtureWithRootAndroidBp(`
+			sdk {
+				name: "mysdk",
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+				java_boot_libs: ["mybootlib"],
+			}
+
+			bootclasspath_fragment {
+				name: "mybootclasspathfragment",
+				contents: ["mybootlib"],
+				hidden_api: {
+					unsupported: [
+							"my-unsupported.txt",
+					],
+					removed: [
+							"my-removed.txt",
+					],
+					max_target_r_low_priority: [
+							"my-max-target-r-low-priority.txt",
+					],
+					max_target_q: [
+							"my-max-target-q.txt",
+					],
+					max_target_p: [
+							"my-max-target-p.txt",
+					],
+					max_target_o_low_priority: [
+							"my-max-target-o-low-priority.txt",
+					],
+					blocked: [
+							"my-blocked.txt",
+					],
+					unsupported_packages: [
+							"my-unsupported-packages.txt",
+					],
+				},
+			}
+
+			java_library {
+				name: "mybootlib",
+				srcs: ["Test.java"],
+				system_modules: "none",
+				sdk_version: "none",
+				compile_dex: true,
+			}
+		`),
+	).RunTest(t)
+
+	CheckSnapshot(t, result, "mysdk", "",
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_bootclasspath_fragment {
+    name: "mybootclasspathfragment",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    contents: ["mybootlib"],
+    hidden_api: {
+        unsupported: ["hiddenapi/my-unsupported.txt"],
+        removed: ["hiddenapi/my-removed.txt"],
+        max_target_r_low_priority: ["hiddenapi/my-max-target-r-low-priority.txt"],
+        max_target_q: ["hiddenapi/my-max-target-q.txt"],
+        max_target_p: ["hiddenapi/my-max-target-p.txt"],
+        max_target_o_low_priority: ["hiddenapi/my-max-target-o-low-priority.txt"],
+        blocked: ["hiddenapi/my-blocked.txt"],
+        unsupported_packages: ["hiddenapi/my-unsupported-packages.txt"],
+    },
+}
+
+java_import {
+    name: "mybootlib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/mybootlib.jar"],
+}
+`),
+		checkAllCopyRules(`
+my-unsupported.txt -> hiddenapi/my-unsupported.txt
+my-removed.txt -> hiddenapi/my-removed.txt
+my-max-target-r-low-priority.txt -> hiddenapi/my-max-target-r-low-priority.txt
+my-max-target-q.txt -> hiddenapi/my-max-target-q.txt
+my-max-target-p.txt -> hiddenapi/my-max-target-p.txt
+my-max-target-o-low-priority.txt -> hiddenapi/my-max-target-o-low-priority.txt
+my-blocked.txt -> hiddenapi/my-blocked.txt
+my-unsupported-packages.txt -> hiddenapi/my-unsupported-packages.txt
+.intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
+`),
+	)
+}
diff --git a/sdk/testing.go b/sdk/testing.go
index 9465e13..bf59aed 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -255,13 +255,14 @@
 
 	var runSnapshotTestWithCheckers = func(t *testing.T, testConfig snapshotTest, extraPreparer android.FixturePreparer) {
 		customization := snapshotBuildInfo.snapshotTestCustomization(testConfig)
+		customizedPreparers := android.GroupFixturePreparers(customization.preparers...)
 
 		// TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the
 		//  files the snapshot needs are actually copied into the snapshot.
 
 		// Run the snapshot with the snapshot preparer and the extra preparer, which must come after as
 		// it may need to modify parts of the MockFS populated by the snapshot preparer.
-		result := android.GroupFixturePreparers(snapshotPreparer, extraPreparer).
+		result := android.GroupFixturePreparers(snapshotPreparer, extraPreparer, customizedPreparers).
 			ExtendWithErrorHandler(customization.errorHandler).
 			RunTest(t)
 
@@ -369,6 +370,15 @@
 
 type resultChecker func(t *testing.T, result *android.TestResult)
 
+// snapshotTestPreparer registers a preparer that will be used to customize the specified
+// snapshotTest.
+func snapshotTestPreparer(snapshotTest snapshotTest, preparer android.FixturePreparer) snapshotBuildInfoChecker {
+	return func(info *snapshotBuildInfo) {
+		customization := info.snapshotTestCustomization(snapshotTest)
+		customization.preparers = append(customization.preparers, preparer)
+	}
+}
+
 // snapshotTestChecker registers a checker that will be run against the result of processing the
 // generated snapshot for the specified snapshotTest.
 func snapshotTestChecker(snapshotTest snapshotTest, checker resultChecker) snapshotBuildInfoChecker {
@@ -395,6 +405,9 @@
 
 // Encapsulates information provided by each test to customize a specific snapshotTest.
 type snapshotTestCustomization struct {
+	// Preparers that are used to customize the test fixture before running the test.
+	preparers []android.FixturePreparer
+
 	// Checkers that are run on the result of processing the preferred snapshot in a specific test
 	// case.
 	checkers []resultChecker
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 6623381..42d5680 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -210,6 +210,10 @@
 	return proptools.Bool(s.properties.Vendor_ramdisk_available) || s.ModuleBase.InstallInVendorRamdisk()
 }
 
+func (s *ShBinary) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
 func (s *ShBinary) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return proptools.Bool(s.properties.Recovery_available) || s.ModuleBase.InstallInRecovery()
 }