Merge changes I94fd8bbc,I497dcdff,I1873a180

* changes:
  rust: strip libraries and binaries
  rust: add ccToolchain to Module
  rust: enable use-android-relr-tags
diff --git a/apex/apex.go b/apex/apex.go
index 84a1e75..f3699b0 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -41,6 +41,9 @@
 	imageApexType     = "image"
 	zipApexType       = "zip"
 	flattenedApexType = "flattened"
+
+	ext4FsType = "ext4"
+	f2fsFsType = "f2fs"
 )
 
 type dependencyTag struct {
@@ -747,18 +750,6 @@
 	if am, ok := mctx.Module().(android.ApexModule); ok {
 		availableToPlatform := am.AvailableFor(android.AvailableToPlatform)
 
-		// In a rare case when a lib is marked as available only to an apex
-		// but the apex doesn't exist. This can happen in a partial manifest branch
-		// like master-art. Currently, libstatssocket in the stats APEX is causing
-		// this problem.
-		// Include the lib in platform because the module SDK that ought to provide
-		// it doesn't exist, so it would otherwise be left out completely.
-		// TODO(b/154888298) remove this by adding those libraries in module SDKS and skipping
-		// this check for libraries provided by SDKs.
-		if !availableToPlatform && !android.InAnyApex(am.Name()) {
-			availableToPlatform = true
-		}
-
 		// If any of the dep is not available to platform, this module is also considered
 		// as being not available to platform even if it has "//apex_available:platform"
 		mctx.VisitDirectDeps(func(child android.Module) {
@@ -1040,6 +1031,10 @@
 	// Should be only used in non-system apexes (e.g. vendor: true).
 	// Default is false.
 	Use_vndk_as_stable *bool
+
+	// The type of filesystem to use for an image apex. Either 'ext4' or 'f2fs'.
+	// Default 'ext4'.
+	Payload_fs_type *string
 }
 
 type apexTargetBundleProperties struct {
@@ -1247,6 +1242,24 @@
 	return false
 }
 
+type fsType int
+
+const (
+	ext4 fsType = iota
+	f2fs
+)
+
+func (f fsType) string() string {
+	switch f {
+	case ext4:
+		return ext4FsType
+	case f2fs:
+		return f2fsFsType
+	default:
+		panic(fmt.Errorf("unknown APEX payload type %d", f))
+	}
+}
+
 type apexBundle struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -1312,6 +1325,8 @@
 
 	// Optional list of lint report zip files for apexes that contain java or app modules
 	lintReports android.Paths
+
+	payloadFsType fsType
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -2284,6 +2299,15 @@
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = filesInfo
 
+	switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
+	case ext4FsType:
+		a.payloadFsType = ext4
+	case f2fsFsType:
+		a.payloadFsType = f2fs
+	default:
+		ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs]", *a.properties.Payload_fs_type)
+	}
+
 	// Optimization. If we are building bundled APEX, for the files that are gathered due to the
 	// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
 	// the same library in the system partition, thus effectively sharing the same libraries
diff --git a/apex/builder.go b/apex/builder.go
index 0a1ec3e..22cd69b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -63,6 +63,8 @@
 	pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
 	pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest")
 	pctx.HostBinToolVariable("extract_apks", "extract_apks")
+	pctx.HostBinToolVariable("make_f2fs", "make_f2fs")
+	pctx.HostBinToolVariable("sload_f2fs", "sload_f2fs")
 }
 
 var (
@@ -116,12 +118,12 @@
 			`--payload_type image ` +
 			`--key ${key} ${opt_flags} ${image_dir} ${out} `,
 		CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
-			"${mke2fs}", "${resize2fs}", "${sefcontext_compile}",
+			"${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}", "${sload_f2fs}",
 			"${soong_zip}", "${zipalign}", "${aapt2}", "prebuilts/sdk/current/public/android.jar"},
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "APEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest")
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest", "payload_fs_type")
 
 	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
@@ -193,7 +195,7 @@
 	// collect jniLibs. Notice that a.filesInfo is already sorted
 	var jniLibs []string
 	for _, fi := range a.filesInfo {
-		if fi.isJniLib {
+		if fi.isJniLib && !android.InList(fi.Stem(), jniLibs) {
 			jniLibs = append(jniLibs, fi.Stem())
 		}
 	}
@@ -582,6 +584,8 @@
 			optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
 		}
 
+		optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string())
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        apexRule,
 			Implicits:   implicitInputs,
diff --git a/cc/builder.go b/cc/builder.go
index ab25e55..8f2da34 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -354,6 +354,7 @@
 	protoOptionsFile bool
 
 	yacc *YaccProperties
+	lex  *LexProperties
 }
 
 type StripFlags struct {
diff --git a/cc/cc.go b/cc/cc.go
index 9bf9c84..9196d47 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -210,6 +210,7 @@
 	protoOptionsFile bool // Whether to look for a .options file next to the .proto
 
 	Yacc *YaccProperties
+	Lex  *LexProperties
 }
 
 // Properties used to compile all C or C++ modules
diff --git a/cc/compiler.go b/cc/compiler.go
index 0d3df27..f504c38 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -111,6 +111,7 @@
 	Gnu_extensions *bool
 
 	Yacc *YaccProperties
+	Lex  *LexProperties
 
 	Aidl struct {
 		// list of directories that will be added to the aidl include paths.
@@ -299,6 +300,7 @@
 	flags.Local.YasmFlags = append(flags.Local.YasmFlags, esc(compiler.Properties.Asflags)...)
 
 	flags.Yacc = compiler.Properties.Yacc
+	flags.Lex = compiler.Properties.Lex
 
 	// Include dir cflags
 	localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs)
diff --git a/cc/gen.go b/cc/gen.go
index b0aadc6..ccc3d0e 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -34,9 +34,9 @@
 var (
 	lex = pctx.AndroidStaticRule("lex",
 		blueprint.RuleParams{
-			Command:     "M4=$m4Cmd $lexCmd -o$out $in",
+			Command:     "M4=$m4Cmd $lexCmd $flags -o$out $in",
 			CommandDeps: []string{"$lexCmd", "$m4Cmd"},
-		})
+		}, "flags")
 
 	sysprop = pctx.AndroidStaticRule("sysprop",
 		blueprint.RuleParams{
@@ -153,12 +153,23 @@
 	}
 }
 
-func genLex(ctx android.ModuleContext, lexFile android.Path, outFile android.ModuleGenPath) {
+type LexProperties struct {
+	// list of module-specific flags that will be used for .l and .ll compiles
+	Flags []string
+}
+
+func genLex(ctx android.ModuleContext, lexFile android.Path, outFile android.ModuleGenPath, props *LexProperties) {
+	var flags []string
+	if props != nil {
+		flags = props.Flags
+	}
+	flagsString := strings.Join(flags[:], " ")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        lex,
 		Description: "lex " + lexFile.Rel(),
 		Output:      outFile,
 		Input:       lexFile,
+		Args:        map[string]string{"flags": flagsString},
 	})
 }
 
@@ -235,11 +246,11 @@
 		case ".l":
 			cFile := android.GenPathWithExt(ctx, "lex", srcFile, "c")
 			srcFiles[i] = cFile
-			genLex(ctx, srcFile, cFile)
+			genLex(ctx, srcFile, cFile, buildFlags.lex)
 		case ".ll":
 			cppFile := android.GenPathWithExt(ctx, "lex", srcFile, "cpp")
 			srcFiles[i] = cppFile
-			genLex(ctx, srcFile, cppFile)
+			genLex(ctx, srcFile, cppFile, buildFlags.lex)
 		case ".proto":
 			ccFile, headerFile := genProto(ctx, srcFile, buildFlags)
 			srcFiles[i] = ccFile
diff --git a/cc/util.go b/cc/util.go
index b53d86c..40374bf 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -97,6 +97,7 @@
 		protoOptionsFile: in.protoOptionsFile,
 
 		yacc: in.Yacc,
+		lex:  in.Lex,
 	}
 }
 
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 78f91df..f22ee47 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -111,12 +111,9 @@
 // LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar.
 type LibraryPaths map[string]*LibraryPath
 
-// Add a new path to the map of library paths, unless a path for this library already exists.
-func (libPaths LibraryPaths) AddLibraryPath(ctx android.PathContext, lib *string, hostPath, installPath android.Path) {
-	if lib == nil {
-		return
-	}
-	if _, present := libPaths[*lib]; !present {
+// Add a new library path to the map, unless a path for this library already exists.
+func (libPaths LibraryPaths) addLibraryPath(ctx android.PathContext, lib string, hostPath, installPath android.Path) {
+	if _, present := libPaths[lib]; !present {
 		var devicePath string
 		if installPath != nil {
 			devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
@@ -127,9 +124,30 @@
 			// but we cannot use if for dexpreopt.
 			devicePath = UnknownInstallLibraryPath
 		}
-		libPaths[*lib] = &LibraryPath{hostPath, devicePath}
+		libPaths[lib] = &LibraryPath{hostPath, devicePath}
 	}
-	return
+}
+
+// Add a new library path to the map. Ensure that the build path to the library exists.
+func (libPaths LibraryPaths) AddLibraryPath(ctx android.PathContext, lib string, hostPath, installPath android.Path) {
+	if hostPath != nil {
+		// Add a library only if the build path to it is known.
+		libPaths.addLibraryPath(ctx, lib, hostPath, installPath)
+	} else if !ctx.Config().AllowMissingDependencies() {
+		// Error on libraries with unknown build paths, unless missing dependencies are allowed.
+		android.ReportPathErrorf(ctx, "unknown build path to <uses-library> '%s'", lib)
+	} else {
+		// Not adding a library to the map will likely result in disabling dexpreopt.
+	}
+}
+
+// Add a new library path to the map, if the library exists (name is not nil).
+func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.PathContext, lib *string, hostPath, installPath android.Path) {
+	if lib != nil {
+		// Don't check the build paths, add in any case. Some libraries may be missing from the
+		// build, but their names still need to be added to <uses-library> tags in the manifest.
+		libPaths.addLibraryPath(ctx, *lib, hostPath, installPath)
+	}
 }
 
 // Add library paths from the second map to the first map (do not override existing entries).
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 8deb0a3..4dbda49 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -81,15 +81,14 @@
 	}
 
 	if !dexpreoptDisabled(ctx, global, module) {
-		// Don't preopt individual boot jars, they will be preopted together.
-		if !global.BootJars.ContainsJar(module.Name) {
+		if clc := genClassLoaderContext(ctx, global, module); clc != nil {
 			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
 				!module.NoCreateAppImage
 
 			generateDM := shouldGenerateDM(module, global)
 
 			for archIdx, _ := range module.Archs {
-				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
+				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, *clc, profile, appImage, generateDM)
 			}
 		}
 	}
@@ -102,6 +101,11 @@
 		return true
 	}
 
+	// Don't preopt individual boot jars, they will be preopted together.
+	if global.BootJars.ContainsJar(module.Name) {
+		return true
+	}
+
 	// Don't preopt system server jars that are updatable.
 	if global.UpdatableSystemServerJars.ContainsJar(module.Name) {
 		return true
@@ -214,13 +218,17 @@
 	return m[sdkVer]
 }
 
-func (m classLoaderContextMap) addLibs(sdkVer int, module *ModuleConfig, libs ...string) {
+func (m classLoaderContextMap) addLibs(sdkVer int, module *ModuleConfig, libs ...string) bool {
 	clc := m.getValue(sdkVer)
 	for _, lib := range libs {
-		p := pathForLibrary(module, lib)
-		clc.Host = append(clc.Host, p.Host)
-		clc.Target = append(clc.Target, p.Device)
+		if p := pathForLibrary(module, lib); p != nil {
+			clc.Host = append(clc.Host, p.Host)
+			clc.Target = append(clc.Target, p.Device)
+		} else {
+			return false
+		}
 	}
+	return true
 }
 
 func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
@@ -231,9 +239,79 @@
 	}
 }
 
+// genClassLoaderContext generates host and target class loader context to be passed to the dex2oat
+// command for the dexpreopted module. There are three possible cases:
+//
+// 1. System server jars. They have a special class loader context that includes other system
+//    server jars.
+//
+// 2. Library jars or APKs which have precise list of their <uses-library> libs. Their class loader
+//    context includes build and on-device paths to these libs. In some cases it may happen that
+//    the path to a <uses-library> is unknown (e.g. the dexpreopted module may depend on stubs
+//    library, whose implementation library is missing from the build altogether). In such case
+//    dexpreopting with the <uses-library> is impossible, and dexpreopting without it is pointless,
+//    as the runtime classpath won't match and the dexpreopted code will be discarded. Therefore in
+//    such cases the function returns nil, which disables dexpreopt.
+//
+// 2. All other library jars or APKs for which the exact <uses-library> list is unknown. They use
+//    the unsafe &-classpath workaround that means empty class loader context and absence of runtime
+//    check that the class loader context provided by the PackageManager agrees with the stored
+//    class loader context recorded in the .odex file.
+//
+func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) *classLoaderContextMap {
+	classLoaderContexts := make(classLoaderContextMap)
+	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
+
+	if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
+		// System server jars should be dexpreopted together: class loader context of each jar
+		// should include all preceding jars on the system server classpath.
+		classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
+
+	} else if module.EnforceUsesLibraries {
+		// Unconditional class loader context.
+		usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
+		if !classLoaderContexts.addLibs(anySdkVersion, module, usesLibs...) {
+			return nil
+		}
+
+		// Conditional class loader context for API version < 28.
+		const httpLegacy = "org.apache.http.legacy"
+		if !contains(usesLibs, httpLegacy) {
+			if !classLoaderContexts.addLibs(28, module, httpLegacy) {
+				return nil
+			}
+		}
+
+		// Conditional class loader context for API version < 29.
+		usesLibs29 := []string{
+			"android.hidl.base-V1.0-java",
+			"android.hidl.manager-V1.0-java",
+		}
+		if !classLoaderContexts.addLibs(29, module, usesLibs29...) {
+			return nil
+		}
+
+		// Conditional class loader context for API version < 30.
+		const testBase = "android.test.base"
+		if !contains(usesLibs, testBase) {
+			if !classLoaderContexts.addLibs(30, module, testBase) {
+				return nil
+			}
+		}
+
+	} else {
+		// Pass special class loader context to skip the classpath and collision check.
+		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
+		// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
+		// to the &.
+	}
+
+	return &classLoaderContexts
+}
+
 func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
-	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
-	appImage bool, generateDM bool) {
+	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, classLoaderContexts classLoaderContextMap,
+	profile android.WritablePath, appImage bool, generateDM bool) {
 
 	arch := module.Archs[archIdx]
 
@@ -264,17 +342,12 @@
 
 	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
 
-	classLoaderContexts := make(classLoaderContextMap)
 	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
 
 	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
 	rule.Command().FlagWithOutput("rm -f ", odexPath)
 
 	if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
-		// System server jars should be dexpreopted together: class loader context of each jar
-		// should include all preceding jars on the system server classpath.
-		classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
-
 		// Copy the system server jar to a predefined location where dex2oat will find it.
 		dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
 		rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
@@ -288,29 +361,6 @@
 			Implicits(clc.Host).
 			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clc.Target, ":") + "]")
 	} else if module.EnforceUsesLibraries {
-		// Unconditional class loader context.
-		usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
-		classLoaderContexts.addLibs(anySdkVersion, module, usesLibs...)
-
-		// Conditional class loader context for API version < 28.
-		const httpLegacy = "org.apache.http.legacy"
-		if !contains(usesLibs, httpLegacy) {
-			classLoaderContexts.addLibs(28, module, httpLegacy)
-		}
-
-		// Conditional class loader context for API version < 29.
-		usesLibs29 := []string{
-			"android.hidl.base-V1.0-java",
-			"android.hidl.manager-V1.0-java",
-		}
-		classLoaderContexts.addLibs(29, module, usesLibs29...)
-
-		// Conditional class loader context for API version < 30.
-		const testBase = "android.test.base"
-		if !contains(usesLibs, testBase) {
-			classLoaderContexts.addLibs(30, module, testBase)
-		}
-
 		// Generate command that saves target SDK version in a shell variable.
 		if module.ManifestPath != nil {
 			rule.Command().Text(`target_sdk_version="$(`).
@@ -540,11 +590,11 @@
 }
 
 func pathForLibrary(module *ModuleConfig, lib string) *LibraryPath {
-	path, ok := module.LibraryPaths[lib]
-	if !ok {
-		panic(fmt.Errorf("unknown library path for %q", lib))
+	if path, ok := module.LibraryPaths[lib]; ok && path.Host != nil && path.Device != "error" {
+		return path
+	} else {
+		return nil
 	}
-	return path
 }
 
 func makefileMatch(pattern, s string) bool {
diff --git a/java/aar.go b/java/aar.go
index 5c19a9c..0f5e30d 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -390,7 +390,7 @@
 			// (including the java_sdk_library) itself then append any implicit sdk library
 			// names to the list of sdk libraries to be added to the manifest.
 			if component, ok := module.(SdkLibraryComponentDependency); ok {
-				sdkLibraries.AddLibraryPath(ctx, component.OptionalImplicitSdkLibrary(),
+				sdkLibraries.MaybeAddLibraryPath(ctx, component.OptionalImplicitSdkLibrary(),
 					component.DexJarBuildPath(), component.DexJarInstallPath())
 			}
 
diff --git a/java/app.go b/java/app.go
index 5e3a9d9..34f96dd 100755
--- a/java/app.go
+++ b/java/app.go
@@ -586,7 +586,7 @@
 	return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
 }
 
-func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
+func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext, sdkLibs dexpreopt.LibraryPaths) android.Path {
 	a.dexpreopter.installPath = a.installPath(ctx)
 	if a.dexProperties.Uncompress_dex == nil {
 		// If the value was not force-set by the user, use reasonable default based on the module.
@@ -597,6 +597,7 @@
 	a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs
 	a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
 	a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
+	a.dexpreopter.libraryPaths.AddLibraryPaths(sdkLibs)
 	a.dexpreopter.manifestFile = a.mergedManifestFile
 	a.exportedSdkLibs = make(dexpreopt.LibraryPaths)
 
@@ -767,6 +768,15 @@
 	// Process all building blocks, from AAPT to certificates.
 	a.aaptBuildActions(ctx)
 
+	// The decision to enforce <uses-library> checks is made before adding implicit SDK libraries.
+	a.usesLibrary.freezeEnforceUsesLibraries()
+
+	// Add implicit SDK libraries to <uses-library> list.
+	for _, usesLib := range android.SortedStringKeys(a.aapt.sdkLibraries) {
+		a.usesLibrary.addLib(usesLib, inList(usesLib, optionalUsesLibs))
+	}
+
+	// Check that the <uses-library> list is coherent with the manifest.
 	if a.usesLibrary.enforceUsesLibraries() {
 		manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(ctx, a.mergedManifestFile)
 		apkDeps = append(apkDeps, manifestCheckFile)
@@ -779,7 +789,7 @@
 	a.linter.resources = a.aapt.resourceFiles
 	a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()
 
-	dexJarFile := a.dexBuildActions(ctx)
+	dexJarFile := a.dexBuildActions(ctx, a.aapt.sdkLibraries)
 
 	jniLibs, certificateDeps := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
 	jniJarFile := a.jniBuildActions(jniLibs, ctx)
@@ -1888,6 +1898,16 @@
 	usesLibraryProperties UsesLibraryProperties
 }
 
+func (u *usesLibrary) addLib(lib string, optional bool) {
+	if !android.InList(lib, u.usesLibraryProperties.Uses_libs) && !android.InList(lib, u.usesLibraryProperties.Optional_uses_libs) {
+		if optional {
+			u.usesLibraryProperties.Optional_uses_libs = append(u.usesLibraryProperties.Optional_uses_libs, lib)
+		} else {
+			u.usesLibraryProperties.Uses_libs = append(u.usesLibraryProperties.Uses_libs, lib)
+		}
+	}
+}
+
 func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) {
 	if !ctx.Config().UnbundledBuild() {
 		ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...)
@@ -1960,6 +1980,12 @@
 	return BoolDefault(u.usesLibraryProperties.Enforce_uses_libs, defaultEnforceUsesLibs)
 }
 
+// Freeze the value of `enforce_uses_libs` based on the current values of `uses_libs` and `optional_uses_libs`.
+func (u *usesLibrary) freezeEnforceUsesLibraries() {
+	enforce := u.enforceUsesLibraries()
+	u.usesLibraryProperties.Enforce_uses_libs = &enforce
+}
+
 // verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against the ones specified
 // in the uses_libs and optional_uses_libs properties.  It returns the path to a copy of the manifest.
 func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
diff --git a/java/app_test.go b/java/app_test.go
index 53d1f0b..b8d8616 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2618,6 +2618,19 @@
 		android_app {
 			name: "app",
 			srcs: ["a.java"],
+			libs: ["qux", "quuz"],
+			static_libs: ["static-runtime-helper"],
+			uses_libs: ["foo"],
+			sdk_version: "current",
+			optional_uses_libs: [
+				"bar",
+				"baz",
+			],
+		}
+
+		android_app {
+			name: "app_with_stub_deps",
+			srcs: ["a.java"],
 			libs: ["qux", "quuz.stubs"],
 			static_libs: ["static-runtime-helper"],
 			uses_libs: ["foo"],
@@ -2648,6 +2661,7 @@
 	run(t, ctx, config)
 
 	app := ctx.ModuleForTests("app", "android_common")
+	appWithStubDeps := ctx.ModuleForTests("app_with_stub_deps", "android_common")
 	prebuilt := ctx.ModuleForTests("prebuilt", "android_common")
 
 	// Test that implicit dependencies on java_sdk_library instances are passed to the manifest.
@@ -2678,15 +2692,24 @@
 		t.Errorf("wanted %q in %q", w, cmd)
 	}
 
-	// Test that only present libraries are preopted
+	// Test that all present libraries are preopted, including implicit SDK dependencies
 	cmd = app.Rule("dexpreopt").RuleParams.Command
-
-	if w := `--target-classpath-for-sdk any /system/framework/foo.jar:/system/framework/bar.jar`; !strings.Contains(cmd, w) {
+	w := `--target-classpath-for-sdk any` +
+		` /system/framework/foo.jar` +
+		`:/system/framework/quuz.jar` +
+		`:/system/framework/qux.jar` +
+		`:/system/framework/runtime-library.jar` +
+		`:/system/framework/bar.jar`
+	if !strings.Contains(cmd, w) {
 		t.Errorf("wanted %q in %q", w, cmd)
 	}
 
-	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
+	// TODO(skvadrik) fix dexpreopt for stub libraries for which the implementation is present
+	if appWithStubDeps.MaybeRule("dexpreopt").RuleParams.Command != "" {
+		t.Errorf("dexpreopt should be disabled for apps with dependencies on stub libraries")
+	}
 
+	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
 	if w := `--target-classpath-for-sdk any /system/framework/foo.jar:/system/framework/bar.jar`; !strings.Contains(cmd, w) {
 		t.Errorf("wanted %q in %q", w, cmd)
 	}
diff --git a/java/java.go b/java/java.go
index 4d7d568..27e425d 100644
--- a/java/java.go
+++ b/java/java.go
@@ -991,7 +991,7 @@
 			case libTag:
 				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
 				// names of sdk libs that are directly depended are exported
-				j.exportedSdkLibs.AddLibraryPath(ctx, dep.OptionalImplicitSdkLibrary(), dep.DexJarBuildPath(), dep.DexJarInstallPath())
+				j.exportedSdkLibs.MaybeAddLibraryPath(ctx, dep.OptionalImplicitSdkLibrary(), dep.DexJarBuildPath(), dep.DexJarInstallPath())
 			case staticLibTag:
 				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
 			}
@@ -1970,7 +1970,7 @@
 	// add the name of that java_sdk_library to the exported sdk libs to make sure
 	// that, if necessary, a <uses-library> element for that java_sdk_library is
 	// added to the Android manifest.
-	j.exportedSdkLibs.AddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), j.DexJarBuildPath(), j.DexJarInstallPath())
+	j.exportedSdkLibs.MaybeAddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), j.DexJarBuildPath(), j.DexJarInstallPath())
 
 	j.distFiles = j.GenerateTaggedDistFiles(ctx)
 }
@@ -2608,7 +2608,7 @@
 			switch tag {
 			case libTag:
 				// names of sdk libs that are directly depended are exported
-				j.exportedSdkLibs.AddLibraryPath(ctx, &otherName, dep.DexJarBuildPath(), dep.DexJarInstallPath())
+				j.exportedSdkLibs.AddLibraryPath(ctx, otherName, dep.DexJarBuildPath(), dep.DexJarInstallPath())
 			}
 		}
 	})
@@ -2623,7 +2623,7 @@
 	// add the name of that java_sdk_library to the exported sdk libs to make sure
 	// that, if necessary, a <uses-library> element for that java_sdk_library is
 	// added to the Android manifest.
-	j.exportedSdkLibs.AddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), outputFile, installFile)
+	j.exportedSdkLibs.MaybeAddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), outputFile, installFile)
 
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 }