Merge "Add model to represent generated snapshot .bp file"
diff --git a/apex/apex.go b/apex/apex.go
index 45184b5..289175b 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -370,20 +370,6 @@
 	Apps []string
 }
 
-type apexFileClass int
-
-const (
-	etc apexFileClass = iota
-	nativeSharedLib
-	nativeExecutable
-	shBinary
-	pyBinary
-	goBinary
-	javaSharedLib
-	nativeTest
-	app
-)
-
 type apexPackaging int
 
 const (
@@ -415,6 +401,20 @@
 	}
 }
 
+type apexFileClass int
+
+const (
+	etc apexFileClass = iota
+	nativeSharedLib
+	nativeExecutable
+	shBinary
+	pyBinary
+	goBinary
+	javaSharedLib
+	nativeTest
+	app
+)
+
 func (class apexFileClass) NameInMake() string {
 	switch class {
 	case etc:
@@ -441,13 +441,30 @@
 	}
 }
 
+// apexFile represents a file in an APEX bundle
 type apexFile struct {
 	builtFile  android.Path
 	moduleName string
 	installDir string
 	class      apexFileClass
 	module     android.Module
-	symlinks   []string
+	// list of symlinks that will be created in installDir that point to this apexFile
+	symlinks      []string
+	transitiveDep bool
+}
+
+func newApexFile(builtFile android.Path, moduleName string, installDir string, class apexFileClass, module android.Module) apexFile {
+	return apexFile{
+		builtFile:  builtFile,
+		moduleName: moduleName,
+		installDir: installDir,
+		class:      class,
+		module:     module,
+	}
+}
+
+func (af *apexFile) Ok() bool {
+	return af.builtFile != nil || af.builtFile.String() == ""
 }
 
 type apexBundle struct {
@@ -761,9 +778,11 @@
 	a.properties.HideFromMake = true
 }
 
-func getCopyManifestForNativeLibrary(ccMod *cc.Module, config android.Config, handleSpecialLibs bool) (fileToCopy android.Path, dirInApex string) {
+// TODO(jiyong) move apexFileFor* close to the apexFile type definition
+func apexFileForNativeLibrary(ccMod *cc.Module, config android.Config, handleSpecialLibs bool) apexFile {
 	// Decide the APEX-local directory by the multilib of the library
 	// In the future, we may query this to the module.
+	var dirInApex string
 	switch ccMod.Arch().ArchType.Multilib {
 	case "lib32":
 		dirInApex = "lib"
@@ -788,83 +807,84 @@
 		dirInApex = filepath.Join(dirInApex, "bionic")
 	}
 
-	fileToCopy = ccMod.OutputFile().Path()
-	return
+	fileToCopy := ccMod.OutputFile().Path()
+	return newApexFile(fileToCopy, ccMod.Name(), dirInApex, nativeSharedLib, ccMod)
 }
 
-func getCopyManifestForExecutable(cc *cc.Module) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = filepath.Join("bin", cc.RelativeInstallPath())
+func apexFileForExecutable(cc *cc.Module) apexFile {
+	dirInApex := filepath.Join("bin", cc.RelativeInstallPath())
 	if cc.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
 	}
-	fileToCopy = cc.OutputFile().Path()
-	return
+	fileToCopy := cc.OutputFile().Path()
+	af := newApexFile(fileToCopy, cc.Name(), dirInApex, nativeExecutable, cc)
+	af.symlinks = cc.Symlinks()
+	return af
 }
 
-func getCopyManifestForPyBinary(py *python.Module) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = "bin"
-	fileToCopy = py.HostToolPath().Path()
-	return
+func apexFileForPyBinary(py *python.Module) apexFile {
+	dirInApex := "bin"
+	fileToCopy := py.HostToolPath().Path()
+	return newApexFile(fileToCopy, py.Name(), dirInApex, pyBinary, py)
 }
-func getCopyManifestForGoBinary(ctx android.ModuleContext, gb bootstrap.GoBinaryTool) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = "bin"
+func apexFileForGoBinary(ctx android.ModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile {
+	dirInApex := "bin"
 	s, err := filepath.Rel(android.PathForOutput(ctx).String(), gb.InstallPath())
 	if err != nil {
 		ctx.ModuleErrorf("Unable to use compiled binary at %s", gb.InstallPath())
-		return
+		return apexFile{}
 	}
-	fileToCopy = android.PathForOutput(ctx, s)
-	return
+	fileToCopy := android.PathForOutput(ctx, s)
+	// NB: Since go binaries are static we don't need the module for anything here, which is
+	// good since the go tool is a blueprint.Module not an android.Module like we would
+	// normally use.
+	return newApexFile(fileToCopy, depName, dirInApex, goBinary, nil)
 }
 
-func getCopyManifestForShBinary(sh *android.ShBinary) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = filepath.Join("bin", sh.SubDir())
-	fileToCopy = sh.OutputFile()
-	return
+func apexFileForShBinary(sh *android.ShBinary) apexFile {
+	dirInApex := filepath.Join("bin", sh.SubDir())
+	fileToCopy := sh.OutputFile()
+	af := newApexFile(fileToCopy, sh.Name(), dirInApex, shBinary, sh)
+	af.symlinks = sh.Symlinks()
+	return af
 }
 
-func getCopyManifestForJavaLibrary(java *java.Library) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = "javalib"
-	fileToCopy = java.DexJarFile()
-	return
+func apexFileForJavaLibrary(java *java.Library) apexFile {
+	dirInApex := "javalib"
+	fileToCopy := java.DexJarFile()
+	return newApexFile(fileToCopy, java.Name(), dirInApex, javaSharedLib, java)
 }
 
-func getCopyManifestForPrebuiltJavaLibrary(java *java.Import) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = "javalib"
+func apexFileForPrebuiltJavaLibrary(java *java.Import) apexFile {
+	dirInApex := "javalib"
 	// The output is only one, but for some reason, ImplementationJars returns Paths, not Path
 	implJars := java.ImplementationJars()
 	if len(implJars) != 1 {
 		panic(fmt.Errorf("java.ImplementationJars() must return single Path, but got: %s",
 			strings.Join(implJars.Strings(), ", ")))
 	}
-	fileToCopy = implJars[0]
-	return
+	fileToCopy := implJars[0]
+	return newApexFile(fileToCopy, java.Name(), dirInApex, javaSharedLib, java)
 }
 
-func getCopyManifestForPrebuiltEtc(prebuilt android.PrebuiltEtcModule) (fileToCopy android.Path, dirInApex string) {
-	dirInApex = filepath.Join("etc", prebuilt.SubDir())
-	fileToCopy = prebuilt.OutputFile()
-	return
+func apexFileForPrebuiltEtc(prebuilt android.PrebuiltEtcModule, depName string) apexFile {
+	dirInApex := filepath.Join("etc", prebuilt.SubDir())
+	fileToCopy := prebuilt.OutputFile()
+	return newApexFile(fileToCopy, depName, dirInApex, etc, prebuilt)
 }
 
-func getCopyManifestForAndroidApp(app *java.AndroidApp, pkgName string) (fileToCopy android.Path, dirInApex string) {
+func apexFileForAndroidApp(aapp interface {
+	android.Module
+	Privileged() bool
+	OutputFile() android.Path
+}, pkgName string) apexFile {
 	appDir := "app"
-	if app.Privileged() {
+	if aapp.Privileged() {
 		appDir = "priv-app"
 	}
-	dirInApex = filepath.Join(appDir, pkgName)
-	fileToCopy = app.OutputFile()
-	return
-}
-
-func getCopyManifestForAndroidAppImport(app *java.AndroidAppImport, pkgName string) (fileToCopy android.Path, dirInApex string) {
-	appDir := "app"
-	if app.Privileged() {
-		appDir = "priv-app"
-	}
-	dirInApex = filepath.Join(appDir, pkgName)
-	fileToCopy = app.OutputFile()
-	return
+	dirInApex := filepath.Join(appDir, pkgName)
+	fileToCopy := aapp.OutputFile()
+	return newApexFile(fileToCopy, aapp.Name(), dirInApex, app, aapp)
 }
 
 // Context "decorator", overriding the InstallBypassMake method to always reply `true`.
@@ -877,8 +897,6 @@
 }
 
 func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	filesInfo := []apexFile{}
-
 	buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
 	switch a.properties.ApexType {
 	case imageApex:
@@ -939,68 +957,67 @@
 		providedNativeSharedLibs = append(providedNativeSharedLibs, other.properties.Native_shared_libs...)
 	})
 
+	var filesInfo []apexFile
 	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
 		depTag := ctx.OtherModuleDependencyTag(child)
 		depName := ctx.OtherModuleName(child)
-		if _, ok := parent.(*apexBundle); ok {
-			// direct dependencies
+		if _, isDirectDep := parent.(*apexBundle); isDirectDep {
 			switch depTag {
 			case sharedLibTag:
 				if cc, ok := child.(*cc.Module); ok {
 					if cc.HasStubsVariants() {
 						provideNativeLibs = append(provideNativeLibs, cc.OutputFile().Path().Base())
 					}
-					fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, ctx.Config(), handleSpecialLibs)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
-					return true
+					filesInfo = append(filesInfo, apexFileForNativeLibrary(cc, ctx.Config(), handleSpecialLibs))
+					return true // track transitive dependencies
 				} else {
 					ctx.PropertyErrorf("native_shared_libs", "%q is not a cc_library or cc_library_shared module", depName)
 				}
 			case executableTag:
 				if cc, ok := child.(*cc.Module); ok {
-					fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeExecutable, cc, cc.Symlinks()})
-					return true
+					filesInfo = append(filesInfo, apexFileForExecutable(cc))
+					return true // track transitive dependencies
 				} else if sh, ok := child.(*android.ShBinary); ok {
-					fileToCopy, dirInApex := getCopyManifestForShBinary(sh)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, shBinary, sh, sh.Symlinks()})
+					filesInfo = append(filesInfo, apexFileForShBinary(sh))
 				} else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
-					fileToCopy, dirInApex := getCopyManifestForPyBinary(py)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, pyBinary, py, nil})
+					filesInfo = append(filesInfo, apexFileForPyBinary(py))
 				} else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
-					fileToCopy, dirInApex := getCopyManifestForGoBinary(ctx, gb)
-					// NB: Since go binaries are static we don't need the module for anything here, which is
-					// good since the go tool is a blueprint.Module not an android.Module like we would
-					// normally use.
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, goBinary, nil, nil})
+					filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
 				} else {
 					ctx.PropertyErrorf("binaries", "%q is neither cc_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
 				}
 			case javaLibTag:
 				if javaLib, ok := child.(*java.Library); ok {
-					fileToCopy, dirInApex := getCopyManifestForJavaLibrary(javaLib)
-					if fileToCopy == nil {
+					af := apexFileForJavaLibrary(javaLib)
+					if !af.Ok() {
 						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
 					} else {
-						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, javaSharedLib, javaLib, nil})
+						filesInfo = append(filesInfo, af)
+						return true // track transitive dependencies
 					}
-					return true
 				} else if javaLib, ok := child.(*java.Import); ok {
-					fileToCopy, dirInApex := getCopyManifestForPrebuiltJavaLibrary(javaLib)
-					if fileToCopy == nil {
+					af := apexFileForPrebuiltJavaLibrary(javaLib)
+					if !af.Ok() {
 						ctx.PropertyErrorf("java_libs", "%q does not have a jar output", depName)
 					} else {
-						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, javaSharedLib, javaLib, nil})
+						filesInfo = append(filesInfo, af)
 					}
-					return true
 				} else {
 					ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
 				}
+			case androidAppTag:
+				pkgName := ctx.DeviceConfig().OverridePackageNameFor(depName)
+				if ap, ok := child.(*java.AndroidApp); ok {
+					filesInfo = append(filesInfo, apexFileForAndroidApp(ap, pkgName))
+					return true // track transitive dependencies
+				} else if ap, ok := child.(*java.AndroidAppImport); ok {
+					filesInfo = append(filesInfo, apexFileForAndroidApp(ap, pkgName))
+				} else {
+					ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
+				}
 			case prebuiltTag:
 				if prebuilt, ok := child.(android.PrebuiltEtcModule); ok {
-					fileToCopy, dirInApex := getCopyManifestForPrebuiltEtc(prebuilt)
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, etc, prebuilt, nil})
-					return true
+					filesInfo = append(filesInfo, apexFileForPrebuiltEtc(prebuilt, depName))
 				} else {
 					ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
 				}
@@ -1016,10 +1033,10 @@
 						return true
 					} else {
 						// Single-output test module (where `test_per_src: false`).
-						fileToCopy, dirInApex := getCopyManifestForExecutable(ccTest)
-						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeTest, ccTest, nil})
+						af := apexFileForExecutable(ccTest)
+						af.class = nativeTest
+						filesInfo = append(filesInfo, af)
 					}
-					return true
 				} else {
 					ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
 				}
@@ -1027,15 +1044,14 @@
 				if key, ok := child.(*apexKey); ok {
 					a.private_key_file = key.private_key_file
 					a.public_key_file = key.public_key_file
-					return false
 				} else {
 					ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
 				}
+				return false
 			case certificateTag:
 				if dep, ok := child.(*java.AndroidAppCertificate); ok {
 					a.container_certificate_file = dep.Certificate.Pem
 					a.container_private_key_file = dep.Certificate.Key
-					return false
 				} else {
 					ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
 				}
@@ -1045,17 +1061,6 @@
 				if prebuilt, ok := child.(*Prebuilt); ok && prebuilt.isForceDisabled() {
 					a.prebuiltFileToDelete = prebuilt.InstallFilename()
 				}
-			case androidAppTag:
-				if ap, ok := child.(*java.AndroidApp); ok {
-					fileToCopy, dirInApex := getCopyManifestForAndroidApp(ap, ctx.DeviceConfig().OverridePackageNameFor(depName))
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, app, ap, nil})
-					return true
-				} else if ap, ok := child.(*java.AndroidAppImport); ok {
-					fileToCopy, dirInApex := getCopyManifestForAndroidAppImport(ap, ctx.DeviceConfig().OverridePackageNameFor(depName))
-					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, app, ap, nil})
-				} else {
-					ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
-				}
 			}
 		} else if !a.vndkApex {
 			// indirect dependencies
@@ -1084,23 +1089,26 @@
 							// Don't track further
 							return false
 						}
-						fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, ctx.Config(), handleSpecialLibs)
-						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
-						return true
+						af := apexFileForNativeLibrary(cc, ctx.Config(), handleSpecialLibs)
+						af.transitiveDep = true
+						filesInfo = append(filesInfo, af)
+						return true // track transitive dependencies
 					}
 				} else if cc.IsTestPerSrcDepTag(depTag) {
 					if cc, ok := child.(*cc.Module); ok {
-						fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
+						af := apexFileForExecutable(cc)
 						// Handle modules created as `test_per_src` variations of a single test module:
 						// use the name of the generated test binary (`fileToCopy`) instead of the name
 						// of the original test module (`depName`, shared by all `test_per_src`
 						// variations of that module).
-						moduleName := filepath.Base(fileToCopy.String())
-						filesInfo = append(filesInfo, apexFile{fileToCopy, moduleName, dirInApex, nativeTest, cc, nil})
-						return true
+						af.moduleName = filepath.Base(af.builtFile.String())
+						af.transitiveDep = true
+						filesInfo = append(filesInfo, af)
+						return true // track transitive dependencies
 					}
 				} else if java.IsJniDepTag(depTag) {
 					// Do nothing for JNI dep. JNI libraries are always embedded in APK-in-APEX.
+					return true
 				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
 					ctx.ModuleErrorf("unexpected tag %q for indirect dependency %q", depTag, depName)
 				}
@@ -1117,7 +1125,8 @@
 			dirInApex := filepath.Join("javalib", arch.String())
 			for _, f := range files {
 				localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
-				filesInfo = append(filesInfo, apexFile{f, localModule, dirInApex, etc, nil, nil})
+				af := newApexFile(f, localModule, dirInApex, etc, nil)
+				filesInfo = append(filesInfo, af)
 			}
 		}
 	}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index a41f914..509d760 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -2994,10 +2994,11 @@
 	var builder strings.Builder
 	data.Custom(&builder, name, "TARGET_", "", data)
 	androidMk := builder.String()
-	ensureContains(t, androidMk, "LOCAL_MODULE := app.override_myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
 }
diff --git a/apex/builder.go b/apex/builder.go
index 70f3e1a..f199bd4 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -501,8 +501,8 @@
 	if a.installable() {
 		// For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
 		// with other ordinary files.
-		a.filesInfo = append(a.filesInfo, apexFile{a.manifestJsonOut, "apex_manifest.json." + a.Name() + a.suffix, ".", etc, nil, nil})
-		a.filesInfo = append(a.filesInfo, apexFile{a.manifestPbOut, "apex_manifest.pb." + a.Name() + a.suffix, ".", etc, nil, nil})
+		a.filesInfo = append(a.filesInfo, newApexFile(a.manifestJsonOut, "apex_manifest.json."+a.Name()+a.suffix, ".", etc, nil))
+		a.filesInfo = append(a.filesInfo, newApexFile(a.manifestPbOut, "apex_manifest.pb."+a.Name()+a.suffix, ".", etc, nil))
 
 		// rename to apex_pubkey
 		copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
@@ -511,7 +511,7 @@
 			Input:  a.public_key_file,
 			Output: copiedPubkey,
 		})
-		a.filesInfo = append(a.filesInfo, apexFile{copiedPubkey, "apex_pubkey." + a.Name() + a.suffix, ".", etc, nil, nil})
+		a.filesInfo = append(a.filesInfo, newApexFile(copiedPubkey, "apex_pubkey."+a.Name()+a.suffix, ".", etc, nil))
 
 		if a.properties.ApexType == flattenedApex {
 			apexName := proptools.StringDefault(a.properties.Apex_name, a.Name())
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 55bdd1c..b4082d3 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -39,7 +39,16 @@
 
 	hwasanCflags = []string{"-fno-omit-frame-pointer", "-Wno-frame-larger-than=",
 		"-fsanitize-hwaddress-abi=platform",
-		"-fno-experimental-new-pass-manager"}
+		"-fno-experimental-new-pass-manager",
+		// The following improves debug location information
+		// availability at the cost of its accuracy. It increases
+		// the likelihood of a stack variable's frame offset
+		// to be recorded in the debug info, which is important
+		// for the quality of hwasan reports. The downside is a
+		// higher number of "optimized out" stack variables.
+		// b/112437883.
+		"-mllvm", "-instcombine-lower-dbg-declare=0",
+	}
 
 	cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
 		"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blacklist.txt"}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index f38d892..35c2b44 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -38,11 +38,10 @@
 	DisableGenerateProfile bool   // don't generate profiles
 	ProfileDir             string // directory to find profiles in
 
-	BootJars []string // modules for jars that form the boot class path
+	BootJars          []string // modules for jars that form the boot class path
+	UpdatableBootJars []string // jars within apex that form the boot class path
 
-	ArtApexJars                   []string // modules for jars that are in the ART APEX
-	ProductUpdatableBootModules   []string
-	ProductUpdatableBootLocations []string
+	ArtApexJars       []string // modules for jars that are in the ART APEX
 
 	SystemServerJars          []string // jars that form the system server
 	SystemServerApps          []string // apps that are loaded into system server
@@ -281,9 +280,8 @@
 		DisableGenerateProfile:             false,
 		ProfileDir:                         "",
 		BootJars:                           nil,
+		UpdatableBootJars:                  nil,
 		ArtApexJars:                        nil,
-		ProductUpdatableBootModules:        nil,
-		ProductUpdatableBootLocations:      nil,
 		SystemServerJars:                   nil,
 		SystemServerApps:                   nil,
 		UpdatableSystemServerJars:          nil,
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 18a38fb..f3bf2ff 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -554,6 +554,12 @@
 	return apexJarPair[0], apexJarPair[1]
 }
 
+// Expected format for apexJarValue = <apex name>:<jar name>
+func GetJarLocationFromApexJarPair(apexJarValue string) (string) {
+	apex, jar := SplitApexJarPair(apexJarValue)
+	return filepath.Join("/apex", apex, "javalib", jar + ".jar")
+}
+
 func contains(l []string, s string) bool {
 	for _, e := range l {
 		if e == s {
diff --git a/java/app_test.go b/java/app_test.go
index 7e461bc..fc8cf8e 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -399,18 +399,21 @@
 
 			android_library {
 				name: "lib",
+				sdk_version: "current",
 				resource_dirs: ["lib/res"],
 				static_libs: ["lib2"],
 			}
 
 			android_library {
 				name: "lib2",
+				sdk_version: "current",
 				resource_dirs: ["lib2/res"],
 			}
 
 			// This library has the same resources as lib (should not lead to dupe RROs)
 			android_library {
 				name: "lib3",
+				sdk_version: "current",
 				resource_dirs: ["lib/res"]
 			}
 		`
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 4747c64..4dd7cfe 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -78,9 +78,8 @@
 				filepath.Join("/system/framework", m+".jar"))
 		}
 		for _, m := range global.UpdatableSystemServerJars {
-			apex, jar := dexpreopt.SplitApexJarPair(m)
 			systemServerClasspathLocations = append(systemServerClasspathLocations,
-				filepath.Join("/apex", apex, "javalib", jar+".jar"))
+				dexpreopt.GetJarLocationFromApexJarPair(m))
 		}
 		return systemServerClasspathLocations
 	})
@@ -111,6 +110,15 @@
 	return moduleName
 }
 
+func getJarsFromApexJarPairs(apexJarPairs []string) []string {
+	modules := make([]string, len(apexJarPairs))
+	for i, p := range apexJarPairs {
+		_, jar := dexpreopt.SplitApexJarPair(p)
+		modules[i] = jar
+	}
+	return modules
+}
+
 // Construct a variant of the global config for dexpreopted bootclasspath jars. The variants differ
 // in the list of input jars (libcore, framework, or both), in the naming scheme for the dexpreopt
 // files (ART recognizes "apex" names as special), and whether to include a zip archive.
@@ -134,7 +142,7 @@
 		}
 
 		if !artApexJarsOnly {
-			nonFrameworkModules := concat(artModules, global.ProductUpdatableBootModules)
+			nonFrameworkModules := concat(artModules, getJarsFromApexJarPairs(global.UpdatableBootJars))
 			frameworkModules := android.RemoveListFromList(global.BootJars, nonFrameworkModules)
 			imageModules = concat(imageModules, frameworkModules)
 
@@ -218,7 +226,11 @@
 	return ctx.Config().OnceStringSlice(defaultBootclasspathKey, func() []string {
 		global := dexpreoptGlobalConfig(ctx)
 		image := defaultBootImageConfig(ctx)
-		bootclasspath := append(copyOf(image.dexLocations), global.ProductUpdatableBootLocations...)
+		updatableBootclasspath := make([]string, len(global.UpdatableBootJars))
+		for i, p := range global.UpdatableBootJars {
+			updatableBootclasspath[i] = dexpreopt.GetJarLocationFromApexJarPair(p)
+		}
+		bootclasspath := append(copyOf(image.dexLocations), updatableBootclasspath...)
 		return bootclasspath
 	})
 }
diff --git a/java/java.go b/java/java.go
index 50fa135..9c0fcba 100644
--- a/java/java.go
+++ b/java/java.go
@@ -689,7 +689,12 @@
 	javaPlatform
 )
 
-func getLinkType(m *Module, name string) (ret linkType, stubs bool) {
+type linkTypeContext interface {
+	android.Module
+	getLinkType(name string) (ret linkType, stubs bool)
+}
+
+func (m *Module) getLinkType(name string) (ret linkType, stubs bool) {
 	ver := m.sdkVersion()
 	switch {
 	case name == "core.current.stubs" || name == "core.platform.api.stubs" ||
@@ -720,16 +725,16 @@
 	}
 }
 
-func checkLinkType(ctx android.ModuleContext, from *Module, to *Library, tag dependencyTag) {
+func checkLinkType(ctx android.ModuleContext, from *Module, to linkTypeContext, tag dependencyTag) {
 	if ctx.Host() {
 		return
 	}
 
-	myLinkType, stubs := getLinkType(from, ctx.ModuleName())
+	myLinkType, stubs := from.getLinkType(ctx.ModuleName())
 	if stubs {
 		return
 	}
-	otherLinkType, _ := getLinkType(&to.Module, ctx.OtherModuleName(to))
+	otherLinkType, _ := to.getLinkType(ctx.OtherModuleName(to))
 	commonMessage := "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source."
 
 	switch myLinkType {
@@ -786,11 +791,14 @@
 			// Handled by AndroidApp.collectAppDeps
 			return
 		}
-
-		if to, ok := module.(*Library); ok {
-			switch tag {
-			case bootClasspathTag, libTag, staticLibTag:
-				checkLinkType(ctx, j, to, tag.(dependencyTag))
+		switch module.(type) {
+		case *Library:
+		case *AndroidLibrary:
+			if to, ok := module.(linkTypeContext); ok {
+				switch tag {
+				case bootClasspathTag, libTag, staticLibTag:
+					checkLinkType(ctx, j, to, tag.(dependencyTag))
+				}
 			}
 		}
 		switch dep := module.(type) {