Merge changes I957f3df8,I68986dcc

* changes:
  Consolidate the code to resolve a deapexer module dependency.
  Propagate the dex jar path as an OptionalPath which is either valid or invalid with a message.
diff --git a/android/deapexer.go b/android/deapexer.go
index 9290481..bed6574 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -69,6 +69,8 @@
 
 // The information exported by the `deapexer` module, access it using `DeapxerInfoProvider`.
 type DeapexerInfo struct {
+	apexModuleName string
+
 	// map from the name of an exported file from a prebuilt_apex to the path to that file. The
 	// exported file name is the apex relative path, e.g. javalib/core-libart.jar.
 	//
@@ -76,6 +78,11 @@
 	exports map[string]WritablePath
 }
 
+// ApexModuleName returns the name of the APEX module that provided the info.
+func (i DeapexerInfo) ApexModuleName() string {
+	return i.apexModuleName
+}
+
 // PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
 // prebuilt_apex that created this ApexInfo.
 //
@@ -95,9 +102,10 @@
 // for use with a prebuilt_apex module.
 //
 // See apex/deapexer.go for more information.
-func NewDeapexerInfo(exports map[string]WritablePath) DeapexerInfo {
+func NewDeapexerInfo(apexModuleName string, exports map[string]WritablePath) DeapexerInfo {
 	return DeapexerInfo{
-		exports: exports,
+		apexModuleName: apexModuleName,
+		exports:        exports,
 	}
 }
 
@@ -133,3 +141,20 @@
 	// Method that differentiates this interface from others.
 	RequiresFilesFromPrebuiltApex()
 }
+
+// FindDeapexerProviderForModule searches through the direct dependencies of the current context
+// module for a DeapexerTag dependency and returns its DeapexerInfo. If there is an error then it is
+// reported with ctx.ModuleErrorf and nil is returned.
+func FindDeapexerProviderForModule(ctx ModuleContext) *DeapexerInfo {
+	var di *DeapexerInfo
+	ctx.VisitDirectDepsWithTag(DeapexerTag, func(m Module) {
+		p := ctx.OtherModuleProvider(m, DeapexerProvider).(DeapexerInfo)
+		di = &p
+	})
+	if di != nil {
+		return di
+	}
+	ai := ctx.Provider(ApexInfoProvider).(ApexInfo)
+	ctx.ModuleErrorf("No prebuilt APEX provides a deapexer module for APEX variant %s", ai.ApexVariationName)
+	return nil
+}
diff --git a/apex/apex.go b/apex/apex.go
index 2d153e2..5294b6c 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1545,7 +1545,7 @@
 type javaModule interface {
 	android.Module
 	BaseModuleName() string
-	DexJarBuildPath() android.Path
+	DexJarBuildPath() java.OptionalDexJarPath
 	JacocoReportClassesFile() android.Path
 	LintDepSets() java.LintDepSets
 	Stem() string
@@ -1559,7 +1559,7 @@
 
 // apexFileForJavaModule creates an apexFile for a java module's dex implementation jar.
 func apexFileForJavaModule(ctx android.BaseModuleContext, module javaModule) apexFile {
-	return apexFileForJavaModuleWithFile(ctx, module, module.DexJarBuildPath())
+	return apexFileForJavaModuleWithFile(ctx, module, module.DexJarBuildPath().PathOrNil())
 }
 
 // apexFileForJavaModuleWithFile creates an apexFile for a java module with the supplied file.
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 2a2a1f4..420489e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4799,9 +4799,10 @@
 	transform := android.NullFixturePreparer
 
 	checkDexJarBuildPath := func(t *testing.T, ctx *android.TestContext, name string) {
+		t.Helper()
 		// Make sure the import has been given the correct path to the dex jar.
 		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
-		dexJarBuildPath := p.DexJarBuildPath()
+		dexJarBuildPath := p.DexJarBuildPath().PathOrNil()
 		stem := android.RemoveOptionalPrebuiltPrefix(name)
 		android.AssertStringEquals(t, "DexJarBuildPath should be apex-related path.",
 			".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar",
@@ -4809,6 +4810,7 @@
 	}
 
 	checkDexJarInstallPath := func(t *testing.T, ctx *android.TestContext, name string) {
+		t.Helper()
 		// Make sure the import has been given the correct path to the dex jar.
 		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
 		dexJarBuildPath := p.DexJarInstallPath()
@@ -4819,6 +4821,7 @@
 	}
 
 	ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) {
+		t.Helper()
 		// Make sure that an apex variant is not created for the source module.
 		android.AssertArrayString(t, "Check if there is no source variant",
 			[]string{"android_common"},
@@ -4856,8 +4859,11 @@
 		// Make sure that dexpreopt can access dex implementation files from the prebuilt.
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
+		deapexerName := deapexerModuleName("myapex")
+		android.AssertStringEquals(t, "APEX module name from deapexer name", "myapex", apexModuleName(deapexerName))
+
 		// Make sure that the deapexer has the correct input APEX.
-		deapexer := ctx.ModuleForTests("myapex.deapexer", "android_common")
+		deapexer := ctx.ModuleForTests(deapexerName, "android_common")
 		rule := deapexer.Rule("deapexer")
 		if expected, actual := []string{"myapex-arm64.apex"}, android.NormalizePathsForTesting(rule.Implicits); !reflect.DeepEqual(expected, actual) {
 			t.Errorf("expected: %q, found: %q", expected, actual)
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 3e19014..cb7d3d1 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -22,6 +22,7 @@
 
 	"android/soong/android"
 	"android/soong/java"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -737,7 +738,7 @@
 
 func getDexJarPath(result *android.TestResult, name string) string {
 	module := result.Module(name, "android_common")
-	return module.(java.UsesLibraryDependency).DexJarBuildPath().RelativeToTop().String()
+	return module.(java.UsesLibraryDependency).DexJarBuildPath().Path().RelativeToTop().String()
 }
 
 // TestBootclasspathFragment_HiddenAPIList checks to make sure that the correct parameters are
diff --git a/apex/deapexer.go b/apex/deapexer.go
index 2c1835a..8c9030a 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -15,6 +15,8 @@
 package apex
 
 import (
+	"strings"
+
 	"android/soong/android"
 )
 
@@ -75,6 +77,17 @@
 	inputApex android.Path
 }
 
+// Returns the name of the deapexer module corresponding to an APEX module with the given name.
+func deapexerModuleName(apexModuleName string) string {
+	return apexModuleName + ".deapexer"
+}
+
+// Returns the name of the APEX module corresponding to an deapexer module with
+// the given name. This reverses deapexerModuleName.
+func apexModuleName(deapexerModuleName string) string {
+	return strings.TrimSuffix(deapexerModuleName, ".deapexer")
+}
+
 func privateDeapexerFactory() android.Module {
 	module := &Deapexer{}
 	module.AddProperties(&module.properties, &module.selectedApexProperties)
@@ -113,7 +126,8 @@
 	// 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))
+		di := android.NewDeapexerInfo(apexModuleName(ctx.ModuleName()), exports)
+		ctx.SetProvider(android.DeapexerProvider, di)
 
 		// Create a sorted list of the files that this exports.
 		exportedPaths = android.SortedUniquePaths(exportedPaths)
@@ -131,6 +145,6 @@
 		for _, p := range exportedPaths {
 			command.Output(p.(android.WritablePath))
 		}
-		builder.Build("deapexer", "deapex "+ctx.ModuleName())
+		builder.Build("deapexer", "deapex "+apexModuleName(ctx.ModuleName()))
 	}
 }
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 4833a64..d59f8bf 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -176,13 +176,15 @@
 		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 {
+			path := child.(interface {
+				DexJarBuildPath() java.OptionalDexJarPath
+			}).DexJarBuildPath()
+			if path.IsSet() {
 				af := apexFile{
 					module:              child,
 					moduleDir:           ctx.OtherModuleDir(child),
 					androidMkModuleName: name,
-					builtFile:           path,
+					builtFile:           path.Path(),
 					class:               javaSharedLib,
 				}
 				if module, ok := child.(java.DexpreopterInterface); ok {
@@ -629,10 +631,6 @@
 	)
 }
 
-func deapexerModuleName(baseModuleName string) string {
-	return baseModuleName + ".deapexer"
-}
-
 func apexSelectorModuleName(baseModuleName string) string {
 	return baseModuleName + ".apex.selector"
 }
diff --git a/java/androidmk.go b/java/androidmk.go
index 71370c9..1914595 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -29,8 +29,8 @@
 
 	if hostDexNeeded {
 		var output android.Path
-		if library.dexJarFile != nil {
-			output = library.dexJarFile
+		if library.dexJarFile.IsSet() {
+			output = library.dexJarFile.Path()
 		} else {
 			output = library.implementationAndResourcesJar
 		}
@@ -44,8 +44,8 @@
 				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetBool("LOCAL_IS_HOST_MODULE", true)
 					entries.SetPath("LOCAL_PREBUILT_MODULE_FILE", output)
-					if library.dexJarFile != nil {
-						entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile)
+					if library.dexJarFile.IsSet() {
+						entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile.Path())
 					}
 					entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile)
 					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar)
@@ -106,8 +106,8 @@
 					if library.installFile == nil {
 						entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true)
 					}
-					if library.dexJarFile != nil {
-						entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile)
+					if library.dexJarFile.IsSet() {
+						entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile.Path())
 					}
 					if len(library.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", library.dexpreopter.builtInstalled)
@@ -207,8 +207,8 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
-				if prebuilt.dexJarFile != nil {
-					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
+				if prebuilt.dexJarFile.IsSet() {
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile.Path())
 				}
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
@@ -227,12 +227,12 @@
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
-		OutputFile: android.OptionalPathForPath(prebuilt.dexJarFile),
+		OutputFile: android.OptionalPathForPath(prebuilt.dexJarFile.Path()),
 		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				if prebuilt.dexJarFile != nil {
-					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
+				if prebuilt.dexJarFile.IsSet() {
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile.Path())
 				}
 				if len(prebuilt.dexpreopter.builtInstalled) > 0 {
 					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", prebuilt.dexpreopter.builtInstalled)
@@ -279,8 +279,8 @@
 				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 					entries.SetPath("LOCAL_SOONG_HEADER_JAR", binary.headerJarFile)
 					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", binary.implementationAndResourcesJar)
-					if binary.dexJarFile != nil {
-						entries.SetPath("LOCAL_SOONG_DEX_JAR", binary.dexJarFile)
+					if binary.dexJarFile.IsSet() {
+						entries.SetPath("LOCAL_SOONG_DEX_JAR", binary.dexJarFile.Path())
 					}
 					if len(binary.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled)
@@ -336,8 +336,8 @@
 				entries.SetString("LOCAL_MODULE", app.installApkName)
 				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", app.appProperties.PreventInstall)
 				entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", app.exportPackage)
-				if app.dexJarFile != nil {
-					entries.SetPath("LOCAL_SOONG_DEX_JAR", app.dexJarFile)
+				if app.dexJarFile.IsSet() {
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", app.dexJarFile.Path())
 				}
 				if app.implementationAndResourcesJar != nil {
 					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", app.implementationAndResourcesJar)
diff --git a/java/app.go b/java/app.go
index a62e442..2fd6463 100755
--- a/java/app.go
+++ b/java/app.go
@@ -476,7 +476,7 @@
 		a.Module.compile(ctx, a.aaptSrcJar)
 	}
 
-	return a.dexJarFile
+	return a.dexJarFile.PathOrNil()
 }
 
 func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath {
@@ -1305,7 +1305,8 @@
 				replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
 			}
 			clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional, tag.implicit,
-				lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
+				lib.DexJarBuildPath().PathOrNil(), lib.DexJarInstallPath(),
+				lib.ClassLoaderContexts())
 		} else if ctx.Config().AllowMissingDependencies() {
 			ctx.AddMissingDependencies([]string{dep})
 		} else {
diff --git a/java/base.go b/java/base.go
index 78aaa19..579085b 100644
--- a/java/base.go
+++ b/java/base.go
@@ -276,6 +276,87 @@
 	return false
 }
 
+// OptionalDexJarPath can be either unset, hold a valid path to a dex jar file,
+// or an invalid path describing the reason it is invalid.
+//
+// It is unset if a dex jar isn't applicable, i.e. no build rule has been
+// requested to create one.
+//
+// If a dex jar has been requested to be built then it is set, and it may be
+// either a valid android.Path, or invalid with a reason message. The latter
+// happens if the source that should produce the dex file isn't able to.
+//
+// E.g. it is invalid with a reason message if there is a prebuilt APEX that
+// could produce the dex jar through a deapexer module, but the APEX isn't
+// installable so doing so wouldn't be safe.
+type OptionalDexJarPath struct {
+	isSet bool
+	path  android.OptionalPath
+}
+
+// IsSet returns true if a path has been set, either invalid or valid.
+func (o OptionalDexJarPath) IsSet() bool {
+	return o.isSet
+}
+
+// Valid returns true if there is a path that is valid.
+func (o OptionalDexJarPath) Valid() bool {
+	return o.isSet && o.path.Valid()
+}
+
+// Path returns the valid path, or panics if it's either not set or is invalid.
+func (o OptionalDexJarPath) Path() android.Path {
+	if !o.isSet {
+		panic("path isn't set")
+	}
+	return o.path.Path()
+}
+
+// PathOrNil returns the path if it's set and valid, or else nil.
+func (o OptionalDexJarPath) PathOrNil() android.Path {
+	if o.Valid() {
+		return o.Path()
+	}
+	return nil
+}
+
+// InvalidReason returns the reason for an invalid path, which is never "". It
+// returns "" for an unset or valid path.
+func (o OptionalDexJarPath) InvalidReason() string {
+	if !o.isSet {
+		return ""
+	}
+	return o.path.InvalidReason()
+}
+
+func (o OptionalDexJarPath) String() string {
+	if !o.isSet {
+		return "<unset>"
+	}
+	return o.path.String()
+}
+
+// makeUnsetDexJarPath returns an unset OptionalDexJarPath.
+func makeUnsetDexJarPath() OptionalDexJarPath {
+	return OptionalDexJarPath{isSet: false}
+}
+
+// makeDexJarPathFromOptionalPath returns an OptionalDexJarPath that is set with
+// the given OptionalPath, which may be valid or invalid.
+func makeDexJarPathFromOptionalPath(path android.OptionalPath) OptionalDexJarPath {
+	return OptionalDexJarPath{isSet: true, path: path}
+}
+
+// makeDexJarPathFromPath returns an OptionalDexJarPath that is set with the
+// valid given path. It returns an unset OptionalDexJarPath if the given path is
+// nil.
+func makeDexJarPathFromPath(path android.Path) OptionalDexJarPath {
+	if path == nil {
+		return makeUnsetDexJarPath()
+	}
+	return makeDexJarPathFromOptionalPath(android.OptionalPathForPath(path))
+}
+
 // Module contains the properties and members used by all java module types
 type Module struct {
 	android.ModuleBase
@@ -310,7 +391,7 @@
 	implementationAndResourcesJar android.Path
 
 	// output file containing classes.dex and resources
-	dexJarFile android.Path
+	dexJarFile OptionalDexJarPath
 
 	// output file containing uninstrumented classes that will be instrumented by jacoco
 	jacocoReportClassesFile android.Path
@@ -1265,12 +1346,13 @@
 			}
 
 			// Initialize the hiddenapi structure.
-			j.initHiddenAPI(ctx, dexOutputFile, j.implementationJarFile, j.dexProperties.Uncompress_dex)
+
+			j.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), j.implementationJarFile, j.dexProperties.Uncompress_dex)
 
 			// Encode hidden API flags in dex file, if needed.
 			dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile)
 
-			j.dexJarFile = dexOutputFile
+			j.dexJarFile = makeDexJarPathFromPath(dexOutputFile)
 
 			// Dexpreopting
 			j.dexpreopt(ctx, dexOutputFile)
@@ -1280,7 +1362,7 @@
 			// There is no code to compile into a dex jar, make sure the resources are propagated
 			// to the APK if this is an app.
 			outputFile = implementationAndResourcesJar
-			j.dexJarFile = j.resourceJar
+			j.dexJarFile = makeDexJarPathFromPath(j.resourceJar)
 		}
 
 		if ctx.Failed() {
@@ -1470,7 +1552,7 @@
 	return android.Paths{j.implementationJarFile}
 }
 
-func (j *Module) DexJarBuildPath() android.Path {
+func (j *Module) DexJarBuildPath() OptionalDexJarPath {
 	return j.dexJarFile
 }
 
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index b13e2db..79c73ca 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -954,23 +954,11 @@
 		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 := android.FindDeapexerProviderForModule(ctx)
+	if di == nil {
+		return nil // An error has been reported by FindDeapexerProviderForModule.
 	}
 
-	di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
 	files := bootImageFilesByArch{}
 	for _, variant := range imageConfig.apexVariants() {
 		arch := variant.target.Arch.ArchType
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 30683da..7c8be1e 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -30,14 +30,14 @@
 	// that information encoded within it.
 	active bool
 
-	// The path to the dex jar that is in the boot class path. If this is nil then the associated
+	// The path to the dex jar that is in the boot class path. If this is unset then the associated
 	// module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional
 	// annotations for the <x> boot dex jar but which do not actually provide a boot dex jar
 	// themselves.
 	//
 	// This must be the path to the unencoded dex jar as the encoded dex jar indirectly depends on
 	// this file so using the encoded dex jar here would result in a cycle in the ninja rules.
-	bootDexJarPath android.Path
+	bootDexJarPath OptionalDexJarPath
 
 	// The paths to the classes jars that contain classes and class members annotated with
 	// the UnsupportedAppUsage annotation that need to be extracted as part of the hidden API
@@ -49,7 +49,7 @@
 	uncompressDexState *bool
 }
 
-func (h *hiddenAPI) bootDexJar() android.Path {
+func (h *hiddenAPI) bootDexJar() OptionalDexJarPath {
 	return h.bootDexJarPath
 }
 
@@ -68,7 +68,7 @@
 }
 
 type hiddenAPIIntf interface {
-	bootDexJar() android.Path
+	bootDexJar() OptionalDexJarPath
 	classesJars() android.Paths
 	uncompressDex() *bool
 }
@@ -79,7 +79,7 @@
 //
 // uncompressedDexState should be nil when the module is a prebuilt and so does not require hidden
 // API encoding.
-func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar, classesJar android.Path, uncompressedDexState *bool) {
+func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar OptionalDexJarPath, classesJar android.Path, uncompressedDexState *bool) {
 
 	// Save the classes jars even if this is not active as they may be used by modular hidden API
 	// processing.
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 1c6fbac..b9a1ca7 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -19,6 +19,7 @@
 	"strings"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint"
 )
 
@@ -277,7 +278,7 @@
 // hiddenAPIRetrieveDexJarBuildPath retrieves the DexJarBuildPath from the specified module, if
 // available, or reports an error.
 func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path {
-	var dexJar android.Path
+	var dexJar OptionalDexJarPath
 	if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
 		dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind)
 	} else if j, ok := module.(UsesLibraryDependency); ok {
@@ -287,10 +288,11 @@
 		return nil
 	}
 
-	if dexJar == nil {
-		ctx.ModuleErrorf("dependency %s does not provide a dex jar, consider setting compile_dex: true", module)
+	if !dexJar.Valid() {
+		ctx.ModuleErrorf("dependency %s does not provide a dex jar: %s", module, dexJar.InvalidReason())
+		return nil
 	}
-	return dexJar
+	return dexJar.Path()
 }
 
 // buildRuleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
@@ -1159,18 +1161,17 @@
 
 // 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.
+// If the module does not provide a boot dex jar, i.e. the returned boot dex jar is unset or
+// invalid, then 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 {
+	if !bootDexJar.Valid() {
 		fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name()))
-		bootDexJar = fake
-
-		handleMissingDexBootFile(ctx, module, fake)
+		handleMissingDexBootFile(ctx, module, fake, bootDexJar.InvalidReason())
+		return fake
 	}
-	return bootDexJar
+	return bootDexJar.Path()
 }
 
 // extractClassesJarsFromModules extracts the class jars from the supplied modules.
@@ -1264,7 +1265,7 @@
 
 // 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) {
+func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath, reason string) {
 	if deferReportingMissingBootDexJar(ctx, module) {
 		// Create an error rule that pretends to create the output file but will actually fail if it
 		// is run.
@@ -1272,11 +1273,11 @@
 			Rule:   android.ErrorRule,
 			Output: fake,
 			Args: map[string]string{
-				"error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module),
+				"error": fmt.Sprintf("missing boot dex jar dependency for %s: %s", module, reason),
 			},
 		})
 	} else {
-		ctx.ModuleErrorf("module %s does not provide a dex jar", module)
+		ctx.ModuleErrorf("module %s does not provide a dex jar: %s", module, reason)
 	}
 }
 
@@ -1287,14 +1288,13 @@
 // 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 {
+	bootDexJar := module.(interface{ DexJarBuildPath() OptionalDexJarPath }).DexJarBuildPath()
+	if !bootDexJar.Valid() {
 		fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name()))
-		bootDexJar = fake
-
-		handleMissingDexBootFile(ctx, module, fake)
+		handleMissingDexBootFile(ctx, module, fake, bootDexJar.InvalidReason())
+		return fake
 	}
-	return bootDexJar
+	return bootDexJar.Path()
 }
 
 // extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules.
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index dcd363c..75b7bb7 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -20,6 +20,7 @@
 	"testing"
 
 	"android/soong/android"
+
 	"github.com/google/blueprint/proptools"
 )
 
@@ -306,7 +307,7 @@
 		android.AssertStringEquals(t, "encode embedded java_library", unencodedDexJar, actualUnencodedDexJar.String())
 
 		// Make sure that the encoded dex jar is the exported one.
-		exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath()
+		exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath().Path()
 		android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar)
 	}
 
diff --git a/java/java.go b/java/java.go
index 4a44866..b2c1999 100644
--- a/java/java.go
+++ b/java/java.go
@@ -219,7 +219,7 @@
 
 // Provides build path and install path to DEX jars.
 type UsesLibraryDependency interface {
-	DexJarBuildPath() android.Path
+	DexJarBuildPath() OptionalDexJarPath
 	DexJarInstallPath() android.Path
 	ClassLoaderContexts() dexpreopt.ClassLoaderContextMap
 }
@@ -1215,7 +1215,7 @@
 	properties ImportProperties
 
 	// output file containing classes.dex and resources
-	dexJarFile        android.Path
+	dexJarFile        OptionalDexJarPath
 	dexJarInstallFile android.Path
 
 	combinedClasspathFile android.Path
@@ -1319,7 +1319,6 @@
 	j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
 
 	var flags javaBuilderFlags
-	var deapexerModule android.Module
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -1340,11 +1339,6 @@
 		}
 
 		addCLCFromDep(ctx, module, j.classLoaderContexts)
-
-		// Save away the `deapexer` module on which this depends, if any.
-		if tag == android.DeapexerTag {
-			deapexerModule = module
-		}
 	})
 
 	if Bool(j.properties.Installable) {
@@ -1359,26 +1353,22 @@
 		// obtained from the associated deapexer module.
 		ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 		if ai.ForPrebuiltApex {
-			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 %q does not depend on a `deapexer` module for prebuilt_apex %q",
-					j.Name(), ai.ApexVariationName)
-				return
-			}
-
 			// Get the path of the dex implementation jar from the `deapexer` module.
-			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+			di := android.FindDeapexerProviderForModule(ctx)
+			if di == nil {
+				return // An error has been reported by FindDeapexerProviderForModule.
+			}
 			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil {
-				j.dexJarFile = dexOutputPath
+				dexJarFile := makeDexJarPathFromPath(dexOutputPath)
+				j.dexJarFile = dexJarFile
 				j.dexJarInstallFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName()))
 
 				// Initialize the hiddenapi structure.
-				j.initHiddenAPI(ctx, dexOutputPath, outputFile, nil)
+				j.initHiddenAPI(ctx, dexJarFile, outputFile, nil)
 			} else {
 				// This should never happen as a variant for a prebuilt_apex is only created if the
 				// prebuilt_apex has been configured to export the java library dex file.
-				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name())
+				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName())
 			}
 		} else if Bool(j.dexProperties.Compile_dex) {
 			sdkDep := decodeSdkDep(ctx, android.SdkContext(j))
@@ -1407,12 +1397,12 @@
 			}
 
 			// Initialize the hiddenapi structure.
-			j.initHiddenAPI(ctx, dexOutputFile, outputFile, j.dexProperties.Uncompress_dex)
+			j.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), outputFile, j.dexProperties.Uncompress_dex)
 
 			// Encode hidden API flags in dex file.
 			dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile)
 
-			j.dexJarFile = dexOutputFile
+			j.dexJarFile = makeDexJarPathFromPath(dexOutputFile)
 			j.dexJarInstallFile = android.PathForModuleInstall(ctx, "framework", jarName)
 		}
 	}
@@ -1450,7 +1440,7 @@
 	return android.Paths{j.combinedClasspathFile}
 }
 
-func (j *Import) DexJarBuildPath() android.Path {
+func (j *Import) DexJarBuildPath() OptionalDexJarPath {
 	return j.dexJarFile
 }
 
@@ -1595,7 +1585,7 @@
 
 	properties DexImportProperties
 
-	dexJarFile android.Path
+	dexJarFile OptionalDexJarPath
 
 	dexpreopter
 
@@ -1686,7 +1676,7 @@
 		})
 	}
 
-	j.dexJarFile = dexOutputFile
+	j.dexJarFile = makeDexJarPathFromPath(dexOutputFile)
 
 	j.dexpreopt(ctx, dexOutputFile)
 
@@ -1696,7 +1686,7 @@
 	}
 }
 
-func (j *DexImport) DexJarBuildPath() android.Path {
+func (j *DexImport) DexJarBuildPath() OptionalDexJarPath {
 	return j.dexJarFile
 }
 
@@ -1865,7 +1855,7 @@
 	// from its CLC should be added to the current CLC.
 	if sdkLib != nil {
 		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false, true,
-			dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
+			dep.DexJarBuildPath().PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
 	} else {
 		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
 	}
diff --git a/java/java_test.go b/java/java_test.go
index 8bb017f..bc9b409 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -600,8 +600,8 @@
 	}
 
 	barDexJar := barModule.Module().(*Import).DexJarBuildPath()
-	if barDexJar != nil {
-		t.Errorf("bar dex jar build path expected to be nil, got %q", barDexJar)
+	if barDexJar.IsSet() {
+		t.Errorf("bar dex jar build path expected to be set, got %s", barDexJar)
 	}
 
 	if !strings.Contains(javac.Args["classpath"], sdklibStubsJar.String()) {
@@ -612,7 +612,7 @@
 		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String())
 	}
 
-	bazDexJar := bazModule.Module().(*Import).DexJarBuildPath()
+	bazDexJar := bazModule.Module().(*Import).DexJarBuildPath().Path()
 	expectedDexJar := "out/soong/.intermediates/baz/android_common/dex/baz.jar"
 	android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar)
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index d469522..58180f7 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -540,7 +540,7 @@
 	// The dex jar for the stubs.
 	//
 	// This is not the implementation jar, it still only contains stubs.
-	stubsDexJarPath android.Path
+	stubsDexJarPath OptionalDexJarPath
 
 	// The API specification file, e.g. system_current.txt.
 	currentApiFilePath android.OptionalPath
@@ -915,10 +915,10 @@
 }
 
 // to satisfy SdkLibraryDependency interface
-func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) android.Path {
+func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath {
 	paths := c.selectScopePaths(ctx, kind)
 	if paths == nil {
-		return nil
+		return makeUnsetDexJarPath()
 	}
 
 	return paths.stubsDexJarPath
@@ -1044,7 +1044,7 @@
 
 	// SdkApiStubDexJar returns the dex jar for the stubs. It is needed by the hiddenapi processing
 	// tool which processes dex files.
-	SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) android.Path
+	SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath
 
 	// SdkRemovedTxtFile returns the optional path to the removed.txt file for the specified sdk kind.
 	SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath
@@ -1941,7 +1941,7 @@
 	xmlPermissionsFileModule *sdkLibraryXml
 
 	// Build path to the dex implementation jar obtained from the prebuilt_apex, if any.
-	dexJarFile android.Path
+	dexJarFile OptionalDexJarPath
 
 	// Expected install file path of the source module(sdk_library)
 	// or dex implementation jar obtained from the prebuilt_apex, if any.
@@ -2169,8 +2169,6 @@
 func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	module.generateCommonBuildActions(ctx)
 
-	var deapexerModule android.Module
-
 	// Assume that source module(sdk_library) is installed in /<sdk_library partition>/framework
 	module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar")
 
@@ -2199,11 +2197,6 @@
 				ctx.ModuleErrorf("xml permissions file module must be of type *sdkLibraryXml but was %T", to)
 			}
 		}
-
-		// Save away the `deapexer` module on which this depends, if any.
-		if tag == android.DeapexerTag {
-			deapexerModule = to
-		}
 	})
 
 	// Populate the scope paths with information from the properties.
@@ -2223,21 +2216,18 @@
 		// obtained from the associated deapexer module.
 		ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 		if ai.ForPrebuiltApex {
-			if deapexerModule == nil {
-				// This should never happen as a variant for a prebuilt_apex is only created if the
-				// deapxer module has been configured to export the dex implementation jar for this module.
-				ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q",
-					module.Name(), ai.ApexVariationName)
-			}
-
 			// Get the path of the dex implementation jar from the `deapexer` module.
-			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+			di := android.FindDeapexerProviderForModule(ctx)
+			if di == nil {
+				return // An error has been reported by FindDeapexerProviderForModule.
+			}
 			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
-				module.dexJarFile = dexOutputPath
+				dexJarFile := makeDexJarPathFromPath(dexOutputPath)
+				module.dexJarFile = dexJarFile
 				installPath := android.PathForModuleInPartitionInstall(
 					ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName()))
 				module.installFile = installPath
-				module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
+				module.initHiddenAPI(ctx, dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
 
 				// Dexpreopting.
 				module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, installPath)
@@ -2247,7 +2237,7 @@
 			} else {
 				// This should never happen as a variant for a prebuilt_apex is only created if the
 				// prebuilt_apex has been configured to export the java library dex file.
-				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name())
+				ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName())
 			}
 		}
 	}
@@ -2282,14 +2272,14 @@
 }
 
 // to satisfy UsesLibraryDependency interface
-func (module *SdkLibraryImport) DexJarBuildPath() android.Path {
+func (module *SdkLibraryImport) DexJarBuildPath() OptionalDexJarPath {
 	// The dex implementation jar extracted from the .apex file should be used in preference to the
 	// source.
-	if module.dexJarFile != nil {
+	if module.dexJarFile.IsSet() {
 		return module.dexJarFile
 	}
 	if module.implLibraryModule == nil {
-		return nil
+		return makeUnsetDexJarPath()
 	} else {
 		return module.implLibraryModule.DexJarBuildPath()
 	}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 43542cb..2b53739 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -1307,7 +1307,7 @@
 			ctx := android.ModuleInstallPathContextForTesting(result.Config)
 			dexJarBuildPath := func(name string, kind android.SdkKind) string {
 				dep := result.Module(name, "android_common").(java.SdkLibraryDependency)
-				path := dep.SdkApiStubDexJar(ctx, kind)
+				path := dep.SdkApiStubDexJar(ctx, kind).Path()
 				return path.RelativeToTop().String()
 			}