Merge "Add a build flag to always enable errorprone per-target"
diff --git a/android/config.go b/android/config.go
index da78c7a..ed90c31 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1690,6 +1690,16 @@
 	return paths
 }
 
+// BuildPathsByModule returns a map from module name to build paths based on the given directory
+// prefix.
+func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath {
+	paths := map[string]WritablePath{}
+	for _, jar := range l.jars {
+		paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar")
+	}
+	return paths
+}
+
 // UnmarshalJSON converts JSON configuration from raw bytes into a
 // ConfiguredJarList structure.
 func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error {
diff --git a/android/testing.go b/android/testing.go
index 191cb8d..b36f62c 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -713,9 +713,11 @@
 
 func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) {
 	var searchedRules []string
-	for _, p := range b.provider.BuildParamsForTests() {
-		searchedRules = append(searchedRules, p.Rule.String())
-		if strings.Contains(p.Rule.String(), rule) {
+	buildParams := b.provider.BuildParamsForTests()
+	for _, p := range buildParams {
+		ruleAsString := p.Rule.String()
+		searchedRules = append(searchedRules, ruleAsString)
+		if strings.Contains(ruleAsString, rule) {
 			return b.newTestingBuildParams(p), searchedRules
 		}
 	}
@@ -725,7 +727,7 @@
 func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams {
 	p, searchRules := b.maybeBuildParamsFromRule(rule)
 	if p.Rule == nil {
-		panic(fmt.Errorf("couldn't find rule %q.\nall rules: %v", rule, searchRules))
+		panic(fmt.Errorf("couldn't find rule %q.\nall rules:\n%s", rule, strings.Join(searchRules, "\n")))
 	}
 	return p
 }
diff --git a/apex/apex.go b/apex/apex.go
index 926085b..33b83c0 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -170,9 +170,10 @@
 	// Default is false.
 	Ignore_system_library_special_case *bool
 
-	// Whenever apex_payload.img of the APEX should include dm-verity hashtree. Should be only
-	// used in tests.
-	Test_only_no_hashtree *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.
+	Generate_hashtree *bool
 
 	// Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only
 	// used in tests.
@@ -1317,9 +1318,9 @@
 	return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
 }
 
-// See the test_only_no_hashtree property
-func (a *apexBundle) testOnlyShouldSkipHashtreeGeneration() bool {
-	return proptools.Bool(a.properties.Test_only_no_hashtree)
+// See the generate_hashtree property
+func (a *apexBundle) shouldGenerateHashtree() bool {
+	return proptools.BoolDefault(a.properties.Generate_hashtree, false)
 }
 
 // See the test_only_unsigned_payload property
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 6a7c35c..1bfe7e9 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4769,7 +4769,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, "failed to find a dex jar path for module 'libfoo'", preparer)
+		testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer)
 	})
 
 	t.Run("prebuilt library preferred with source", func(t *testing.T) {
diff --git a/apex/builder.go b/apex/builder.go
index 021e499..24c049b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -630,11 +630,7 @@
 			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(a.mergedNotices.HtmlGzOutput.String()))
 		}
 
-		if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && ctx.ModuleDir() != "system/apex/shim/build" && a.testOnlyShouldSkipHashtreeGeneration() {
-			ctx.PropertyErrorf("test_only_no_hashtree", "not available")
-			return
-		}
-		if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) || a.testOnlyShouldSkipHashtreeGeneration()) && !compressionEnabled {
+		if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) && !a.shouldGenerateHashtree()) && !compressionEnabled {
 			// Apexes which are supposed to be installed in builtin dirs(/system, etc)
 			// don't need hashtree for activation. Therefore, by removing hashtree from
 			// apex bundle (filesystem image in it, to be specific), we can save storage.
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 4aaf3d4..fc8f557 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -140,7 +140,7 @@
 	// produceHiddenAPIAllFlagsFile produces the all-flags.csv and intermediate files.
 	//
 	// Updates the supplied hiddenAPIInfo with the paths to the generated files set.
-	produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput
+	produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput
 }
 
 var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
@@ -465,7 +465,7 @@
 			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 extractBootDexJarsFromHiddenAPIModules which must have been
+				// 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
 			}
@@ -561,12 +561,9 @@
 	// TODO(b/179354495): Stop hidden API processing being conditional once all bootclasspath_fragment
 	//  modules have been updated to support it.
 	if input.canPerformHiddenAPIProcessing(ctx, b.properties) {
-		// Get the content modules that contribute to the hidden API processing.
-		hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, contents)
-
 		// 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, hiddenAPIModules, input)
+		output = common.produceHiddenAPIAllFlagsFile(ctx, contents, input)
 	}
 
 	// Initialize a HiddenAPIInfo structure.
@@ -620,7 +617,7 @@
 
 // produceHiddenAPIAllFlagsFile produces the hidden API all-flags.csv file (and supporting files)
 // for the fragment.
-func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
 	// 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)
@@ -648,7 +645,8 @@
 	}
 
 	// Copy the dex jars of this fragment's content modules to their predefined locations.
-	copyBootJarsToPredefinedLocations(ctx, contents, imageConfig.modules, imageConfig.dexPaths)
+	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)
@@ -840,7 +838,7 @@
 
 // produceHiddenAPIAllFlagsFile returns a path to the prebuilt all-flags.csv or nil if none is
 // specified.
-func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, _ HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, _ HiddenAPIFlagInput) *HiddenAPIFlagOutput {
 	pathForOptionalSrc := func(src *string) android.Path {
 		if src == nil {
 			// TODO(b/179354495): Fail if this is not provided once prebuilts have been updated.
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index e1a3650..dc8df5e 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -15,7 +15,6 @@
 package java
 
 import (
-	"fmt"
 	"path/filepath"
 	"sort"
 	"strings"
@@ -254,6 +253,9 @@
 	dexPaths     android.WritablePaths // for this image
 	dexPathsDeps android.WritablePaths // for the dependency images and in this image
 
+	// Map from module name (without prebuilt_ prefix) to the predefined build path.
+	dexPathsByModule map[string]android.WritablePath
+
 	// File path to a zip archive with all image files (or nil, if not needed).
 	zip android.WritablePath
 
@@ -276,13 +278,24 @@
 	dexLocationsDeps []string // for the dependency images and in this image
 
 	// Paths to image files.
-	imagePathOnHost   android.OutputPath  // first image file path on host
-	imagePathOnDevice string              // first image file path on device
-	imagesDeps        android.OutputPaths // all files
+	imagePathOnHost   android.OutputPath // first image file path on host
+	imagePathOnDevice string             // first image file path on device
 
-	// Only for extensions, paths to the primary boot images.
+	// All the files that constitute this image variant, i.e. .art, .oat and .vdex files.
+	imagesDeps android.OutputPaths
+
+	// The path to the primary image variant's imagePathOnHost field, where primary image variant
+	// means the image variant that this extends.
+	//
+	// This is only set for a variant of an image that extends another image.
 	primaryImages android.OutputPath
 
+	// The paths to the primary image variant's imagesDeps field, where primary image variant
+	// means the image variant that this extends.
+	//
+	// This is only set for a variant of an image that extends another image.
+	primaryImagesDeps android.Paths
+
 	// Rules which should be used in make to install the outputs.
 	installs           android.RuleBuilderInstalls
 	vdexInstalls       android.RuleBuilderInstalls
@@ -450,53 +463,27 @@
 	return true
 }
 
-// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to
-// predefined paths in the global config.
-func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, bootModules []android.Module, bootjars android.ConfiguredJarList, jarPathsPredefined android.WritablePaths) {
-	jarPaths := make(android.Paths, bootjars.Len())
-	for i, module := range bootModules {
-		if module != nil {
-			bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
-			jarPaths[i] = bootDexJar
+// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined
+// paths in the global config.
+func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) {
+	// Create the super set of module names.
+	names := []string{}
+	names = append(names, android.SortedStringKeys(srcBootDexJarsByModule)...)
+	names = append(names, android.SortedStringKeys(dstBootJarsByModule)...)
+	names = android.SortedUniqueStrings(names)
+	for _, name := range names {
+		src := srcBootDexJarsByModule[name]
+		dst := dstBootJarsByModule[name]
 
-			name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(module))
-			if bootjars.Jar(i) != name {
-				ctx.ModuleErrorf("expected module %s at position %d but found %s", bootjars.Jar(i), i, name)
-			}
-		}
-	}
-
-	// The paths to bootclasspath DEX files need to be known at module GenerateAndroidBuildAction
-	// time, before the boot images are built (these paths are used in dexpreopt rule generation for
-	// Java libraries and apps). Generate rules that copy bootclasspath DEX jars to the predefined
-	// paths.
-	for i := range jarPaths {
-		input := jarPaths[i]
-		output := jarPathsPredefined[i]
-		module := bootjars.Jar(i)
-		if input == nil {
-			if ctx.Config().AllowMissingDependencies() {
-				apex := bootjars.Apex(i)
-
-				// 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: output,
-					Args: map[string]string{
-						"error": fmt.Sprintf("missing dependencies: dex jar for %s:%s", module, apex),
-					},
-				})
-			} else {
-				ctx.ModuleErrorf("failed to find a dex jar path for module '%s'"+
-					", note that some jars may be filtered out by module constraints", module)
-			}
-
+		if src == nil {
+			ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+		} else if dst == nil {
+			ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
 		} else {
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   android.Cp,
-				Input:  input,
-				Output: output,
+				Input:  src,
+				Output: dst,
 			})
 		}
 	}
@@ -588,7 +575,15 @@
 		cmd.
 			Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
 			Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
-			FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage)
+			// Add the path to the first file in the boot image with the arch specific directory removed,
+			// dex2oat will reconstruct the path to the actual file when it needs it. As the actual path
+			// to the file cannot be passed to the command make sure to add the actual path as an Implicit
+			// dependency to ensure that it is built before the command runs.
+			FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage).
+			// Similarly, the dex2oat tool will automatically find the paths to other files in the base
+			// boot image so make sure to add them as implicit dependencies to ensure that they are built
+			// before this command is run.
+			Implicits(image.primaryImagesDeps)
 	} else {
 		// It is a primary image, so it needs a base address.
 		cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 39a3e11..b13955f 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -100,6 +100,7 @@
 			// TODO(b/143682396): use module dependencies instead
 			inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input")
 			c.dexPaths = c.modules.BuildPaths(ctx, inputDir)
+			c.dexPathsByModule = c.modules.BuildPathsByModule(ctx, inputDir)
 			c.dexPathsDeps = c.dexPaths
 
 			// Create target-specific variants.
@@ -125,6 +126,7 @@
 		frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
 		for i := range targets {
 			frameworkCfg.variants[i].primaryImages = artCfg.variants[i].imagePathOnHost
+			frameworkCfg.variants[i].primaryImagesDeps = artCfg.variants[i].imagesDeps.Paths()
 			frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...)
 		}
 
@@ -152,6 +154,9 @@
 	// later on a singleton adds commands to copy actual jars to the predefined paths.
 	dexPaths android.WritablePaths
 
+	// Map from module name (without prebuilt_ prefix) to the predefined build path.
+	dexPathsByModule map[string]android.WritablePath
+
 	// A list of dex locations (a.k.a. on-device paths) to the boot jars.
 	dexLocations []string
 }
@@ -165,10 +170,11 @@
 
 		dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "updatable_bootjars")
 		dexPaths := updatableBootJars.BuildPaths(ctx, dir)
+		dexPathsByModuleName := updatableBootJars.BuildPathsByModule(ctx, dir)
 
 		dexLocations := updatableBootJars.DevicePaths(ctx.Config(), android.Android)
 
-		return updatableBootConfig{updatableBootJars, dexPaths, dexLocations}
+		return updatableBootConfig{updatableBootJars, dexPaths, dexPathsByModuleName, dexLocations}
 	}).(updatableBootConfig)
 }
 
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 54effa9..643c5cb 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -571,6 +571,15 @@
 	AllFlagsPath android.Path
 }
 
+// bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex
+// path.
+type bootDexJarByModule map[string]android.Path
+
+// addPath adds the path for a module to the map.
+func (b bootDexJarByModule) addPath(module android.Module, path android.Path) {
+	b[android.RemoveOptionalPrebuiltPrefix(module.Name())] = path
+}
+
 // pathForValidation creates a path of the same type as the supplied type but with a name of
 // <path>.valid.
 //
@@ -670,11 +679,11 @@
 // * metadata.csv
 // * index.csv
 // * all-flags.csv
-func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
 	hiddenApiSubDir := "modular-hiddenapi"
 
 	// Gather the dex files for the boot libraries provided by this fragment.
-	bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, contents)
+	bootDexJars := extractBootDexJarsFromModules(ctx, contents)
 
 	// Generate the stub-flags.csv.
 	stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv")
@@ -682,7 +691,7 @@
 	rule.Build("modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags")
 
 	// Extract the classes jars from the contents.
-	classesJars := extractClassJarsFromHiddenAPIModules(ctx, contents)
+	classesJars := extractClassesJarsFromModules(contents)
 
 	// Generate the set of flags from the annotations in the source code.
 	annotationFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "annotation-flags.csv")
@@ -737,29 +746,18 @@
 	return android.OptionalPathForPath(output)
 }
 
-// gatherHiddenAPIModuleFromContents gathers the hiddenAPIModule from the supplied contents.
-func gatherHiddenAPIModuleFromContents(ctx android.ModuleContext, contents []android.Module) []hiddenAPIModule {
-	hiddenAPIModules := []hiddenAPIModule{}
-	for _, module := range contents {
-		if hiddenAPI, ok := module.(hiddenAPIModule); ok {
-			hiddenAPIModules = append(hiddenAPIModules, hiddenAPI)
-		} else if _, ok := module.(*DexImport); ok {
-			// Ignore this for the purposes of hidden API processing
-		} else {
-			ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module)
-		}
-	}
-	return hiddenAPIModules
-}
-
-// extractBootDexJarsFromHiddenAPIModules extracts the boot dex jars from the supplied modules.
-func extractBootDexJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths {
+// extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules.
+func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) android.Paths {
 	bootDexJars := android.Paths{}
 	for _, module := range contents {
-		bootDexJar := module.bootDexJar()
+		hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module)
+		if hiddenAPIModule == nil {
+			continue
+		}
+		bootDexJar := hiddenAPIModule.bootDexJar()
 		if bootDexJar == nil {
 			if ctx.Config().AlwaysUsePrebuiltSdks() {
-				// TODO(b/179354495): Remove this work around when it is unnecessary.
+				// 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())
@@ -784,11 +782,142 @@
 	return bootDexJars
 }
 
-// extractClassJarsFromHiddenAPIModules extracts the class jars from the supplied modules.
-func extractClassJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths {
+func hiddenAPIModuleFromModule(ctx android.BaseModuleContext, module android.Module) hiddenAPIModule {
+	if hiddenAPIModule, ok := module.(hiddenAPIModule); ok {
+		return hiddenAPIModule
+	} else if _, ok := module.(*DexImport); ok {
+		// Ignore this for the purposes of hidden API processing
+	} else {
+		ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module)
+	}
+
+	return nil
+}
+
+// extractClassesJarsFromModules extracts the class jars from the supplied modules.
+func extractClassesJarsFromModules(contents []android.Module) android.Paths {
 	classesJars := android.Paths{}
 	for _, module := range contents {
-		classesJars = append(classesJars, module.classesJars()...)
+		classesJars = append(classesJars, retrieveClassesJarsFromModule(module)...)
 	}
 	return classesJars
 }
+
+// retrieveClassesJarsFromModule retrieves the classes jars from the supplied module.
+func retrieveClassesJarsFromModule(module android.Module) android.Paths {
+	if hiddenAPIModule, ok := module.(hiddenAPIModule); ok {
+		return hiddenAPIModule.classesJars()
+	}
+
+	return nil
+}
+
+// deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by
+// Soong but should instead only be reported in ninja if the file is actually built.
+func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool {
+	// 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.
+	if ctx.Config().AlwaysUsePrebuiltSdks() {
+		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.
+	// Ideally, a bootclasspath_fragment module should never have a platform variant created for it
+	// but unfortunately, due to b/187910671 it does.
+	//
+	// That causes issues when obtaining a boot dex jar for a prebuilt module as a prebuilt module
+	// used by a bootclasspath_fragment can only provide a boot dex jar when it is part of APEX, i.e.
+	// has an APEX variant not a platform variant.
+	//
+	// There are some other situations when a prebuilt module used by a bootclasspath_fragment cannot
+	// provide a boot dex jar:
+	// 1. If the bootclasspath_fragment is not exported by the prebuilt_apex/apex_set module then it
+	//    does not have an APEX variant and only has a platform variant and neither do its content
+	//    modules.
+	// 2. Some build configurations, e.g. setting TARGET_BUILD_USE_PREBUILT_SDKS causes all
+	//    java_sdk_library_import modules to be treated as preferred and as many of them are not part
+	//    of an apex they cannot provide a boot dex jar.
+	//
+	// The first case causes problems when the affected prebuilt modules are preferred but that is an
+	// invalid configuration and it is ok for it to fail as the work to enable that is not yet
+	// complete. The second case is used for building targets that do not use boot dex jars and so
+	// deferring error reporting to ninja is fine as the affected ninja targets should never be built.
+	// That is handled above.
+	//
+	// A platform_bootclasspath module can use libraries from both platform and APEX variants. Unlike
+	// the bootclasspath_fragment it supports dex_import modules which provides the dex file. So, it
+	// can obtain a boot dex jar from a prebuilt that is not part of an APEX. However, it is assumed
+	// that if the library can be part of an APEX then it is the APEX variant that is used.
+	//
+	// This check handles the slightly different requirements of the bootclasspath_fragment and
+	// platform_bootclasspath modules by only deferring error reporting for the platform variant of
+	// a prebuilt modules that has other variants which are part of an APEX.
+	//
+	// TODO(b/187910671): Remove this once platform variants are no longer created unnecessarily.
+	if android.IsModulePrebuilt(module) {
+		if am, ok := module.(android.ApexModule); ok && am.InAnyApex() {
+			apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+			if apexInfo.IsForPlatform() {
+				return true
+			}
+		}
+	}
+
+	// A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there
+	// is no equivalently versioned prebuilt APEX file from which it can be obtained. However,
+	// versioned bootclasspath modules are processed by Soong so in order to avoid them causing build
+	// failures missing boot dex jars need to be deferred.
+	if android.IsModuleInVersionedSdk(ctx.Module()) {
+		return true
+	}
+
+	return false
+}
+
+// handleMissingDexBootFile will either log a warning or create an error rule to create the fake
+// file depending on the value returned from deferReportingMissingBootDexJar.
+func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath) {
+	if deferReportingMissingBootDexJar(ctx, module) {
+		// 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),
+			},
+		})
+	} else {
+		ctx.ModuleErrorf("module %s does not provide a dex jar", module)
+	}
+}
+
+// retrieveEncodedBootDexJarFromModule returns a path to the boot dex jar from the supplied module's
+// DexJarBuildPath() method.
+//
+// The returned path will usually be to a dex jar file that has been encoded with hidden API flags.
+// However, under certain conditions, e.g. errors, or special build configurations it will return
+// a path to a fake file.
+func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.Module) android.Path {
+	bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
+	if bootDexJar == nil {
+		fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name()))
+		bootDexJar = fake
+
+		handleMissingDexBootFile(ctx, module, fake)
+	}
+	return bootDexJar
+}
+
+// extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules.
+func extractEncodedDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
+	encodedDexJarsByModuleName := bootDexJarByModule{}
+	for _, module := range contents {
+		path := retrieveEncodedBootDexJarFromModule(ctx, module)
+		encodedDexJarsByModuleName.addPath(module, path)
+	}
+	return encodedDexJarsByModuleName
+}
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 7d1e53f..fba73d0 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -290,16 +290,14 @@
 	// Use the flag files from this module and all the fragments.
 	input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory
 
-	hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, modules)
-
 	// Generate the monolithic stub-flags.csv file.
-	bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, hiddenAPIModules)
+	bootDexJars := extractBootDexJarsFromModules(ctx, modules)
 	stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
 	rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJars, input)
 	rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags")
 
 	// Extract the classes jars from the contents.
-	classesJars := extractClassJarsFromHiddenAPIModules(ctx, hiddenAPIModules)
+	classesJars := extractClassesJarsFromModules(modules)
 
 	// Generate the annotation-flags.csv file from all the module annotations.
 	annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags.csv")
@@ -389,11 +387,13 @@
 	generateUpdatableBcpPackagesRule(ctx, imageConfig, updatableModules)
 
 	// Copy non-updatable module dex jars to their predefined locations.
-	copyBootJarsToPredefinedLocations(ctx, nonUpdatableModules, imageConfig.modules, imageConfig.dexPaths)
+	nonUpdatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, nonUpdatableModules)
+	copyBootJarsToPredefinedLocations(ctx, nonUpdatableBootDexJarsByModule, imageConfig.dexPathsByModule)
 
 	// Copy updatable module dex jars to their predefined locations.
 	config := GetUpdatableBootConfig(ctx)
-	copyBootJarsToPredefinedLocations(ctx, updatableModules, config.modules, config.dexPaths)
+	updatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, updatableModules)
+	copyBootJarsToPredefinedLocations(ctx, updatableBootDexJarsByModule, config.dexPathsByModule)
 
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)