Merge "Propagated srcs from cc_defaults to cc_prebuilt_library_shared." into sc-dev
diff --git a/android/deapexer.go b/android/deapexer.go
index 63508d7..de3f635 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -15,49 +15,75 @@
 package android
 
 import (
-	"fmt"
-	"strings"
-
 	"github.com/google/blueprint"
 )
 
 // Provides support for interacting with the `deapexer` module to which a `prebuilt_apex` module
 // will delegate the work to export files from a prebuilt '.apex` file.
+//
+// The actual processing that is done is quite convoluted but it is all about combining information
+// from multiple different sources in order to allow a prebuilt module to use a file extracted from
+// an apex file. As follows:
+//
+// 1. A prebuilt module, e.g. prebuilt_bootclasspath_fragment or java_import needs to use a file
+//    from a prebuilt_apex/apex_set. It knows the path of the file within the apex but does not know
+//    where the apex file is or what apex to use.
+//
+// 2. The connection between the prebuilt module and the prebuilt_apex/apex_set is created through
+//    use of an exported_... property on the latter. That causes four things to occur:
+//    a. A `deapexer` mopdule is created by the prebuilt_apex/apex_set to extract files from the
+//       apex file.
+//    b. A dependency is added from the prebuilt_apex/apex_set modules onto the prebuilt modules
+//       listed in those properties.
+//    c. An APEX variant is created for each of those prebuilt modules.
+//    d. A dependency is added from the prebuilt modules to the `deapexer` module.
+//
+// 3. The prebuilt_apex/apex_set modules do not know which files are available in the apex file.
+//    That information could be specified on the prebuilt_apex/apex_set modules but without
+//    automated generation of those modules it would be expensive to maintain. So, instead they
+//    obtain that information from the prebuilt modules. They do not know what files are actually in
+//    the apex file either but they know what files they need from it. So, the
+//    prebuilt_apex/apex_set modules obtain the files that should be in the apex file from those
+//    modules and then pass those onto the `deapexer` module.
+//
+// 4. The `deapexer` module's ninja rule extracts all the files from the apex file into an output
+//    directory and checks that all the expected files are there. The expected files are declared as
+//    the outputs of the ninja rule so they are available to other modules.
+//
+// 5. The prebuilt modules then retrieve the paths to the files that they needed from the `deapexer`
+//    module.
+//
+// The files that are passed to `deapexer` and those that are passed back have a unique identifier
+// that links them together. e.g. If the `deapexer` is passed something like this:
+//     javalib/core-libart.jar -> javalib/core-libart.jar
+// it will return something like this:
+//     javalib/core-libart.jar -> out/soong/.....deapexer.../javalib/core-libart.jar
+//
+// The reason why the `deapexer` module is separate from the prebuilt_apex/apex_set is to avoid
+// cycles. e.g.
+//   prebuilt_apex "com.android.art" depends upon java_import "core-libart":
+//       This is so it can create an APEX variant of the latter and obtain information about the
+//       files that it needs from the apex file.
+//   java_import "core-libart" depends upon `deapexer` module:
+//       This is so it can retrieve the paths to the files it needs.
 
 // The information exported by the `deapexer` module, access it using `DeapxerInfoProvider`.
 type DeapexerInfo struct {
 	// map from the name of an exported file from a prebuilt_apex to the path to that file. The
-	// exported file name is of the form <module>{<tag>} where <tag> is currently only allowed to be
-	// ".dexjar".
+	// exported file name is the apex relative path, e.g. javalib/core-libart.jar.
 	//
 	// See Prebuilt.ApexInfoMutator for more information.
 	exports map[string]Path
 }
 
-// The set of supported prebuilt export tags. Used to verify the tag parameter for
-// `PrebuiltExportPath`.
-var supportedPrebuiltExportTags = map[string]struct{}{
-	".dexjar": {},
-}
-
 // PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
 // prebuilt_apex that created this ApexInfo.
 //
-// The exported file is identified by the module name and the tag:
-// * The module name is the name of the module that contributed the file when the .apex file
-//   referenced by the prebuilt_apex was built. It must be specified in one of the exported_...
-//   properties on the prebuilt_apex module.
-// * The tag identifies the type of file and is dependent on the module type.
+// The exported file is identified by the apex relative path, e.g. "javalib/core-libart.jar".
 //
 // See apex/deapexer.go for more information.
-func (i DeapexerInfo) PrebuiltExportPath(name, tag string) Path {
-
-	if _, ok := supportedPrebuiltExportTags[tag]; !ok {
-		panic(fmt.Errorf("unsupported prebuilt export tag %q, expected one of %s",
-			tag, strings.Join(SortedStringKeys(supportedPrebuiltExportTags), ", ")))
-	}
-
-	path := i.exports[name+"{"+tag+"}"]
+func (i DeapexerInfo) PrebuiltExportPath(apexRelativePath string) Path {
+	path := i.exports[apexRelativePath]
 	return path
 }
 
@@ -79,5 +105,31 @@
 	blueprint.BaseDependencyTag
 }
 
+// Mark this tag so dependencies that use it are excluded from APEX contents.
+func (t deapexerTagStruct) ExcludeFromApexContents() {}
+
+var _ ExcludeFromApexContentsTag = DeapexerTag
+
 // A tag that is used for dependencies on the `deapexer` module.
 var DeapexerTag = deapexerTagStruct{}
+
+// RequiredFilesFromPrebuiltApex must be implemented by modules that require files to be exported
+// from a prebuilt_apex/apex_set.
+type RequiredFilesFromPrebuiltApex interface {
+	// RequiredFilesFromPrebuiltApex returns a list of the file paths (relative to the root of the
+	// APEX's contents) that the implementing module requires from within a prebuilt .apex file.
+	//
+	// For each file path this will cause the file to be extracted out of the prebuilt .apex file, and
+	// the path to the extracted file will be stored in the DeapexerInfo using the APEX relative file
+	// path as the key, The path can then be retrieved using the PrebuiltExportPath(key) method.
+	RequiredFilesFromPrebuiltApex(ctx BaseModuleContext) []string
+}
+
+// Marker interface that identifies dependencies on modules that may require files from a prebuilt
+// apex.
+type RequiresFilesFromPrebuiltApexTag interface {
+	blueprint.DependencyTag
+
+	// Method that differentiates this interface from others.
+	RequiresFilesFromPrebuiltApex()
+}
diff --git a/android/sdk.go b/android/sdk.go
index 36c576d..e644806 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -38,6 +38,36 @@
 type sdkAwareWithoutModule interface {
 	RequiredSdks
 
+	// SdkMemberComponentName will return the name to use for a component of this module based on the
+	// base name of this module.
+	//
+	// The baseName is the name returned by ModuleBase.BaseModuleName(), i.e. the name specified in
+	// the name property in the .bp file so will not include the prebuilt_ prefix.
+	//
+	// The componentNameCreator is a func for creating the name of a component from the base name of
+	// the module, e.g. it could just append ".component" to the name passed in.
+	//
+	// This is intended to be called by prebuilt modules that create component models. It is because
+	// prebuilt module base names come in a variety of different forms:
+	// * unversioned - this is the same as the source module.
+	// * internal to an sdk - this is the unversioned name prefixed by the base name of the sdk
+	//   module.
+	// * versioned - this is the same as the internal with the addition of an "@<version>" suffix.
+	//
+	// While this can be called from a source module in that case it will behave the same way as the
+	// unversioned name and return the result of calling the componentNameCreator func on the supplied
+	// base name.
+	//
+	// e.g. Assuming the componentNameCreator func simply appends ".component" to the name passed in
+	// then this will work as follows:
+	// * An unversioned name of "foo" will return "foo.component".
+	// * An internal to the sdk name of "sdk_foo" will return "sdk_foo.component".
+	// * A versioned name of "sdk_foo@current" will return "sdk_foo.component@current".
+	//
+	// Note that in the latter case the ".component" suffix is added before the version. Adding it
+	// after would change the version.
+	SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string
+
 	sdkBase() *SdkBase
 	MakeMemberOf(sdk SdkRef)
 	IsInAnySdk() bool
@@ -135,6 +165,18 @@
 	return s
 }
 
+func (s *SdkBase) SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string {
+	if s.MemberName() == "" {
+		return componentNameCreator(baseName)
+	} else {
+		index := strings.LastIndex(baseName, "@")
+		unversionedName := baseName[:index]
+		unversionedComponentName := componentNameCreator(unversionedName)
+		versionSuffix := baseName[index:]
+		return unversionedComponentName + versionSuffix
+	}
+}
+
 // MakeMemberOf sets this module to be a member of a specific SDK
 func (s *SdkBase) MakeMemberOf(sdk SdkRef) {
 	s.properties.ContainingSdk = &sdk
@@ -643,3 +685,30 @@
 	// into which to copy the prebuilt files.
 	Name() string
 }
+
+// ExportedComponentsInfo contains information about the components that this module exports to an
+// sdk snapshot.
+//
+// A component of a module is a child module that the module creates and which forms an integral
+// part of the functionality that the creating module provides. A component module is essentially
+// owned by its creator and is tightly coupled to the creator and other components.
+//
+// e.g. the child modules created by prebuilt_apis are not components because they are not tightly
+// coupled to the prebuilt_apis module. Once they are created the prebuilt_apis ignores them. The
+// child impl and stub library created by java_sdk_library (and corresponding import) are components
+// because the creating module depends upon them in order to provide some of its own functionality.
+//
+// A component is exported if it is part of an sdk snapshot. e.g. The xml and impl child modules are
+// components but they are not exported as they are not part of an sdk snapshot.
+//
+// This information is used by the sdk snapshot generation code to ensure that it does not create
+// an sdk snapshot that contains a declaration of the component module and the module that creates
+// it as that would result in duplicate modules when attempting to use the snapshot. e.g. a snapshot
+// that included the java_sdk_library_import "foo" and also a java_import "foo.stubs" would fail
+// as there would be two modules called "foo.stubs".
+type ExportedComponentsInfo struct {
+	// The names of the exported components.
+	Components []string
+}
+
+var ExportedComponentsInfoProvider = blueprint.NewProvider(ExportedComponentsInfo{})
diff --git a/apex/Android.bp b/apex/Android.bp
index 14c8771..6269757 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -31,6 +31,7 @@
     testSrcs: [
         "apex_test.go",
         "bootclasspath_fragment_test.go",
+        "classpath_element_test.go",
         "platform_bootclasspath_test.go",
         "systemserver_classpath_fragment_test.go",
         "vndk_test.go",
diff --git a/apex/apex.go b/apex/apex.go
index 0c127b2..e731702 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -166,8 +166,7 @@
 	Ignore_system_library_special_case *bool
 
 	// Whenever apex_payload.img of the APEX should include dm-verity hashtree.
-	// Default value is false.
-	// TODO(b/190621617): change default value to true.
+	// Default value is true.
 	Generate_hashtree *bool
 
 	// Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only
@@ -1044,8 +1043,9 @@
 
 	// apexBundle itself is mutated so that it and its dependencies have the same apex variant.
 	// TODO(jiyong): document the reason why the VNDK APEX is an exception here.
-	if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
-		apexBundleName := mctx.ModuleName()
+	unprefixedModuleName := android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName())
+	if apexModuleTypeRequiresVariant(mctx.Module()) {
+		apexBundleName := unprefixedModuleName
 		mctx.CreateVariations(apexBundleName)
 		if strings.HasPrefix(apexBundleName, "com.android.art") {
 			// Create an alias from the platform variant. This is done to make
@@ -1063,6 +1063,12 @@
 			mctx.ModuleErrorf("base property is not set")
 			return
 		}
+		// Workaround the issue reported in b/191269918 by using the unprefixed module name of this
+		// module as the default variation to use if dependencies of this module do not have the correct
+		// apex variant name. This name matches the name used to create the variations of modules for
+		// which apexModuleTypeRequiresVariant return true.
+		// TODO(b/191269918): Remove this workaround.
+		mctx.SetDefaultDependencyVariation(&unprefixedModuleName)
 		mctx.CreateVariations(apexBundleName)
 		if strings.HasPrefix(apexBundleName, "com.android.art") {
 			// TODO(b/183882457): See note for CreateAliasVariation above.
@@ -1071,6 +1077,22 @@
 	}
 }
 
+// apexModuleTypeRequiresVariant determines whether the module supplied requires an apex specific
+// variant.
+func apexModuleTypeRequiresVariant(module android.Module) bool {
+	if a, ok := module.(*apexBundle); ok {
+		return !a.vndkApex
+	}
+
+	// Match apex_set and prebuilt_apex. Would also match apexBundle but that is handled specially
+	// above.
+	if _, ok := module.(ApexInfoMutator); ok {
+		return true
+	}
+
+	return false
+}
+
 // See android.UpdateDirectlyInAnyApex
 // TODO(jiyong): move this to android/apex.go?
 func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
@@ -1271,7 +1293,7 @@
 
 // See the generate_hashtree property
 func (a *apexBundle) shouldGenerateHashtree() bool {
-	return proptools.BoolDefault(a.properties.Generate_hashtree, false)
+	return proptools.BoolDefault(a.properties.Generate_hashtree, true)
 }
 
 // See the test_only_unsigned_payload property
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 92e098c..c50c4a9 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4166,7 +4166,7 @@
 		}
 	`)
 
-	prebuilt := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt)
+	prebuilt := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
 
 	expectedInput := "myapex-arm64.apex"
 	if prebuilt.inputApex.String() != expectedInput {
@@ -4175,7 +4175,7 @@
 }
 
 func TestPrebuiltMissingSrc(t *testing.T) {
-	testApexError(t, `module "myapex" variant "android_common".*: prebuilt_apex does not support "arm64_armv8-a"`, `
+	testApexError(t, `module "myapex" variant "android_common_myapex".*: prebuilt_apex does not support "arm64_armv8-a"`, `
 		prebuilt_apex {
 			name: "myapex",
 		}
@@ -4191,7 +4191,7 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt)
+	p := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
 
 	expected := "notmyapex.apex"
 	if p.installFilename != expected {
@@ -4210,7 +4210,7 @@
 		}
 	`)
 
-	p := ctx.ModuleForTests("myapex.prebuilt", "android_common").Module().(*Prebuilt)
+	p := ctx.ModuleForTests("myapex.prebuilt", "android_common_myapex.prebuilt").Module().(*Prebuilt)
 
 	expected := []string{"myapex"}
 	actual := android.AndroidMkEntriesForTest(t, ctx, p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
@@ -4280,7 +4280,7 @@
 		}
 
 		// Make sure that the prebuilt_apex has the correct input APEX.
-		prebuiltApex := ctx.ModuleForTests("myapex", "android_common")
+		prebuiltApex := ctx.ModuleForTests("myapex", "android_common_myapex")
 		rule = prebuiltApex.Rule("android/soong/android.Cp")
 		if expected, actual := "myapex-arm64.apex", android.NormalizePathForTesting(rule.Input); !reflect.DeepEqual(expected, actual) {
 			t.Errorf("expected: %q, found: %q", expected, actual)
@@ -4416,11 +4416,18 @@
 		}
 	}
 
-	checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedInputs string) {
+	checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
 		t.Helper()
 		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
-		indexRule := platformBootclasspath.Rule("monolithic_hidden_API_index")
-		java.CheckHiddenAPIRuleInputs(t, expectedInputs, indexRule)
+		var rule android.TestingBuildParams
+
+		rule = platformBootclasspath.Output("hiddenapi-monolithic/index-from-classes.csv")
+		java.CheckHiddenAPIRuleInputs(t, "intermediate index", expectedIntermediateInputs, rule)
+	}
+
+	fragment := java.ApexVariantReference{
+		Apex:   proptools.StringPtr("myapex"),
+		Module: proptools.StringPtr("my-bootclasspath-fragment"),
 	}
 
 	t.Run("prebuilt only", func(t *testing.T) {
@@ -4435,7 +4442,13 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo", "libbar"],
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			contents: ["libfoo", "libbar"],
+			apex_available: ["myapex"],
 		}
 
 		java_import {
@@ -4450,18 +4463,19 @@
 				jars: ["libbar.jar"],
 			},
 			apex_available: ["myapex"],
+			shared_library: false,
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
-.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
-`)
+			out/soong/.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
+			out/soong/.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
+		`)
 	})
 
 	t.Run("apex_set only", func(t *testing.T) {
@@ -4469,7 +4483,13 @@
 		apex_set {
 			name: "myapex",
 			set: "myapex.apks",
-			exported_java_libs: ["libfoo", "libbar"],
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			contents: ["libfoo", "libbar"],
+			apex_available: ["myapex"],
 		}
 
 		java_import {
@@ -4484,18 +4504,19 @@
 				jars: ["libbar.jar"],
 			},
 			apex_available: ["myapex"],
+			shared_library: false,
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
-.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
-`)
+			out/soong/.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
+			out/soong/.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
+		`)
 	})
 
 	t.Run("prebuilt with source library preferred", func(t *testing.T) {
@@ -4510,7 +4531,13 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo", "libbar"],
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			contents: ["libfoo", "libbar"],
+			apex_available: ["myapex"],
 		}
 
 		java_import {
@@ -4531,6 +4558,7 @@
 				jars: ["libbar.jar"],
 			},
 			apex_available: ["myapex"],
+			shared_library: false,
 		}
 
 		java_sdk_library {
@@ -4546,7 +4574,7 @@
 		// 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, "module libfoo does not provide a dex boot jar", preparer)
+		testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer, fragment)
 	})
 
 	t.Run("prebuilt library preferred with source", func(t *testing.T) {
@@ -4561,7 +4589,13 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo", "libbar"],
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			contents: ["libfoo", "libbar"],
+			apex_available: ["myapex"],
 		}
 
 		java_import {
@@ -4584,6 +4618,7 @@
 				jars: ["libbar.jar"],
 			},
 			apex_available: ["myapex"],
+			shared_library: false,
 		}
 
 		java_sdk_library {
@@ -4594,15 +4629,15 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
-.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
-`)
+			out/soong/.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
+			out/soong/.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
+		`)
 	})
 
 	t.Run("prebuilt with source apex preferred", func(t *testing.T) {
@@ -4630,7 +4665,13 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo", "libbar"],
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			contents: ["libfoo", "libbar"],
+			apex_available: ["myapex"],
 		}
 
 		java_import {
@@ -4651,6 +4692,7 @@
 				jars: ["libbar.jar"],
 			},
 			apex_available: ["myapex"],
+			shared_library: false,
 		}
 
 		java_sdk_library {
@@ -4661,15 +4703,15 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/libbar/android_common_myapex/javac/libbar.jar
-.intermediates/libfoo/android_common_apex10000/javac/libfoo.jar
-`)
+			out/soong/.intermediates/libbar/android_common_myapex/javac/libbar.jar
+			out/soong/.intermediates/libfoo/android_common_apex10000/javac/libfoo.jar
+		`)
 	})
 
 	t.Run("prebuilt preferred with source apex disabled", func(t *testing.T) {
@@ -4697,7 +4739,13 @@
 					src: "myapex-arm.apex",
 				},
 			},
-			exported_java_libs: ["libfoo", "libbar"],
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			contents: ["libfoo", "libbar"],
+			apex_available: ["myapex"],
 		}
 
 		java_import {
@@ -4720,6 +4768,7 @@
 				jars: ["libbar.jar"],
 			},
 			apex_available: ["myapex"],
+			shared_library: false,
 		}
 
 		java_sdk_library {
@@ -4730,15 +4779,15 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
-.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
-`)
+			out/soong/.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
+			out/soong/.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
+		`)
 	})
 }
 
@@ -6299,13 +6348,13 @@
 	android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings())
 
 	// Ditto for the apex.
-	m = ctx.ModuleForTests("myapex", "android_common")
-	copiedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
+	m = ctx.ModuleForTests("myapex", "android_common_myapex")
+	copiedApex := m.Output("out/soong/.intermediates/myapex/android_common_myapex/foo_v2.apex")
 
 	android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
 }
 
-func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer) {
+func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) {
 	t.Helper()
 
 	bp := `
@@ -6324,6 +6373,15 @@
 			apex_available: [
 				"some-non-updatable-apex",
 			],
+			compile_dex: true,
+		}
+
+		bootclasspath_fragment {
+			name: "some-non-updatable-fragment",
+			contents: ["some-non-updatable-apex-lib"],
+			apex_available: [
+				"some-non-updatable-apex",
+			],
 		}
 
 		java_library {
@@ -6341,6 +6399,7 @@
 				"com.android.art.debug",
 			],
 			hostdex: true,
+			compile_dex: true,
 		}
 
 		apex {
@@ -6354,7 +6413,7 @@
 		apex {
 			name: "some-non-updatable-apex",
 			key: "some-non-updatable-apex.key",
-			java_libs: ["some-non-updatable-apex-lib"],
+			bootclasspath_fragments: ["some-non-updatable-fragment"],
 			updatable: false,
 		}
 
@@ -6369,7 +6428,7 @@
 		apex {
 			name: "com.android.art.debug",
 			key: "com.android.art.debug.key",
-			java_libs: ["some-art-lib"],
+			bootclasspath_fragments: ["art-bootclasspath-fragment"],
 			updatable: true,
 			min_sdk_version: "current",
 		}
@@ -6402,10 +6461,10 @@
 		}
 	`
 
-	testDexpreoptWithApexes(t, bp, errmsg, preparer)
+	testDexpreoptWithApexes(t, bp, errmsg, preparer, fragments...)
 }
 
-func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.FixturePreparer) *android.TestContext {
+func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.FixturePreparer, fragments ...java.ApexVariantReference) *android.TestContext {
 	t.Helper()
 
 	fs := android.MockFS{
@@ -6433,11 +6492,22 @@
 		PrepareForTestWithApexBuildComponents,
 		preparer,
 		fs.AddToFixture(),
-		android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
-			platform_bootclasspath {
-				name: "platform-bootclasspath",
+		android.FixtureModifyMockFS(func(fs android.MockFS) {
+			if _, ok := fs["frameworks/base/boot/Android.bp"]; !ok {
+				insert := ""
+				for _, fragment := range fragments {
+					insert += fmt.Sprintf("{apex: %q, module: %q},\n", *fragment.Apex, *fragment.Module)
+				}
+				fs["frameworks/base/boot/Android.bp"] = []byte(fmt.Sprintf(`
+					platform_bootclasspath {
+						name: "platform-bootclasspath",
+						fragments: [
+  						%s
+						],
+					}
+				`, insert))
 			}
-		`),
+		}),
 	).
 		ExtendWithErrorHandler(errorHandler).
 		RunTestWithBp(t, bp)
@@ -6504,7 +6574,11 @@
 
 	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
 		preparer := java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib")
-		testNoUpdatableJarsInBootImage(t, "", preparer)
+		fragment := java.ApexVariantReference{
+			Apex:   proptools.StringPtr("com.android.art.debug"),
+			Module: proptools.StringPtr("art-bootclasspath-fragment"),
+		}
+		testNoUpdatableJarsInBootImage(t, "", preparer, fragment)
 	})
 
 	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
@@ -6536,7 +6610,11 @@
 
 	t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
 		preparer := java.FixtureConfigureBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
-		testNoUpdatableJarsInBootImage(t, "", preparer)
+		fragment := java.ApexVariantReference{
+			Apex:   proptools.StringPtr("some-non-updatable-apex"),
+			Module: proptools.StringPtr("some-non-updatable-fragment"),
+		}
+		testNoUpdatableJarsInBootImage(t, "", preparer, fragment)
 	})
 
 	t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
@@ -6567,6 +6645,11 @@
 func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
 	preparer := java.FixtureConfigureBootJars("myapex:libfoo")
 	t.Run("prebuilt no source", func(t *testing.T) {
+		fragment := java.ApexVariantReference{
+			Apex:   proptools.StringPtr("myapex"),
+			Module: proptools.StringPtr("my-bootclasspath-fragment"),
+		}
+
 		testDexpreoptWithApexes(t, `
 			prebuilt_apex {
 				name: "myapex" ,
@@ -6578,36 +6661,21 @@
 						src: "myapex-arm.apex",
 					},
 				},
-			exported_java_libs: ["libfoo"],
-		}
+				exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+			}
 
-		java_import {
-			name: "libfoo",
-			jars: ["libfoo.jar"],
-		}
-`, "", preparer)
-	})
+			prebuilt_bootclasspath_fragment {
+				name: "my-bootclasspath-fragment",
+				contents: ["libfoo"],
+				apex_available: ["myapex"],
+			}
 
-	t.Run("prebuilt no source", func(t *testing.T) {
-		testDexpreoptWithApexes(t, `
-			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"],
-		}
-`, "", preparer)
+			java_import {
+				name: "libfoo",
+				jars: ["libfoo.jar"],
+				apex_available: ["myapex"],
+			}
+		`, "", preparer, fragment)
 	})
 }
 
@@ -6960,7 +7028,7 @@
 		t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
 	}
 
-	m = ctx.ModuleForTests("myapex", "android_common")
+	m = ctx.ModuleForTests("myapex", "android_common_myapex")
 	a := m.Module().(*ApexSet)
 	expectedOverrides := []string{"foo"}
 	actualOverrides := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 7aecff6..66bc9e0 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"sort"
 	"strings"
 	"testing"
 
@@ -82,6 +83,7 @@
 				"com.android.art",
 			],
 			srcs: ["b.java"],
+			compile_dex: true,
 		}
 
 		java_library {
@@ -90,6 +92,7 @@
 				"com.android.art",
 			],
 			srcs: ["b.java"],
+			compile_dex: true,
 		}
 
 		bootclasspath_fragment {
@@ -318,6 +321,7 @@
 			apex_available: [
 				"com.android.art",
 			],
+			compile_dex: true,
 		}
 
 		java_import {
@@ -326,6 +330,7 @@
 			apex_available: [
 				"com.android.art",
 			],
+			compile_dex: true,
 		}
 	`),
 	)
@@ -355,6 +360,19 @@
 
 	addPrebuilt := func(prefer bool, contents ...string) android.FixturePreparer {
 		text := fmt.Sprintf(`
+			prebuilt_apex {
+				name: "com.android.art",
+				arch: {
+					arm64: {
+						src: "com.android.art-arm64.apex",
+					},
+					arm: {
+						src: "com.android.art-arm.apex",
+					},
+				},
+				exported_bootclasspath_fragments: ["mybootclasspathfragment"],
+			}
+
 			prebuilt_bootclasspath_fragment {
 				name: "mybootclasspathfragment",
 				image_name: "art",
@@ -368,7 +386,47 @@
 		return android.FixtureAddTextFile("prebuilts/module_sdk/art/Android.bp", text)
 	}
 
-	t.Run("boot image files", func(t *testing.T) {
+	t.Run("boot image files from source", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			commonPreparer,
+
+			// Configure some libraries in the art bootclasspath_fragment that match the source
+			// bootclasspath_fragment's contents property.
+			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+			addSource("foo", "bar"),
+		).RunTest(t)
+
+		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"etc/classpaths/bootclasspath.pb",
+			"javalib/arm/boot.art",
+			"javalib/arm/boot.oat",
+			"javalib/arm/boot.vdex",
+			"javalib/arm/boot-bar.art",
+			"javalib/arm/boot-bar.oat",
+			"javalib/arm/boot-bar.vdex",
+			"javalib/arm64/boot.art",
+			"javalib/arm64/boot.oat",
+			"javalib/arm64/boot.vdex",
+			"javalib/arm64/boot-bar.art",
+			"javalib/arm64/boot-bar.oat",
+			"javalib/arm64/boot-bar.vdex",
+			"javalib/bar.jar",
+			"javalib/foo.jar",
+		})
+
+		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			`bar`,
+			`com.android.art.key`,
+			`mybootclasspathfragment`,
+		})
+
+		// Make sure that the source bootclasspath_fragment copies its dex files to the predefined
+		// locations for the art image.
+		module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
+	})
+
+	t.Run("boot image files with preferred prebuilt", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -403,7 +461,13 @@
 			`bar`,
 			`com.android.art.key`,
 			`mybootclasspathfragment`,
+			`prebuilt_com.android.art`,
 		})
+
+		// Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined
+		// locations for the art image.
+		module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art")
+		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
 	t.Run("source with inconsistency between config and contents", func(t *testing.T) {
@@ -489,7 +553,7 @@
 					src: "com.android.art-arm.apex",
 				},
 			},
-			exported_java_libs: ["foo", "bar"],
+			exported_bootclasspath_fragments: ["mybootclasspathfragment"],
 		}
 
 		java_import {
@@ -519,17 +583,42 @@
 		}
 	`)
 
-	java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common", []string{
+	java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
 		`com.android.art.apex.selector`,
-		`prebuilt_bar`,
-		`prebuilt_foo`,
+		`prebuilt_mybootclasspathfragment`,
 	})
 
-	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common", []string{
+	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{
+		`com.android.art.deapexer`,
 		`dex2oatd`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
 	})
+
+	module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art")
+	checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
+}
+
+// checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the
+// predefined locations of boot dex jars used as inputs for the ART boot image.
+func checkCopiesToPredefinedLocationForArt(t *testing.T, config android.Config, module android.TestingModule, modules ...string) {
+	t.Helper()
+	bootJarLocations := []string{}
+	for _, output := range module.AllOutputs() {
+		output = android.StringRelativeToTop(config, output)
+		if strings.HasPrefix(output, "out/soong/test_device/dex_artjars_input/") {
+			bootJarLocations = append(bootJarLocations, output)
+		}
+	}
+
+	sort.Strings(bootJarLocations)
+	expected := []string{}
+	for _, m := range modules {
+		expected = append(expected, fmt.Sprintf("out/soong/test_device/dex_artjars_input/%s.jar", m))
+	}
+	sort.Strings(expected)
+
+	android.AssertArrayString(t, "copies to predefined locations for art", expected, bootJarLocations)
 }
 
 func TestBootclasspathFragmentContentsNoName(t *testing.T) {
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
new file mode 100644
index 0000000..0193127
--- /dev/null
+++ b/apex/classpath_element_test.go
@@ -0,0 +1,317 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+	"reflect"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
+	"github.com/google/blueprint"
+)
+
+// Contains tests for java.CreateClasspathElements logic from java/classpath_element.go that
+// requires apexes.
+
+// testClasspathElementContext is a ClasspathElementContext suitable for use in tests.
+type testClasspathElementContext struct {
+	testContext *android.TestContext
+	module      android.Module
+	errs        []error
+}
+
+func (t *testClasspathElementContext) OtherModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool {
+	return t.testContext.ModuleHasProvider(module, provider)
+}
+
+func (t *testClasspathElementContext) OtherModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{} {
+	return t.testContext.ModuleProvider(module, provider)
+}
+
+func (t *testClasspathElementContext) ModuleErrorf(fmt string, args ...interface{}) {
+	t.errs = append(t.errs, t.testContext.ModuleErrorf(t.module, fmt, args...))
+}
+
+var _ java.ClasspathElementContext = (*testClasspathElementContext)(nil)
+
+func TestCreateClasspathElements(t *testing.T) {
+	preparer := android.GroupFixturePreparers(
+		prepareForTestWithPlatformBootclasspath,
+		prepareForTestWithArtApex,
+		prepareForTestWithMyapex,
+		// For otherapex.
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/otherapex-file_contexts": nil,
+		}),
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("foo", "othersdklibrary"),
+		android.FixtureWithRootAndroidBp(`
+		apex {
+			name: "com.android.art",
+			key: "com.android.art.key",
+ 			bootclasspath_fragments: [
+				"art-bootclasspath-fragment",
+			],
+			java_libs: [
+				"othersdklibrary",
+			],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "com.android.art.key",
+			public_key: "com.android.art.avbpubkey",
+			private_key: "com.android.art.pem",
+		}
+
+		bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			apex_available: [
+				"com.android.art",
+			],
+			contents: [
+				"baz",
+				"quuz",
+			],
+		}
+
+		java_library {
+			name: "baz",
+			apex_available: [
+				"com.android.art",
+			],
+			srcs: ["b.java"],
+			installable: true,
+		}
+
+		java_library {
+			name: "quuz",
+			apex_available: [
+				"com.android.art",
+			],
+			srcs: ["b.java"],
+			installable: true,
+		}
+
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+ 			bootclasspath_fragments: [
+				"mybootclasspath-fragment",
+			],
+			java_libs: [
+				"othersdklibrary",
+			],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		bootclasspath_fragment {
+			name: "mybootclasspath-fragment",
+			apex_available: [
+				"myapex",
+			],
+			contents: [
+				"bar",
+			],
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: ["myapex"],
+			permitted_packages: ["bar"],
+		}
+
+		java_sdk_library {
+			name: "foo",
+			srcs: ["b.java"],
+		}
+
+		java_sdk_library {
+			name: "othersdklibrary",
+			srcs: ["b.java"],
+			shared_library: false,
+			apex_available: [
+				"com.android.art",
+				"myapex",
+			],
+		}
+
+		bootclasspath_fragment {
+			name: "non-apex-fragment",
+			contents: ["othersdklibrary"],
+		}
+
+		apex {
+			name: "otherapex",
+			key: "otherapex.key",
+			java_libs: [
+				"otherapexlibrary",
+			],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "otherapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "otherapexlibrary",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: ["otherapex"],
+			permitted_packages: ["otherapexlibrary"],
+		}
+
+		platform_bootclasspath {
+			name: "myplatform-bootclasspath",
+
+			fragments: [
+				{
+					apex: "com.android.art",
+					module: "art-bootclasspath-fragment",
+				},
+			],
+		}
+	`),
+	)
+
+	result := preparer.RunTest(t)
+
+	artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000")
+	artBaz := result.Module("baz", "android_common_apex10000")
+	artQuuz := result.Module("quuz", "android_common_apex10000")
+
+	myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000")
+	myBar := result.Module("bar", "android_common_apex10000")
+
+	nonApexFragment := result.Module("non-apex-fragment", "android_common")
+	other := result.Module("othersdklibrary", "android_common_apex10000")
+
+	otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000")
+
+	platformFoo := result.Module("quuz", "android_common")
+
+	bootclasspath := result.Module("myplatform-bootclasspath", "android_common")
+
+	// Use a custom assertion method instead of AssertDeepEquals as the latter formats the output
+	// using %#v which results in meaningless output as ClasspathElements are pointers.
+	assertElementsEquals := func(t *testing.T, message string, expected, actual java.ClasspathElements) {
+		if !reflect.DeepEqual(expected, actual) {
+			t.Errorf("%s: expected:\n  %s\n got:\n  %s", message, expected, actual)
+		}
+	}
+
+	expectFragmentElement := func(module android.Module, contents ...android.Module) java.ClasspathElement {
+		return &java.ClasspathFragmentElement{module, contents}
+	}
+	expectLibraryElement := func(module android.Module) java.ClasspathElement {
+		return &java.ClasspathLibraryElement{module}
+	}
+
+	newCtx := func() *testClasspathElementContext {
+		return &testClasspathElementContext{testContext: result.TestContext, module: bootclasspath}
+	}
+
+	// Verify that CreateClasspathElements works when given valid input.
+	t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) {
+		ctx := newCtx()
+		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment})
+		expectedElements := java.ClasspathElements{
+			expectFragmentElement(artFragment, artBaz, artQuuz),
+			expectFragmentElement(myFragment, myBar),
+			expectLibraryElement(platformFoo),
+		}
+		assertElementsEquals(t, "elements", expectedElements, elements)
+	})
+
+	// Verify that CreateClasspathElements detects when a fragment does not have an associated apex.
+	t.Run("non apex fragment", func(t *testing.T) {
+		ctx := newCtx()
+		elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{nonApexFragment})
+		android.FailIfNoMatchingErrors(t, "fragment non-apex-fragment{.*} is not part of an apex", ctx.errs)
+		expectedElements := java.ClasspathElements{}
+		assertElementsEquals(t, "elements", expectedElements, elements)
+	})
+
+	// Verify that CreateClasspathElements detects when an apex has multiple fragments.
+	t.Run("multiple fragments for same apex", func(t *testing.T) {
+		ctx := newCtx()
+		elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment})
+		android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs)
+		expectedElements := java.ClasspathElements{}
+		assertElementsEquals(t, "elements", expectedElements, elements)
+	})
+
+	// Verify that CreateClasspathElements detects when a library is in multiple fragments.
+	t.Run("library from multiple fragments", func(t *testing.T) {
+		ctx := newCtx()
+		elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment})
+		android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs)
+		expectedElements := java.ClasspathElements{}
+		assertElementsEquals(t, "elements", expectedElements, elements)
+	})
+
+	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
+	// are separated by a library from another fragment.
+	t.Run("discontiguous separated by fragment", func(t *testing.T) {
+		ctx := newCtx()
+		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment})
+		expectedElements := java.ClasspathElements{
+			expectFragmentElement(artFragment, artBaz, artQuuz),
+			expectFragmentElement(myFragment, myBar),
+			expectLibraryElement(platformFoo),
+		}
+		assertElementsEquals(t, "elements", expectedElements, elements)
+		android.FailIfNoMatchingErrors(t, "libraries from the same fragment must be contiguous, however baz{.*} and quuz{os:android,arch:common,apex:apex10000} from fragment art-bootclasspath-fragment{.*} are separated by libraries from fragment mybootclasspath-fragment{.*} like bar{.*}", ctx.errs)
+	})
+
+	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
+	// are separated by a standalone library.
+	t.Run("discontiguous separated by library", func(t *testing.T) {
+		ctx := newCtx()
+		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment})
+		expectedElements := java.ClasspathElements{
+			expectFragmentElement(artFragment, artBaz, artQuuz),
+			expectLibraryElement(platformFoo),
+			expectFragmentElement(myFragment, myBar),
+		}
+		assertElementsEquals(t, "elements", expectedElements, elements)
+		android.FailIfNoMatchingErrors(t, "libraries from the same fragment must be contiguous, however baz{.*} and quuz{os:android,arch:common,apex:apex10000} from fragment art-bootclasspath-fragment{.*} are separated by library quuz{.*}", ctx.errs)
+	})
+
+	// Verify that CreateClasspathElements detects when there a library on the classpath that
+	// indicates it is from an apex the supplied fragments list does not contain a fragment for that
+	// apex.
+	t.Run("no fragment for apex", func(t *testing.T) {
+		ctx := newCtx()
+		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment})
+		expectedElements := java.ClasspathElements{
+			expectFragmentElement(artFragment, artBaz),
+		}
+		assertElementsEquals(t, "elements", expectedElements, elements)
+		android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs)
+	})
+}
diff --git a/apex/deapexer.go b/apex/deapexer.go
index c7cdbfa..c70da15 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -40,16 +40,6 @@
 // This is intentionally not registered by name as it is not intended to be used from within an
 // `Android.bp` file.
 
-// DeapexerExportedFile defines the properties needed to expose a file from the deapexer module.
-type DeapexerExportedFile struct {
-	// The tag parameter which must be passed to android.OutputFileProducer OutputFiles(tag) method
-	// to retrieve the path to the unpacked file.
-	Tag string
-
-	// The path within the APEX that needs to be exported.
-	Path string `android:"path"`
-}
-
 // DeapexerProperties specifies the properties supported by the deapexer module.
 //
 // As these are never intended to be supplied in a .bp file they use a different naming convention
@@ -62,7 +52,9 @@
 	CommonModules []string
 
 	// List of files exported from the .apex file by this module
-	ExportedFiles []DeapexerExportedFile
+	//
+	// Each entry is a path from the apex root, e.g. javalib/core-libart.jar.
+	ExportedFiles []string
 }
 
 type SelectedApexProperties struct {
@@ -107,27 +99,23 @@
 
 	exports := make(map[string]android.Path)
 
-	// Create mappings from name+tag to all the required exported paths.
-	for _, e := range p.properties.ExportedFiles {
-		tag := e.Tag
-		path := e.Path
-
+	// Create mappings from apex relative path to the extracted file's path.
+	exportedPaths := make(android.Paths, 0, len(exports))
+	for _, path := range p.properties.ExportedFiles {
 		// Populate the exports that this makes available.
-		exports[tag] = deapexerOutput.Join(ctx, path)
+		extractedPath := deapexerOutput.Join(ctx, path)
+		exports[path] = extractedPath
+		exportedPaths = append(exportedPaths, extractedPath)
 	}
 
 	// If the prebuilt_apex exports any files then create a build rule that unpacks the apex using
 	// deapexer and verifies that all the required files were created. Also, make the mapping from
-	// name+tag to path available for other modules.
+	// apex relative path to extracted file path available for other modules.
 	if len(exports) > 0 {
 		// Make the information available for other modules.
 		ctx.SetProvider(android.DeapexerProvider, android.NewDeapexerInfo(exports))
 
 		// Create a sorted list of the files that this exports.
-		exportedPaths := make(android.Paths, 0, len(exports))
-		for _, p := range exports {
-			exportedPaths = append(exportedPaths, p)
-		}
 		exportedPaths = android.SortedUniquePaths(exportedPaths)
 
 		// The apex needs to export some files so create a ninja rule to unpack the apex and check that
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 7297926..bd4a9d5 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -234,12 +234,18 @@
 		apex {
 			name: "myapex",
 			key: "myapex.key",
-			java_libs: [
-				"bar",
+			bootclasspath_fragments: [
+				"my-bootclasspath-fragment",
 			],
 			updatable: false,
 		}
 
+		bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			contents: ["bar"],
+			apex_available: ["myapex"],
+		}
+
 		apex_key {
 			name: "myapex.key",
 			public_key: "testkey.avbpubkey",
@@ -267,6 +273,10 @@
 					apex: "com.android.art",
 					module: "art-bootclasspath-fragment",
 				},
+				{
+					apex: "myapex",
+					module: "my-bootclasspath-fragment",
+				},
 			],
 		}
 `,
@@ -283,7 +293,8 @@
 	})
 
 	java.CheckPlatformBootclasspathFragments(t, result, "myplatform-bootclasspath", []string{
-		`com.android.art:art-bootclasspath-fragment`,
+		"com.android.art:art-bootclasspath-fragment",
+		"myapex:my-bootclasspath-fragment",
 	})
 
 	// Make sure that the myplatform-bootclasspath has the correct dependencies.
@@ -307,6 +318,7 @@
 
 		// The fragments.
 		`com.android.art:art-bootclasspath-fragment`,
+		`myapex:my-bootclasspath-fragment`,
 	})
 }
 
@@ -410,6 +422,12 @@
 
 		platform_bootclasspath {
 			name: "myplatform-bootclasspath",
+			fragments: [
+				{
+					apex: "myapex",
+					module:"mybootclasspath-fragment",
+				},
+			],
 		}
 `,
 	)
@@ -431,7 +449,7 @@
 		"platform:legacy.core.platform.api.stubs",
 
 		// Needed for generating the boot image.
-		`platform:dex2oatd`,
+		"platform:dex2oatd",
 
 		// The platform_bootclasspath intentionally adds dependencies on both source and prebuilt
 		// modules when available as it does not know which one will be preferred.
@@ -442,6 +460,9 @@
 
 		// Only a source module exists.
 		"myapex:bar",
+
+		// The fragments.
+		"myapex:mybootclasspath-fragment",
 	})
 }
 
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index bf8c073..9504b07 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -16,7 +16,7 @@
 
 import (
 	"fmt"
-	"path/filepath"
+	"io"
 	"strconv"
 	"strings"
 
@@ -56,6 +56,10 @@
 	installFilename string
 	outputApex      android.WritablePath
 
+	// A list of apexFile objects created in prebuiltCommon.initApexFilesForAndroidMk which are used
+	// to create make modules in prebuiltCommon.AndroidMkEntries.
+	apexFilesForAndroidMk []apexFile
+
 	// list of commands to create symlinks for backward compatibility.
 	// these commands will be attached as LOCAL_POST_INSTALL_CMD
 	compatSymlinks []string
@@ -154,8 +158,37 @@
 	return proptools.BoolDefault(p.prebuiltCommonProperties.Installable, true)
 }
 
+// initApexFilesForAndroidMk initializes the prebuiltCommon.apexFilesForAndroidMk field from the
+// modules that this depends upon.
+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)
+
+		name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
+		if java.IsBootclasspathFragmentContentDepTag(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() android.Path }).DexJarBuildPath()
+			if path != nil {
+				p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, apexFile{
+					module:              child,
+					moduleDir:           ctx.OtherModuleDir(child),
+					androidMkModuleName: name,
+					builtFile:           path,
+					class:               javaSharedLib,
+				})
+			}
+		} else if tag == exportedBootclasspathFragmentTag {
+			// Visit the children of the bootclasspath_fragment.
+			return true
+		}
+
+		return false
+	})
+}
+
 func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{
+	entriesList := []android.AndroidMkEntries{
 		{
 			Class:         "ETC",
 			OutputFile:    android.OptionalPathForPath(p.outputApex),
@@ -176,6 +209,61 @@
 			},
 		},
 	}
+
+	// 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.
+	apexName := p.BaseModuleName()
+	for _, fi := range p.apexFilesForAndroidMk {
+		entries := p.createEntriesForApexFile(fi, apexName)
+		entriesList = append(entriesList, entries)
+	}
+
+	return entriesList
+}
+
+// createEntriesForApexFile creates an AndroidMkEntries for the supplied apexFile
+func (p *prebuiltCommon) createEntriesForApexFile(fi apexFile, apexName string) android.AndroidMkEntries {
+	moduleName := fi.androidMkModuleName + "." + apexName
+	entries := android.AndroidMkEntries{
+		Class:        fi.class.nameInMake(),
+		OverrideName: moduleName,
+		OutputFile:   android.OptionalPathForPath(fi.builtFile),
+		Include:      "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+
+				// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar  Therefore
+				// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
+				// we will have foo.jar.jar
+				entries.SetString("LOCAL_MODULE_STEM", strings.TrimSuffix(fi.stem(), ".jar"))
+				var classesJar android.Path
+				var headerJar android.Path
+				if javaModule, ok := fi.module.(java.ApexDependency); ok {
+					classesJar = javaModule.ImplementationAndResourcesJars()[0]
+					headerJar = javaModule.HeaderJars()[0]
+				} else {
+					classesJar = fi.builtFile
+					headerJar = fi.builtFile
+				}
+				entries.SetString("LOCAL_SOONG_CLASSES_JAR", classesJar.String())
+				entries.SetString("LOCAL_SOONG_HEADER_JAR", headerJar.String())
+				entries.SetString("LOCAL_SOONG_DEX_JAR", fi.builtFile.String())
+				entries.SetString("LOCAL_DEX_PREOPT", "false")
+			},
+		},
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string) {
+				// m <module_name> will build <module_name>.<apex_name> as well.
+				if fi.androidMkModuleName != moduleName {
+					fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName)
+					fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
+				}
+			},
+		},
+	}
+	return entries
 }
 
 // prebuiltApexModuleCreator defines the methods that need to be implemented by prebuilt_apex and
@@ -464,21 +552,25 @@
 	}
 
 	// Compute the deapexer properties from the transitive dependencies of this module.
-	javaModules := []string{}
-	exportedFiles := map[string]string{}
+	commonModules := []string{}
+	exportedFiles := []string{}
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		tag := ctx.OtherModuleDependencyTag(child)
 
+		// If the child is not in the same apex as the parent then ignore it and all its children.
+		if !android.IsDepInSameApex(ctx, parent, child) {
+			return false
+		}
+
 		name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
-		if java.IsBootclasspathFragmentContentDepTag(tag) || tag == exportedJavaLibTag {
-			javaModules = append(javaModules, name)
+		if _, ok := tag.(android.RequiresFilesFromPrebuiltApexTag); ok {
+			commonModules = append(commonModules, name)
 
-			// Add the dex implementation jar to the set of exported files. The path here must match the
-			// path of the file in the APEX created by apexFileForJavaModule(...).
-			exportedFiles[name+"{.dexjar}"] = filepath.Join("javalib", name+".jar")
+			requiredFiles := child.(android.RequiredFilesFromPrebuiltApex).RequiredFilesFromPrebuiltApex(ctx)
+			exportedFiles = append(exportedFiles, requiredFiles...)
 
-		} else if tag == exportedBootclasspathFragmentTag {
-			// Only visit the children of the bootclasspath_fragment for now.
+			// Visit the dependencies of this module just in case they also require files from the
+			// prebuilt apex.
 			return true
 		}
 
@@ -487,18 +579,13 @@
 
 	// Create properties for deapexer module.
 	deapexerProperties := &DeapexerProperties{
-		// Remove any duplicates from the java modules lists as a module may be included via a direct
+		// Remove any duplicates from the common modules lists as a module may be included via a direct
 		// dependency as well as transitive ones.
-		CommonModules: android.SortedUniqueStrings(javaModules),
+		CommonModules: android.SortedUniqueStrings(commonModules),
 	}
 
 	// Populate the exported files property in a fixed order.
-	for _, tag := range android.SortedStringKeys(exportedFiles) {
-		deapexerProperties.ExportedFiles = append(deapexerProperties.ExportedFiles, DeapexerExportedFile{
-			Tag:  tag,
-			Path: exportedFiles[tag],
-		})
-	}
+	deapexerProperties.ExportedFiles = android.SortedUniqueStrings(exportedFiles)
 
 	props := struct {
 		Name          *string
@@ -555,6 +642,10 @@
 // incorrectly.
 func (t exportedDependencyTag) ExcludeFromVisibilityEnforcement() {}
 
+func (t exportedDependencyTag) RequiresFilesFromPrebuiltApex() {}
+
+var _ android.RequiresFilesFromPrebuiltApexTag = exportedDependencyTag{}
+
 var (
 	exportedJavaLibTag               = exportedDependencyTag{name: "exported_java_libs"}
 	exportedBootclasspathFragmentTag = exportedDependencyTag{name: "exported_bootclasspath_fragments"}
@@ -634,6 +725,9 @@
 		return
 	}
 
+	// Save the files that need to be made available to Make.
+	p.initApexFilesForAndroidMk(ctx)
+
 	if p.installable() {
 		ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
 	}
@@ -833,6 +927,9 @@
 		return
 	}
 
+	// Save the files that need to be made available to Make.
+	a.initApexFilesForAndroidMk(ctx)
+
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	if a.installable() {
 		ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 8afc82b..bf40a6b 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -41,6 +41,8 @@
 )
 
 func TestCcLibraryBp2Build(t *testing.T) {
+	// b/191166471 disabled in sc-dev
+	t.Skip()
 	testCases := []struct {
 		description                        string
 		moduleTypeUnderTest                string
diff --git a/cc/linker.go b/cc/linker.go
index 5bd21ed..449b9ad 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -122,7 +122,7 @@
 
 			// version script for vendor or product variant
 			Version_script *string `android:"arch_variant"`
-		}
+		} `android:"arch_variant"`
 		Recovery struct {
 			// list of shared libs that only should be used to build the recovery
 			// variant of the C/C++ module.
@@ -182,7 +182,7 @@
 			// variant of the C/C++ module.
 			Exclude_static_libs []string
 		}
-	}
+	} `android:"arch_variant"`
 
 	// make android::build:GetBuildNumber() available containing the build ID.
 	Use_version_lib *bool `android:"arch_variant"`
diff --git a/cc/pgo.go b/cc/pgo.go
index 95c9c2e..e78549e 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -56,7 +56,7 @@
 type PgoProperties struct {
 	Pgo struct {
 		Instrumentation    *bool
-		Sampling           *bool
+		Sampling           *bool   `android:"arch_variant"`
 		Profile_file       *string `android:"arch_variant"`
 		Benchmarks         []string
 		Enable_profile_use *bool `android:"arch_variant"`
diff --git a/cc/sanitize.go b/cc/sanitize.go
index f486ee4..941a955 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -485,6 +485,11 @@
 	if Bool(s.Hwaddress) {
 		s.Address = nil
 		s.Thread = nil
+		// Disable ubsan diagnosic as a workaround for a compiler bug.
+		// TODO(b/191808836): re-enable.
+		s.Diag.Undefined = nil
+		s.Diag.Integer_overflow = nil
+		s.Diag.Misc_undefined = nil
 	}
 
 	// TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is
diff --git a/java/Android.bp b/java/Android.bp
index 680f3a1..5952602 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -33,6 +33,7 @@
         "bootclasspath.go",
         "bootclasspath_fragment.go",
         "builder.go",
+        "classpath_element.go",
         "classpath_fragment.go",
         "device_host_converter.go",
         "dex.go",
diff --git a/java/base.go b/java/base.go
index cf6fafa..34514ff 100644
--- a/java/base.go
+++ b/java/base.go
@@ -253,8 +253,8 @@
 	EmbeddableSdkLibraryComponent
 }
 
-func (e *embeddableInModuleAndImport) initModuleAndImport(moduleBase *android.ModuleBase) {
-	e.initSdkLibraryComponent(moduleBase)
+func (e *embeddableInModuleAndImport) initModuleAndImport(module android.Module) {
+	e.initSdkLibraryComponent(module)
 }
 
 // Module/Import's DepIsInSameApex(...) delegates to this method.
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 7abda80..86ebe36 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -18,37 +18,6 @@
 	"android/soong/android"
 )
 
-func init() {
-	android.RegisterSingletonType("boot_jars", bootJarsSingletonFactory)
-}
-
-func bootJarsSingletonFactory() android.Singleton {
-	return &bootJarsSingleton{}
-}
-
-type bootJarsSingleton struct{}
-
-func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex map[string]string, list android.ConfiguredJarList, name string) bool {
-	for i := 0; i < list.Len(); i++ {
-		module := list.Jar(i)
-		// Ignore jacocoagent it is only added when instrumenting and so has no impact on
-		// app compatibility.
-		if module == "jacocoagent" {
-			continue
-		}
-		apex := list.Apex(i)
-		if existing, ok := moduleToApex[module]; ok {
-			ctx.Errorf("Configuration property %q is invalid as it contains multiple references to module (%s) in APEXes (%s and %s)",
-				module, existing, apex)
-			return false
-		}
-
-		moduleToApex[module] = apex
-	}
-
-	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.
@@ -59,73 +28,17 @@
 	return android.IsModulePreferred(module)
 }
 
-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. 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") {
-		return
-	}
-
-	// Map from module name to the correct apex variant.
-	nameToApexVariant := make(map[string]android.Module)
-
-	// Scan all the modules looking for the module/apex variants corresponding to the
-	// boot jars.
-	ctx.VisitAllModules(func(module android.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.InApexModule(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 {
-					ctx.Errorf("found multiple variants matching %s:%s: %q and %q", apex, name, existing, module)
-				}
-				nameToApexVariant[name] = module
-			}
-		}
-	})
-
+// buildRuleForBootJarsPackageCheck generates the build rule to perform the boot jars package
+// check.
+func buildRuleForBootJarsPackageCheck(ctx android.ModuleContext, bootDexJarByModule bootDexJarByModule) {
 	timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp")
 
 	rule := android.NewRuleBuilder(pctx, ctx)
-	checkBootJars := rule.Command().BuiltTool("check_boot_jars").
+	rule.Command().BuiltTool("check_boot_jars").
 		Input(ctx.Config().HostToolPath(ctx, "dexdump")).
-		Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt"))
-
-	// If this is not an unbundled build and missing dependencies are not allowed
-	// then all the boot jars listed must have been found.
-	strict := !config.UnbundledBuild() && !config.AllowMissingDependencies()
-
-	// Iterate over the module names on the boot classpath in order
-	for _, name := range android.SortedStringKeys(moduleToApex) {
-		if apexVariant, ok := nameToApexVariant[name]; ok {
-			if dep, ok := apexVariant.(interface{ DexJarBuildPath() android.Path }); ok {
-				// Add the dex implementation jar for the module to be checked.
-				checkBootJars.Input(dep.DexJarBuildPath())
-			} else {
-				ctx.Errorf("module %q is of type %q which is not supported as a boot jar", name, ctx.ModuleType(apexVariant))
-			}
-		} else if strict {
-			ctx.Errorf("boot jars package check failed as it could not find module %q for apex %q", name, moduleToApex[name])
-		}
-	}
-
-	checkBootJars.Text("&& touch").Output(timestamp)
+		Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt")).
+		Inputs(bootDexJarByModule.bootDexJarsWithoutCoverage()).
+		Text("&& touch").Output(timestamp)
 	rule.Build("boot_jars_package_check", "check boot jar packages")
 
 	// The check-boot-jars phony target depends on the timestamp created if the check succeeds.
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index fc8f557..03faf34 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -81,6 +81,9 @@
 // they were listed in java_libs.
 func (b bootclasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {}
 
+// Contents of bootclasspath fragments require files from prebuilt apex files.
+func (b bootclasspathFragmentContentDependencyTag) RequiresFilesFromPrebuiltApex() {}
+
 // The tag used for the dependency between the bootclasspath_fragment module and its contents.
 var bootclasspathFragmentContentDepTag = bootclasspathFragmentContentDependencyTag{}
 
@@ -88,6 +91,7 @@
 var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
 var _ android.SdkMemberTypeDependencyTag = bootclasspathFragmentContentDepTag
 var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag
+var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag
 
 func IsBootclasspathFragmentContentDepTag(tag blueprint.DependencyTag) bool {
 	return tag == bootclasspathFragmentContentDepTag
@@ -137,14 +141,29 @@
 // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
 // bootclasspath fragment modules.
 type commonBootclasspathFragment interface {
-	// produceHiddenAPIAllFlagsFile produces the all-flags.csv and intermediate files.
+	// produceHiddenAPIOutput produces the all-flags.csv and intermediate files and encodes the flags
+	// into dex files.
 	//
-	// Updates the supplied hiddenAPIInfo with the paths to the generated files set.
-	produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput
+	// Returns a *HiddenAPIOutput containing the paths for the generated files. Returns nil if the
+	// module cannot contribute to hidden API processing, e.g. because it is a prebuilt module in a
+	// versioned sdk.
+	produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput
+
+	// produceBootImageFiles produces the boot image (i.e. .art, .oat and .vdex) files for each of the
+	// required android.ArchType values in the returned map.
+	//
+	// It must return nil if the boot image files cannot be produced for whatever reason.
+	produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch
 }
 
 var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
 
+// bootImageFilesByArch is a map from android.ArchType to the paths to the boot image files.
+//
+// The paths include the .art, .oat and .vdex files, one for each of the modules from which the boot
+// image is created.
+type bootImageFilesByArch map[android.ArchType]android.Paths
+
 func bootclasspathFragmentFactory() android.Module {
 	m := &BootclasspathFragmentModule{}
 	m.AddProperties(&m.properties)
@@ -230,14 +249,6 @@
 				commonApex, apex)
 		}
 	}
-
-	if len(contents) != 0 {
-		// Nothing to do.
-		return
-	}
-
-	// Store the jars in the Contents property so that they can be used to add dependencies.
-	m.properties.Contents = modules.CopyOfJars()
 }
 
 // bootclasspathImageNameContentsConsistencyCheck checks that the configuration that applies to this
@@ -290,11 +301,11 @@
 	modules android.ConfiguredJarList
 
 	// Map from arch type to the boot image files.
-	bootImageFilesByArch map[android.ArchType]android.OutputPaths
+	bootImageFilesByArch bootImageFilesByArch
 
-	// Map from the name of the context module (as returned by Name()) to the hidden API encoded dex
-	// jar path.
-	contentModuleDexJarPaths map[string]android.Path
+	// Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the
+	// hidden API encoded dex jar path.
+	contentModuleDexJarPaths bootDexJarByModule
 }
 
 func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList {
@@ -304,7 +315,7 @@
 // Get a map from ArchType to the associated boot image's contents for Android.
 //
 // Extension boot images only return their own files, not the files of the boot images they extend.
-func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() map[android.ArchType]android.OutputPaths {
+func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() bootImageFilesByArch {
 	return i.bootImageFilesByArch
 }
 
@@ -312,6 +323,8 @@
 //
 // The dex boot jar is one which has had hidden API encoding performed on it.
 func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(module android.Module) (android.Path, error) {
+	// A bootclasspath_fragment cannot use a prebuilt library so Name() will return the base name
+	// without a prebuilt_ prefix so is safe to use as the key for the contentModuleDexJarPaths.
 	name := module.Name()
 	if dexJar, ok := i.contentModuleDexJarPaths[name]; ok {
 		return dexJar, nil
@@ -400,89 +413,80 @@
 
 	fragments := gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag)
 
-	// Perform hidden API processing.
-	hiddenAPIFlagOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
-
 	// Verify that the image_name specified on a bootclasspath_fragment is valid even if this is a
 	// prebuilt which will not use the image config.
 	imageConfig := b.getImageConfig(ctx)
 
-	// A prebuilt fragment cannot contribute to the apex.
-	if !android.IsModulePrebuilt(ctx.Module()) {
-		// Provide the apex content info.
-		b.provideApexContentInfo(ctx, imageConfig, contents, hiddenAPIFlagOutput)
+	// A versioned prebuilt_bootclasspath_fragment cannot and does not need to perform hidden API
+	// processing. It cannot do it because it is not part of a prebuilt_apex and so has no access to
+	// the correct dex implementation jar. It does not need to because the platform-bootclasspath
+	// always references the latest bootclasspath_fragments.
+	if !android.IsModuleInVersionedSdk(ctx.Module()) {
+		// Perform hidden API processing.
+		hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
+
+		var bootImageFilesByArch bootImageFilesByArch
+		if imageConfig != nil {
+			// Delegate the production of the boot image files to a module type specific method.
+			common := ctx.Module().(commonBootclasspathFragment)
+			bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig, contents)
+
+			if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+				// Copy the dex jars of this fragment's content modules to their predefined locations.
+				copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
+			}
+		}
+
+		// A prebuilt fragment cannot contribute to an apex.
+		if !android.IsModulePrebuilt(ctx.Module()) {
+			// Provide the apex content info.
+			b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFilesByArch)
+		}
 	}
 }
 
+// shouldCopyBootFilesToPredefinedLocations determines whether the current module should copy boot
+// files, e.g. boot dex jars or boot image files, to the predefined location expected by the rest
+// of the build.
+//
+// This ensures that only a single module will copy its files to the image configuration.
+func shouldCopyBootFilesToPredefinedLocations(ctx android.ModuleContext, imageConfig *bootImageConfig) bool {
+	// Bootclasspath fragment modules that are for the platform do not produce boot related files.
+	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	if apexInfo.IsForPlatform() {
+		return false
+	}
+
+	// If the image configuration has no modules specified then it means that the build has been
+	// configured to build something other than a boot image, e.g. an sdk, so do not try and copy the
+	// files.
+	if imageConfig.modules.Len() == 0 {
+		return false
+	}
+
+	// Only copy files from the module that is preferred.
+	return isActiveModule(ctx.Module())
+}
+
 // provideApexContentInfo creates, initializes and stores the apex content info for use by other
 // modules.
-func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) {
+func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, hiddenAPIOutput *HiddenAPIOutput, bootImageFilesByArch bootImageFilesByArch) {
 	// Construct the apex content info from the config.
-	info := BootclasspathFragmentApexContentInfo{}
-
-	// Populate the apex content info with paths to the dex jars.
-	b.populateApexContentInfoDexJars(ctx, &info, contents, hiddenAPIFlagOutput)
+	info := BootclasspathFragmentApexContentInfo{
+		// Populate the apex content info with paths to the dex jars.
+		contentModuleDexJarPaths: hiddenAPIOutput.EncodedBootDexFilesByModule,
+	}
 
 	if imageConfig != nil {
 		info.modules = imageConfig.modules
-
-		if !SkipDexpreoptBootJars(ctx) {
-			// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
-			// GenerateSingletonBuildActions method as it cannot create it for itself.
-			dexpreopt.GetGlobalSoongConfig(ctx)
-
-			// Only generate the boot image if the configuration does not skip it.
-			if b.generateBootImageBuildActions(ctx, contents, imageConfig) {
-				// Allow the apex to access the boot image files.
-				files := map[android.ArchType]android.OutputPaths{}
-				for _, variant := range imageConfig.variants {
-					// We also generate boot images for host (for testing), but we don't need those in the apex.
-					// TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
-					if variant.target.Os == android.Android {
-						files[variant.target.Arch.ArchType] = variant.imagesDeps
-					}
-				}
-				info.bootImageFilesByArch = files
-			}
-		}
 	}
 
+	info.bootImageFilesByArch = bootImageFilesByArch
+
 	// Make the apex content info available for other modules.
 	ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info)
 }
 
-// populateApexContentInfoDexJars adds paths to the dex jars provided by this fragment to the
-// apex content info.
-func (b *BootclasspathFragmentModule) populateApexContentInfoDexJars(ctx android.ModuleContext, info *BootclasspathFragmentApexContentInfo, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) {
-
-	info.contentModuleDexJarPaths = map[string]android.Path{}
-	if hiddenAPIFlagOutput != nil {
-		// Hidden API encoding has been performed.
-		flags := hiddenAPIFlagOutput.AllFlagsPath
-		for _, m := range contents {
-			h := m.(hiddenAPIModule)
-			unencodedDex := h.bootDexJar()
-			if unencodedDex == nil {
-				// This is an error. Sometimes Soong will report the error directly, other times it will
-				// defer the error reporting to happen only when trying to use the missing file in ninja.
-				// Either way it is handled by extractBootDexJarsFromModules which must have been
-				// called before this as it generates the flags that are used to encode these files.
-				continue
-			}
-
-			outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
-			encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, flags, *h.uncompressDex(), outputDir)
-			info.contentModuleDexJarPaths[m.Name()] = encodedDex
-		}
-	} else {
-		for _, m := range contents {
-			j := m.(UsesLibraryDependency)
-			dexJar := j.DexJarBuildPath()
-			info.contentModuleDexJarPaths[m.Name()] = dexJar
-		}
-	}
-}
-
 // generateClasspathProtoBuildActions generates all required build actions for classpath.proto config
 func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) {
 	var classpathJars []classpathJar
@@ -548,12 +552,12 @@
 }
 
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
-func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIFlagOutput {
+func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput {
 
 	// Create hidden API input structure.
 	input := b.createHiddenAPIFlagInput(ctx, contents, fragments)
 
-	var output *HiddenAPIFlagOutput
+	var output *HiddenAPIOutput
 
 	// Hidden API processing is conditional as a temporary workaround as not all
 	// bootclasspath_fragments provide the appropriate information needed for hidden API processing
@@ -563,7 +567,14 @@
 	if input.canPerformHiddenAPIProcessing(ctx, b.properties) {
 		// Delegate the production of the hidden API all-flags.csv file to a module type specific method.
 		common := ctx.Module().(commonBootclasspathFragment)
-		output = common.produceHiddenAPIAllFlagsFile(ctx, contents, input)
+		output = common.produceHiddenAPIOutput(ctx, contents, input)
+	} else {
+		// As hidden API processing cannot be performed fall back to trying to retrieve the legacy
+		// encoded boot dex files, i.e. those files encoded by the individual libraries and returned
+		// from the DexJarBuildPath() method.
+		output = &HiddenAPIOutput{
+			EncodedBootDexFilesByModule: retrieveLegacyEncodedBootDexFiles(ctx, contents),
+		}
 	}
 
 	// Initialize a HiddenAPIInfo structure.
@@ -580,11 +591,9 @@
 		TransitiveStubDexJarsByKind: input.transitiveStubDexJarsByKind(),
 	}
 
-	if output != nil {
-		// The monolithic hidden API processing also needs access to all the output files produced by
-		// hidden API processing of this fragment.
-		hiddenAPIInfo.HiddenAPIFlagOutput = *output
-	}
+	// The monolithic hidden API processing also needs access to all the output files produced by
+	// hidden API processing of this fragment.
+	hiddenAPIInfo.HiddenAPIFlagOutput = (*output).HiddenAPIFlagOutput
 
 	//  Provide it for use by other modules.
 	ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo)
@@ -592,10 +601,24 @@
 	return output
 }
 
+// retrieveLegacyEncodedBootDexFiles attempts to retrieve the legacy encoded boot dex jar files.
+func retrieveLegacyEncodedBootDexFiles(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
+	// If the current bootclasspath_fragment is the active module or a source module then retrieve the
+	// encoded dex files, otherwise return an empty map.
+	//
+	// An inactive (i.e. not preferred) bootclasspath_fragment needs to retrieve the encoded dex jars
+	// as they are still needed by an apex. An inactive prebuilt_bootclasspath_fragment does not need
+	// to do so and may not yet have access to dex boot jars from a prebuilt_apex/apex_set.
+	if isActiveModule(ctx.Module()) || !android.IsModulePrebuilt(ctx.Module()) {
+		return extractEncodedDexJarsFromModules(ctx, contents)
+	} else {
+		return nil
+	}
+}
+
 // createHiddenAPIFlagInput creates a HiddenAPIFlagInput struct and initializes it with information derived
 // from the properties on this module and its dependencies.
 func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) HiddenAPIFlagInput {
-
 	// Merge the HiddenAPIInfo from all the fragment dependencies.
 	dependencyHiddenApiInfo := newHiddenAPIInfo()
 	dependencyHiddenApiInfo.mergeFromFragmentDeps(ctx, fragments)
@@ -615,12 +638,36 @@
 	return input
 }
 
-// produceHiddenAPIAllFlagsFile produces the hidden API all-flags.csv file (and supporting files)
-// for the fragment.
-func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+// produceHiddenAPIOutput produces the hidden API all-flags.csv file (and supporting files)
+// for the fragment as well as encoding the flags in the boot dex jars.
+func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
 	// Generate the rules to create the hidden API flags and update the supplied hiddenAPIInfo with the
 	// paths to the created files.
-	return hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx, contents, input)
+	return hiddenAPIRulesForBootclasspathFragment(ctx, contents, input)
+}
+
+// produceBootImageFiles builds the boot image files from the source if it is required.
+func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+	if SkipDexpreoptBootJars(ctx) {
+		return nil
+	}
+
+	// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
+	// GenerateSingletonBuildActions method as it cannot create it for itself.
+	dexpreopt.GetGlobalSoongConfig(ctx)
+
+	// Only generate the boot image if the configuration does not skip it.
+	if !b.generateBootImageBuildActions(ctx, contents, imageConfig) {
+		return nil
+	}
+
+	// Only make the files available to an apex if they were actually generated.
+	files := bootImageFilesByArch{}
+	for _, variant := range imageConfig.apexVariants() {
+		files[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
+	}
+
+	return files
 }
 
 // generateBootImageBuildActions generates ninja rules to create the boot image if required for this
@@ -644,10 +691,6 @@
 		return false
 	}
 
-	// Copy the dex jars of this fragment's content modules to their predefined locations.
-	bootDexJarByModule := extractEncodedDexJarsFromModules(ctx, contents)
-	copyBootJarsToPredefinedLocations(ctx, bootDexJarByModule, imageConfig.dexPathsByModule)
-
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)
 	buildBootImage(ctx, imageConfig, profile)
@@ -836,9 +879,8 @@
 	return module.prebuilt.Name(module.ModuleBase.Name())
 }
 
-// produceHiddenAPIAllFlagsFile returns a path to the prebuilt all-flags.csv or nil if none is
-// specified.
-func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, _ HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+// produceHiddenAPIOutput returns a path to the prebuilt all-flags.csv or nil if none is specified.
+func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
 	pathForOptionalSrc := func(src *string) android.Path {
 		if src == nil {
 			// TODO(b/179354495): Fail if this is not provided once prebuilts have been updated.
@@ -847,19 +889,106 @@
 		return android.PathForModuleSrc(ctx, *src)
 	}
 
-	output := HiddenAPIFlagOutput{
-		StubFlagsPath:       pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags),
-		AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags),
-		MetadataPath:        pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata),
-		IndexPath:           pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index),
-		AllFlagsPath:        pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags),
+	// Retrieve the dex files directly from the content modules. They in turn should retrieve the
+	// encoded dex jars from the prebuilt .apex files.
+	encodedBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, contents)
+
+	output := HiddenAPIOutput{
+		HiddenAPIFlagOutput: HiddenAPIFlagOutput{
+			StubFlagsPath:       pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags),
+			AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags),
+			MetadataPath:        pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata),
+			IndexPath:           pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index),
+			AllFlagsPath:        pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags),
+		},
+		EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
 	}
 
 	return &output
 }
 
+// produceBootImageFiles extracts the boot image files from the APEX if available.
+func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+	if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+		return nil
+	}
+
+	var deapexerModule android.Module
+	ctx.VisitDirectDeps(func(module android.Module) {
+		tag := ctx.OtherModuleDependencyTag(module)
+		// Save away the `deapexer` module on which this depends, if any.
+		if tag == android.DeapexerTag {
+			deapexerModule = module
+		}
+	})
+
+	if deapexerModule == nil {
+		// This should never happen as a variant for a prebuilt_apex is only created if the
+		// deapexer module has been configured to export the dex implementation jar for this module.
+		ctx.ModuleErrorf("internal error: module does not depend on a `deapexer` module")
+		return nil
+	}
+
+	di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+	for _, variant := range imageConfig.apexVariants() {
+		arch := variant.target.Arch.ArchType
+		for _, toPath := range variant.imagesDeps {
+			apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
+			// Get the path to the file that the deapexer extracted from the prebuilt apex file.
+			fromPath := di.PrebuiltExportPath(apexRelativePath)
+
+			// Copy the file to the predefined location.
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Cp,
+				Input:  fromPath,
+				Output: toPath,
+			})
+		}
+	}
+
+	// The returned files will be made available to APEXes that include a bootclasspath_fragment.
+	// However, as a prebuilt_bootclasspath_fragment can never contribute to an APEX there is no point
+	// in returning any files.
+	return nil
+}
+
 var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil)
 
+// createBootImageTag creates the tag to uniquely identify the boot image file among all of the
+// files that a module requires from the prebuilt .apex file.
+func createBootImageTag(arch android.ArchType, baseName string) string {
+	tag := fmt.Sprintf(".bootimage-%s-%s", arch, baseName)
+	return tag
+}
+
+// RequiredFilesFromPrebuiltApex returns the list of all files the prebuilt_bootclasspath_fragment
+// requires from a prebuilt .apex file.
+//
+// If there is no image config associated with this fragment then it returns nil. Otherwise, it
+// returns the files that are listed in the image config.
+func (module *prebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
+	imageConfig := module.getImageConfig(ctx)
+	if imageConfig != nil {
+		// Add the boot image files, e.g. .art, .oat and .vdex files.
+		files := []string{}
+		for _, variant := range imageConfig.apexVariants() {
+			arch := variant.target.Arch.ArchType
+			for _, path := range variant.imagesDeps.Paths() {
+				base := path.Base()
+				files = append(files, apexRootRelativePathToBootImageFile(arch, base))
+			}
+		}
+		return files
+	}
+	return nil
+}
+
+func apexRootRelativePathToBootImageFile(arch android.ArchType, base string) string {
+	return filepath.Join("javalib", arch.String(), base)
+}
+
+var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltBootclasspathFragmentModule)(nil)
+
 func prebuiltBootclasspathFragmentFactory() android.Module {
 	m := &prebuiltBootclasspathFragmentModule{}
 	m.AddProperties(&m.properties, &m.prebuiltProperties)
diff --git a/java/classpath_element.go b/java/classpath_element.go
new file mode 100644
index 0000000..753e7f8
--- /dev/null
+++ b/java/classpath_element.go
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java
+
+import (
+	"fmt"
+	"strings"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+// Supports constructing a list of ClasspathElement from a set of fragments and modules.
+
+// ClasspathElement represents a component that contributes to a classpath. That can be
+// either a java module or a classpath fragment module.
+type ClasspathElement interface {
+	Module() android.Module
+	String() string
+}
+
+type ClasspathElements []ClasspathElement
+
+// ClasspathFragmentElement is a ClasspathElement that encapsulates a classpath fragment module.
+type ClasspathFragmentElement struct {
+	Fragment android.Module
+	Contents []android.Module
+}
+
+func (b *ClasspathFragmentElement) Module() android.Module {
+	return b.Fragment
+}
+
+func (b *ClasspathFragmentElement) String() string {
+	contents := []string{}
+	for _, module := range b.Contents {
+		contents = append(contents, module.String())
+	}
+	return fmt.Sprintf("fragment(%s, %s)", b.Fragment, strings.Join(contents, ", "))
+}
+
+var _ ClasspathElement = (*ClasspathFragmentElement)(nil)
+
+// ClasspathLibraryElement is a ClasspathElement that encapsulates a java library.
+type ClasspathLibraryElement struct {
+	Library android.Module
+}
+
+func (b *ClasspathLibraryElement) Module() android.Module {
+	return b.Library
+}
+
+func (b *ClasspathLibraryElement) String() string {
+	return fmt.Sprintf("library{%s}", b.Library)
+}
+
+var _ ClasspathElement = (*ClasspathLibraryElement)(nil)
+
+// ClasspathElementContext defines the context methods needed by CreateClasspathElements
+type ClasspathElementContext interface {
+	OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
+	OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{}
+	ModuleErrorf(fmt string, args ...interface{})
+}
+
+// CreateClasspathElements creates a list of ClasspathElement objects from a list of libraries and
+// a list of fragments.
+//
+// The libraries parameter contains the set of libraries from which the classpath is constructed.
+// The fragments parameter contains the classpath fragment modules whose contents are libraries that
+// are part of the classpath. Each library in the libraries parameter may be part of a fragment. The
+// determination as to which libraries belong to fragments and which do not is based on the apex to
+// which they belong, if any.
+//
+// Every fragment in the fragments list must be part of one or more apexes and each apex is assumed
+// to contain only a single fragment from the fragments list. A library in the libraries parameter
+// that is part of an apex must be provided by a classpath fragment in the corresponding apex.
+//
+// This will return a ClasspathElements list that contains a ClasspathElement for each standalone
+// library and each fragment. The order of the elements in the list is such that if the list was
+// flattened into a list of library modules that it would result in the same list or modules as the
+// input libraries. Flattening the list can be done by replacing each ClasspathFragmentElement in
+// the list with its Contents field.
+//
+// Requirements/Assumptions:
+// * A fragment can be associated with more than one apex but each apex must only be associated with
+//   a single fragment from the fragments list.
+// * All of a fragment's contents must appear as a contiguous block in the same order in the
+//   libraries list.
+// * Each library must only appear in a single fragment.
+//
+// The apex is used to identify which libraries belong to which fragment. First a mapping is created
+// from apex to fragment. Then the libraries are iterated over and any library in an apex is
+// associated with an element for the fragment to which it belongs. Otherwise, the libraries are
+// standalone and have their own element.
+//
+// e.g. Given the following input:
+//     libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext
+//     fragments: com.android.art:art-bootclasspath-fragment
+//
+// Then this will return:
+//     ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]),
+//     ClasspathLibraryElement(framework),
+//     ClasspathLibraryElement(ext),
+func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module) ClasspathElements {
+	// Create a map from apex name to the fragment module. This makes it easy to find the fragment
+	// associated with a particular apex.
+	apexToFragment := map[string]android.Module{}
+	for _, fragment := range fragments {
+		if !ctx.OtherModuleHasProvider(fragment, android.ApexInfoProvider) {
+			ctx.ModuleErrorf("fragment %s is not part of an apex", fragment)
+			continue
+		}
+
+		apexInfo := ctx.OtherModuleProvider(fragment, android.ApexInfoProvider).(android.ApexInfo)
+		for _, apex := range apexInfo.InApexVariants {
+			if existing, ok := apexToFragment[apex]; ok {
+				ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing)
+				continue
+			}
+			apexToFragment[apex] = fragment
+		}
+	}
+
+	fragmentToElement := map[android.Module]*ClasspathFragmentElement{}
+	elements := []ClasspathElement{}
+	var currentElement ClasspathElement
+
+skipLibrary:
+	// Iterate over the libraries to construct the ClasspathElements list.
+	for _, library := range libraries {
+		var element ClasspathElement
+		if ctx.OtherModuleHasProvider(library, android.ApexInfoProvider) {
+			apexInfo := ctx.OtherModuleProvider(library, android.ApexInfoProvider).(android.ApexInfo)
+
+			var fragment android.Module
+
+			// Make sure that the library is in only one fragment of the classpath.
+			for _, apex := range apexInfo.InApexVariants {
+				if f, ok := apexToFragment[apex]; ok {
+					if fragment == nil {
+						// This is the first fragment so just save it away.
+						fragment = f
+					} else if f != fragment {
+						// This apex variant of the library is in a different fragment.
+						ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f)
+						// Skip over this library entirely as otherwise the resulting classpath elements would
+						// be invalid.
+						continue skipLibrary
+					}
+				} else {
+					// There is no fragment associated with the library's apex.
+				}
+			}
+
+			if fragment == nil {
+				ctx.ModuleErrorf("library %s is from apexes %s which have no corresponding fragment in %s",
+					library, apexInfo.InApexVariants, fragments)
+				// Skip over this library entirely as otherwise the resulting classpath elements would
+				// be invalid.
+				continue skipLibrary
+			} else if existingFragmentElement, ok := fragmentToElement[fragment]; ok {
+				// This library is in a fragment element that has already been added.
+
+				// If the existing fragment element is still the current element then this library is
+				// contiguous with other libraries in that fragment so there is nothing more to do.
+				// Otherwise this library is not contiguous with other libraries in the same fragment which
+				// is an error.
+				if existingFragmentElement != currentElement {
+					separator := ""
+					if fragmentElement, ok := currentElement.(*ClasspathFragmentElement); ok {
+						separator = fmt.Sprintf("libraries from fragment %s like %s", fragmentElement.Fragment, fragmentElement.Contents[0])
+					} else {
+						libraryElement := currentElement.(*ClasspathLibraryElement)
+						separator = fmt.Sprintf("library %s", libraryElement.Library)
+					}
+
+					// Get the library that precedes this library in the fragment. That is the last library as
+					// this library has not yet been added.
+					precedingLibraryInFragment := existingFragmentElement.Contents[len(existingFragmentElement.Contents)-1]
+					ctx.ModuleErrorf("libraries from the same fragment must be contiguous, however %s and %s from fragment %s are separated by %s",
+						precedingLibraryInFragment, library, fragment, separator)
+				}
+
+				// Add this library to the fragment element's contents.
+				existingFragmentElement.Contents = append(existingFragmentElement.Contents, library)
+			} else {
+				// This is the first library in this fragment so add a new element for the fragment,
+				// including the library.
+				fragmentElement := &ClasspathFragmentElement{
+					Fragment: fragment,
+					Contents: []android.Module{library},
+				}
+
+				// Store it away so we can detect when attempting to create another element for the same
+				// fragment.
+				fragmentToElement[fragment] = fragmentElement
+				element = fragmentElement
+			}
+		} else {
+			// The library is from the platform so just add an element for it.
+			element = &ClasspathLibraryElement{Library: library}
+		}
+
+		// If no element was created then it means that the library has been added to an existing
+		// fragment element so the list of elements and current element are unaffected.
+		if element != nil {
+			// Add the element to the list and make it the current element for the next iteration.
+			elements = append(elements, element)
+			currentElement = element
+		}
+	}
+
+	return elements
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index dc8df5e..bb85784 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -358,6 +358,19 @@
 	return ret
 }
 
+// apexVariants returns a list of all *bootImageVariant that could be included in an apex.
+func (image *bootImageConfig) apexVariants() []*bootImageVariant {
+	variants := []*bootImageVariant{}
+	for _, variant := range image.variants {
+		// We also generate boot images for host (for testing), but we don't need those in the apex.
+		// TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
+		if variant.target.Os == android.Android {
+			variants = append(variants, variant)
+		}
+	}
+	return variants
+}
+
 // Return boot image locations (as a list of symbolic paths).
 //
 // The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
@@ -489,7 +502,7 @@
 	}
 }
 
-// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
+// buildBootImage takes a bootImageConfig, and creates rules to build it.
 func buildBootImage(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
 	var zipFiles android.Paths
 	for _, variant := range image.variants {
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 643c5cb..6e22614 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -524,7 +524,7 @@
 		}
 	}
 
-	ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) {
+	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
 		if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
 			kind := hiddenAPIStubsTag.sdkKind
@@ -580,6 +580,36 @@
 	b[android.RemoveOptionalPrebuiltPrefix(module.Name())] = path
 }
 
+// bootDexJars returns the boot dex jar paths sorted by their keys.
+func (b bootDexJarByModule) bootDexJars() android.Paths {
+	paths := android.Paths{}
+	for _, k := range android.SortedStringKeys(b) {
+		paths = append(paths, b[k])
+	}
+	return paths
+}
+
+// bootDexJarsWithoutCoverage returns the boot dex jar paths sorted by their keys without coverage
+// libraries if present.
+func (b bootDexJarByModule) bootDexJarsWithoutCoverage() android.Paths {
+	paths := android.Paths{}
+	for _, k := range android.SortedStringKeys(b) {
+		if k == "jacocoagent" {
+			continue
+		}
+		paths = append(paths, b[k])
+	}
+	return paths
+}
+
+// HiddenAPIOutput encapsulates the output from the hidden API processing.
+type HiddenAPIOutput struct {
+	HiddenAPIFlagOutput
+
+	// The map from base module name to the path to the encoded boot dex file.
+	EncodedBootDexFilesByModule bootDexJarByModule
+}
+
 // pathForValidation creates a path of the same type as the supplied type but with a name of
 // <path>.valid.
 //
@@ -604,7 +634,7 @@
 // hiddenAPIInfo is a struct containing paths to files that augment the information provided by
 // the annotationFlags.
 func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string,
-	outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlags android.Path,
+	outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlagPaths android.Paths,
 	flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths, generatedRemovedDexSignatures android.OptionalPath) {
 
 	// The file which is used to record that the flags file is valid.
@@ -635,7 +665,7 @@
 	command := rule.Command().
 		BuiltTool("generate_hiddenapi_lists").
 		FlagWithInput("--csv ", baseFlagsPath).
-		Input(annotationFlags).
+		Inputs(annotationFlagPaths).
 		FlagWithOutput("--output ", tempPath)
 
 	// Add the options for the different categories of flag files.
@@ -665,8 +695,8 @@
 	rule.Build(name, desc)
 }
 
-// hiddenAPIGenerateAllFlagsForBootclasspathFragment will generate all the flags for a fragment
-// of the bootclasspath.
+// hiddenAPIRulesForBootclasspathFragment will generate all the flags for a fragment of the
+// bootclasspath and then encode the flags into the boot dex files.
 //
 // It takes:
 // * Map from android.SdkKind to stub dex jar paths defining the API for that sdk kind.
@@ -679,15 +709,16 @@
 // * metadata.csv
 // * index.csv
 // * all-flags.csv
-func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+// * encoded boot dex files
+func hiddenAPIRulesForBootclasspathFragment(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
 	hiddenApiSubDir := "modular-hiddenapi"
 
-	// Gather the dex files for the boot libraries provided by this fragment.
-	bootDexJars := extractBootDexJarsFromModules(ctx, contents)
+	// Gather information about the boot dex files for the boot libraries provided by this fragment.
+	bootDexInfoByModule := extractBootDexInfoFromModules(ctx, contents)
 
 	// Generate the stub-flags.csv.
 	stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv")
-	rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexJars, input)
+	rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexInfoByModule.bootDexJars(), input)
 	rule.Build("modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags")
 
 	// Extract the classes jars from the contents.
@@ -715,16 +746,29 @@
 
 	// Generate the all-flags.csv which are the flags that will, in future, be encoded into the dex
 	// files.
-	outputPath := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
-	buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", outputPath, stubFlagsCSV, annotationFlagsCSV, input.FlagFilesByCategory, nil, removedDexSignatures)
+	allFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
+	buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", allFlagsCSV, stubFlagsCSV, android.Paths{annotationFlagsCSV}, input.FlagFilesByCategory, nil, removedDexSignatures)
+
+	// Encode the flags into the boot dex files.
+	encodedBootDexJarsByModule := map[string]android.Path{}
+	outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
+	for _, name := range android.SortedStringKeys(bootDexInfoByModule) {
+		bootDexInfo := bootDexInfoByModule[name]
+		unencodedDex := bootDexInfo.path
+		encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, outputDir)
+		encodedBootDexJarsByModule[name] = encodedDex
+	}
 
 	// Store the paths in the info for use by other modules and sdk snapshot generation.
-	output := HiddenAPIFlagOutput{
-		StubFlagsPath:       stubFlagsCSV,
-		AnnotationFlagsPath: annotationFlagsCSV,
-		MetadataPath:        metadataCSV,
-		IndexPath:           indexCSV,
-		AllFlagsPath:        outputPath,
+	output := HiddenAPIOutput{
+		HiddenAPIFlagOutput: HiddenAPIFlagOutput{
+			StubFlagsPath:       stubFlagsCSV,
+			AnnotationFlagsPath: annotationFlagsCSV,
+			MetadataPath:        metadataCSV,
+			IndexPath:           indexCSV,
+			AllFlagsPath:        allFlagsCSV,
+		},
+		EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
 	}
 	return &output
 }
@@ -747,37 +791,15 @@
 }
 
 // extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules.
-func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) android.Paths {
-	bootDexJars := android.Paths{}
+func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
+	bootDexJars := bootDexJarByModule{}
 	for _, module := range contents {
 		hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module)
 		if hiddenAPIModule == nil {
 			continue
 		}
-		bootDexJar := hiddenAPIModule.bootDexJar()
-		if bootDexJar == nil {
-			if ctx.Config().AlwaysUsePrebuiltSdks() {
-				// TODO(b/179354495): Remove this workaround when it is unnecessary.
-				// Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So,
-				// create a fake one that will cause a build error only if it is used.
-				fake := android.PathForModuleOut(ctx, "fake/boot-dex/%s.jar", module.Name())
-
-				// Create an error rule that pretends to create the output file but will actually fail if it
-				// is run.
-				ctx.Build(pctx, android.BuildParams{
-					Rule:   android.ErrorRule,
-					Output: fake,
-					Args: map[string]string{
-						"error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module),
-					},
-				})
-				bootDexJars = append(bootDexJars, fake)
-			} else {
-				ctx.ModuleErrorf("module %s does not provide a dex jar", module)
-			}
-		} else {
-			bootDexJars = append(bootDexJars, bootDexJar)
-		}
+		bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule)
+		bootDexJars.addPath(module, bootDexJar)
 	}
 	return bootDexJars
 }
@@ -794,6 +816,60 @@
 	return nil
 }
 
+// bootDexInfo encapsulates both the path and uncompressDex status retrieved from a hiddenAPIModule.
+type bootDexInfo struct {
+	// The path to the dex jar that has not had hidden API flags encoded into it.
+	path android.Path
+
+	// Indicates whether the dex jar needs uncompressing before encoding.
+	uncompressDex bool
+}
+
+// bootDexInfoByModule is a map from module name (as returned by module.Name()) to the boot dex
+// path (as returned by hiddenAPIModule.bootDexJar()) and the uncompressDex flag.
+type bootDexInfoByModule map[string]bootDexInfo
+
+// bootDexJars returns the boot dex jar paths sorted by their keys.
+func (b bootDexInfoByModule) bootDexJars() android.Paths {
+	paths := android.Paths{}
+	for _, m := range android.SortedStringKeys(b) {
+		paths = append(paths, b[m].path)
+	}
+	return paths
+}
+
+// extractBootDexInfoFromModules extracts the boot dex jar and uncompress dex state from
+// each of the supplied modules which must implement hiddenAPIModule.
+func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android.Module) bootDexInfoByModule {
+	bootDexJarsByModule := bootDexInfoByModule{}
+	for _, module := range contents {
+		hiddenAPIModule := module.(hiddenAPIModule)
+		bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule)
+		bootDexJarsByModule[module.Name()] = bootDexInfo{
+			path:          bootDexJar,
+			uncompressDex: *hiddenAPIModule.uncompressDex(),
+		}
+	}
+
+	return bootDexJarsByModule
+}
+
+// retrieveBootDexJarFromHiddenAPIModule retrieves the boot dex jar from the hiddenAPIModule.
+//
+// If the module does not provide a boot dex jar, i.e. the returned boot dex jar is nil, then  that
+// create a fake path and either report an error immediately or defer reporting of the error until
+// the path is actually used.
+func retrieveBootDexJarFromHiddenAPIModule(ctx android.ModuleContext, module hiddenAPIModule) android.Path {
+	bootDexJar := module.bootDexJar()
+	if bootDexJar == nil {
+		fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name()))
+		bootDexJar = fake
+
+		handleMissingDexBootFile(ctx, module, fake)
+	}
+	return bootDexJar
+}
+
 // extractClassesJarsFromModules extracts the class jars from the supplied modules.
 func extractClassesJarsFromModules(contents []android.Module) android.Paths {
 	classesJars := android.Paths{}
@@ -822,6 +898,11 @@
 		return true
 	}
 
+	// Any missing dependency should be allowed.
+	if ctx.Config().AllowMissingDependencies() {
+		return true
+	}
+
 	// This is called for both platform_bootclasspath and bootclasspath_fragment modules.
 	//
 	// A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules.
diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go
index edf4235..52f0770 100644
--- a/java/hiddenapi_monolithic.go
+++ b/java/hiddenapi_monolithic.go
@@ -43,22 +43,45 @@
 
 	// The paths to the generated all-flags.csv files.
 	AllFlagsPaths android.Paths
+
+	// The classes jars from the libraries on the platform bootclasspath.
+	ClassesJars android.Paths
 }
 
 // newMonolithicHiddenAPIInfo creates a new MonolithicHiddenAPIInfo from the flagFilesByCategory
 // plus information provided by each of the fragments.
-func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory FlagFilesByCategory, fragments []android.Module) MonolithicHiddenAPIInfo {
+func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory FlagFilesByCategory, classpathElements ClasspathElements) MonolithicHiddenAPIInfo {
 	monolithicInfo := MonolithicHiddenAPIInfo{}
 
 	monolithicInfo.FlagsFilesByCategory = flagFilesByCategory
 
-	// Merge all the information from the fragments. The fragments form a DAG so it is possible that
-	// this will introduce duplicates so they will be resolved after processing all the fragments.
-	for _, fragment := range fragments {
-		if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
-			info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
-			monolithicInfo.append(&info)
+	// Merge all the information from the classpathElements. The fragments form a DAG so it is possible that
+	// this will introduce duplicates so they will be resolved after processing all the classpathElements.
+	for _, element := range classpathElements {
+		var classesJars android.Paths
+		switch e := element.(type) {
+		case *ClasspathLibraryElement:
+			classesJars = retrieveClassesJarsFromModule(e.Module())
+
+		case *ClasspathFragmentElement:
+			fragment := e.Module()
+			if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
+				info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
+				monolithicInfo.append(&info)
+
+				// If the bootclasspath fragment actually perform hidden API processing itself then use the
+				// CSV files it provides and do not bother processing the classesJars files. This ensures
+				// consistent behavior between source and prebuilt as prebuilt modules do not provide
+				// classesJars.
+				if info.AllFlagsPath != nil {
+					continue
+				}
+			}
+
+			classesJars = extractClassesJarsFromModules(e.Contents)
 		}
+
+		monolithicInfo.ClassesJars = append(monolithicInfo.ClassesJars, classesJars...)
 	}
 
 	// Dedup paths.
diff --git a/java/java.go b/java/java.go
index 2bbb5b1..71c1b33 100644
--- a/java/java.go
+++ b/java/java.go
@@ -643,7 +643,7 @@
 
 	module.addHostAndDeviceProperties()
 
-	module.initModuleAndImport(&module.ModuleBase)
+	module.initModuleAndImport(module)
 
 	android.InitApexModule(module)
 	android.InitSdkAwareModule(module)
@@ -1309,7 +1309,7 @@
 
 			// Get the path of the dex implementation jar from the `deapexer` module.
 			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
-			if dexOutputPath := di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil {
 				j.dexJarFile = dexOutputPath
 
 				// Initialize the hiddenapi structure.
@@ -1426,6 +1426,29 @@
 	return nil
 }
 
+// requiredFilesFromPrebuiltApexForImport returns information about the files that a java_import or
+// java_sdk_library_import with the specified base module name requires to be exported from a
+// prebuilt_apex/apex_set.
+func requiredFilesFromPrebuiltApexForImport(name string) []string {
+	// Add the dex implementation jar to the set of exported files.
+	return []string{
+		apexRootRelativePathToJavaLib(name),
+	}
+}
+
+// apexRootRelativePathToJavaLib returns the path, relative to the root of the apex's contents, for
+// the java library with the specified name.
+func apexRootRelativePathToJavaLib(name string) string {
+	return filepath.Join("javalib", name+".jar")
+}
+
+var _ android.RequiredFilesFromPrebuiltApex = (*Import)(nil)
+
+func (j *Import) RequiredFilesFromPrebuiltApex(_ android.BaseModuleContext) []string {
+	name := j.BaseModuleName()
+	return requiredFilesFromPrebuiltApexForImport(name)
+}
+
 // Add compile time check for interface implementation
 var _ android.IDEInfo = (*Import)(nil)
 var _ android.IDECustomizedModuleName = (*Import)(nil)
@@ -1473,7 +1496,7 @@
 		&module.dexer.dexProperties,
 	)
 
-	module.initModuleAndImport(&module.ModuleBase)
+	module.initModuleAndImport(module)
 
 	module.dexProperties.Optimize.EnabledByDefault = false
 
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index fba73d0..7b651ba 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -44,13 +44,9 @@
 	properties platformBootclasspathProperties
 
 	// The apex:module pairs obtained from the configured modules.
-	//
-	// Currently only for testing.
 	configuredModules []android.Module
 
 	// The apex:module pairs obtained from the fragments.
-	//
-	// Currently only for testing.
 	fragments []android.Module
 
 	// Path to the monolithic hiddenapi-flags.csv file.
@@ -189,7 +185,8 @@
 
 	b.generateClasspathProtoBuildActions(ctx)
 
-	b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
+	bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
+	buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
 
 	// Nothing to do if skipping the dexpreopt of boot image jars.
 	if SkipDexpreoptBootJars(ctx) {
@@ -258,13 +255,15 @@
 }
 
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
-func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) {
+func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule {
 
 	// Save the paths to the monolithic files for retrieval via OutputFiles().
 	b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags
 	b.hiddenAPIIndexCSV = hiddenAPISingletonPaths(ctx).index
 	b.hiddenAPIMetadataCSV = hiddenAPISingletonPaths(ctx).metadata
 
+	bootDexJarByModule := extractBootDexJarsFromModules(ctx, modules)
+
 	// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true. This is a performance
 	// optimization that can be used to reduce the incremental build time but as its name suggests it
 	// can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath.
@@ -276,10 +275,18 @@
 				Output: path,
 			})
 		}
-		return
+		return bootDexJarByModule
 	}
 
-	monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, fragments)
+	// Construct a list of ClasspathElement objects from the modules and fragments.
+	classpathElements := CreateClasspathElements(ctx, modules, fragments)
+
+	monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, classpathElements)
+
+	// Extract the classes jars only from those libraries that do not have corresponding fragments as
+	// the fragments will have already provided the flags that are needed.
+	classesJars := monolithicInfo.ClassesJars
+
 	// Create the input to pass to ruleToGenerateHiddenAPIStubFlagsFile
 	input := newHiddenAPIFlagInput()
 
@@ -291,42 +298,60 @@
 	input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory
 
 	// Generate the monolithic stub-flags.csv file.
-	bootDexJars := extractBootDexJarsFromModules(ctx, modules)
 	stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
-	rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJars, input)
+	rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJarByModule.bootDexJars(), input)
 	rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags")
 
-	// Extract the classes jars from the contents.
-	classesJars := extractClassesJarsFromModules(modules)
-
 	// Generate the annotation-flags.csv file from all the module annotations.
-	annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags.csv")
-	buildRuleToGenerateAnnotationFlags(ctx, "monolithic hiddenapi flags", classesJars, stubFlags, annotationFlags)
+	annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags-from-classes.csv")
+	buildRuleToGenerateAnnotationFlags(ctx, "intermediate hidden API flags", classesJars, stubFlags, annotationFlags)
 
-	// Generate the monotlithic hiddenapi-flags.csv file.
+	// Generate the monolithic hiddenapi-flags.csv file.
+	//
+	// Use annotation flags generated directly from the classes jars as well as annotation flag files
+	// provided by prebuilts.
+	allAnnotationFlagFiles := android.Paths{annotationFlags}
+	allAnnotationFlagFiles = append(allAnnotationFlagFiles, monolithicInfo.AnnotationFlagsPaths...)
 	allFlags := hiddenAPISingletonPaths(ctx).flags
-	buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "hiddenapi flags", allFlags, stubFlags, annotationFlags, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{})
+	buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "monolithic hidden API flags", allFlags, stubFlags, allAnnotationFlagFiles, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{})
 
 	// Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations
 	// in the source code.
-	intermediateMetadataCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "intermediate-metadata.csv")
-	buildRuleToGenerateMetadata(ctx, "monolithic hidden API metadata", classesJars, stubFlags, intermediateMetadataCSV)
+	intermediateMetadataCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "metadata-from-classes.csv")
+	buildRuleToGenerateMetadata(ctx, "intermediate hidden API metadata", classesJars, stubFlags, intermediateMetadataCSV)
 
-	// Reformat the intermediate file to add | quotes just in case that is important for the tools
-	// that consume the metadata file.
-	// TODO(b/179354495): Investigate whether it is possible to remove this reformatting step.
+	// Generate the monolithic hiddenapi-metadata.csv file.
+	//
+	// Use metadata files generated directly from the classes jars as well as metadata files provided
+	// by prebuilts.
+	//
+	// This has the side effect of ensuring that the output file uses | quotes just in case that is
+	// important for the tools that consume the metadata file.
+	allMetadataFlagFiles := android.Paths{intermediateMetadataCSV}
+	allMetadataFlagFiles = append(allMetadataFlagFiles, monolithicInfo.MetadataPaths...)
 	metadataCSV := hiddenAPISingletonPaths(ctx).metadata
-	b.buildRuleMergeCSV(ctx, "reformat monolithic hidden API metadata", android.Paths{intermediateMetadataCSV}, metadataCSV)
+	b.buildRuleMergeCSV(ctx, "monolithic hidden API metadata", allMetadataFlagFiles, metadataCSV)
 
-	// Generate the monolithic hiddenapi-index.csv file directly from the CSV files in the classes
-	// jars.
+	// Generate an intermediate monolithic hiddenapi-index.csv file directly from the CSV files in the
+	// classes jars.
+	intermediateIndexCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "index-from-classes.csv")
+	buildRuleToGenerateIndex(ctx, "intermediate hidden API index", classesJars, intermediateIndexCSV)
+
+	// Generate the monolithic hiddenapi-index.csv file.
+	//
+	// Use index files generated directly from the classes jars as well as index files provided
+	// by prebuilts.
+	allIndexFlagFiles := android.Paths{intermediateIndexCSV}
+	allIndexFlagFiles = append(allIndexFlagFiles, monolithicInfo.IndexPaths...)
 	indexCSV := hiddenAPISingletonPaths(ctx).index
-	buildRuleToGenerateIndex(ctx, "monolithic hidden API index", classesJars, indexCSV)
+	b.buildRuleMergeCSV(ctx, "monolithic hidden API index", allIndexFlagFiles, indexCSV)
+
+	return bootDexJarByModule
 }
 
 // createAndProvideMonolithicHiddenAPIInfo creates a MonolithicHiddenAPIInfo and provides it for
 // testing.
-func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ctx android.ModuleContext, fragments []android.Module) MonolithicHiddenAPIInfo {
+func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ctx android.ModuleContext, classpathElements ClasspathElements) MonolithicHiddenAPIInfo {
 	// Create a temporary input structure in which to collate information provided directly by this
 	// module, either through properties or direct dependencies.
 	temporaryInput := newHiddenAPIFlagInput()
@@ -336,7 +361,7 @@
 
 	// Create the monolithic info, by starting with the flag files specified on this and then merging
 	// in information from all the fragment dependencies of this.
-	monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, fragments)
+	monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, classpathElements)
 
 	// Store the information for testing.
 	ctx.SetProvider(MonolithicHiddenAPIInfoProvider, monolithicInfo)
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 0318a07..1c2a3ae 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -318,11 +318,41 @@
 	// Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that
 	// creates the index.csv file.
 	platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common")
-	indexRule := platformBootclasspath.Rule("monolithic_hidden_API_index")
-	CheckHiddenAPIRuleInputs(t, `
-.intermediates/bar/android_common/javac/bar.jar
-.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
-.intermediates/foo/android_common/javac/foo.jar
-`,
-		indexRule)
+
+	var rule android.TestingBuildParams
+
+	// All the intermediate rules use the same inputs.
+	expectedIntermediateInputs := `
+		out/soong/.intermediates/bar/android_common/javac/bar.jar
+		out/soong/.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar
+		out/soong/.intermediates/foo/android_common/javac/foo.jar
+	`
+
+	// Check flags output.
+	rule = platformBootclasspath.Output("hiddenapi-monolithic/annotation-flags-from-classes.csv")
+	CheckHiddenAPIRuleInputs(t, "intermediate flags", expectedIntermediateInputs, rule)
+
+	rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-flags.csv")
+	CheckHiddenAPIRuleInputs(t, "monolithic flags", `
+		out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/annotation-flags-from-classes.csv
+		out/soong/hiddenapi/hiddenapi-stub-flags.txt
+	`, rule)
+
+	// Check metadata output.
+	rule = platformBootclasspath.Output("hiddenapi-monolithic/metadata-from-classes.csv")
+	CheckHiddenAPIRuleInputs(t, "intermediate metadata", expectedIntermediateInputs, rule)
+
+	rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-unsupported.csv")
+	CheckHiddenAPIRuleInputs(t, "monolithic metadata", `
+		out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/metadata-from-classes.csv
+	`, rule)
+
+	// Check index output.
+	rule = platformBootclasspath.Output("hiddenapi-monolithic/index-from-classes.csv")
+	CheckHiddenAPIRuleInputs(t, "intermediate index", expectedIntermediateInputs, rule)
+
+	rule = platformBootclasspath.Output("out/soong/hiddenapi/hiddenapi-index.csv")
+	CheckHiddenAPIRuleInputs(t, "monolithic index", `
+		out/soong/.intermediates/myplatform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
+	`, rule)
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 133deda..56c40f0 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -631,9 +631,17 @@
 	Doctag_files []string `android:"path"`
 }
 
+// commonSdkLibraryAndImportModule defines the interface that must be provided by a module that
+// embeds the commonToSdkLibraryAndImport struct.
+type commonSdkLibraryAndImportModule interface {
+	android.SdkAware
+
+	BaseModuleName() string
+}
+
 // Common code between sdk library and sdk library import
 type commonToSdkLibraryAndImport struct {
-	moduleBase *android.ModuleBase
+	module commonSdkLibraryAndImportModule
 
 	scopePaths map[*apiScope]*scopePaths
 
@@ -648,13 +656,13 @@
 	EmbeddableSdkLibraryComponent
 }
 
-func (c *commonToSdkLibraryAndImport) initCommon(moduleBase *android.ModuleBase) {
-	c.moduleBase = moduleBase
+func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImportModule) {
+	c.module = module
 
-	moduleBase.AddProperties(&c.commonSdkLibraryProperties)
+	module.AddProperties(&c.commonSdkLibraryProperties)
 
 	// Initialize this as an sdk library component.
-	c.initSdkLibraryComponent(moduleBase)
+	c.initSdkLibraryComponent(module)
 }
 
 func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool {
@@ -670,7 +678,7 @@
 	// Only track this sdk library if this can be used as a shared library.
 	if c.sharedLibrary() {
 		// Use the name specified in the module definition as the owner.
-		c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName())
+		c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.module.BaseModuleName())
 	}
 
 	return true
@@ -682,29 +690,29 @@
 
 // Module name of the runtime implementation library
 func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
-	return c.moduleBase.BaseModuleName() + ".impl"
+	return c.module.BaseModuleName() + ".impl"
 }
 
 // Module name of the XML file for the lib
 func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
-	return c.moduleBase.BaseModuleName() + sdkXmlFileSuffix
+	return c.module.BaseModuleName() + sdkXmlFileSuffix
 }
 
 // Name of the java_library module that compiles the stubs source.
 func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
-	return c.namingScheme.stubsLibraryModuleName(apiScope, c.moduleBase.BaseModuleName())
+	baseName := c.module.BaseModuleName()
+	return c.module.SdkMemberComponentName(baseName, func(name string) string {
+		return c.namingScheme.stubsLibraryModuleName(apiScope, name)
+	})
 }
 
 // Name of the droidstubs module that generates the stubs source and may also
 // generate/check the API.
 func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string {
-	return c.namingScheme.stubsSourceModuleName(apiScope, c.moduleBase.BaseModuleName())
-}
-
-// Name of the droidstubs module that generates/checks the API. Only used if it
-// requires different arts to the stubs source generating module.
-func (c *commonToSdkLibraryAndImport) apiModuleName(apiScope *apiScope) string {
-	return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName())
+	baseName := c.module.BaseModuleName()
+	return c.module.SdkMemberComponentName(baseName, func(name string) string {
+		return c.namingScheme.stubsSourceModuleName(apiScope, name)
+	})
 }
 
 // The component names for different outputs of the java_sdk_library.
@@ -753,7 +761,7 @@
 		if scope, ok := scopeByName[scopeName]; ok {
 			paths := c.findScopePaths(scope)
 			if paths == nil {
-				return nil, fmt.Errorf("%q does not provide api scope %s", c.moduleBase.BaseModuleName(), scopeName)
+				return nil, fmt.Errorf("%q does not provide api scope %s", c.module.BaseModuleName(), scopeName)
 			}
 
 			switch component {
@@ -784,7 +792,7 @@
 			if c.doctagPaths != nil {
 				return c.doctagPaths, nil
 			} else {
-				return nil, fmt.Errorf("no doctag_files specified on %s", c.moduleBase.BaseModuleName())
+				return nil, fmt.Errorf("no doctag_files specified on %s", c.module.BaseModuleName())
 			}
 		}
 		return nil, nil
@@ -830,7 +838,7 @@
 
 	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
 	if !sdkVersion.ApiLevel.IsPreview() {
-		return PrebuiltJars(ctx, c.moduleBase.BaseModuleName(), sdkVersion)
+		return PrebuiltJars(ctx, c.module.BaseModuleName(), sdkVersion)
 	}
 
 	paths := c.selectScopePaths(ctx, sdkVersion.Kind)
@@ -857,7 +865,7 @@
 				scopes = append(scopes, s.name)
 			}
 		}
-		ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.moduleBase.BaseModuleName(), scopes)
+		ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.BaseModuleName(), scopes)
 		return nil
 	}
 
@@ -913,7 +921,7 @@
 		// any app that includes code which depends (directly or indirectly) on the stubs
 		// library will have the appropriate <uses-library> invocation inserted into its
 		// manifest if necessary.
-		componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName())
+		componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.module.BaseModuleName())
 	}
 
 	return componentProps
@@ -945,8 +953,8 @@
 	sdkLibraryComponentProperties SdkLibraryComponentProperties
 }
 
-func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(moduleBase *android.ModuleBase) {
-	moduleBase.AddProperties(&e.sdkLibraryComponentProperties)
+func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(module android.Module) {
+	module.AddProperties(&e.sdkLibraryComponentProperties)
 }
 
 // to satisfy SdkLibraryComponentDependency
@@ -1168,6 +1176,10 @@
 		module.Library.GenerateAndroidBuildActions(ctx)
 	}
 
+	// Collate the components exported by this module. All scope specific modules are exported but
+	// the impl and xml component modules are not.
+	exportedComponents := map[string]struct{}{}
+
 	// Record the paths to the header jars of the library (stubs and impl).
 	// When this java_sdk_library is depended upon from others via "libs" property,
 	// the recorded paths will be returned depending on the link type of the caller.
@@ -1182,8 +1194,14 @@
 			// Extract information from the dependency. The exact information extracted
 			// is determined by the nature of the dependency which is determined by the tag.
 			scopeTag.extractDepInfo(ctx, to, scopePaths)
+
+			exportedComponents[ctx.OtherModuleName(to)] = struct{}{}
 		}
 	})
+
+	// Make the set of components exported by this module available for use elsewhere.
+	exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedStringKeys(exportedComponents)}
+	ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo)
 }
 
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
@@ -1709,7 +1727,7 @@
 	module.addHostAndDeviceProperties()
 	module.AddProperties(&module.sdkLibraryProperties)
 
-	module.initSdkLibraryComponent(&module.ModuleBase)
+	module.initSdkLibraryComponent(module)
 
 	module.properties.Installable = proptools.BoolPtr(true)
 	module.deviceProperties.IsSDKLibrary = true
@@ -1728,8 +1746,6 @@
 	stubsLibraryModuleName(scope *apiScope, baseName string) string
 
 	stubsSourceModuleName(scope *apiScope, baseName string) string
-
-	apiModuleName(scope *apiScope, baseName string) string
 }
 
 type defaultNamingScheme struct {
@@ -1743,10 +1759,6 @@
 	return scope.stubsSourceModuleName(baseName)
 }
 
-func (s *defaultNamingScheme) apiModuleName(scope *apiScope, baseName string) string {
-	return scope.apiModuleName(baseName)
-}
-
 var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
 
 func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) {
@@ -1780,7 +1792,7 @@
 	module := &SdkLibrary{}
 
 	// Initialize information common between source and prebuilt.
-	module.initCommon(&module.ModuleBase)
+	module.initCommon(module)
 
 	module.InitSdkLibraryProperties()
 	android.InitApexModule(module)
@@ -1928,7 +1940,7 @@
 	module.AddProperties(&module.properties, allScopeProperties)
 
 	// Initialize information common between source and prebuilt.
-	module.initCommon(&module.ModuleBase)
+	module.initCommon(module)
 
 	android.InitPrebuiltModule(module, &[]string{""})
 	android.InitApexModule(module)
@@ -2140,7 +2152,7 @@
 
 			// Get the path of the dex implementation jar from the `deapexer` module.
 			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
-			if dexOutputPath := di.PrebuiltExportPath(module.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
 				module.dexJarFile = dexOutputPath
 				module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
 			} else {
@@ -2265,6 +2277,13 @@
 	}
 }
 
+var _ android.RequiredFilesFromPrebuiltApex = (*SdkLibraryImport)(nil)
+
+func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
+	name := module.BaseModuleName()
+	return requiredFilesFromPrebuiltApexForImport(name)
+}
+
 //
 // java_sdk_library_xml
 //
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 2520dde..65af953 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -110,7 +110,7 @@
 		`)
 
 	// check the existence of the internal modules
-	result.ModuleForTests("foo", "android_common")
+	foo := result.ModuleForTests("foo", "android_common")
 	result.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
 	result.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
 	result.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
@@ -122,6 +122,17 @@
 	result.ModuleForTests("foo.api.system.28", "")
 	result.ModuleForTests("foo.api.test.28", "")
 
+	exportedComponentsInfo := result.ModuleProvider(foo.Module(), ExportedComponentsInfoProvider).(ExportedComponentsInfo)
+	expectedFooExportedComponents := []string{
+		"foo.stubs",
+		"foo.stubs.source",
+		"foo.stubs.source.system",
+		"foo.stubs.source.test",
+		"foo.stubs.system",
+		"foo.stubs.test",
+	}
+	android.AssertArrayString(t, "foo exported components", expectedFooExportedComponents, exportedComponentsInfo.Components)
+
 	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
 	// tests if baz is actually linked to the stubs lib
 	android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar")
diff --git a/java/testing.go b/java/testing.go
index 1fef337..7b452f7 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"reflect"
+	"regexp"
 	"sort"
 	"strings"
 	"testing"
@@ -393,12 +394,20 @@
 	android.AssertDeepEquals(t, fmt.Sprintf("%s fragments", "platform-bootclasspath"), expected, pairs)
 }
 
-func CheckHiddenAPIRuleInputs(t *testing.T, expected string, hiddenAPIRule android.TestingBuildParams) {
+func CheckHiddenAPIRuleInputs(t *testing.T, message string, expected string, hiddenAPIRule android.TestingBuildParams) {
 	t.Helper()
-	actual := strings.TrimSpace(strings.Join(android.NormalizePathsForTesting(hiddenAPIRule.Implicits), "\n"))
-	expected = strings.TrimSpace(expected)
+	inputs := android.Paths{}
+	if hiddenAPIRule.Input != nil {
+		inputs = append(inputs, hiddenAPIRule.Input)
+	}
+	inputs = append(inputs, hiddenAPIRule.Inputs...)
+	inputs = append(inputs, hiddenAPIRule.Implicits...)
+	inputs = android.SortedUniquePaths(inputs)
+	actual := strings.TrimSpace(strings.Join(inputs.RelativeToTop().Strings(), "\n"))
+	re := regexp.MustCompile(`\n\s+`)
+	expected = strings.TrimSpace(re.ReplaceAllString(expected, "\n"))
 	if actual != expected {
-		t.Errorf("Expected hiddenapi rule inputs:\n%s\nactual inputs:\n%s", expected, actual)
+		t.Errorf("Expected hiddenapi rule inputs - %s:\n%s\nactual inputs:\n%s", message, expected, actual)
 	}
 }
 
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index f2ab6a1..378f7cf 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -40,6 +40,7 @@
 			}
 		`, apex, fragment)),
 		android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
+		android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
 	)
 }
 
@@ -173,9 +174,29 @@
 .intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
 `),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+
+		// Check the behavior of the snapshot without the source.
+		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
+			// Make sure that the boot jars package check rule includes the dex jar retrieved from the prebuilt apex.
+			checkBootJarsPackageCheckRule(t, result, "out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/mybootlib.jar")
+		}),
+
 		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
 		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
 	)
+
+	// Make sure that the boot jars package check rule includes the dex jar created from the source.
+	checkBootJarsPackageCheckRule(t, result, "out/soong/.intermediates/mybootlib/android_common_apex10000/aligned/mybootlib.jar")
+}
+
+// checkBootJarsPackageCheckRule checks that the supplied module is an input to the boot jars
+// package check rule.
+func checkBootJarsPackageCheckRule(t *testing.T, result *android.TestResult, expectedModule string) {
+	platformBcp := result.ModuleForTests("platform-bootclasspath", "android_common")
+	bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check")
+	command := bootJarsCheckRule.RuleParams.Command
+	expectedCommandArgs := " out/soong/host/linux-x86/bin/dexdump build/soong/scripts/check_boot_jars/package_allowed_list.txt " + expectedModule + " &&"
+	android.AssertStringDoesContain(t, "boot jars package check", command, expectedCommandArgs)
 }
 
 func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
@@ -463,6 +484,28 @@
 .intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
 `),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
+			module := result.ModuleForTests("platform-bootclasspath", "android_common")
+			var rule android.TestingBuildParams
+			rule = module.Output("out/soong/hiddenapi/hiddenapi-flags.csv")
+			java.CheckHiddenAPIRuleInputs(t, "monolithic flags", `
+				out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/annotation-flags-from-classes.csv
+        out/soong/hiddenapi/hiddenapi-stub-flags.txt
+        snapshot/hiddenapi/annotation-flags.csv
+			`, rule)
+
+			rule = module.Output("out/soong/hiddenapi/hiddenapi-unsupported.csv")
+			java.CheckHiddenAPIRuleInputs(t, "monolithic metadata", `
+				out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/metadata-from-classes.csv
+        snapshot/hiddenapi/metadata.csv
+			`, rule)
+
+			rule = module.Output("out/soong/hiddenapi/hiddenapi-index.csv")
+			java.CheckHiddenAPIRuleInputs(t, "monolithic index", `
+				out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
+        snapshot/hiddenapi/index.csv
+			`, rule)
+		}),
 		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
 		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
 	)
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 6f769a3..a2cfe6d 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -663,16 +663,28 @@
 }
 
 func TestSnapshotWithJavaSystemModules(t *testing.T) {
-	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_header_libs: ["exported-system-module"],
+			java_sdk_libs: ["myjavalib"],
 			java_system_modules: ["my-system-modules"],
 		}
 
+		java_sdk_library {
+			name: "myjavalib",
+			apex_available: ["//apex_available:anyapex"],
+			srcs: ["Test.java"],
+			sdk_version: "current",
+			shared_library: false,
+			public: {
+				enabled: true,
+			},
+		}
+
 		java_system_modules {
 			name: "my-system-modules",
-			libs: ["system-module", "exported-system-module"],
+			libs: ["system-module", "exported-system-module", "myjavalib.stubs"],
 		}
 
 		java_library {
@@ -710,6 +722,21 @@
     jars: ["java/system-module.jar"],
 }
 
+java_sdk_library_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:anyapex"],
+    shared_library: false,
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        sdk_version: "current",
+    },
+}
+
 java_system_modules_import {
     name: "my-system-modules",
     prefer: false,
@@ -717,6 +744,7 @@
     libs: [
         "mysdk_system-module",
         "exported-system-module",
+        "myjavalib.stubs",
     ],
 }
 `),
@@ -739,6 +767,21 @@
     jars: ["java/system-module.jar"],
 }
 
+java_sdk_library_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:anyapex"],
+    shared_library: false,
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        sdk_version: "current",
+    },
+}
+
 java_system_modules_import {
     name: "mysdk_my-system-modules@current",
     sdk_member_name: "my-system-modules",
@@ -746,6 +789,7 @@
     libs: [
         "mysdk_system-module@current",
         "mysdk_exported-system-module@current",
+        "mysdk_myjavalib.stubs@current",
     ],
 }
 
@@ -753,12 +797,16 @@
     name: "mysdk@current",
     visibility: ["//visibility:public"],
     java_header_libs: ["mysdk_exported-system-module@current"],
+    java_sdk_libs: ["mysdk_myjavalib@current"],
     java_system_modules: ["mysdk_my-system-modules@current"],
 }
 `),
 		checkAllCopyRules(`
 .intermediates/exported-system-module/android_common/turbine-combined/exported-system-module.jar -> java/exported-system-module.jar
 .intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
 `),
 	)
 }
@@ -1085,7 +1133,14 @@
 		checkMergeZips(
 			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
 			".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
-			".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip"),
+			".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip",
+		),
+		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
+			// Make sure that the name of the child modules created by a versioned java_sdk_library_import
+			// module is correct, i.e. the suffix is added before the version and not after.
+			result.Module("mysdk_myjavalib.stubs@current", "android_common")
+			result.Module("mysdk_myjavalib.stubs.source@current", "android_common")
+		}),
 	)
 }
 
diff --git a/sdk/update.go b/sdk/update.go
index 36b564f..13c2484 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -22,7 +22,6 @@
 
 	"android/soong/apex"
 	"android/soong/cc"
-
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -140,8 +139,8 @@
 
 // Collect all the members.
 //
-// Updates the sdk module with a list of sdkMemberVariantDeps and details as to which multilibs
-// (32/64/both) are used by this sdk variant.
+// Updates the sdk module with a list of sdkMemberVariantDep instances and details as to which
+// multilibs (32/64/both) are used by this sdk variant.
 func (s *sdk) collectMembers(ctx android.ModuleContext) {
 	s.multilibUsages = multilibNone
 	ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
@@ -157,8 +156,15 @@
 			// Keep track of which multilib variants are used by the sdk.
 			s.multilibUsages = s.multilibUsages.addArchType(child.Target().Arch.ArchType)
 
+			var exportedComponentsInfo android.ExportedComponentsInfo
+			if ctx.OtherModuleHasProvider(child, android.ExportedComponentsInfoProvider) {
+				exportedComponentsInfo = ctx.OtherModuleProvider(child, android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
+			}
+
 			export := memberTag.ExportMember()
-			s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{s, memberType, child.(android.SdkAware), export})
+			s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{
+				s, memberType, child.(android.SdkAware), export, exportedComponentsInfo,
+			})
 
 			// Recurse down into the member's dependencies as it may have dependencies that need to be
 			// automatically added to the sdk.
@@ -245,26 +251,41 @@
 // the contents (header files, stub libraries, etc) into the zip file.
 func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath {
 
-	allMembersByName := make(map[string]struct{})
-	exportedMembersByName := make(map[string]struct{})
+	// Aggregate all the sdkMemberVariantDep instances from all the sdk variants.
 	hasLicenses := false
 	var memberVariantDeps []sdkMemberVariantDep
 	for _, sdkVariant := range sdkVariants {
 		memberVariantDeps = append(memberVariantDeps, sdkVariant.memberVariantDeps...)
+	}
 
-		// Record the names of all the members, both explicitly specified and implicitly
-		// included.
-		for _, memberVariantDep := range sdkVariant.memberVariantDeps {
-			name := memberVariantDep.variant.Name()
-			allMembersByName[name] = struct{}{}
+	// Filter out any sdkMemberVariantDep that is a component of another.
+	memberVariantDeps = filterOutComponents(ctx, memberVariantDeps)
 
-			if memberVariantDep.export {
-				exportedMembersByName[name] = struct{}{}
-			}
+	// Record the names of all the members, both explicitly specified and implicitly
+	// included.
+	allMembersByName := make(map[string]struct{})
+	exportedMembersByName := make(map[string]struct{})
 
-			if memberVariantDep.memberType == android.LicenseModuleSdkMemberType {
-				hasLicenses = true
-			}
+	addMember := func(name string, export bool) {
+		allMembersByName[name] = struct{}{}
+		if export {
+			exportedMembersByName[name] = struct{}{}
+		}
+	}
+
+	for _, memberVariantDep := range memberVariantDeps {
+		name := memberVariantDep.variant.Name()
+		export := memberVariantDep.export
+
+		addMember(name, export)
+
+		// Add any components provided by the module.
+		for _, component := range memberVariantDep.exportedComponentsInfo.Components {
+			addMember(component, export)
+		}
+
+		if memberVariantDep.memberType == android.LicenseModuleSdkMemberType {
+			hasLicenses = true
 		}
 	}
 
@@ -423,6 +444,47 @@
 	return outputZipFile
 }
 
+// filterOutComponents removes any item from the deps list that is a component of another item in
+// the deps list, e.g. if the deps list contains "foo" and "foo.stubs" which is component of "foo"
+// then it will remove "foo.stubs" from the deps.
+func filterOutComponents(ctx android.ModuleContext, deps []sdkMemberVariantDep) []sdkMemberVariantDep {
+	// Collate the set of components that all the modules added to the sdk provide.
+	components := map[string]*sdkMemberVariantDep{}
+	for i, _ := range deps {
+		dep := &deps[i]
+		for _, c := range dep.exportedComponentsInfo.Components {
+			components[c] = dep
+		}
+	}
+
+	// If no module provides components then return the input deps unfiltered.
+	if len(components) == 0 {
+		return deps
+	}
+
+	filtered := make([]sdkMemberVariantDep, 0, len(deps))
+	for _, dep := range deps {
+		name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(dep.variant))
+		if owner, ok := components[name]; ok {
+			// This is a component of another module that is a member of the sdk.
+
+			// If the component is exported but the owning module is not then the configuration is not
+			// supported.
+			if dep.export && !owner.export {
+				ctx.ModuleErrorf("Module %s is internal to the SDK but provides component %s which is used outside the SDK")
+				continue
+			}
+
+			// This module must not be added to the list of members of the sdk as that would result in a
+			// duplicate module in the sdk snapshot.
+			continue
+		}
+
+		filtered = append(filtered, dep)
+	}
+	return filtered
+}
+
 // addSnapshotModule adds the sdk_snapshot/module_exports_snapshot module to the builder.
 func (s *sdk) addSnapshotModule(ctx android.ModuleContext, builder *snapshotBuilder, sdkVariants []*sdk, memberVariantDeps []sdkMemberVariantDep) {
 	bpFile := builder.bpFile
@@ -1086,9 +1148,18 @@
 type sdkMemberVariantDep struct {
 	// The sdk variant that depends (possibly indirectly) on the member variant.
 	sdkVariant *sdk
+
+	// The type of sdk member the variant is to be treated as.
 	memberType android.SdkMemberType
-	variant    android.SdkAware
-	export     bool
+
+	// The variant that is added to the sdk.
+	variant android.SdkAware
+
+	// True if the member should be exported, i.e. accessible, from outside the sdk.
+	export bool
+
+	// The names of additional component modules provided by the variant.
+	exportedComponentsInfo android.ExportedComponentsInfo
 }
 
 var _ android.SdkMember = (*sdkMember)(nil)