Merge "Capture the build command that executed the build system."
diff --git a/Android.bp b/Android.bp
index 3075d67..4d69877 100644
--- a/Android.bp
+++ b/Android.bp
@@ -47,6 +47,7 @@
     defaults: ["linux_bionic_supported"],
     vendor_available: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
 
@@ -94,6 +95,7 @@
     defaults: ["linux_bionic_supported"],
     vendor_available: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
     sdk_version: "current",
diff --git a/android/apex.go b/android/apex.go
index 3039e79..e70ec4f 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -67,6 +67,15 @@
 	return i.ApexVariationName == ""
 }
 
+func (i ApexInfo) InApex(apex string) bool {
+	for _, a := range i.InApexes {
+		if a == apex {
+			return true
+		}
+	}
+	return false
+}
+
 // ApexTestForInfo stores the contents of APEXes for which this module is a test and thus has
 // access to APEX internals.
 type ApexTestForInfo struct {
diff --git a/android/config.go b/android/config.go
index dbae7f7..a499057 100644
--- a/android/config.go
+++ b/android/config.go
@@ -793,6 +793,11 @@
 	return Bool(c.productVariables.Always_use_prebuilt_sdks)
 }
 
+// Returns true if the boot jars check should be skipped.
+func (c *config) SkipBootJarsCheck() bool {
+	return Bool(c.productVariables.Skip_boot_jars_check)
+}
+
 func (c *config) Fuchsia() bool {
 	return Bool(c.productVariables.Fuchsia)
 }
@@ -1341,6 +1346,11 @@
 	return l.jars[idx]
 }
 
+// Apex component of idx-th pair on the list.
+func (l *ConfiguredJarList) Apex(idx int) string {
+	return l.apexes[idx]
+}
+
 // If the list contains a pair with the given jar.
 func (l *ConfiguredJarList) ContainsJar(jar string) bool {
 	return InList(jar, l.jars)
@@ -1538,3 +1548,11 @@
 		return list
 	}).([]string)
 }
+
+func (c *config) NonUpdatableBootJars() ConfiguredJarList {
+	return c.productVariables.BootJars
+}
+
+func (c *config) UpdatableBootJars() ConfiguredJarList {
+	return c.productVariables.UpdatableBootJars
+}
diff --git a/android/paths.go b/android/paths.go
index 2fb5f25..b13979d 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1557,6 +1557,72 @@
 	}
 }
 
+type testModuleInstallPathContext struct {
+	baseModuleContext
+
+	inData          bool
+	inTestcases     bool
+	inSanitizerDir  bool
+	inRamdisk       bool
+	inVendorRamdisk bool
+	inRecovery      bool
+	inRoot          bool
+	forceOS         *OsType
+	forceArch       *ArchType
+}
+
+func (m testModuleInstallPathContext) Config() Config {
+	return m.baseModuleContext.config
+}
+
+func (testModuleInstallPathContext) AddNinjaFileDeps(deps ...string) {}
+
+func (m testModuleInstallPathContext) InstallInData() bool {
+	return m.inData
+}
+
+func (m testModuleInstallPathContext) InstallInTestcases() bool {
+	return m.inTestcases
+}
+
+func (m testModuleInstallPathContext) InstallInSanitizerDir() bool {
+	return m.inSanitizerDir
+}
+
+func (m testModuleInstallPathContext) InstallInRamdisk() bool {
+	return m.inRamdisk
+}
+
+func (m testModuleInstallPathContext) InstallInVendorRamdisk() bool {
+	return m.inVendorRamdisk
+}
+
+func (m testModuleInstallPathContext) InstallInRecovery() bool {
+	return m.inRecovery
+}
+
+func (m testModuleInstallPathContext) InstallInRoot() bool {
+	return m.inRoot
+}
+
+func (m testModuleInstallPathContext) InstallBypassMake() bool {
+	return false
+}
+
+func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
+	return m.forceOS, m.forceArch
+}
+
+// Construct a minimal ModuleInstallPathContext for testing. Note that baseModuleContext is
+// default-initialized, which leaves blueprint.baseModuleContext set to nil, so methods that are
+// delegated to it will panic.
+func ModuleInstallPathContextForTesting(config Config) ModuleInstallPathContext {
+	ctx := &testModuleInstallPathContext{}
+	ctx.config = config
+	ctx.os = Android
+	return ctx
+}
+
 // Rel performs the same function as filepath.Rel, but reports errors to a PathContext, and reports an error if
 // targetPath is not inside basePath.
 func Rel(ctx PathContext, basePath string, targetPath string) string {
diff --git a/android/paths_test.go b/android/paths_test.go
index 51e4ba5..e7fd763 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -197,62 +197,6 @@
 	}
 }
 
-type moduleInstallPathContextImpl struct {
-	baseModuleContext
-
-	inData          bool
-	inTestcases     bool
-	inSanitizerDir  bool
-	inRamdisk       bool
-	inVendorRamdisk bool
-	inRecovery      bool
-	inRoot          bool
-	forceOS         *OsType
-	forceArch       *ArchType
-}
-
-func (m moduleInstallPathContextImpl) Config() Config {
-	return m.baseModuleContext.config
-}
-
-func (moduleInstallPathContextImpl) AddNinjaFileDeps(deps ...string) {}
-
-func (m moduleInstallPathContextImpl) InstallInData() bool {
-	return m.inData
-}
-
-func (m moduleInstallPathContextImpl) InstallInTestcases() bool {
-	return m.inTestcases
-}
-
-func (m moduleInstallPathContextImpl) InstallInSanitizerDir() bool {
-	return m.inSanitizerDir
-}
-
-func (m moduleInstallPathContextImpl) InstallInRamdisk() bool {
-	return m.inRamdisk
-}
-
-func (m moduleInstallPathContextImpl) InstallInVendorRamdisk() bool {
-	return m.inVendorRamdisk
-}
-
-func (m moduleInstallPathContextImpl) InstallInRecovery() bool {
-	return m.inRecovery
-}
-
-func (m moduleInstallPathContextImpl) InstallInRoot() bool {
-	return m.inRoot
-}
-
-func (m moduleInstallPathContextImpl) InstallBypassMake() bool {
-	return false
-}
-
-func (m moduleInstallPathContextImpl) InstallForceOS() (*OsType, *ArchType) {
-	return m.forceOS, m.forceArch
-}
-
 func pathTestConfig(buildDir string) Config {
 	return TestConfig(buildDir, nil, "", nil)
 }
@@ -265,14 +209,14 @@
 
 	testCases := []struct {
 		name         string
-		ctx          *moduleInstallPathContextImpl
+		ctx          *testModuleInstallPathContext
 		in           []string
 		out          string
 		partitionDir string
 	}{
 		{
 			name: "host binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     hostTarget.Os,
 					target: hostTarget,
@@ -285,7 +229,7 @@
 
 		{
 			name: "system binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -297,7 +241,7 @@
 		},
 		{
 			name: "vendor binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -312,7 +256,7 @@
 		},
 		{
 			name: "odm binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -327,7 +271,7 @@
 		},
 		{
 			name: "product binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -342,7 +286,7 @@
 		},
 		{
 			name: "system_ext binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -357,7 +301,7 @@
 		},
 		{
 			name: "root binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -370,7 +314,7 @@
 		},
 		{
 			name: "recovery binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -383,7 +327,7 @@
 		},
 		{
 			name: "recovery root binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -398,7 +342,7 @@
 
 		{
 			name: "system native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -411,7 +355,7 @@
 		},
 		{
 			name: "vendor native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -427,7 +371,7 @@
 		},
 		{
 			name: "odm native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -443,7 +387,7 @@
 		},
 		{
 			name: "product native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -460,7 +404,7 @@
 
 		{
 			name: "system_ext native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -477,7 +421,7 @@
 
 		{
 			name: "sanitized system binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -490,7 +434,7 @@
 		},
 		{
 			name: "sanitized vendor binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -506,7 +450,7 @@
 		},
 		{
 			name: "sanitized odm binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -522,7 +466,7 @@
 		},
 		{
 			name: "sanitized product binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -539,7 +483,7 @@
 
 		{
 			name: "sanitized system_ext binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -556,7 +500,7 @@
 
 		{
 			name: "sanitized system native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -570,7 +514,7 @@
 		},
 		{
 			name: "sanitized vendor native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -587,7 +531,7 @@
 		},
 		{
 			name: "sanitized odm native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -604,7 +548,7 @@
 		},
 		{
 			name: "sanitized product native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -621,7 +565,7 @@
 		},
 		{
 			name: "sanitized system_ext native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -637,7 +581,7 @@
 			partitionDir: "target/product/test_device/data/asan/data",
 		}, {
 			name: "device testcases",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -649,7 +593,7 @@
 			partitionDir: "target/product/test_device/testcases",
 		}, {
 			name: "host testcases",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     hostTarget.Os,
 					target: hostTarget,
@@ -661,7 +605,7 @@
 			partitionDir: "host/linux-x86/testcases",
 		}, {
 			name: "forced host testcases",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -697,7 +641,7 @@
 	testConfig := pathTestConfig("")
 	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
 
-	ctx := &moduleInstallPathContextImpl{
+	ctx := &testModuleInstallPathContext{
 		baseModuleContext: baseModuleContext{
 			os:     deviceTarget.Os,
 			target: deviceTarget,
diff --git a/android/variable.go b/android/variable.go
index 7999f0f..a9a9c87 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -224,6 +224,7 @@
 	Unbundled_build              *bool `json:",omitempty"`
 	Unbundled_build_apps         *bool `json:",omitempty"`
 	Always_use_prebuilt_sdks     *bool `json:",omitempty"`
+	Skip_boot_jars_check         *bool `json:",omitempty"`
 	Malloc_not_svelte            *bool `json:",omitempty"`
 	Malloc_zero_contents         *bool `json:",omitempty"`
 	Malloc_pattern_fill_contents *bool `json:",omitempty"`
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
index b3cf8d5..3fa3a10 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -422,6 +422,7 @@
 ndk_libc++_static(minSdkVersion:16)
 ndk_libc++abi(minSdkVersion:(no version))
 ndk_libc++abi(minSdkVersion:16)
+ndk_libunwind(minSdkVersion:16)
 net-utils-framework-common(minSdkVersion:current)
 netd_aidl_interface-unstable-java(minSdkVersion:29)
 netd_event_listener_interface-ndk_platform(minSdkVersion:29)
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index fcf6069..fa6b326 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -383,12 +383,25 @@
 }
 
 func (p *nativeLibInfoProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
+	addOutputFile := true
 	ccModule := variant.(*Module)
 
-	// If the library has some link types then it produces an output binary file, otherwise it
-	// is header only.
-	if !p.memberType.noOutputFiles {
-		p.outputFile = getRequiredMemberOutputFile(ctx, ccModule)
+	if s := ccModule.sanitize; s != nil {
+		// We currently do not capture sanitizer flags for libs with sanitizers
+		// enabled, because they may vary among variants that cannot be represented
+		// in the input blueprint files. In particular, sanitizerDepsMutator enables
+		// various sanitizers on dependencies, but in many cases only on static
+		// ones, and we cannot specify sanitizer flags at the link type level (i.e.
+		// in StaticOrSharedProperties).
+		if s.isUnsanitizedVariant() {
+			// This still captures explicitly disabled sanitizers, which may be
+			// necessary to avoid cyclic dependencies.
+			p.Sanitize = s.Properties.Sanitize
+		} else {
+			// Do not add the output file to the snapshot if we don't represent it
+			// properly.
+			addOutputFile = false
+		}
 	}
 
 	exportedInfo := ctx.SdkModuleContext().OtherModuleProvider(variant, FlagExporterInfoProvider).(FlagExporterInfo)
@@ -431,8 +444,8 @@
 		p.StubsVersions = ccModule.AllStubsVersions()
 	}
 
-	if ccModule.sanitize != nil {
-		p.Sanitize = ccModule.sanitize.Properties.Sanitize
+	if !p.memberType.noOutputFiles && addOutputFile {
+		p.outputFile = getRequiredMemberOutputFile(ctx, ccModule)
 	}
 }
 
diff --git a/dexpreopt/Android.bp b/dexpreopt/Android.bp
index b8f7ea6..35f90df 100644
--- a/dexpreopt/Android.bp
+++ b/dexpreopt/Android.bp
@@ -2,11 +2,13 @@
     name: "soong-dexpreopt",
     pkgPath: "android/soong/dexpreopt",
     srcs: [
+        "class_loader_context.go",
         "config.go",
         "dexpreopt.go",
         "testing.go",
     ],
     testSrcs: [
+        "class_loader_context_test.go",
         "dexpreopt_test.go",
     ],
     deps: [
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
new file mode 100644
index 0000000..6d77812
--- /dev/null
+++ b/dexpreopt/class_loader_context.go
@@ -0,0 +1,351 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dexpreopt
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"android/soong/android"
+)
+
+// These libs are added as <uses-library> dependencies for apps if the targetSdkVersion in the
+// app manifest is less than the specified version. This is needed because these libraries haven't
+// existed prior to certain SDK version, but classes in them were in bootclasspath jars, etc.
+// Some of the compatibility libraries are optional (their <uses-library> tag has "required=false"),
+// so that if this library is missing this in not a build or run-time error.
+var OrgApacheHttpLegacy = "org.apache.http.legacy"
+var AndroidTestBase = "android.test.base"
+var AndroidTestMock = "android.test.mock"
+var AndroidHidlBase = "android.hidl.base-V1.0-java"
+var AndroidHidlManager = "android.hidl.manager-V1.0-java"
+
+var OptionalCompatUsesLibs28 = []string{
+	OrgApacheHttpLegacy,
+}
+var OptionalCompatUsesLibs30 = []string{
+	AndroidTestBase,
+	AndroidTestMock,
+}
+var CompatUsesLibs29 = []string{
+	AndroidHidlBase,
+	AndroidHidlManager,
+}
+var OptionalCompatUsesLibs = append(android.CopyOf(OptionalCompatUsesLibs28), OptionalCompatUsesLibs30...)
+var CompatUsesLibs = android.CopyOf(CompatUsesLibs29)
+
+const UnknownInstallLibraryPath = "error"
+
+const AnySdkVersion int = 9999 // should go last in class loader context
+
+// LibraryPath contains paths to the library DEX jar on host and on device.
+type LibraryPath struct {
+	Host   android.Path
+	Device string
+}
+
+// LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar.
+type LibraryPaths map[string]*LibraryPath
+
+type classLoaderContext struct {
+	// Library names
+	Names []string
+
+	// The class loader context using paths in the build.
+	Host android.Paths
+
+	// The class loader context using paths as they will be on the device.
+	Target []string
+}
+
+// A map of class loader contexts for each SDK version.
+// A map entry for "any" version contains libraries that are unconditionally added to class loader
+// context. Map entries for existing versions contains libraries that were in the default classpath
+// until that API version, and should be added to class loader context if and only if the
+// targetSdkVersion in the manifest or APK is less than that API version.
+type classLoaderContextMap map[int]*classLoaderContext
+
+// Add a new library path to the map, unless a path for this library already exists.
+// If necessary, check that the build and install paths exist.
+func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleInstallPathContext, lib string,
+	hostPath, installPath android.Path, strict bool) error {
+
+	// If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
+	// not found. However, this is likely to result is disabling dexpreopt, as it won't be
+	// possible to construct class loader context without on-host and on-device library paths.
+	strict = strict && !ctx.Config().AllowMissingDependencies()
+
+	if hostPath == nil && strict {
+		return fmt.Errorf("unknown build path to <uses-library> '%s'", lib)
+	}
+
+	if installPath == nil {
+		if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
+			// Assume that compatibility libraries are installed in /system/framework.
+			installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
+		} else if strict {
+			return fmt.Errorf("unknown install path to <uses-library> '%s'", lib)
+		}
+	}
+
+	// Add a library only if the build and install path to it is known.
+	if _, present := libPaths[lib]; !present {
+		var devicePath string
+		if installPath != nil {
+			devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
+		} else {
+			// For some stub libraries the only known thing is the name of their implementation
+			// library, but the library itself is unavailable (missing or part of a prebuilt). In
+			// such cases we still need to add the library to <uses-library> tags in the manifest,
+			// but we cannot use if for dexpreopt.
+			devicePath = UnknownInstallLibraryPath
+		}
+		libPaths[lib] = &LibraryPath{hostPath, devicePath}
+	}
+	return nil
+}
+
+// Wrapper around addLibraryPath that does error reporting.
+func (libPaths LibraryPaths) addLibraryPathOrReportError(ctx android.ModuleInstallPathContext, lib string,
+	hostPath, installPath android.Path, strict bool) {
+
+	err := libPaths.addLibraryPath(ctx, lib, hostPath, installPath, strict)
+	if err != nil {
+		android.ReportPathErrorf(ctx, err.Error())
+	}
+}
+
+// Add a new library path to the map. Enforce checks that the library paths exist.
+func (libPaths LibraryPaths) AddLibraryPath(ctx android.ModuleInstallPathContext, lib string, hostPath, installPath android.Path) {
+	libPaths.addLibraryPathOrReportError(ctx, lib, hostPath, installPath, true)
+}
+
+// Add a new library path to the map, if the library exists (name is not nil).
+// Don't enforce checks that the library paths exist. Some libraries may be missing from the build,
+// but their names still need to be added to <uses-library> tags in the manifest.
+func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.ModuleInstallPathContext, lib *string, hostPath, installPath android.Path) {
+	if lib != nil {
+		libPaths.addLibraryPathOrReportError(ctx, *lib, hostPath, installPath, false)
+	}
+}
+
+// Add library paths from the second map to the first map (do not override existing entries).
+func (libPaths LibraryPaths) AddLibraryPaths(otherPaths LibraryPaths) {
+	for lib, path := range otherPaths {
+		if _, present := libPaths[lib]; !present {
+			libPaths[lib] = path
+		}
+	}
+}
+
+func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
+	if _, ok := m[sdkVer]; !ok {
+		m[sdkVer] = &classLoaderContext{}
+	}
+	return m[sdkVer]
+}
+
+func (clc *classLoaderContext) addLib(lib string, hostPath android.Path, targetPath string) {
+	clc.Names = append(clc.Names, lib)
+	clc.Host = append(clc.Host, hostPath)
+	clc.Target = append(clc.Target, targetPath)
+}
+
+func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, module *ModuleConfig,
+	libs ...string) (bool, error) {
+
+	clc := m.getValue(sdkVer)
+	for _, lib := range libs {
+		if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath {
+			clc.addLib(lib, p.Host, p.Device)
+		} else {
+			if sdkVer == AnySdkVersion {
+				// Fail the build if dexpreopt doesn't know paths to one of the <uses-library>
+				// dependencies. In the future we may need to relax this and just disable dexpreopt.
+				return false, fmt.Errorf("dexpreopt cannot find path for <uses-library> '%s'", lib)
+			} else {
+				// No error for compatibility libraries, as Soong doesn't know if they are needed
+				// (this depends on the targetSdkVersion in the manifest).
+				return false, nil
+			}
+		}
+	}
+	return true, nil
+}
+
+func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
+	clc := m.getValue(sdkVer)
+	for _, lib := range libs {
+		clc.addLib(lib, SystemServerDexJarHostPath(ctx, lib), filepath.Join("/system/framework", lib+".jar"))
+	}
+}
+
+func (m classLoaderContextMap) usesLibs() []string {
+	if clc, ok := m[AnySdkVersion]; ok {
+		return clc.Names
+	}
+	return nil
+}
+
+// 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.
+//
+// 3. 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, error) {
+	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 ok, err := classLoaderContexts.addLibs(ctx, AnySdkVersion, module, usesLibs...); !ok {
+			return nil, err
+		}
+
+		// Conditional class loader context for API version < 28.
+		const httpLegacy = "org.apache.http.legacy"
+		if ok, err := classLoaderContexts.addLibs(ctx, 28, module, httpLegacy); !ok {
+			return nil, err
+		}
+
+		// Conditional class loader context for API version < 29.
+		usesLibs29 := []string{
+			"android.hidl.base-V1.0-java",
+			"android.hidl.manager-V1.0-java",
+		}
+		if ok, err := classLoaderContexts.addLibs(ctx, 29, module, usesLibs29...); !ok {
+			return nil, err
+		}
+
+		// Conditional class loader context for API version < 30.
+		if ok, err := classLoaderContexts.addLibs(ctx, 30, module, OptionalCompatUsesLibs30...); !ok {
+			return nil, err
+		}
+
+	} 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 &.
+	}
+
+	fixConditionalClassLoaderContext(classLoaderContexts)
+
+	return &classLoaderContexts, nil
+}
+
+// Now that the full unconditional context is known, reconstruct conditional context.
+// Apply filters for individual libraries, mirroring what the PackageManager does when it
+// constructs class loader context on device.
+//
+// TODO(b/132357300):
+//   - remove android.hidl.manager and android.hidl.base unless the app is a system app.
+//
+func fixConditionalClassLoaderContext(clcMap classLoaderContextMap) {
+	usesLibs := clcMap.usesLibs()
+
+	for sdkVer, clc := range clcMap {
+		if sdkVer == AnySdkVersion {
+			continue
+		}
+		clcMap[sdkVer] = &classLoaderContext{}
+		for i, lib := range clc.Names {
+			if android.InList(lib, usesLibs) {
+				// skip compatibility libraries that are already included in unconditional context
+			} else if lib == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
+				// android.test.mock is only needed as a compatibility library (in conditional class
+				// loader context) if android.test.runner is used, otherwise skip it
+			} else {
+				clcMap[sdkVer].addLib(lib, clc.Host[i], clc.Target[i])
+			}
+		}
+	}
+}
+
+// Return the class loader context as a string and a slice of build paths for all dependencies.
+func computeClassLoaderContext(ctx android.PathContext, clcMap classLoaderContextMap) (clcStr string, paths android.Paths) {
+	for _, ver := range android.SortedIntKeys(clcMap) {
+		clc := clcMap.getValue(ver)
+
+		clcLen := len(clc.Names)
+		if clcLen != len(clc.Host) || clcLen != len(clc.Target) {
+			android.ReportPathErrorf(ctx, "ill-formed class loader context")
+		}
+
+		var hostClc, targetClc []string
+		var hostPaths android.Paths
+
+		for i := 0; i < clcLen; i++ {
+			hostStr := "PCL[" + clc.Host[i].String() + "]"
+			targetStr := "PCL[" + clc.Target[i] + "]"
+
+			hostClc = append(hostClc, hostStr)
+			targetClc = append(targetClc, targetStr)
+			hostPaths = append(hostPaths, clc.Host[i])
+		}
+
+		if hostPaths != nil {
+			sdkVerStr := fmt.Sprintf("%d", ver)
+			if ver == AnySdkVersion {
+				sdkVerStr = "any" // a special keyword that means any SDK version
+			}
+			clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, strings.Join(hostClc, "#"))
+			clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, strings.Join(targetClc, "#"))
+			paths = append(paths, hostPaths...)
+		}
+	}
+
+	return clcStr, paths
+}
+
+type jsonLibraryPath struct {
+	Host   string
+	Device string
+}
+
+type jsonLibraryPaths map[string]jsonLibraryPath
+
+// convert JSON map of library paths to LibraryPaths
+func constructLibraryPaths(ctx android.PathContext, paths jsonLibraryPaths) LibraryPaths {
+	m := LibraryPaths{}
+	for lib, path := range paths {
+		m[lib] = &LibraryPath{
+			constructPath(ctx, path.Host),
+			path.Device,
+		}
+	}
+	return m
+}
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
new file mode 100644
index 0000000..51c1a0a
--- /dev/null
+++ b/dexpreopt/class_loader_context_test.go
@@ -0,0 +1,225 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dexpreopt
+
+// This file contains unit tests for class loader context structure.
+// For class loader context tests involving .bp files, see TestUsesLibraries in java package.
+
+import (
+	"reflect"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestCLC(t *testing.T) {
+	// Construct class loader context with the following structure:
+	// .
+	// ├── 29
+	// │   ├── android.hidl.manager
+	// │   └── android.hidl.base
+	// │
+	// └── any
+	//     ├── a
+	//     ├── b
+	//     ├── c
+	//     ├── d
+	//     ├── a2
+	//     ├── b2
+	//     ├── c2
+	//     ├── a1
+	//     ├── b1
+	//     ├── f
+	//     ├── a3
+	//     └── b3
+	//
+	ctx := testContext()
+
+	lp := make(LibraryPaths)
+
+	lp.AddLibraryPath(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
+	lp.AddLibraryPath(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
+
+	// "Maybe" variant in the good case: add as usual.
+	c := "c"
+	lp.MaybeAddLibraryPath(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
+
+	// "Maybe" variant in the bad case: don't add library with unknown name, keep going.
+	lp.MaybeAddLibraryPath(ctx, nil, nil, nil)
+
+	// Add some libraries with nested subcontexts.
+
+	lp1 := make(LibraryPaths)
+	lp1.AddLibraryPath(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
+	lp1.AddLibraryPath(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
+
+	lp2 := make(LibraryPaths)
+	lp2.AddLibraryPath(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
+	lp2.AddLibraryPath(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
+	lp2.AddLibraryPath(ctx, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"))
+	lp2.AddLibraryPaths(lp1)
+
+	lp.AddLibraryPath(ctx, "d", buildPath(ctx, "d"), installPath(ctx, "d"))
+	lp.AddLibraryPaths(lp2)
+
+	lp3 := make(LibraryPaths)
+	lp3.AddLibraryPath(ctx, "f", buildPath(ctx, "f"), installPath(ctx, "f"))
+	lp3.AddLibraryPath(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
+	lp3.AddLibraryPath(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
+	lp.AddLibraryPaths(lp3)
+
+	// Compatibility libraries with unknown install paths get default paths.
+	lp.AddLibraryPath(ctx, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil)
+	lp.AddLibraryPath(ctx, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil)
+	lp.AddLibraryPath(ctx, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil)
+
+	module := testSystemModuleConfig(ctx, "test")
+	module.LibraryPaths = lp
+
+	m := make(classLoaderContextMap)
+	valid := true
+
+	ok, err := m.addLibs(ctx, AnySdkVersion, module, "a", "b", "c", "d", "a2", "b2", "c2", "a1", "b1", "f", "a3", "b3")
+	valid = valid && ok && err == nil
+
+	// Add compatibility libraries to conditional CLC for SDK level 29.
+	ok, err = m.addLibs(ctx, 29, module, AndroidHidlManager, AndroidHidlBase)
+	valid = valid && ok && err == nil
+
+	// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
+	// needed as a compatibility library if "android.test.runner" is in CLC as well.
+	ok, err = m.addLibs(ctx, 30, module, AndroidTestMock)
+	valid = valid && ok && err == nil
+
+	// When the same library is both in conditional and unconditional context, it should be removed
+	// from conditional context.
+	ok, err = m.addLibs(ctx, 42, module, "f")
+	valid = valid && ok && err == nil
+
+	fixConditionalClassLoaderContext(m)
+
+	var haveStr string
+	var havePaths android.Paths
+	var haveUsesLibs []string
+	if valid {
+		haveStr, havePaths = computeClassLoaderContext(ctx, m)
+		haveUsesLibs = m.usesLibs()
+	}
+
+	// Test that validation is successful (all paths are known).
+	t.Run("validate", func(t *testing.T) {
+		if !valid {
+			t.Errorf("invalid class loader context")
+		}
+	})
+
+	// Test that class loader context structure is correct.
+	t.Run("string", func(t *testing.T) {
+		wantStr := " --host-context-for-sdk 29 " +
+			"PCL[out/" + AndroidHidlManager + ".jar]#" +
+			"PCL[out/" + AndroidHidlBase + ".jar]" +
+			" --target-context-for-sdk 29 " +
+			"PCL[/system/framework/" + AndroidHidlManager + ".jar]#" +
+			"PCL[/system/framework/" + AndroidHidlBase + ".jar]" +
+			" --host-context-for-sdk any " +
+			"PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]#" +
+			"PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]#" +
+			"PCL[out/a1.jar]#PCL[out/b1.jar]#" +
+			"PCL[out/f.jar]#PCL[out/a3.jar]#PCL[out/b3.jar]" +
+			" --target-context-for-sdk any " +
+			"PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]#" +
+			"PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]#" +
+			"PCL[/system/a1.jar]#PCL[/system/b1.jar]#" +
+			"PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]"
+		if wantStr != haveStr {
+			t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
+		}
+	})
+
+	// Test that all expected build paths are gathered.
+	t.Run("paths", func(t *testing.T) {
+		wantPaths := []string{
+			"out/android.hidl.manager-V1.0-java.jar", "out/android.hidl.base-V1.0-java.jar",
+			"out/a.jar", "out/b.jar", "out/c.jar", "out/d.jar",
+			"out/a2.jar", "out/b2.jar", "out/c2.jar",
+			"out/a1.jar", "out/b1.jar",
+			"out/f.jar", "out/a3.jar", "out/b3.jar",
+		}
+		if !reflect.DeepEqual(wantPaths, havePaths.Strings()) {
+			t.Errorf("\nwant paths: %s\nhave paths: %s", wantPaths, havePaths)
+		}
+	})
+
+	// Test for libraries that are added by the manifest_fixer.
+	t.Run("uses libs", func(t *testing.T) {
+		wantUsesLibs := []string{"a", "b", "c", "d", "a2", "b2", "c2", "a1", "b1", "f", "a3", "b3"}
+		if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
+			t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
+		}
+	})
+}
+
+// Test that an unexpected unknown build path causes immediate error.
+func TestCLCUnknownBuildPath(t *testing.T) {
+	ctx := testContext()
+	lp := make(LibraryPaths)
+	err := lp.addLibraryPath(ctx, "a", nil, nil, true)
+	checkError(t, err, "unknown build path to <uses-library> 'a'")
+}
+
+// Test that an unexpected unknown install path causes immediate error.
+func TestCLCUnknownInstallPath(t *testing.T) {
+	ctx := testContext()
+	lp := make(LibraryPaths)
+	err := lp.addLibraryPath(ctx, "a", buildPath(ctx, "a"), nil, true)
+	checkError(t, err, "unknown install path to <uses-library> 'a'")
+}
+
+func TestCLCMaybeAdd(t *testing.T) {
+	ctx := testContext()
+
+	lp := make(LibraryPaths)
+	a := "a"
+	lp.MaybeAddLibraryPath(ctx, &a, nil, nil)
+
+	module := testSystemModuleConfig(ctx, "test")
+	module.LibraryPaths = lp
+
+	m := make(classLoaderContextMap)
+	_, err := m.addLibs(ctx, AnySdkVersion, module, "a")
+	checkError(t, err, "dexpreopt cannot find path for <uses-library> 'a'")
+}
+
+func checkError(t *testing.T, have error, want string) {
+	if have == nil {
+		t.Errorf("\nwant error: '%s'\nhave: none", want)
+	} else if msg := have.Error(); !strings.HasPrefix(msg, want) {
+		t.Errorf("\nwant error: '%s'\nhave error: '%s'\n", want, msg)
+	}
+}
+
+func testContext() android.ModuleInstallPathContext {
+	config := android.TestConfig("out", nil, "", nil)
+	return android.ModuleInstallPathContextForTesting(config)
+}
+
+func buildPath(ctx android.PathContext, lib string) android.Path {
+	return android.PathForOutput(ctx, lib+".jar")
+}
+
+func installPath(ctx android.ModuleInstallPathContext, lib string) android.InstallPath {
+	return android.PathForModuleInstall(ctx, lib+".jar")
+}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 2052847..03accc8 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -100,104 +100,6 @@
 	ConstructContext android.Path
 }
 
-// These libs are added as <uses-library> dependencies for apps if the targetSdkVersion in the
-// app manifest is less than the specified version. This is needed because these libraries haven't
-// existed prior to certain SDK version, but classes in them were in bootclasspath jars, etc.
-// Some of the compatibility libraries are optional (their <uses-library> tag has "required=false"),
-// so that if this library is missing this in not a build or run-time error.
-var OrgApacheHttpLegacy = "org.apache.http.legacy"
-var AndroidTestBase = "android.test.base"
-var AndroidTestMock = "android.test.mock"
-var AndroidHidlBase = "android.hidl.base-V1.0-java"
-var AndroidHidlManager = "android.hidl.manager-V1.0-java"
-
-var OptionalCompatUsesLibs28 = []string{
-	OrgApacheHttpLegacy,
-}
-var OptionalCompatUsesLibs30 = []string{
-	AndroidTestBase,
-	AndroidTestMock,
-}
-var CompatUsesLibs29 = []string{
-	AndroidHidlBase,
-	AndroidHidlManager,
-}
-var OptionalCompatUsesLibs = append(android.CopyOf(OptionalCompatUsesLibs28), OptionalCompatUsesLibs30...)
-var CompatUsesLibs = android.CopyOf(CompatUsesLibs29)
-
-const UnknownInstallLibraryPath = "error"
-
-// LibraryPath contains paths to the library DEX jar on host and on device.
-type LibraryPath struct {
-	Host   android.Path
-	Device string
-}
-
-// 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 library path to the map, unless a path for this library already exists.
-// If necessary, check that the build and install paths exist.
-func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleContext, lib string,
-	hostPath, installPath android.Path, strict bool) {
-
-	// If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
-	// not found. However, this is likely to result is disabling dexpreopt, as it won't be
-	// possible to construct class loader context without on-host and on-device library paths.
-	strict = strict && !ctx.Config().AllowMissingDependencies()
-
-	if hostPath == nil && strict {
-		android.ReportPathErrorf(ctx, "unknown build path to <uses-library> '%s'", lib)
-	}
-
-	if installPath == nil {
-		if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
-			// Assume that compatibility libraries are installed in /system/framework.
-			installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
-		} else if strict {
-			android.ReportPathErrorf(ctx, "unknown install path to <uses-library> '%s'", lib)
-		}
-	}
-
-	// Add a library only if the build and install path to it is known.
-	if _, present := libPaths[lib]; !present {
-		var devicePath string
-		if installPath != nil {
-			devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
-		} else {
-			// For some stub libraries the only known thing is the name of their implementation
-			// library, but the library itself is unavailable (missing or part of a prebuilt). In
-			// such cases we still need to add the library to <uses-library> tags in the manifest,
-			// but we cannot use if for dexpreopt.
-			devicePath = UnknownInstallLibraryPath
-		}
-		libPaths[lib] = &LibraryPath{hostPath, devicePath}
-	}
-}
-
-// Add a new library path to the map. Enforce checks that the library paths exist.
-func (libPaths LibraryPaths) AddLibraryPath(ctx android.ModuleContext, lib string, hostPath, installPath android.Path) {
-	libPaths.addLibraryPath(ctx, lib, hostPath, installPath, true)
-}
-
-// Add a new library path to the map, if the library exists (name is not nil).
-// Don't enforce checks that the library paths exist. Some libraries may be missing from the build,
-// but their names still need to be added to <uses-library> tags in the manifest.
-func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.ModuleContext, lib *string, hostPath, installPath android.Path) {
-	if lib != nil {
-		libPaths.addLibraryPath(ctx, *lib, hostPath, installPath, false)
-	}
-}
-
-// Add library paths from the second map to the first map (do not override existing entries).
-func (libPaths LibraryPaths) AddLibraryPaths(otherPaths LibraryPaths) {
-	for lib, path := range otherPaths {
-		if _, present := libPaths[lib]; !present {
-			libPaths[lib] = path
-		}
-	}
-}
-
 type ModuleConfig struct {
 	Name            string
 	DexLocation     string // dex location on device
@@ -354,13 +256,6 @@
 // from Make to read the module dexpreopt.config written in the Make config
 // stage.
 func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) {
-	type jsonLibraryPath struct {
-		Host   string
-		Device string
-	}
-
-	type jsonLibraryPaths map[string]jsonLibraryPath
-
 	type ModuleJSONConfig struct {
 		*ModuleConfig
 
@@ -376,18 +271,6 @@
 		PreoptBootClassPathDexFiles []string
 	}
 
-	// convert JSON map of library paths to LibraryPaths
-	constructLibraryPaths := func(ctx android.PathContext, paths jsonLibraryPaths) LibraryPaths {
-		m := LibraryPaths{}
-		for lib, path := range paths {
-			m[lib] = &LibraryPath{
-				constructPath(ctx, path.Host),
-				path.Device,
-			}
-		}
-		return m
-	}
-
 	config := ModuleJSONConfig{}
 
 	err := json.Unmarshal(data, &config)
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 66e765f..a07f1fa 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -81,7 +81,9 @@
 	}
 
 	if !dexpreoptDisabled(ctx, global, module) {
-		if clc := genClassLoaderContext(ctx, global, module); clc != nil {
+		if clc, err := genClassLoaderContext(ctx, global, module); err != nil {
+			android.ReportPathErrorf(ctx, err.Error())
+		} else if clc != nil {
 			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
 				!module.NoCreateAppImage
 
@@ -194,243 +196,6 @@
 	return profilePath
 }
 
-type classLoaderContext struct {
-	// Library names
-	Names []string
-
-	// The class loader context using paths in the build.
-	Host android.Paths
-
-	// The class loader context using paths as they will be on the device.
-	Target []string
-}
-
-// A map of class loader contexts for each SDK version.
-// A map entry for "any" version contains libraries that are unconditionally added to class loader
-// context. Map entries for existing versions contains libraries that were in the default classpath
-// until that API version, and should be added to class loader context if and only if the
-// targetSdkVersion in the manifest or APK is less than that API version.
-type classLoaderContextMap map[int]*classLoaderContext
-
-const AnySdkVersion int = 9999 // should go last in class loader context
-
-func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
-	if _, ok := m[sdkVer]; !ok {
-		m[sdkVer] = &classLoaderContext{}
-	}
-	return m[sdkVer]
-}
-
-func (clc *classLoaderContext) addLib(lib string, hostPath android.Path, targetPath string) {
-	clc.Names = append(clc.Names, lib)
-	clc.Host = append(clc.Host, hostPath)
-	clc.Target = append(clc.Target, targetPath)
-}
-
-func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, module *ModuleConfig, libs ...string) bool {
-	clc := m.getValue(sdkVer)
-	for _, lib := range libs {
-		if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath {
-			clc.addLib(lib, p.Host, p.Device)
-		} else {
-			if sdkVer == AnySdkVersion {
-				// Fail the build if dexpreopt doesn't know paths to one of the <uses-library>
-				// dependencies. In the future we may need to relax this and just disable dexpreopt.
-				android.ReportPathErrorf(ctx, "dexpreopt cannot find path for <uses-library> '%s'", lib)
-			} else {
-				// No error for compatibility libraries, as Soong doesn't know if they are needed
-				// (this depends on the targetSdkVersion in the manifest).
-			}
-			return false
-		}
-	}
-	return true
-}
-
-func (m classLoaderContextMap) usesLibs() []string {
-	if clc, ok := m[AnySdkVersion]; ok {
-		return clc.Names
-	}
-	return nil
-}
-
-func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
-	clc := m.getValue(sdkVer)
-	for _, lib := range libs {
-		clc.addLib(lib, SystemServerDexJarHostPath(ctx, lib), filepath.Join("/system/framework", lib+".jar"))
-	}
-}
-
-// 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.
-//
-// 3. 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(ctx, AnySdkVersion, module, usesLibs...) {
-			return nil
-		}
-
-		// Conditional class loader context for API version < 28.
-		const httpLegacy = "org.apache.http.legacy"
-		if !classLoaderContexts.addLibs(ctx, 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(ctx, 29, module, usesLibs29...) {
-			return nil
-		}
-
-		// Conditional class loader context for API version < 30.
-		if !classLoaderContexts.addLibs(ctx, 30, module, OptionalCompatUsesLibs30...) {
-			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 &.
-	}
-
-	fixConditionalClassLoaderContext(classLoaderContexts)
-
-	return &classLoaderContexts
-}
-
-// Find build and install paths to "android.hidl.base". The library must be present in conditional
-// class loader context for SDK version 29, because it's one of the compatibility libraries.
-func findHidlBasePaths(ctx android.PathContext, clcMap classLoaderContextMap) (android.Path, string) {
-	var hostPath android.Path
-	targetPath := UnknownInstallLibraryPath
-
-	if clc, ok := clcMap[29]; ok {
-		for i, lib := range clc.Names {
-			if lib == AndroidHidlBase {
-				hostPath = clc.Host[i]
-				targetPath = clc.Target[i]
-				break
-			}
-		}
-	}
-
-	// Fail if the library paths were not found. This may happen if the function is called at the
-	// wrong time (either before the compatibility libraries were added to context, or after they
-	// have been removed for some reason).
-	if hostPath == nil {
-		android.ReportPathErrorf(ctx, "dexpreopt cannot find build path to '%s'", AndroidHidlBase)
-	} else if targetPath == UnknownInstallLibraryPath {
-		android.ReportPathErrorf(ctx, "dexpreopt cannot find install path to '%s'", AndroidHidlBase)
-	}
-
-	return hostPath, targetPath
-}
-
-// Now that the full unconditional context is known, reconstruct conditional context.
-// Apply filters for individual libraries, mirroring what the PackageManager does when it
-// constructs class loader context on device.
-//
-// TODO(b/132357300):
-//   - move handling of android.hidl.manager -> android.hidl.base dependency here
-//   - remove android.hidl.manager and android.hidl.base unless the app is a system app.
-//
-func fixConditionalClassLoaderContext(clcMap classLoaderContextMap) {
-	usesLibs := clcMap.usesLibs()
-
-	for sdkVer, clc := range clcMap {
-		if sdkVer == AnySdkVersion {
-			continue
-		}
-		clcMap[sdkVer] = &classLoaderContext{}
-		for i, lib := range clc.Names {
-			if android.InList(lib, usesLibs) {
-				// skip compatibility libraries that are already included in unconditional context
-			} else if lib == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
-				// android.test.mock is only needed as a compatibility library (in conditional class
-				// loader context) if android.test.runner is used, otherwise skip it
-			} else {
-				clcMap[sdkVer].addLib(lib, clc.Host[i], clc.Target[i])
-			}
-		}
-	}
-}
-
-// Return the class loader context as a string and a slice of build paths for all dependencies.
-func computeClassLoaderContext(ctx android.PathContext, clcMap classLoaderContextMap) (clcStr string, paths android.Paths) {
-	hidlBaseHostPath, hidlBaseTargetPath := findHidlBasePaths(ctx, clcMap)
-
-	for _, ver := range android.SortedIntKeys(clcMap) {
-		clc := clcMap.getValue(ver)
-
-		clcLen := len(clc.Names)
-		if clcLen != len(clc.Host) || clcLen != len(clc.Target) {
-			android.ReportPathErrorf(ctx, "ill-formed class loader context")
-		}
-
-		var hostClc, targetClc []string
-		var hostPaths android.Paths
-
-		for i := 0; i < clcLen; i++ {
-			hostStr := "PCL[" + clc.Host[i].String() + "]"
-			targetStr := "PCL[" + clc.Target[i] + "]"
-
-			// Add dependency of android.hidl.manager on android.hidl.base (it is not tracked as
-			// a regular dependency by the build system, so it needs special handling).
-			if clc.Names[i] == AndroidHidlManager {
-				hostStr += "{PCL[" + hidlBaseHostPath.String() + "]}"
-				targetStr += "{PCL[" + hidlBaseTargetPath + "]}"
-				hostPaths = append(hostPaths, hidlBaseHostPath)
-			}
-
-			hostClc = append(hostClc, hostStr)
-			targetClc = append(targetClc, targetStr)
-			hostPaths = append(hostPaths, clc.Host[i])
-		}
-
-		if hostPaths != nil {
-			sdkVerStr := fmt.Sprintf("%d", ver)
-			if ver == AnySdkVersion {
-				sdkVerStr = "any" // a special keyword that means any SDK version
-			}
-			clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, strings.Join(hostClc, "#"))
-			clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, strings.Join(targetClc, "#"))
-			paths = append(paths, hostPaths...)
-		}
-	}
-
-	return clcStr, paths
-}
-
 func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
 	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, classLoaderContexts classLoaderContextMap,
 	profile android.WritablePath, appImage bool, generateDM bool) {
diff --git a/java/Android.bp b/java/Android.bp
index 92e8ca4..9e8dc78 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -22,6 +22,7 @@
         "androidmk.go",
         "app_builder.go",
         "app.go",
+        "boot_jars.go",
         "builder.go",
         "device_host_converter.go",
         "dex.go",
diff --git a/java/androidmk.go b/java/androidmk.go
index c21c83a..e1a661f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -217,10 +217,6 @@
 			func(entries *android.AndroidMkEntries) {
 				if prebuilt.dexJarFile != nil {
 					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
-					// TODO(b/125517186): export the dex jar as a classes jar to match some mis-uses in Make until
-					// boot_jars_package_check.mk can check dex jars.
-					entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.dexJarFile)
-					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.dexJarFile)
 				}
 				if len(prebuilt.dexpreopter.builtInstalled) > 0 {
 					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", prebuilt.dexpreopter.builtInstalled)
diff --git a/java/app_test.go b/java/app_test.go
index 8c77573..82577e3 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2831,10 +2831,9 @@
 	}
 
 	// Test conditional context for target SDK version 29.
-	// Hardcoded dependency "android.hidl.manager" -> "android.hidl.base" is present.
 	if w := `--target-context-for-sdk 29` +
 		` PCL[/system/framework/android.hidl.base-V1.0-java.jar]` +
-		`#PCL[/system/framework/android.hidl.manager-V1.0-java.jar]{PCL[/system/framework/android.hidl.base-V1.0-java.jar]} `; !strings.Contains(cmd, w) {
+		`#PCL[/system/framework/android.hidl.manager-V1.0-java.jar] `; !strings.Contains(cmd, w) {
 		t.Errorf("wanted %q in %q", w, cmd)
 	}
 
diff --git a/java/boot_jars.go b/java/boot_jars.go
new file mode 100644
index 0000000..900eb7a
--- /dev/null
+++ b/java/boot_jars.go
@@ -0,0 +1,123 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterSingletonType("boot_jars", bootJarsSingletonFactory)
+}
+
+func bootJarsSingletonFactory() android.Singleton {
+	return &bootJarsSingleton{}
+}
+
+type bootJarsSingleton struct{}
+
+func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex map[string]string, list android.ConfiguredJarList, name string) bool {
+	for i := 0; i < list.Len(); i++ {
+		module := list.Jar(i)
+		// Ignore jacocoagent it is only added when instrumenting and so has no impact on
+		// app compatibility.
+		if module == "jacocoagent" {
+			continue
+		}
+		apex := list.Apex(i)
+		if existing, ok := moduleToApex[module]; ok {
+			ctx.Errorf("Configuration property %q is invalid as it contains multiple references to module (%s) in APEXes (%s and %s)",
+				module, existing, apex)
+			return false
+		}
+
+		moduleToApex[module] = apex
+	}
+
+	return true
+}
+
+func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	config := ctx.Config()
+	if config.SkipBootJarsCheck() {
+		return
+	}
+
+	// Populate a map from module name to APEX from the boot jars. If there is a problem
+	// such as duplicate modules then fail and return immediately.
+	moduleToApex := make(map[string]string)
+	if !populateMapFromConfiguredJarList(ctx, moduleToApex, config.NonUpdatableBootJars(), "BootJars") ||
+		!populateMapFromConfiguredJarList(ctx, moduleToApex, config.UpdatableBootJars(), "UpdatableBootJars") {
+		return
+	}
+
+	// Map from module name to the correct apex variant.
+	nameToApexVariant := make(map[string]android.Module)
+
+	// Scan all the modules looking for the module/apex variants corresponding to the
+	// boot jars.
+	ctx.VisitAllModules(func(module android.Module) {
+		name := ctx.ModuleName(module)
+		if apex, ok := moduleToApex[name]; ok {
+			apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+			if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApex(apex) {
+				// The module name/apex variant should be unique in the system but double check
+				// just in case something has gone wrong.
+				if existing, ok := nameToApexVariant[name]; ok {
+					ctx.Errorf("found multiple variants matching %s:%s: %q and %q", apex, name, existing, module)
+				}
+				nameToApexVariant[name] = module
+			}
+		}
+	})
+
+	timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp")
+
+	rule := android.NewRuleBuilder()
+	checkBootJars := rule.Command().BuiltTool(ctx, "check_boot_jars").
+		Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt"))
+
+	// If this is not an unbundled build and missing dependencies are not allowed
+	// then all the boot jars listed must have been found.
+	strict := !config.UnbundledBuild() && !config.AllowMissingDependencies()
+
+	// Iterate over the module names on the boot classpath in order
+	for _, name := range android.SortedStringKeys(moduleToApex) {
+		if apexVariant, ok := nameToApexVariant[name]; ok {
+			if dep, ok := apexVariant.(Dependency); ok {
+				// Add the implementation jars for the module to be checked. This uses implementation
+				// and resources jar as that is what the previous make based check uses.
+				for _, jar := range dep.ImplementationAndResourcesJars() {
+					checkBootJars.Input(jar)
+				}
+			} else if _, ok := apexVariant.(*DexImport); ok {
+				// TODO(b/171479578): ignore deximport when doing package check until boot_jars.go can check dex jars.
+			} else {
+				ctx.Errorf("module %q is of type %q which is not supported as a boot jar", name, ctx.ModuleType(apexVariant))
+			}
+		} else if strict {
+			ctx.Errorf("boot jars package check failed as it could not find module %q for apex %q", name, moduleToApex[name])
+		}
+	}
+
+	checkBootJars.Text("&& touch").Output(timestamp)
+	rule.Build(pctx, ctx, "boot_jars_package_check", "check boot jar packages")
+
+	// The check-boot-jars phony target depends on the timestamp created if the check succeeds.
+	ctx.Phony("check-boot-jars", timestamp)
+
+	// The droidcore phony target depends on the check-boot-jars phony target
+	ctx.Phony("droidcore", android.PathForPhony(ctx, "check-boot-jars"))
+}
diff --git a/java/java.go b/java/java.go
index 4b48a15..3167512 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1187,18 +1187,6 @@
 	// javaVersion flag.
 	flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
 
-	// javac flags.
-	javacFlags := j.properties.Javacflags
-	if flags.javaVersion.usesJavaModules() {
-		javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
-	}
-	if ctx.Config().MinimizeJavaDebugInfo() && !ctx.Host() {
-		// For non-host binaries, override the -g flag passed globally to remove
-		// local variable debug info to reduce disk and memory usage.
-		javacFlags = append(javacFlags, "-g:source,lines")
-	}
-	javacFlags = append(javacFlags, "-Xlint:-dep-ann")
-
 	if ctx.Config().RunErrorProne() {
 		if config.ErrorProneClasspath == nil {
 			ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
@@ -1249,24 +1237,42 @@
 		}
 	}
 
-	if j.properties.Patch_module != nil && flags.javaVersion.usesJavaModules() {
-		// Manually specify build directory in case it is not under the repo root.
-		// (javac doesn't seem to expand into symbolc links when searching for patch-module targets, so
-		// just adding a symlink under the root doesn't help.)
-		patchPaths := ".:" + ctx.Config().BuildDir()
-		classPath := flags.classpath.FormJavaClassPath("")
-		if classPath != "" {
-			patchPaths += ":" + classPath
-		}
-		javacFlags = append(javacFlags, "--patch-module="+String(j.properties.Patch_module)+"="+patchPaths)
-	}
-
 	// systemModules
 	flags.systemModules = deps.systemModules
 
 	// aidl flags.
 	flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
 
+	return flags
+}
+
+func (j *Module) collectJavacFlags(ctx android.ModuleContext, flags javaBuilderFlags) javaBuilderFlags {
+	// javac flags.
+	javacFlags := j.properties.Javacflags
+
+	if ctx.Config().MinimizeJavaDebugInfo() && !ctx.Host() {
+		// For non-host binaries, override the -g flag passed globally to remove
+		// local variable debug info to reduce disk and memory usage.
+		javacFlags = append(javacFlags, "-g:source,lines")
+	}
+	javacFlags = append(javacFlags, "-Xlint:-dep-ann")
+
+	if flags.javaVersion.usesJavaModules() {
+		javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
+
+		if j.properties.Patch_module != nil {
+			// Manually specify build directory in case it is not under the repo root.
+			// (javac doesn't seem to expand into symbolc links when searching for patch-module targets, so
+			// just adding a symlink under the root doesn't help.)
+			patchPaths := ".:" + ctx.Config().BuildDir()
+			classPath := flags.classpath.FormJavaClassPath("")
+			if classPath != "" {
+				patchPaths += ":" + classPath
+			}
+			javacFlags = append(javacFlags, "--patch-module="+String(j.properties.Patch_module)+"="+patchPaths)
+		}
+	}
+
 	if len(javacFlags) > 0 {
 		// optimization.
 		ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
@@ -1297,6 +1303,8 @@
 
 	srcFiles = j.genSources(ctx, srcFiles, flags)
 
+	flags = j.collectJavacFlags(ctx, flags)
+
 	srcJars := srcFiles.FilterByExt(".srcjar")
 	srcJars = append(srcJars, deps.srcJars...)
 	if aaptSrcJar != nil {
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index b5c70dc..3ad32fa 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -14,6 +14,7 @@
 		"frameworks/native/libs/binder/rust",
 		"packages/modules/Virtualization",
 		"prebuilts/rust",
+		"system/bt",
 		"system/extras/profcollectd",
 		"system/hardware/interfaces/keystore2",
 		"system/security",
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 92f5c53..dd03f28 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -1,4 +1,20 @@
 python_binary_host {
+    name: "check_boot_jars",
+    main: "check_boot_jars/check_boot_jars.py",
+    srcs: [
+        "check_boot_jars/check_boot_jars.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+}
+
+python_binary_host {
     name: "manifest_fixer",
     main: "manifest_fixer.py",
     srcs: [
diff --git a/scripts/check_boot_jars/check_boot_jars.py b/scripts/check_boot_jars/check_boot_jars.py
new file mode 100755
index 0000000..cf4ef27
--- /dev/null
+++ b/scripts/check_boot_jars/check_boot_jars.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+
+"""
+Check boot jars.
+
+Usage: check_boot_jars.py <package_allow_list_file> <jar1> <jar2> ...
+"""
+import logging
+import os.path
+import re
+import subprocess
+import sys
+
+
+# The compiled allow list RE.
+allow_list_re = None
+
+
+def LoadAllowList(filename):
+  """ Load and compile allow list regular expressions from filename.
+  """
+  lines = []
+  with open(filename, 'r') as f:
+    for line in f:
+      line = line.strip()
+      if not line or line.startswith('#'):
+        continue
+      lines.append(line)
+  combined_re = r'^(%s)$' % '|'.join(lines)
+  global allow_list_re
+  try:
+    allow_list_re = re.compile(combined_re)
+  except re.error:
+    logging.exception(
+        'Cannot compile package allow list regular expression: %r',
+        combined_re)
+    allow_list_re = None
+    return False
+  return True
+
+
+def CheckJar(allow_list_path, jar):
+  """Check a jar file.
+  """
+  # Get the list of files inside the jar file.
+  p = subprocess.Popen(args='jar tf %s' % jar,
+      stdout=subprocess.PIPE, shell=True)
+  stdout, _ = p.communicate()
+  if p.returncode != 0:
+    return False
+  items = stdout.split()
+  classes = 0
+  for f in items:
+    if f.endswith('.class'):
+      classes += 1
+      package_name = os.path.dirname(f)
+      package_name = package_name.replace('/', '.')
+      if not package_name or not allow_list_re.match(package_name):
+        print >> sys.stderr, ('Error: %s contains class file %s, whose package name %s is empty or'
+                              ' not in the allow list %s of packages allowed on the bootclasspath.'
+                              % (jar, f, package_name, allow_list_path))
+        return False
+  if classes == 0:
+    print >> sys.stderr, ('Error: %s does not contain any class files.' % jar)
+    return False
+  return True
+
+
+def main(argv):
+  if len(argv) < 2:
+    print __doc__
+    return 1
+  allow_list_path = argv[0]
+
+  if not LoadAllowList(allow_list_path):
+    return 1
+
+  passed = True
+  for jar in argv[1:]:
+    if not CheckJar(allow_list_path, jar):
+      passed = False
+  if not passed:
+    return 1
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
new file mode 100644
index 0000000..18ab427
--- /dev/null
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -0,0 +1,248 @@
+# Boot jar package name allowed list.
+# Each line is interpreted as a regular expression.
+
+###################################################
+# core-libart.jar & core-oj.jar
+java\.awt\.font
+java\.beans
+java\.io
+java\.lang
+java\.lang\.annotation
+java\.lang\.invoke
+java\.lang\.ref
+java\.lang\.reflect
+java\.math
+java\.net
+java\.nio
+java\.nio\.file
+java\.nio\.file\.spi
+java\.nio\.file\.attribute
+java\.nio\.channels
+java\.nio\.channels\.spi
+java\.nio\.charset
+java\.nio\.charset\.spi
+java\.security
+java\.security\.acl
+java\.security\.cert
+java\.security\.interfaces
+java\.security\.spec
+java\.sql
+java\.text
+java\.text\.spi
+java\.time
+java\.time\.chrono
+java\.time\.format
+java\.time\.temporal
+java\.time\.zone
+java\.util
+java\.util\.concurrent
+java\.util\.concurrent\.atomic
+java\.util\.concurrent\.locks
+java\.util\.function
+java\.util\.jar
+java\.util\.logging
+java\.util\.prefs
+java\.util\.regex
+java\.util\.spi
+java\.util\.stream
+java\.util\.zip
+# TODO: Remove javax.annotation.processing if possible, see http://b/132338110:
+javax\.annotation\.processing
+javax\.crypto
+javax\.crypto\.interfaces
+javax\.crypto\.spec
+javax\.net
+javax\.net\.ssl
+javax\.security\.auth
+javax\.security\.auth\.callback
+javax\.security\.auth\.login
+javax\.security\.auth\.x500
+javax\.security\.cert
+javax\.sql
+javax\.xml
+javax\.xml\.datatype
+javax\.xml\.namespace
+javax\.xml\.parsers
+javax\.xml\.transform
+javax\.xml\.transform\.dom
+javax\.xml\.transform\.sax
+javax\.xml\.transform\.stream
+javax\.xml\.validation
+javax\.xml\.xpath
+jdk\.internal\.util
+jdk\.internal\.vm\.annotation
+jdk\.net
+org\.w3c\.dom
+org\.w3c\.dom\.ls
+org\.w3c\.dom\.traversal
+# OpenJdk internal implementation.
+sun\.invoke\.util
+sun\.invoke\.empty
+sun\.misc
+sun\.util.*
+sun\.text.*
+sun\.security.*
+sun\.reflect.*
+sun\.nio.*
+sun\.net.*
+com\.sun\..*
+
+# TODO: Move these internal org.apache.harmony classes to libcore.*
+org\.apache\.harmony\.crypto\.internal
+org\.apache\.harmony\.dalvik
+org\.apache\.harmony\.dalvik\.ddmc
+org\.apache\.harmony\.luni\.internal\.util
+org\.apache\.harmony\.security
+org\.apache\.harmony\.security\.asn1
+org\.apache\.harmony\.security\.fortress
+org\.apache\.harmony\.security\.pkcs10
+org\.apache\.harmony\.security\.pkcs7
+org\.apache\.harmony\.security\.pkcs8
+org\.apache\.harmony\.security\.provider\.crypto
+org\.apache\.harmony\.security\.utils
+org\.apache\.harmony\.security\.x501
+org\.apache\.harmony\.security\.x509
+org\.apache\.harmony\.security\.x509\.tsp
+org\.apache\.harmony\.xml
+org\.apache\.harmony\.xml\.dom
+org\.apache\.harmony\.xml\.parsers
+
+org\.json
+org\.xmlpull\.v1
+org\.xmlpull\.v1\.sax2
+
+# TODO:  jarjar org.kxml2.io to com.android org\.kxml2\.io
+org\.kxml2\.io
+org\.xml
+org\.xml\.sax
+org\.xml\.sax\.ext
+org\.xml\.sax\.helpers
+
+dalvik\..*
+libcore\..*
+android\..*
+com\.android\..*
+###################################################
+# android.test.base.jar
+junit\.extensions
+junit\.framework
+android\.test
+android\.test\.suitebuilder\.annotation
+
+
+###################################################
+# ext.jar
+# TODO: jarjar javax.sip to com.android
+javax\.sip
+javax\.sip\.address
+javax\.sip\.header
+javax\.sip\.message
+
+# TODO: jarjar org.apache.commons to com.android
+org\.apache\.commons\.codec
+org\.apache\.commons\.codec\.binary
+org\.apache\.commons\.codec\.language
+org\.apache\.commons\.codec\.net
+org\.apache\.commons\.logging
+org\.apache\.commons\.logging\.impl
+org\.apache\.http
+org\.apache\.http\.auth
+org\.apache\.http\.auth\.params
+org\.apache\.http\.client
+org\.apache\.http\.client\.entity
+org\.apache\.http\.client\.methods
+org\.apache\.http\.client\.params
+org\.apache\.http\.client\.protocol
+org\.apache\.http\.client\.utils
+org\.apache\.http\.conn
+org\.apache\.http\.conn\.params
+org\.apache\.http\.conn\.routing
+org\.apache\.http\.conn\.scheme
+org\.apache\.http\.conn\.ssl
+org\.apache\.http\.conn\.util
+org\.apache\.http\.cookie
+org\.apache\.http\.cookie\.params
+org\.apache\.http\.entity
+org\.apache\.http\.impl
+org\.apache\.http\.impl\.auth
+org\.apache\.http\.impl\.client
+org\.apache\.http\.impl\.client
+org\.apache\.http\.impl\.conn
+org\.apache\.http\.impl\.conn\.tsccm
+org\.apache\.http\.impl\.cookie
+org\.apache\.http\.impl\.entity
+org\.apache\.http\.impl\.io
+org\.apache\.http\.impl\.io
+org\.apache\.http\.io
+org\.apache\.http\.message
+org\.apache\.http\.params
+org\.apache\.http\.protocol
+org\.apache\.http\.util
+
+# TODO: jarjar gov.nist to com.android
+gov\.nist\.core
+gov\.nist\.core\.net
+gov\.nist\.javax\.sip
+gov\.nist\.javax\.sip\.address
+gov\.nist\.javax\.sip\.clientauthutils
+gov\.nist\.javax\.sip\.header
+gov\.nist\.javax\.sip\.header\.extensions
+gov\.nist\.javax\.sip\.header\.ims
+gov\.nist\.javax\.sip\.message
+gov\.nist\.javax\.sip\.parser
+gov\.nist\.javax\.sip\.parser\.extensions
+gov\.nist\.javax\.sip\.parser\.ims
+gov\.nist\.javax\.sip\.stack
+
+org\.ccil\.cowan\.tagsoup
+org\.ccil\.cowan\.tagsoup\.jaxp
+
+###################################################
+# framework.jar
+javax\.microedition\.khronos\.opengles
+javax\.microedition\.khronos\.egl
+
+android
+
+###################################################
+# apache-xml.jar
+org\.apache\.xml\.res
+org\.apache\.xml\.utils
+org\.apache\.xml\.utils\.res
+org\.apache\.xml\.dtm
+org\.apache\.xml\.dtm\.ref
+org\.apache\.xml\.dtm\.ref\.dom2dtm
+org\.apache\.xml\.dtm\.ref\.sax2dtm
+org\.apache\.xml\.serializer
+org\.apache\.xml\.serializer\.utils
+org\.apache\.xml\.serializer\.dom3
+org\.apache\.xpath
+org\.apache\.xpath\.operations
+org\.apache\.xpath\.domapi
+org\.apache\.xpath\.functions
+org\.apache\.xpath\.res
+org\.apache\.xpath\.axes
+org\.apache\.xpath\.objects
+org\.apache\.xpath\.patterns
+org\.apache\.xpath\.jaxp
+org\.apache\.xpath\.compiler
+org\.apache\.xalan
+org\.apache\.xalan\.res
+org\.apache\.xalan\.templates
+org\.apache\.xalan\.serialize
+org\.apache\.xalan\.extensions
+org\.apache\.xalan\.processor
+org\.apache\.xalan\.transformer
+org\.apache\.xalan\.xslt
+
+###################################################
+# Packages in the google namespace across all bootclasspath jars.
+com\.google\.android\..*
+com\.google\.vr\.platform.*
+com\.google\.i18n\.phonenumbers\..*
+com\.google\.i18n\.phonenumbers
+
+###################################################
+# Packages used for Android in Chrome OS
+org\.chromium\.arc
+org\.chromium\.arc\..*
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index c1813ec..84e4f28 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -464,7 +464,6 @@
 				arm64: {
 					export_system_include_dirs: ["arm64/include"],
 					sanitize: {
-						hwaddress: true,
 						integer_overflow: false,
 					},
 				},
@@ -496,7 +495,6 @@
             srcs: ["arm64/lib/mynativelib.so"],
             export_system_include_dirs: ["arm64/include/arm64/include"],
             sanitize: {
-                hwaddress: true,
                 integer_overflow: false,
             },
         },
@@ -527,7 +525,6 @@
             srcs: ["arm64/lib/mynativelib.so"],
             export_system_include_dirs: ["arm64/include/arm64/include"],
             sanitize: {
-                hwaddress: true,
                 integer_overflow: false,
             },
         },
@@ -548,7 +545,7 @@
 `),
 		checkAllCopyRules(`
 include/Test.h -> include/include/Test.h
-.intermediates/mynativelib/android_arm64_armv8-a_shared_hwasan/mynativelib.so -> arm64/lib/mynativelib.so
+.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
 arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
 .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
 	)
@@ -2727,3 +2724,75 @@
 `),
 	)
 }
+
+func TestNoSanitizerMembers(t *testing.T) {
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			native_shared_libs: ["mynativelib"],
+		}
+
+		cc_library_shared {
+			name: "mynativelib",
+			srcs: ["Test.cpp"],
+			export_include_dirs: ["include"],
+			arch: {
+				arm64: {
+					export_system_include_dirs: ["arm64/include"],
+					sanitize: {
+						hwaddress: true,
+					},
+				},
+			},
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+    name: "mysdk_mynativelib@current",
+    sdk_member_name: "mynativelib",
+    visibility: ["//visibility:public"],
+    installable: false,
+    compile_multilib: "both",
+    export_include_dirs: ["include/include"],
+    arch: {
+        arm64: {
+            export_system_include_dirs: ["arm64/include/arm64/include"],
+        },
+        arm: {
+            srcs: ["arm/lib/mynativelib.so"],
+        },
+    },
+}
+
+cc_prebuilt_library_shared {
+    name: "mynativelib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    compile_multilib: "both",
+    export_include_dirs: ["include/include"],
+    arch: {
+        arm64: {
+            export_system_include_dirs: ["arm64/include/arm64/include"],
+        },
+        arm: {
+            srcs: ["arm/lib/mynativelib.so"],
+        },
+    },
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    visibility: ["//visibility:public"],
+    native_shared_libs: ["mysdk_mynativelib@current"],
+}
+`),
+		checkAllCopyRules(`
+include/Test.h -> include/include/Test.h
+arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+	)
+}