Combine hidden API encoding with flag generation

Previously, the rules to perform hidden API encoding were generated
separately to the rules to perform hidden API flag generation. This
change combines them within the (renamed) produceHiddenAPIOutput()
method and makes the paths to the encoded dex files an output of the
generateHiddenAPIBuildActions method alongside the paths to the
generated flag files.

As encoded dex jars are now an output of the produceHiddenAPIOutput()
method which is implemented for both prebuilts and source
bootclasspath_fragment modules that necessitated the prebuilt also
providing paths to encoded dex files. That in turn required updates to
some of the tests to provide dex files from prebuilt_apex modules.

Similarly, as the produceHiddenAPIOutput() method may not be called
for some bootclasspath_fragment modules as they do not yet provide all
the information needed to perform hidden API encoding then it is
necessary to extract the encoded dex files produced by the modules
themselves. That also required a few changes to tests that did not
previously provide dex files.

Bug: 177892522
Test: m com.android.art
      - check that this change does not change the contents of the apex
        file, i.e. is byte-for-byte identical.
Change-Id: I60996a34d06ed1c87ed244ab3509621999ad86ec
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index fc8f557..fecf893 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -137,10 +137,13 @@
 // 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
 }
 
 var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
@@ -292,9 +295,9 @@
 	// Map from arch type to the boot image files.
 	bootImageFilesByArch map[android.ArchType]android.OutputPaths
 
-	// 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 {
@@ -312,6 +315,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,28 +405,34 @@
 
 	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)
+
+		// A prebuilt fragment cannot contribute to an apex.
+		if !android.IsModulePrebuilt(ctx.Module()) {
+			// Provide the apex content info.
+			b.provideApexContentInfo(ctx, imageConfig, contents, hiddenAPIOutput)
+		}
 	}
 }
 
 // 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, contents []android.Module, hiddenAPIOutput *HiddenAPIOutput) {
 	// 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
@@ -451,38 +462,6 @@
 	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 +527,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 +542,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 +566,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,6 +576,21 @@
 	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 {
@@ -615,12 +614,12 @@
 	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)
 }
 
 // generateBootImageBuildActions generates ninja rules to create the boot image if required for this
@@ -836,9 +835,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,12 +845,19 @@
 		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
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 643c5cb..60300af 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -580,6 +580,23 @@
 	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
+}
+
+// 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.
 //
@@ -665,8 +682,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 +696,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 +733,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, 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 +778,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 +803,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 +885,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/platform_bootclasspath.go b/java/platform_bootclasspath.go
index fba73d0..75a2bda 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -291,9 +291,9 @@
 	input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory
 
 	// Generate the monolithic stub-flags.csv file.
-	bootDexJars := extractBootDexJarsFromModules(ctx, modules)
+	bootDexJarByModule := 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.