Merge "Exclude directories with manual BUILD files from bp2build."
diff --git a/OWNERS b/OWNERS
index f15bd32..f0ccd82 100644
--- a/OWNERS
+++ b/OWNERS
@@ -3,13 +3,18 @@
 
 # AMER
 ahumesky@google.com
+alexmarquez@google.com
 asmundak@google.com
 ccross@android.com
 cparsons@google.com
 dwillemsen@google.com
 eakammer@google.com
+jobredeaux@google.com
 joeo@google.com
+lamontjones@google.com
 spandandas@google.com
+weiwli@google.com
+yudiliu@google.com
 yuntaoxu@google.com
 
 # APAC
diff --git a/README.md b/README.md
index e92349e..a67c393 100644
--- a/README.md
+++ b/README.md
@@ -33,8 +33,9 @@
 Every module must have a `name` property, and the value must be unique across
 all Android.bp files.
 
-For a list of valid module types and their properties see
-[$OUT_DIR/soong/docs/soong_build.html](https://ci.android.com/builds/latest/branches/aosp-build-tools/targets/linux/view/soong_build.html).
+The list of valid module types and their properties can be generated by calling
+`m soong_docs`. It will be written to `$OUT_DIR/soong/docs/soong_build.html`.
+This list for the current version of Soong can be found [here](https://ci.android.com/builds/latest/branches/aosp-build-tools/targets/linux/view/soong_build.html).
 
 ### File lists
 
diff --git a/android/Android.bp b/android/Android.bp
index 1bccd7b..f3a3850 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -66,7 +66,6 @@
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "proto.go",
-        "queryview.go",
         "register.go",
         "rule_builder.go",
         "sandbox.go",
@@ -81,7 +80,6 @@
         "util.go",
         "variable.go",
         "visibility.go",
-        "writedocs.go",
     ],
     testSrcs: [
         "android_test.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 9853d2c..967c550 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -845,7 +845,7 @@
 		case "*selinux.selinuxContextsModule": // license properties written
 		case "*sysprop.syspropLibrary": // license properties written
 		default:
-			if ctx.Config().IsEnvTrue("ANDROID_REQUIRE_LICENSES") {
+			if !ctx.Config().IsEnvFalse("ANDROID_REQUIRE_LICENSES") {
 				return fmt.Errorf("custom make rules not allowed for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), ctx.ModuleName(mod))
 			}
 		}
diff --git a/android/arch.go b/android/arch.go
index f7eb963..ddc082b 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -631,8 +631,7 @@
 	image := base.commonProperties.ImageVariation
 	// Filter NativeBridge targets unless they are explicitly supported.
 	// Skip creating native bridge variants for non-core modules.
-	if os == Android &&
-		!(Bool(base.commonProperties.Native_bridge_supported) && image == CoreVariation) {
+	if os == Android && !(base.IsNativeBridgeSupported() && image == CoreVariation) {
 
 		var targets []Target
 		for _, t := range osTargets {
diff --git a/android/bazel.go b/android/bazel.go
index 347f5c0..bfd0f90 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -61,8 +61,8 @@
 	HandcraftedLabel() string
 	GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
 	ConvertWithBp2build(ctx BazelConversionPathContext) bool
+	convertWithBp2build(ctx BazelConversionPathContext, module blueprint.Module) bool
 	GetBazelBuildFileContents(c Config, path, name string) (string, error)
-	ConvertedToBazel(ctx BazelConversionPathContext) bool
 }
 
 // BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
@@ -120,6 +120,10 @@
 	// allows modules to opt-out.
 	Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
 
+	// all modules in this package (not recursively) default to bp2build_available: true.
+	// allows modules to opt-out.
+	Bp2BuildDefaultTrue
+
 	// all modules in this package (not recursively) default to bp2build_available: false.
 	// allows modules to opt-in.
 	Bp2BuildDefaultFalse
@@ -141,6 +145,7 @@
 		"build/bazel/bazel_skylib":/* recursive = */ true,
 		"build/bazel/rules":/* recursive = */ true,
 		"build/bazel/rules_cc":/* recursive = */ true,
+		"build/bazel/scripts":/* recursive = */ true,
 		"build/bazel/tests":/* recursive = */ true,
 		"build/bazel/platforms":/* recursive = */ true,
 		"build/bazel/product_variables":/* recursive = */ true,
@@ -168,17 +173,23 @@
 	bp2buildDefaultConfig = Bp2BuildConfig{
 		"bionic":                            Bp2BuildDefaultTrueRecursively,
 		"build/bazel/examples/apex/minimal": Bp2BuildDefaultTrueRecursively,
+		"development/sdk":                   Bp2BuildDefaultTrueRecursively,
 		"external/gwp_asan":                 Bp2BuildDefaultTrueRecursively,
+		"external/brotli":                   Bp2BuildDefaultTrue,
 		"system/core/libcutils":             Bp2BuildDefaultTrueRecursively,
+		"system/core/libprocessgroup":       Bp2BuildDefaultTrue,
 		"system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
 		"system/libbase":                  Bp2BuildDefaultTrueRecursively,
 		"system/logging/liblog":           Bp2BuildDefaultTrueRecursively,
+		"system/sepolicy/apex":            Bp2BuildDefaultTrueRecursively,
 		"system/timezone/apex":            Bp2BuildDefaultTrueRecursively,
 		"system/timezone/output_data":     Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
 		"external/fmtlib":                 Bp2BuildDefaultTrueRecursively,
 		"external/jemalloc_new":           Bp2BuildDefaultTrueRecursively,
+		"external/libcxx":                 Bp2BuildDefaultTrueRecursively,
 		"external/libcxxabi":              Bp2BuildDefaultTrueRecursively,
+		"external/libcap":                 Bp2BuildDefaultTrueRecursively,
 		"external/scudo":                  Bp2BuildDefaultTrueRecursively,
 		"prebuilts/clang/host/linux-x86":  Bp2BuildDefaultTrueRecursively,
 	}
@@ -207,6 +218,13 @@
 		"libc_ndk",          // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661)
 		"libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook
 
+		// There are unexported symbols that don't surface on a shared library build,
+		// from the source static archive
+		// e.g. _Unwind_{GetRegionStart,GetLanguageSpecificData,GetIP,Set{IP,GR},Resume,{Raise,Delete}Exception}, pthread_atfork
+		// ... from: cxa_{personality,exception}.o, system_error.o, wrappers_c_bionic.o
+		// cf. http://b/198403271
+		"libc++",
+
 		// http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx
 		// c++_static
 		"libbase_ndk", // http://b/186826477, cc_library, no such target '//build/bazel/platforms/os:darwin' when --platforms //build/bazel/platforms:android_x86 is added
@@ -218,10 +236,22 @@
 		"libbase",                  // Requires liblog. http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx.
 		// Also depends on fmtlib.
 
+		"libfdtrack", // depends on STL
+
 		"libseccomp_policy", // depends on libbase
 
 		"gwp_asan_crash_handler", // cc_library, ld.lld: error: undefined symbol: memset
 
+		//system/core/libprocessgroup/...
+		"libprocessgroup", // depends on //system/core/libprocessgroup/cgrouprc:libcgrouprc
+
+		//external/brotli/...
+		"brotli-fuzzer-corpus", // "declared output 'external/brotli/c/fuzz/73231c6592f195ffd41100b8706d1138ff6893b9' was not created by genrule"
+
+		// //external/libcap/...
+		"libcap",      // http://b/198595332, depends on _makenames, a cc_binary
+		"cap_names.h", // http://b/198596102, depends on _makenames, a cc_binary
+
 		// Tests. Handle later.
 		"libbionic_tests_headers_posix", // http://b/186024507, cc_library_static, sched.h, time.h not found
 		"libjemalloc5_integrationtest",
@@ -235,15 +265,18 @@
 	// Per-module denylist of cc_library modules to only generate the static
 	// variant if their shared variant isn't ready or buildable by Bazel.
 	bp2buildCcLibraryStaticOnlyList = []string{
-		"libstdc++",    // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno
 		"libjemalloc5", // http://b/188503688, cc_library, `target: { android: { enabled: false } }` for android targets.
 	}
 
 	// Per-module denylist to opt modules out of mixed builds. Such modules will
 	// still be generated via bp2build.
 	mixedBuildsDisabledList = []string{
-		"libc++abi",      // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
-		"libc++demangle", // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
+		"libbrotli",           // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
+		"libc++fs",            // http://b/198403271, Missing symbols/members in the global namespace when referenced from headers in //external/libcxx/includes
+		"libc++_experimental", // http://b/198403271, Missing symbols/members in the global namespace when referenced from headers in //external/libcxx/includes
+		"libc++_static",       // http://b/198403271, Missing symbols/members in the global namespace when referenced from headers in //external/libcxx/includes
+		"libc++abi",           // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
+		"libc++demangle",      // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
 	}
 
 	// Used for quicker lookups
@@ -266,8 +299,8 @@
 	}
 }
 
-func GenerateCcLibraryStaticOnly(ctx BazelConversionPathContext) bool {
-	return bp2buildCcLibraryStaticOnly[ctx.Module().Name()]
+func GenerateCcLibraryStaticOnly(moduleName string) bool {
+	return bp2buildCcLibraryStaticOnly[moduleName]
 }
 
 func ShouldKeepExistingBuildFileForDir(dir string) bool {
@@ -293,10 +326,11 @@
 	if !ctx.Config().BazelContext.BazelEnabled() {
 		return false
 	}
-	if len(b.GetBazelLabel(ctx, ctx.Module())) == 0 {
+	if !convertedToBazel(ctx, ctx.Module()) {
 		return false
 	}
-	if GenerateCcLibraryStaticOnly(ctx) {
+
+	if GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
 		// Don't use partially-converted cc_library targets in mixed builds,
 		// since mixed builds would generally rely on both static and shared
 		// variants of a cc_library.
@@ -305,20 +339,33 @@
 	return !mixedBuildsDisabled[ctx.Module().Name()]
 }
 
+// ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
+func convertedToBazel(ctx BazelConversionPathContext, module blueprint.Module) bool {
+	b, ok := module.(Bazelable)
+	if !ok {
+		return false
+	}
+	return b.convertWithBp2build(ctx, module) || b.HasHandcraftedLabel()
+}
+
 // ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
 func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
-	if bp2buildModuleDoNotConvert[ctx.Module().Name()] {
+	return b.convertWithBp2build(ctx, ctx.Module())
+}
+
+func (b *BazelModuleBase) convertWithBp2build(ctx BazelConversionPathContext, module blueprint.Module) bool {
+	if bp2buildModuleDoNotConvert[module.Name()] {
 		return false
 	}
 
 	// Ensure that the module type of this module has a bp2build converter. This
 	// prevents mixed builds from using auto-converted modules just by matching
 	// the package dir; it also has to have a bp2build mutator as well.
-	if ctx.Config().bp2buildModuleTypeConfig[ctx.ModuleType()] == false {
+	if ctx.Config().bp2buildModuleTypeConfig[ctx.OtherModuleType(module)] == false {
 		return false
 	}
 
-	packagePath := ctx.ModuleDir()
+	packagePath := ctx.OtherModuleDir(module)
 	config := ctx.Config().bp2buildPackageConfig
 
 	// This is a tristate value: true, false, or unset.
@@ -345,11 +392,10 @@
 func bp2buildDefaultTrueRecursively(packagePath string, config Bp2BuildConfig) bool {
 	ret := false
 
-	// Return exact matches in the config.
-	if config[packagePath] == Bp2BuildDefaultTrueRecursively {
+	// Check if the package path has an exact match in the config.
+	if config[packagePath] == Bp2BuildDefaultTrue || config[packagePath] == Bp2BuildDefaultTrueRecursively {
 		return true
-	}
-	if config[packagePath] == Bp2BuildDefaultFalse {
+	} else if config[packagePath] == Bp2BuildDefaultFalse {
 		return false
 	}
 
@@ -390,9 +436,3 @@
 	}
 	return string(data[:]), nil
 }
-
-// ConvertedToBazel returns whether this module has been converted to Bazel, whether automatically
-// or manually
-func (b *BazelModuleBase) ConvertedToBazel(ctx BazelConversionPathContext) bool {
-	return b.ConvertWithBp2build(ctx) || b.HasHandcraftedLabel()
-}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 9c922dd..50b79fa 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -27,9 +27,9 @@
 	"sync"
 
 	"android/soong/bazel/cquery"
+	"android/soong/shared"
 
 	"android/soong/bazel"
-	"android/soong/shared"
 )
 
 type cqueryRequest interface {
@@ -492,6 +492,12 @@
 )
 `
 
+	commonArchFilegroupString := `
+filegroup(name = "common",
+    srcs = [%s],
+)
+`
+
 	configNodesSection := ""
 
 	labelsByArch := map[string][]string{}
@@ -501,14 +507,22 @@
 		labelsByArch[archString] = append(labelsByArch[archString], labelString)
 	}
 
-	configNodeLabels := []string{}
+	allLabels := []string{}
 	for archString, labels := range labelsByArch {
-		configNodeLabels = append(configNodeLabels, fmt.Sprintf("\":%s\"", archString))
-		labelsString := strings.Join(labels, ",\n            ")
-		configNodesSection += fmt.Sprintf(configNodeFormatString, archString, archString, labelsString)
+		if archString == "common" {
+			// arch-less labels (e.g. filegroups) don't need a config_node
+			allLabels = append(allLabels, "\":common\"")
+			labelsString := strings.Join(labels, ",\n            ")
+			configNodesSection += fmt.Sprintf(commonArchFilegroupString, labelsString)
+		} else {
+			// Create a config_node, and add the config_node's label to allLabels
+			allLabels = append(allLabels, fmt.Sprintf("\":%s\"", archString))
+			labelsString := strings.Join(labels, ",\n            ")
+			configNodesSection += fmt.Sprintf(configNodeFormatString, archString, archString, labelsString)
+		}
 	}
 
-	return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(configNodeLabels, ",\n            ")))
+	return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(allLabels, ",\n            ")))
 }
 
 func indent(original string) string {
@@ -573,6 +587,12 @@
 %s
 
 def get_arch(target):
+  # TODO(b/199363072): filegroups and file targets aren't associated with any
+  # specific platform architecture in mixed builds. This is consistent with how
+  # Soong treats filegroups, but it may not be the case with manually-written
+  # filegroup BUILD targets.
+  if target.kind in ["filegroup", ""]:
+    return "common"
   buildoptions = build_options(target)
   platforms = build_options(target)["//command_line_option:platforms"]
   if len(platforms) != 1:
@@ -671,11 +691,12 @@
 	if err != nil {
 		return err
 	}
+
 	buildrootLabel := "@soong_injection//mixed_builds:buildroot"
 	cqueryOutput, cqueryErr, err = context.issueBazelCommand(
 		context.paths,
 		bazel.CqueryBuildRootRunName,
-		bazelCommand{"cquery", fmt.Sprintf("kind(rule, deps(%s))", buildrootLabel)},
+		bazelCommand{"cquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
 		"--output=starlark",
 		"--starlark:file="+absolutePath(cqueryFileRelpath))
 	err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"),
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 557faea..cdf1a63 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -11,7 +11,7 @@
 	label := "//foo:bar"
 	arch := Arm64
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
-		bazelCommand{command: "cquery", expression: "kind(rule, deps(@soong_injection//mixed_builds:buildroot))"}: `//foo:bar|arm64>>out/foo/bar.txt`,
+		bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: `//foo:bar|arm64>>out/foo/bar.txt`,
 	})
 	g, ok := bazelContext.GetOutputFiles(label, arch)
 	if ok {
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index a4bd2ef..b5746f7 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -15,11 +15,12 @@
 package android
 
 import (
-	"android/soong/bazel"
 	"fmt"
 	"path/filepath"
 	"strings"
 
+	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 )
@@ -75,32 +76,17 @@
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 	ModuleFromName(name string) (blueprint.Module, bool)
 	Module() Module
-	ModuleType() string
+	OtherModuleType(m blueprint.Module) string
 	OtherModuleName(m blueprint.Module) string
 	OtherModuleDir(m blueprint.Module) string
+	AddUnconvertedBp2buildDep(string)
 }
 
 // BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
 // or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
 // module within the given ctx.
-func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
-	return bazelLabelForModuleDeps(ctx, modules, false)
-}
-
-// BazelLabelForModuleWholeDeps expects a list of references to other modules, ("<module>"
-// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
-// module within the given ctx, where prebuilt dependencies will be appended with _alwayslink so
-// they can be handled as whole static libraries.
-func BazelLabelForModuleWholeDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
-	return bazelLabelForModuleDeps(ctx, modules, true)
-}
-
-// BazelLabelForModuleDepsExcludes expects two lists: modules (containing modules to include in the
-// list), and excludes (modules to exclude from the list). Both of these should contain references
-// to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label list which
-// corresponds to dependencies on the module within the given ctx, and the excluded dependencies.
-func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
-	return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, false)
+func BazelLabelForModuleDeps(ctx TopDownMutatorContext, modules []string) bazel.LabelList {
+	return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel)
 }
 
 // BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in
@@ -109,11 +95,15 @@
 // list which corresponds to dependencies on the module within the given ctx, and the excluded
 // dependencies.  Prebuilt dependencies will be appended with _alwayslink so they can be handled as
 // whole static libraries.
-func BazelLabelForModuleWholeDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
-	return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, true)
+func BazelLabelForModuleDepsExcludes(ctx TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+	return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel)
 }
 
-func bazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string, isWholeLibs bool) bazel.LabelList {
+// BazelLabelForModuleDepsWithFn expects a list of reference to other modules, ("<module>"
+// or ":<module>") and applies moduleToLabelFn to determine and return a Bazel-compatible label
+// which corresponds to dependencies on the module within the given ctx.
+func BazelLabelForModuleDepsWithFn(ctx TopDownMutatorContext, modules []string,
+	moduleToLabelFn func(TopDownMutatorContext, blueprint.Module) string) bazel.LabelList {
 	var labels bazel.LabelList
 	// In some cases, a nil string list is different than an explicitly empty list.
 	if len(modules) == 0 && modules != nil {
@@ -126,7 +116,7 @@
 			module = ":" + module
 		}
 		if m, t := SrcIsModuleWithTag(module); m != "" {
-			l := getOtherModuleLabel(ctx, m, t, isWholeLibs)
+			l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn)
 			l.OriginalModuleName = bpText
 			labels.Includes = append(labels.Includes, l)
 		} else {
@@ -136,23 +126,29 @@
 	return labels
 }
 
-func bazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string, isWholeLibs bool) bazel.LabelList {
-	moduleLabels := bazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes), isWholeLibs)
+// BazelLabelForModuleDepsExcludesWithFn expects two lists: modules (containing modules to include in the
+// list), and excludes (modules to exclude from the list). Both of these should contain references
+// to other modules, ("<module>" or ":<module>"). It applies moduleToLabelFn to determine and return a
+// Bazel-compatible label list which corresponds to dependencies on the module within the given ctx, and
+// the excluded dependencies.
+func BazelLabelForModuleDepsExcludesWithFn(ctx TopDownMutatorContext, modules, excludes []string,
+	moduleToLabelFn func(TopDownMutatorContext, blueprint.Module) string) bazel.LabelList {
+	moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn)
 	if len(excludes) == 0 {
 		return moduleLabels
 	}
-	excludeLabels := bazelLabelForModuleDeps(ctx, excludes, isWholeLibs)
+	excludeLabels := BazelLabelForModuleDepsWithFn(ctx, excludes, moduleToLabelFn)
 	return bazel.LabelList{
 		Includes: moduleLabels.Includes,
 		Excludes: excludeLabels.Includes,
 	}
 }
 
-func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
+func BazelLabelForModuleSrcSingle(ctx TopDownMutatorContext, path string) bazel.Label {
 	return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0]
 }
 
-func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label {
+func BazelLabelForModuleDepSingle(ctx TopDownMutatorContext, path string) bazel.Label {
 	return BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes[0]
 }
 
@@ -162,7 +158,7 @@
 // relative if within the same package).
 // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
 // will have already been handled by the path_deps mutator.
-func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
+func BazelLabelForModuleSrc(ctx TopDownMutatorContext, paths []string) bazel.LabelList {
 	return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
 }
 
@@ -172,7 +168,7 @@
 // (absolute if in a different package or relative if within the same package).
 // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
 // will have already been handled by the path_deps mutator.
-func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
+func BazelLabelForModuleSrcExcludes(ctx TopDownMutatorContext, paths, excludes []string) bazel.LabelList {
 	excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
 	excluded := make([]string, 0, len(excludeLabels.Includes))
 	for _, e := range excludeLabels.Includes {
@@ -292,7 +288,7 @@
 // Properties passed as the paths or excludes argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on other modules will have already been handled by the
 // path_deps mutator.
-func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
+func expandSrcsForBazel(ctx TopDownMutatorContext, paths, expandedExcludes []string) bazel.LabelList {
 	if paths == nil {
 		return bazel.LabelList{}
 	}
@@ -309,7 +305,7 @@
 
 	for _, p := range paths {
 		if m, tag := SrcIsModuleWithTag(p); m != "" {
-			l := getOtherModuleLabel(ctx, m, tag, false)
+			l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel)
 			if !InList(l.Label, expandedExcludes) {
 				l.OriginalModuleName = fmt.Sprintf(":%s", m)
 				labels.Includes = append(labels.Includes, l)
@@ -340,18 +336,20 @@
 // getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
 // module. The label will be relative to the current directory if appropriate. The dependency must
 // already be resolved by either deps mutator or path deps mutator.
-func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string, isWholeLibs bool) bazel.Label {
+func getOtherModuleLabel(ctx TopDownMutatorContext, dep, tag string,
+	labelFromModule func(TopDownMutatorContext, blueprint.Module) string) bazel.Label {
 	m, _ := ctx.ModuleFromName(dep)
 	if m == nil {
 		panic(fmt.Errorf("No module named %q found, but was a direct dep of %q", dep, ctx.Module().Name()))
 	}
-	otherLabel := bazelModuleLabel(ctx, m, tag)
-	label := bazelModuleLabel(ctx, ctx.Module(), "")
-	if isWholeLibs {
-		if m, ok := m.(Module); ok && IsModulePrebuilt(m) {
-			otherLabel += "_alwayslink"
-		}
+	if !convertedToBazel(ctx, m) {
+		ctx.AddUnconvertedBp2buildDep(dep)
 	}
+	label := BazelModuleLabel(ctx, ctx.Module())
+	otherLabel := labelFromModule(ctx, m)
+
+	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
+
 	if samePackage(label, otherLabel) {
 		otherLabel = bazelShortLabel(otherLabel)
 	}
@@ -361,13 +359,12 @@
 	}
 }
 
-func bazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module, tag string) string {
+func BazelModuleLabel(ctx TopDownMutatorContext, module blueprint.Module) string {
 	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
-	b, ok := module.(Bazelable)
-	// TODO(b/181155349): perhaps return an error here if the module can't be/isn't being converted
-	if !ok || !b.ConvertedToBazel(ctx) {
+	if !convertedToBazel(ctx, module) {
 		return bp2buildModuleLabel(ctx, module)
 	}
+	b, _ := module.(Bazelable)
 	return b.GetBazelLabel(ctx, module)
 }
 
diff --git a/android/config.go b/android/config.go
index b69963f..993aaa7 100644
--- a/android/config.go
+++ b/android/config.go
@@ -66,19 +66,31 @@
 	*config
 }
 
-// BuildDir returns the build output directory for the configuration.
+// SoongOutDir returns the build output directory for the configuration.
 func (c Config) SoongOutDir() string {
 	return c.soongOutDir
 }
 
 func (c Config) OutDir() string {
-	return c.soongOutDir
+	return c.outDir
+}
+
+func (c Config) RunGoTests() bool {
+	return c.runGoTests
 }
 
 func (c Config) DebugCompilation() bool {
 	return false // Never compile Go code in the main build for debugging
 }
 
+func (c Config) Subninjas() []string {
+	return []string{}
+}
+
+func (c Config) PrimaryBuilderInvocations() []bootstrap.PrimaryBuilderInvocation {
+	return []bootstrap.PrimaryBuilderInvocation{}
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -122,9 +134,12 @@
 
 	deviceConfig *deviceConfig
 
-	soongOutDir    string // the path of the build output directory
+	outDir         string // The output directory (usually out/)
+	soongOutDir    string
 	moduleListFile string // the path to the file which lists blueprint files to parse.
 
+	runGoTests bool
+
 	env       map[string]string
 	envLock   sync.Mutex
 	envDeps   map[string]string
@@ -137,8 +152,6 @@
 	captureBuild      bool // true for tests, saves build parameters for each module
 	ignoreEnvironment bool // true for tests, returns empty from all Getenv calls
 
-	stopBefore bootstrap.StopBefore
-
 	fs         pathtools.FileSystem
 	mockBpList string
 
@@ -283,9 +296,10 @@
 
 // NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that
 // use the android package.
-func NullConfig(soongOutDir string) Config {
+func NullConfig(outDir, soongOutDir string) Config {
 	return Config{
 		config: &config{
+			outDir:      outDir,
 			soongOutDir: soongOutDir,
 			fs:          pathtools.OsFs,
 		},
@@ -319,6 +333,9 @@
 			ShippingApiLevel:                  stringPtr("30"),
 		},
 
+		outDir: buildDir,
+		// soongOutDir is inconsistent with production (it should be buildDir + "/soong")
+		// but a lot of tests assume this :(
 		soongOutDir:  buildDir,
 		captureBuild: true,
 		env:          envCopy,
@@ -397,7 +414,7 @@
 // multiple runs in the same program execution is carried over (such as Bazel
 // context or environment deps).
 func ConfigForAdditionalRun(c Config) (Config, error) {
-	newConfig, err := NewConfig(c.soongOutDir, c.moduleListFile, c.env)
+	newConfig, err := NewConfig(c.moduleListFile, c.runGoTests, c.outDir, c.soongOutDir, c.env)
 	if err != nil {
 		return Config{}, err
 	}
@@ -408,14 +425,16 @@
 
 // NewConfig creates a new Config object. The srcDir argument specifies the path
 // to the root source directory. It also loads the config file, if found.
-func NewConfig(soongOutDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
+func NewConfig(moduleListFile string, runGoTests bool, outDir, soongOutDir string, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
 		ProductVariablesFileName: filepath.Join(soongOutDir, productVariablesFileName),
 
 		env: availableEnv,
 
+		outDir:            outDir,
 		soongOutDir:       soongOutDir,
+		runGoTests:        runGoTests,
 		multilibConflicts: make(map[ArchType]bool),
 
 		moduleListFile: moduleListFile,
@@ -525,7 +544,7 @@
 	pathsToParse := []string{}
 	for candidate := range mockFS {
 		base := filepath.Base(candidate)
-		if base == "Blueprints" || base == "Android.bp" {
+		if base == "Android.bp" {
 			pathsToParse = append(pathsToParse, candidate)
 		}
 	}
@@ -538,29 +557,16 @@
 	c.mockBpList = blueprint.MockModuleListFile
 }
 
-func (c *config) StopBefore() bootstrap.StopBefore {
-	return c.stopBefore
-}
-
-// SetStopBefore configures soong_build to exit earlier at a specific point.
-func (c *config) SetStopBefore(stopBefore bootstrap.StopBefore) {
-	c.stopBefore = stopBefore
-}
-
 func (c *config) SetAllowMissingDependencies() {
 	c.productVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
 }
 
-var _ bootstrap.ConfigStopBefore = (*config)(nil)
-
 // BlueprintToolLocation returns the directory containing build system tools
 // from Blueprint, like soong_zip and merge_zips.
-func (c *config) BlueprintToolLocation() string {
+func (c *config) HostToolDir() string {
 	return filepath.Join(c.soongOutDir, "host", c.PrebuiltOS(), "bin")
 }
 
-var _ bootstrap.ConfigBlueprintToolLocation = (*config)(nil)
-
 func (c *config) HostToolPath(ctx PathContext, tool string) Path {
 	return PathForOutput(ctx, "host", c.PrebuiltOS(), "bin", tool)
 }
@@ -1525,6 +1531,10 @@
 		c.config.productVariables.RecoverySnapshotDirsIncluded)
 }
 
+func (c *deviceConfig) HostFakeSnapshotEnabled() bool {
+	return c.config.productVariables.HostFakeSnapshotEnabled
+}
+
 func (c *deviceConfig) ShippingApiLevel() ApiLevel {
 	if c.config.productVariables.ShippingApiLevel == nil {
 		return NoneApiLevel
@@ -1649,6 +1659,20 @@
 	return ConfiguredJarList{apexes, jars}
 }
 
+// Append a list of (apex, jar) pairs to the list.
+func (l *ConfiguredJarList) AppendList(other ConfiguredJarList) ConfiguredJarList {
+	apexes := make([]string, 0, l.Len()+other.Len())
+	jars := make([]string, 0, l.Len()+other.Len())
+
+	apexes = append(apexes, l.apexes...)
+	jars = append(jars, l.jars...)
+
+	apexes = append(apexes, other.apexes...)
+	jars = append(jars, other.jars...)
+
+	return ConfiguredJarList{apexes, jars}
+}
+
 // RemoveList filters out a list of (apex, jar) pairs from the receiving list of pairs.
 func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) ConfiguredJarList {
 	apexes := make([]string, 0, l.Len())
diff --git a/android/deapexer.go b/android/deapexer.go
index de3f635..9290481 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -73,7 +73,7 @@
 	// exported file name is the apex relative path, e.g. javalib/core-libart.jar.
 	//
 	// See Prebuilt.ApexInfoMutator for more information.
-	exports map[string]Path
+	exports map[string]WritablePath
 }
 
 // PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
@@ -82,7 +82,7 @@
 // The exported file is identified by the apex relative path, e.g. "javalib/core-libart.jar".
 //
 // See apex/deapexer.go for more information.
-func (i DeapexerInfo) PrebuiltExportPath(apexRelativePath string) Path {
+func (i DeapexerInfo) PrebuiltExportPath(apexRelativePath string) WritablePath {
 	path := i.exports[apexRelativePath]
 	return path
 }
@@ -95,7 +95,7 @@
 // for use with a prebuilt_apex module.
 //
 // See apex/deapexer.go for more information.
-func NewDeapexerInfo(exports map[string]Path) DeapexerInfo {
+func NewDeapexerInfo(exports map[string]WritablePath) DeapexerInfo {
 	return DeapexerInfo{
 		exports: exports,
 	}
diff --git a/android/defs.go b/android/defs.go
index b3ff376..c8e2e9b 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -188,6 +188,15 @@
 	buildWriteFileRule(ctx, outputFile, content)
 }
 
+func CatFileRule(ctx BuilderContext, paths Paths, outputFile WritablePath) {
+	ctx.Build(pctx, BuildParams{
+		Rule:        Cat,
+		Inputs:      paths,
+		Output:      outputFile,
+		Description: "combine files to " + outputFile.Base(),
+	})
+}
+
 // shellUnescape reverses proptools.ShellEscape
 func shellUnescape(s string) string {
 	// Remove leading and trailing quotes if present
diff --git a/android/filegroup.go b/android/filegroup.go
index 54d01d3..4db165f 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -42,6 +42,27 @@
 
 	srcs := bazel.MakeLabelListAttribute(
 		BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs))
+
+	// For Bazel compatibility, don't generate the filegroup if there is only 1
+	// source file, and that the source file is named the same as the module
+	// itself. In Bazel, eponymous filegroups like this would be an error.
+	//
+	// Instead, dependents on this single-file filegroup can just depend
+	// on the file target, instead of rule target, directly.
+	//
+	// You may ask: what if a filegroup has multiple files, and one of them
+	// shares the name? The answer: we haven't seen that in the wild, and
+	// should lock Soong itself down to prevent the behavior. For now,
+	// we raise an error if bp2build sees this problem.
+	for _, f := range srcs.Value.Includes {
+		if f.Label == fg.Name() {
+			if len(srcs.Value.Includes) > 1 {
+				ctx.ModuleErrorf("filegroup '%s' cannot contain a file with the same name", fg.Name())
+			}
+			return
+		}
+	}
+
 	attrs := &bazelFilegroupAttributes{
 		Srcs: srcs,
 	}
@@ -97,7 +118,7 @@
 	}
 
 	bazelCtx := ctx.Config().BazelContext
-	filePaths, ok := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), ctx.Arch().ArchType)
+	filePaths, ok := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), Common)
 	if !ok {
 		return false
 	}
diff --git a/android/license_kind_test.go b/android/license_kind_test.go
index 1f09568..7a909a6 100644
--- a/android/license_kind_test.go
+++ b/android/license_kind_test.go
@@ -14,38 +14,38 @@
 	{
 		name: "license_kind must not accept licenses property",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license_kind {
 					name: "top_license",
 					licenses: ["other_license"],
 				}`),
 		},
 		expectedErrors: []string{
-			`top/Blueprints:4:14: unrecognized property "licenses"`,
+			`top/Android.bp:4:14: unrecognized property "licenses"`,
 		},
 	},
 	{
 		name: "bad license_kind",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license_kind {
 					name: "top_notice",
 					conditions: ["notice"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_license {
 					name: "other_notice",
 					license_kinds: ["notice"],
 				}`),
 		},
 		expectedErrors: []string{
-			`other/Blueprints:2:5: "other_notice" depends on undefined module "notice"`,
+			`other/Android.bp:2:5: "other_notice" depends on undefined module "notice"`,
 		},
 	},
 	{
 		name: "good license kind",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license_kind {
 					name: "top_by_exception_only",
 					conditions: ["by_exception_only"],
@@ -55,7 +55,7 @@
 					name: "top_proprietary",
 					license_kinds: ["top_by_exception_only"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_license {
 					name: "other_proprietary",
 					license_kinds: ["top_proprietary"],
@@ -65,7 +65,7 @@
 	{
 		name: "multiple license kinds",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license_kind {
 					name: "top_notice",
 					conditions: ["notice"],
@@ -85,7 +85,7 @@
 					name: "top_proprietary",
 					license_kinds: ["top_by_exception_only"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_license {
 					name: "other_rule",
 					license_kinds: ["top_by_exception_only"],
diff --git a/android/license_sdk_member.go b/android/license_sdk_member.go
index cd36ed6..2ce921b 100644
--- a/android/license_sdk_member.go
+++ b/android/license_sdk_member.go
@@ -31,9 +31,9 @@
 	SdkMemberTypeBase
 }
 
-func (l *licenseSdkMemberType) AddDependencies(mctx BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+func (l *licenseSdkMemberType) AddDependencies(ctx SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
 	// Add dependencies onto the license module from the sdk module.
-	mctx.AddDependency(mctx.Module(), dependencyTag, names...)
+	ctx.AddDependency(ctx.Module(), dependencyTag, names...)
 }
 
 func (l *licenseSdkMemberType) IsInstance(module Module) bool {
diff --git a/android/license_test.go b/android/license_test.go
index 26b33c3..7222cd7 100644
--- a/android/license_test.go
+++ b/android/license_test.go
@@ -27,7 +27,7 @@
 	{
 		name: "license must not accept licenses property",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license {
 					name: "top_license",
 					visibility: ["//visibility:private"],
@@ -35,13 +35,13 @@
 				}`),
 		},
 		expectedErrors: []string{
-			`top/Blueprints:5:14: unrecognized property "licenses"`,
+			`top/Android.bp:5:14: unrecognized property "licenses"`,
 		},
 	},
 	{
 		name: "private license",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license_kind {
 					name: "top_notice",
 					conditions: ["notice"],
@@ -53,27 +53,27 @@
 					license_kinds: ["top_notice"],
 					visibility: ["//visibility:private"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				rule {
 					name: "arule",
 					licenses: ["top_allowed_as_notice"],
 				}`),
-			"yetmore/Blueprints": []byte(`
+			"yetmore/Android.bp": []byte(`
 				package {
 					default_applicable_licenses: ["top_allowed_as_notice"],
 				}`),
 		},
 		expectedErrors: []string{
-			`other/Blueprints:2:5: module "arule": depends on //top:top_allowed_as_notice ` +
+			`other/Android.bp:2:5: module "arule": depends on //top:top_allowed_as_notice ` +
 				`which is not visible to this module`,
-			`yetmore/Blueprints:2:5: module "//yetmore": depends on //top:top_allowed_as_notice ` +
+			`yetmore/Android.bp:2:5: module "//yetmore": depends on //top:top_allowed_as_notice ` +
 				`which is not visible to this module`,
 		},
 	},
 	{
 		name: "must reference license_kind module",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				rule {
 					name: "top_by_exception_only",
 				}
@@ -85,14 +85,14 @@
 				}`),
 		},
 		expectedErrors: []string{
-			`top/Blueprints:6:5: module "top_proprietary": license_kinds property ` +
+			`top/Android.bp:6:5: module "top_proprietary": license_kinds property ` +
 				`"top_by_exception_only" is not a license_kind module`,
 		},
 	},
 	{
 		name: "license_kind module must exist",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license {
 					name: "top_notice_allowed",
 					license_kinds: ["top_notice"],
@@ -100,13 +100,13 @@
 				}`),
 		},
 		expectedErrors: []string{
-			`top/Blueprints:2:5: "top_notice_allowed" depends on undefined module "top_notice"`,
+			`top/Android.bp:2:5: "top_notice_allowed" depends on undefined module "top_notice"`,
 		},
 	},
 	{
 		name: "public license",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license_kind {
 					name: "top_by_exception_only",
 					conditions: ["by_exception_only"],
@@ -118,12 +118,12 @@
 					license_kinds: ["top_by_exception_only"],
 					visibility: ["//visibility:public"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				rule {
 					name: "arule",
 					licenses: ["top_proprietary"],
 				}`),
-			"yetmore/Blueprints": []byte(`
+			"yetmore/Android.bp": []byte(`
 				package {
 					default_applicable_licenses: ["top_proprietary"],
 				}`),
@@ -132,7 +132,7 @@
 	{
 		name: "multiple licenses",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_applicable_licenses: ["top_proprietary"],
 				}
@@ -162,12 +162,12 @@
 					name: "myrule",
 					licenses: ["top_allowed_as_notice", "top_proprietary"]
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				rule {
 					name: "arule",
 					licenses: ["top_proprietary"],
 				}`),
-			"yetmore/Blueprints": []byte(`
+			"yetmore/Android.bp": []byte(`
 				package {
 					default_applicable_licenses: ["top_proprietary"],
 				}`),
diff --git a/android/licenses.go b/android/licenses.go
index 464ba49..bcd85f9 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -48,7 +48,7 @@
 
 	// License modules, i.e. modules depended upon via a licensesTag, must be automatically added to
 	// any sdk/module_exports to which their referencing module is a member.
-	_ SdkMemberTypeDependencyTag = licensesTag
+	_ SdkMemberDependencyTag = licensesTag
 )
 
 // Describes the property provided by a module to reference applicable licenses.
@@ -253,7 +253,7 @@
 
 	primaryProperty := module.base().primaryLicensesProperty
 	if primaryProperty == nil {
-		if ctx.Config().IsEnvTrue("ANDROID_REQUIRE_LICENSES") {
+		if !ctx.Config().IsEnvFalse("ANDROID_REQUIRE_LICENSES") {
 			ctx.ModuleErrorf("module type %q must have an applicable licenses property", ctx.OtherModuleType(module))
 		}
 		return nil
@@ -293,7 +293,7 @@
 	case "*android.soongConfigModuleTypeModule": // creates aliases for modules with licenses
 	case "*android.soongConfigModuleTypeImport": // creates aliases for modules with licenses
 	case "*android.soongConfigStringVariableDummyModule": // used for creating aliases
-	case "*android.SoongConfigBoolVariableDummyModule": // used for creating aliases
+	case "*android.soongConfigBoolVariableDummyModule": // used for creating aliases
 	default:
 		return false
 	}
diff --git a/android/licenses_test.go b/android/licenses_test.go
index 8503310..d05b0a3 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -20,7 +20,7 @@
 	{
 		name: "invalid module type without licenses property",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_bad_module {
 					name: "libexample",
 				}`),
@@ -30,7 +30,7 @@
 	{
 		name: "license must exist",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					licenses: ["notice"],
@@ -41,7 +41,7 @@
 	{
 		name: "all good",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license_kind {
 					name: "notice",
 					conditions: ["shownotice"],
@@ -58,12 +58,12 @@
 					name: "libexample1",
 					licenses: ["top_Apache2"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					licenses: ["top_Apache2"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					licenses: ["top_Apache2"],
@@ -101,7 +101,7 @@
 		// Check that licenses is the union of the defaults modules.
 		name: "defaults union, basic",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license_kind {
 					name: "top_notice",
 					conditions: ["notice"],
@@ -125,7 +125,7 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				license_kind {
 					name: "nested_notice",
 					conditions: ["notice"],
@@ -140,7 +140,7 @@
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
@@ -174,7 +174,7 @@
 	{
 		name: "defaults union, multiple defaults",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				license {
 					name: "top",
 				}
@@ -194,7 +194,7 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				license {
 					name: "top_nested",
 					license_text: ["LICENSE.txt"],
@@ -203,7 +203,7 @@
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				license {
 					name: "other",
 				}
@@ -211,7 +211,7 @@
 					name: "libother",
 					deps: ["libexample"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -251,7 +251,7 @@
 	{
 		name: "defaults_licenses invalid",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "top_defaults",
 					licenses: ["notice"],
@@ -262,7 +262,7 @@
 	{
 		name: "defaults_licenses overrides package default",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_applicable_licenses: ["by_exception_only"],
 				}
@@ -298,7 +298,7 @@
 	{
 		name: "package default_applicable_licenses must exist",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_applicable_licenses: ["notice"],
 				}`),
@@ -309,7 +309,7 @@
 		// This test relies on the default licenses being legacy_public.
 		name: "package default_applicable_licenses property used when no licenses specified",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_applicable_licenses: ["top_notice"],
 				}
@@ -320,7 +320,7 @@
 				mock_library {
 					name: "libexample",
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -338,7 +338,7 @@
 	{
 		name: "package default_applicable_licenses not inherited to subpackages",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_applicable_licenses: ["top_notice"],
 				}
@@ -348,7 +348,7 @@
 				mock_library {
 					name: "libexample",
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				package {
 					default_applicable_licenses: ["outsider"],
 				}
@@ -356,11 +356,11 @@
 				mock_library {
 					name: "libnested",
 				}`),
-			"top/other/Blueprints": []byte(`
+			"top/other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				license {
 					name: "outsider",
 				}
@@ -385,7 +385,7 @@
 	{
 		name: "verify that prebuilt dependencies are included",
 		fs: map[string][]byte{
-			"prebuilts/Blueprints": []byte(`
+			"prebuilts/Android.bp": []byte(`
 				license {
 					name: "prebuilt"
 				}
@@ -394,7 +394,7 @@
 					licenses: ["prebuilt"],
 				}`),
 			"top/sources/source_file": nil,
-			"top/sources/Blueprints": []byte(`
+			"top/sources/Android.bp": []byte(`
 				license {
 					name: "top_sources"
 				}
@@ -403,7 +403,7 @@
 					licenses: ["top_sources"],
 				}`),
 			"top/other/source_file": nil,
-			"top/other/Blueprints": []byte(`
+			"top/other/Android.bp": []byte(`
 				source {
 					name: "other",
 					deps: [":module"],
@@ -419,7 +419,7 @@
 	{
 		name: "verify that prebuilt dependencies are ignored for licenses reasons (preferred)",
 		fs: map[string][]byte{
-			"prebuilts/Blueprints": []byte(`
+			"prebuilts/Android.bp": []byte(`
 				license {
 					name: "prebuilt"
 				}
@@ -429,7 +429,7 @@
 					prefer: true,
 				}`),
 			"top/sources/source_file": nil,
-			"top/sources/Blueprints": []byte(`
+			"top/sources/Android.bp": []byte(`
 				license {
 					name: "top_sources"
 				}
@@ -438,7 +438,7 @@
 					licenses: ["top_sources"],
 				}`),
 			"top/other/source_file": nil,
-			"top/other/Blueprints": []byte(`
+			"top/other/Android.bp": []byte(`
 				source {
 					name: "other",
 					deps: [":module"],
diff --git a/android/module.go b/android/module.go
index cc03418..2803455 100644
--- a/android/module.go
+++ b/android/module.go
@@ -316,6 +316,9 @@
 
 	AddMissingDependencies(missingDeps []string)
 
+	// AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build
+	AddUnconvertedBp2buildDep(dep string)
+
 	Target() Target
 	TargetPrimary() bool
 
@@ -405,6 +408,7 @@
 	PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec
 
 	CheckbuildFile(srcPath Path)
+	TidyFile(srcPath Path)
 
 	InstallInData() bool
 	InstallInTestcases() bool
@@ -495,6 +499,7 @@
 	IsConvertedByBp2build() bool
 	// Bp2buildTargets returns the target(s) generated for Bazel via bp2build for this module
 	Bp2buildTargets() []bp2buildInfo
+	GetUnconvertedBp2buildDeps() []string
 
 	BuildParamsForTests() []BuildParams
 	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
@@ -521,62 +526,6 @@
 	TransitivePackagingSpecs() []PackagingSpec
 }
 
-// BazelTargetModule is a lightweight wrapper interface around Module for
-// bp2build conversion purposes.
-//
-// In bp2build's bootstrap.Main execution, Soong runs an alternate pipeline of
-// mutators that creates BazelTargetModules from regular Module objects,
-// performing the mapping from Soong properties to Bazel rule attributes in the
-// process. This process may optionally create additional BazelTargetModules,
-// resulting in a 1:many mapping.
-//
-// bp2build.Codegen is then responsible for visiting all modules in the graph,
-// filtering for BazelTargetModules, and code-generating BUILD targets from
-// them.
-type BazelTargetModule interface {
-	Module
-
-	bazelTargetModuleProperties() *bazel.BazelTargetModuleProperties
-	SetBazelTargetModuleProperties(props bazel.BazelTargetModuleProperties)
-
-	RuleClass() string
-	BzlLoadLocation() string
-}
-
-// InitBazelTargetModule is a wrapper function that decorates BazelTargetModule
-// with property structs containing metadata for bp2build conversion.
-func InitBazelTargetModule(module BazelTargetModule) {
-	module.AddProperties(module.bazelTargetModuleProperties())
-	InitAndroidModule(module)
-}
-
-// BazelTargetModuleBase contains the property structs with metadata for
-// bp2build conversion.
-type BazelTargetModuleBase struct {
-	ModuleBase
-	Properties bazel.BazelTargetModuleProperties
-}
-
-// bazelTargetModuleProperties getter.
-func (btmb *BazelTargetModuleBase) bazelTargetModuleProperties() *bazel.BazelTargetModuleProperties {
-	return &btmb.Properties
-}
-
-// SetBazelTargetModuleProperties setter for BazelTargetModuleProperties
-func (btmb *BazelTargetModuleBase) SetBazelTargetModuleProperties(props bazel.BazelTargetModuleProperties) {
-	btmb.Properties = props
-}
-
-// RuleClass returns the rule class for this Bazel target
-func (b *BazelTargetModuleBase) RuleClass() string {
-	return b.bazelTargetModuleProperties().Rule_class
-}
-
-// BzlLoadLocation returns the rule class for this Bazel target
-func (b *BazelTargetModuleBase) BzlLoadLocation() string {
-	return b.bazelTargetModuleProperties().Bzl_load_location
-}
-
 // Qualified id for a module
 type qualifiedModuleName struct {
 	// The package (i.e. directory) in which the module is defined, without trailing /
@@ -888,6 +837,10 @@
 	// supported as Soong handles some things within a single target that we may choose to split into
 	// multiple targets, e.g. renderscript, protos, yacc within a cc module.
 	Bp2buildInfo []bp2buildInfo `blueprint:"mutated"`
+
+	// UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to
+	// Bazel
+	UnconvertedBp2buildDeps []string `blueprint:"mutated"`
 }
 
 type distProperties struct {
@@ -987,12 +940,12 @@
 	DeviceSupported = deviceSupported | deviceDefault
 
 	// By default, _only_ device variant is built. Device variant can be disabled with `device_supported: false`
-    // Host and HostCross are disabled by default and can be enabled with `host_supported: true`
+	// Host and HostCross are disabled by default and can be enabled with `host_supported: true`
 	HostAndDeviceSupported = hostSupported | hostCrossSupported | deviceSupported | deviceDefault
 
 	// Host, HostCross, and Device are built by default.
-    // Building Device can be disabled with `device_supported: false`
-    // Building Host and HostCross can be disabled with `host_supported: false`
+	// Building Device can be disabled with `device_supported: false`
+	// Building Host and HostCross can be disabled with `host_supported: false`
 	HostAndDeviceDefault = hostSupported | hostCrossSupported | hostDefault |
 		deviceSupported | deviceDefault
 
@@ -1190,6 +1143,7 @@
 	installFiles         InstallPaths
 	installFilesDepSet   *installPathsDepSet
 	checkbuildFiles      Paths
+	tidyFiles            Paths
 	packagingSpecs       []PackagingSpec
 	packagingSpecsDepSet *packagingSpecsDepSet
 	noticeFiles          Paths
@@ -1202,6 +1156,7 @@
 	// Only set on the final variant of each module
 	installTarget    WritablePath
 	checkbuildTarget WritablePath
+	tidyTarget       WritablePath
 	blueprintDir     string
 
 	hooks hooks
@@ -1265,6 +1220,18 @@
 	return m.commonProperties.Bp2buildInfo
 }
 
+// AddUnconvertedBp2buildDep stores module name of a dependency that was not converted to Bazel.
+func (b *baseModuleContext) AddUnconvertedBp2buildDep(dep string) {
+	unconvertedDeps := &b.Module().base().commonProperties.UnconvertedBp2buildDeps
+	*unconvertedDeps = append(*unconvertedDeps, dep)
+}
+
+// GetUnconvertedBp2buildDeps returns the list of module names of this module's direct dependencies that
+// were not converted to Bazel.
+func (m *ModuleBase) GetUnconvertedBp2buildDeps() []string {
+	return m.commonProperties.UnconvertedBp2buildDeps
+}
+
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
 	(*d)["Android"] = map[string]interface{}{}
 }
@@ -1282,7 +1249,30 @@
 }
 
 func (m *ModuleBase) BuildParamsForTests() []BuildParams {
-	return m.buildParams
+	// Expand the references to module variables like $flags[0-9]*,
+	// so we do not need to change many existing unit tests.
+	// This looks like undoing the shareFlags optimization in cc's
+	// transformSourceToObj, and should only affects unit tests.
+	vars := m.VariablesForTests()
+	buildParams := append([]BuildParams(nil), m.buildParams...)
+	for i, _ := range buildParams {
+		newArgs := make(map[string]string)
+		for k, v := range buildParams[i].Args {
+			newArgs[k] = v
+			// Replaces both ${flags1} and $flags1 syntax.
+			if strings.HasPrefix(v, "${") && strings.HasSuffix(v, "}") {
+				if value, found := vars[v[2:len(v)-1]]; found {
+					newArgs[k] = value
+				}
+			} else if strings.HasPrefix(v, "$") {
+				if value, found := vars[v[1:]]; found {
+					newArgs[k] = value
+				}
+			}
+		}
+		buildParams[i].Args = newArgs
+	}
+	return buildParams
 }
 
 func (m *ModuleBase) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
@@ -1727,10 +1717,12 @@
 func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
 	var allInstalledFiles InstallPaths
 	var allCheckbuildFiles Paths
+	var allTidyFiles Paths
 	ctx.VisitAllModuleVariants(func(module Module) {
 		a := module.base()
 		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
 		allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
+		allTidyFiles = append(allTidyFiles, a.tidyFiles...)
 	})
 
 	var deps Paths
@@ -1754,6 +1746,13 @@
 		deps = append(deps, m.checkbuildTarget)
 	}
 
+	if len(allTidyFiles) > 0 {
+		name := namespacePrefix + ctx.ModuleName() + "-tidy"
+		ctx.Phony(name, allTidyFiles...)
+		m.tidyTarget = PathForPhony(ctx, name)
+		deps = append(deps, m.tidyTarget)
+	}
+
 	if len(deps) > 0 {
 		suffix := ""
 		if ctx.Config().KatiEnabled() {
@@ -1962,6 +1961,7 @@
 
 		m.installFiles = append(m.installFiles, ctx.installFiles...)
 		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
+		m.tidyFiles = append(m.tidyFiles, ctx.tidyFiles...)
 		m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
 		for k, v := range ctx.phonies {
 			m.phonies[k] = append(m.phonies[k], v...)
@@ -2160,6 +2160,7 @@
 	packagingSpecs  []PackagingSpec
 	installFiles    InstallPaths
 	checkbuildFiles Paths
+	tidyFiles       Paths
 	module          Module
 	phonies         map[string]Paths
 
@@ -2892,6 +2893,10 @@
 	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
 }
 
+func (m *moduleContext) TidyFile(srcPath Path) {
+	m.tidyFiles = append(m.tidyFiles, srcPath)
+}
+
 func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext {
 	return m.bp
 }
@@ -3150,19 +3155,49 @@
 
 type buildTargetSingleton struct{}
 
+func addAncestors(ctx SingletonContext, dirMap map[string]Paths, mmName func(string) string) []string {
+	// Ensure ancestor directories are in dirMap
+	// Make directories build their direct subdirectories
+	dirs := SortedStringKeys(dirMap)
+	for _, dir := range dirs {
+		dir := parentDir(dir)
+		for dir != "." && dir != "/" {
+			if _, exists := dirMap[dir]; exists {
+				break
+			}
+			dirMap[dir] = nil
+			dir = parentDir(dir)
+		}
+	}
+	dirs = SortedStringKeys(dirMap)
+	for _, dir := range dirs {
+		p := parentDir(dir)
+		if p != "." && p != "/" {
+			dirMap[p] = append(dirMap[p], PathForPhony(ctx, mmName(dir)))
+		}
+	}
+	return SortedStringKeys(dirMap)
+}
+
 func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) {
 	var checkbuildDeps Paths
+	var tidyDeps Paths
 
 	mmTarget := func(dir string) string {
 		return "MODULES-IN-" + strings.Replace(filepath.Clean(dir), "/", "-", -1)
 	}
+	mmTidyTarget := func(dir string) string {
+		return "tidy-" + strings.Replace(filepath.Clean(dir), "/", "-", -1)
+	}
 
 	modulesInDir := make(map[string]Paths)
+	tidyModulesInDir := make(map[string]Paths)
 
 	ctx.VisitAllModules(func(module Module) {
 		blueprintDir := module.base().blueprintDir
 		installTarget := module.base().installTarget
 		checkbuildTarget := module.base().checkbuildTarget
+		tidyTarget := module.base().tidyTarget
 
 		if checkbuildTarget != nil {
 			checkbuildDeps = append(checkbuildDeps, checkbuildTarget)
@@ -3172,6 +3207,16 @@
 		if installTarget != nil {
 			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], installTarget)
 		}
+
+		if tidyTarget != nil {
+			tidyDeps = append(tidyDeps, tidyTarget)
+			// tidyTarget is in modulesInDir so it will be built with "mm".
+			modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], tidyTarget)
+			// tidyModulesInDir contains tidyTarget but not checkbuildTarget
+			// or installTarget, so tidy targets in a directory can be built
+			// without other checkbuild or install targets.
+			tidyModulesInDir[blueprintDir] = append(tidyModulesInDir[blueprintDir], tidyTarget)
+		}
 	})
 
 	suffix := ""
@@ -3182,31 +3227,24 @@
 	// Create a top-level checkbuild target that depends on all modules
 	ctx.Phony("checkbuild"+suffix, checkbuildDeps...)
 
+	// Create a top-level tidy target that depends on all modules
+	ctx.Phony("tidy"+suffix, tidyDeps...)
+
+	dirs := addAncestors(ctx, tidyModulesInDir, mmTidyTarget)
+
+	// Kati does not generate tidy-* phony targets yet.
+	// Create a tidy-<directory> target that depends on all subdirectories
+	// and modules in the directory.
+	for _, dir := range dirs {
+		ctx.Phony(mmTidyTarget(dir), tidyModulesInDir[dir]...)
+	}
+
 	// Make will generate the MODULES-IN-* targets
 	if ctx.Config().KatiEnabled() {
 		return
 	}
 
-	// Ensure ancestor directories are in modulesInDir
-	dirs := SortedStringKeys(modulesInDir)
-	for _, dir := range dirs {
-		dir := parentDir(dir)
-		for dir != "." && dir != "/" {
-			if _, exists := modulesInDir[dir]; exists {
-				break
-			}
-			modulesInDir[dir] = nil
-			dir = parentDir(dir)
-		}
-	}
-
-	// Make directories build their direct subdirectories
-	for _, dir := range dirs {
-		p := parentDir(dir)
-		if p != "." && p != "/" {
-			modulesInDir[p] = append(modulesInDir[p], PathForPhony(ctx, mmTarget(dir)))
-		}
-	}
+	dirs = addAncestors(ctx, modulesInDir, mmTarget)
 
 	// Create a MODULES-IN-<directory> target that depends on all modules in a directory, and
 	// depends on the MODULES-IN-* targets of all of its subdirectories that contain Android.bp
diff --git a/android/mutator.go b/android/mutator.go
index 20ec621..b361c51 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -16,9 +16,7 @@
 
 import (
 	"android/soong/bazel"
-	"fmt"
 	"reflect"
-	"strings"
 	"sync"
 
 	"github.com/google/blueprint"
@@ -519,12 +517,6 @@
 	name string,
 	bazelProps bazel.BazelTargetModuleProperties,
 	attrs interface{}) {
-	if strings.HasPrefix(name, bazel.BazelTargetModuleNamePrefix) {
-		panic(fmt.Errorf(
-			"The %s name prefix is added automatically, do not set it manually: %s",
-			bazel.BazelTargetModuleNamePrefix,
-			name))
-	}
 
 	info := bp2buildInfo{
 		Name:       name,
@@ -532,7 +524,6 @@
 		BazelProps: bazelProps,
 		Attrs:      attrs,
 	}
-
 	t.Module().base().addBp2buildInfo(info)
 }
 
diff --git a/android/override_module.go b/android/override_module.go
index e72cb78..51e74d4 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -295,7 +295,7 @@
 }
 
 func overridableModuleDepsMutator(ctx BottomUpMutatorContext) {
-	if b, ok := ctx.Module().(OverridableModule); ok {
+	if b, ok := ctx.Module().(OverridableModule); ok && b.Enabled() {
 		b.OverridablePropertiesDepsMutator(ctx)
 	}
 }
diff --git a/android/package_test.go b/android/package_test.go
index 3bd30cc..7ea10a4 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -13,7 +13,7 @@
 	{
 		name: "package must not accept visibility and name properties",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					name: "package",
 					visibility: ["//visibility:private"],
@@ -21,21 +21,21 @@
 				}`),
 		},
 		expectedErrors: []string{
-			`top/Blueprints:5:14: unrecognized property "licenses"`,
-			`top/Blueprints:3:10: unrecognized property "name"`,
-			`top/Blueprints:4:16: unrecognized property "visibility"`,
+			`top/Android.bp:5:14: unrecognized property "licenses"`,
+			`top/Android.bp:3:10: unrecognized property "name"`,
+			`top/Android.bp:4:16: unrecognized property "visibility"`,
 		},
 	},
 	{
 		name: "multiple packages in separate directories",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				package {
 				}`),
-			"other/nested/Blueprints": []byte(`
+			"other/nested/Android.bp": []byte(`
 				package {
 				}`),
 		},
@@ -43,7 +43,7 @@
 	{
 		name: "package must not be specified more than once per package",
 		fs: map[string][]byte{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_visibility: ["//visibility:private"],
 					default_applicable_licenses: ["license"],
diff --git a/android/paths.go b/android/paths.go
index a733558..2e378ba 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -263,38 +263,56 @@
 
 // OptionalPath is a container that may or may not contain a valid Path.
 type OptionalPath struct {
-	valid bool
-	path  Path
+	path          Path   // nil if invalid.
+	invalidReason string // Not applicable if path != nil. "" if the reason is unknown.
 }
 
 // OptionalPathForPath returns an OptionalPath containing the path.
 func OptionalPathForPath(path Path) OptionalPath {
-	if path == nil {
-		return OptionalPath{}
-	}
-	return OptionalPath{valid: true, path: path}
+	return OptionalPath{path: path}
+}
+
+// InvalidOptionalPath returns an OptionalPath that is invalid with the given reason.
+func InvalidOptionalPath(reason string) OptionalPath {
+
+	return OptionalPath{invalidReason: reason}
 }
 
 // Valid returns whether there is a valid path
 func (p OptionalPath) Valid() bool {
-	return p.valid
+	return p.path != nil
 }
 
 // Path returns the Path embedded in this OptionalPath. You must be sure that
 // there is a valid path, since this method will panic if there is not.
 func (p OptionalPath) Path() Path {
-	if !p.valid {
-		panic("Requesting an invalid path")
+	if p.path == nil {
+		msg := "Requesting an invalid path"
+		if p.invalidReason != "" {
+			msg += ": " + p.invalidReason
+		}
+		panic(msg)
 	}
 	return p.path
 }
 
+// InvalidReason returns the reason that the optional path is invalid, or "" if it is valid.
+func (p OptionalPath) InvalidReason() string {
+	if p.path != nil {
+		return ""
+	}
+	if p.invalidReason == "" {
+		return "unknown"
+	}
+	return p.invalidReason
+}
+
 // AsPaths converts the OptionalPath into Paths.
 //
 // It returns nil if this is not valid, or a single length slice containing the Path embedded in
 // this OptionalPath.
 func (p OptionalPath) AsPaths() Paths {
-	if !p.valid {
+	if p.path == nil {
 		return nil
 	}
 	return Paths{p.path}
@@ -303,7 +321,7 @@
 // RelativeToTop returns an OptionalPath with the path that was embedded having been replaced by the
 // result of calling Path.RelativeToTop on it.
 func (p OptionalPath) RelativeToTop() OptionalPath {
-	if !p.valid {
+	if p.path == nil {
 		return p
 	}
 	p.path = p.path.RelativeToTop()
@@ -312,7 +330,7 @@
 
 // String returns the string version of the Path, or "" if it isn't valid.
 func (p OptionalPath) String() string {
-	if p.valid {
+	if p.path != nil {
 		return p.path.String()
 	} else {
 		return ""
@@ -1077,6 +1095,7 @@
 	path, err := pathForSource(ctx, pathComponents...)
 	if err != nil {
 		reportPathError(ctx, err)
+		// No need to put the error message into the returned path since it has been reported already.
 		return OptionalPath{}
 	}
 
@@ -1091,7 +1110,7 @@
 		return OptionalPath{}
 	}
 	if !exists {
-		return OptionalPath{}
+		return InvalidOptionalPath(path.String() + " does not exist")
 	}
 	return OptionalPathForPath(path)
 }
@@ -1127,6 +1146,7 @@
 		relDir = srcPath.path
 	} else {
 		ReportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
+		// No need to put the error message into the returned path since it has been reported already.
 		return OptionalPath{}
 	}
 	dir := filepath.Join(p.srcDir, p.path, relDir)
@@ -1140,7 +1160,7 @@
 		return OptionalPath{}
 	}
 	if len(paths) == 0 {
-		return OptionalPath{}
+		return InvalidOptionalPath(dir + " does not exist")
 	}
 	relPath := Rel(ctx, p.srcDir, paths[0])
 	return OptionalPathForPath(PathForSource(ctx, relPath))
@@ -1150,7 +1170,7 @@
 type OutputPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.BuildDir()
+	// The soong build directory, i.e. Config.SoongOutDir()
 	soongOutDir string
 
 	fullPath string
@@ -1544,7 +1564,7 @@
 type InstallPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.BuildDir()
+	// The soong build directory, i.e. Config.SoongOutDir()
 	soongOutDir string
 
 	// partitionDir is the part of the InstallPath that is automatically determined according to the context.
diff --git a/android/paths_test.go b/android/paths_test.go
index f4e4ce1..3f4625d 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -137,26 +137,35 @@
 
 func TestOptionalPath(t *testing.T) {
 	var path OptionalPath
-	checkInvalidOptionalPath(t, path)
+	checkInvalidOptionalPath(t, path, "unknown")
 
 	path = OptionalPathForPath(nil)
-	checkInvalidOptionalPath(t, path)
+	checkInvalidOptionalPath(t, path, "unknown")
+
+	path = InvalidOptionalPath("foo")
+	checkInvalidOptionalPath(t, path, "foo")
+
+	path = InvalidOptionalPath("")
+	checkInvalidOptionalPath(t, path, "unknown")
 
 	path = OptionalPathForPath(PathForTesting("path"))
 	checkValidOptionalPath(t, path, "path")
 }
 
-func checkInvalidOptionalPath(t *testing.T, path OptionalPath) {
+func checkInvalidOptionalPath(t *testing.T, path OptionalPath, expectedInvalidReason string) {
 	t.Helper()
 	if path.Valid() {
-		t.Errorf("Uninitialized OptionalPath should not be valid")
+		t.Errorf("Invalid OptionalPath should not be valid")
+	}
+	if path.InvalidReason() != expectedInvalidReason {
+		t.Errorf("Wrong invalid reason: expected %q, got %q", expectedInvalidReason, path.InvalidReason())
 	}
 	if path.String() != "" {
-		t.Errorf("Uninitialized OptionalPath String() should return \"\", not %q", path.String())
+		t.Errorf("Invalid OptionalPath String() should return \"\", not %q", path.String())
 	}
 	paths := path.AsPaths()
 	if len(paths) != 0 {
-		t.Errorf("Uninitialized OptionalPath AsPaths() should return empty Paths, not %q", paths)
+		t.Errorf("Invalid OptionalPath AsPaths() should return empty Paths, not %q", paths)
 	}
 	defer func() {
 		if r := recover(); r == nil {
@@ -171,6 +180,9 @@
 	if !path.Valid() {
 		t.Errorf("Initialized OptionalPath should not be invalid")
 	}
+	if path.InvalidReason() != "" {
+		t.Errorf("Initialized OptionalPath should not have an invalid reason, got: %q", path.InvalidReason())
+	}
 	if path.String() != expectedString {
 		t.Errorf("Initialized OptionalPath String() should return %q, not %q", expectedString, path.String())
 	}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index e611502..e189892 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -240,7 +240,7 @@
 			value = value.Elem()
 		}
 		if value.Kind() != reflect.String {
-			panic(fmt.Errorf("prebuilt src field %q should be a string or a pointer to one but was %d %q", srcPropertyName, value.Kind(), value))
+			panic(fmt.Errorf("prebuilt src field %q in %T in module %s should be a string or a pointer to one but was %v", srcField, srcProps, module, value))
 		}
 		src := value.String()
 		if src == "" {
diff --git a/android/queryview.go b/android/queryview.go
deleted file mode 100644
index 224652e..0000000
--- a/android/queryview.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// 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 android
-
-import (
-	"fmt"
-	"os"
-	"strings"
-
-	"github.com/google/blueprint"
-)
-
-// The Bazel QueryView singleton is responsible for generating the Ninja actions
-// for calling the soong_build primary builder in the main build.ninja file.
-func init() {
-	RegisterSingletonType("bazel_queryview", BazelQueryViewSingleton)
-}
-
-// BazelQueryViewSingleton is the singleton responsible for registering the
-// soong_build build statement that will convert the Soong module graph after
-// applying *all* mutators, enabing the feature to query the final state of the
-// Soong graph. This mode is meant for querying the build graph state, and not meant
-// for generating BUILD files to be checked in.
-func BazelQueryViewSingleton() Singleton {
-	return &bazelQueryViewSingleton{}
-}
-
-// BazelConverterSingleton is the singleton responsible for registering the soong_build
-// build statement that will convert the Soong module graph by applying an alternate
-// pipeline of mutators, with the goal of reaching semantic equivalence between the original
-// Blueprint and final BUILD files. Using this mode, the goal is to be able to
-// build with these BUILD files directly in the source tree.
-func BazelConverterSingleton() Singleton {
-	return &bazelConverterSingleton{}
-}
-
-type bazelQueryViewSingleton struct{}
-type bazelConverterSingleton struct{}
-
-func generateBuildActionsForBazelConversion(ctx SingletonContext, converterMode bool) {
-	name := "queryview"
-	descriptionTemplate := "[EXPERIMENTAL, PRE-PRODUCTION] Creating the Bazel QueryView workspace with %s at $outDir"
-
-	// Create a build and rule statement, using the Bazel QueryView's WORKSPACE
-	// file as the output file marker.
-	var deps Paths
-	moduleListFilePath := pathForBuildToolDep(ctx, ctx.Config().moduleListFile)
-	deps = append(deps, moduleListFilePath)
-	deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().ProductVariablesFileName))
-
-	bazelQueryViewDirectory := PathForOutput(ctx, name)
-	bazelQueryViewWorkspaceFile := bazelQueryViewDirectory.Join(ctx, "WORKSPACE")
-	primaryBuilder := primaryBuilderPath(ctx)
-	bazelQueryView := ctx.Rule(pctx, "bazelQueryView",
-		blueprint.RuleParams{
-			Command: fmt.Sprintf(
-				`rm -rf "${outDir}/"* && `+
-					`mkdir -p "${outDir}" && `+
-					`echo WORKSPACE: $$(cat "%s") > "${outDir}/.queryview-depfile.d" && `+
-					`BUILDER="%s" && `+
-					`echo BUILDER=$$BUILDER && `+
-					`cd "$$(dirname "$$BUILDER")" && `+
-					`echo PWD=$$PWD && `+
-					`ABSBUILDER="$$PWD/$$(basename "$$BUILDER")" && `+
-					`echo ABSBUILDER=$$ABSBUILDER && `+
-					`cd / && `+
-					`env -i "$$ABSBUILDER" --bazel_queryview_dir "${outDir}" "%s"`,
-				moduleListFilePath.String(), // Use the contents of Android.bp.list as the depfile.
-				primaryBuilder.String(),
-				strings.Join(os.Args[1:], "\" \""),
-			),
-			CommandDeps: []string{primaryBuilder.String()},
-			Description: fmt.Sprintf(
-				descriptionTemplate,
-				primaryBuilder.Base()),
-			Deps:    blueprint.DepsGCC,
-			Depfile: "${outDir}/.queryview-depfile.d",
-		},
-		"outDir")
-
-	ctx.Build(pctx, BuildParams{
-		Rule:   bazelQueryView,
-		Output: bazelQueryViewWorkspaceFile,
-		Inputs: deps,
-		Args: map[string]string{
-			"outDir": bazelQueryViewDirectory.String(),
-		},
-	})
-
-	// Add a phony target for generating the workspace
-	ctx.Phony(name, bazelQueryViewWorkspaceFile)
-}
-
-func (c *bazelQueryViewSingleton) GenerateBuildActions(ctx SingletonContext) {
-	generateBuildActionsForBazelConversion(ctx, false)
-}
-
-func (c *bazelConverterSingleton) GenerateBuildActions(ctx SingletonContext) {
-	generateBuildActionsForBazelConversion(ctx, true)
-}
diff --git a/android/sdk.go b/android/sdk.go
index da740f3..100f63b 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -22,6 +22,8 @@
 	"github.com/google/blueprint/proptools"
 )
 
+// RequiredSdks provides access to the set of SDKs required by an APEX and its contents.
+//
 // Extracted from SdkAware to make it easier to define custom subsets of the
 // SdkAware interface and improve code navigation within the IDE.
 //
@@ -30,11 +32,11 @@
 // is expected to implement RequiredSdks() by reading its own properties like
 // `uses_sdks`.
 type RequiredSdks interface {
-	// The set of SDKs required by an APEX and its contents.
+	// RequiredSdks returns the set of SDKs required by an APEX and its contents.
 	RequiredSdks() SdkRefs
 }
 
-// Provided to improve code navigation with the IDE.
+// sdkAwareWithoutModule is provided simply to improve code navigation with the IDE.
 type sdkAwareWithoutModule interface {
 	RequiredSdks
 
@@ -233,75 +235,85 @@
 	return false
 }
 
-// Provide support for generating the build rules which will build the snapshot.
+// SnapshotBuilder provides support for generating the build rules which will build the snapshot.
 type SnapshotBuilder interface {
-	// Copy src to the dest (which is a snapshot relative path) and add the dest
-	// to the zip
+	// CopyToSnapshot generates a rule that will copy the src to the dest (which is a snapshot
+	// relative path) and add the dest to the zip.
 	CopyToSnapshot(src Path, dest string)
 
-	// Return the path to an empty file.
+	// EmptyFile returns the path to an empty file.
 	//
 	// This can be used by sdk member types that need to create an empty file in the snapshot, simply
 	// pass the value returned from this to the CopyToSnapshot() method.
 	EmptyFile() Path
 
-	// Unzip the supplied zip into the snapshot relative directory destDir.
+	// UnzipToSnapshot generates a rule that will unzip the supplied zip into the snapshot relative
+	// directory destDir.
 	UnzipToSnapshot(zipPath Path, destDir string)
 
-	// Add a new prebuilt module to the snapshot. The returned module
-	// must be populated with the module type specific properties. The following
-	// properties will be automatically populated.
+	// AddPrebuiltModule adds a new prebuilt module to the snapshot.
+	//
+	// It is intended to be called from SdkMemberType.AddPrebuiltModule which can add module type
+	// specific properties that are not variant specific. The following properties will be
+	// automatically populated before returning.
 	//
 	// * name
 	// * sdk_member_name
 	// * prefer
 	//
-	// This will result in two Soong modules being generated in the Android. One
-	// that is versioned, coupled to the snapshot version and marked as
-	// prefer=true. And one that is not versioned, not marked as prefer=true and
-	// will only be used if the equivalently named non-prebuilt module is not
-	// present.
+	// Properties that are variant specific will be handled by SdkMemberProperties structure.
+	//
+	// Each module created by this method can be output to the generated Android.bp file in two
+	// different forms, depending on the setting of the SOONG_SDK_SNAPSHOT_VERSION build property.
+	// The two forms are:
+	// 1. A versioned Soong module that is referenced from a corresponding similarly versioned
+	//    snapshot module.
+	// 2. An unversioned Soong module that.
+	//
+	// See sdk/update.go for more information.
 	AddPrebuiltModule(member SdkMember, moduleType string) BpModule
 
-	// The property tag to use when adding a property to a BpModule that contains
-	// references to other sdk members. Using this will ensure that the reference
-	// is correctly output for both versioned and unversioned prebuilts in the
-	// snapshot.
+	// SdkMemberReferencePropertyTag returns a property tag to use when adding a property to a
+	// BpModule that contains references to other sdk members.
 	//
-	// "required: true" means that the property must only contain references
-	// to other members of the sdk. Passing a reference to a module that is not a
-	// member of the sdk will result in a build error.
+	// Using this will ensure that the reference is correctly output for both versioned and
+	// unversioned prebuilts in the snapshot.
 	//
-	// "required: false" means that the property can contain references to modules
-	// that are either members or not members of the sdk. If a reference is to a
-	// module that is a non member then the reference is left unchanged, i.e. it
-	// is not transformed as references to members are.
+	// "required: true" means that the property must only contain references to other members of the
+	// sdk. Passing a reference to a module that is not a member of the sdk will result in a build
+	// error.
 	//
-	// The handling of the member names is dependent on whether it is an internal or
-	// exported member. An exported member is one whose name is specified in one of
-	// the member type specific properties. An internal member is one that is added
-	// due to being a part of an exported (or other internal) member and is not itself
-	// an exported member.
+	// "required: false" means that the property can contain references to modules that are either
+	// members or not members of the sdk. If a reference is to a module that is a non member then the
+	// reference is left unchanged, i.e. it is not transformed as references to members are.
+	//
+	// The handling of the member names is dependent on whether it is an internal or exported member.
+	// An exported member is one whose name is specified in one of the member type specific
+	// properties. An internal member is one that is added due to being a part of an exported (or
+	// other internal) member and is not itself an exported member.
 	//
 	// Member names are handled as follows:
-	// * When creating the unversioned form of the module the name is left unchecked
-	//   unless the member is internal in which case it is transformed into an sdk
-	//   specific name, i.e. by prefixing with the sdk name.
+	// * When creating the unversioned form of the module the name is left unchecked unless the member
+	//   is internal in which case it is transformed into an sdk specific name, i.e. by prefixing with
+	//   the sdk name.
 	//
-	// * When creating the versioned form of the module the name is transformed into
-	//   a versioned sdk specific name, i.e. by prefixing with the sdk name and
-	//   suffixing with the version.
+	// * When creating the versioned form of the module the name is transformed into a versioned sdk
+	//   specific name, i.e. by prefixing with the sdk name and suffixing with the version.
 	//
 	// e.g.
 	// bpPropertySet.AddPropertyWithTag("libs", []string{"member1", "member2"}, builder.SdkMemberReferencePropertyTag(true))
 	SdkMemberReferencePropertyTag(required bool) BpPropertyTag
 }
 
+// BpPropertyTag is a marker interface that can be associated with properties in a BpPropertySet to
+// provide additional information which can be used to customize their behavior.
 type BpPropertyTag interface{}
 
-// A set of properties for use in a .bp file.
+// BpPropertySet is a set of properties for use in a .bp file.
 type BpPropertySet interface {
-	// Add a property, the value can be one of the following types:
+	// AddProperty adds a property.
+	//
+	// The value can be one of the following types:
 	// * string
 	// * array of the above
 	// * bool
@@ -326,18 +338,18 @@
 	// * Otherwise, if a property exists with the name then it is an error.
 	AddProperty(name string, value interface{})
 
-	// Add a property with an associated tag
+	// AddPropertyWithTag adds a property with an associated property tag.
 	AddPropertyWithTag(name string, value interface{}, tag BpPropertyTag)
 
-	// Add a property set with the specified name and return so that additional
-	// properties can be added.
+	// AddPropertySet adds a property set with the specified name and returns it so that additional
+	// properties can be added to it.
 	AddPropertySet(name string) BpPropertySet
 
-	// Add comment for property (or property set).
+	// AddCommentForProperty adds a comment for the named property (or property set).
 	AddCommentForProperty(name, text string)
 }
 
-// A .bp module definition.
+// BpModule represents a module definition in a .bp file.
 type BpModule interface {
 	BpPropertySet
 
@@ -364,19 +376,20 @@
 
 var _ BpPrintable = BpPrintableBase{}
 
-// An individual member of the SDK, includes all of the variants that the SDK
-// requires.
+// SdkMember is an individual member of the SDK.
+//
+// It includes all of the variants that the SDK depends upon.
 type SdkMember interface {
-	// The name of the member.
+	// Name returns the name of the member.
 	Name() string
 
-	// All the variants required by the SDK.
+	// Variants returns all the variants of this module depended upon by the SDK.
 	Variants() []SdkAware
 }
 
-// SdkMemberTypeDependencyTag is the interface that a tag must implement in order to allow the
+// SdkMemberDependencyTag is the interface that a tag must implement in order to allow the
 // dependent module to be automatically added to the sdk.
-type SdkMemberTypeDependencyTag interface {
+type SdkMemberDependencyTag interface {
 	blueprint.DependencyTag
 
 	// SdkMemberType returns the SdkMemberType that will be used to automatically add the child module
@@ -401,7 +414,7 @@
 	ExportMember() bool
 }
 
-var _ SdkMemberTypeDependencyTag = (*sdkMemberDependencyTag)(nil)
+var _ SdkMemberDependencyTag = (*sdkMemberDependencyTag)(nil)
 var _ ReplaceSourceWithPrebuilt = (*sdkMemberDependencyTag)(nil)
 
 type sdkMemberDependencyTag struct {
@@ -418,20 +431,20 @@
 	return t.export
 }
 
-// Prevent dependencies from the sdk/module_exports onto their members from being
-// replaced with a preferred prebuilt.
+// ReplaceSourceWithPrebuilt prevents dependencies from the sdk/module_exports onto their members
+// from being replaced with a preferred prebuilt.
 func (t *sdkMemberDependencyTag) ReplaceSourceWithPrebuilt() bool {
 	return false
 }
 
-// DependencyTagForSdkMemberType creates an SdkMemberTypeDependencyTag that will cause any
+// DependencyTagForSdkMemberType creates an SdkMemberDependencyTag that will cause any
 // dependencies added by the tag to be added to the sdk as the specified SdkMemberType and exported
 // (or not) as specified by the export parameter.
-func DependencyTagForSdkMemberType(memberType SdkMemberType, export bool) SdkMemberTypeDependencyTag {
+func DependencyTagForSdkMemberType(memberType SdkMemberType, export bool) SdkMemberDependencyTag {
 	return &sdkMemberDependencyTag{memberType: memberType, export: export}
 }
 
-// Interface that must be implemented for every type that can be a member of an
+// SdkMemberType is the interface that must be implemented for every type that can be a member of an
 // sdk.
 //
 // The basic implementation should look something like this, where ModuleType is
@@ -452,43 +465,43 @@
 //    ...methods...
 //
 type SdkMemberType interface {
-	// The name of the member type property on an sdk module.
+	// SdkPropertyName returns the name of the member type property on an sdk module.
 	SdkPropertyName() string
 
 	// RequiresBpProperty returns true if this member type requires its property to be usable within
 	// an Android.bp file.
 	RequiresBpProperty() bool
 
-	// True if the member type supports the sdk/sdk_snapshot, false otherwise.
+	// UsableWithSdkAndSdkSnapshot returns true if the member type supports the sdk/sdk_snapshot,
+	// false otherwise.
 	UsableWithSdkAndSdkSnapshot() bool
 
-	// Return true if prebuilt host artifacts may be specific to the host OS. Only
-	// applicable to modules where HostSupported() is true. If this is true,
-	// snapshots will list each host OS variant explicitly and disable all other
-	// host OS'es.
+	// IsHostOsDependent returns true if prebuilt host artifacts may be specific to the host OS. Only
+	// applicable to modules where HostSupported() is true. If this is true, snapshots will list each
+	// host OS variant explicitly and disable all other host OS'es.
 	IsHostOsDependent() bool
 
-	// Add dependencies from the SDK module to all the module variants the member
-	// type contributes to the SDK. `names` is the list of module names given in
-	// the member type property (as returned by SdkPropertyName()) in the SDK
-	// module. The exact set of variants required is determined by the SDK and its
-	// properties. The dependencies must be added with the supplied tag.
+	// AddDependencies adds dependencies from the SDK module to all the module variants the member
+	// type contributes to the SDK. `names` is the list of module names given in the member type
+	// property (as returned by SdkPropertyName()) in the SDK module. The exact set of variants
+	// required is determined by the SDK and its properties. The dependencies must be added with the
+	// supplied tag.
 	//
 	// The BottomUpMutatorContext provided is for the SDK module.
-	AddDependencies(mctx BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string)
+	AddDependencies(ctx SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string)
 
-	// Return true if the supplied module is an instance of this member type.
+	// IsInstance returns true if the supplied module is an instance of this member type.
 	//
-	// This is used to check the type of each variant before added to the
-	// SdkMember. Returning false will cause an error to be logged expaining that
-	// the module is not allowed in whichever sdk property it was added.
+	// This is used to check the type of each variant before added to the SdkMember. Returning false
+	// will cause an error to be logged explaining that the module is not allowed in whichever sdk
+	// property it was added.
 	IsInstance(module Module) bool
 
 	// UsesSourceModuleTypeInSnapshot returns true when the AddPrebuiltModule() method returns a
 	// source module type.
 	UsesSourceModuleTypeInSnapshot() bool
 
-	// Add a prebuilt module that the sdk will populate.
+	// AddPrebuiltModule is called to add a prebuilt module that the sdk will populate.
 	//
 	// The sdk module code generates the snapshot as follows:
 	//
@@ -525,11 +538,19 @@
 	//
 	AddPrebuiltModule(ctx SdkMemberContext, member SdkMember) BpModule
 
-	// Create a structure into which variant specific properties can be added.
+	// CreateVariantPropertiesStruct creates a structure into which variant specific properties can be
+	// added.
 	CreateVariantPropertiesStruct() SdkMemberProperties
 }
 
-// Base type for SdkMemberType implementations.
+// SdkDependencyContext provides access to information needed by the SdkMemberType.AddDependencies()
+// implementations.
+type SdkDependencyContext interface {
+	BottomUpMutatorContext
+}
+
+// SdkMemberTypeBase is the base type for SdkMemberType implementations and must be embedded in any
+// struct that implements SdkMemberType.
 type SdkMemberTypeBase struct {
 	PropertyName string
 
@@ -566,13 +587,10 @@
 	return b.UseSourceModuleTypeInSnapshot
 }
 
-// Encapsulates the information about registered SdkMemberTypes.
+// SdkMemberTypesRegistry encapsulates the information about registered SdkMemberTypes.
 type SdkMemberTypesRegistry struct {
 	// The list of types sorted by property name.
 	list []SdkMemberType
-
-	// The key that uniquely identifies this registry instance.
-	key OnceKey
 }
 
 func (r *SdkMemberTypesRegistry) copyAndAppend(memberType SdkMemberType) *SdkMemberTypesRegistry {
@@ -592,18 +610,9 @@
 		return t1.SdkPropertyName() < t2.SdkPropertyName()
 	})
 
-	// Generate a key that identifies the slice of SdkMemberTypes by joining the property names
-	// from all the SdkMemberType .
-	var properties []string
-	for _, t := range list {
-		properties = append(properties, t.SdkPropertyName())
-	}
-	key := NewOnceKey(strings.Join(properties, "|"))
-
 	// Create a new registry so the pointer uniquely identifies the set of registered types.
 	return &SdkMemberTypesRegistry{
 		list: list,
-		key:  key,
 	}
 }
 
@@ -616,12 +625,15 @@
 	return NewCustomOnceKey(r)
 }
 
-// The set of registered SdkMemberTypes, one for sdk module and one for module_exports.
+// ModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports modules.
 var ModuleExportsMemberTypes = &SdkMemberTypesRegistry{}
+
+// SdkMemberTypes is the set of registered SdkMemberTypes for sdk modules.
 var SdkMemberTypes = &SdkMemberTypesRegistry{}
 
-// Register an SdkMemberType object to allow them to be used in the sdk and sdk_snapshot module
-// types.
+// RegisterSdkMemberType registers an SdkMemberType object to allow them to be used in the
+// module_exports, module_exports_snapshot and (depending on the value returned from
+// SdkMemberType.UsableWithSdkAndSdkSnapshot) the sdk and sdk_snapshot module types.
 func RegisterSdkMemberType(memberType SdkMemberType) {
 	// All member types are usable with module_exports.
 	ModuleExportsMemberTypes = ModuleExportsMemberTypes.copyAndAppend(memberType)
@@ -632,7 +644,8 @@
 	}
 }
 
-// Base structure for all implementations of SdkMemberProperties.
+// SdkMemberPropertiesBase is the base structure for all implementations of SdkMemberProperties and
+// must be embedded in any struct that implements SdkMemberProperties.
 //
 // Contains common properties that apply across many different member types.
 type SdkMemberPropertiesBase struct {
@@ -659,7 +672,7 @@
 	Compile_multilib string `android:"arch_variant"`
 }
 
-// The os prefix to use for any file paths in the sdk.
+// OsPrefix returns the os prefix to use for any file paths in the sdk.
 //
 // Is an empty string if the member only provides variants for a single os type, otherwise
 // is the OsType.Name.
@@ -675,35 +688,47 @@
 	return b
 }
 
-// Interface to be implemented on top of a structure that contains variant specific
-// information.
+// SdkMemberProperties is the interface to be implemented on top of a structure that contains
+// variant specific information.
 //
-// Struct fields that are capitalized are examined for common values to extract. Fields
-// that are not capitalized are assumed to be arch specific.
+// Struct fields that are capitalized are examined for common values to extract. Fields that are not
+// capitalized are assumed to be arch specific.
 type SdkMemberProperties interface {
-	// Access the base structure.
+	// Base returns the base structure.
 	Base() *SdkMemberPropertiesBase
 
-	// Populate this structure with information from the variant.
+	// PopulateFromVariant populates this structure with information from a module variant.
+	//
+	// It will typically be called once for each variant of a member module that the SDK depends upon.
 	PopulateFromVariant(ctx SdkMemberContext, variant Module)
 
-	// Add the information from this structure to the property set.
+	// AddToPropertySet adds the information from this structure to the property set.
+	//
+	// This will be called for each instance of this structure on which the PopulateFromVariant method
+	// was called and also on a number of different instances of this structure into which properties
+	// common to one or more variants have been copied. Therefore, implementations of this must handle
+	// the case when this structure is only partially populated.
 	AddToPropertySet(ctx SdkMemberContext, propertySet BpPropertySet)
 }
 
-// Provides access to information common to a specific member.
+// SdkMemberContext provides access to information common to a specific member.
 type SdkMemberContext interface {
 
-	// The module context of the sdk common os variant which is creating the snapshot.
+	// SdkModuleContext returns the module context of the sdk common os variant which is creating the
+	// snapshot.
+	//
+	// This is common to all members of the sdk and is not specific to the member being processed.
+	// If information about the member being processed needs to be obtained from this ModuleContext it
+	// must be obtained using one of the OtherModule... methods not the Module... methods.
 	SdkModuleContext() ModuleContext
 
-	// The builder of the snapshot.
+	// SnapshotBuilder the builder of the snapshot.
 	SnapshotBuilder() SnapshotBuilder
 
-	// The type of the member.
+	// MemberType returns the type of the member currently being processed.
 	MemberType() SdkMemberType
 
-	// The name of the member.
+	// Name returns the name of the member currently being processed.
 	//
 	// Provided for use by sdk members to create a member specific location within the snapshot
 	// into which to copy the prebuilt files.
diff --git a/android/testing.go b/android/testing.go
index e25e5c5..b9d8fa8 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -491,6 +491,66 @@
 	ctx.preSingletons = append(ctx.preSingletons, newPreSingleton(name, factory))
 }
 
+// ModuleVariantForTests selects a specific variant of the module with the given
+// name by matching the variations map against the variations of each module
+// variant. A module variant matches the map if every variation that exists in
+// both have the same value. Both the module and the map are allowed to have
+// extra variations that the other doesn't have. Panics if not exactly one
+// module variant matches.
+func (ctx *TestContext) ModuleVariantForTests(name string, matchVariations map[string]string) TestingModule {
+	modules := []Module{}
+	ctx.VisitAllModules(func(m blueprint.Module) {
+		if ctx.ModuleName(m) == name {
+			am := m.(Module)
+			amMut := am.base().commonProperties.DebugMutators
+			amVar := am.base().commonProperties.DebugVariations
+			matched := true
+			for i, mut := range amMut {
+				if wantedVar, found := matchVariations[mut]; found && amVar[i] != wantedVar {
+					matched = false
+					break
+				}
+			}
+			if matched {
+				modules = append(modules, am)
+			}
+		}
+	})
+
+	if len(modules) == 0 {
+		// Show all the modules or module variants that do exist.
+		var allModuleNames []string
+		var allVariants []string
+		ctx.VisitAllModules(func(m blueprint.Module) {
+			allModuleNames = append(allModuleNames, ctx.ModuleName(m))
+			if ctx.ModuleName(m) == name {
+				allVariants = append(allVariants, m.(Module).String())
+			}
+		})
+
+		if len(allVariants) == 0 {
+			panic(fmt.Errorf("failed to find module %q. All modules:\n  %s",
+				name, strings.Join(SortedUniqueStrings(allModuleNames), "\n  ")))
+		} else {
+			sort.Strings(allVariants)
+			panic(fmt.Errorf("failed to find module %q matching %v. All variants:\n  %s",
+				name, matchVariations, strings.Join(allVariants, "\n  ")))
+		}
+	}
+
+	if len(modules) > 1 {
+		moduleStrings := []string{}
+		for _, m := range modules {
+			moduleStrings = append(moduleStrings, m.String())
+		}
+		sort.Strings(moduleStrings)
+		panic(fmt.Errorf("module %q has more than one variant that match %v:\n  %s",
+			name, matchVariations, strings.Join(moduleStrings, "\n  ")))
+	}
+
+	return newTestingModule(ctx.config, modules[0])
+}
+
 func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
 	var module Module
 	ctx.VisitAllModules(func(m blueprint.Module) {
@@ -509,12 +569,11 @@
 				allVariants = append(allVariants, ctx.ModuleSubDir(m))
 			}
 		})
-		sort.Strings(allModuleNames)
 		sort.Strings(allVariants)
 
 		if len(allVariants) == 0 {
 			panic(fmt.Errorf("failed to find module %q. All modules:\n  %s",
-				name, strings.Join(allModuleNames, "\n  ")))
+				name, strings.Join(SortedUniqueStrings(allModuleNames), "\n  ")))
 		} else {
 			panic(fmt.Errorf("failed to find module %q variant %q. All variants:\n  %s",
 				name, variant, strings.Join(allVariants, "\n  ")))
@@ -749,7 +808,7 @@
 }
 
 func (b baseTestingComponent) maybeBuildParamsFromOutput(file string) (TestingBuildParams, []string) {
-	var searchedOutputs []string
+	searchedOutputs := WritablePaths(nil)
 	for _, p := range b.provider.BuildParamsForTests() {
 		outputs := append(WritablePaths(nil), p.Outputs...)
 		outputs = append(outputs, p.ImplicitOutputs...)
@@ -760,10 +819,17 @@
 			if f.String() == file || f.Rel() == file || PathRelativeToTop(f) == file {
 				return b.newTestingBuildParams(p), nil
 			}
-			searchedOutputs = append(searchedOutputs, f.Rel())
+			searchedOutputs = append(searchedOutputs, f)
 		}
 	}
-	return TestingBuildParams{}, searchedOutputs
+
+	formattedOutputs := []string{}
+	for _, f := range searchedOutputs {
+		formattedOutputs = append(formattedOutputs,
+			fmt.Sprintf("%s (rel=%s)", PathRelativeToTop(f), f.Rel()))
+	}
+
+	return TestingBuildParams{}, formattedOutputs
 }
 
 func (b baseTestingComponent) buildParamsFromOutput(file string) TestingBuildParams {
diff --git a/android/variable.go b/android/variable.go
index 5cf9aa8..a308d2b 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -46,6 +46,10 @@
 			Java_resource_dirs []string
 		}
 
+		Platform_sdk_extension_version struct {
+			Cmd *string
+		}
+
 		// unbundled_build is a catch-all property to annotate modules that don't build in one or
 		// more unbundled branches, usually due to dependencies missing from the manifest.
 		Unbundled_build struct {
@@ -108,6 +112,8 @@
 			Static_libs       []string
 			Whole_static_libs []string
 			Shared_libs       []string
+
+			Cmdline []string
 		}
 
 		// eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
@@ -170,6 +176,7 @@
 	Platform_sdk_codename                     *string  `json:",omitempty"`
 	Platform_sdk_version_or_codename          *string  `json:",omitempty"`
 	Platform_sdk_final                        *bool    `json:",omitempty"`
+	Platform_sdk_extension_version            *int     `json:",omitempty"`
 	Platform_version_active_codenames         []string `json:",omitempty"`
 	Platform_vndk_version                     *string  `json:",omitempty"`
 	Platform_systemsdk_versions               []string `json:",omitempty"`
@@ -326,6 +333,7 @@
 	VendorSnapshotDirsExcluded   []string `json:",omitempty"`
 	RecoverySnapshotDirsExcluded []string `json:",omitempty"`
 	RecoverySnapshotDirsIncluded []string `json:",omitempty"`
+	HostFakeSnapshotEnabled      bool     `json:",omitempty"`
 
 	BoardVendorSepolicyDirs      []string `json:",omitempty"`
 	BoardOdmSepolicyDirs         []string `json:",omitempty"`
diff --git a/android/visibility_test.go b/android/visibility_test.go
index ffd7909..714c92a 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -16,7 +16,7 @@
 	{
 		name: "invalid visibility: empty list",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: [],
@@ -27,7 +27,7 @@
 	{
 		name: "invalid visibility: empty rule",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: [""],
@@ -38,7 +38,7 @@
 	{
 		name: "invalid visibility: unqualified",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["target"],
@@ -49,7 +49,7 @@
 	{
 		name: "invalid visibility: empty namespace",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//"],
@@ -60,7 +60,7 @@
 	{
 		name: "invalid visibility: empty module",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: [":"],
@@ -71,7 +71,7 @@
 	{
 		name: "invalid visibility: empty namespace and module",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//:"],
@@ -82,7 +82,7 @@
 	{
 		name: "//visibility:unknown",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//visibility:unknown"],
@@ -93,7 +93,7 @@
 	{
 		name: "//visibility:xxx mixed",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//visibility:public", "//namespace"],
@@ -114,7 +114,7 @@
 	{
 		name: "//visibility:legacy_public",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//visibility:legacy_public"],
@@ -130,7 +130,7 @@
 		// the current directory, a nested directory and a directory in a separate tree.
 		name: "//visibility:public",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//visibility:public"],
@@ -140,12 +140,12 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
@@ -157,7 +157,7 @@
 		// directory only.
 		name: "//visibility:private",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//visibility:private"],
@@ -167,12 +167,12 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
@@ -189,7 +189,7 @@
 		// Verify that :__pkg__ allows the module to be referenced from the current directory only.
 		name: ":__pkg__",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: [":__pkg__"],
@@ -199,12 +199,12 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
@@ -222,7 +222,7 @@
 		// the top/nested directory only, not a subdirectory of top/nested and not peak directory.
 		name: "//top/nested",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//top/nested"],
@@ -232,17 +232,17 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"top/nested/again/Blueprints": []byte(`
+			"top/nested/again/Android.bp": []byte(`
 				mock_library {
 					name: "libnestedagain",
 					deps: ["libexample"],
 				}`),
-			"peak/Blueprints": []byte(`
+			"peak/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
@@ -260,7 +260,7 @@
 		// and sub directories but nowhere else.
 		name: ":__subpackages__",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: [":__subpackages__"],
@@ -270,12 +270,12 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"peak/other/Blueprints": []byte(`
+			"peak/other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
@@ -291,7 +291,7 @@
 		// directory and sub directories but nowhere else.
 		name: "//top/nested:__subpackages__",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//top/nested:__subpackages__", "//other"],
@@ -301,12 +301,12 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"top/other/Blueprints": []byte(`
+			"top/other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
@@ -322,7 +322,7 @@
 		// the current directory, top/nested and peak and all its subpackages.
 		name: `["//top/nested", "//peak:__subpackages__"]`,
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//top/nested", "//peak:__subpackages__"],
@@ -332,12 +332,12 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"peak/other/Blueprints": []byte(`
+			"peak/other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
@@ -348,7 +348,7 @@
 		// Verify that //vendor... cannot be used outside vendor apart from //vendor:__subpackages__
 		name: `//vendor`,
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//vendor:__subpackages__"],
@@ -358,13 +358,13 @@
 					name: "libsamepackage",
 					visibility: ["//vendor/apps/AcmeSettings"],
 				}`),
-			"vendor/Blueprints": []byte(`
+			"vendor/Android.bp": []byte(`
 				mock_library {
 					name: "libvendorexample",
 					deps: ["libexample"],
 					visibility: ["//vendor/nested"],
 				}`),
-			"vendor/nested/Blueprints": []byte(`
+			"vendor/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libvendornested",
 					deps: ["libexample", "libvendorexample"],
@@ -382,7 +382,7 @@
 		// Check that visibility is the union of the defaults modules.
 		name: "defaults union, basic",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//other"],
@@ -396,17 +396,17 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -420,7 +420,7 @@
 	{
 		name: "defaults union, multiple defaults",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults_1",
 					visibility: ["//other"],
@@ -437,17 +437,17 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -461,7 +461,7 @@
 	{
 		name: "//visibility:public mixed with other in defaults",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//visibility:public", "//namespace"],
@@ -479,7 +479,7 @@
 	{
 		name: "//visibility:public overriding defaults",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//namespace"],
@@ -489,7 +489,7 @@
 					visibility: ["//visibility:public"],
 					defaults: ["libexample_defaults"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -502,7 +502,7 @@
 	{
 		name: "//visibility:public mixed with other from different defaults 1",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults_1",
 					visibility: ["//namespace"],
@@ -515,7 +515,7 @@
 					name: "libexample",
 					defaults: ["libexample_defaults_1", "libexample_defaults_2"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -525,7 +525,7 @@
 	{
 		name: "//visibility:public mixed with other from different defaults 2",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults_1",
 					visibility: ["//visibility:public"],
@@ -538,7 +538,7 @@
 					name: "libexample",
 					defaults: ["libexample_defaults_1", "libexample_defaults_2"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -548,7 +548,7 @@
 	{
 		name: "//visibility:private in defaults",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//visibility:private"],
@@ -561,12 +561,12 @@
 					name: "libsamepackage",
 					deps: ["libexample"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
@@ -582,7 +582,7 @@
 	{
 		name: "//visibility:private mixed with other in defaults",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//visibility:private", "//namespace"],
@@ -600,7 +600,7 @@
 	{
 		name: "//visibility:private overriding defaults",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//namespace"],
@@ -619,7 +619,7 @@
 	{
 		name: "//visibility:private in defaults overridden",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//visibility:private"],
@@ -638,7 +638,7 @@
 	{
 		name: "//visibility:private override //visibility:public",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//visibility:public"],
@@ -656,7 +656,7 @@
 	{
 		name: "//visibility:public override //visibility:private",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//visibility:private"],
@@ -674,7 +674,7 @@
 	{
 		name: "//visibility:override must be first in the list",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_library {
 					name: "libexample",
 					visibility: ["//other", "//visibility:override", "//namespace"],
@@ -687,7 +687,7 @@
 	{
 		name: "//visibility:override discards //visibility:private",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//visibility:private"],
@@ -698,7 +698,7 @@
 					visibility: ["//visibility:override", "//other"],
 					defaults: ["libexample_defaults"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
@@ -708,7 +708,7 @@
 	{
 		name: "//visibility:override discards //visibility:public",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//visibility:public"],
@@ -719,12 +719,12 @@
 					visibility: ["//visibility:override", "//other"],
 					defaults: ["libexample_defaults"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
 				}`),
-			"namespace/Blueprints": []byte(`
+			"namespace/Android.bp": []byte(`
 				mock_library {
 					name: "libnamespace",
 					deps: ["libexample"],
@@ -737,7 +737,7 @@
 	{
 		name: "//visibility:override discards defaults supplied rules",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//namespace"],
@@ -748,12 +748,12 @@
 					visibility: ["//visibility:override", "//other"],
 					defaults: ["libexample_defaults"],
 				}`),
-			"other/Blueprints": []byte(`
+			"other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libexample"],
 				}`),
-			"namespace/Blueprints": []byte(`
+			"namespace/Android.bp": []byte(`
 				mock_library {
 					name: "libnamespace",
 					deps: ["libexample"],
@@ -766,7 +766,7 @@
 	{
 		name: "//visibility:override can override //visibility:public with //visibility:private",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//visibility:public"],
@@ -776,7 +776,7 @@
 					visibility: ["//visibility:override", "//visibility:private"],
 					defaults: ["libexample_defaults"],
 				}`),
-			"namespace/Blueprints": []byte(`
+			"namespace/Android.bp": []byte(`
 				mock_library {
 					name: "libnamespace",
 					deps: ["libexample"],
@@ -789,7 +789,7 @@
 	{
 		name: "//visibility:override can override //visibility:private with //visibility:public",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults",
 					visibility: ["//visibility:private"],
@@ -799,7 +799,7 @@
 					visibility: ["//visibility:override", "//visibility:public"],
 					defaults: ["libexample_defaults"],
 				}`),
-			"namespace/Blueprints": []byte(`
+			"namespace/Android.bp": []byte(`
 				mock_library {
 					name: "libnamespace",
 					deps: ["libexample"],
@@ -809,7 +809,7 @@
 	{
 		name: "//visibility:private mixed with itself",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "libexample_defaults_1",
 					visibility: ["//visibility:private"],
@@ -823,7 +823,7 @@
 					visibility: ["//visibility:private"],
 					defaults: ["libexample_defaults_1", "libexample_defaults_2"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -839,7 +839,7 @@
 	{
 		name: "defaults_visibility invalid",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_defaults {
 					name: "top_defaults",
 					defaults_visibility: ["//visibility:invalid"],
@@ -852,7 +852,7 @@
 	{
 		name: "defaults_visibility overrides package default",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_visibility: ["//visibility:private"],
 				}
@@ -860,7 +860,7 @@
 					name: "top_defaults",
 					defaults_visibility: ["//visibility:public"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					defaults: ["top_defaults"],
@@ -872,7 +872,7 @@
 	{
 		name: "package default_visibility property is checked",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_visibility: ["//visibility:invalid"],
 				}`),
@@ -883,7 +883,7 @@
 		// This test relies on the default visibility being legacy_public.
 		name: "package default_visibility property used when no visibility specified",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_visibility: ["//visibility:private"],
 				}
@@ -891,7 +891,7 @@
 				mock_library {
 					name: "libexample",
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -905,7 +905,7 @@
 	{
 		name: "package default_visibility public does not override visibility private",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_visibility: ["//visibility:public"],
 				}
@@ -914,7 +914,7 @@
 					name: "libexample",
 					visibility: ["//visibility:private"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -928,7 +928,7 @@
 	{
 		name: "package default_visibility private does not override visibility public",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_visibility: ["//visibility:private"],
 				}
@@ -937,7 +937,7 @@
 					name: "libexample",
 					visibility: ["//visibility:public"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -947,7 +947,7 @@
 	{
 		name: "package default_visibility :__subpackages__",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_visibility: [":__subpackages__"],
 				}
@@ -955,12 +955,12 @@
 				mock_library {
 					name: "libexample",
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample"],
@@ -974,7 +974,7 @@
 	{
 		name: "package default_visibility inherited to subpackages",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_visibility: ["//outsider"],
 				}
@@ -983,12 +983,12 @@
 					name: "libexample",
           visibility: [":__subpackages__"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libexample"],
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libexample", "libnested"],
@@ -1002,11 +1002,11 @@
 	{
 		name: "package default_visibility inherited to subpackages",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				package {
 					default_visibility: ["//visibility:private"],
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				package {
 					default_visibility: ["//outsider"],
 				}
@@ -1014,11 +1014,11 @@
 				mock_library {
 					name: "libnested",
 				}`),
-			"top/other/Blueprints": []byte(`
+			"top/other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 				}`),
-			"outsider/Blueprints": []byte(`
+			"outsider/Android.bp": []byte(`
 				mock_library {
 					name: "liboutsider",
 					deps: ["libother", "libnested"],
@@ -1032,19 +1032,19 @@
 	{
 		name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred)",
 		fs: MockFS{
-			"prebuilts/Blueprints": []byte(`
+			"prebuilts/Android.bp": []byte(`
 				prebuilt {
 					name: "module",
 					visibility: ["//top/other"],
 				}`),
 			"top/sources/source_file": nil,
-			"top/sources/Blueprints": []byte(`
+			"top/sources/Android.bp": []byte(`
 				source {
 					name: "module",
 					visibility: ["//top/other"],
 				}`),
 			"top/other/source_file": nil,
-			"top/other/Blueprints": []byte(`
+			"top/other/Android.bp": []byte(`
 				source {
 					name: "other",
 					deps: [":module"],
@@ -1054,20 +1054,20 @@
 	{
 		name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred)",
 		fs: MockFS{
-			"prebuilts/Blueprints": []byte(`
+			"prebuilts/Android.bp": []byte(`
 				prebuilt {
 					name: "module",
 					visibility: ["//top/other"],
 					prefer: true,
 				}`),
 			"top/sources/source_file": nil,
-			"top/sources/Blueprints": []byte(`
+			"top/sources/Android.bp": []byte(`
 				source {
 					name: "module",
 					visibility: ["//top/other"],
 				}`),
 			"top/other/source_file": nil,
-			"top/other/Blueprints": []byte(`
+			"top/other/Android.bp": []byte(`
 				source {
 					name: "other",
 					deps: [":module"],
@@ -1077,7 +1077,7 @@
 	{
 		name: "ensure visibility properties are checked for correctness",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_parent {
 					name: "parent",
 					visibility: ["//top/nested"],
@@ -1094,7 +1094,7 @@
 	{
 		name: "invalid visibility added to child detected during gather phase",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_parent {
 					name: "parent",
 					visibility: ["//top/nested"],
@@ -1116,7 +1116,7 @@
 	{
 		name: "automatic visibility inheritance enabled",
 		fs: MockFS{
-			"top/Blueprints": []byte(`
+			"top/Android.bp": []byte(`
 				mock_parent {
 					name: "parent",
 					visibility: ["//top/nested"],
@@ -1125,12 +1125,12 @@
 						visibility: ["//top/other"],
 					},
 				}`),
-			"top/nested/Blueprints": []byte(`
+			"top/nested/Android.bp": []byte(`
 				mock_library {
 					name: "libnested",
 					deps: ["libchild"],
 				}`),
-			"top/other/Blueprints": []byte(`
+			"top/other/Android.bp": []byte(`
 				mock_library {
 					name: "libother",
 					deps: ["libchild"],
diff --git a/android/writedocs.go b/android/writedocs.go
deleted file mode 100644
index c380a3d..0000000
--- a/android/writedocs.go
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2015 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 android
-
-import (
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"github.com/google/blueprint"
-)
-
-func init() {
-	RegisterSingletonType("writedocs", DocsSingleton)
-}
-
-func DocsSingleton() Singleton {
-	return &docsSingleton{}
-}
-
-type docsSingleton struct{}
-
-func primaryBuilderPath(ctx SingletonContext) Path {
-	soongOutDir := absolutePath(ctx.Config().SoongOutDir())
-	binary := absolutePath(os.Args[0])
-	primaryBuilder, err := filepath.Rel(soongOutDir, binary)
-	if err != nil {
-		ctx.Errorf("path to primary builder %q is not in build dir %q (%q)",
-			os.Args[0], ctx.Config().SoongOutDir(), err)
-	}
-
-	return PathForOutput(ctx, primaryBuilder)
-}
-
-func (c *docsSingleton) GenerateBuildActions(ctx SingletonContext) {
-	var deps Paths
-	deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().moduleListFile))
-	deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().ProductVariablesFileName))
-
-	// The dexpreopt configuration may not exist, but if it does, it's a dependency
-	// of soong_build.
-	dexpreoptConfigPath := ctx.Config().DexpreoptGlobalConfigPath(ctx)
-	if dexpreoptConfigPath.Valid() {
-		deps = append(deps, dexpreoptConfigPath.Path())
-	}
-
-	// Generate build system docs for the primary builder.  Generating docs reads the source
-	// files used to build the primary builder, but that dependency will be picked up through
-	// the dependency on the primary builder itself.  There are no dependencies on the
-	// Blueprints files, as any relevant changes to the Blueprints files would have caused
-	// a rebuild of the primary builder.
-	docsFile := PathForOutput(ctx, "docs", "soong_build.html")
-	primaryBuilder := primaryBuilderPath(ctx)
-	soongDocs := ctx.Rule(pctx, "soongDocs",
-		blueprint.RuleParams{
-			Command: fmt.Sprintf("rm -f ${outDir}/* && %s --soong_docs %s %s",
-				primaryBuilder.String(),
-				docsFile.String(),
-				"\""+strings.Join(os.Args[1:], "\" \"")+"\""),
-			CommandDeps: []string{primaryBuilder.String()},
-			Description: fmt.Sprintf("%s docs $out", primaryBuilder.Base()),
-		},
-		"outDir")
-
-	ctx.Build(pctx, BuildParams{
-		Rule:   soongDocs,
-		Output: docsFile,
-		Inputs: deps,
-		Args: map[string]string{
-			"outDir": PathForOutput(ctx, "docs").String(),
-		},
-	})
-
-	// Add a phony target for building the documentation
-	ctx.Phony("soong_docs", docsFile)
-}
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 08616a9..80801b2 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -201,6 +201,7 @@
 			"LOCAL_VENDOR_MODULE":              "vendor",
 			"LOCAL_ODM_MODULE":                 "device_specific",
 			"LOCAL_PRODUCT_MODULE":             "product_specific",
+			"LOCAL_PRODUCT_SERVICES_MODULE":    "product_specific",
 			"LOCAL_SYSTEM_EXT_MODULE":          "system_ext_specific",
 			"LOCAL_EXPORT_PACKAGE_RESOURCES":   "export_package_resources",
 			"LOCAL_PRIVILEGED_MODULE":          "privileged",
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 2f10904..94b8116 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -382,7 +382,7 @@
 				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.ToMakePath().String())
 				stemSuffix := apexType.suffix()
 				if a.isCompressed {
-					stemSuffix = ".capex"
+					stemSuffix = imageCapexSuffix
 				}
 				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix)
 				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
diff --git a/apex/apex.go b/apex/apex.go
index 6bafcae..2d153e2 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -111,9 +111,6 @@
 	// List of java libraries that are embedded inside this APEX bundle.
 	Java_libs []string
 
-	// List of prebuilt files that are embedded inside this APEX bundle.
-	Prebuilts []string
-
 	// List of platform_compat_config files that are embedded inside this APEX bundle.
 	Compat_configs []string
 
@@ -291,6 +288,9 @@
 	// List of APKs that are embedded inside this APEX.
 	Apps []string
 
+	// List of prebuilt files that are embedded inside this APEX bundle.
+	Prebuilts []string
+
 	// List of runtime resource overlays (RROs) that are embedded inside this APEX.
 	Rros []string
 
@@ -684,7 +684,6 @@
 	// each target os/architectures, appropriate dependencies are selected by their
 	// target.<os>.multilib.<type> groups and are added as (direct) dependencies.
 	targets := ctx.MultiTargets()
-	config := ctx.DeviceConfig()
 	imageVariation := a.getImageVariation(ctx)
 
 	a.combineProperties(ctx)
@@ -758,23 +757,6 @@
 		}
 	}
 
-	if prebuilts := a.properties.Prebuilts; len(prebuilts) > 0 {
-		// For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
-		// regardless of the TARGET_PREFER_* setting. See b/144532908
-		archForPrebuiltEtc := config.Arches()[0]
-		for _, arch := range config.Arches() {
-			// Prefer 64-bit arch if there is any
-			if arch.ArchType.Multilib == "lib64" {
-				archForPrebuiltEtc = arch
-				break
-			}
-		}
-		ctx.AddFarVariationDependencies([]blueprint.Variation{
-			{Mutator: "os", Variation: ctx.Os().String()},
-			{Mutator: "arch", Variation: archForPrebuiltEtc.String()},
-		}, prebuiltTag, prebuilts...)
-	}
-
 	// Common-arch dependencies come next
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
 	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...)
@@ -814,6 +796,25 @@
 	ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
 	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.overridableProperties.Bpfs...)
 	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
+	if prebuilts := a.overridableProperties.Prebuilts; len(prebuilts) > 0 {
+		// For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
+		// regardless of the TARGET_PREFER_* setting. See b/144532908
+		arches := ctx.DeviceConfig().Arches()
+		if len(arches) != 0 {
+			archForPrebuiltEtc := arches[0]
+			for _, arch := range arches {
+				// Prefer 64-bit arch if there is any
+				if arch.ArchType.Multilib == "lib64" {
+					archForPrebuiltEtc = arch
+					break
+				}
+			}
+			ctx.AddFarVariationDependencies([]blueprint.Variation{
+				{Mutator: "os", Variation: ctx.Os().String()},
+				{Mutator: "arch", Variation: archForPrebuiltEtc.String()},
+			}, prebuiltTag, prebuilts...)
+		}
+	}
 
 	// Dependencies for signing
 	if String(a.overridableProperties.Key) == "" {
@@ -1151,9 +1152,10 @@
 
 const (
 	// File extensions of an APEX for different packaging methods
-	imageApexSuffix = ".apex"
-	zipApexSuffix   = ".zipapex"
-	flattenedSuffix = ".flattened"
+	imageApexSuffix  = ".apex"
+	imageCapexSuffix = ".capex"
+	zipApexSuffix    = ".zipapex"
+	flattenedSuffix  = ".flattened"
 
 	// variant names each of which is for a packaging method
 	imageApexType     = "image"
@@ -1567,6 +1569,11 @@
 	af.jacocoReportClassesFile = module.JacocoReportClassesFile()
 	af.lintDepSets = module.LintDepSets()
 	af.customStem = module.Stem() + ".jar"
+	if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
+		for _, install := range dexpreopter.DexpreoptBuiltInstalledForApex() {
+			af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
+		}
+	}
 	return af
 }
 
@@ -3282,7 +3289,7 @@
 	nativeSharedLibsLabelList := android.BazelLabelForModuleDeps(ctx, nativeSharedLibs)
 	nativeSharedLibsLabelListAttribute := bazel.MakeLabelListAttribute(nativeSharedLibsLabelList)
 
-	prebuilts := module.properties.Prebuilts
+	prebuilts := module.overridableProperties.Prebuilts
 	prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, prebuilts)
 	prebuiltsLabelListAttribute := bazel.MakeLabelListAttribute(prebuiltsLabelList)
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index e0bf5e5..2a2a1f4 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -932,9 +932,17 @@
 	// .. and not linking to the stubs variant of mylib3
 	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12/mylib3.so")
 
+	// Comment out this test. Now it fails after the optimization of sharing "cflags" in cc/cc.go
+	// is replaced by sharing of "cFlags" in cc/builder.go.
+	// The "cflags" contains "-include mylib.h", but cFlags contained only a reference to the
+	// module variable representing "cflags". So it was not detected by ensureNotContains.
+	// Now "cFlags" is a reference to a module variable like $flags1, which includes all previous
+	// content of "cflags". ModuleForTests...Args["cFlags"] returns the full string of $flags1,
+	// including the original cflags's "-include mylib.h".
+	//
 	// Ensure that stubs libs are built without -include flags
-	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
-	ensureNotContains(t, mylib2Cflags, "-include ")
+	// mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	// ensureNotContains(t, mylib2Cflags, "-include ")
 
 	// Ensure that genstub is invoked with --apex
 	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"])
@@ -2179,6 +2187,7 @@
 		name          string
 		expectedError string
 		bp            string
+		preparer      android.FixturePreparer
 	}{
 		{
 			name: "Non-updatable apex with non-stable dep",
@@ -2250,6 +2259,30 @@
 			`,
 		},
 		{
+			name:          "Updatable apex with non-stable legacy core platform dep",
+			expectedError: `\Qcannot depend on "myjar-uses-legacy": non stable SDK core_platform_current - uses legacy core platform\E`,
+			bp: `
+				apex {
+					name: "myapex",
+					java_libs: ["myjar-uses-legacy"],
+					key: "myapex.key",
+					updatable: true,
+				}
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+				java_library {
+					name: "myjar-uses-legacy",
+					srcs: ["foo/bar/MyClass.java"],
+					sdk_version: "core_platform",
+					apex_available: ["myapex"],
+				}
+			`,
+			preparer: java.FixtureUseLegacyCorePlatformApi("myjar-uses-legacy"),
+		},
+		{
 			name: "Updatable apex with non-stable transitive dep",
 			// This is not actually detecting that the transitive dependency is unstable, rather it is
 			// detecting that the transitive dependency is building against a wider API surface than the
@@ -2285,12 +2318,22 @@
 	}
 
 	for _, test := range testCases {
+		if test.name != "Updatable apex with non-stable legacy core platform dep" {
+			continue
+		}
 		t.Run(test.name, func(t *testing.T) {
-			if test.expectedError == "" {
-				testApex(t, test.bp)
-			} else {
-				testApexError(t, test.expectedError, test.bp)
+			errorHandler := android.FixtureExpectsNoErrors
+			if test.expectedError != "" {
+				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
 			}
+			android.GroupFixturePreparers(
+				java.PrepareForTestWithJavaDefaultModules,
+				PrepareForTestWithApexBuildComponents,
+				prepareForTestWithMyapex,
+				android.OptionalFixturePreparer(test.preparer),
+			).
+				ExtendWithErrorHandler(errorHandler).
+				RunTestWithBp(t, test.bp)
 		})
 	}
 }
@@ -4640,6 +4683,35 @@
 	}
 }
 
+func TestApexSetFilenameOverride(t *testing.T) {
+	testApex(t, `
+		apex_set {
+ 			name: "com.company.android.myapex",
+			apex_name: "com.android.myapex",
+			set: "company-myapex.apks",
+      filename: "com.company.android.myapex.apex"
+		}
+	`).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex")
+
+	testApex(t, `
+		apex_set {
+ 			name: "com.company.android.myapex",
+			apex_name: "com.android.myapex",
+			set: "company-myapex.apks",
+      filename: "com.company.android.myapex.capex"
+		}
+	`).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex")
+
+	testApexError(t, `filename should end in .apex or .capex for apex_set`, `
+		apex_set {
+ 			name: "com.company.android.myapex",
+			apex_name: "com.android.myapex",
+			set: "company-myapex.apks",
+      filename: "some-random-suffix"
+		}
+	`)
+}
+
 func TestPrebuiltOverrides(t *testing.T) {
 	ctx := testApex(t, `
 		prebuilt_apex {
@@ -5129,6 +5201,12 @@
 		// find the dex boot jar in it. We either need to disable the source libfoo
 		// or make the prebuilt libfoo preferred.
 		testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer, fragment)
+		// dexbootjar check is skipped if AllowMissingDependencies is true
+		preparerAllowMissingDeps := android.GroupFixturePreparers(
+			preparer,
+			android.PrepareForTestWithAllowMissingDependencies,
+		)
+		testDexpreoptWithApexes(t, bp, "", preparerAllowMissingDeps, fragment)
 	})
 
 	t.Run("prebuilt library preferred with source", func(t *testing.T) {
@@ -6074,6 +6152,7 @@
 			key: "myapex.key",
 			apps: ["app"],
 			bpfs: ["bpf"],
+			prebuilts: ["myetc"],
 			overrides: ["oldapex"],
 			updatable: false,
 		}
@@ -6083,6 +6162,7 @@
 			base: "myapex",
 			apps: ["override_app"],
 			bpfs: ["override_bpf"],
+			prebuilts: ["override_myetc"],
 			overrides: ["unknownapex"],
 			logging_parent: "com.foo.bar",
 			package_name: "test.overridden.package",
@@ -6131,6 +6211,16 @@
 			name: "override_bpf",
 			srcs: ["override_bpf.c"],
 		}
+
+		prebuilt_etc {
+			name: "myetc",
+			src: "myprebuilt",
+		}
+
+		prebuilt_etc {
+			name: "override_myetc",
+			src: "override_myprebuilt",
+		}
 	`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
 
 	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule)
@@ -6152,6 +6242,9 @@
 	ensureNotContains(t, copyCmds, "image.apex/etc/bpf/bpf.o")
 	ensureContains(t, copyCmds, "image.apex/etc/bpf/override_bpf.o")
 
+	ensureNotContains(t, copyCmds, "image.apex/etc/myetc")
+	ensureContains(t, copyCmds, "image.apex/etc/override_myetc")
+
 	apexBundle := module.Module().(*apexBundle)
 	name := apexBundle.Name()
 	if name != "override_myapex" {
diff --git a/apex/builder.go b/apex/builder.go
index 5baa5c0..6df40f4 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -17,7 +17,6 @@
 import (
 	"encoding/json"
 	"fmt"
-	"path"
 	"path/filepath"
 	"runtime"
 	"sort"
@@ -256,14 +255,24 @@
 // labeled as system_file.
 func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.OutputPath {
 	var fileContexts android.Path
+	var fileContextsDir string
 	if a.properties.File_contexts == nil {
 		fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts")
 	} else {
+		if m, t := android.SrcIsModuleWithTag(*a.properties.File_contexts); m != "" {
+			otherModule := android.GetModuleFromPathDep(ctx, m, t)
+			fileContextsDir = ctx.OtherModuleDir(otherModule)
+		}
 		fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts)
 	}
+	if fileContextsDir == "" {
+		fileContextsDir = filepath.Dir(fileContexts.String())
+	}
+	fileContextsDir += string(filepath.Separator)
+
 	if a.Platform() {
-		if matched, err := path.Match("system/sepolicy/**/*", fileContexts.String()); err != nil || !matched {
-			ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but %q", fileContexts)
+		if !strings.HasPrefix(fileContextsDir, "system/sepolicy/") {
+			ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but found in  %q", fileContextsDir)
 		}
 	}
 	if !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() {
@@ -786,7 +795,7 @@
 
 	if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
 		a.isCompressed = true
-		unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex.unsigned")
+		unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned")
 
 		compressRule := android.NewRuleBuilder(pctx, ctx)
 		compressRule.Command().
@@ -800,7 +809,7 @@
 			FlagWithOutput("--output ", unsignedCompressedOutputFile)
 		compressRule.Build("compressRule", "Generate unsigned compressed APEX file")
 
-		signedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex")
+		signedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix)
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
 			args["outCommaList"] = signedCompressedOutputFile.String()
 		}
diff --git a/apex/deapexer.go b/apex/deapexer.go
index c70da15..2c1835a 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -97,7 +97,7 @@
 	// Create and remember the directory into which the .apex file's contents will be unpacked.
 	deapexerOutput := android.PathForModuleOut(ctx, "deapexer")
 
-	exports := make(map[string]android.Path)
+	exports := make(map[string]android.WritablePath)
 
 	// Create mappings from apex relative path to the extracted file's path.
 	exportedPaths := make(android.Paths, 0, len(exports))
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 513ddc0..d8a4a7a 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -21,6 +21,7 @@
 
 	"android/soong/android"
 	"android/soong/java"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -325,31 +326,15 @@
 }
 
 // TestPlatformBootclasspath_AlwaysUsePrebuiltSdks verifies that the build does not fail when
-// AlwaysUsePrebuiltSdk() returns true. The structure of the modules in this test matches what
-// currently exists in some places in the Android build but it is not the intended structure. It is
-// in fact an invalid structure that should cause build failures. However, fixing that structure
-// will take too long so in the meantime this tests the workarounds to avoid build breakages.
-//
-// The main issues with this structure are:
-// 1. There is no prebuilt_bootclasspath_fragment referencing the "foo" java_sdk_library_import.
-// 2. There is no prebuilt_apex/apex_set which makes the dex implementation jar available to the
-//    prebuilt_bootclasspath_fragment and the "foo" java_sdk_library_import.
-//
-// Together these cause the following symptoms:
-// 1. The "foo" java_sdk_library_import does not have a dex implementation jar.
-// 2. The "foo" java_sdk_library_import does not have a myapex variant.
-//
-// TODO(b/179354495): Fix the structure in this test once the main Android build has been fixed.
+// AlwaysUsePrebuiltSdk() returns true.
 func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
 		prepareForTestWithMyapex,
 		// Configure two libraries, the first is a java_sdk_library whose prebuilt will be used because
-		// of AlwaysUsePrebuiltsSdk() but does not have an appropriate apex variant and does not provide
-		// a boot dex jar. The second is a normal library that is unaffected. The order matters because
-		// if the dependency on myapex:foo is filtered out because of either of those conditions then
-		// the dependencies resolved by the platform_bootclasspath will not match the configured list
-		// and so will fail the test.
+		// of AlwaysUsePrebuiltsSdk(). The second is a normal library that is unaffected. The order
+		// matters, so that the dependencies resolved by the platform_bootclasspath matches the
+		// configured list.
 		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -394,6 +379,12 @@
 			permitted_packages: ["foo"],
 		}
 
+		prebuilt_apex {
+			name: "myapex",
+			src: "myapex.apex",
+			exported_bootclasspath_fragments: ["mybootclasspath-fragment"],
+		}
+
 		// A prebuilt java_sdk_library_import that is not preferred by default but will be preferred
 		// because AlwaysUsePrebuiltSdks() is true.
 		java_sdk_library_import {
@@ -423,6 +414,23 @@
 			],
 		}
 
+		prebuilt_bootclasspath_fragment {
+			name: "mybootclasspath-fragment",
+			apex_available: [
+				"myapex",
+			],
+			contents: [
+				"foo",
+			],
+			hidden_api: {
+				stub_flags: "",
+				annotation_flags: "",
+				metadata: "",
+				index: "",
+				all_flags: "",
+			},
+		}
+
 		platform_bootclasspath {
 			name: "myplatform-bootclasspath",
 			fragments: [
@@ -437,7 +445,7 @@
 
 	java.CheckPlatformBootclasspathModules(t, result, "myplatform-bootclasspath", []string{
 		// The configured contents of BootJars.
-		"platform:prebuilt_foo", // Note: This is the platform not myapex variant.
+		"myapex:prebuilt_foo",
 		"myapex:bar",
 	})
 
@@ -456,16 +464,15 @@
 
 		// The platform_bootclasspath intentionally adds dependencies on both source and prebuilt
 		// modules when available as it does not know which one will be preferred.
-		//
-		// The source module has an APEX variant but the prebuilt does not.
 		"myapex:foo",
-		"platform:prebuilt_foo",
+		"myapex:prebuilt_foo",
 
 		// Only a source module exists.
 		"myapex:bar",
 
 		// The fragments.
 		"myapex:mybootclasspath-fragment",
+		"myapex:prebuilt_mybootclasspath-fragment",
 	})
 }
 
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 1bb0fb5..4833a64 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -178,13 +178,19 @@
 			// If the exported java module provides a dex jar path then add it to the list of apexFiles.
 			path := child.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
 			if path != nil {
-				p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, apexFile{
+				af := apexFile{
 					module:              child,
 					moduleDir:           ctx.OtherModuleDir(child),
 					androidMkModuleName: name,
 					builtFile:           path,
 					class:               javaSharedLib,
-				})
+				}
+				if module, ok := child.(java.DexpreopterInterface); ok {
+					for _, install := range module.DexpreoptBuiltInstalledForApex() {
+						af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
+					}
+				}
+				p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, af)
 			}
 		} else if tag == exportedBootclasspathFragmentTag {
 			// Visit the children of the bootclasspath_fragment.
@@ -195,6 +201,14 @@
 	})
 }
 
+func (p *prebuiltCommon) addRequiredModules(entries *android.AndroidMkEntries) {
+	for _, fi := range p.apexFilesForAndroidMk {
+		entries.AddStrings("LOCAL_REQUIRED_MODULES", fi.requiredModuleNames...)
+		entries.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", fi.targetRequiredModuleNames...)
+		entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", fi.hostRequiredModuleNames...)
+	}
+}
+
 func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := []android.AndroidMkEntries{
 		{
@@ -213,6 +227,7 @@
 					if len(postInstallCommands) > 0 {
 						entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(postInstallCommands, " && "))
 					}
+					p.addRequiredModules(entries)
 				},
 			},
 		},
@@ -924,8 +939,8 @@
 
 func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.installFilename = a.InstallFilename()
-	if !strings.HasSuffix(a.installFilename, imageApexSuffix) {
-		ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix)
+	if !strings.HasSuffix(a.installFilename, imageApexSuffix) && !strings.HasSuffix(a.installFilename, imageCapexSuffix) {
+		ctx.ModuleErrorf("filename should end in %s or %s for apex_set", imageApexSuffix, imageCapexSuffix)
 	}
 
 	inputApex := android.OptionalPathForModuleSrc(ctx, a.prebuiltCommonProperties.Selected_apex).Path()
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 131f0ec..d731f3e 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -25,6 +25,7 @@
 	// be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles,
 	// but general cc_library will also have dynamic libraries in output files).
 	RootDynamicLibraries []string
+	TocFile              string
 }
 
 type getOutputFilesRequestType struct{}
@@ -100,14 +101,15 @@
 func (g getCcInfoType) StarlarkFunctionBody() string {
 	return `
 outputFiles = [f.path for f in target.files.to_list()]
+cc_info = providers(target)["CcInfo"]
 
-includes = providers(target)["CcInfo"].compilation_context.includes.to_list()
-system_includes = providers(target)["CcInfo"].compilation_context.system_includes.to_list()
+includes = cc_info.compilation_context.includes.to_list()
+system_includes = cc_info.compilation_context.system_includes.to_list()
 
 ccObjectFiles = []
 staticLibraries = []
 rootStaticArchives = []
-linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
+linker_inputs = cc_info.linking_context.linker_inputs.to_list()
 
 for linker_input in linker_inputs:
   for library in linker_input.libraries:
@@ -120,11 +122,17 @@
 
 rootDynamicLibraries = []
 
-if "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo" in providers(target):
-  shared_info = providers(target)["@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"]
+shared_info_tag = "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"
+if shared_info_tag in providers(target):
+  shared_info = providers(target)[shared_info_tag]
   for lib in shared_info.linker_input.libraries:
     rootDynamicLibraries += [lib.dynamic_library.path]
 
+toc_file = ""
+toc_file_tag = "//build/bazel/rules:generate_toc.bzl%CcTocInfo"
+if toc_file_tag in providers(target):
+  toc_file = providers(target)[toc_file_tag].toc.path
+
 returns = [
   outputFiles,
   staticLibraries,
@@ -132,10 +140,10 @@
   includes,
   system_includes,
   rootStaticArchives,
-  rootDynamicLibraries
+  rootDynamicLibraries,
 ]
 
-return "|".join([", ".join(r) for r in returns])`
+return "|".join([", ".join(r) for r in returns] + [toc_file])`
 }
 
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
@@ -146,7 +154,7 @@
 	var ccObjects []string
 
 	splitString := strings.Split(rawString, "|")
-	if expectedLen := 7; len(splitString) != expectedLen {
+	if expectedLen := 8; len(splitString) != expectedLen {
 		return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
 	}
 	outputFilesString := splitString[0]
@@ -159,6 +167,7 @@
 	systemIncludes := splitOrEmpty(splitString[4], ", ")
 	rootStaticArchives := splitOrEmpty(splitString[5], ", ")
 	rootDynamicLibraries := splitOrEmpty(splitString[6], ", ")
+	tocFile := splitString[7] // NOTE: Will be the empty string if there wasn't
 	return CcInfo{
 		OutputFiles:          outputFiles,
 		CcObjectFiles:        ccObjects,
@@ -167,6 +176,7 @@
 		SystemIncludes:       systemIncludes,
 		RootStaticArchives:   rootStaticArchives,
 		RootDynamicLibraries: rootDynamicLibraries,
+		TocFile:              tocFile,
 	}, nil
 }
 
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 49019ab..34d0832 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -71,7 +71,7 @@
 	}{
 		{
 			description: "no result",
-			input:       "||||||",
+			input:       "|||||||",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{},
 				CcObjectFiles:        []string{},
@@ -80,11 +80,12 @@
 				SystemIncludes:       []string{},
 				RootStaticArchives:   []string{},
 				RootDynamicLibraries: []string{},
+				TocFile:              "",
 			},
 		},
 		{
 			description: "only output",
-			input:       "test||||||",
+			input:       "test|||||||",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{"test"},
 				CcObjectFiles:        []string{},
@@ -93,11 +94,12 @@
 				SystemIncludes:       []string{},
 				RootStaticArchives:   []string{},
 				RootDynamicLibraries: []string{},
+				TocFile:              "",
 			},
 		},
 		{
 			description: "all items set",
-			input:       "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1",
+			input:       "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1|lib.so.toc",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{"out1", "out2"},
 				CcObjectFiles:        []string{"object1", "object2"},
@@ -106,19 +108,20 @@
 				SystemIncludes:       []string{"system/dir", "system/other/dir"},
 				RootStaticArchives:   []string{"rootstaticarchive1"},
 				RootDynamicLibraries: []string{"rootdynamiclibrary1"},
+				TocFile:              "lib.so.toc",
 			},
 		},
 		{
 			description:          "too few result splits",
 			input:                "|",
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, []string{"", ""}),
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 8, []string{"", ""}),
 		},
 		{
 			description:          "too many result splits",
 			input:                strings.Repeat("|", 8),
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, make([]string, 9)),
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 8, make([]string, 9)),
 		},
 	}
 	for _, tc := range testCases {
diff --git a/bazel/properties.go b/bazel/properties.go
index 1a846ba..d3b40a2 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -19,7 +19,6 @@
 	"path/filepath"
 	"regexp"
 	"sort"
-	"strings"
 )
 
 // BazelTargetModuleProperties contain properties and metadata used for
@@ -32,12 +31,6 @@
 	Bzl_load_location string `blueprint:"mutated"`
 }
 
-const BazelTargetModuleNamePrefix = "__bp2build__"
-
-func StripNamePrefix(moduleName string) string {
-	return strings.TrimPrefix(moduleName, BazelTargetModuleNamePrefix)
-}
-
 var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
 
 // Label is used to represent a Bazel compatible Label. Also stores the original
@@ -743,6 +736,31 @@
 	return keys
 }
 
+// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and
+// configuration-specific values. For example, if we would convert this StringListAttribute as:
+// ["a", "b", "c"] + select({
+//    "//condition:one": ["a", "d"],
+//    "//conditions:default": [],
+// })
+// after this function, we would convert this StringListAttribute as:
+// ["a", "b", "c"] + select({
+//    "//condition:one": ["d"],
+//    "//conditions:default": [],
+// })
+func (sla *StringListAttribute) DeduplicateAxesFromBase() {
+	base := sla.Value
+	for axis, configToList := range sla.ConfigurableValues {
+		for config, list := range configToList {
+			remaining := SubtractStrings(list, base)
+			if len(remaining) == 0 {
+				delete(sla.ConfigurableValues[axis], config)
+			} else {
+				sla.ConfigurableValues[axis][config] = remaining
+			}
+		}
+	}
+}
+
 // TryVariableSubstitution, replace string substitution formatting within each string in slice with
 // Starlark string.format compatible tag for productVariable.
 func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 9464245..85596e2 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -293,3 +293,74 @@
 		}
 	}
 }
+
+func TestDeduplicateAxesFromBase(t *testing.T) {
+	attr := StringListAttribute{
+		Value: []string{
+			"all_include",
+			"arm_include",
+			"android_include",
+			"linux_x86_include",
+		},
+		ConfigurableValues: configurableStringLists{
+			ArchConfigurationAxis: stringListSelectValues{
+				"arm": []string{"arm_include"},
+				"x86": []string{"x86_include"},
+			},
+			OsConfigurationAxis: stringListSelectValues{
+				"android": []string{"android_include"},
+				"linux":   []string{"linux_include"},
+			},
+			OsArchConfigurationAxis: stringListSelectValues{
+				"linux_x86": {"linux_x86_include"},
+			},
+			ProductVariableConfigurationAxis("a"): stringListSelectValues{
+				"a": []string{"not_in_value"},
+			},
+		},
+	}
+
+	attr.DeduplicateAxesFromBase()
+
+	expectedBaseIncludes := []string{
+		"all_include",
+		"arm_include",
+		"android_include",
+		"linux_x86_include",
+	}
+	if !reflect.DeepEqual(expectedBaseIncludes, attr.Value) {
+		t.Errorf("Expected Value includes %q, got %q", attr.Value, expectedBaseIncludes)
+	}
+	expectedConfiguredIncludes := configurableStringLists{
+		ArchConfigurationAxis: stringListSelectValues{
+			"x86": []string{"x86_include"},
+		},
+		OsConfigurationAxis: stringListSelectValues{
+			"linux": []string{"linux_include"},
+		},
+		OsArchConfigurationAxis: stringListSelectValues{},
+		ProductVariableConfigurationAxis("a"): stringListSelectValues{
+			"a": []string{"not_in_value"},
+		},
+	}
+	for _, axis := range attr.SortedConfigurationAxes() {
+		if _, ok := expectedConfiguredIncludes[axis]; !ok {
+			t.Errorf("Found unexpected axis %s", axis)
+			continue
+		}
+		expectedForAxis := expectedConfiguredIncludes[axis]
+		gotForAxis := attr.ConfigurableValues[axis]
+		if len(expectedForAxis) != len(gotForAxis) {
+			t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis)
+		}
+		for config, value := range gotForAxis {
+			if expected, ok := expectedForAxis[config]; ok {
+				if !reflect.DeepEqual(expected, value) {
+					t.Errorf("For %s, expected: %#v, got %#v", axis, expected, value)
+				}
+			} else {
+				t.Errorf("Got unexpected config %q for %s", config, axis)
+			}
+		}
+	}
+}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index b1ccc96..40526a6 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -11,7 +11,6 @@
         "build_conversion.go",
         "bzl_conversion.go",
         "configurability.go",
-        "compatibility.go",
         "constants.go",
         "conversion.go",
         "metrics.go",
@@ -37,8 +36,11 @@
         "cc_library_conversion_test.go",
         "cc_library_headers_conversion_test.go",
         "cc_library_static_conversion_test.go",
+        "cc_library_shared_conversion_test.go",
         "cc_object_conversion_test.go",
         "conversion_test.go",
+        "filegroup_conversion_test.go",
+        "genrule_conversion_test.go",
         "performance_test.go",
         "prebuilt_etc_conversion_test.go",
         "python_binary_conversion_test.go",
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 06a7306..45a3cb6 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -19,6 +19,7 @@
 	"android/soong/bazel"
 	"fmt"
 	"os"
+	"strings"
 )
 
 // Codegen is the backend of bp2build. The code generator is responsible for
@@ -29,14 +30,22 @@
 	bp2buildDir := android.PathForOutput(ctx, "bp2build")
 	android.RemoveAllOutputDir(bp2buildDir)
 
-	buildToTargets, metrics, compatLayer := GenerateBazelTargets(ctx, true)
-	bp2buildFiles := CreateBazelFiles(nil, buildToTargets, ctx.mode)
+	res, errs := GenerateBazelTargets(ctx, true)
+	if len(errs) > 0 {
+		errMsgs := make([]string, len(errs))
+		for i, err := range errs {
+			errMsgs[i] = fmt.Sprintf("%q", err)
+		}
+		fmt.Printf("ERROR: Encountered %d error(s): \nERROR: %s", len(errs), strings.Join(errMsgs, "\n"))
+		os.Exit(1)
+	}
+	bp2buildFiles := CreateBazelFiles(nil, res.buildFileToTargets, ctx.mode)
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 
 	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
-	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(compatLayer))
+	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(res.metrics))
 
-	return metrics
+	return res.metrics
 }
 
 // Get the output directory and create it if it doesn't exist.
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index f652a35..b1a6e2c 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -153,10 +153,11 @@
 }
 
 type CodegenContext struct {
-	config         android.Config
-	context        android.Context
-	mode           CodegenMode
-	additionalDeps []string
+	config             android.Config
+	context            android.Context
+	mode               CodegenMode
+	additionalDeps     []string
+	unconvertedDepMode unconvertedDepsMode
 }
 
 func (c *CodegenContext) Mode() CodegenMode {
@@ -181,6 +182,16 @@
 	QueryView
 )
 
+type unconvertedDepsMode int
+
+const (
+	// Include a warning in conversion metrics about converted modules with unconverted direct deps
+	warnUnconvertedDeps unconvertedDepsMode = iota
+	// Error and fail conversion if encountering a module with unconverted direct deps
+	// Enabled by setting environment variable `BP2BUILD_ERROR_UNCONVERTED`
+	errorModulesUnconvertedDeps
+)
+
 func (mode CodegenMode) String() string {
 	switch mode {
 	case Bp2Build:
@@ -211,10 +222,15 @@
 // NewCodegenContext creates a wrapper context that conforms to PathContext for
 // writing BUILD files in the output directory.
 func NewCodegenContext(config android.Config, context android.Context, mode CodegenMode) *CodegenContext {
+	var unconvertedDeps unconvertedDepsMode
+	if config.IsEnvTrue("BP2BUILD_ERROR_UNCONVERTED") {
+		unconvertedDeps = errorModulesUnconvertedDeps
+	}
 	return &CodegenContext{
-		context: context,
-		config:  config,
-		mode:    mode,
+		context:            context,
+		config:             config,
+		mode:               mode,
+		unconvertedDepMode: unconvertedDeps,
 	}
 }
 
@@ -230,21 +246,28 @@
 	return attributes
 }
 
-func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[string]BazelTargets, CodegenMetrics, CodegenCompatLayer) {
+type conversionResults struct {
+	buildFileToTargets map[string]BazelTargets
+	metrics            CodegenMetrics
+}
+
+func (r conversionResults) BuildDirToTargets() map[string]BazelTargets {
+	return r.buildFileToTargets
+}
+
+func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) {
 	buildFileToTargets := make(map[string]BazelTargets)
 	buildFileToAppend := make(map[string]bool)
 
 	// Simple metrics tracking for bp2build
 	metrics := CodegenMetrics{
-		RuleClassCount: make(map[string]int),
-	}
-
-	compatLayer := CodegenCompatLayer{
-		NameToLabelMap: make(map[string]string),
+		ruleClassCount: make(map[string]int),
 	}
 
 	dirs := make(map[string]bool)
 
+	var errs []error
+
 	bpCtx := ctx.Context()
 	bpCtx.VisitAllModules(func(m blueprint.Module) {
 		dir := bpCtx.ModuleDir(m)
@@ -254,35 +277,63 @@
 
 		switch ctx.Mode() {
 		case Bp2Build:
+			// There are two main ways of converting a Soong module to Bazel:
+			// 1) Manually handcrafting a Bazel target and associating the module with its label
+			// 2) Automatically generating with bp2build converters
+			//
+			// bp2build converters are used for the majority of modules.
 			if b, ok := m.(android.Bazelable); ok && b.HasHandcraftedLabel() {
-				metrics.handCraftedTargetCount += 1
-				metrics.TotalModuleCount += 1
-				compatLayer.AddNameToLabelEntry(m.Name(), b.HandcraftedLabel())
+				// Handle modules converted to handcrafted targets.
+				//
+				// Since these modules are associated with some handcrafted
+				// target in a BUILD file, we simply append the entire contents
+				// of that BUILD file to the generated BUILD file.
+				//
+				// The append operation is only done once, even if there are
+				// multiple modules from the same directory associated to
+				// targets in the same BUILD file (or package).
+
+				// Log the module.
+				metrics.AddConvertedModule(m.Name(), Handcrafted)
+
 				pathToBuildFile := getBazelPackagePath(b)
-				// We are using the entire contents of handcrafted build file, so if multiple targets within
-				// a package have handcrafted targets, we only want to include the contents one time.
 				if _, exists := buildFileToAppend[pathToBuildFile]; exists {
+					// Append the BUILD file content once per package, at most.
 					return
 				}
 				t, err := getHandcraftedBuildContent(ctx, b, pathToBuildFile)
 				if err != nil {
-					panic(fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
+					errs = append(errs, fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
+					return
 				}
 				targets = append(targets, t)
 				// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
 				// something more targeted based on the rule type and target
 				buildFileToAppend[pathToBuildFile] = true
 			} else if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
+				// Handle modules converted to generated targets.
+
+				// Log the module.
+				metrics.AddConvertedModule(m.Name(), Generated)
+
+				// Handle modules with unconverted deps. By default, emit a warning.
+				if unconvertedDeps := aModule.GetUnconvertedBp2buildDeps(); len(unconvertedDeps) > 0 {
+					msg := fmt.Sprintf("%q depends on unconverted modules: %s", m.Name(), strings.Join(unconvertedDeps, ", "))
+					if ctx.unconvertedDepMode == warnUnconvertedDeps {
+						metrics.moduleWithUnconvertedDepsMsgs = append(metrics.moduleWithUnconvertedDepsMsgs, msg)
+					} else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
+						errs = append(errs, fmt.Errorf(msg))
+						return
+					}
+				}
 				targets = generateBazelTargets(bpCtx, aModule)
 				for _, t := range targets {
-					if t.name == m.Name() {
-						// only add targets that exist in Soong to compatibility layer
-						compatLayer.AddNameToLabelEntry(m.Name(), t.Label())
-					}
-					metrics.RuleClassCount[t.ruleClass] += 1
+					// A module can potentially generate more than 1 Bazel
+					// target, each of a different rule class.
+					metrics.IncrementRuleClassCount(t.ruleClass)
 				}
 			} else {
-				metrics.TotalModuleCount += 1
+				metrics.IncrementUnconvertedCount()
 				return
 			}
 		case QueryView:
@@ -295,11 +346,17 @@
 			t := generateSoongModuleTarget(bpCtx, m)
 			targets = append(targets, t)
 		default:
-			panic(fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
+			errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
+			return
 		}
 
 		buildFileToTargets[dir] = append(buildFileToTargets[dir], targets...)
 	})
+
+	if len(errs) > 0 {
+		return conversionResults{}, errs
+	}
+
 	if generateFilegroups {
 		// Add a filegroup target that exposes all sources in the subtree of this package
 		// NOTE: This also means we generate a BUILD file for every Android.bp file (as long as it has at least one module)
@@ -312,7 +369,10 @@
 		}
 	}
 
-	return buildFileToTargets, metrics, compatLayer
+	return conversionResults{
+		buildFileToTargets: buildFileToTargets,
+		metrics:            metrics,
+	}, errs
 }
 
 func getBazelPackagePath(b android.Bazelable) string {
@@ -575,6 +635,19 @@
 			// Ignore zero-valued fields
 			continue
 		}
+		// if the struct is embedded (anonymous), flatten the properties into the containing struct
+		if field.Anonymous {
+			if field.Type.Kind() == reflect.Ptr {
+				fieldValue = fieldValue.Elem()
+			}
+			if fieldValue.Type().Kind() == reflect.Struct {
+				propsToMerge := extractStructProperties(fieldValue, indent)
+				for prop, value := range propsToMerge {
+					ret[prop] = value
+				}
+				continue
+			}
+		}
 
 		propertyName := proptools.PropertyNameForField(field.Name)
 		prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
@@ -648,10 +721,6 @@
 	return strings.Repeat("    ", indent)
 }
 
-func targetNameForBp2Build(c bpToBuildContext, logicModule blueprint.Module) string {
-	return strings.Replace(c.ModuleName(logicModule), bazel.BazelTargetModuleNamePrefix, "", 1)
-}
-
 func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
 	name := ""
 	if c.ModuleSubDir(logicModule) != "" {
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 0d9106c..e904627 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -16,7 +16,7 @@
 
 import (
 	"android/soong/android"
-	"android/soong/genrule"
+	"fmt"
 	"strings"
 	"testing"
 )
@@ -200,7 +200,8 @@
 			android.FailIfErrored(t, errs)
 
 			codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
-			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+			android.FailIfErrored(t, err)
 			if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
 				t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
 			}
@@ -218,13 +219,9 @@
 }
 
 func TestGenerateBazelTargetModules(t *testing.T) {
-	testCases := []struct {
-		name                 string
-		bp                   string
-		expectedBazelTargets []string
-	}{
+	testCases := []bp2buildTestCase{
 		{
-			bp: `custom {
+			blueprint: `custom {
 	name: "foo",
     string_list_prop: ["a", "b"],
     string_prop: "a",
@@ -241,7 +238,7 @@
 			},
 		},
 		{
-			bp: `custom {
+			blueprint: `custom {
 	name: "control_characters",
     string_list_prop: ["\t", "\n"],
     string_prop: "a\t\n\r",
@@ -258,7 +255,7 @@
 			},
 		},
 		{
-			bp: `custom {
+			blueprint: `custom {
   name: "has_dep",
   arch_paths: [":dep"],
   bazel_module: { bp2build_available: true },
@@ -280,7 +277,7 @@
 			},
 		},
 		{
-			bp: `custom {
+			blueprint: `custom {
     name: "arch_paths",
     arch: {
       x86: {
@@ -299,7 +296,7 @@
 			},
 		},
 		{
-			bp: `custom {
+			blueprint: `custom {
   name: "has_dep",
   arch: {
     x86: {
@@ -327,26 +324,51 @@
 )`,
 			},
 		},
+		{
+			blueprint: `custom {
+    name: "embedded_props",
+    embedded_prop: "abc",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`custom(
+    name = "embedded_props",
+    embedded_attr = "abc",
+)`,
+			},
+		},
+		{
+			blueprint: `custom {
+    name: "ptr_to_embedded_props",
+    other_embedded_prop: "abc",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`custom(
+    name = "ptr_to_embedded_props",
+    other_embedded_attr = "abc",
+)`,
+			},
+		},
 	}
 
 	dir := "."
 	for _, testCase := range testCases {
-		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+		config := android.TestConfig(buildDir, nil, testCase.blueprint, nil)
 		ctx := android.NewTestContext(config)
 
 		registerCustomModuleForBp2buildConversion(ctx)
 
 		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
-		if errored(t, "", errs) {
+		if errored(t, testCase, errs) {
 			continue
 		}
 		_, errs = ctx.ResolveDependencies(config)
-		if errored(t, "", errs) {
+		if errored(t, testCase, errs) {
 			continue
 		}
 
 		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+		android.FailIfErrored(t, err)
 
 		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
 			t.Errorf("Expected %d bazel target, got %d", expectedCount, actualCount)
@@ -507,7 +529,8 @@
 		android.FailIfErrored(t, errs)
 
 		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+		android.FailIfErrored(t, err)
 		if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount {
 			t.Fatalf("Expected %d bazel target, got %d", testCase.expectedBazelTargetCount, actualCount)
 		}
@@ -533,38 +556,13 @@
 }
 
 func TestModuleTypeBp2Build(t *testing.T) {
-	otherGenruleBp := map[string]string{
-		"other/Android.bp": `genrule {
-    name: "foo.tool",
-    out: ["foo_tool.out"],
-    srcs: ["foo_tool.in"],
-    cmd: "cp $(in) $(out)",
-}
-genrule {
-    name: "other.tool",
-    out: ["other_tool.out"],
-    srcs: ["other_tool.in"],
-    cmd: "cp $(in) $(out)",
-}`,
-	}
-
-	testCases := []struct {
-		description                        string
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-		preArchMutators                    []android.RegisterMutatorFunc
-		bp                                 string
-		expectedBazelTargets               []string
-		fs                                 map[string]string
-		dir                                string
-	}{
+	testCases := []bp2buildTestCase{
 		{
 			description:                        "filegroup with does not specify srcs",
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
     name: "fg_foo",
     bazel_module: { bp2build_available: true },
 }`,
@@ -579,7 +577,7 @@
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
     name: "fg_foo",
     srcs: [],
     bazel_module: { bp2build_available: true },
@@ -595,7 +593,7 @@
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
     name: "fg_foo",
     srcs: ["a", "b"],
     bazel_module: { bp2build_available: true },
@@ -614,7 +612,7 @@
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
     name: "fg_foo",
     srcs: ["a", "b"],
     exclude_srcs: ["a"],
@@ -631,7 +629,7 @@
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
     name: "foo",
     srcs: ["**/*.txt"],
     bazel_module: { bp2build_available: true },
@@ -645,7 +643,7 @@
     ],
 )`,
 			},
-			fs: map[string]string{
+			filesystem: map[string]string{
 				"other/a.txt":        "",
 				"other/b.txt":        "",
 				"other/subdir/a.txt": "",
@@ -657,7 +655,7 @@
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
     name: "foo",
     srcs: ["a.txt"],
     bazel_module: { bp2build_available: true },
@@ -672,7 +670,7 @@
     ],
 )`,
 			},
-			fs: map[string]string{
+			filesystem: map[string]string{
 				"other/Android.bp": `filegroup {
     name: "fg_foo",
     srcs: ["**/*.txt"],
@@ -689,7 +687,7 @@
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
     name: "foobar",
     srcs: [
         ":foo",
@@ -705,455 +703,47 @@
     ],
 )`,
 			},
-			fs: map[string]string{
+			filesystem: map[string]string{
+				"other/Android.bp": `filegroup {
+    name: "foo",
+    srcs: ["a", "b"],
+    bazel_module: { bp2build_available: true },
+}`,
+			},
+		},
+		{
+			description:                        "depends_on_other_unconverted_module_error",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			unconvertedDepsMode:                errorModulesUnconvertedDeps,
+			blueprint: `filegroup {
+    name: "foobar",
+    srcs: [
+        ":foo",
+        "c",
+    ],
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedErr: fmt.Errorf(`"foobar" depends on unconverted modules: foo`),
+			filesystem: map[string]string{
 				"other/Android.bp": `filegroup {
     name: "foo",
     srcs: ["a", "b"],
 }`,
 			},
 		},
-		{
-			description:                        "genrule with command line variable replacements",
-			moduleTypeUnderTest:                "genrule",
-			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
-			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			bp: `genrule {
-    name: "foo.tool",
-    out: ["foo_tool.out"],
-    srcs: ["foo_tool.in"],
-    cmd: "cp $(in) $(out)",
-    bazel_module: { bp2build_available: true },
-}
-
-genrule {
-    name: "foo",
-    out: ["foo.out"],
-    srcs: ["foo.in"],
-    tools: [":foo.tool"],
-    cmd: "$(location :foo.tool) --genDir=$(genDir) arg $(in) $(out)",
-    bazel_module: { bp2build_available: true },
-}`,
-			expectedBazelTargets: []string{
-				`genrule(
-    name = "foo",
-    cmd = "$(location :foo.tool) --genDir=$(GENDIR) arg $(SRCS) $(OUTS)",
-    outs = ["foo.out"],
-    srcs = ["foo.in"],
-    tools = [":foo.tool"],
-)`,
-				`genrule(
-    name = "foo.tool",
-    cmd = "cp $(SRCS) $(OUTS)",
-    outs = ["foo_tool.out"],
-    srcs = ["foo_tool.in"],
-)`,
-			},
-		},
-		{
-			description:                        "genrule using $(locations :label)",
-			moduleTypeUnderTest:                "genrule",
-			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
-			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			bp: `genrule {
-    name: "foo.tools",
-    out: ["foo_tool.out", "foo_tool2.out"],
-    srcs: ["foo_tool.in"],
-    cmd: "cp $(in) $(out)",
-    bazel_module: { bp2build_available: true },
-}
-
-genrule {
-    name: "foo",
-    out: ["foo.out"],
-    srcs: ["foo.in"],
-    tools: [":foo.tools"],
-    cmd: "$(locations :foo.tools) -s $(out) $(in)",
-    bazel_module: { bp2build_available: true },
-}`,
-			expectedBazelTargets: []string{`genrule(
-    name = "foo",
-    cmd = "$(locations :foo.tools) -s $(OUTS) $(SRCS)",
-    outs = ["foo.out"],
-    srcs = ["foo.in"],
-    tools = [":foo.tools"],
-)`,
-				`genrule(
-    name = "foo.tools",
-    cmd = "cp $(SRCS) $(OUTS)",
-    outs = [
-        "foo_tool.out",
-        "foo_tool2.out",
-    ],
-    srcs = ["foo_tool.in"],
-)`,
-			},
-		},
-		{
-			description:                        "genrule using $(locations //absolute:label)",
-			moduleTypeUnderTest:                "genrule",
-			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
-			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			bp: `genrule {
-    name: "foo",
-    out: ["foo.out"],
-    srcs: ["foo.in"],
-    tool_files: [":foo.tool"],
-    cmd: "$(locations :foo.tool) -s $(out) $(in)",
-    bazel_module: { bp2build_available: true },
-}`,
-			expectedBazelTargets: []string{`genrule(
-    name = "foo",
-    cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
-    outs = ["foo.out"],
-    srcs = ["foo.in"],
-    tools = ["//other:foo.tool"],
-)`,
-			},
-			fs: otherGenruleBp,
-		},
-		{
-			description:                        "genrule srcs using $(locations //absolute:label)",
-			moduleTypeUnderTest:                "genrule",
-			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
-			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			bp: `genrule {
-    name: "foo",
-    out: ["foo.out"],
-    srcs: [":other.tool"],
-    tool_files: [":foo.tool"],
-    cmd: "$(locations :foo.tool) -s $(out) $(location :other.tool)",
-    bazel_module: { bp2build_available: true },
-}`,
-			expectedBazelTargets: []string{`genrule(
-    name = "foo",
-    cmd = "$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)",
-    outs = ["foo.out"],
-    srcs = ["//other:other.tool"],
-    tools = ["//other:foo.tool"],
-)`,
-			},
-			fs: otherGenruleBp,
-		},
-		{
-			description:                        "genrule using $(location) label should substitute first tool label automatically",
-			moduleTypeUnderTest:                "genrule",
-			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
-			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			bp: `genrule {
-    name: "foo",
-    out: ["foo.out"],
-    srcs: ["foo.in"],
-    tool_files: [":foo.tool", ":other.tool"],
-    cmd: "$(location) -s $(out) $(in)",
-    bazel_module: { bp2build_available: true },
-}`,
-			expectedBazelTargets: []string{`genrule(
-    name = "foo",
-    cmd = "$(location //other:foo.tool) -s $(OUTS) $(SRCS)",
-    outs = ["foo.out"],
-    srcs = ["foo.in"],
-    tools = [
-        "//other:foo.tool",
-        "//other:other.tool",
-    ],
-)`,
-			},
-			fs: otherGenruleBp,
-		},
-		{
-			description:                        "genrule using $(locations) label should substitute first tool label automatically",
-			moduleTypeUnderTest:                "genrule",
-			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
-			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			bp: `genrule {
-    name: "foo",
-    out: ["foo.out"],
-    srcs: ["foo.in"],
-    tools: [":foo.tool", ":other.tool"],
-    cmd: "$(locations) -s $(out) $(in)",
-    bazel_module: { bp2build_available: true },
-}`,
-			expectedBazelTargets: []string{`genrule(
-    name = "foo",
-    cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
-    outs = ["foo.out"],
-    srcs = ["foo.in"],
-    tools = [
-        "//other:foo.tool",
-        "//other:other.tool",
-    ],
-)`,
-			},
-			fs: otherGenruleBp,
-		},
-		{
-			description:                        "genrule without tools or tool_files can convert successfully",
-			moduleTypeUnderTest:                "genrule",
-			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
-			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			bp: `genrule {
-    name: "foo",
-    out: ["foo.out"],
-    srcs: ["foo.in"],
-    cmd: "cp $(in) $(out)",
-    bazel_module: { bp2build_available: true },
-}`,
-			expectedBazelTargets: []string{`genrule(
-    name = "foo",
-    cmd = "cp $(SRCS) $(OUTS)",
-    outs = ["foo.out"],
-    srcs = ["foo.in"],
-)`,
-			},
-		},
 	}
 
-	dir := "."
 	for _, testCase := range testCases {
-		fs := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.fs {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
-			}
-			fs[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.bp, fs)
-		ctx := android.NewTestContext(config)
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
-
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if errored(t, testCase.description, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if errored(t, testCase.description, errs) {
-			continue
-		}
-
-		checkDir := dir
-		if testCase.dir != "" {
-			checkDir = testCase.dir
-		}
-
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
-		} else {
-			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
-					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
-					)
-				}
-			}
-		}
+		t.Run(testCase.description, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, testCase)
+		})
 	}
 }
 
 type bp2buildMutator = func(android.TopDownMutatorContext)
 
-func TestBp2BuildInlinesDefaults(t *testing.T) {
-	testCases := []struct {
-		moduleTypesUnderTest      map[string]android.ModuleFactory
-		bp2buildMutatorsUnderTest map[string]bp2buildMutator
-		bp                        string
-		expectedBazelTarget       string
-		description               string
-	}{
-		{
-			moduleTypesUnderTest: map[string]android.ModuleFactory{
-				"genrule":          genrule.GenRuleFactory,
-				"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
-			},
-			bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
-				"genrule": genrule.GenruleBp2Build,
-			},
-			bp: `genrule_defaults {
-    name: "gen_defaults",
-    cmd: "do-something $(in) $(out)",
-}
-genrule {
-    name: "gen",
-    out: ["out"],
-    srcs: ["in1"],
-    defaults: ["gen_defaults"],
-    bazel_module: { bp2build_available: true },
-}
-`,
-			expectedBazelTarget: `genrule(
-    name = "gen",
-    cmd = "do-something $(SRCS) $(OUTS)",
-    outs = ["out"],
-    srcs = ["in1"],
-)`,
-			description: "genrule applies properties from a genrule_defaults dependency if not specified",
-		},
-		{
-			moduleTypesUnderTest: map[string]android.ModuleFactory{
-				"genrule":          genrule.GenRuleFactory,
-				"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
-			},
-			bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
-				"genrule": genrule.GenruleBp2Build,
-			},
-			bp: `genrule_defaults {
-    name: "gen_defaults",
-    out: ["out-from-defaults"],
-    srcs: ["in-from-defaults"],
-    cmd: "cmd-from-defaults",
-}
-genrule {
-    name: "gen",
-    out: ["out"],
-    srcs: ["in1"],
-    defaults: ["gen_defaults"],
-    cmd: "do-something $(in) $(out)",
-    bazel_module: { bp2build_available: true },
-}
-`,
-			expectedBazelTarget: `genrule(
-    name = "gen",
-    cmd = "do-something $(SRCS) $(OUTS)",
-    outs = [
-        "out-from-defaults",
-        "out",
-    ],
-    srcs = [
-        "in-from-defaults",
-        "in1",
-    ],
-)`,
-			description: "genrule does merges properties from a genrule_defaults dependency, latest-first",
-		},
-		{
-			moduleTypesUnderTest: map[string]android.ModuleFactory{
-				"genrule":          genrule.GenRuleFactory,
-				"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
-			},
-			bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
-				"genrule": genrule.GenruleBp2Build,
-			},
-			bp: `genrule_defaults {
-    name: "gen_defaults1",
-    cmd: "cp $(in) $(out)",
-}
-
-genrule_defaults {
-    name: "gen_defaults2",
-    srcs: ["in1"],
-}
-
-genrule {
-    name: "gen",
-    out: ["out"],
-    defaults: ["gen_defaults1", "gen_defaults2"],
-    bazel_module: { bp2build_available: true },
-}
-`,
-			expectedBazelTarget: `genrule(
-    name = "gen",
-    cmd = "cp $(SRCS) $(OUTS)",
-    outs = ["out"],
-    srcs = ["in1"],
-)`,
-			description: "genrule applies properties from list of genrule_defaults",
-		},
-		{
-			moduleTypesUnderTest: map[string]android.ModuleFactory{
-				"genrule":          genrule.GenRuleFactory,
-				"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
-			},
-			bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
-				"genrule": genrule.GenruleBp2Build,
-			},
-			bp: `genrule_defaults {
-    name: "gen_defaults1",
-    defaults: ["gen_defaults2"],
-    cmd: "cmd1 $(in) $(out)", // overrides gen_defaults2's cmd property value.
-}
-
-genrule_defaults {
-    name: "gen_defaults2",
-    defaults: ["gen_defaults3"],
-    cmd: "cmd2 $(in) $(out)",
-    out: ["out-from-2"],
-    srcs: ["in1"],
-}
-
-genrule_defaults {
-    name: "gen_defaults3",
-    out: ["out-from-3"],
-    srcs: ["srcs-from-3"],
-}
-
-genrule {
-    name: "gen",
-    out: ["out"],
-    defaults: ["gen_defaults1"],
-    bazel_module: { bp2build_available: true },
-}
-`,
-			expectedBazelTarget: `genrule(
-    name = "gen",
-    cmd = "cmd1 $(SRCS) $(OUTS)",
-    outs = [
-        "out-from-3",
-        "out-from-2",
-        "out",
-    ],
-    srcs = [
-        "srcs-from-3",
-        "in1",
-    ],
-)`,
-			description: "genrule applies properties from genrule_defaults transitively",
-		},
-	}
-
-	dir := "."
-	for _, testCase := range testCases {
-		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
-		ctx := android.NewTestContext(config)
-		for m, factory := range testCase.moduleTypesUnderTest {
-			ctx.RegisterModuleType(m, factory)
-		}
-		for mutator, f := range testCase.bp2buildMutatorsUnderTest {
-			ctx.RegisterBp2BuildMutator(mutator, f)
-		}
-		ctx.RegisterForBazelConversion()
-
-		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
-		android.FailIfErrored(t, errs)
-		_, errs = ctx.ResolveDependencies(config)
-		android.FailIfErrored(t, errs)
-
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
-		if actualCount := len(bazelTargets); actualCount != 1 {
-			t.Fatalf("%s: Expected 1 bazel target, got %d", testCase.description, actualCount)
-		}
-
-		actualBazelTarget := bazelTargets[0]
-		if actualBazelTarget.content != testCase.expectedBazelTarget {
-			t.Errorf(
-				"%s: Expected generated Bazel target to be '%s', got '%s'",
-				testCase.description,
-				testCase.expectedBazelTarget,
-				actualBazelTarget.content,
-			)
-		}
-	}
-}
-
 func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) {
 	testCases := []struct {
 		moduleTypeUnderTest                string
@@ -1226,7 +816,8 @@
 			android.FailIfErrored(t, errs)
 
 			codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+			android.FailIfErrored(t, err)
 			if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
 				t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount)
 			}
@@ -1338,7 +929,8 @@
 
 		// For each directory, test that the expected number of generated targets is correct.
 		for dir, expectedCount := range testCase.expectedCount {
-			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+			android.FailIfErrored(t, err)
 			if actualCount := len(bazelTargets); actualCount != expectedCount {
 				t.Fatalf(
 					"%s: Expected %d bazel target for %s package, got %d",
@@ -1353,30 +945,20 @@
 }
 
 func TestCombineBuildFilesBp2buildTargets(t *testing.T) {
-	testCases := []struct {
-		description                        string
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-		preArchMutators                    []android.RegisterMutatorFunc
-		bp                                 string
-		expectedBazelTargets               []string
-		fs                                 map[string]string
-		dir                                string
-	}{
+	testCases := []bp2buildTestCase{
 		{
 			description:                        "filegroup bazel_module.label",
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
     name: "fg_foo",
     bazel_module: { label: "//other:fg_foo" },
 }`,
 			expectedBazelTargets: []string{
 				`// BUILD file`,
 			},
-			fs: map[string]string{
+			filesystem: map[string]string{
 				"other/BUILD.bazel": `// BUILD file`,
 			},
 		},
@@ -1385,7 +967,7 @@
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
 		    name: "fg_foo",
 		    bazel_module: { label: "//other:fg_foo" },
 		}
@@ -1397,7 +979,7 @@
 			expectedBazelTargets: []string{
 				`// BUILD file`,
 			},
-			fs: map[string]string{
+			filesystem: map[string]string{
 				"other/BUILD.bazel": `// BUILD file`,
 			},
 		},
@@ -1407,8 +989,8 @@
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
 			dir:                                "other",
-			bp:                                 ``,
-			fs: map[string]string{
+			blueprint:                          ``,
+			filesystem: map[string]string{
 				"other/Android.bp": `filegroup {
 				name: "fg_foo",
 				bazel_module: {
@@ -1434,7 +1016,7 @@
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
 		    name: "fg_foo",
 		    bazel_module: {
 		      label: "//other:fg_foo",
@@ -1453,7 +1035,7 @@
 )`,
 				`// BUILD file`,
 			},
-			fs: map[string]string{
+			filesystem: map[string]string{
 				"other/BUILD.bazel": `// BUILD file`,
 			},
 		},
@@ -1466,24 +1048,24 @@
 			toParse := []string{
 				"Android.bp",
 			}
-			for f, content := range testCase.fs {
+			for f, content := range testCase.filesystem {
 				if strings.HasSuffix(f, "Android.bp") {
 					toParse = append(toParse, f)
 				}
 				fs[f] = []byte(content)
 			}
-			config := android.TestConfig(buildDir, nil, testCase.bp, fs)
+			config := android.TestConfig(buildDir, nil, testCase.blueprint, fs)
 			ctx := android.NewTestContext(config)
 			ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
 			ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
 			ctx.RegisterForBazelConversion()
 
 			_, errs := ctx.ParseFileList(dir, toParse)
-			if errored(t, testCase.description, errs) {
+			if errored(t, testCase, errs) {
 				return
 			}
 			_, errs = ctx.ResolveDependencies(config)
-			if errored(t, testCase.description, errs) {
+			if errored(t, testCase, errs) {
 				return
 			}
 
@@ -1491,7 +1073,9 @@
 			if testCase.dir != "" {
 				checkDir = testCase.dir
 			}
-			bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
+			codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
+			android.FailIfErrored(t, err)
 			bazelTargets.sort()
 			actualCount := len(bazelTargets)
 			expectedCount := len(testCase.expectedBazelTargets)
@@ -1517,22 +1101,13 @@
 }
 
 func TestGlobExcludeSrcs(t *testing.T) {
-	testCases := []struct {
-		description                        string
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-		bp                                 string
-		expectedBazelTargets               []string
-		fs                                 map[string]string
-		dir                                string
-	}{
+	testCases := []bp2buildTestCase{
 		{
 			description:                        "filegroup top level exclude_srcs",
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
+			blueprint: `filegroup {
     name: "fg_foo",
     srcs: ["**/*.txt"],
     exclude_srcs: ["c.txt"],
@@ -1548,7 +1123,7 @@
     ],
 )`,
 			},
-			fs: map[string]string{
+			filesystem: map[string]string{
 				"a.txt":          "",
 				"b.txt":          "",
 				"c.txt":          "",
@@ -1562,9 +1137,9 @@
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp:                                 "",
+			blueprint:                          "",
 			dir:                                "dir",
-			fs: map[string]string{
+			filesystem: map[string]string{
 				"dir/Android.bp": `filegroup {
     name: "fg_foo",
     srcs: ["**/*.txt"],
@@ -1596,24 +1171,24 @@
 		toParse := []string{
 			"Android.bp",
 		}
-		for f, content := range testCase.fs {
+		for f, content := range testCase.filesystem {
 			if strings.HasSuffix(f, "Android.bp") {
 				toParse = append(toParse, f)
 			}
 			fs[f] = []byte(content)
 		}
-		config := android.TestConfig(buildDir, nil, testCase.bp, fs)
+		config := android.TestConfig(buildDir, nil, testCase.blueprint, fs)
 		ctx := android.NewTestContext(config)
 		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
 		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, toParse)
-		if errored(t, testCase.description, errs) {
+		if errored(t, testCase, errs) {
 			continue
 		}
 		_, errs = ctx.ResolveDependencies(config)
-		if errored(t, testCase.description, errs) {
+		if errored(t, testCase, errs) {
 			continue
 		}
 
@@ -1621,7 +1196,9 @@
 		if testCase.dir != "" {
 			checkDir = testCase.dir
 		}
-		bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
+		android.FailIfErrored(t, err)
 		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
 			t.Errorf("%s: Expected %d bazel target, got %d\n%s", testCase.description, expectedCount, actualCount, bazelTargets)
 		} else {
diff --git a/bp2build/bzl_conversion.go b/bp2build/bzl_conversion.go
index f2f6b01..992cc1c 100644
--- a/bp2build/bzl_conversion.go
+++ b/bp2build/bzl_conversion.go
@@ -160,8 +160,15 @@
 		if shouldSkipStructField(field) {
 			continue
 		}
-
-		properties = append(properties, extractPropertyDescriptions(field.Name, field.Type)...)
+		subProps := extractPropertyDescriptions(field.Name, field.Type)
+		// if the struct is embedded (anonymous), flatten the properties into the containing struct
+		if field.Anonymous {
+			for _, prop := range subProps {
+				properties = append(properties, prop.properties...)
+			}
+		} else {
+			properties = append(properties, subProps...)
+		}
 	}
 	return properties
 }
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 9e0c0a1..1e78c0e 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -92,6 +92,7 @@
         # bazel_module end
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
+        "embedded_prop": attr.string(),
         "int64_ptr_prop": attr.int(),
         # nested_props start
 #         "nested_prop": attr.string(),
@@ -99,6 +100,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
@@ -118,6 +120,7 @@
         "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
+        "embedded_prop": attr.string(),
         "int64_ptr_prop": attr.int(),
         # nested_props start
 #         "nested_prop": attr.string(),
@@ -125,6 +128,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
@@ -144,6 +148,7 @@
         "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
+        "embedded_prop": attr.string(),
         "int64_ptr_prop": attr.int(),
         # nested_props start
 #         "nested_prop": attr.string(),
@@ -151,6 +156,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index c840016..b3a1053 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -25,18 +25,18 @@
 	// See cc/testing.go for more context
 	soongCcLibraryPreamble = `
 cc_defaults {
-  name: "linux_bionic_supported",
+    name: "linux_bionic_supported",
 }
 
 toolchain_library {
-  name: "libclang_rt.builtins-x86_64-android",
-  defaults: ["linux_bionic_supported"],
-  vendor_available: true,
-  vendor_ramdisk_available: true,
-  product_available: true,
-  recovery_available: true,
-  native_bridge_supported: true,
-  src: "",
+    name: "libclang_rt.builtins-x86_64-android",
+    defaults: ["linux_bionic_supported"],
+    vendor_available: true,
+    vendor_ramdisk_available: true,
+    product_available: true,
+    recovery_available: true,
+    native_bridge_supported: true,
+    src: "",
 }`
 )
 
@@ -113,17 +113,14 @@
           srcs: ["bionic.cpp"]
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "foo-lib",
-    copts = [
-        "-Wall",
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
+    copts = ["-Wall"],
+    export_includes = ["foo-dir"],
     implementation_deps = [":some-headers"],
-    includes = ["foo-dir"],
     linkopts = ["-Wl,--exclude-libs=bar.a"] + select({
         "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=baz.a"],
         "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=qux.a"],
@@ -186,6 +183,7 @@
             ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
@@ -195,8 +193,6 @@
         "-Wextra",
         "-Wunused",
         "-Werror",
-        "-I.",
-        "-I$(BINDIR)/.",
     ],
     implementation_deps = [":libc_headers"],
     linkopts = [
@@ -259,13 +255,11 @@
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "fake-libarm-optimized-routines-math",
-    copts = [
-        "-Iexternal",
-        "-I$(BINDIR)/external",
-    ] + select({
+    copts = select({
         "//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"],
         "//conditions:default": [],
     }),
+    local_includes = ["."],
     srcs_c = ["math/cosf.c"],
 )`},
 	})
@@ -277,12 +271,12 @@
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
 		filesystem: map[string]string{
-			"foo/bar/both.cpp":       "",
-			"foo/bar/sharedonly.cpp": "",
-			"foo/bar/staticonly.cpp": "",
-			"foo/bar/Android.bp": `
+			"both.cpp":       "",
+			"sharedonly.cpp": "",
+			"staticonly.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "a",
     srcs: ["both.cpp"],
@@ -304,36 +298,57 @@
         static_libs: ["static_dep_for_shared"],
         whole_static_libs: ["whole_static_lib_for_shared"],
     },
-    bazel_module: { bp2build_available: true },
+    include_build_directory: false,
 }
 
-cc_library_static { name: "static_dep_for_shared" }
+cc_library_static {
+    name: "static_dep_for_shared",
+    bazel_module: { bp2build_available: false },
+}
 
-cc_library_static { name: "static_dep_for_static" }
+cc_library_static {
+    name: "static_dep_for_static",
+    bazel_module: { bp2build_available: false },
+}
 
-cc_library_static { name: "static_dep_for_both" }
+cc_library_static {
+    name: "static_dep_for_both",
+    bazel_module: { bp2build_available: false },
+}
 
-cc_library_static { name: "whole_static_lib_for_shared" }
+cc_library_static {
+    name: "whole_static_lib_for_shared",
+    bazel_module: { bp2build_available: false },
+}
 
-cc_library_static { name: "whole_static_lib_for_static" }
+cc_library_static {
+    name: "whole_static_lib_for_static",
+    bazel_module: { bp2build_available: false },
+}
 
-cc_library_static { name: "whole_static_lib_for_both" }
+cc_library_static {
+    name: "whole_static_lib_for_both",
+    bazel_module: { bp2build_available: false },
+}
 
-cc_library { name: "shared_dep_for_shared" }
+cc_library {
+    name: "shared_dep_for_shared",
+    bazel_module: { bp2build_available: false },
+}
 
-cc_library { name: "shared_dep_for_static" }
+cc_library {
+    name: "shared_dep_for_static",
+    bazel_module: { bp2build_available: false },
+}
 
-cc_library { name: "shared_dep_for_both" }
+cc_library {
+    name: "shared_dep_for_both",
+    bazel_module: { bp2build_available: false },
+}
 `,
-		},
-		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = [
-        "bothflag",
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
+    copts = ["bothflag"],
     dynamic_deps = [":shared_dep_for_both"],
     implementation_deps = [":static_dep_for_both"],
     shared = {
@@ -374,6 +389,7 @@
         whole_static_libs: ["whole_static_lib_for_shared"],
     },
     bazel_module: { bp2build_available: true },
+    include_build_directory: false,
 }
 
 cc_prebuilt_library_static { name: "whole_static_lib_for_shared" }
@@ -386,10 +402,6 @@
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     shared = {
         "whole_archive_deps": [":whole_static_lib_for_shared_alwayslink"],
     },
@@ -480,12 +492,9 @@
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = [
-        "bothflag",
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
+    copts = ["bothflag"],
     implementation_deps = [":static_dep_for_both"],
+    local_includes = ["."],
     shared = {
         "copts": ["sharedflag"] + select({
             "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
@@ -574,24 +583,24 @@
         ":both_filegroup",
   ],
     static: {
-    srcs: [
-      "static_source.cpp",
-      "static_source.cc",
-      "static_source.c",
-      "static_source.s",
-      "static_source.S",
-      ":static_filegroup",
-    ],
+        srcs: [
+          "static_source.cpp",
+          "static_source.cc",
+          "static_source.c",
+          "static_source.s",
+          "static_source.S",
+          ":static_filegroup",
+        ],
     },
     shared: {
-    srcs: [
-      "shared_source.cpp",
-      "shared_source.cc",
-      "shared_source.c",
-      "shared_source.s",
-      "shared_source.S",
-      ":shared_filegroup",
-    ],
+        srcs: [
+          "shared_source.cpp",
+          "shared_source.cc",
+          "shared_source.c",
+          "shared_source.s",
+          "shared_source.S",
+          ":shared_filegroup",
+        ],
     },
     bazel_module: { bp2build_available: true },
 }
@@ -621,14 +630,7 @@
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    asflags = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
+    local_includes = ["."],
     shared = {
         "srcs": [
             ":shared_filegroup_cpp_srcs",
@@ -693,16 +695,13 @@
     srcs: ["a.cpp"],
     version_script: "v.map",
     bazel_module: { bp2build_available: true },
+    include_build_directory: false,
 }
 `,
 		},
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     srcs = ["a.cpp"],
     version_script = "v.map",
 )`},
@@ -718,29 +717,26 @@
 		dir:                                "foo/bar",
 		filesystem: map[string]string{
 			"foo/bar/Android.bp": `
-    cc_library {
-       name: "a",
-       srcs: ["a.cpp"],
-       arch: {
-         arm: {
-           version_script: "arm.map",
-         },
-         arm64: {
-           version_script: "arm64.map",
-         },
-       },
+cc_library {
+   name: "a",
+   srcs: ["a.cpp"],
+   arch: {
+     arm: {
+       version_script: "arm.map",
+     },
+     arm64: {
+       version_script: "arm64.map",
+     },
+   },
 
-       bazel_module: { bp2build_available: true },
-    }
+   bazel_module: { bp2build_available: true },
+    include_build_directory: false,
+}
     `,
 		},
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     srcs = ["a.cpp"],
     version_script = select({
         "//build/bazel/platforms/arch:arm": "arm.map",
@@ -757,35 +753,21 @@
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
-		filesystem: map[string]string{
-			"foo/bar/Android.bp": `
+		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "mylib",
-    bazel_module: { bp2build_available: true },
+    bazel_module: { bp2build_available: false },
 }
 
 cc_library {
     name: "a",
     shared_libs: ["mylib",],
-    bazel_module: { bp2build_available: true },
+    include_build_directory: false,
 }
 `,
-		},
-		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     dynamic_deps = [":mylib"],
-)`, `cc_library(
-    name = "mylib",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
 )`},
 	})
 }
@@ -796,14 +778,12 @@
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
-		filesystem: map[string]string{
-			"foo/bar/Android.bp": `
+		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "a",
     srcs: ["a.cpp"],
     pack_relocations: false,
-    bazel_module: { bp2build_available: true },
+    include_build_directory: false,
 }
 
 cc_library {
@@ -811,10 +791,10 @@
     srcs: ["b.cpp"],
     arch: {
         x86_64: {
-    pack_relocations: false,
-  },
+            pack_relocations: false,
+        },
     },
-    bazel_module: { bp2build_available: true },
+    include_build_directory: false,
 }
 
 cc_library {
@@ -822,27 +802,17 @@
     srcs: ["c.cpp"],
     target: {
         darwin: {
-    pack_relocations: false,
-  },
+            pack_relocations: false,
+        },
     },
-    bazel_module: { bp2build_available: true },
+    include_build_directory: false,
 }`,
-		},
-		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     linkopts = ["-Wl,--pack-dyn-relocs=none"],
     srcs = ["a.cpp"],
 )`, `cc_library(
     name = "b",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     linkopts = select({
         "//build/bazel/platforms/arch:x86_64": ["-Wl,--pack-dyn-relocs=none"],
         "//conditions:default": [],
@@ -850,10 +820,6 @@
     srcs = ["b.cpp"],
 )`, `cc_library(
     name = "c",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     linkopts = select({
         "//build/bazel/platforms/os:darwin": ["-Wl,--pack-dyn-relocs=none"],
         "//conditions:default": [],
@@ -869,24 +835,18 @@
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
-		filesystem: map[string]string{
-			"foo/bar/Android.bp": `
+		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "a",
     cflags: ["-include header.h",],
-    bazel_module: { bp2build_available: true },
+    include_build_directory: false,
 }
 `,
-		},
-		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
     copts = [
         "-include",
         "header.h",
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
     ],
 )`},
 	})
@@ -898,40 +858,30 @@
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
-		filesystem: map[string]string{
-			"foo/bar/Android.bp": `cc_library {
+		blueprint: soongCcLibraryPreamble + `cc_library {
     name: "a",
     srcs: ["a.cpp"],
-    cflags: [
-    "-Wall",
-  ],
+    cflags: ["-Wall"],
     cppflags: [
         "-fsigned-char",
         "-pedantic",
-  ],
+    ],
     arch: {
         arm64: {
             cppflags: ["-DARM64=1"],
+        },
     },
-  },
     target: {
         android: {
             cppflags: ["-DANDROID=1"],
+        },
     },
-  },
-    bazel_module: { bp2build_available: true  },
+    include_build_directory: false,
 }
 `,
-		},
-		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = [
-        "-Wall",
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
+    copts = ["-Wall"],
     cppflags = [
         "-fsigned-char",
         "-pedantic",
@@ -953,32 +903,23 @@
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
-		filesystem: map[string]string{
-			"foo/bar/Android.bp": `
-    cc_library {
-       name: "a",
-       srcs: ["a.cpp"],
-       target: {
-         android_arm: {
-           version_script: "android_arm.map",
-         },
-         linux_bionic_arm64: {
-           version_script: "linux_bionic_arm64.map",
-         },
-       },
-
-       bazel_module: { bp2build_available: true },
-    }
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+   name: "a",
+   srcs: ["a.cpp"],
+   target: {
+     android_arm: {
+       version_script: "android_arm.map",
+     },
+     linux_bionic_arm64: {
+       version_script: "linux_bionic_arm64.map",
+     },
+   },
+    include_build_directory: false,
+}
     `,
-		},
-		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     srcs = ["a.cpp"],
     version_script = select({
         "//build/bazel/platforms/os_arch:android_arm": "android_arm.map",
@@ -1031,6 +972,7 @@
             ],
         },
     },
+    include_build_directory: false,
 }
 
 cc_library {
@@ -1071,10 +1013,6 @@
 		expectedBazelTargets: []string{
 			`cc_library(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     dynamic_deps = select({
         "//build/bazel/platforms/arch:arm": [],
         "//conditions:default": [":arm_shared_lib_excludes"],
@@ -1084,18 +1022,18 @@
     }),
     implementation_deps = select({
         "//build/bazel/platforms/arch:arm": [],
-        "//conditions:default": [":arm_static_lib_excludes"],
+        "//conditions:default": [":arm_static_lib_excludes_bp2build_cc_library_static"],
     }) + select({
         "//build/bazel/product_variables:malloc_not_svelte": [],
-        "//conditions:default": [":malloc_not_svelte_static_lib_excludes"],
+        "//conditions:default": [":malloc_not_svelte_static_lib_excludes_bp2build_cc_library_static"],
     }),
     srcs_c = ["common.c"],
     whole_archive_deps = select({
         "//build/bazel/platforms/arch:arm": [],
-        "//conditions:default": [":arm_whole_static_lib_excludes"],
+        "//conditions:default": [":arm_whole_static_lib_excludes_bp2build_cc_library_static"],
     }) + select({
-        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib"],
-        "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes"],
+        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib_bp2build_cc_library_static"],
+        "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes_bp2build_cc_library_static"],
     }),
 )`,
 		},
@@ -1117,14 +1055,11 @@
     name: "foo-lib",
     srcs: ["impl.cpp"],
     no_libcrt: true,
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "foo-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     srcs = ["impl.cpp"],
     use_libcrt = False,
 )`}})
@@ -1144,14 +1079,11 @@
     name: "foo-lib",
     srcs: ["impl.cpp"],
     no_libcrt: false,
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "foo-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     srcs = ["impl.cpp"],
     use_libcrt = True,
 )`}})
@@ -1166,7 +1098,6 @@
 			"impl.cpp": "",
 		},
 		blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "some-headers" }
 cc_library {
     name: "foo-lib",
     srcs: ["impl.cpp"],
@@ -1178,14 +1109,11 @@
             no_libcrt: true,
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "foo-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     srcs = ["impl.cpp"],
     use_libcrt = select({
         "//build/bazel/platforms/arch:arm": False,
@@ -1204,7 +1132,6 @@
 			"impl.cpp": "",
 		},
 		blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "some-headers" }
 cc_library {
     name: "foo-lib",
     srcs: ["impl.cpp"],
@@ -1217,14 +1144,11 @@
             no_libcrt: true,
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "foo-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     srcs = ["impl.cpp"],
     use_libcrt = select({
         "//build/bazel/platforms/arch:arm": False,
@@ -1240,102 +1164,74 @@
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
-		filesystem: map[string]string{
-			"foo/bar/Android.bp": `
+		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "nothing",
-    bazel_module: { bp2build_available: true },
+    include_build_directory: false,
 }
 cc_library {
     name: "keep_symbols",
-    bazel_module: { bp2build_available: true },
     strip: {
-		keep_symbols: true,
-	}
+        keep_symbols: true,
+    },
+    include_build_directory: false,
 }
 cc_library {
     name: "keep_symbols_and_debug_frame",
-    bazel_module: { bp2build_available: true },
     strip: {
-		keep_symbols_and_debug_frame: true,
-	}
+        keep_symbols_and_debug_frame: true,
+    },
+    include_build_directory: false,
 }
 cc_library {
     name: "none",
-    bazel_module: { bp2build_available: true },
     strip: {
-		none: true,
-	}
+        none: true,
+    },
+    include_build_directory: false,
 }
 cc_library {
     name: "keep_symbols_list",
-    bazel_module: { bp2build_available: true },
     strip: {
-		keep_symbols_list: ["symbol"],
-	}
+        keep_symbols_list: ["symbol"],
+    },
+    include_build_directory: false,
 }
 cc_library {
     name: "all",
-    bazel_module: { bp2build_available: true },
     strip: {
-		all: true,
-	}
+        all: true,
+    },
+    include_build_directory: false,
 }
 `,
-		},
-		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "all",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     strip = {
         "all": True,
     },
 )`, `cc_library(
     name = "keep_symbols",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     strip = {
         "keep_symbols": True,
     },
 )`, `cc_library(
     name = "keep_symbols_and_debug_frame",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     strip = {
         "keep_symbols_and_debug_frame": True,
     },
 )`, `cc_library(
     name = "keep_symbols_list",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     strip = {
         "keep_symbols_list": ["symbol"],
     },
 )`, `cc_library(
     name = "none",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     strip = {
         "none": True,
     },
 )`, `cc_library(
     name = "nothing",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
 )`},
 	})
 }
@@ -1346,12 +1242,9 @@
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
-		filesystem: map[string]string{
-			"foo/bar/Android.bp": `
+		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "multi-arch",
-    bazel_module: { bp2build_available: true },
     target: {
         darwin: {
             strip: {
@@ -1370,17 +1263,12 @@
                 keep_symbols: true,
             },
         },
-    }
+    },
+    include_build_directory: false,
 }
 `,
-		},
-		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{`cc_library(
     name = "multi-arch",
-    copts = [
-        "-Ifoo/bar",
-        "-I$(BINDIR)/foo/bar",
-    ],
     strip = {
         "keep_symbols": select({
             "//build/bazel/platforms/arch:arm64": True,
@@ -1411,15 +1299,12 @@
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "root_empty",
-	  system_shared_libs: [],
+    system_shared_libs: [],
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "root_empty",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     system_dynamic_deps = [],
 )`},
 	})
@@ -1435,16 +1320,13 @@
 cc_library {
     name: "static_empty",
     static: {
-				system_shared_libs: [],
-		},
+        system_shared_libs: [],
+    },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "static_empty",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     static = {
         "system_dynamic_deps": [],
     },
@@ -1462,16 +1344,13 @@
 cc_library {
     name: "shared_empty",
     shared: {
-				system_shared_libs: [],
-		},
+        system_shared_libs: [],
+    },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "shared_empty",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     shared = {
         "system_dynamic_deps": [],
     },
@@ -1494,15 +1373,12 @@
                 system_shared_libs: [],
             }
         }
-		},
+    },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "shared_empty",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     shared = {
         "system_dynamic_deps": [],
     },
@@ -1528,14 +1404,11 @@
             system_shared_libs: [],
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "target_linux_bionic_empty",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     system_dynamic_deps = [],
 )`},
 	})
@@ -1555,14 +1428,11 @@
             system_shared_libs: [],
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "target_bionic_empty",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     system_dynamic_deps = [],
 )`},
 	})
@@ -1575,39 +1445,30 @@
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
 		blueprint: soongCcLibraryPreamble + `
-cc_library {name: "libc"}
-cc_library {name: "libm"}
+cc_library {
+    name: "libc",
+    bazel_module: { bp2build_available: false },
+}
+cc_library {
+    name: "libm",
+    bazel_module: { bp2build_available: false },
+}
 
 cc_library {
     name: "foo",
     system_shared_libs: ["libc"],
     shared: {
-				system_shared_libs: ["libm"],
+        system_shared_libs: ["libm"],
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library(
     name = "foo",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     shared = {
         "system_dynamic_deps": [":libm"],
     },
     system_dynamic_deps = [":libc"],
-)`, `cc_library(
-    name = "libc",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-)`, `cc_library(
-    name = "libm",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
 )`},
 	})
 }
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index ea2c10a..37d806c 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -25,18 +25,18 @@
 	// See cc/testing.go for more context
 	soongCcLibraryHeadersPreamble = `
 cc_defaults {
-	name: "linux_bionic_supported",
+    name: "linux_bionic_supported",
 }
 
 toolchain_library {
-	name: "libclang_rt.builtins-x86_64-android",
-	defaults: ["linux_bionic_supported"],
-	vendor_available: true,
-	vendor_ramdisk_available: true,
-	product_available: true,
-	recovery_available: true,
-	native_bridge_supported: true,
-	src: "",
+    name: "libclang_rt.builtins-x86_64-android",
+    defaults: ["linux_bionic_supported"],
+    vendor_available: true,
+    vendor_ramdisk_available: true,
+    product_available: true,
+    recovery_available: true,
+    native_bridge_supported: true,
+    src: "",
 }`
 )
 
@@ -99,11 +99,13 @@
 cc_library_headers {
     name: "lib-1",
     export_include_dirs: ["lib-1"],
+    bazel_module: { bp2build_available: false },
 }
 
 cc_library_headers {
     name: "lib-2",
     export_include_dirs: ["lib-2"],
+    bazel_module: { bp2build_available: false },
 }
 
 cc_library_headers {
@@ -113,7 +115,7 @@
 
     arch: {
         arm64: {
-	    // We expect dir-1 headers to be dropped, because dir-1 is already in export_include_dirs
+      // We expect dir-1 headers to be dropped, because dir-1 is already in export_include_dirs
             export_include_dirs: ["arch_arm64_exported_include_dir", "dir-1"],
         },
         x86: {
@@ -128,15 +130,7 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_headers(
     name = "foo_headers",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    implementation_deps = [
-        ":lib-1",
-        ":lib-2",
-    ],
-    includes = [
+    export_includes = [
         "dir-1",
         "dir-2",
     ] + select({
@@ -145,25 +139,15 @@
         "//build/bazel/platforms/arch:x86_64": ["arch_x86_64_exported_include_dir"],
         "//conditions:default": [],
     }),
-)`, `cc_library_headers(
-    name = "lib-1",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
+    implementation_deps = [
+        ":lib-1",
+        ":lib-2",
     ],
-    includes = ["lib-1"],
-)`, `cc_library_headers(
-    name = "lib-2",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    includes = ["lib-2"],
 )`},
 	})
 }
 
-func TestCcLibraryHeadersOSSpecificHeader(t *testing.T) {
+func TestCcLibraryHeadersOsSpecificHeader(t *testing.T) {
 	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
 		description:                        "cc_library_headers test with os-specific header_libs props",
 		moduleTypeUnderTest:                "cc_library_headers",
@@ -171,12 +155,30 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
 		filesystem:                         map[string]string{},
 		blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "android-lib" }
-cc_library_headers { name: "base-lib" }
-cc_library_headers { name: "darwin-lib" }
-cc_library_headers { name: "linux-lib" }
-cc_library_headers { name: "linux_bionic-lib" }
-cc_library_headers { name: "windows-lib" }
+cc_library_headers {
+    name: "android-lib",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_headers {
+    name: "base-lib",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_headers {
+    name: "darwin-lib",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_headers {
+    name: "linux-lib",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_headers {
+    name: "linux_bionic-lib",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_headers {
+    name: "windows-lib",
+    bazel_module: { bp2build_available: false },
+}
 cc_library_headers {
     name: "foo_headers",
     header_libs: ["base-lib"],
@@ -187,32 +189,10 @@
         linux_glibc: { header_libs: ["linux-lib"] },
         windows: { header_libs: ["windows-lib"] },
     },
-    bazel_module: { bp2build_available: true },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_headers(
-    name = "android-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-)`, `cc_library_headers(
-    name = "base-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-)`, `cc_library_headers(
-    name = "darwin-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-)`, `cc_library_headers(
     name = "foo_headers",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     implementation_deps = [":base-lib"] + select({
         "//build/bazel/platforms/os:android": [":android-lib"],
         "//build/bazel/platforms/os:darwin": [":darwin-lib"],
@@ -221,24 +201,6 @@
         "//build/bazel/platforms/os:windows": [":windows-lib"],
         "//conditions:default": [],
     }),
-)`, `cc_library_headers(
-    name = "linux-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-)`, `cc_library_headers(
-    name = "linux_bionic-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-)`, `cc_library_headers(
-    name = "windows-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
 )`},
 	})
 }
@@ -251,32 +213,23 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
 		filesystem:                         map[string]string{},
 		blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "android-lib" }
-cc_library_headers { name: "exported-lib" }
+cc_library_headers {
+    name: "android-lib",
+    bazel_module: { bp2build_available: false },
+  }
+cc_library_headers {
+    name: "exported-lib",
+    bazel_module: { bp2build_available: false },
+}
 cc_library_headers {
     name: "foo_headers",
     target: {
         android: { header_libs: ["android-lib"], export_header_lib_headers: ["exported-lib"] },
     },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_headers(
-    name = "android-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-)`, `cc_library_headers(
-    name = "exported-lib",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-)`, `cc_library_headers(
     name = "foo_headers",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     deps = select({
         "//build/bazel/platforms/os:android": [":exported-lib"],
         "//conditions:default": [],
@@ -299,14 +252,14 @@
 		blueprint: soongCcLibraryPreamble + `cc_library_headers {
     name: "foo_headers",
     export_system_include_dirs: [
-	"shared_include_dir",
+        "shared_include_dir",
     ],
     target: {
-	android: {
-	    export_system_include_dirs: [
-		"android_include_dir",
+        android: {
+            export_system_include_dirs: [
+                "android_include_dir",
             ],
-	},
+        },
         linux_glibc: {
             export_system_include_dirs: [
                 "linux_include_dir",
@@ -320,24 +273,21 @@
     },
     arch: {
         arm: {
-	    export_system_include_dirs: [
-		"arm_include_dir",
+            export_system_include_dirs: [
+                "arm_include_dir",
             ],
-	},
+        },
         x86_64: {
             export_system_include_dirs: [
                 "x86_64_include_dir",
             ],
         },
     },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_headers(
     name = "foo_headers",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    includes = ["shared_include_dir"] + select({
+    export_system_includes = ["shared_include_dir"] + select({
         "//build/bazel/platforms/arch:arm": ["arm_include_dir"],
         "//build/bazel/platforms/arch:x86_64": ["x86_64_include_dir"],
         "//conditions:default": [],
@@ -375,14 +325,11 @@
     name: "lib-1",
     export_include_dirs: ["lib-1"],
     no_libcrt: true,
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_headers(
     name = "lib-1",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    includes = ["lib-1"],
+    export_includes = ["lib-1"],
 )`},
 	})
 }
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
new file mode 100644
index 0000000..52a07cc
--- /dev/null
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -0,0 +1,366 @@
+// Copyright 2021 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 bp2build
+
+import (
+	"testing"
+
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+const (
+	// See cc/testing.go for more context
+	// TODO(alexmarquez): Split out the preamble into common code?
+	soongCcLibrarySharedPreamble = soongCcLibraryStaticPreamble
+)
+
+func registerCcLibrarySharedModuleTypes(ctx android.RegistrationContext) {
+	cc.RegisterCCBuildComponents(ctx)
+	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+}
+
+func runCcLibrarySharedTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerCcLibrarySharedModuleTypes, tc)
+}
+
+func TestCcLibrarySharedSimple(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared simple overall test",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem: map[string]string{
+			// NOTE: include_dir headers *should not* appear in Bazel hdrs later (?)
+			"include_dir_1/include_dir_1_a.h": "",
+			"include_dir_1/include_dir_1_b.h": "",
+			"include_dir_2/include_dir_2_a.h": "",
+			"include_dir_2/include_dir_2_b.h": "",
+			// NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?)
+			"local_include_dir_1/local_include_dir_1_a.h": "",
+			"local_include_dir_1/local_include_dir_1_b.h": "",
+			"local_include_dir_2/local_include_dir_2_a.h": "",
+			"local_include_dir_2/local_include_dir_2_b.h": "",
+			// NOTE: export_include_dir headers *should* appear in Bazel hdrs later
+			"export_include_dir_1/export_include_dir_1_a.h": "",
+			"export_include_dir_1/export_include_dir_1_b.h": "",
+			"export_include_dir_2/export_include_dir_2_a.h": "",
+			"export_include_dir_2/export_include_dir_2_b.h": "",
+			// NOTE: Soong implicitly includes headers in the current directory
+			"implicit_include_1.h": "",
+			"implicit_include_2.h": "",
+		},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_headers {
+    name: "header_lib_1",
+    export_include_dirs: ["header_lib_1"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_headers {
+    name: "header_lib_2",
+    export_include_dirs: ["header_lib_2"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_shared {
+    name: "shared_lib_1",
+    srcs: ["shared_lib_1.cc"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_shared {
+    name: "shared_lib_2",
+    srcs: ["shared_lib_2.cc"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+    name: "whole_static_lib_1",
+    srcs: ["whole_static_lib_1.cc"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_static {
+    name: "whole_static_lib_2",
+    srcs: ["whole_static_lib_2.cc"],
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library_shared {
+    name: "foo_shared",
+    srcs: [
+        "foo_shared1.cc",
+        "foo_shared2.cc",
+    ],
+    cflags: [
+        "-Dflag1",
+        "-Dflag2"
+    ],
+    shared_libs: [
+        "shared_lib_1",
+        "shared_lib_2"
+    ],
+    whole_static_libs: [
+        "whole_static_lib_1",
+        "whole_static_lib_2"
+    ],
+    include_dirs: [
+        "include_dir_1",
+        "include_dir_2",
+    ],
+    local_include_dirs: [
+        "local_include_dir_1",
+        "local_include_dir_2",
+    ],
+    export_include_dirs: [
+        "export_include_dir_1",
+        "export_include_dir_2"
+    ],
+    header_libs: [
+        "header_lib_1",
+        "header_lib_2"
+    ],
+
+    // TODO: Also support export_header_lib_headers
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    absolute_includes = [
+        "include_dir_1",
+        "include_dir_2",
+    ],
+    copts = [
+        "-Dflag1",
+        "-Dflag2",
+    ],
+    dynamic_deps = [
+        ":shared_lib_1",
+        ":shared_lib_2",
+    ],
+    export_includes = [
+        "export_include_dir_1",
+        "export_include_dir_2",
+    ],
+    implementation_deps = [
+        ":header_lib_1",
+        ":header_lib_2",
+    ],
+    local_includes = [
+        "local_include_dir_1",
+        "local_include_dir_2",
+        ".",
+    ],
+    srcs = [
+        "foo_shared1.cc",
+        "foo_shared2.cc",
+    ],
+    whole_archive_deps = [
+        ":whole_static_lib_1",
+        ":whole_static_lib_2",
+    ],
+)`},
+	})
+}
+
+func TestCcLibrarySharedArchSpecificSharedLib(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared arch-specific shared_libs with whole_static_libs",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_static {
+    name: "static_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "shared_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "foo_shared",
+    arch: { arm64: { shared_libs: ["shared_dep"], whole_static_libs: ["static_dep"] } },
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    dynamic_deps = select({
+        "//build/bazel/platforms/arch:arm64": [":shared_dep"],
+        "//conditions:default": [],
+    }),
+    whole_archive_deps = select({
+        "//build/bazel/platforms/arch:arm64": [":static_dep"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
+
+func TestCcLibrarySharedOsSpecificSharedLib(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared os-specific shared_libs",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "shared_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "foo_shared",
+    target: { android: { shared_libs: ["shared_dep"], } },
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    dynamic_deps = select({
+        "//build/bazel/platforms/os:android": [":shared_dep"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
+
+func TestCcLibrarySharedBaseArchOsSpecificSharedLib(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared base, arch, and os-specific shared_libs",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "shared_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "shared_dep2",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "shared_dep3",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_shared {
+    name: "foo_shared",
+    shared_libs: ["shared_dep"],
+    target: { android: { shared_libs: ["shared_dep2"] } },
+    arch: { arm64: { shared_libs: ["shared_dep3"] } },
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    dynamic_deps = [":shared_dep"] + select({
+        "//build/bazel/platforms/arch:arm64": [":shared_dep3"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": [":shared_dep2"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
+
+func TestCcLibrarySharedSimpleExcludeSrcs(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared simple exclude_srcs",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem: map[string]string{
+			"common.c":       "",
+			"foo-a.c":        "",
+			"foo-excluded.c": "",
+		},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    srcs: ["common.c", "foo-*.c"],
+    exclude_srcs: ["foo-excluded.c"],
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    srcs_c = [
+        "common.c",
+        "foo-a.c",
+    ],
+)`},
+	})
+}
+
+func TestCcLibrarySharedStrip(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared stripping",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    strip: {
+        keep_symbols: false,
+        keep_symbols_and_debug_frame: true,
+        keep_symbols_list: ["sym", "sym2"],
+        all: true,
+        none: false,
+    },
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    strip = {
+        "all": True,
+        "keep_symbols": False,
+        "keep_symbols_and_debug_frame": True,
+        "keep_symbols_list": [
+            "sym",
+            "sym2",
+        ],
+        "none": False,
+    },
+)`},
+	})
+}
+
+func TestCcLibrarySharedVersionScript(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared version script",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem: map[string]string{
+			"version_script": "",
+		},
+		blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    version_script: "version_script",
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    version_script = "version_script",
+)`},
+	})
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index d9145f6..91b7478 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -26,18 +26,18 @@
 	// See cc/testing.go for more context
 	soongCcLibraryStaticPreamble = `
 cc_defaults {
-	name: "linux_bionic_supported",
+    name: "linux_bionic_supported",
 }
 
 toolchain_library {
-	name: "libclang_rt.builtins-x86_64-android",
-	defaults: ["linux_bionic_supported"],
-	vendor_available: true,
-	vendor_ramdisk_available: true,
-	product_available: true,
-	recovery_available: true,
-	native_bridge_supported: true,
-	src: "",
+    name: "libclang_rt.builtins-x86_64-android",
+    defaults: ["linux_bionic_supported"],
+    vendor_available: true,
+    vendor_ramdisk_available: true,
+    product_available: true,
+    recovery_available: true,
+    native_bridge_supported: true,
+    src: "",
 }`
 )
 
@@ -112,31 +112,37 @@
 cc_library_headers {
     name: "header_lib_1",
     export_include_dirs: ["header_lib_1"],
+    bazel_module: { bp2build_available: false },
 }
 
 cc_library_headers {
     name: "header_lib_2",
     export_include_dirs: ["header_lib_2"],
+    bazel_module: { bp2build_available: false },
 }
 
 cc_library_static {
     name: "static_lib_1",
     srcs: ["static_lib_1.cc"],
+    bazel_module: { bp2build_available: false },
 }
 
 cc_library_static {
     name: "static_lib_2",
     srcs: ["static_lib_2.cc"],
+    bazel_module: { bp2build_available: false },
 }
 
 cc_library_static {
     name: "whole_static_lib_1",
     srcs: ["whole_static_lib_1.cc"],
+    bazel_module: { bp2build_available: false },
 }
 
 cc_library_static {
     name: "whole_static_lib_2",
     srcs: ["whole_static_lib_2.cc"],
+    bazel_module: { bp2build_available: false },
 }
 
 cc_library_static {
@@ -178,19 +184,17 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
+    absolute_includes = [
+        "include_dir_1",
+        "include_dir_2",
+    ],
     copts = [
         "-Dflag1",
         "-Dflag2",
-        "-Iinclude_dir_1",
-        "-I$(BINDIR)/include_dir_1",
-        "-Iinclude_dir_2",
-        "-I$(BINDIR)/include_dir_2",
-        "-Ilocal_include_dir_1",
-        "-I$(BINDIR)/local_include_dir_1",
-        "-Ilocal_include_dir_2",
-        "-I$(BINDIR)/local_include_dir_2",
-        "-I.",
-        "-I$(BINDIR)/.",
+    ],
+    export_includes = [
+        "export_include_dir_1",
+        "export_include_dir_2",
     ],
     implementation_deps = [
         ":header_lib_1",
@@ -198,11 +202,11 @@
         ":static_lib_1",
         ":static_lib_2",
     ],
-    includes = [
-        "export_include_dir_1",
-        "export_include_dir_2",
+    local_includes = [
+        "local_include_dir_1",
+        "local_include_dir_2",
+        ".",
     ],
-    linkstatic = True,
     srcs = [
         "foo_static1.cc",
         "foo_static2.cc",
@@ -211,38 +215,6 @@
         ":whole_static_lib_1",
         ":whole_static_lib_2",
     ],
-)`, `cc_library_static(
-    name = "static_lib_1",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
-    srcs = ["static_lib_1.cc"],
-)`, `cc_library_static(
-    name = "static_lib_2",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
-    srcs = ["static_lib_2.cc"],
-)`, `cc_library_static(
-    name = "whole_static_lib_1",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
-    srcs = ["whole_static_lib_1.cc"],
-)`, `cc_library_static(
-    name = "whole_static_lib_2",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
-    srcs = ["whole_static_lib_2.cc"],
 )`},
 	})
 }
@@ -270,21 +242,15 @@
 		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
-    srcs: [
-    ],
+    srcs: [],
     include_dirs: [
-	"subpackage",
+        "subpackage",
     ],
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-Isubpackage",
-        "-I$(BINDIR)/subpackage",
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
+    absolute_includes = ["subpackage"],
+    local_includes = ["."],
 )`},
 	})
 }
@@ -305,15 +271,11 @@
 cc_library_static {
     name: "foo_static",
     export_include_dirs: ["subpackage"],
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    includes = ["subpackage"],
-    linkstatic = True,
+    export_includes = ["subpackage"],
 )`},
 	})
 }
@@ -334,15 +296,11 @@
 cc_library_static {
     name: "foo_static",
     export_system_include_dirs: ["subpackage"],
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    includes = ["subpackage"],
-    linkstatic = True,
+    export_system_includes = ["subpackage"],
 )`},
 	})
 }
@@ -379,20 +337,16 @@
 		blueprint: soongCcLibraryStaticPreamble,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-Isubpackage/subsubpackage",
-        "-I$(BINDIR)/subpackage/subsubpackage",
-        "-Isubpackage2",
-        "-I$(BINDIR)/subpackage2",
-        "-Isubpackage3/subsubpackage",
-        "-I$(BINDIR)/subpackage3/subsubpackage",
-        "-Isubpackage/subsubpackage2",
-        "-I$(BINDIR)/subpackage/subsubpackage2",
-        "-Isubpackage",
-        "-I$(BINDIR)/subpackage",
+    absolute_includes = [
+        "subpackage/subsubpackage",
+        "subpackage2",
+        "subpackage3/subsubpackage",
     ],
-    includes = ["./exported_subsubpackage"],
-    linkstatic = True,
+    export_includes = ["./exported_subsubpackage"],
+    local_includes = [
+        "subsubpackage2",
+        ".",
+    ],
 )`},
 	})
 }
@@ -418,13 +372,8 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-Isubpackage",
-        "-I$(BINDIR)/subpackage",
-        "-Isubpackage2",
-        "-I$(BINDIR)/subpackage2",
-    ],
-    linkstatic = True,
+    absolute_includes = ["subpackage"],
+    local_includes = ["subpackage2"],
 )`},
 	})
 }
@@ -452,15 +401,11 @@
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-Isubpackage",
-        "-I$(BINDIR)/subpackage",
-        "-Isubpackage2",
-        "-I$(BINDIR)/subpackage2",
-        "-I.",
-        "-I$(BINDIR)/.",
+    absolute_includes = ["subpackage"],
+    local_includes = [
+        "subpackage2",
+        ".",
     ],
-    linkstatic = True,
 )`},
 	})
 }
@@ -473,41 +418,29 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
 		filesystem:                         map[string]string{},
 		blueprint: soongCcLibraryStaticPreamble + `
-cc_library_static { name: "static_dep" }
-cc_library_static { name: "static_dep2" }
+cc_library_static {
+    name: "static_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_static {
+    name: "static_dep2",
+    bazel_module: { bp2build_available: false },
+}
 cc_library_static {
     name: "foo_static",
     arch: { arm64: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     implementation_deps = select({
         "//build/bazel/platforms/arch:arm64": [":static_dep"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     whole_archive_deps = select({
         "//build/bazel/platforms/arch:arm64": [":static_dep2"],
         "//conditions:default": [],
     }),
-)`, `cc_library_static(
-    name = "static_dep",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
-)`, `cc_library_static(
-    name = "static_dep2",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
 )`},
 	})
 }
@@ -520,41 +453,29 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
 		filesystem:                         map[string]string{},
 		blueprint: soongCcLibraryStaticPreamble + `
-cc_library_static { name: "static_dep" }
-cc_library_static { name: "static_dep2" }
+cc_library_static {
+    name: "static_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_static {
+    name: "static_dep2",
+    bazel_module: { bp2build_available: false },
+}
 cc_library_static {
     name: "foo_static",
     target: { android: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     implementation_deps = select({
         "//build/bazel/platforms/os:android": [":static_dep"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     whole_archive_deps = select({
         "//build/bazel/platforms/os:android": [":static_dep2"],
         "//conditions:default": [],
     }),
-)`, `cc_library_static(
-    name = "static_dep",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
-)`, `cc_library_static(
-    name = "static_dep2",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
 )`},
 	})
 }
@@ -567,23 +488,32 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
 		filesystem:                         map[string]string{},
 		blueprint: soongCcLibraryStaticPreamble + `
-cc_library_static { name: "static_dep" }
-cc_library_static { name: "static_dep2" }
-cc_library_static { name: "static_dep3" }
-cc_library_static { name: "static_dep4" }
+cc_library_static {
+    name: "static_dep",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_static {
+    name: "static_dep2",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_static {
+    name: "static_dep3",
+    bazel_module: { bp2build_available: false },
+}
+cc_library_static {
+    name: "static_dep4",
+    bazel_module: { bp2build_available: false },
+}
 cc_library_static {
     name: "foo_static",
     static_libs: ["static_dep"],
     whole_static_libs: ["static_dep2"],
     target: { android: { static_libs: ["static_dep3"] } },
     arch: { arm64: { static_libs: ["static_dep4"] } },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     implementation_deps = [":static_dep"] + select({
         "//build/bazel/platforms/arch:arm64": [":static_dep4"],
         "//conditions:default": [],
@@ -591,36 +521,7 @@
         "//build/bazel/platforms/os:android": [":static_dep3"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     whole_archive_deps = [":static_dep2"],
-)`, `cc_library_static(
-    name = "static_dep",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
-)`, `cc_library_static(
-    name = "static_dep2",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
-)`, `cc_library_static(
-    name = "static_dep3",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
-)`, `cc_library_static(
-    name = "static_dep4",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
 )`},
 	})
 }
@@ -641,14 +542,10 @@
     name: "foo_static",
     srcs: ["common.c", "foo-*.c"],
     exclude_srcs: ["foo-excluded.c"],
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs_c = [
         "common.c",
         "foo-a.c",
@@ -671,15 +568,11 @@
 cc_library_static {
     name: "foo_static",
     srcs: ["common.c"],
-    arch: { arm: { srcs: ["foo-arm.c"] } }
+    arch: { arm: { srcs: ["foo-arm.c"] } },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["foo-arm.c"],
         "//conditions:default": [],
@@ -708,14 +601,10 @@
     arch: {
         arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] },
     },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["for-arm.c"],
         "//conditions:default": ["not-for-arm.c"],
@@ -746,14 +635,10 @@
         arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] },
         x86: { srcs: ["for-x86.c"], exclude_srcs: ["not-for-x86.c"] },
     },
+    include_build_directory: false,
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
@@ -799,15 +684,11 @@
         arm64: { srcs: ["for-arm64.c"], exclude_srcs: ["not-for-arm64.c"] },
         x86: { srcs: ["for-x86.c"], exclude_srcs: ["not-for-x86.c"] },
         x86_64: { srcs: ["for-x86_64.c"], exclude_srcs: ["not-for-x86_64.c"] },
-	},
+  },
+    include_build_directory: false,
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
@@ -863,14 +744,10 @@
     arch: {
         arm: { exclude_srcs: ["foo-no-arm.cc"] },
     },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs = ["common.cc"] + select({
         "//build/bazel/platforms/arch:arm": [],
         "//conditions:default": ["foo-no-arm.cc"],
@@ -900,14 +777,10 @@
         arm: { exclude_srcs: ["foo-no-arm.cc"] },
         x86: { srcs: ["x86-only.cc"] },
     },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs = ["common.cc"] + select({
         "//build/bazel/platforms/arch:arm": [],
         "//build/bazel/platforms/arch:x86": [
@@ -928,26 +801,18 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
 		filesystem:                         map[string]string{},
 		blueprint: soongCcLibraryStaticPreamble + `
-cc_library_static { name: "static_dep" }
+cc_library_static {
+    name: "static_dep",
+    bazel_module: { bp2build_available: false },
+}
 cc_library_static {
     name: "foo_static",
     static_libs: ["static_dep", "static_dep"],
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
     implementation_deps = [":static_dep"],
-    linkstatic = True,
-)`, `cc_library_static(
-    name = "static_dep",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
 )`},
 	})
 }
@@ -970,14 +835,10 @@
     multilib: {
         lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] },
     },
+    include_build_directory: false,
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["for-lib32.c"],
         "//build/bazel/platforms/arch:x86": ["for-lib32.c"],
@@ -1008,14 +869,10 @@
         lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] },
         lib64: { srcs: ["for-lib64.c"], exclude_srcs: ["not-for-lib64.c"] },
     },
+    include_build_directory: false,
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static2",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-lib32.c",
@@ -1079,14 +936,10 @@
        lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] },
        lib64: { srcs: ["for-lib64.c"], exclude_srcs: ["not-for-lib64.c"] },
    },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static3",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
@@ -1146,21 +999,21 @@
 			"not-for-everything.cpp": "",
 			"dep/Android.bp": `
 genrule {
-	name: "generated_src_other_pkg",
-	out: ["generated_src_other_pkg.cpp"],
-	cmd: "nothing to see here",
+  name: "generated_src_other_pkg",
+  out: ["generated_src_other_pkg.cpp"],
+  cmd: "nothing to see here",
 }
 
 genrule {
-	name: "generated_hdr_other_pkg",
-	out: ["generated_hdr_other_pkg.cpp"],
-	cmd: "nothing to see here",
+  name: "generated_hdr_other_pkg",
+  out: ["generated_hdr_other_pkg.cpp"],
+  cmd: "nothing to see here",
 }
 
 genrule {
-	name: "generated_hdr_other_pkg_x86",
-	out: ["generated_hdr_other_pkg_x86.cpp"],
-	cmd: "nothing to see here",
+  name: "generated_hdr_other_pkg_x86",
+  out: ["generated_hdr_other_pkg_x86.cpp"],
+  cmd: "nothing to see here",
 }`,
 		},
 		blueprint: soongCcLibraryStaticPreamble + `
@@ -1196,15 +1049,11 @@
            generated_headers: ["generated_hdr_other_pkg_x86"],
        },
    },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static3",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs = [
         "//dep:generated_hdr_other_pkg",
         "//dep:generated_src_other_pkg",
@@ -1256,14 +1105,10 @@
             srcs: ["linux_bionic_x86_64_src.c"],
         },
     },
+    include_build_directory: false,
 }`,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs_c = select({
         "//build/bazel/platforms/os:android": ["android_src.c"],
         "//conditions:default": [],
@@ -1301,13 +1146,11 @@
         cflags: ["-Wbinder32bit"],
       },
     },
+    include_build_directory: false,
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ] + select({
+    copts = select({
         "//build/bazel/product_variables:binder32bit": ["-Wbinder32bit"],
         "//conditions:default": [],
     }) + select({
@@ -1317,7 +1160,6 @@
         "//build/bazel/product_variables:malloc_zero_contents": ["-Wmalloc_zero_contents"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     srcs_c = ["common.c"],
 )`},
 	})
@@ -1339,40 +1181,38 @@
         cflags: ["-Wmalloc_not_svelte"],
       },
     },
-		arch: {
-				arm64: {
-						product_variables: {
-								malloc_not_svelte: {
-										cflags: ["-Warm64_malloc_not_svelte"],
-								},
-						},
-				},
-		},
-		multilib: {
-				lib32: {
-						product_variables: {
-								malloc_not_svelte: {
-										cflags: ["-Wlib32_malloc_not_svelte"],
-								},
-						},
-				},
-		},
-		target: {
-				android: {
-						product_variables: {
-								malloc_not_svelte: {
-										cflags: ["-Wandroid_malloc_not_svelte"],
-								},
-						},
-				}
-		},
+    arch: {
+        arm64: {
+            product_variables: {
+                malloc_not_svelte: {
+                    cflags: ["-Warm64_malloc_not_svelte"],
+                },
+            },
+        },
+    },
+    multilib: {
+        lib32: {
+            product_variables: {
+                malloc_not_svelte: {
+                    cflags: ["-Wlib32_malloc_not_svelte"],
+                },
+            },
+        },
+    },
+    target: {
+        android: {
+            product_variables: {
+                malloc_not_svelte: {
+                    cflags: ["-Wandroid_malloc_not_svelte"],
+                },
+            },
+        }
+    },
+    include_build_directory: false,
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ] + select({
+    copts = select({
         "//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"],
         "//conditions:default": [],
     }) + select({
@@ -1388,7 +1228,6 @@
         "//build/bazel/product_variables:malloc_not_svelte-x86": ["-Wlib32_malloc_not_svelte"],
         "//conditions:default": [],
     }),
-    linkstatic = True,
     srcs_c = ["common.c"],
 )`},
 	})
@@ -1410,21 +1249,14 @@
           asflags: ["-DPLATFORM_SDK_VERSION=%d"],
       },
     },
+    include_build_directory: false,
 } `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    asflags = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ] + select({
+    asflags = select({
         "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
         "//conditions:default": [],
     }),
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     srcs_as = ["common.S"],
 )`},
 	})
@@ -1439,16 +1271,12 @@
 		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "root_empty",
-	  system_shared_libs: [],
+    system_shared_libs: [],
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "root_empty",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     system_dynamic_deps = [],
 )`},
 	})
@@ -1464,21 +1292,17 @@
 cc_defaults {
     name: "static_empty_defaults",
     static: {
-				system_shared_libs: [],
-		},
+        system_shared_libs: [],
+    },
+    include_build_directory: false,
 }
 cc_library_static {
     name: "static_empty",
-	  defaults: ["static_empty_defaults"],
+    defaults: ["static_empty_defaults"],
 }
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "static_empty",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     system_dynamic_deps = [],
 )`},
 	})
@@ -1498,15 +1322,11 @@
             system_shared_libs: [],
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "target_bionic_empty",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     system_dynamic_deps = [],
 )`},
 	})
@@ -1530,15 +1350,11 @@
             system_shared_libs: [],
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "target_linux_bionic_empty",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     system_dynamic_deps = [],
 )`},
 	})
@@ -1560,15 +1376,11 @@
             system_shared_libs: ["libc"],
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "target_bionic",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     system_dynamic_deps = select({
         "//build/bazel/platforms/os:bionic": [":libc"],
         "//conditions:default": [],
@@ -1589,21 +1401,17 @@
 
 cc_library_static {
     name: "target_linux_bionic",
-		system_shared_libs: ["libc"],
+    system_shared_libs: ["libc"],
     target: {
         linux_bionic: {
             system_shared_libs: ["libm"],
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "target_linux_bionic",
-    copts = [
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
-    linkstatic = True,
     system_dynamic_deps = [":libc"] + select({
         "//build/bazel/platforms/os:linux_bionic": [":libm"],
         "//conditions:default": [],
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 9ac28a5..b0a88ae 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -65,10 +65,10 @@
         "-Wno-gcc-compat",
         "-Wall",
         "-Werror",
-        "-Iinclude",
-        "-I$(BINDIR)/include",
-        "-I.",
-        "-I$(BINDIR)/.",
+    ],
+    local_includes = [
+        "include",
+        ".",
     ],
     srcs = ["a/b/c.c"],
 )`,
@@ -78,14 +78,12 @@
 
 func TestCcObjectDefaults(t *testing.T) {
 	runCcObjectTestCase(t, bp2buildTestCase{
-		description:                        "simple cc_object with defaults",
 		moduleTypeUnderTest:                "cc_object",
 		moduleTypeUnderTestFactory:         cc.ObjectFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
 		blueprint: `cc_object {
     name: "foo",
     system_shared_libs: [],
-    local_include_dirs: ["include"],
     srcs: [
         "a/b/*.h",
         "a/b/c.c"
@@ -115,11 +113,8 @@
         "-Wall",
         "-Werror",
         "-fno-addrsig",
-        "-Iinclude",
-        "-I$(BINDIR)/include",
-        "-I.",
-        "-I$(BINDIR)/.",
     ],
+    local_includes = ["."],
     srcs = ["a/b/c.c"],
 )`,
 		}})
@@ -140,29 +135,23 @@
     system_shared_libs: [],
     srcs: ["a/b/c.c"],
     objs: ["bar"],
+    include_build_directory: false,
 }
 
 cc_object {
     name: "bar",
     system_shared_libs: [],
     srcs: ["x/y/z.c"],
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{`cc_object(
     name = "bar",
-    copts = [
-        "-fno-addrsig",
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
+    copts = ["-fno-addrsig"],
     srcs = ["x/y/z.c"],
 )`, `cc_object(
     name = "foo",
-    copts = [
-        "-fno-addrsig",
-        "-I.",
-        "-I$(BINDIR)/.",
-    ],
+    copts = ["-fno-addrsig"],
     deps = [":bar"],
     srcs = ["a/b/c.c"],
 )`,
@@ -245,16 +234,13 @@
             srcs: ["arch/arm/file.cpp"], // label list
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{
 			`cc_object(
     name = "foo",
-    copts = [
-        "-fno-addrsig",
-        "-I.",
-        "-I$(BINDIR)/.",
-    ] + select({
+    copts = ["-fno-addrsig"] + select({
         "//build/bazel/platforms/arch:x86": ["-fPIC"],
         "//conditions:default": [],
     }),
@@ -295,16 +281,13 @@
             cflags: ["-Wall"],
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{
 			`cc_object(
     name = "foo",
-    copts = [
-        "-fno-addrsig",
-        "-I.",
-        "-I$(BINDIR)/.",
-    ] + select({
+    copts = ["-fno-addrsig"] + select({
         "//build/bazel/platforms/arch:arm": ["-Wall"],
         "//build/bazel/platforms/arch:arm64": ["-Wall"],
         "//build/bazel/platforms/arch:x86": ["-fPIC"],
@@ -344,16 +327,13 @@
             cflags: ["-Wall"],
         },
     },
+    include_build_directory: false,
 }
 `,
 		expectedBazelTargets: []string{
 			`cc_object(
     name = "foo",
-    copts = [
-        "-fno-addrsig",
-        "-I.",
-        "-I$(BINDIR)/.",
-    ] + select({
+    copts = ["-fno-addrsig"] + select({
         "//build/bazel/platforms/os:android": ["-fPIC"],
         "//build/bazel/platforms/os:darwin": ["-Wall"],
         "//build/bazel/platforms/os:windows": ["-fPIC"],
diff --git a/bp2build/compatibility.go b/bp2build/compatibility.go
deleted file mode 100644
index 5baa524..0000000
--- a/bp2build/compatibility.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package bp2build
-
-import (
-	"android/soong/bazel"
-	"fmt"
-)
-
-// Data from the code generation process that is used to improve compatibility
-// between build systems.
-type CodegenCompatLayer struct {
-	// A map from the original module name to the generated/handcrafted Bazel
-	// label for legacy build systems to be able to build a fully-qualified
-	// Bazel target from an unique module name.
-	NameToLabelMap map[string]string
-}
-
-// Log an entry of module name -> Bazel target label.
-func (compatLayer CodegenCompatLayer) AddNameToLabelEntry(name, label string) {
-	// The module name may be prefixed with bazel.BazelTargetModuleNamePrefix if
-	// generated from bp2build.
-	name = bazel.StripNamePrefix(name)
-	if existingLabel, ok := compatLayer.NameToLabelMap[name]; ok {
-		panic(fmt.Errorf(
-			"Module '%s' maps to more than one Bazel target label: %s, %s. "+
-				"This shouldn't happen. It probably indicates a bug with the bp2build internals.",
-			name,
-			existingLabel,
-			label))
-	}
-	compatLayer.NameToLabelMap[name] = label
-}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 75bc2b4..354abf6 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -16,29 +16,19 @@
 	Contents string
 }
 
-func CreateSoongInjectionFiles(compatLayer CodegenCompatLayer) []BazelFile {
+func CreateSoongInjectionFiles(metrics CodegenMetrics) []BazelFile {
 	var files []BazelFile
 
 	files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
 	files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars()))
 
-	files = append(files, newFile("module_name_to_label", GeneratedBuildFileName, nameToLabelAliases(compatLayer.NameToLabelMap)))
+	files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))
 
 	return files
 }
 
-func nameToLabelAliases(nameToLabelMap map[string]string) string {
-	ret := make([]string, len(nameToLabelMap))
-
-	for k, v := range nameToLabelMap {
-		// v is the fully qualified label rooted at '//'
-		ret = append(ret, fmt.Sprintf(
-			`alias(
-    name = "%s",
-    actual = "@%s",
-)`, k, v))
-	}
-	return strings.Join(ret, "\n\n")
+func convertedModules(convertedModules []string) string {
+	return strings.Join(convertedModules, "\n")
 }
 
 func CreateBazelFiles(
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 56ea589..dfa1a9e 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -80,7 +80,7 @@
 }
 
 func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
-	files := CreateSoongInjectionFiles(CodegenCompatLayer{})
+	files := CreateSoongInjectionFiles(CodegenMetrics{})
 
 	expectedFilePaths := []bazelFilepath{
 		{
@@ -92,8 +92,8 @@
 			basename: "constants.bzl",
 		},
 		{
-			dir:      "module_name_to_label",
-			basename: GeneratedBuildFileName,
+			dir:      "metrics",
+			basename: "converted_modules.txt",
 		},
 	}
 
@@ -107,9 +107,5 @@
 		if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename {
 			t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename)
 		}
-
-		if expectedFile.basename != GeneratedBuildFileName && actualFile.Contents == "" {
-			t.Errorf("Contents of %s unexpected empty.", actualFile)
-		}
 	}
 }
diff --git a/bp2build/filegroup_conversion_test.go b/bp2build/filegroup_conversion_test.go
new file mode 100644
index 0000000..ad99236
--- /dev/null
+++ b/bp2build/filegroup_conversion_test.go
@@ -0,0 +1,62 @@
+// Copyright 2021 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 bp2build
+
+import (
+	"android/soong/android"
+	"fmt"
+
+	"testing"
+)
+
+func runFilegroupTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerFilegroupModuleTypes, tc)
+}
+
+func registerFilegroupModuleTypes(ctx android.RegistrationContext) {}
+
+func TestFilegroupSameNameAsFile_OneFile(t *testing.T) {
+	runFilegroupTestCase(t, bp2buildTestCase{
+		description:                        "filegroup - same name as file, with one file",
+		moduleTypeUnderTest:                "filegroup",
+		moduleTypeUnderTestFactory:         android.FileGroupFactory,
+		moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: `
+filegroup {
+    name: "foo",
+    srcs: ["foo"],
+}
+`,
+		expectedBazelTargets: []string{}})
+}
+
+func TestFilegroupSameNameAsFile_MultipleFiles(t *testing.T) {
+	runFilegroupTestCase(t, bp2buildTestCase{
+		description:                        "filegroup - same name as file, with multiple files",
+		moduleTypeUnderTest:                "filegroup",
+		moduleTypeUnderTestFactory:         android.FileGroupFactory,
+		moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: `
+filegroup {
+	name: "foo",
+	srcs: ["foo", "bar"],
+}
+`,
+		expectedErr: fmt.Errorf("filegroup 'foo' cannot contain a file with the same name"),
+	})
+}
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
new file mode 100644
index 0000000..f3bc1ba
--- /dev/null
+++ b/bp2build/genrule_conversion_test.go
@@ -0,0 +1,481 @@
+// Copyright 2021 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 bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/genrule"
+	"strings"
+	"testing"
+)
+
+func TestGenruleBp2Build(t *testing.T) {
+	otherGenruleBp := map[string]string{
+		"other/Android.bp": `genrule {
+    name: "foo.tool",
+    out: ["foo_tool.out"],
+    srcs: ["foo_tool.in"],
+    cmd: "cp $(in) $(out)",
+}
+genrule {
+    name: "other.tool",
+    out: ["other_tool.out"],
+    srcs: ["other_tool.in"],
+    cmd: "cp $(in) $(out)",
+}`,
+	}
+
+	testCases := []bp2buildTestCase{
+		{
+			description:                        "genrule with command line variable replacements",
+			moduleTypeUnderTest:                "genrule",
+			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
+			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			blueprint: `genrule {
+    name: "foo.tool",
+    out: ["foo_tool.out"],
+    srcs: ["foo_tool.in"],
+    cmd: "cp $(in) $(out)",
+    bazel_module: { bp2build_available: true },
+}
+
+genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: ["foo.in"],
+    tools: [":foo.tool"],
+    cmd: "$(location :foo.tool) --genDir=$(genDir) arg $(in) $(out)",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				`genrule(
+    name = "foo",
+    cmd = "$(location :foo.tool) --genDir=$(GENDIR) arg $(SRCS) $(OUTS)",
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
+    tools = [":foo.tool"],
+)`,
+				`genrule(
+    name = "foo.tool",
+    cmd = "cp $(SRCS) $(OUTS)",
+    outs = ["foo_tool.out"],
+    srcs = ["foo_tool.in"],
+)`,
+			},
+		},
+		{
+			description:                        "genrule using $(locations :label)",
+			moduleTypeUnderTest:                "genrule",
+			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
+			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			blueprint: `genrule {
+    name: "foo.tools",
+    out: ["foo_tool.out", "foo_tool2.out"],
+    srcs: ["foo_tool.in"],
+    cmd: "cp $(in) $(out)",
+    bazel_module: { bp2build_available: true },
+}
+
+genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: ["foo.in"],
+    tools: [":foo.tools"],
+    cmd: "$(locations :foo.tools) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`genrule(
+    name = "foo",
+    cmd = "$(locations :foo.tools) -s $(OUTS) $(SRCS)",
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
+    tools = [":foo.tools"],
+)`,
+				`genrule(
+    name = "foo.tools",
+    cmd = "cp $(SRCS) $(OUTS)",
+    outs = [
+        "foo_tool.out",
+        "foo_tool2.out",
+    ],
+    srcs = ["foo_tool.in"],
+)`,
+			},
+		},
+		{
+			description:                        "genrule using $(locations //absolute:label)",
+			moduleTypeUnderTest:                "genrule",
+			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
+			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			blueprint: `genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: ["foo.in"],
+    tool_files: [":foo.tool"],
+    cmd: "$(locations :foo.tool) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`genrule(
+    name = "foo",
+    cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
+    tools = ["//other:foo.tool"],
+)`,
+			},
+			filesystem: otherGenruleBp,
+		},
+		{
+			description:                        "genrule srcs using $(locations //absolute:label)",
+			moduleTypeUnderTest:                "genrule",
+			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
+			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			blueprint: `genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: [":other.tool"],
+    tool_files: [":foo.tool"],
+    cmd: "$(locations :foo.tool) -s $(out) $(location :other.tool)",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`genrule(
+    name = "foo",
+    cmd = "$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)",
+    outs = ["foo.out"],
+    srcs = ["//other:other.tool"],
+    tools = ["//other:foo.tool"],
+)`,
+			},
+			filesystem: otherGenruleBp,
+		},
+		{
+			description:                        "genrule using $(location) label should substitute first tool label automatically",
+			moduleTypeUnderTest:                "genrule",
+			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
+			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			blueprint: `genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: ["foo.in"],
+    tool_files: [":foo.tool", ":other.tool"],
+    cmd: "$(location) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`genrule(
+    name = "foo",
+    cmd = "$(location //other:foo.tool) -s $(OUTS) $(SRCS)",
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
+    tools = [
+        "//other:foo.tool",
+        "//other:other.tool",
+    ],
+)`,
+			},
+			filesystem: otherGenruleBp,
+		},
+		{
+			description:                        "genrule using $(locations) label should substitute first tool label automatically",
+			moduleTypeUnderTest:                "genrule",
+			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
+			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			blueprint: `genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: ["foo.in"],
+    tools: [":foo.tool", ":other.tool"],
+    cmd: "$(locations) -s $(out) $(in)",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`genrule(
+    name = "foo",
+    cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
+    tools = [
+        "//other:foo.tool",
+        "//other:other.tool",
+    ],
+)`,
+			},
+			filesystem: otherGenruleBp,
+		},
+		{
+			description:                        "genrule without tools or tool_files can convert successfully",
+			moduleTypeUnderTest:                "genrule",
+			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
+			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
+			blueprint: `genrule {
+    name: "foo",
+    out: ["foo.out"],
+    srcs: ["foo.in"],
+    cmd: "cp $(in) $(out)",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`genrule(
+    name = "foo",
+    cmd = "cp $(SRCS) $(OUTS)",
+    outs = ["foo.out"],
+    srcs = ["foo.in"],
+)`,
+			},
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		fs := make(map[string][]byte)
+		toParse := []string{
+			"Android.bp",
+		}
+		for f, content := range testCase.filesystem {
+			if strings.HasSuffix(f, "Android.bp") {
+				toParse = append(toParse, f)
+			}
+			fs[f] = []byte(content)
+		}
+		config := android.TestConfig(buildDir, nil, testCase.blueprint, fs)
+		ctx := android.NewTestContext(config)
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, toParse)
+		if errored(t, testCase, errs) {
+			continue
+		}
+		_, errs = ctx.ResolveDependencies(config)
+		if errored(t, testCase, errs) {
+			continue
+		}
+
+		checkDir := dir
+		if testCase.dir != "" {
+			checkDir = testCase.dir
+		}
+
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, checkDir)
+		android.FailIfErrored(t, err)
+		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+		} else {
+			for i, target := range bazelTargets {
+				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+					t.Errorf(
+						"%s: Expected generated Bazel target to be '%s', got '%s'",
+						testCase.description,
+						w,
+						g,
+					)
+				}
+			}
+		}
+	}
+}
+
+func TestBp2BuildInlinesDefaults(t *testing.T) {
+	testCases := []struct {
+		moduleTypesUnderTest      map[string]android.ModuleFactory
+		bp2buildMutatorsUnderTest map[string]bp2buildMutator
+		bp                        string
+		expectedBazelTarget       string
+		description               string
+	}{
+		{
+			moduleTypesUnderTest: map[string]android.ModuleFactory{
+				"genrule":          genrule.GenRuleFactory,
+				"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
+			},
+			bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
+				"genrule": genrule.GenruleBp2Build,
+			},
+			bp: `genrule_defaults {
+    name: "gen_defaults",
+    cmd: "do-something $(in) $(out)",
+}
+genrule {
+    name: "gen",
+    out: ["out"],
+    srcs: ["in1"],
+    defaults: ["gen_defaults"],
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTarget: `genrule(
+    name = "gen",
+    cmd = "do-something $(SRCS) $(OUTS)",
+    outs = ["out"],
+    srcs = ["in1"],
+)`,
+			description: "genrule applies properties from a genrule_defaults dependency if not specified",
+		},
+		{
+			moduleTypesUnderTest: map[string]android.ModuleFactory{
+				"genrule":          genrule.GenRuleFactory,
+				"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
+			},
+			bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
+				"genrule": genrule.GenruleBp2Build,
+			},
+			bp: `genrule_defaults {
+    name: "gen_defaults",
+    out: ["out-from-defaults"],
+    srcs: ["in-from-defaults"],
+    cmd: "cmd-from-defaults",
+}
+genrule {
+    name: "gen",
+    out: ["out"],
+    srcs: ["in1"],
+    defaults: ["gen_defaults"],
+    cmd: "do-something $(in) $(out)",
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTarget: `genrule(
+    name = "gen",
+    cmd = "do-something $(SRCS) $(OUTS)",
+    outs = [
+        "out-from-defaults",
+        "out",
+    ],
+    srcs = [
+        "in-from-defaults",
+        "in1",
+    ],
+)`,
+			description: "genrule does merges properties from a genrule_defaults dependency, latest-first",
+		},
+		{
+			moduleTypesUnderTest: map[string]android.ModuleFactory{
+				"genrule":          genrule.GenRuleFactory,
+				"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
+			},
+			bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
+				"genrule": genrule.GenruleBp2Build,
+			},
+			bp: `genrule_defaults {
+    name: "gen_defaults1",
+    cmd: "cp $(in) $(out)",
+}
+
+genrule_defaults {
+    name: "gen_defaults2",
+    srcs: ["in1"],
+}
+
+genrule {
+    name: "gen",
+    out: ["out"],
+    defaults: ["gen_defaults1", "gen_defaults2"],
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTarget: `genrule(
+    name = "gen",
+    cmd = "cp $(SRCS) $(OUTS)",
+    outs = ["out"],
+    srcs = ["in1"],
+)`,
+			description: "genrule applies properties from list of genrule_defaults",
+		},
+		{
+			moduleTypesUnderTest: map[string]android.ModuleFactory{
+				"genrule":          genrule.GenRuleFactory,
+				"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
+			},
+			bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
+				"genrule": genrule.GenruleBp2Build,
+			},
+			bp: `genrule_defaults {
+    name: "gen_defaults1",
+    defaults: ["gen_defaults2"],
+    cmd: "cmd1 $(in) $(out)", // overrides gen_defaults2's cmd property value.
+}
+
+genrule_defaults {
+    name: "gen_defaults2",
+    defaults: ["gen_defaults3"],
+    cmd: "cmd2 $(in) $(out)",
+    out: ["out-from-2"],
+    srcs: ["in1"],
+}
+
+genrule_defaults {
+    name: "gen_defaults3",
+    out: ["out-from-3"],
+    srcs: ["srcs-from-3"],
+}
+
+genrule {
+    name: "gen",
+    out: ["out"],
+    defaults: ["gen_defaults1"],
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTarget: `genrule(
+    name = "gen",
+    cmd = "cmd1 $(SRCS) $(OUTS)",
+    outs = [
+        "out-from-3",
+        "out-from-2",
+        "out",
+    ],
+    srcs = [
+        "srcs-from-3",
+        "in1",
+    ],
+)`,
+			description: "genrule applies properties from genrule_defaults transitively",
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+		ctx := android.NewTestContext(config)
+		for m, factory := range testCase.moduleTypesUnderTest {
+			ctx.RegisterModuleType(m, factory)
+		}
+		for mutator, f := range testCase.bp2buildMutatorsUnderTest {
+			ctx.RegisterBp2BuildMutator(mutator, f)
+		}
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+		android.FailIfErrored(t, errs)
+		_, errs = ctx.ResolveDependencies(config)
+		android.FailIfErrored(t, errs)
+
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
+		android.FailIfErrored(t, err)
+		if actualCount := len(bazelTargets); actualCount != 1 {
+			t.Fatalf("%s: Expected 1 bazel target, got %d", testCase.description, actualCount)
+		}
+
+		actualBazelTarget := bazelTargets[0]
+		if actualBazelTarget.content != testCase.expectedBazelTarget {
+			t.Errorf(
+				"%s: Expected generated Bazel target to be '%s', got '%s'",
+				testCase.description,
+				testCase.expectedBazelTarget,
+				actualBazelTarget.content,
+			)
+		}
+	}
+}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 65b06c6..1cc4143 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -3,32 +3,75 @@
 import (
 	"android/soong/android"
 	"fmt"
+	"strings"
 )
 
 // Simple metrics struct to collect information about a Blueprint to BUILD
 // conversion process.
 type CodegenMetrics struct {
-	// Total number of Soong/Blueprint modules
-	TotalModuleCount int
+	// Total number of Soong modules converted to generated targets
+	generatedModuleCount int
+
+	// Total number of Soong modules converted to handcrafted targets
+	handCraftedModuleCount int
+
+	// Total number of unconverted Soong modules
+	unconvertedModuleCount int
 
 	// Counts of generated Bazel targets per Bazel rule class
-	RuleClassCount map[string]int
+	ruleClassCount map[string]int
 
-	// Total number of handcrafted targets
-	handCraftedTargetCount int
+	moduleWithUnconvertedDepsMsgs []string
+
+	convertedModules []string
 }
 
 // Print the codegen metrics to stdout.
-func (metrics CodegenMetrics) Print() {
+func (metrics *CodegenMetrics) Print() {
 	generatedTargetCount := 0
-	for _, ruleClass := range android.SortedStringKeys(metrics.RuleClassCount) {
-		count := metrics.RuleClassCount[ruleClass]
+	for _, ruleClass := range android.SortedStringKeys(metrics.ruleClassCount) {
+		count := metrics.ruleClassCount[ruleClass]
 		fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count)
 		generatedTargetCount += count
 	}
 	fmt.Printf(
-		"[bp2build] Generated %d total BUILD targets and included %d handcrafted BUILD targets from %d Android.bp modules.\n",
+		"[bp2build] Generated %d total BUILD targets and included %d handcrafted BUILD targets from %d Android.bp modules.\n With %d modules with unconverted deps \n\t%s",
 		generatedTargetCount,
-		metrics.handCraftedTargetCount,
-		metrics.TotalModuleCount)
+		metrics.handCraftedModuleCount,
+		metrics.TotalModuleCount(),
+		len(metrics.moduleWithUnconvertedDepsMsgs),
+		strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"))
+}
+
+func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) {
+	metrics.ruleClassCount[ruleClass] += 1
+}
+
+func (metrics *CodegenMetrics) IncrementUnconvertedCount() {
+	metrics.unconvertedModuleCount += 1
+}
+
+func (metrics *CodegenMetrics) TotalModuleCount() int {
+	return metrics.handCraftedModuleCount +
+		metrics.generatedModuleCount +
+		metrics.unconvertedModuleCount
+}
+
+type ConversionType int
+
+const (
+	Generated ConversionType = iota
+	Handcrafted
+)
+
+func (metrics *CodegenMetrics) AddConvertedModule(moduleName string, conversionType ConversionType) {
+	// Undo prebuilt_ module name prefix modifications
+	moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName)
+	metrics.convertedModules = append(metrics.convertedModules, moduleName)
+
+	if conversionType == Handcrafted {
+		metrics.handCraftedModuleCount += 1
+	} else if conversionType == Generated {
+		metrics.generatedModuleCount += 1
+	}
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index a549a93..74084b1 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -36,14 +36,34 @@
 	buildDir string
 )
 
-func errored(t *testing.T, desc string, errs []error) bool {
+func checkError(t *testing.T, errs []error, expectedErr error) bool {
 	t.Helper()
+
+	if len(errs) != 1 {
+		return false
+	}
+	if errs[0].Error() == expectedErr.Error() {
+		return true
+	}
+
+	return false
+}
+
+func errored(t *testing.T, tc bp2buildTestCase, errs []error) bool {
+	t.Helper()
+	if tc.expectedErr != nil {
+		// Rely on checkErrors, as this test case is expected to have an error.
+		return false
+	}
+
 	if len(errs) > 0 {
 		for _, err := range errs {
-			t.Errorf("%s: %s", desc, err)
+			t.Errorf("%s: %s", tc.description, err)
 		}
 		return true
 	}
+
+	// All good, continue execution.
 	return false
 }
 
@@ -61,6 +81,8 @@
 	expectedBazelTargets               []string
 	filesystem                         map[string]string
 	dir                                string
+	expectedErr                        error
+	unconvertedDepsMode                unconvertedDepsMode
 }
 
 func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
@@ -85,12 +107,17 @@
 	ctx.RegisterBp2BuildMutator(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestBp2BuildMutator)
 	ctx.RegisterForBazelConversion()
 
-	_, errs := ctx.ParseFileList(dir, toParse)
-	if errored(t, tc.description, errs) {
+	_, parseErrs := ctx.ParseFileList(dir, toParse)
+	if errored(t, tc, parseErrs) {
 		return
 	}
-	_, errs = ctx.ResolveDependencies(config)
-	if errored(t, tc.description, errs) {
+	_, resolveDepsErrs := ctx.ResolveDependencies(config)
+	if errored(t, tc, resolveDepsErrs) {
+		return
+	}
+
+	errs := append(parseErrs, resolveDepsErrs...)
+	if tc.expectedErr != nil && checkError(t, errs, tc.expectedErr) {
 		return
 	}
 
@@ -99,7 +126,13 @@
 		checkDir = tc.dir
 	}
 	codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-	bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+	codegenCtx.unconvertedDepMode = tc.unconvertedDepsMode
+	bazelTargets, errs := generateBazelTargetsForDir(codegenCtx, checkDir)
+	if tc.expectedErr != nil && checkError(t, errs, tc.expectedErr) {
+		return
+	} else {
+		android.FailIfErrored(t, errs)
+	}
 	if actualCount, expectedCount := len(bazelTargets), len(tc.expectedBazelTargets); actualCount != expectedCount {
 		t.Errorf("%s: Expected %d bazel target, got %d; %v",
 			tc.description, expectedCount, actualCount, bazelTargets)
@@ -121,7 +154,18 @@
 	Nested_prop string
 }
 
+type EmbeddedProps struct {
+	Embedded_prop string
+}
+
+type OtherEmbeddedProps struct {
+	Other_embedded_prop string
+}
+
 type customProps struct {
+	EmbeddedProps
+	*OtherEmbeddedProps
+
 	Bool_prop     bool
 	Bool_ptr_prop *bool
 	// Ensure that properties tagged `blueprint:mutated` are omitted
@@ -219,17 +263,22 @@
 	return m
 }
 
+type EmbeddedAttr struct {
+	Embedded_attr string
+}
+
+type OtherEmbeddedAttr struct {
+	Other_embedded_attr string
+}
+
 type customBazelModuleAttributes struct {
+	EmbeddedAttr
+	*OtherEmbeddedAttr
 	String_prop      string
 	String_list_prop []string
 	Arch_paths       bazel.LabelListAttribute
 }
 
-type customBazelModule struct {
-	android.BazelTargetModuleBase
-	customBazelModuleAttributes
-}
-
 func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
 	if m, ok := ctx.Module().(*customModule); ok {
 		if !m.ConvertWithBp2build(ctx) {
@@ -253,6 +302,10 @@
 			String_list_prop: m.props.String_list_prop,
 			Arch_paths:       paths,
 		}
+		attrs.Embedded_attr = m.props.Embedded_prop
+		if m.props.OtherEmbeddedProps != nil {
+			attrs.OtherEmbeddedAttr = &OtherEmbeddedAttr{Other_embedded_attr: m.props.OtherEmbeddedProps.Other_embedded_prop}
+		}
 
 		props := bazel.BazelTargetModuleProperties{
 			Rule_class: "custom",
@@ -294,10 +347,10 @@
 }
 
 // Helper method for tests to easily access the targets in a dir.
-func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) BazelTargets {
+func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) (BazelTargets, []error) {
 	// TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
-	buildFileToTargets, _, _ := GenerateBazelTargets(codegenCtx, false)
-	return buildFileToTargets[dir]
+	res, err := GenerateBazelTargets(codegenCtx, false)
+	return res.buildFileToTargets[dir], err
 }
 
 func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
diff --git a/bpfix/cmd_lib/bpfix.go b/bpfix/cmd_lib/bpfix.go
index f90f65b..1106d4a 100644
--- a/bpfix/cmd_lib/bpfix.go
+++ b/bpfix/cmd_lib/bpfix.go
@@ -114,7 +114,7 @@
 
 func makeFileVisitor(fixRequest bpfix.FixRequest) func(string, os.FileInfo, error) error {
 	return func(path string, f os.FileInfo, err error) error {
-		if err == nil && (f.Name() == "Blueprints" || f.Name() == "Android.bp") {
+		if err == nil && f.Name() == "Android.bp" {
 			err = openAndProcess(path, os.Stdout, fixRequest)
 		}
 		if err != nil {
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index ebf89ea..71e0cd8 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -38,16 +38,16 @@
 	android.SdkMemberTypeBase
 }
 
-func (mt *binarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	targets := mctx.MultiTargets()
+func (mt *binarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	targets := ctx.MultiTargets()
 	for _, bin := range names {
 		for _, target := range targets {
 			variations := target.Variations()
-			if mctx.Device() {
+			if ctx.Device() {
 				variations = append(variations,
 					blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
 			}
-			mctx.AddFarVariationDependencies(variations, dependencyTag, bin)
+			ctx.AddFarVariationDependencies(variations, dependencyTag, bin)
 		}
 	}
 }
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 537f01c..e48f757 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -20,6 +20,7 @@
 
 	"android/soong/android"
 	"android/soong/bazel"
+	"github.com/google/blueprint"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -111,24 +112,23 @@
 	return
 }
 
-// bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
-func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
+// bp2BuildParseLibProps returns the attributes for a variant of a cc_library.
+func bp2BuildParseLibProps(ctx android.TopDownMutatorContext, module *Module, isStatic bool) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
 	if !ok {
 		return staticOrSharedAttributes{}
 	}
+	return bp2buildParseStaticOrSharedProps(ctx, module, lib, isStatic)
+}
 
-	return bp2buildParseStaticOrSharedProps(ctx, module, lib, false)
+// bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
+func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
+	return bp2BuildParseLibProps(ctx, module, false)
 }
 
 // bp2buildParseStaticProps returns the attributes for the static variant of a cc_library.
 func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
-	lib, ok := module.compiler.(*libraryDecorator)
-	if !ok {
-		return staticOrSharedAttributes{}
-	}
-
-	return bp2buildParseStaticOrSharedProps(ctx, module, lib, true)
+	return bp2BuildParseLibProps(ctx, module, true)
 }
 
 func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
@@ -137,10 +137,10 @@
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
 		attrs.Copts.SetSelectValue(axis, config, props.Cflags)
 		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
-		attrs.Static_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
-		attrs.Dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
-		attrs.Whole_archive_deps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs))
-		attrs.System_dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.System_shared_libs))
+		attrs.Static_deps.SetSelectValue(axis, config, bazelLabelForStaticDeps(ctx, props.Static_libs))
+		attrs.Dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.Shared_libs))
+		attrs.Whole_archive_deps.SetSelectValue(axis, config, bazelLabelForWholeDeps(ctx, props.Whole_static_libs))
+		attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
 	}
 	// system_dynamic_deps distinguishes between nil/empty list behavior:
 	//    nil -> use default values
@@ -178,6 +178,7 @@
 	Src bazel.LabelAttribute
 }
 
+// NOTE: Used outside of Soong repo project, in the clangprebuilts.go bootstrap_go_package
 func Bp2BuildParsePrebuiltLibraryProps(ctx android.TopDownMutatorContext, module *Module) prebuiltAttributes {
 	var srcLabelAttribute bazel.LabelAttribute
 
@@ -216,6 +217,9 @@
 	srcs     bazel.LabelListAttribute
 
 	rtti bazel.BoolAttribute
+
+	localIncludes    bazel.StringListAttribute
+	absoluteIncludes bazel.StringListAttribute
 }
 
 // bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
@@ -226,28 +230,8 @@
 	var conlyFlags bazel.StringListAttribute
 	var cppFlags bazel.StringListAttribute
 	var rtti bazel.BoolAttribute
-
-	// Creates the -I flags for a directory, while making the directory relative
-	// to the exec root for Bazel to work.
-	includeFlags := func(dir string) []string {
-		// filepath.Join canonicalizes the path, i.e. it takes care of . or .. elements.
-		moduleDirRootedPath := filepath.Join(ctx.ModuleDir(), dir)
-		return []string{
-			"-I" + moduleDirRootedPath,
-			// Include the bindir-rooted path (using make variable substitution). This most
-			// closely matches Bazel's native include path handling, which allows for dependency
-			// on generated headers in these directories.
-			// TODO(b/188084383): Handle local include directories in Bazel.
-			"-I$(BINDIR)/" + moduleDirRootedPath,
-		}
-	}
-
-	// Parse the list of module-relative include directories (-I).
-	parseLocalIncludeDirs := func(baseCompilerProps *BaseCompilerProperties) []string {
-		// include_dirs are root-relative, not module-relative.
-		includeDirs := bp2BuildMakePathsRelativeToModule(ctx, baseCompilerProps.Include_dirs)
-		return append(includeDirs, baseCompilerProps.Local_include_dirs...)
-	}
+	var localIncludes bazel.StringListAttribute
+	var absoluteIncludes bazel.StringListAttribute
 
 	parseCommandLineFlags := func(soongFlags []string) []string {
 		var result []string
@@ -285,18 +269,14 @@
 
 				archVariantCopts := parseCommandLineFlags(baseCompilerProps.Cflags)
 				archVariantAsflags := parseCommandLineFlags(baseCompilerProps.Asflags)
-				for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
-					archVariantCopts = append(archVariantCopts, includeFlags(dir)...)
-					archVariantAsflags = append(archVariantAsflags, includeFlags(dir)...)
+
+				localIncludeDirs := baseCompilerProps.Local_include_dirs
+				if axis == bazel.NoConfigAxis && includeBuildDirectory(baseCompilerProps.Include_build_directory) {
+					localIncludeDirs = append(localIncludeDirs, ".")
 				}
 
-				if axis == bazel.NoConfigAxis {
-					if includeBuildDirectory(baseCompilerProps.Include_build_directory) {
-						flags := includeFlags(".")
-						archVariantCopts = append(archVariantCopts, flags...)
-						archVariantAsflags = append(archVariantAsflags, flags...)
-					}
-				}
+				absoluteIncludes.SetSelectValue(axis, config, baseCompilerProps.Include_dirs)
+				localIncludes.SetSelectValue(axis, config, localIncludeDirs)
 
 				copts.SetSelectValue(axis, config, archVariantCopts)
 				asFlags.SetSelectValue(axis, config, archVariantAsflags)
@@ -308,6 +288,8 @@
 	}
 
 	srcs.ResolveExcludes()
+	absoluteIncludes.DeduplicateAxesFromBase()
+	localIncludes.DeduplicateAxesFromBase()
 
 	productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
 		"Cflags":   &copts,
@@ -331,14 +313,16 @@
 	srcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, srcs)
 
 	return compilerAttributes{
-		copts:      copts,
-		srcs:       srcs,
-		asFlags:    asFlags,
-		asSrcs:     asSrcs,
-		cSrcs:      cSrcs,
-		conlyFlags: conlyFlags,
-		cppFlags:   cppFlags,
-		rtti:       rtti,
+		copts:            copts,
+		srcs:             srcs,
+		asFlags:          asFlags,
+		asSrcs:           asSrcs,
+		cSrcs:            cSrcs,
+		conlyFlags:       conlyFlags,
+		cppFlags:         cppFlags,
+		rtti:             rtti,
+		localIncludes:    localIncludes,
+		absoluteIncludes: absoluteIncludes,
 	}
 }
 
@@ -405,9 +389,9 @@
 				// Excludes to parallel Soong:
 				// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
 				staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs)
-				staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs))
+				staticDeps.SetSelectValue(axis, config, bazelLabelForStaticDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs))
 				wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
-				wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
+				wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
 
 				systemSharedLibs := baseLinkerProps.System_shared_libs
 				// systemSharedLibs distinguishes between nil/empty list behavior:
@@ -416,15 +400,15 @@
 				if len(systemSharedLibs) > 0 {
 					systemSharedLibs = android.FirstUniqueStrings(systemSharedLibs)
 				}
-				systemSharedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, systemSharedLibs))
+				systemSharedDeps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, systemSharedLibs))
 
 				sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
-				dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
+				dynamicDeps.SetSelectValue(axis, config, bazelLabelForSharedDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
 
 				headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
-				headerDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, headerLibs))
+				headerDeps.SetSelectValue(axis, config, bazelLabelForHeaderDeps(ctx, headerLibs))
 				exportedLibs := android.FirstUniqueStrings(baseLinkerProps.Export_header_lib_headers)
-				exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs))
+				exportedDeps.SetSelectValue(axis, config, bazelLabelForHeaderDeps(ctx, exportedLibs))
 
 				linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps))
 				if baseLinkerProps.Version_script != nil {
@@ -441,14 +425,14 @@
 		// reference to the bazel attribute that should be set for the given product variable config
 		attribute *bazel.LabelListAttribute
 
-		depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList
+		depResolutionFunc func(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList
 	}
 
 	productVarToDepFields := map[string]productVarDep{
 		// product variables do not support exclude_shared_libs
-		"Shared_libs":       productVarDep{attribute: &dynamicDeps, depResolutionFunc: android.BazelLabelForModuleDepsExcludes},
-		"Static_libs":       productVarDep{"Exclude_static_libs", &staticDeps, android.BazelLabelForModuleDepsExcludes},
-		"Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, android.BazelLabelForModuleWholeDepsExcludes},
+		"Shared_libs":       productVarDep{attribute: &dynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes},
+		"Static_libs":       productVarDep{"Exclude_static_libs", &staticDeps, bazelLabelForStaticDepsExcludes},
+		"Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, bazelLabelForWholeDepsExcludes},
 	}
 
 	productVariableProps := android.ProductVariableProperties(ctx)
@@ -535,12 +519,21 @@
 	return relativePaths
 }
 
-func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) bazel.StringListAttribute {
+// BazelIncludes contains information about -I and -isystem paths from a module converted to Bazel
+// attributes.
+type BazelIncludes struct {
+	Includes       bazel.StringListAttribute
+	SystemIncludes bazel.StringListAttribute
+}
+
+func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) BazelIncludes {
 	libraryDecorator := module.linker.(*libraryDecorator)
 	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator)
 }
 
-func Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx android.TopDownMutatorContext, module *Module) bazel.StringListAttribute {
+// Bp2buildParseExportedIncludesForPrebuiltLibrary returns a BazelIncludes with Bazel-ified values
+// to export includes from the underlying module's properties.
+func Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx android.TopDownMutatorContext, module *Module) BazelIncludes {
 	prebuiltLibraryLinker := module.linker.(*prebuiltLibraryLinker)
 	libraryDecorator := prebuiltLibraryLinker.libraryDecorator
 	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator)
@@ -548,36 +541,78 @@
 
 // bp2BuildParseExportedIncludes creates a string list attribute contains the
 // exported included directories of a module.
-func bp2BuildParseExportedIncludesHelper(ctx android.TopDownMutatorContext, module *Module, libraryDecorator *libraryDecorator) bazel.StringListAttribute {
-	// Export_system_include_dirs and export_include_dirs are already module dir
-	// relative, so they don't need to be relativized like include_dirs, which
-	// are root-relative.
-	includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs
-	includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
-	var includeDirsAttribute bazel.StringListAttribute
-
-	getVariantIncludeDirs := func(includeDirs []string, flagExporterProperties *FlagExporterProperties, subtract bool) []string {
-		variantIncludeDirs := flagExporterProperties.Export_system_include_dirs
-		variantIncludeDirs = append(variantIncludeDirs, flagExporterProperties.Export_include_dirs...)
-
-		if subtract {
-			// To avoid duplicate includes when base includes + arch includes are combined
-			// TODO: Add something similar to ResolveExcludes() in bazel/properties.go
-			variantIncludeDirs = bazel.SubtractStrings(variantIncludeDirs, includeDirs)
-		}
-		return variantIncludeDirs
-	}
-
+func bp2BuildParseExportedIncludesHelper(ctx android.TopDownMutatorContext, module *Module, libraryDecorator *libraryDecorator) BazelIncludes {
+	exported := BazelIncludes{}
 	for axis, configToProps := range module.GetArchVariantProperties(ctx, &FlagExporterProperties{}) {
 		for config, props := range configToProps {
 			if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
-				archVariantIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties, axis != bazel.NoConfigAxis)
-				if len(archVariantIncludeDirs) > 0 {
-					includeDirsAttribute.SetSelectValue(axis, config, archVariantIncludeDirs)
+				if len(flagExporterProperties.Export_include_dirs) > 0 {
+					exported.Includes.SetSelectValue(axis, config, flagExporterProperties.Export_include_dirs)
+				}
+				if len(flagExporterProperties.Export_system_include_dirs) > 0 {
+					exported.SystemIncludes.SetSelectValue(axis, config, flagExporterProperties.Export_system_include_dirs)
 				}
 			}
 		}
 	}
+	exported.Includes.DeduplicateAxesFromBase()
+	exported.SystemIncludes.DeduplicateAxesFromBase()
 
-	return includeDirsAttribute
+	return exported
+}
+
+func bazelLabelForStaticModule(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+	label := android.BazelModuleLabel(ctx, m)
+	if aModule, ok := m.(android.Module); ok {
+		if ctx.OtherModuleType(aModule) == "cc_library" && !android.GenerateCcLibraryStaticOnly(m.Name()) {
+			label += "_bp2build_cc_library_static"
+		}
+	}
+	return label
+}
+
+func bazelLabelForSharedModule(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+	// cc_library, at it's root name, propagates the shared library, which depends on the static
+	// library.
+	return android.BazelModuleLabel(ctx, m)
+}
+
+func bazelLabelForStaticWholeModuleDeps(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+	label := bazelLabelForStaticModule(ctx, m)
+	if aModule, ok := m.(android.Module); ok {
+		if android.IsModulePrebuilt(aModule) {
+			label += "_alwayslink"
+		}
+	}
+	return label
+}
+
+func bazelLabelForWholeDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticWholeModuleDeps)
+}
+
+func bazelLabelForWholeDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticWholeModuleDeps)
+}
+
+func bazelLabelForStaticDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticModule)
+}
+
+func bazelLabelForStaticDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticModule)
+}
+
+func bazelLabelForSharedDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForSharedModule)
+}
+
+func bazelLabelForHeaderDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+	// This is not elegant, but bp2build's shared library targets only propagate
+	// their header information as part of the normal C++ provider.
+	return bazelLabelForSharedDeps(ctx, modules)
+}
+
+func bazelLabelForSharedDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
 }
diff --git a/cc/builder.go b/cc/builder.go
index 842ce85..b494f7b 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -21,6 +21,7 @@
 import (
 	"path/filepath"
 	"runtime"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -198,19 +199,32 @@
 	// Rule for invoking clang-tidy (a clang-based linter).
 	clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && touch $out",
-			CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
+			Depfile: "${out}.d",
+			Deps:    blueprint.DepsGCC,
+			// Pick bash because some machines with old /bin/sh cannot handle arrays.
+			// All $cFlags and $tidyFlags should have single quotes escaped.
+			// Assume no single quotes in other parameters like $in, $out, $ccCmd.
+			Command: "/bin/bash -c 'SRCF=$in; TIDYF=$out; CLANGFLAGS=($cFlags); " +
+				"rm -f $$TIDYF $${TIDYF}.d && " +
+				"${config.CcWrapper}$ccCmd \"$${CLANGFLAGS[@]}\" -E -o /dev/null $$SRCF " +
+				"-MQ $$TIDYF -MD -MF $${TIDYF}.d && " +
+				"$tidyVars $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $$SRCF " +
+				"-- \"$${CLANGFLAGS[@]}\" && touch $$TIDYF'",
+			CommandDeps: []string{"${config.ClangBin}/clang-tidy", "$ccCmd"},
 		},
 		&remoteexec.REParams{
-			Labels:       map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
-			ExecStrategy: "${config.REClangTidyExecStrategy}",
-			Inputs:       []string{"$in"},
-			// OutputFile here is $in for remote-execution since its possible that
-			// clang-tidy modifies the given input file itself and $out refers to the
-			// ".tidy" file generated for ninja-dependency reasons.
-			OutputFiles: []string{"$in"},
-			Platform:    map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
-		}, []string{"cFlags", "tidyFlags"}, []string{})
+			Labels:               map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
+			ExecStrategy:         "${config.REClangTidyExecStrategy}",
+			Inputs:               []string{"$in"},
+			EnvironmentVariables: []string{"TIDY_TIMEOUT"},
+			// Although clang-tidy has an option to "fix" source files, that feature is hardly useable
+			// under parallel compilation and RBE. So we assume no OutputFiles here.
+			// The clang-tidy fix option is best run locally in single thread.
+			// Copying source file back to local caused two problems:
+			// (1) New timestamps trigger clang and clang-tidy compilations again.
+			// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
+			Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
+		}, []string{"ccCmd", "cFlags", "tidyFlags", "tidyVars"}, []string{})
 
 	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
 
@@ -384,9 +398,6 @@
 
 	systemIncludeFlags string
 
-	// True if static libraries should be grouped (using `-Wl,--start-group` and `-Wl,--end-group`).
-	groupStaticLibs bool
-
 	proto            android.ProtoFlags
 	protoC           bool // If true, compile protos as `.c` files. Otherwise, output as `.cc`.
 	protoOptionsFile bool // If true, output a proto options file.
@@ -436,15 +447,30 @@
 	}
 }
 
+func escapeSingleQuotes(s string) string {
+	// Replace single quotes to work when embedded in a single quoted string for bash.
+	// Relying on string concatenation of bash to get A'B from quoted 'A'\''B'.
+	return strings.Replace(s, `'`, `'\''`, -1)
+}
+
 // Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
-func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles android.Paths,
+func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles, noTidySrcs android.Paths,
 	flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
 
 	// Source files are one-to-one with tidy, coverage, or kythe files, if enabled.
 	objFiles := make(android.Paths, len(srcFiles))
 	var tidyFiles android.Paths
+	noTidySrcsMap := make(map[android.Path]bool)
+	var tidyVars string
 	if flags.tidy {
 		tidyFiles = make(android.Paths, 0, len(srcFiles))
+		for _, path := range noTidySrcs {
+			noTidySrcsMap[path] = true
+		}
+		tidyTimeout := ctx.Config().Getenv("TIDY_TIMEOUT")
+		if len(tidyTimeout) > 0 {
+			tidyVars += "TIDY_TIMEOUT=" + tidyTimeout
+		}
 	}
 	var coverageFiles android.Paths
 	if flags.gcovCoverage {
@@ -505,6 +531,32 @@
 	cppflags += " ${config.NoOverrideGlobalCflags}"
 	toolingCppflags += " ${config.NoOverrideGlobalCflags}"
 
+	// Multiple source files have build rules usually share the same cFlags or tidyFlags.
+	// Define only one version in this module and share it in multiple build rules.
+	// To simplify the code, the shared variables are all named as $flags<nnn>.
+	numSharedFlags := 0
+	flagsMap := make(map[string]string)
+
+	// Share flags only when there are multiple files or tidy rules.
+	var hasMultipleRules = len(srcFiles) > 1 || flags.tidy
+
+	var shareFlags = func(kind string, flags string) string {
+		if !hasMultipleRules || len(flags) < 60 {
+			// Modules have long names and so do the module variables.
+			// It does not save space by replacing a short name with a long one.
+			return flags
+		}
+		mapKey := kind + flags
+		n, ok := flagsMap[mapKey]
+		if !ok {
+			numSharedFlags += 1
+			n = strconv.Itoa(numSharedFlags)
+			flagsMap[mapKey] = n
+			ctx.Variable(pctx, kind+n, flags)
+		}
+		return "$" + kind + n
+	}
+
 	for i, srcFile := range srcFiles {
 		objFile := android.ObjPathWithExt(ctx, subdir, srcFile, "o")
 
@@ -521,7 +573,7 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"asFlags": flags.globalYasmFlags + " " + flags.localYasmFlags,
+					"asFlags": shareFlags("asFlags", flags.globalYasmFlags+" "+flags.localYasmFlags),
 				},
 			})
 			continue
@@ -535,7 +587,7 @@
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
 					"windresCmd": mingwCmd(flags.toolchain, "windres"),
-					"flags":      flags.toolchain.WindresFlags(),
+					"flags":      shareFlags("flags", flags.toolchain.WindresFlags()),
 				},
 			})
 			continue
@@ -603,8 +655,8 @@
 			Implicits:       cFlagsDeps,
 			OrderOnly:       pathDeps,
 			Args: map[string]string{
-				"cFlags": moduleFlags,
-				"ccCmd":  ccCmd,
+				"cFlags": shareFlags("cFlags", moduleFlags),
+				"ccCmd":  ccCmd, // short and not shared
 			},
 		})
 
@@ -619,13 +671,14 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"cFlags": moduleFlags,
+					"cFlags": shareFlags("cFlags", moduleFlags),
 				},
 			})
 			kytheFiles = append(kytheFiles, kytheFile)
 		}
 
-		if tidy {
+		//  Even with tidy, some src file could be skipped by noTidySrcsMap.
+		if tidy && !noTidySrcsMap[srcFile] {
 			tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
 			tidyFiles = append(tidyFiles, tidyFile)
 
@@ -634,19 +687,19 @@
 				rule = clangTidyRE
 			}
 
+			ctx.TidyFile(tidyFile)
 			ctx.Build(pctx, android.BuildParams{
 				Rule:        rule,
 				Description: "clang-tidy " + srcFile.Rel(),
 				Output:      tidyFile,
 				Input:       srcFile,
-				// We must depend on objFile, since clang-tidy doesn't
-				// support exporting dependencies.
-				Implicit:  objFile,
-				Implicits: cFlagsDeps,
-				OrderOnly: pathDeps,
+				Implicits:   cFlagsDeps,
+				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"cFlags":    moduleToolingFlags,
-					"tidyFlags": flags.tidyFlags,
+					"ccCmd":     ccCmd,
+					"cFlags":    shareFlags("cFlags", escapeSingleQuotes(moduleToolingFlags)),
+					"tidyFlags": shareFlags("tidyFlags", escapeSingleQuotes(config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags))),
+					"tidyVars":  tidyVars, // short and not shared
 				},
 			})
 		}
@@ -668,8 +721,8 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"cFlags":     moduleToolingFlags,
-					"exportDirs": flags.sAbiFlags,
+					"cFlags":     shareFlags("cFlags", moduleToolingFlags),
+					"exportDirs": shareFlags("exportDirs", flags.sAbiFlags),
 				},
 			})
 		}
@@ -752,13 +805,7 @@
 		}
 	}
 
-	if flags.groupStaticLibs && !ctx.Darwin() && len(staticLibs) > 0 {
-		libFlagsList = append(libFlagsList, "-Wl,--start-group")
-	}
 	libFlagsList = append(libFlagsList, staticLibs.Strings()...)
-	if flags.groupStaticLibs && !ctx.Darwin() && len(staticLibs) > 0 {
-		libFlagsList = append(libFlagsList, "-Wl,--end-group")
-	}
 
 	if groupLate && !ctx.Darwin() && len(lateStaticLibs) > 0 {
 		libFlagsList = append(libFlagsList, "-Wl,--start-group")
diff --git a/cc/cc.go b/cc/cc.go
index 9c1f559..d2f8a12 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -31,6 +31,7 @@
 	"android/soong/cc/config"
 	"android/soong/fuzz"
 	"android/soong/genrule"
+	"android/soong/snapshot"
 )
 
 func init() {
@@ -217,8 +218,6 @@
 
 	// True if .s files should be processed with the c preprocessor.
 	AssemblerWithCpp bool
-	// True if static libraries should be grouped (using `-Wl,--start-group` and `-Wl,--end-group`).
-	GroupStaticLibs bool
 
 	proto            android.ProtoFlags
 	protoC           bool // Whether to use C instead of C++
@@ -1815,15 +1814,6 @@
 
 	flags.AssemblerWithCpp = inList("-xassembler-with-cpp", flags.Local.AsFlags)
 
-	// Optimization to reduce size of build.ninja
-	// Replace the long list of flags for each file with a module-local variable
-	ctx.Variable(pctx, "cflags", strings.Join(flags.Local.CFlags, " "))
-	ctx.Variable(pctx, "cppflags", strings.Join(flags.Local.CppFlags, " "))
-	ctx.Variable(pctx, "asflags", strings.Join(flags.Local.AsFlags, " "))
-	flags.Local.CFlags = []string{"$cflags"}
-	flags.Local.CppFlags = []string{"$cppflags"}
-	flags.Local.AsFlags = []string{"$asflags"}
-
 	var objs Objects
 	if c.compiler != nil {
 		objs = c.compiler.compile(ctx, flags, deps)
@@ -3403,6 +3393,8 @@
 	return c.IsStubs() || c.Target().NativeBridge == android.NativeBridgeEnabled
 }
 
+var _ snapshot.RelativeInstallPath = (*Module)(nil)
+
 //
 // Defaults
 //
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index 04536fc..ad130ba 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -316,7 +316,7 @@
 	if strings.HasPrefix(parameter, "--sysroot") {
 		return systemRoot
 	}
-	if strings.HasPrefix(parameter, "-fsanitize-blacklist") {
+	if strings.HasPrefix(parameter, "-fsanitize-ignorelist") {
 		return relativeFilePathFlag
 	}
 	if strings.HasPrefix(parameter, "-fprofile-sample-use") {
diff --git a/cc/compiler.go b/cc/compiler.go
index 34d68a1..b535e7f 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -39,6 +39,9 @@
 	// or filegroup using the syntax ":module".
 	Srcs []string `android:"path,arch_variant"`
 
+	// list of source files that should not be compiled with clang-tidy.
+	Tidy_disabled_srcs []string `android:"path,arch_variant"`
+
 	// list of source files that should not be used to build the C/C++ module.
 	// This is most useful in the arch/multilib variants to remove non-common files
 	Exclude_srcs []string `android:"path,arch_variant"`
@@ -663,7 +666,9 @@
 	compiler.srcs = srcs
 
 	// Compile files listed in c.Properties.Srcs into objects
-	objs := compileObjs(ctx, buildFlags, "", srcs, pathDeps, compiler.cFlagsDeps)
+	objs := compileObjs(ctx, buildFlags, "", srcs,
+		android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_disabled_srcs),
+		pathDeps, compiler.cFlagsDeps)
 
 	if ctx.Failed() {
 		return Objects{}
@@ -673,10 +678,10 @@
 }
 
 // Compile a list of source files into objects a specified subdirectory
-func compileObjs(ctx android.ModuleContext, flags builderFlags,
-	subdir string, srcFiles, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
+func compileObjs(ctx android.ModuleContext, flags builderFlags, subdir string,
+	srcFiles, noTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
 
-	return transformSourceToObj(ctx, subdir, srcFiles, flags, pathDeps, cFlagsDeps)
+	return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, flags, pathDeps, cFlagsDeps)
 }
 
 // Properties for rust_bindgen related to generating rust bindings.
diff --git a/cc/config/OWNERS b/cc/config/OWNERS
index 701db92..580f215 100644
--- a/cc/config/OWNERS
+++ b/cc/config/OWNERS
@@ -1,3 +1,3 @@
 per-file vndk.go = smoreland@google.com, victoryang@google.com
-per-file clang.go,global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
+per-file clang.go,global.go,tidy.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
 
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 53a7306..3caa688 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -31,23 +31,15 @@
 	"-fno-tree-sra",
 	"-fprefetch-loop-arrays",
 	"-funswitch-loops",
-	"-Werror=unused-but-set-parameter",
-	"-Werror=unused-but-set-variable",
 	"-Wmaybe-uninitialized",
 	"-Wno-error=clobbered",
 	"-Wno-error=maybe-uninitialized",
-	"-Wno-error=unused-but-set-parameter",
-	"-Wno-error=unused-but-set-variable",
 	"-Wno-extended-offsetof",
 	"-Wno-free-nonheap-object",
 	"-Wno-literal-suffix",
 	"-Wno-maybe-uninitialized",
 	"-Wno-old-style-declaration",
-	"-Wno-unused-but-set-parameter",
-	"-Wno-unused-but-set-variable",
 	"-Wno-unused-local-typedefs",
-	"-Wunused-but-set-parameter",
-	"-Wunused-but-set-variable",
 	"-fdiagnostics-color",
 	// http://b/153759688
 	"-fuse-init-array",
diff --git a/cc/config/global.go b/cc/config/global.go
index 9773345..5f41f9e 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -230,6 +230,9 @@
 		"-Wno-string-concatenation", // http://b/175068488
 		// New warnings to be fixed after clang-r428724
 		"-Wno-align-mismatch", // http://b/193679946
+		// New warnings to be fixed after clang-r433403
+		"-Wno-error=unused-but-set-variable",  // http://b/197240255
+		"-Wno-error=unused-but-set-parameter", // http://b/197240255
 	}
 
 	// Extra cflags for external third-party projects to disable warnings that
@@ -255,6 +258,9 @@
 
 		// http://b/165945989
 		"-Wno-psabi",
+
+		// http://b/199369603
+		"-Wno-null-pointer-subtraction",
 	}
 
 	IllegalFlags = []string{
@@ -268,8 +274,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r428724"
-	ClangDefaultShortVersion = "13.0.1"
+	ClangDefaultVersion      = "clang-r433403"
+	ClangDefaultShortVersion = "13.0.2"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index c4563e2..fdc246c 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -39,6 +39,7 @@
 			"misc-*",
 			"performance-*",
 			"portability-*",
+			"-bugprone-easily-swappable-parameters",
 			"-bugprone-narrowing-conversions",
 			"-google-readability*",
 			"-google-runtime-references",
@@ -106,6 +107,7 @@
 
 const tidyDefault = "${config.TidyDefaultGlobalChecks}"
 const tidyExternalVendor = "${config.TidyExternalVendorChecks}"
+const tidyDefaultNoAnalyzer = "${config.TidyDefaultGlobalChecks},-clang-analyzer-*"
 
 // This is a map of local path prefixes to the set of default clang-tidy checks
 // to be used.
@@ -114,6 +116,7 @@
 	{"external/", tidyExternalVendor},
 	{"external/google", tidyDefault},
 	{"external/webrtc", tidyDefault},
+	{"external/googletest/", tidyExternalVendor},
 	{"frameworks/compile/mclinker/", tidyExternalVendor},
 	{"hardware/qcom", tidyExternalVendor},
 	{"vendor/", tidyExternalVendor},
@@ -132,6 +135,7 @@
 }
 
 func TidyChecksForDir(dir string) string {
+	dir = dir + "/"
 	for _, pathCheck := range reversedDefaultLocalTidyChecks {
 		if strings.HasPrefix(dir, pathCheck.PathPrefix) {
 			return pathCheck.Checks
@@ -139,3 +143,17 @@
 	}
 	return tidyDefault
 }
+
+func TidyFlagsForSrcFile(srcFile android.Path, flags string) string {
+	// Disable clang-analyzer-* checks globally for generated source files
+	// because some of them are too huge. Local .bp files can add wanted
+	// clang-analyzer checks through the tidy_checks property.
+	// Need to do this patch per source file, because some modules
+	// have both generated and organic source files.
+	if _, ok := srcFile.(android.WritablePath); ok {
+		if strings.Contains(flags, tidyDefault) {
+			return strings.ReplaceAll(flags, tidyDefault, tidyDefaultNoAnalyzer)
+		}
+	}
+	return flags
+}
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 24e8fa4..d4fcf7c 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -53,6 +53,10 @@
 	"android.hardware.power.stats-V1-ndk_platform",
 	"android.hardware.power.stats-ndk_platform",
 	"android.hardware.power.stats-unstable-ndk_platform",
+	"android.hardware.radio-V1-ndk",
+	"android.hardware.radio-V1-ndk_platform",
+	"android.hardware.radio.config-V1-ndk",
+	"android.hardware.radio.config-V1-ndk_platform",
 	"android.hardware.rebootescrow-ndk_platform",
 	"android.hardware.security.keymint-V1-ndk",
 	"android.hardware.security.keymint-V1-ndk_platform",
@@ -74,6 +78,8 @@
 	"android.hardware.weaver-ndk_platform",
 	"android.hardware.weaver-unstable-ndk_platform",
 	"android.system.keystore2-V1-ndk",
+	"android.hardware.wifi.hostapd-V1-ndk",
+	"android.hardware.wifi.hostapd-V1-ndk_platform",
 	"android.system.keystore2-V1-ndk_platform",
 	"android.system.keystore2-ndk_platform",
 	"android.system.keystore2-unstable-ndk_platform",
diff --git a/cc/fuzz.go b/cc/fuzz.go
index fbef12b..83f0037 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -123,7 +123,7 @@
 // that should be installed in the fuzz target output directories. This function
 // returns true, unless:
 //  - The module is not an installable shared library, or
-//  - The module is a header, stub, or vendor-linked library, or
+//  - The module is a header or stub, or
 //  - The module is a prebuilt and its source is available, or
 //  - The module is a versioned member of an SDK snapshot.
 func isValidSharedDependency(dependency android.Module) bool {
@@ -141,11 +141,6 @@
 		return false
 	}
 
-	if linkable.UseVndk() {
-		// Discard vendor linked libraries.
-		return false
-	}
-
 	if lib := moduleLibraryInterface(dependency); lib != nil && lib.buildStubs() && linkable.CcLibrary() {
 		// Discard stubs libs (only CCLibrary variants). Prebuilt libraries should not
 		// be excluded on the basis of they're not CCLibrary()'s.
diff --git a/cc/library.go b/cc/library.go
index 196116b..8a572f9 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -143,6 +143,8 @@
 type StaticOrSharedProperties struct {
 	Srcs []string `android:"path,arch_variant"`
 
+	Tidy_disabled_srcs []string `android:"path,arch_variant"`
+
 	Sanitized Sanitized `android:"arch_variant"`
 
 	Cflags []string `android:"arch_variant"`
@@ -205,6 +207,7 @@
 	RegisterLibraryBuildComponents(android.InitRegistrationContext)
 
 	android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
+	android.RegisterBp2BuildMutator("cc_library_shared", CcLibrarySharedBp2Build)
 	android.RegisterBp2BuildMutator("cc_library", CcLibraryBp2Build)
 }
 
@@ -216,6 +219,7 @@
 	ctx.RegisterModuleType("cc_library_host_shared", LibraryHostSharedFactory)
 }
 
+// TODO(b/199902614): Can this be factored to share with the other Attributes?
 // For bp2build conversion.
 type bazelCcLibraryAttributes struct {
 	// Attributes pertaining to both static and shared variants.
@@ -228,16 +232,19 @@
 	Conlyflags bazel.StringListAttribute
 	Asflags    bazel.StringListAttribute
 
-	Hdrs                bazel.LabelListAttribute
-	Deps                bazel.LabelListAttribute
-	Implementation_deps bazel.LabelListAttribute
-	Dynamic_deps        bazel.LabelListAttribute
-	Whole_archive_deps  bazel.LabelListAttribute
-	System_dynamic_deps bazel.LabelListAttribute
-	Includes            bazel.StringListAttribute
-	Linkopts            bazel.StringListAttribute
-	Use_libcrt          bazel.BoolAttribute
-	Rtti                bazel.BoolAttribute
+	Hdrs                   bazel.LabelListAttribute
+	Deps                   bazel.LabelListAttribute
+	Implementation_deps    bazel.LabelListAttribute
+	Dynamic_deps           bazel.LabelListAttribute
+	Whole_archive_deps     bazel.LabelListAttribute
+	System_dynamic_deps    bazel.LabelListAttribute
+	Export_includes        bazel.StringListAttribute
+	Export_system_includes bazel.StringListAttribute
+	Local_includes         bazel.StringListAttribute
+	Absolute_includes      bazel.StringListAttribute
+	Linkopts               bazel.StringListAttribute
+	Use_libcrt             bazel.BoolAttribute
+	Rtti                   bazel.BoolAttribute
 
 	// This is shared only.
 	Version_script bazel.LabelAttribute
@@ -270,8 +277,8 @@
 	// For some cc_library modules, their static variants are ready to be
 	// converted, but not their shared variants. For these modules, delegate to
 	// the cc_library_static bp2build converter temporarily instead.
-	if android.GenerateCcLibraryStaticOnly(ctx) {
-		ccLibraryStaticBp2BuildInternal(ctx, m)
+	if android.GenerateCcLibraryStaticOnly(ctx.Module().Name()) {
+		ccSharedOrStaticBp2BuildMutatorInternal(ctx, m, "cc_library_static")
 		return
 	}
 
@@ -299,15 +306,18 @@
 		Conlyflags: compilerAttrs.conlyFlags,
 		Asflags:    asFlags,
 
-		Implementation_deps: linkerAttrs.deps,
-		Deps:                linkerAttrs.exportedDeps,
-		Dynamic_deps:        linkerAttrs.dynamicDeps,
-		Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
-		System_dynamic_deps: linkerAttrs.systemDynamicDeps,
-		Includes:            exportedIncludes,
-		Linkopts:            linkerAttrs.linkopts,
-		Use_libcrt:          linkerAttrs.useLibcrt,
-		Rtti:                compilerAttrs.rtti,
+		Implementation_deps:    linkerAttrs.deps,
+		Deps:                   linkerAttrs.exportedDeps,
+		Dynamic_deps:           linkerAttrs.dynamicDeps,
+		Whole_archive_deps:     linkerAttrs.wholeArchiveDeps,
+		System_dynamic_deps:    linkerAttrs.systemDynamicDeps,
+		Export_includes:        exportedIncludes.Includes,
+		Export_system_includes: exportedIncludes.SystemIncludes,
+		Local_includes:         compilerAttrs.localIncludes,
+		Absolute_includes:      compilerAttrs.absoluteIncludes,
+		Linkopts:               linkerAttrs.linkopts,
+		Use_libcrt:             linkerAttrs.useLibcrt,
+		Rtti:                   compilerAttrs.rtti,
 
 		Version_script: linkerAttrs.versionScript,
 
@@ -362,6 +372,7 @@
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyShared()
 	module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -595,7 +606,10 @@
 
 	handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath
 
-	tocFile := getTocFile(ctx, label, ccInfo.OutputFiles)
+	var tocFile android.OptionalPath
+	if len(ccInfo.TocFile) > 0 {
+		tocFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, ccInfo.TocFile))
+	}
 	handler.module.linker.(*libraryDecorator).tocFile = tocFile
 
 	ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
@@ -608,25 +622,6 @@
 	return true
 }
 
-// getTocFile looks for the .so.toc file in the target's output files, if any. The .so.toc file
-// contains the table of contents of all symbols of a shared object.
-func getTocFile(ctx android.ModuleContext, label string, outputFiles []string) android.OptionalPath {
-	var tocFile string
-	for _, file := range outputFiles {
-		if strings.HasSuffix(file, ".so.toc") {
-			if tocFile != "" {
-				ctx.ModuleErrorf("The %s target cannot produce more than 1 .toc file.", label)
-			}
-			tocFile = file
-			// Don't break to validate that there are no multiple .toc files per .so.
-		}
-	}
-	if tocFile == "" {
-		return android.OptionalPath{}
-	}
-	return android.OptionalPathForPath(android.PathForBazelOut(ctx, tocFile))
-}
-
 func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
@@ -996,12 +991,14 @@
 
 	if library.static() {
 		srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs)
-		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary,
-			srcs, library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
+		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, srcs,
+			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_disabled_srcs),
+			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	} else if library.shared() {
 		srcs := android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Srcs)
-		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary,
-			srcs, library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
+		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary, srcs,
+			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_disabled_srcs),
+			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	}
 
 	return objs
@@ -2317,98 +2314,7 @@
 	return outputFile
 }
 
-type bazelCcLibraryStaticAttributes struct {
-	Copts               bazel.StringListAttribute
-	Srcs                bazel.LabelListAttribute
-	Implementation_deps bazel.LabelListAttribute
-	Deps                bazel.LabelListAttribute
-	Whole_archive_deps  bazel.LabelListAttribute
-	Dynamic_deps        bazel.LabelListAttribute
-	System_dynamic_deps bazel.LabelListAttribute
-	Linkopts            bazel.StringListAttribute
-	Linkstatic          bool
-	Use_libcrt          bazel.BoolAttribute
-	Rtti                bazel.BoolAttribute
-	Includes            bazel.StringListAttribute
-	Hdrs                bazel.LabelListAttribute
-
-	Cppflags   bazel.StringListAttribute
-	Srcs_c     bazel.LabelListAttribute
-	Conlyflags bazel.StringListAttribute
-	Srcs_as    bazel.LabelListAttribute
-	Asflags    bazel.StringListAttribute
-
-	Static staticOrSharedAttributes
-}
-
-type bazelCcLibraryStatic struct {
-	android.BazelTargetModuleBase
-	bazelCcLibraryStaticAttributes
-}
-
-func BazelCcLibraryStaticFactory() android.Module {
-	module := &bazelCcLibraryStatic{}
-	module.AddProperties(&module.bazelCcLibraryStaticAttributes)
-	android.InitBazelTargetModule(module)
-	return module
-}
-
-func ccLibraryStaticBp2BuildInternal(ctx android.TopDownMutatorContext, module *Module) {
-	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
-	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
-	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
-
-	asFlags := compilerAttrs.asFlags
-	if compilerAttrs.asSrcs.IsEmpty() {
-		// Skip asflags for BUILD file simplicity if there are no assembly sources.
-		asFlags = bazel.MakeStringListAttribute(nil)
-	}
-
-	// Append static{} stanza properties. These won't be specified on
-	// cc_library_static itself, but may be specified in cc_defaults that this module
-	// depends on.
-	staticAttrs := bp2BuildParseStaticProps(ctx, module)
-
-	compilerAttrs.srcs.Append(staticAttrs.Srcs)
-	compilerAttrs.cSrcs.Append(staticAttrs.Srcs_c)
-	compilerAttrs.asSrcs.Append(staticAttrs.Srcs_as)
-	compilerAttrs.copts.Append(staticAttrs.Copts)
-	linkerAttrs.exportedDeps.Append(staticAttrs.Static_deps)
-	linkerAttrs.dynamicDeps.Append(staticAttrs.Dynamic_deps)
-	linkerAttrs.wholeArchiveDeps.Append(staticAttrs.Whole_archive_deps)
-	linkerAttrs.systemDynamicDeps.Append(staticAttrs.System_dynamic_deps)
-
-	attrs := &bazelCcLibraryStaticAttributes{
-		Copts:               compilerAttrs.copts,
-		Srcs:                compilerAttrs.srcs,
-		Implementation_deps: linkerAttrs.deps,
-		Deps:                linkerAttrs.exportedDeps,
-		Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
-		Dynamic_deps:        linkerAttrs.dynamicDeps,
-		System_dynamic_deps: linkerAttrs.systemDynamicDeps,
-
-		Linkopts:   linkerAttrs.linkopts,
-		Linkstatic: true,
-		Use_libcrt: linkerAttrs.useLibcrt,
-		Rtti:       compilerAttrs.rtti,
-		Includes:   exportedIncludes,
-
-		Cppflags:   compilerAttrs.cppFlags,
-		Srcs_c:     compilerAttrs.cSrcs,
-		Conlyflags: compilerAttrs.conlyFlags,
-		Srcs_as:    compilerAttrs.asSrcs,
-		Asflags:    asFlags,
-	}
-
-	props := bazel.BazelTargetModuleProperties{
-		Rule_class:        "cc_library_static",
-		Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
-	}
-
-	ctx.CreateBazelTargetModule(module.Name(), props, attrs)
-}
-
-func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
+func ccSharedOrStaticBp2BuildMutator(ctx android.TopDownMutatorContext, modType string) {
 	module, ok := ctx.Module().(*Module)
 	if !ok {
 		// Not a cc module
@@ -2417,15 +2323,172 @@
 	if !module.ConvertWithBp2build(ctx) {
 		return
 	}
-	if ctx.ModuleType() != "cc_library_static" {
+	if ctx.ModuleType() != modType {
 		return
 	}
 
-	ccLibraryStaticBp2BuildInternal(ctx, module)
+	ccSharedOrStaticBp2BuildMutatorInternal(ctx, module, modType)
 }
 
-func (m *bazelCcLibraryStatic) Name() string {
-	return m.BaseModuleName()
+func ccSharedOrStaticBp2BuildMutatorInternal(ctx android.TopDownMutatorContext, module *Module, modType string) {
+	if modType != "cc_library_static" && modType != "cc_library_shared" {
+		panic("ccSharedOrStaticBp2BuildMutatorInternal only supports cc_library_{static,shared}")
+	}
+	isStatic := modType == "cc_library_static"
+
+	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
+	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
+	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
+
+	// Append shared/static{} stanza properties. These won't be specified on
+	// cc_library_* itself, but may be specified in cc_defaults that this module
+	// depends on.
+	libSharedOrStaticAttrs := bp2BuildParseLibProps(ctx, module, isStatic)
+
+	compilerAttrs.srcs.Append(libSharedOrStaticAttrs.Srcs)
+	compilerAttrs.cSrcs.Append(libSharedOrStaticAttrs.Srcs_c)
+	compilerAttrs.asSrcs.Append(libSharedOrStaticAttrs.Srcs_as)
+	compilerAttrs.copts.Append(libSharedOrStaticAttrs.Copts)
+	linkerAttrs.exportedDeps.Append(libSharedOrStaticAttrs.Static_deps)
+	linkerAttrs.dynamicDeps.Append(libSharedOrStaticAttrs.Dynamic_deps)
+	linkerAttrs.wholeArchiveDeps.Append(libSharedOrStaticAttrs.Whole_archive_deps)
+	linkerAttrs.systemDynamicDeps.Append(libSharedOrStaticAttrs.System_dynamic_deps)
+
+	asFlags := compilerAttrs.asFlags
+	if compilerAttrs.asSrcs.IsEmpty() {
+		// Skip asflags for BUILD file simplicity if there are no assembly sources.
+		asFlags = bazel.MakeStringListAttribute(nil)
+	}
+
+	var attrs interface{}
+	if isStatic {
+		attrs = &bazelCcLibraryStaticAttributes{
+			Copts:               compilerAttrs.copts,
+			Srcs:                compilerAttrs.srcs,
+			Implementation_deps: linkerAttrs.deps,
+			Deps:                linkerAttrs.exportedDeps,
+			Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
+			Dynamic_deps:        linkerAttrs.dynamicDeps,
+			System_dynamic_deps: linkerAttrs.systemDynamicDeps,
+
+			Linkopts:               linkerAttrs.linkopts,
+			Use_libcrt:             linkerAttrs.useLibcrt,
+			Rtti:                   compilerAttrs.rtti,
+			Export_includes:        exportedIncludes.Includes,
+			Export_system_includes: exportedIncludes.SystemIncludes,
+			Local_includes:         compilerAttrs.localIncludes,
+			Absolute_includes:      compilerAttrs.absoluteIncludes,
+
+			Cppflags:   compilerAttrs.cppFlags,
+			Srcs_c:     compilerAttrs.cSrcs,
+			Conlyflags: compilerAttrs.conlyFlags,
+			Srcs_as:    compilerAttrs.asSrcs,
+			Asflags:    asFlags,
+		}
+	} else {
+		attrs = &bazelCcLibrarySharedAttributes{
+			Srcs:    compilerAttrs.srcs,
+			Srcs_c:  compilerAttrs.cSrcs,
+			Srcs_as: compilerAttrs.asSrcs,
+
+			Implementation_deps: linkerAttrs.deps,
+			Deps:                linkerAttrs.exportedDeps,
+			Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
+			Dynamic_deps:        linkerAttrs.dynamicDeps,
+			System_dynamic_deps: linkerAttrs.systemDynamicDeps,
+
+			Copts:      compilerAttrs.copts,
+			Cppflags:   compilerAttrs.cppFlags,
+			Conlyflags: compilerAttrs.conlyFlags,
+			Asflags:    asFlags,
+			Linkopts:   linkerAttrs.linkopts,
+
+			Use_libcrt: linkerAttrs.useLibcrt,
+			Rtti:       compilerAttrs.rtti,
+
+			Export_includes:        exportedIncludes.Includes,
+			Export_system_includes: exportedIncludes.SystemIncludes,
+			Local_includes:         compilerAttrs.localIncludes,
+			Absolute_includes:      compilerAttrs.absoluteIncludes,
+			Version_script:         linkerAttrs.versionScript,
+
+			Strip: stripAttributes{
+				Keep_symbols:                 linkerAttrs.stripKeepSymbols,
+				Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
+				Keep_symbols_list:            linkerAttrs.stripKeepSymbolsList,
+				All:                          linkerAttrs.stripAll,
+				None:                         linkerAttrs.stripNone,
+			},
+		}
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        modType,
+		Bzl_load_location: fmt.Sprintf("//build/bazel/rules:%s.bzl", modType),
+	}
+
+	ctx.CreateBazelTargetModule(module.Name(), props, attrs)
 }
 
-func (m *bazelCcLibraryStatic) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+// TODO(b/199902614): Can this be factored to share with the other Attributes?
+type bazelCcLibraryStaticAttributes struct {
+	Copts                  bazel.StringListAttribute
+	Srcs                   bazel.LabelListAttribute
+	Implementation_deps    bazel.LabelListAttribute
+	Deps                   bazel.LabelListAttribute
+	Whole_archive_deps     bazel.LabelListAttribute
+	Dynamic_deps           bazel.LabelListAttribute
+	System_dynamic_deps    bazel.LabelListAttribute
+	Linkopts               bazel.StringListAttribute
+	Use_libcrt             bazel.BoolAttribute
+	Rtti                   bazel.BoolAttribute
+	Export_includes        bazel.StringListAttribute
+	Export_system_includes bazel.StringListAttribute
+	Local_includes         bazel.StringListAttribute
+	Absolute_includes      bazel.StringListAttribute
+	Hdrs                   bazel.LabelListAttribute
+
+	Cppflags   bazel.StringListAttribute
+	Srcs_c     bazel.LabelListAttribute
+	Conlyflags bazel.StringListAttribute
+	Srcs_as    bazel.LabelListAttribute
+	Asflags    bazel.StringListAttribute
+}
+
+func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
+	ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_static")
+}
+
+// TODO(b/199902614): Can this be factored to share with the other Attributes?
+type bazelCcLibrarySharedAttributes struct {
+	Srcs    bazel.LabelListAttribute
+	Srcs_c  bazel.LabelListAttribute
+	Srcs_as bazel.LabelListAttribute
+
+	Implementation_deps bazel.LabelListAttribute
+	Deps                bazel.LabelListAttribute
+	Whole_archive_deps  bazel.LabelListAttribute
+	Dynamic_deps        bazel.LabelListAttribute
+	System_dynamic_deps bazel.LabelListAttribute
+
+	Linkopts   bazel.StringListAttribute
+	Use_libcrt bazel.BoolAttribute
+	Rtti       bazel.BoolAttribute
+	Strip      stripAttributes
+
+	Export_includes        bazel.StringListAttribute
+	Export_system_includes bazel.StringListAttribute
+	Local_includes         bazel.StringListAttribute
+	Absolute_includes      bazel.StringListAttribute
+	Hdrs                   bazel.LabelListAttribute
+	Version_script         bazel.LabelAttribute
+
+	Copts      bazel.StringListAttribute
+	Cppflags   bazel.StringListAttribute
+	Conlyflags bazel.StringListAttribute
+	Asflags    bazel.StringListAttribute
+}
+
+func CcLibrarySharedBp2Build(ctx android.TopDownMutatorContext) {
+	ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_shared")
+}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 44a7a71..14b90c1 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -103,12 +103,12 @@
 }
 
 type bazelCcLibraryHeadersAttributes struct {
-	Copts               bazel.StringListAttribute
-	Hdrs                bazel.LabelListAttribute
-	Includes            bazel.StringListAttribute
-	Deps                bazel.LabelListAttribute
-	Implementation_deps bazel.LabelListAttribute
-	System_dynamic_deps bazel.LabelListAttribute
+	Hdrs                   bazel.LabelListAttribute
+	Export_includes        bazel.StringListAttribute
+	Export_system_includes bazel.StringListAttribute
+	Deps                   bazel.LabelListAttribute
+	Implementation_deps    bazel.LabelListAttribute
+	System_dynamic_deps    bazel.LabelListAttribute
 }
 
 func CcLibraryHeadersBp2Build(ctx android.TopDownMutatorContext) {
@@ -127,15 +127,14 @@
 	}
 
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
-	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
-		Copts:               compilerAttrs.copts,
-		Includes:            exportedIncludes,
-		Implementation_deps: linkerAttrs.deps,
-		Deps:                linkerAttrs.exportedDeps,
-		System_dynamic_deps: linkerAttrs.systemDynamicDeps,
+		Export_includes:        exportedIncludes.Includes,
+		Export_system_includes: exportedIncludes.SystemIncludes,
+		Implementation_deps:    linkerAttrs.deps,
+		Deps:                   linkerAttrs.exportedDeps,
+		System_dynamic_deps:    linkerAttrs.systemDynamicDeps,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 9010a1a..559e940 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -74,8 +74,8 @@
 	linkTypes []string
 }
 
-func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	targets := mctx.MultiTargets()
+func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	targets := ctx.MultiTargets()
 	for _, lib := range names {
 		for _, target := range targets {
 			name, version := StubsLibNameAndVersion(lib)
@@ -83,21 +83,21 @@
 				version = "latest"
 			}
 			variations := target.Variations()
-			if mctx.Device() {
+			if ctx.Device() {
 				variations = append(variations,
 					blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
 			}
 			if mt.linkTypes == nil {
-				mctx.AddFarVariationDependencies(variations, dependencyTag, name)
+				ctx.AddFarVariationDependencies(variations, dependencyTag, name)
 			} else {
 				for _, linkType := range mt.linkTypes {
 					libVariations := append(variations,
 						blueprint.Variation{Mutator: "link", Variation: linkType})
-					if mctx.Device() && linkType == "shared" {
+					if ctx.Device() && linkType == "shared" {
 						libVariations = append(libVariations,
 							blueprint.Variation{Mutator: "version", Variation: version})
 					}
-					mctx.AddFarVariationDependencies(libVariations, dependencyTag, name)
+					ctx.AddFarVariationDependencies(libVariations, dependencyTag, name)
 				}
 			}
 		}
@@ -265,8 +265,8 @@
 	// values where necessary.
 	for _, propertyInfo := range includeDirProperties {
 		// Calculate the base directory in the snapshot into which the files will be copied.
-		// lib.archType is "" for common properties.
-		targetDir := filepath.Join(libInfo.OsPrefix(), libInfo.archType, propertyInfo.snapshotDir)
+		// lib.archSubDir is "" for common properties.
+		targetDir := filepath.Join(libInfo.OsPrefix(), libInfo.archSubDir, propertyInfo.snapshotDir)
 
 		propertyName := propertyInfo.propertyName
 
@@ -334,7 +334,7 @@
 
 // path to the native library. Relative to <sdk_root>/<api_dir>
 func nativeLibraryPathFor(lib *nativeLibInfoProperties) string {
-	return filepath.Join(lib.OsPrefix(), lib.archType,
+	return filepath.Join(lib.OsPrefix(), lib.archSubDir,
 		nativeStubDir, lib.outputFile.Base())
 }
 
@@ -347,9 +347,12 @@
 
 	memberType *librarySdkMemberType
 
-	// archType is not exported as if set (to a non default value) it is always arch specific.
-	// This is "" for common properties.
-	archType string
+	// archSubDir is the subdirectory within the OS directory in the sdk snapshot into which arch
+	// specific files will be copied.
+	//
+	// It is not exported since any value other than "" is always going to be arch specific.
+	// This is "" for non-arch specific common properties.
+	archSubDir string
 
 	// The list of possibly common exported include dirs.
 	//
@@ -433,7 +436,7 @@
 	exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate(
 		exportedInfo.IncludeDirs, isGeneratedHeaderDirectory)
 
-	p.archType = ccModule.Target().Arch.ArchType.String()
+	p.archSubDir = ccModule.Target().Arch.ArchType.String()
 
 	// Make sure that the include directories are unique.
 	p.ExportedIncludeDirs = android.FirstUniquePaths(exportedIncludeDirs)
diff --git a/cc/library_test.go b/cc/library_test.go
index 6b349b6..7ddfaa7 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -320,3 +320,48 @@
 		libfoo.Args["ldFlags"], "-Wl,--dynamic-list,foo.dynamic.txt")
 
 }
+
+func TestCcLibrarySharedWithBazel(t *testing.T) {
+	bp := `
+cc_library_shared {
+	name: "foo",
+	srcs: ["foo.cc"],
+	bazel_module: { label: "//foo/bar:bar" },
+}`
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir: "outputbase",
+		LabelToCcInfo: map[string]cquery.CcInfo{
+			"//foo/bar:bar": cquery.CcInfo{
+				CcObjectFiles:        []string{"foo.o"},
+				Includes:             []string{"include"},
+				SystemIncludes:       []string{"system_include"},
+				RootDynamicLibraries: []string{"foo.so"},
+				TocFile:              "foo.so.toc",
+			},
+		},
+	}
+	ctx := testCcWithConfig(t, config)
+
+	sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+	producer := sharedFoo.(android.OutputFileProducer)
+	outputFiles, err := producer.OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.so"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+	tocFilePath := sharedFoo.(*Module).Toc()
+	if !tocFilePath.Valid() {
+		t.Errorf("Invalid tocFilePath: %s", tocFilePath)
+	}
+	tocFile := tocFilePath.Path()
+	expectedToc := "outputbase/execroot/__main__/foo.so.toc"
+	android.AssertStringEquals(t, "toc file", expectedToc, tocFile.String())
+
+	entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0]
+	expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
+	gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+	android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+}
diff --git a/cc/linker.go b/cc/linker.go
index 8671dec..0d612b5 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -86,8 +86,7 @@
 	// compiling crt or libc.
 	Nocrt *bool `android:"arch_variant"`
 
-	// group static libraries.  This can resolve missing symbols issues with interdependencies
-	// between static libraries, but it is generally better to order them correctly instead.
+	// deprecated and ignored because lld makes it unnecessary. See b/189475744.
 	Group_static_libs *bool `android:"arch_variant"`
 
 	// list of modules that should be installed with this module.  This is similar to 'required'
@@ -543,10 +542,6 @@
 
 	flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ToolchainLdflags())
 
-	if Bool(linker.Properties.Group_static_libs) {
-		flags.GroupStaticLibs = true
-	}
-
 	// Version_script is not needed when linking stubs lib where the version
 	// script is created from the symbol map file.
 	if !linker.dynamicProperties.BuildStubs {
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 51cdddf..7879a7d 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -45,11 +45,17 @@
 	abidw = pctx.AndroidStaticRule("abidw",
 		blueprint.RuleParams{
 			Command: "$abidw --type-id-style hash --no-corpus-path " +
-				"--no-show-locs --no-comp-dir-path -w $symbolList $in | " +
-				"$abitidy --all -o $out",
-			CommandDeps: []string{"$abitidy", "$abidw"},
+				"--no-show-locs --no-comp-dir-path -w $symbolList " +
+				"$in --out-file $out",
+			CommandDeps: []string{"$abidw"},
 		}, "symbolList")
 
+	abitidy = pctx.AndroidStaticRule("abitidy",
+		blueprint.RuleParams{
+			Command:     "$abitidy --all -i $in -o $out",
+			CommandDeps: []string{"$abitidy"},
+		})
+
 	abidiff = pctx.AndroidStaticRule("abidiff",
 		blueprint.RuleParams{
 			// Need to create *some* output for ninja. We don't want to use tee
@@ -266,7 +272,7 @@
 
 func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
 	return compileObjs(ctx, flagsToBuilderFlags(flags), "",
-		android.Paths{src}, nil, nil)
+		android.Paths{src}, nil, nil, nil)
 }
 
 func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
@@ -313,19 +319,28 @@
 
 func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
 	implementationLibrary := this.findImplementationLibrary(ctx)
-	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
+	abiRawPath := getNdkAbiDumpInstallBase(ctx).Join(ctx,
 		this.apiLevel.String(), ctx.Arch().ArchType.String(),
-		this.libraryName(ctx), "abi.xml")
+		this.libraryName(ctx), "abi.raw.xml")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        abidw,
 		Description: fmt.Sprintf("abidw %s", implementationLibrary),
-		Output:      this.abiDumpPath,
 		Input:       implementationLibrary,
+		Output:      abiRawPath,
 		Implicit:    symbolList,
 		Args: map[string]string{
 			"symbolList": symbolList.String(),
 		},
 	})
+	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
+		this.apiLevel.String(), ctx.Arch().ArchType.String(),
+		this.libraryName(ctx), "abi.xml")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        abitidy,
+		Description: fmt.Sprintf("abitidy %s", implementationLibrary),
+		Input:       abiRawPath,
+		Output:      this.abiDumpPath,
+	})
 }
 
 func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel {
diff --git a/cc/object.go b/cc/object.go
index 606e368..99b257a 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -122,12 +122,14 @@
 
 // For bp2build conversion.
 type bazelObjectAttributes struct {
-	Srcs    bazel.LabelListAttribute
-	Srcs_as bazel.LabelListAttribute
-	Hdrs    bazel.LabelListAttribute
-	Deps    bazel.LabelListAttribute
-	Copts   bazel.StringListAttribute
-	Asflags bazel.StringListAttribute
+	Srcs              bazel.LabelListAttribute
+	Srcs_as           bazel.LabelListAttribute
+	Hdrs              bazel.LabelListAttribute
+	Deps              bazel.LabelListAttribute
+	Copts             bazel.StringListAttribute
+	Asflags           bazel.StringListAttribute
+	Local_includes    bazel.StringListAttribute
+	Absolute_includes bazel.StringListAttribute
 }
 
 // ObjectBp2Build is the bp2build converter from cc_object modules to the
@@ -170,11 +172,13 @@
 	}
 
 	attrs := &bazelObjectAttributes{
-		Srcs:    srcs,
-		Srcs_as: compilerAttrs.asSrcs,
-		Deps:    deps,
-		Copts:   compilerAttrs.copts,
-		Asflags: asFlags,
+		Srcs:              srcs,
+		Srcs_as:           compilerAttrs.asSrcs,
+		Deps:              deps,
+		Copts:             compilerAttrs.copts,
+		Asflags:           asFlags,
+		Local_includes:    compilerAttrs.localIncludes,
+		Absolute_includes: compilerAttrs.absoluteIncludes,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 90974aa..f6a9d5b 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -56,7 +56,7 @@
 	}
 
 	cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
-		"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blocklist.txt"}
+		"-fsanitize-ignorelist=external/compiler-rt/lib/cfi/cfi_blocklist.txt"}
 	// -flto and -fvisibility are required by clang when -fsanitize=cfi is
 	// used, but have no effect on assembly files
 	cfiAsflags = []string{"-flto", "-fvisibility=default"}
@@ -64,7 +64,7 @@
 		"-Wl,-plugin-opt,O1"}
 	cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
 
-	intOverflowCflags = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blocklist.txt"}
+	intOverflowCflags = []string{"-fsanitize-ignorelist=build/soong/cc/config/integer_overflow_blocklist.txt"}
 
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
 		"-fno-sanitize-recover=integer,undefined"}
@@ -260,7 +260,7 @@
 	// the first one
 	Recover []string
 
-	// value to pass to -fsanitize-blacklist
+	// value to pass to -fsanitize-ignorelist
 	Blocklist *string
 }
 
@@ -756,7 +756,7 @@
 
 	blocklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blocklist)
 	if blocklist.Valid() {
-		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-blacklist="+blocklist.String())
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-ignorelist="+blocklist.String())
 		flags.CFlagsDeps = append(flags.CFlagsDeps, blocklist.Path())
 	}
 
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
index 426dfc5..a621166 100644
--- a/cc/test_data_test.go
+++ b/cc/test_data_test.go
@@ -127,7 +127,7 @@
 			ctx.RegisterModuleType("test", newTest)
 			ctx.Register()
 
-			_, errs := ctx.ParseBlueprintsFiles("Blueprints")
+			_, errs := ctx.ParseBlueprintsFiles("Android.bp")
 			android.FailIfErrored(t, errs)
 			_, errs = ctx.PrepareBuildActions(config)
 			android.FailIfErrored(t, errs)
diff --git a/cc/tidy.go b/cc/tidy.go
index fefa7f0..53ff156 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -148,6 +148,9 @@
 	tidyChecks = tidyChecks + ",-bugprone-branch-clone"
 	// http://b/193716442
 	tidyChecks = tidyChecks + ",-bugprone-implicit-widening-of-multiplication-result"
+	// Too many existing functions trigger this rule, and fixing it requires large code
+	// refactoring. The cost of maintaining this tidy rule outweighs the benefit it brings.
+	tidyChecks = tidyChecks + ",-bugprone-easily-swappable-parameters"
 	flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
 
 	if ctx.Config().IsEnvTrue("WITH_TIDY") {
diff --git a/cc/util.go b/cc/util.go
index 9bba876..88b0aba 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -91,7 +91,6 @@
 		systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
 
 		assemblerWithCpp: in.AssemblerWithCpp,
-		groupStaticLibs:  in.GroupStaticLibs,
 
 		proto:            in.proto,
 		protoC:           in.protoC,
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index ba4d79f..8a17e2e 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -132,12 +132,9 @@
 	return false
 }
 
-// This is to be saved as .json files, which is for development/vendor_snapshot/update.py.
-// These flags become Android.bp snapshot module properties.
+// Extend the snapshot.SnapshotJsonFlags to include cc specific fields.
 type snapshotJsonFlags struct {
-	ModuleName          string `json:",omitempty"`
-	RelativeInstallPath string `json:",omitempty"`
-
+	snapshot.SnapshotJsonFlags
 	// library flags
 	ExportedDirs       []string `json:",omitempty"`
 	ExportedSystemDirs []string `json:",omitempty"`
@@ -154,7 +151,6 @@
 	SharedLibs  []string `json:",omitempty"`
 	StaticLibs  []string `json:",omitempty"`
 	RuntimeLibs []string `json:",omitempty"`
-	Required    []string `json:",omitempty"`
 
 	// extra config files
 	InitRc         []string `json:",omitempty"`
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 2846387..fa63b46 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -20,6 +20,7 @@
 	"flag"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"log"
 	"os"
 	"os/exec"
@@ -76,6 +77,36 @@
 	return nil
 }
 
+const errorLeadingLines = 20
+const errorTrailingLines = 20
+
+func errMsgFromLog(filename string) string {
+	if filename == "" {
+		return ""
+	}
+
+	data, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return ""
+	}
+
+	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
+	if len(lines) > errorLeadingLines+errorTrailingLines+1 {
+		lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
+			len(lines)-errorLeadingLines-errorTrailingLines)
+
+		lines = append(lines[:errorLeadingLines+1],
+			lines[len(lines)-errorTrailingLines:]...)
+	}
+	var buf strings.Builder
+	for _, line := range lines {
+		buf.WriteString("> ")
+		buf.WriteString(line)
+		buf.WriteString("\n")
+	}
+	return buf.String()
+}
+
 // TODO(b/70370883): This tool uses a lot of open files -- over the default
 // soft limit of 1024 on some systems. So bump up to the hard limit until I fix
 // the algorithm.
@@ -170,6 +201,23 @@
 	}
 }
 
+func outDirBase() string {
+	outDirBase := os.Getenv("OUT_DIR")
+	if outDirBase == "" {
+		return "out"
+	} else {
+		return outDirBase
+	}
+}
+
+func distDir(outDir string) string {
+	if distDir := os.Getenv("DIST_DIR"); distDir != "" {
+		return filepath.Clean(distDir)
+	} else {
+		return filepath.Join(outDir, "dist")
+	}
+}
+
 func main() {
 	stdio := terminal.StdioImpl{}
 
@@ -177,6 +225,12 @@
 	log := logger.New(output)
 	defer log.Cleanup()
 
+	for _, v := range os.Environ() {
+		log.Println("Environment: " + v)
+	}
+
+	log.Printf("Argv: %v\n", os.Args)
+
 	flag.Parse()
 
 	_, cancel := context.WithCancel(context.Background())
@@ -208,13 +262,7 @@
 		if !*incremental {
 			name += "-" + time.Now().Format("20060102150405")
 		}
-
-		outDirBase := os.Getenv("OUT_DIR")
-		if outDirBase == "" {
-			outDirBase = "out"
-		}
-
-		outputDir = filepath.Join(outDirBase, name)
+		outputDir = filepath.Join(outDirBase(), name)
 	}
 
 	log.Println("Output directory:", outputDir)
@@ -231,11 +279,13 @@
 
 	var configLogsDir string
 	if *alternateResultDir {
-		configLogsDir = filepath.Join(outputDir, "dist/logs")
+		configLogsDir = filepath.Join(distDir(outDirBase()), "logs")
 	} else {
 		configLogsDir = outputDir
 	}
 
+	log.Println("Logs dir: " + configLogsDir)
+
 	os.MkdirAll(configLogsDir, 0777)
 	log.SetOutput(filepath.Join(configLogsDir, "soong.log"))
 	trace.SetOutput(filepath.Join(configLogsDir, "build.trace"))
@@ -348,10 +398,11 @@
 			FileArgs: []zip.FileArg{
 				{GlobDir: logsDir, SourcePrefixToStrip: logsDir},
 			},
-			OutputFilePath:   filepath.Join(outputDir, "dist/logs.zip"),
+			OutputFilePath:   filepath.Join(distDir(outDirBase()), "logs.zip"),
 			NumParallelJobs:  runtime.NumCPU(),
 			CompressionLevel: 5,
 		}
+		log.Printf("Logs zip: %v\n", args.OutputFilePath)
 		if err := zip.Zip(args); err != nil {
 			log.Fatalf("Error zipping logs: %v", err)
 		}
@@ -424,10 +475,6 @@
 		args = append(args, "--soong-only")
 	}
 
-	if *alternateResultDir {
-		args = append(args, "dist")
-	}
-
 	cmd := exec.Command(mpctx.SoongUi, args...)
 	cmd.Stdout = consoleLogWriter
 	cmd.Stderr = consoleLogWriter
@@ -439,6 +486,11 @@
 		"TARGET_BUILD_APPS=",
 		"TARGET_BUILD_UNBUNDLED=")
 
+	if *alternateResultDir {
+		cmd.Env = append(cmd.Env,
+			"DIST_DIR="+filepath.Join(distDir(outDirBase()), "products/"+product))
+	}
+
 	action := &status.Action{
 		Description: product,
 		Outputs:     []string{product},
@@ -459,9 +511,17 @@
 			}
 		}
 	}
+	var errOutput string
+	if err == nil {
+		errOutput = ""
+	} else {
+		errOutput = errMsgFromLog(consoleLogPath)
+	}
+
 	mpctx.Status.FinishAction(status.ActionResult{
 		Action: action,
 		Error:  err,
+		Output: errOutput,
 	})
 }
 
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index d31489e..be81487 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -24,6 +24,7 @@
 	"io/ioutil"
 	"os"
 	"os/exec"
+	"path"
 	"path/filepath"
 	"regexp"
 	"sort"
@@ -93,6 +94,8 @@
 
 var extraLibs = make(ExtraDeps)
 
+var optionalUsesLibs = make(ExtraDeps)
+
 type Exclude map[string]bool
 
 func (e Exclude) String() string {
@@ -162,7 +165,8 @@
 type Dependency struct {
 	XMLName xml.Name `xml:"dependency"`
 
-	BpTarget string `xml:"-"`
+	BpTarget    string `xml:"-"`
+	BazelTarget string `xml:"-"`
 
 	GroupId    string `xml:"groupId"`
 	ArtifactId string `xml:"artifactId"`
@@ -228,6 +232,14 @@
 	}
 }
 
+func (p Pom) BazelTargetType() string {
+	if p.IsAar() {
+		return "android_library"
+	} else {
+		return "java_library"
+	}
+}
+
 func (p Pom) ImportModuleType() string {
 	if p.IsAar() {
 		return "android_library_import"
@@ -238,6 +250,14 @@
 	}
 }
 
+func (p Pom) BazelImportTargetType() string {
+	if p.IsAar() {
+		return "aar_import"
+	} else {
+		return "java_import"
+	}
+}
+
 func (p Pom) ImportProperty() string {
 	if p.IsAar() {
 		return "aars"
@@ -246,6 +266,14 @@
 	}
 }
 
+func (p Pom) BazelImportProperty() string {
+	if p.IsAar() {
+		return "aar"
+	} else {
+		return "jars"
+	}
+}
+
 func (p Pom) BpName() string {
 	if p.BpTarget == "" {
 		p.BpTarget = rewriteNames.MavenToBp(p.GroupId, p.ArtifactId)
@@ -261,6 +289,14 @@
 	return p.BpDeps("aar", []string{"compile", "runtime"})
 }
 
+func (p Pom) BazelJarDeps() []string {
+	return p.BazelDeps("jar", []string{"compile", "runtime"})
+}
+
+func (p Pom) BazelAarDeps() []string {
+	return p.BazelDeps("aar", []string{"compile", "runtime"})
+}
+
 func (p Pom) BpExtraStaticLibs() []string {
 	return extraStaticLibs[p.BpName()]
 }
@@ -269,6 +305,10 @@
 	return extraLibs[p.BpName()]
 }
 
+func (p Pom) BpOptionalUsesLibs() []string {
+	return optionalUsesLibs[p.BpName()]
+}
+
 // BpDeps obtains dependencies filtered by type and scope. The results of this
 // method are formatted as Android.bp targets, e.g. run through MavenToBp rules.
 func (p Pom) BpDeps(typeExt string, scopes []string) []string {
@@ -283,6 +323,91 @@
 	return ret
 }
 
+// BazelDeps obtains dependencies filtered by type and scope. The results of this
+// method are formatted as Bazel BUILD targets.
+func (p Pom) BazelDeps(typeExt string, scopes []string) []string {
+	var ret []string
+	for _, d := range p.Dependencies {
+		if d.Type != typeExt || !InList(d.Scope, scopes) {
+			continue
+		}
+		ret = append(ret, d.BazelTarget)
+	}
+	return ret
+}
+
+func PathModVars() (string, string, string) {
+	cmd := "/bin/bash"
+	androidTop := os.Getenv("ANDROID_BUILD_TOP")
+	envSetupSh := path.Join(androidTop, "build/envsetup.sh")
+	return cmd, androidTop, envSetupSh
+}
+
+func InitRefreshMod(poms []*Pom) error {
+	cmd, _, envSetupSh := PathModVars()
+	// refreshmod is expensive, so if pathmod is already working we can skip it.
+	_, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && pathmod "+poms[0].BpName()).Output()
+	if exitErr, _ := err.(*exec.ExitError); exitErr != nil || err != nil {
+		_, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && refreshmod").Output()
+		if exitErr, _ := err.(*exec.ExitError); exitErr != nil {
+			return fmt.Errorf("failed to run %s\n%s\ntry running lunch.", cmd, string(exitErr.Stderr))
+		} else if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func BazelifyExtraDeps(extraDeps ExtraDeps, modules map[string]*Pom) error {
+	for _, deps := range extraDeps {
+		for _, dep := range deps {
+			bazelName, err := BpNameToBazelTarget(dep, modules)
+			if err != nil {
+				return err
+			}
+			dep = bazelName
+		}
+
+	}
+	return nil
+}
+
+func (p *Pom) GetBazelDepNames(modules map[string]*Pom) error {
+	for _, d := range p.Dependencies {
+		bazelName, err := BpNameToBazelTarget(d.BpName(), modules)
+		if err != nil {
+			return err
+		}
+		d.BazelTarget = bazelName
+	}
+	return nil
+}
+
+func BpNameToBazelTarget(bpName string, modules map[string]*Pom) (string, error) {
+	cmd, androidTop, envSetupSh := PathModVars()
+
+	if _, ok := modules[bpName]; ok {
+		// We've seen the POM for this dependency, it will be local to the output BUILD file
+		return ":" + bpName, nil
+	} else {
+		// we don't have the POM for this artifact, find and use the fully qualified target name.
+		output, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && pathmod "+bpName).Output()
+		if exitErr, _ := err.(*exec.ExitError); exitErr != nil {
+			return "", fmt.Errorf("failed to run %s %s\n%s", cmd, bpName, string(exitErr.Stderr))
+		} else if err != nil {
+			return "", err
+		}
+		relPath := ""
+		for _, line := range strings.Fields(string(output)) {
+			if strings.Contains(line, androidTop) {
+				relPath = strings.TrimPrefix(line, androidTop)
+				relPath = strings.TrimLeft(relPath, "/")
+			}
+		}
+		return "//" + relPath + ":" + bpName, nil
+	}
+}
+
 func (p Pom) SdkVersion() string {
 	return sdkVersion
 }
@@ -401,6 +526,13 @@
         {{- end}}
     ],
     {{- end}}
+    {{- if .BpOptionalUsesLibs}}
+    optional_uses_libs: [
+        {{- range .BpOptionalUsesLibs}}
+        "{{.}}",
+        {{- end}}
+    ],
+    {{- end}}
     {{- else if not .IsHostOnly}}
     min_sdk_version: "{{.DefaultMinSdkVersion}}",
     {{- end}}
@@ -488,10 +620,86 @@
         {{- end}}
     ],
     {{- end}}
+    {{- if .BpOptionalUsesLibs}}
+    optional_uses_libs: [
+        {{- range .BpOptionalUsesLibs}}
+        "{{.}}",
+        {{- end}}
+    ],
+    {{- end}}
     java_version: "1.7",
 }
 `))
 
+var bazelTemplate = template.Must(template.New("bp").Parse(`
+{{.BazelImportTargetType}} (
+    name = "{{.BpName}}",
+    {{.BazelImportProperty}}: {{- if not .IsAar}}[{{- end}}"{{.ArtifactFile}}"{{- if not .IsAar}}]{{- end}},
+    visibility = ["//visibility:public"],
+    {{- if .IsAar}}
+    deps = [
+        {{- range .BazelJarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BazelAarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraStaticLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpOptionalUsesLibs}}
+        "{{.}}",
+        {{- end}}
+    ],
+    {{- end}}
+)
+`))
+
+var bazelDepsTemplate = template.Must(template.New("bp").Parse(`
+{{.BazelImportTargetType}} (
+    name = "{{.BpName}}",
+    {{.BazelImportProperty}} = {{- if not .IsAar}}[{{- end}}"{{.ArtifactFile}}"{{- if not .IsAar}}]{{- end}},
+    visibility = ["//visibility:public"],
+    deps = [
+        {{- range .BazelJarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BazelAarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraStaticLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpOptionalUsesLibs}}
+        "{{.}}",
+        {{- end}}
+    ],
+    exports = [
+        {{- range .BazelJarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BazelAarDeps}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraStaticLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpExtraLibs}}
+        "{{.}}",
+        {{- end}}
+        {{- range .BpOptionalUsesLibs}}
+        "{{.}}",
+        {{- end}}
+    ],
+)
+`))
+
 func parse(filename string) (*Pom, error) {
 	data, err := ioutil.ReadFile(filename)
 	if err != nil {
@@ -539,12 +747,14 @@
 
 	// Extract the old args from the file
 	line := scanner.Text()
-	if strings.HasPrefix(line, "// pom2bp ") {
+	if strings.HasPrefix(line, "// pom2bp ") { // .bp file
 		line = strings.TrimPrefix(line, "// pom2bp ")
-	} else if strings.HasPrefix(line, "// pom2mk ") {
+	} else if strings.HasPrefix(line, "// pom2mk ") { // .bp file converted from .mk file
 		line = strings.TrimPrefix(line, "// pom2mk ")
-	} else if strings.HasPrefix(line, "# pom2mk ") {
+	} else if strings.HasPrefix(line, "# pom2mk ") { // .mk file
 		line = strings.TrimPrefix(line, "# pom2mk ")
+	} else if strings.HasPrefix(line, "# pom2bp ") { // Bazel BUILD file
+		line = strings.TrimPrefix(line, "# pom2bp ")
 	} else {
 		return fmt.Errorf("unexpected second line: %q", line)
 	}
@@ -587,7 +797,7 @@
 The tool will extract the necessary information from *.pom files to create an Android.bp whose
 aar libraries can be linked against when using AAPT2.
 
-Usage: %s [--rewrite <regex>=<replace>] [-exclude <module>] [--extra-static-libs <module>=<module>[,<module>]] [--extra-libs <module>=<module>[,<module>]] [<dir>] [-regen <file>]
+Usage: %s [--rewrite <regex>=<replace>] [--exclude <module>] [--extra-static-libs <module>=<module>[,<module>]] [--extra-libs <module>=<module>[,<module>]] [--optional-uses-libs <module>=<module>[,<module>]] [<dir>] [-regen <file>]
 
   -rewrite <regex>=<replace>
      rewrite can be used to specify mappings between Maven projects and Android.bp modules. The -rewrite
@@ -605,6 +815,11 @@
      Some Android.bp modules have transitive runtime dependencies that must be specified when they
      are depended upon (like androidx.test.rules requires android.test.base).
      This may be specified multiple times to declare these dependencies.
+  -optional-uses-libs <module>=<module>[,<module>]
+     Some Android.bp modules have optional dependencies (typically specified with <uses-library> in
+     the module's AndroidManifest.xml) that must be specified when they are depended upon (like
+     androidx.window:window optionally requires androidx.window:window-extensions).
+     This may be specified multiple times to declare these dependencies.
   -sdk-version <version>
      Sets sdk_version: "<version>" for all modules.
   -default-min-sdk-version
@@ -625,10 +840,12 @@
 	}
 
 	var regen string
+	var pom2build bool
 
 	flag.Var(&excludes, "exclude", "Exclude module")
 	flag.Var(&extraStaticLibs, "extra-static-libs", "Extra static dependencies needed when depending on a module")
 	flag.Var(&extraLibs, "extra-libs", "Extra runtime dependencies needed when depending on a module")
+	flag.Var(&optionalUsesLibs, "optional-uses-libs", "Extra optional dependencies needed when depending on a module")
 	flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names")
 	flag.Var(&hostModuleNames, "host", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is a host module")
 	flag.Var(&hostAndDeviceModuleNames, "host-and-device", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is both a host and device module.")
@@ -638,6 +855,7 @@
 	flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies")
 	flag.BoolVar(&jetifier, "jetifier", false, "Sets jetifier: true on all modules")
 	flag.StringVar(&regen, "regen", "", "Rewrite specified file")
+	flag.BoolVar(&pom2build, "pom2build", false, "If true, will generate a Bazel BUILD file *instead* of a .bp file")
 	flag.Parse()
 
 	if regen != "" {
@@ -732,6 +950,16 @@
 		os.Exit(1)
 	}
 
+	if pom2build {
+		if err := InitRefreshMod(poms); err != nil {
+			fmt.Fprintf(os.Stderr, "Error in refreshmod: %s", err)
+			os.Exit(1)
+		}
+		BazelifyExtraDeps(extraStaticLibs, modules)
+		BazelifyExtraDeps(extraLibs, modules)
+		BazelifyExtraDeps(optionalUsesLibs, modules)
+	}
+
 	for _, pom := range poms {
 		if pom.IsAar() {
 			err := pom.ExtractMinSdkVersion()
@@ -741,19 +969,32 @@
 			}
 		}
 		pom.FixDeps(modules)
+		if pom2build {
+			pom.GetBazelDepNames(modules)
+		}
 	}
 
 	buf := &bytes.Buffer{}
+	commentString := "//"
+	if pom2build {
+		commentString = "#"
+	}
+	fmt.Fprintln(buf, commentString, "Automatically generated with:")
+	fmt.Fprintln(buf, commentString, "pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " "))
 
-	fmt.Fprintln(buf, "// Automatically generated with:")
-	fmt.Fprintln(buf, "// pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " "))
+	depsTemplate := bpDepsTemplate
+	template := bpTemplate
+	if pom2build {
+		depsTemplate = bazelDepsTemplate
+		template = bazelTemplate
+	}
 
 	for _, pom := range poms {
 		var err error
 		if staticDeps {
-			err = bpDepsTemplate.Execute(buf, pom)
+			err = depsTemplate.Execute(buf, pom)
 		} else {
-			err = bpTemplate.Execute(buf, pom)
+			err = template.Execute(buf, pom)
 		}
 		if err != nil {
 			fmt.Fprintln(os.Stderr, "Error writing", pom.PomFile, pom.BpName(), err)
@@ -761,11 +1002,15 @@
 		}
 	}
 
-	out, err := bpfix.Reformat(buf.String())
-	if err != nil {
-		fmt.Fprintln(os.Stderr, "Error formatting output", err)
-		os.Exit(1)
+	if pom2build {
+		os.Stdout.WriteString(buf.String())
+	} else {
+		out, err := bpfix.Reformat(buf.String())
+		if err != nil {
+			fmt.Fprintln(os.Stderr, "Error formatting output", err)
+			os.Exit(1)
+		}
+		os.Stdout.WriteString(out)
 	}
 
-	os.Stdout.WriteString(out)
 }
diff --git a/cmd/run_with_timeout/run_with_timeout_test.go b/cmd/run_with_timeout/run_with_timeout_test.go
index ed6ec11..6729e61 100644
--- a/cmd/run_with_timeout/run_with_timeout_test.go
+++ b/cmd/run_with_timeout/run_with_timeout_test.go
@@ -50,7 +50,7 @@
 			args: args{
 				command: "echo",
 				args:    []string{"foo"},
-				timeout: 1 * time.Second,
+				timeout: 10 * time.Second,
 			},
 			wantStdout: "foo\n",
 		},
@@ -58,7 +58,7 @@
 			name: "timed out",
 			args: args{
 				command: "sh",
-				args:    []string{"-c", "sleep 1 && echo foo"},
+				args:    []string{"-c", "sleep 10 && echo foo"},
 				timeout: 1 * time.Millisecond,
 			},
 			wantStderr: ".*: process timed out after .*\n",
@@ -68,7 +68,7 @@
 			name: "on_timeout command",
 			args: args{
 				command:      "sh",
-				args:         []string{"-c", "sleep 1 && echo foo"},
+				args:         []string{"-c", "sleep 10 && echo foo"},
 				timeout:      1 * time.Millisecond,
 				onTimeoutCmd: "echo bar",
 			},
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 9f09224..703a875 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -16,7 +16,7 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-bootstrap_go_binary {
+blueprint_go_binary {
     name: "soong_build",
     deps: [
         "blueprint",
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index beda184..09a2234 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -36,9 +36,12 @@
 var (
 	topDir           string
 	outDir           string
+	soongOutDir      string
 	availableEnvFile string
 	usedEnvFile      string
 
+	runGoTests bool
+
 	globFile    string
 	globListDir string
 	delveListen string
@@ -55,33 +58,33 @@
 func init() {
 	// Flags that make sense in every mode
 	flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree")
-	flag.StringVar(&outDir, "out", "", "Soong output directory (usually $TOP/out/soong)")
+	flag.StringVar(&soongOutDir, "soong_out", "", "Soong output directory (usually $TOP/out/soong)")
 	flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables")
 	flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
+	flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
+	flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
+	flag.StringVar(&outDir, "out", "", "the ninja builddir directory")
+	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
 
 	// Debug flags
 	flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
 	flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set")
+	flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file")
+	flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file")
+	flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file")
+	flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging")
 
 	// Flags representing various modes soong_build can run in
 	flag.StringVar(&moduleGraphFile, "module_graph_file", "", "JSON module graph file to output")
 	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
 	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
 	flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
-
 	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
-	flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
-	flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
-	flag.StringVar(&cmdlineArgs.BuildDir, "b", ".", "the build output directory")
-	flag.StringVar(&cmdlineArgs.NinjaBuildDir, "n", "", "the ninja builddir directory")
-	flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file")
-	flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file")
-	flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file")
-	flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging")
-	flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
-	flag.BoolVar(&cmdlineArgs.UseValidations, "use-validations", false, "use validations to depend on go tests")
-	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
 	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
+
+	// Flags that probably shouldn't be flags of soong_build but we haven't found
+	// the time to remove them yet
+	flag.BoolVar(&runGoTests, "t", false, "build and run go tests during bootstrap")
 }
 
 func newNameResolver(config android.Config) *android.NameResolver {
@@ -100,19 +103,16 @@
 	return android.NewNameResolver(exportFilter)
 }
 
-func newContext(configuration android.Config, prepareBuildActions bool) *android.Context {
+func newContext(configuration android.Config) *android.Context {
 	ctx := android.NewContext(configuration)
 	ctx.Register()
-	if !prepareBuildActions {
-		configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
-	}
 	ctx.SetNameInterface(newNameResolver(configuration))
 	ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
 	return ctx
 }
 
-func newConfig(outDir string, availableEnv map[string]string) android.Config {
-	configuration, err := android.NewConfig(outDir, cmdlineArgs.ModuleListFile, availableEnv)
+func newConfig(availableEnv map[string]string) android.Config {
+	configuration, err := android.NewConfig(cmdlineArgs.ModuleListFile, runGoTests, outDir, soongOutDir, availableEnv)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -126,11 +126,7 @@
 // TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
 // the incorrect results from the first pass, and file I/O is expensive.
 func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) {
-	var firstArgs, secondArgs bootstrap.Args
-
-	firstArgs = cmdlineArgs
-	configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
-	bootstrap.RunBlueprint(firstArgs, firstCtx.Context, configuration)
+	bootstrap.RunBlueprint(cmdlineArgs, bootstrap.StopBeforeWriteNinja, firstCtx.Context, configuration)
 
 	// Invoke bazel commands and save results for second pass.
 	if err := configuration.BazelContext.InvokeBazel(); err != nil {
@@ -143,35 +139,26 @@
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
 	}
-	secondCtx := newContext(secondConfig, true)
-	secondArgs = cmdlineArgs
-	ninjaDeps := bootstrap.RunBlueprint(secondArgs, secondCtx.Context, secondConfig)
+	secondCtx := newContext(secondConfig)
+	ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, secondCtx.Context, secondConfig)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
 	globListFiles := writeBuildGlobsNinjaFile(secondCtx.SrcDir(), configuration.SoongOutDir(), secondCtx.Globs, configuration)
 	ninjaDeps = append(ninjaDeps, globListFiles...)
 
-	writeDepFile(secondArgs.OutFile, ninjaDeps)
+	writeDepFile(cmdlineArgs.OutFile, ninjaDeps)
 }
 
 // Run the code-generation phase to convert BazelTargetModules to BUILD files.
-func runQueryView(configuration android.Config, ctx *android.Context) {
+func runQueryView(queryviewDir, queryviewMarker string, configuration android.Config, ctx *android.Context) {
 	codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
-	absoluteQueryViewDir := shared.JoinPath(topDir, bazelQueryViewDir)
+	absoluteQueryViewDir := shared.JoinPath(topDir, queryviewDir)
 	if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
 	}
-}
 
-func runSoongDocs(configuration android.Config) {
-	ctx := newContext(configuration, false)
-	soongDocsArgs := cmdlineArgs
-	bootstrap.RunBlueprint(soongDocsArgs, ctx.Context, configuration)
-	if err := writeDocs(ctx, configuration, docFile); err != nil {
-		fmt.Fprintf(os.Stderr, "%s", err)
-		os.Exit(1)
-	}
+	touch(shared.JoinPath(topDir, queryviewMarker))
 }
 
 func writeMetrics(configuration android.Config) {
@@ -218,24 +205,33 @@
 // or the actual Soong build for the build.ninja file. Returns the top level
 // output file of the specific activity.
 func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string {
-	bazelConversionRequested := bp2buildMarker != ""
 	mixedModeBuild := configuration.BazelContext.BazelEnabled()
+	generateBazelWorkspace := bp2buildMarker != ""
 	generateQueryView := bazelQueryViewDir != ""
+	generateModuleGraphFile := moduleGraphFile != ""
+	generateDocFile := docFile != ""
 
 	blueprintArgs := cmdlineArgs
-	prepareBuildActions := !generateQueryView && moduleGraphFile == ""
-	if bazelConversionRequested {
+
+	var stopBefore bootstrap.StopBefore
+	if !generateModuleGraphFile && !generateQueryView && !generateDocFile {
+		stopBefore = bootstrap.DoEverything
+	} else {
+		stopBefore = bootstrap.StopBeforePrepareBuildActions
+	}
+
+	if generateBazelWorkspace {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
 		// Blueprint to BUILD files before everything else.
 		runBp2Build(configuration, extraNinjaDeps)
 		return bp2buildMarker
 	}
 
-	ctx := newContext(configuration, prepareBuildActions)
+	ctx := newContext(configuration)
 	if mixedModeBuild {
 		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
 	} else {
-		ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, ctx.Context, configuration)
+		ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, stopBefore, ctx.Context, configuration)
 		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
 		globListFiles := writeBuildGlobsNinjaFile(ctx.SrcDir(), configuration.SoongOutDir(), ctx.Globs, configuration)
@@ -243,12 +239,24 @@
 
 		// Convert the Soong module graph into Bazel BUILD files.
 		if generateQueryView {
-			runQueryView(configuration, ctx)
-			return cmdlineArgs.OutFile // TODO: This is a lie
-		} else if moduleGraphFile != "" {
+			queryviewMarkerFile := bazelQueryViewDir + ".marker"
+			runQueryView(bazelQueryViewDir, queryviewMarkerFile, configuration, ctx)
+			writeDepFile(queryviewMarkerFile, ninjaDeps)
+			return queryviewMarkerFile
+		} else if generateModuleGraphFile {
 			writeJsonModuleGraph(ctx, moduleGraphFile)
 			writeDepFile(moduleGraphFile, ninjaDeps)
 			return moduleGraphFile
+		} else if generateDocFile {
+			// TODO: we could make writeDocs() return the list of documentation files
+			// written and add them to the .d file. Then soong_docs would be re-run
+			// whenever one is deleted.
+			if err := writeDocs(ctx, shared.JoinPath(topDir, docFile)); err != nil {
+				fmt.Fprintf(os.Stderr, "error building Soong documentation: %s\n", err)
+				os.Exit(1)
+			}
+			writeDepFile(docFile, ninjaDeps)
+			return docFile
 		} else {
 			// The actual output (build.ninja) was written in the RunBlueprint() call
 			// above
@@ -296,7 +304,7 @@
 
 	availableEnv := parseAvailableEnv()
 
-	configuration := newConfig(outDir, availableEnv)
+	configuration := newConfig(availableEnv)
 	extraNinjaDeps := []string{
 		configuration.ProductVariablesFileName,
 		usedEnvFile,
@@ -312,16 +320,6 @@
 		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve"))
 	}
 
-	if docFile != "" {
-		// We don't write an used variables file when generating documentation
-		// because that is done from within the actual builds as a Ninja action and
-		// thus it would overwrite the actual used variables file so this is
-		// special-cased.
-		// TODO: Fix this by not passing --used_env to the soong_docs invocation
-		runSoongDocs(configuration)
-		return
-	}
-
 	finalOutputFile := doChosenActivity(configuration, extraNinjaDeps)
 	writeUsedEnvironmentFile(configuration, finalOutputFile)
 }
@@ -474,14 +472,11 @@
 
 	extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
 
-	// No need to generate Ninja build rules/statements from Modules and Singletons.
-	configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
-
 	// Run the loading and analysis pipeline to prepare the graph of regular
 	// Modules parsed from Android.bp files, and the BazelTargetModules mapped
 	// from the regular Modules.
 	blueprintArgs := cmdlineArgs
-	ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bp2buildCtx.Context, configuration)
+	ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
 	globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx.SrcDir(), configuration.SoongOutDir(), bp2buildCtx.Globs, configuration)
@@ -503,8 +498,8 @@
 		"bazel-" + filepath.Base(topDir),
 	}
 
-	if cmdlineArgs.NinjaBuildDir[0] != '/' {
-		excludes = append(excludes, cmdlineArgs.NinjaBuildDir)
+	if outDir[0] != '/' {
+		excludes = append(excludes, outDir)
 	}
 
 	existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index a8602de..d63ded5 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -15,22 +15,24 @@
 package main
 
 import (
-	"android/soong/android"
-	"android/soong/bp2build"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+
+	"android/soong/android"
+	"android/soong/bp2build"
 )
 
 func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error {
+	os.RemoveAll(bazelQueryViewDir)
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
 
-	// Ignore metrics reporting and compat layers for queryview, since queryview
-	// is already a full-repo conversion and can use data from bazel query
-	// directly.
-	buildToTargets, _, _ := bp2build.GenerateBazelTargets(ctx, true)
+	res, err := bp2build.GenerateBazelTargets(ctx, true)
+	if err != nil {
+		panic(err)
+	}
 
-	filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
+	filesToWrite := bp2build.CreateBazelFiles(ruleShims, res.BuildDirToTargets(), bp2build.QueryView)
 	for _, f := range filesToWrite {
 		if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
 			return err
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index b7c260c..d2fbed4 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -15,13 +15,14 @@
 package main
 
 import (
-	"android/soong/android"
 	"bytes"
 	"html/template"
 	"io/ioutil"
 	"path/filepath"
 	"sort"
 
+	"android/soong/android"
+
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/bootstrap/bpdoc"
 )
@@ -95,13 +96,13 @@
 	return result
 }
 
-func getPackages(ctx *android.Context, config interface{}) ([]*bpdoc.Package, error) {
+func getPackages(ctx *android.Context) ([]*bpdoc.Package, error) {
 	moduleTypeFactories := android.ModuleTypeFactoriesForDocs()
-	return bootstrap.ModuleTypeDocs(ctx.Context, config, moduleTypeFactories)
+	return bootstrap.ModuleTypeDocs(ctx.Context, moduleTypeFactories)
 }
 
-func writeDocs(ctx *android.Context, config interface{}, filename string) error {
-	packages, err := getPackages(ctx, config)
+func writeDocs(ctx *android.Context, filename string) error {
+	packages, err := getPackages(ctx)
 	if err != nil {
 		return err
 	}
@@ -371,6 +372,7 @@
     {{if .Properties -}}
       <div class="accordion"  id="{{getModule}}.{{.Name}}">
         <span class="fixed">&#x2295</span><b>{{.Name}}</b>
+        <i>{{.Type}}</i>
         {{- range .OtherNames -}}, {{.}}{{- end -}}
       </div>
       <div class="collapsible">
diff --git a/cuj/cuj.go b/cuj/cuj.go
index 413f423..c671f6f 100644
--- a/cuj/cuj.go
+++ b/cuj/cuj.go
@@ -138,6 +138,8 @@
 
 	cujDir := filepath.Join(outDir, "cuj_tests")
 
+	wd, _ := os.Getwd()
+	os.Setenv("TOP", wd)
 	// Use a subdirectory for the out directory for the tests to keep them isolated.
 	os.Setenv("OUT_DIR", filepath.Join(cujDir, "out"))
 
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 1401c75..7733c1b 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -110,17 +110,12 @@
 		return true
 	}
 
-	// Don't preopt system server jars that are updatable.
-	if global.ApexSystemServerJars.ContainsJar(module.Name) {
-		return true
-	}
-
 	// If OnlyPreoptBootImageAndSystemServer=true and module is not in boot class path skip
 	// Also preopt system server jars since selinux prevents system server from loading anything from
 	// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
 	// or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options.
 	if global.OnlyPreoptBootImageAndSystemServer && !global.BootJars.ContainsJar(module.Name) &&
-		!global.SystemServerJars.ContainsJar(module.Name) && !module.PreoptExtractedApk {
+		!AllSystemServerJars(ctx, global).ContainsJar(module.Name) && !module.PreoptExtractedApk {
 		return true
 	}
 
@@ -201,6 +196,14 @@
 	return profilePath
 }
 
+// Returns the dex location of a system server java library.
+func GetSystemServerDexLocation(global *GlobalConfig, lib string) string {
+	if apex := global.ApexSystemServerJars.ApexOfJar(lib); apex != "" {
+		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apex, lib)
+	}
+	return fmt.Sprintf("/system/framework/%s.jar", lib)
+}
+
 func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
 	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
 	appImage bool, generateDM bool) {
@@ -216,6 +219,13 @@
 	}
 
 	toOdexPath := func(path string) string {
+		if global.ApexSystemServerJars.ContainsJar(module.Name) {
+			return filepath.Join(
+				"/system/framework/oat",
+				arch.String(),
+				strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
+		}
+
 		return filepath.Join(
 			filepath.Dir(path),
 			"oat",
@@ -234,20 +244,21 @@
 
 	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
 
-	systemServerJars := NonApexSystemServerJars(ctx, global)
+	systemServerJars := AllSystemServerJars(ctx, global)
 
 	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
 	rule.Command().FlagWithOutput("rm -f ", odexPath)
 
-	if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
+	if jarIndex := systemServerJars.IndexOfJar(module.Name); 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.
 
 		var clcHost android.Paths
 		var clcTarget []string
-		for _, lib := range systemServerJars[:jarIndex] {
+		for i := 0; i < jarIndex; i++ {
+			lib := systemServerJars.Jar(i)
 			clcHost = append(clcHost, SystemServerDexJarHostPath(ctx, lib))
-			clcTarget = append(clcTarget, filepath.Join("/system/framework", lib+".jar"))
+			clcTarget = append(clcTarget, GetSystemServerDexLocation(global, lib))
 		}
 
 		// Copy the system server jar to a predefined location where dex2oat will find it.
@@ -362,7 +373,7 @@
 
 	if !android.PrefixInList(preoptFlags, "--compiler-filter=") {
 		var compilerFilter string
-		if global.SystemServerJars.ContainsJar(module.Name) {
+		if systemServerJars.ContainsJar(module.Name) {
 			// Jars of system server, use the product option if it is set, speed otherwise.
 			if global.SystemServerCompilerFilter != "" {
 				compilerFilter = global.SystemServerCompilerFilter
@@ -416,7 +427,7 @@
 
 	// PRODUCT_SYSTEM_SERVER_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
 	// PRODUCT_OTHER_JAVA_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
-	if global.SystemServerJars.ContainsJar(module.Name) {
+	if systemServerJars.ContainsJar(module.Name) {
 		if global.AlwaysSystemServerDebugInfo {
 			debugInfo = true
 		} else if global.NeverSystemServerDebugInfo {
@@ -518,14 +529,15 @@
 	}
 }
 
-var nonApexSystemServerJarsKey = android.NewOnceKey("nonApexSystemServerJars")
+var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars")
 
 // TODO: eliminate the superficial global config parameter by moving global config definition
 // from java subpackage to dexpreopt.
-func NonApexSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
-	return ctx.Config().Once(nonApexSystemServerJarsKey, func() interface{} {
-		return android.RemoveListFromList(global.SystemServerJars.CopyOfJars(), global.ApexSystemServerJars.CopyOfJars())
-	}).([]string)
+func AllSystemServerJars(ctx android.PathContext, global *GlobalConfig) *android.ConfiguredJarList {
+	return ctx.Config().Once(allSystemServerJarsKey, func() interface{} {
+		allSystemServerJars := global.SystemServerJars.AppendList(global.ApexSystemServerJars)
+		return &allSystemServerJars
+	}).(*android.ConfiguredJarList)
 }
 
 // A predefined location for the system server dex jars. This is needed in order to generate
@@ -551,12 +563,12 @@
 	mctx, isModule := ctx.(android.ModuleContext)
 	if isModule {
 		config := GetGlobalConfig(ctx)
-		jars := NonApexSystemServerJars(ctx, config)
+		jars := AllSystemServerJars(ctx, config)
 		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
-			depIndex := android.IndexList(dep.Name(), jars)
+			depIndex := jars.IndexOfJar(dep.Name())
 			if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
-				jar := jars[jarIndex]
-				dep := jars[depIndex]
+				jar := jars.Jar(jarIndex)
+				dep := jars.Jar(depIndex)
 				mctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
 					" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
 					" references from '%s' to '%s'.\n", jar, dep, jar, dep)
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index 7dbe74c..ba05d94 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -87,7 +87,9 @@
 		usage("--module configuration file is required")
 	}
 
-	ctx := &builderContext{android.NullConfig(*outDir)}
+	// NOTE: duplicating --out_dir here is incorrect (one should be the another
+	// plus "/soong" but doing so apparently breaks dexpreopt
+	ctx := &builderContext{android.NullConfig(*outDir, *outDir)}
 
 	globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath)
 	if err != nil {
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 4ee61b6..798d776 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -33,17 +33,35 @@
 }
 
 func testModuleConfig(ctx android.PathContext, name, partition string) *ModuleConfig {
+	return createTestModuleConfig(
+		name,
+		fmt.Sprintf("/%s/app/test/%s.apk", partition, name),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/dex/%s.jar", name, name)),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
+}
+
+func testApexModuleConfig(ctx android.PathContext, name, apexName string) *ModuleConfig {
+	return createTestModuleConfig(
+		name,
+		fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, name),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/dexpreopt/%s.jar", name, name)),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/aligned/%s.jar", name, name)),
+		android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
+}
+
+func createTestModuleConfig(name, dexLocation string, buildPath, dexPath, enforceUsesLibrariesStatusFile android.OutputPath) *ModuleConfig {
 	return &ModuleConfig{
 		Name:                            name,
-		DexLocation:                     fmt.Sprintf("/%s/app/test/%s.apk", partition, name),
-		BuildPath:                       android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)),
-		DexPath:                         android.PathForOutput(ctx, fmt.Sprintf("%s/dex/%s.jar", name, name)),
+		DexLocation:                     dexLocation,
+		BuildPath:                       buildPath,
+		DexPath:                         dexPath,
 		UncompressedDex:                 false,
 		HasApkLibraries:                 false,
 		PreoptFlags:                     nil,
 		ProfileClassListing:             android.OptionalPath{},
 		ProfileIsTextListing:            false,
-		EnforceUsesLibrariesStatusFile:  android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)),
+		EnforceUsesLibrariesStatusFile:  enforceUsesLibrariesStatusFile,
 		EnforceUsesLibraries:            false,
 		ClassLoaderContexts:             nil,
 		Archs:                           []android.ArchType{android.Arm},
@@ -140,6 +158,29 @@
 
 }
 
+func TestDexPreoptApexSystemServerJars(t *testing.T) {
+	config := android.TestConfig("out", nil, "", nil)
+	ctx := android.BuilderContextForTesting(config)
+	globalSoong := globalSoongConfigForTests()
+	global := GlobalConfigForTests(ctx)
+	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+
+	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
+		[]string{"com.android.apex1:service-A"})
+
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	wantInstalls := android.RuleBuilderInstalls{
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.odex"), "/system/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.odex"},
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.vdex"},
+	}
+
+	android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
+}
+
 func TestDexPreoptProfile(t *testing.T) {
 	config := android.TestConfig("out", nil, "", nil)
 	ctx := android.BuilderContextForTesting(config)
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 3213e5c..85abf59 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -519,13 +519,6 @@
 	return module
 }
 
-// Flags to be included in the snapshot
-type snapshotJsonFlags struct {
-	ModuleName          string `json:",omitempty"`
-	Filename            string `json:",omitempty"`
-	RelativeInstallPath string `json:",omitempty"`
-}
-
 // Copy file into the snapshot
 func copyFile(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath {
 	if fake {
@@ -612,7 +605,7 @@
 		snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "etc", m.BaseModuleName())
 		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, m.OutputFile(), snapshotLibOut, s.Fake))
 
-		prop := snapshotJsonFlags{}
+		prop := snapshot.SnapshotJsonFlags{}
 		propOut := snapshotLibOut + ".json"
 		prop.ModuleName = m.BaseModuleName()
 		if m.subdirProperties.Relative_install_path != nil {
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 29a8a39..73d807d 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -60,8 +60,8 @@
 	// https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
 	Vendor_boot *bool
 
-	// Optional kernel commandline
-	Cmdline *string `android:"arch_variant"`
+	// Optional kernel commandline arguments
+	Cmdline []string `android:"arch_variant"`
 
 	// File that contains bootconfig parameters. This can be set only when `vendor_boot` is true
 	// and `header_version` is greater than or equal to 4.
@@ -152,7 +152,7 @@
 	dtb := android.PathForModuleSrc(ctx, dtbName)
 	cmd.FlagWithInput("--dtb ", dtb)
 
-	cmdline := proptools.String(b.properties.Cmdline)
+	cmdline := strings.Join(b.properties.Cmdline, " ")
 	if cmdline != "" {
 		flag := "--cmdline "
 		if vendor {
diff --git a/java/aar.go b/java/aar.go
index afbaea2..13390db 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -486,6 +486,18 @@
 	exportedStaticPackages    android.Paths
 }
 
+var _ android.OutputFileProducer = (*AndroidLibrary)(nil)
+
+// For OutputFileProducer interface
+func (a *AndroidLibrary) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case ".aar":
+		return []android.Path{a.aarFile}, nil
+	default:
+		return a.Library.OutputFiles(tag)
+	}
+}
+
 func (a *AndroidLibrary) ExportedProguardFlagFiles() android.Paths {
 	return a.exportedProguardFlagFiles
 }
diff --git a/java/androidmk.go b/java/androidmk.go
index 68ccd82..71370c9 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -61,7 +61,13 @@
 	var entriesList []android.AndroidMkEntries
 
 	if library.hideApexVariantFromMake {
-		// For a java library built for an APEX we don't need Make module
+		// For a java library built for an APEX, we don't need a Make module for itself. Otherwise, it
+		// will conflict with the platform variant because they have the same module name in the
+		// makefile. However, we need to add its dexpreopt outputs as sub-modules, if it is preopted.
+		dexpreoptEntries := library.dexpreopter.AndroidMkEntriesForApex()
+		if len(dexpreoptEntries) > 0 {
+			entriesList = append(entriesList, dexpreoptEntries...)
+		}
 		entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true})
 	} else if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) {
 		// Platform variant.  If not available for the platform, we don't need Make module.
diff --git a/java/app.go b/java/app.go
index 5104f07..a62e442 100755
--- a/java/app.go
+++ b/java/app.go
@@ -760,18 +760,18 @@
 				}
 
 				lib := dep.OutputFile()
-				path := lib.Path()
-				if seenModulePaths[path.String()] {
-					return false
-				}
-				seenModulePaths[path.String()] = true
-
-				if checkNativeSdkVersion && dep.SdkVersion() == "" {
-					ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not",
-						otherName)
-				}
-
 				if lib.Valid() {
+					path := lib.Path()
+					if seenModulePaths[path.String()] {
+						return false
+					}
+					seenModulePaths[path.String()] = true
+
+					if checkNativeSdkVersion && dep.SdkVersion() == "" {
+						ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not",
+							otherName)
+					}
+
 					jniLibs = append(jniLibs, jniLib{
 						name:           ctx.OtherModuleName(module),
 						path:           path,
diff --git a/java/app_import.go b/java/app_import.go
index b5a6084..3e5f972 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -204,9 +204,9 @@
 		return false
 	}
 
-	// Uncompress dex in APKs of privileged apps
-	if ctx.Config().UncompressPrivAppDex() && a.Privileged() {
-		return true
+	// Uncompress dex in APKs of priv-apps if and only if DONT_UNCOMPRESS_PRIV_APPS_DEXS is false.
+	if a.Privileged() {
+		return ctx.Config().UncompressPrivAppDex()
 	}
 
 	return shouldUncompressDex(ctx, &a.dexpreopter)
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 024a3df..efa52c1 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"fmt"
 	"reflect"
 	"regexp"
 	"strings"
@@ -656,3 +657,74 @@
 		}
 	}
 }
+
+func TestAndroidTestImport_UncompressDex(t *testing.T) {
+	testCases := []struct {
+		name string
+		bp   string
+	}{
+		{
+			name: "normal",
+			bp: `
+				android_app_import {
+					name: "foo",
+					presigned: true,
+					apk: "prebuilts/apk/app.apk",
+				}
+			`,
+		},
+		{
+			name: "privileged",
+			bp: `
+				android_app_import {
+					name: "foo",
+					presigned: true,
+					privileged: true,
+					apk: "prebuilts/apk/app.apk",
+				}
+			`,
+		},
+	}
+
+	test := func(t *testing.T, bp string, unbundled bool, dontUncompressPrivAppDexs bool) {
+		t.Helper()
+
+		result := android.GroupFixturePreparers(
+			prepareForJavaTest,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				if unbundled {
+					variables.Unbundled_build = proptools.BoolPtr(true)
+				}
+				variables.UncompressPrivAppDex = proptools.BoolPtr(!dontUncompressPrivAppDexs)
+			}),
+		).RunTestWithBp(t, bp)
+
+		foo := result.ModuleForTests("foo", "android_common")
+		actual := foo.MaybeRule("uncompress-dex").Rule != nil
+
+		expect := !unbundled
+		if strings.Contains(bp, "privileged: true") {
+			if dontUncompressPrivAppDexs {
+				expect = false
+			} else {
+				// TODO(b/194504107): shouldn't priv-apps be always uncompressed unless
+				// DONT_UNCOMPRESS_PRIV_APPS_DEXS is true (regardless of unbundling)?
+				// expect = true
+			}
+		}
+
+		android.AssertBoolEquals(t, "uncompress dex", expect, actual)
+	}
+
+	for _, unbundled := range []bool{false, true} {
+		for _, dontUncompressPrivAppDexs := range []bool{false, true} {
+			for _, tt := range testCases {
+				name := fmt.Sprintf("%s,unbundled:%t,dontUncompressPrivAppDexs:%t",
+					tt.name, unbundled, dontUncompressPrivAppDexs)
+				t.Run(name, func(t *testing.T) {
+					test(t, tt.bp, unbundled, dontUncompressPrivAppDexs)
+				})
+			}
+		}
+	}
+}
diff --git a/java/base.go b/java/base.go
index 86022c3..78aaa19 100644
--- a/java/base.go
+++ b/java/base.go
@@ -382,7 +382,7 @@
 		return nil
 	}
 	if sdkVersion.Kind == android.SdkCorePlatform {
-		if useLegacyCorePlatformApiByName(j.BaseModuleName()) {
+		if useLegacyCorePlatformApi(ctx, j.BaseModuleName()) {
 			return fmt.Errorf("non stable SDK %v - uses legacy core platform", sdkVersion)
 		} else {
 			// Treat stable core platform as stable.
@@ -643,6 +643,11 @@
 	} else if j.shouldInstrumentStatic(ctx) {
 		ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
 	}
+
+	if j.useCompose() {
+		ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag,
+			"androidx.compose.compiler_compiler-hosted")
+	}
 }
 
 func hasSrcExt(srcs []string, ext string) bool {
@@ -911,6 +916,12 @@
 		if ctx.Device() {
 			kotlincFlags = append(kotlincFlags, "-no-jdk")
 		}
+
+		for _, plugin := range deps.kotlinPlugins {
+			kotlincFlags = append(kotlincFlags, "-Xplugin="+plugin.String())
+		}
+		flags.kotlincDeps = append(flags.kotlincDeps, deps.kotlinPlugins...)
+
 		if len(kotlincFlags) > 0 {
 			// optimization.
 			ctx.Variable(pctx, "kotlincFlags", strings.Join(kotlincFlags, " "))
@@ -1325,6 +1336,10 @@
 	j.outputFile = outputFile.WithoutRel()
 }
 
+func (j *Module) useCompose() bool {
+	return android.InList("androidx.compose.runtime_runtime", j.properties.Static_libs)
+}
+
 // Returns a copy of the supplied flags, but with all the errorprone-related
 // fields copied to the regular build's fields.
 func enableErrorproneFlags(flags javaBuilderFlags) javaBuilderFlags {
@@ -1755,6 +1770,8 @@
 				deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...)
 			case kotlinAnnotationsTag:
 				deps.kotlinAnnotations = dep.HeaderJars
+			case kotlinPluginTag:
+				deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...)
 			case syspropPublicStubDepTag:
 				// This is a sysprop implementation library, forward the JavaInfoProvider from
 				// the corresponding sysprop public stub library as SyspropPublicStubInfoProvider.
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index 4108770..52ce77d 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -95,15 +95,6 @@
 	if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) {
 		ctx.AddVariationDependencies(variations, tag, prebuiltName)
 		addedDep = true
-	} else if ctx.Config().AlwaysUsePrebuiltSdks() && len(variations) > 0 {
-		// TODO(b/179354495): Remove this code path once the Android build has been fully migrated to
-		//  use bootclasspath_fragment properly.
-		// Some prebuilt java_sdk_library modules do not yet have an APEX variations so try and add a
-		// dependency on the non-APEX variant.
-		if ctx.OtherModuleDependencyVariantExists(nil, prebuiltName) {
-			ctx.AddVariationDependencies(nil, tag, prebuiltName)
-			addedDep = true
-		}
 	}
 
 	// If no appropriate variant existing for this, so no dependency could be added, then it is an
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 7577316..b13e2db 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -89,7 +89,7 @@
 
 var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContentDepTag
 var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
-var _ android.SdkMemberTypeDependencyTag = bootclasspathFragmentContentDepTag
+var _ android.SdkMemberDependencyTag = bootclasspathFragmentContentDepTag
 var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag
 var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag
 
@@ -718,8 +718,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (b *bootclasspathFragmentMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (b *bootclasspathFragmentMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (b *bootclasspathFragmentMemberType) IsInstance(module android.Module) bool {
diff --git a/java/builder.go b/java/builder.go
index ea011b8..ae124a3 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -263,6 +263,7 @@
 
 	kotlincFlags     string
 	kotlincClasspath classpath
+	kotlincDeps      android.Paths
 
 	proto android.ProtoFlags
 }
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index 51d998a..b198c24 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -24,6 +24,10 @@
 // core libraries.
 //
 // Don't use this directly, use "sdk_version: core_current".
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 java_library {
     name: "core.current.stubs",
     visibility: ["//visibility:public"],
diff --git a/java/dex.go b/java/dex.go
index 667800f..8045b5c 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -32,6 +32,9 @@
 	// list of module-specific flags that will be used for dex compiles
 	Dxflags []string `android:"arch_variant"`
 
+	// A list of files containing rules that specify the classes to keep in the main dex file.
+	Main_dex_rules []string `android:"path"`
+
 	Optimize struct {
 		// If false, disable all optimization.  Defaults to true for android_app and android_test
 		// modules, false for java_library and java_test modules.
@@ -164,13 +167,20 @@
 	}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
 		"r8Flags", "zipFlags", "tmpJar"}, []string{"implicits"})
 
-func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android.SdkSpec) []string {
-	flags := d.dexProperties.Dxflags
+func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
+	minSdkVersion android.SdkSpec) (flags []string, deps android.Paths) {
+
+	flags = d.dexProperties.Dxflags
 	// Translate all the DX flags to D8 ones until all the build files have been migrated
 	// to D8 flags. See: b/69377755
 	flags = android.RemoveListFromList(flags,
 		[]string{"--core-library", "--dex", "--multi-dex"})
 
+	for _, f := range android.PathsForModuleSrc(ctx, d.dexProperties.Main_dex_rules) {
+		flags = append(flags, "--main-dex-rules", f.String())
+		deps = append(deps, f)
+	}
+
 	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" {
 		flags = append(flags, "--debug")
 	}
@@ -187,7 +197,7 @@
 	}
 
 	flags = append(flags, "--min-api "+strconv.Itoa(effectiveVersion.FinalOrFutureInt()))
-	return flags
+	return flags, deps
 }
 
 func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) {
@@ -286,7 +296,7 @@
 		zipFlags += " -L 0"
 	}
 
-	commonFlags := d.dexCommonFlags(ctx, minSdkVersion)
+	commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion)
 
 	useR8 := d.effectiveOptimizeEnabled()
 	if useR8 {
@@ -298,6 +308,7 @@
 		proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip")
 		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
 		r8Flags, r8Deps := d.r8Flags(ctx, flags)
+		r8Deps = append(r8Deps, commonDeps...)
 		rule := r8
 		args := map[string]string{
 			"r8Flags":     strings.Join(append(commonFlags, r8Flags...), " "),
@@ -324,6 +335,7 @@
 		})
 	} else {
 		d8Flags, d8Deps := d8Flags(flags)
+		d8Deps = append(d8Deps, commonDeps...)
 		rule := d8
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
 			rule = d8RE
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 0faae36..e9dc982 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -15,13 +15,46 @@
 package java
 
 import (
+	"path/filepath"
+	"strings"
+
 	"android/soong/android"
 	"android/soong/dexpreopt"
 )
 
-type dexpreopterInterface interface {
+type DexpreopterInterface interface {
 	IsInstallable() bool // Structs that embed dexpreopter must implement this.
 	dexpreoptDisabled(ctx android.BaseModuleContext) bool
+	DexpreoptBuiltInstalledForApex() []dexpreopterInstall
+	AndroidMkEntriesForApex() []android.AndroidMkEntries
+}
+
+type dexpreopterInstall struct {
+	// A unique name to distinguish an output from others for the same java library module. Usually in
+	// the form of `<arch>-<encoded-path>.odex/vdex/art`.
+	name string
+
+	// The name of the input java module.
+	moduleName string
+
+	// The path to the dexpreopt output on host.
+	outputPathOnHost android.Path
+
+	// The directory on the device for the output to install to.
+	installDirOnDevice android.InstallPath
+
+	// The basename (the last segment of the path) for the output to install as.
+	installFileOnDevice string
+}
+
+// The full module name of the output in the makefile.
+func (install *dexpreopterInstall) FullModuleName() string {
+	return install.moduleName + install.SubModuleName()
+}
+
+// The sub-module name of the output in the makefile (the name excluding the java module name).
+func (install *dexpreopterInstall) SubModuleName() string {
+	return "-dexpreopt-" + install.name
 }
 
 type dexpreopter struct {
@@ -39,7 +72,9 @@
 	enforceUsesLibs     bool
 	classLoaderContexts dexpreopt.ClassLoaderContextMap
 
-	builtInstalled string
+	// See the `dexpreopt` function for details.
+	builtInstalled        string
+	builtInstalledForApex []dexpreopterInstall
 
 	// The config is used for two purposes:
 	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
@@ -74,6 +109,17 @@
 	dexpreopt.DexpreoptRunningInSoong = true
 }
 
+func isApexVariant(ctx android.BaseModuleContext) bool {
+	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	return !apexInfo.IsForPlatform()
+}
+
+func moduleName(ctx android.BaseModuleContext) string {
+	// Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not
+	// expected by dexpreopter.
+	return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
+}
+
 func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
@@ -81,7 +127,7 @@
 		return true
 	}
 
-	if inList(ctx.ModuleName(), global.DisablePreoptModules) {
+	if inList(moduleName(ctx), global.DisablePreoptModules) {
 		return true
 	}
 
@@ -93,7 +139,7 @@
 		return true
 	}
 
-	if !ctx.Module().(dexpreopterInterface).IsInstallable() {
+	if !ctx.Module().(DexpreopterInterface).IsInstallable() {
 		return true
 	}
 
@@ -101,8 +147,20 @@
 		return true
 	}
 
-	// Don't preopt APEX variant module
-	if apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo); !apexInfo.IsForPlatform() {
+	if isApexVariant(ctx) {
+		// Don't preopt APEX variant module unless the module is an APEX system server jar and we are
+		// building the entire system image.
+		if !global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) || ctx.Config().UnbundledBuild() {
+			return true
+		}
+	} else {
+		// Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
+		if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+			return true
+		}
+	}
+
+	if !android.IsModulePreferred(ctx.Module()) {
 		return true
 	}
 
@@ -112,17 +170,40 @@
 }
 
 func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
-	if d, ok := ctx.Module().(dexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
+	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
 		return
 	}
 	dexpreopt.RegisterToolDeps(ctx)
 }
 
-func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
-	return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
+func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
+	return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
+}
+
+// Returns the install path of the dex jar of a module.
+//
+// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather
+// than the `name` in the path `/apex/<name>` as suggested in its comment.
+//
+// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a
+// system server jar, which is fine because we currently only preopt system server jars for APEXes.
+func (d *dexpreopter) getInstallPath(
+	ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+		dexLocation := dexpreopt.GetSystemServerDexLocation(global, moduleName(ctx))
+		return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/"))
+	}
+	if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) &&
+		filepath.Base(defaultInstallPath.PartitionDir()) != "apex" {
+		ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt")
+	}
+	return defaultInstallPath
 }
 
 func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
+	global := dexpreopt.GetGlobalConfig(ctx)
+
 	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
 	// the dexpreopter struct hasn't been fully initialized before we're called,
 	// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
@@ -133,7 +214,7 @@
 
 	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
 
-	providesUsesLib := ctx.ModuleName()
+	providesUsesLib := moduleName(ctx)
 	if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
 		name := ulib.ProvidesUsesLib()
 		if name != nil {
@@ -147,17 +228,15 @@
 		return
 	}
 
-	global := dexpreopt.GetGlobalConfig(ctx)
-
-	isSystemServerJar := global.SystemServerJars.ContainsJar(ctx.ModuleName())
+	isSystemServerJar := global.SystemServerJars.ContainsJar(moduleName(ctx)) ||
+		global.ApexSystemServerJars.ContainsJar(moduleName(ctx))
 
 	bootImage := defaultBootImageConfig(ctx)
 	if global.UseArtImage {
 		bootImage = artBootImageConfig(ctx)
 	}
 
-	// System server jars are an exception: they are dexpreopted without updatable bootclasspath.
-	dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp && !isSystemServerJar)
+	dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
 
 	targets := ctx.MultiTargets()
 	if len(targets) == 0 {
@@ -199,15 +278,15 @@
 			profileIsTextListing = true
 		} else if global.ProfileDir != "" {
 			profileClassListing = android.ExistentPathForSource(ctx,
-				global.ProfileDir, ctx.ModuleName()+".prof")
+				global.ProfileDir, moduleName(ctx)+".prof")
 		}
 	}
 
 	// Full dexpreopt config, used to create dexpreopt build rules.
 	dexpreoptConfig := &dexpreopt.ModuleConfig{
-		Name:            ctx.ModuleName(),
+		Name:            moduleName(ctx),
 		DexLocation:     dexLocation,
-		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath,
+		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath,
 		DexPath:         dexJarFile,
 		ManifestPath:    android.OptionalPathForPath(d.manifestFile),
 		UncompressedDex: d.uncompressedDex,
@@ -256,5 +335,53 @@
 
 	dexpreoptRule.Build("dexpreopt", "dexpreopt")
 
-	d.builtInstalled = dexpreoptRule.Installs().String()
+	if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+		// APEX variants of java libraries are hidden from Make, so their dexpreopt outputs need special
+		// handling. Currently, for APEX variants of java libraries, only those in the system server
+		// classpath are handled here. Preopting of boot classpath jars in the ART APEX are handled in
+		// java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
+		for _, install := range dexpreoptRule.Installs() {
+			// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
+			installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
+			installBase := filepath.Base(install.To)
+			arch := filepath.Base(installDir)
+			installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+			// The installs will be handled by Make as sub-modules of the java library.
+			d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
+				name:                arch + "-" + installBase,
+				moduleName:          moduleName(ctx),
+				outputPathOnHost:    install.From,
+				installDirOnDevice:  installPath,
+				installFileOnDevice: installBase,
+			})
+		}
+	} else {
+		// The installs will be handled by Make as LOCAL_SOONG_BUILT_INSTALLED of the java library
+		// module.
+		d.builtInstalled = dexpreoptRule.Installs().String()
+	}
+}
+
+func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
+	return d.builtInstalledForApex
+}
+
+func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
+	var entries []android.AndroidMkEntries
+	for _, install := range d.builtInstalledForApex {
+		install := install
+		entries = append(entries, android.AndroidMkEntries{
+			Class:      "ETC",
+			SubName:    install.SubModuleName(),
+			OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
+			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+					entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.ToMakePath().String())
+					entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
+					entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
+				},
+			},
+		})
+	}
+	return entries
 }
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 1019b4c..946092c 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -500,7 +500,11 @@
 		dst := dstBootJarsByModule[name]
 
 		if src == nil {
-			ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+			if !ctx.Config().AllowMissingDependencies() {
+				ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+			} else {
+				ctx.AddMissingDependencies([]string{name})
+			}
 		} else if dst == nil {
 			ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
 		} else {
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 8dc7b79..1c1070a 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"runtime"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -24,11 +25,17 @@
 	"android/soong/dexpreopt"
 )
 
+func init() {
+	RegisterFakeRuntimeApexMutator()
+}
+
 func TestDexpreoptEnabled(t *testing.T) {
 	tests := []struct {
-		name    string
-		bp      string
-		enabled bool
+		name        string
+		bp          string
+		moduleName  string
+		apexVariant bool
+		enabled     bool
 	}{
 		{
 			name: "app",
@@ -148,13 +155,81 @@
 				}`,
 			enabled: true,
 		},
+		{
+			name: "apex variant",
+			bp: `
+				java_library {
+					name: "foo",
+					installable: true,
+					srcs: ["a.java"],
+					apex_available: ["com.android.apex1"],
+				}`,
+			apexVariant: true,
+			enabled:     false,
+		},
+		{
+			name: "apex variant of apex system server jar",
+			bp: `
+				java_library {
+					name: "service-foo",
+					installable: true,
+					srcs: ["a.java"],
+					apex_available: ["com.android.apex1"],
+				}`,
+			moduleName:  "service-foo",
+			apexVariant: true,
+			enabled:     true,
+		},
+		{
+			name: "apex variant of prebuilt apex system server jar",
+			bp: `
+				java_library {
+					name: "prebuilt_service-foo",
+					installable: true,
+					srcs: ["a.java"],
+					apex_available: ["com.android.apex1"],
+				}`,
+			moduleName:  "prebuilt_service-foo",
+			apexVariant: true,
+			enabled:     true,
+		},
+		{
+			name: "platform variant of apex system server jar",
+			bp: `
+				java_library {
+					name: "service-foo",
+					installable: true,
+					srcs: ["a.java"],
+					apex_available: ["com.android.apex1"],
+				}`,
+			moduleName:  "service-foo",
+			apexVariant: false,
+			enabled:     false,
+		},
 	}
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			ctx, _ := testJava(t, test.bp)
+			preparers := android.GroupFixturePreparers(
+				PrepareForTestWithJavaDefaultModules,
+				PrepareForTestWithFakeApexMutator,
+				dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
+			)
 
-			dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
+			result := preparers.RunTestWithBp(t, test.bp)
+			ctx := result.TestContext
+
+			moduleName := "foo"
+			if test.moduleName != "" {
+				moduleName = test.moduleName
+			}
+
+			variant := "android_common"
+			if test.apexVariant {
+				variant += "_apex1000"
+			}
+
+			dexpreopt := ctx.ModuleForTests(moduleName, variant).MaybeRule("dexpreopt")
 			enabled := dexpreopt.Rule != nil
 
 			if enabled != test.enabled {
@@ -220,3 +295,145 @@
 	testDex2oatToolDep(true, true, true, prebuiltDex2oatPath)
 	testDex2oatToolDep(false, true, false, prebuiltDex2oatPath)
 }
+
+func TestDexpreoptBuiltInstalledForApex(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithFakeApexMutator,
+		dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
+	)
+
+	// An APEX system server jar.
+	result := preparers.RunTestWithBp(t, `
+		java_library {
+			name: "service-foo",
+			installable: true,
+			srcs: ["a.java"],
+			apex_available: ["com.android.apex1"],
+		}`)
+	ctx := result.TestContext
+	module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
+	library := module.Module().(*Library)
+
+	installs := library.dexpreopter.DexpreoptBuiltInstalledForApex()
+
+	android.AssertIntEquals(t, "install count", 2, len(installs))
+
+	android.AssertStringEquals(t, "installs[0] FullModuleName",
+		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+		installs[0].FullModuleName())
+
+	android.AssertStringEquals(t, "installs[0] SubModuleName",
+		"-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+		installs[0].SubModuleName())
+
+	android.AssertStringEquals(t, "installs[1] FullModuleName",
+		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+		installs[1].FullModuleName())
+
+	android.AssertStringEquals(t, "installs[1] SubModuleName",
+		"-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+		installs[1].SubModuleName())
+
+	// Not an APEX system server jar.
+	result = preparers.RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			installable: true,
+			srcs: ["a.java"],
+		}`)
+	ctx = result.TestContext
+	module = ctx.ModuleForTests("foo", "android_common")
+	library = module.Module().(*Library)
+
+	installs = library.dexpreopter.DexpreoptBuiltInstalledForApex()
+
+	android.AssertIntEquals(t, "install count", 0, len(installs))
+}
+
+func filterDexpreoptEntriesList(entriesList []android.AndroidMkEntries) []android.AndroidMkEntries {
+	var results []android.AndroidMkEntries
+	for _, entries := range entriesList {
+		if strings.Contains(entries.EntryMap["LOCAL_MODULE"][0], "-dexpreopt-") {
+			results = append(results, entries)
+		}
+	}
+	return results
+}
+
+func verifyEntries(t *testing.T, message string, expectedModule string,
+	expectedPrebuiltModuleFile string, expectedModulePath string, expectedInstalledModuleStem string,
+	entries android.AndroidMkEntries) {
+	android.AssertStringEquals(t, message+" LOCAL_MODULE", expectedModule,
+		entries.EntryMap["LOCAL_MODULE"][0])
+
+	android.AssertStringEquals(t, message+" LOCAL_MODULE_CLASS", "ETC",
+		entries.EntryMap["LOCAL_MODULE_CLASS"][0])
+
+	android.AssertStringDoesContain(t, message+" LOCAL_PREBUILT_MODULE_FILE",
+		entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"][0], expectedPrebuiltModuleFile)
+
+	android.AssertStringDoesContain(t, message+" LOCAL_MODULE_PATH",
+		entries.EntryMap["LOCAL_MODULE_PATH"][0], expectedModulePath)
+
+	android.AssertStringEquals(t, message+" LOCAL_INSTALLED_MODULE_STEM",
+		expectedInstalledModuleStem, entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"][0])
+
+	android.AssertStringEquals(t, message+" LOCAL_NOT_AVAILABLE_FOR_PLATFORM",
+		"false", entries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"][0])
+}
+
+func TestAndroidMkEntriesForApex(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithFakeApexMutator,
+		dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
+	)
+
+	// An APEX system server jar.
+	result := preparers.RunTestWithBp(t, `
+		java_library {
+			name: "service-foo",
+			installable: true,
+			srcs: ["a.java"],
+			apex_available: ["com.android.apex1"],
+		}`)
+	ctx := result.TestContext
+	module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
+
+	entriesList := android.AndroidMkEntriesForTest(t, ctx, module.Module())
+	entriesList = filterDexpreoptEntriesList(entriesList)
+
+	android.AssertIntEquals(t, "entries count", 2, len(entriesList))
+
+	verifyEntries(t,
+		"entriesList[0]",
+		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+		"/dexpreopt/oat/arm64/javalib.odex",
+		"/system/framework/oat/arm64",
+		"apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+		entriesList[0])
+
+	verifyEntries(t,
+		"entriesList[1]",
+		"service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+		"/dexpreopt/oat/arm64/javalib.vdex",
+		"/system/framework/oat/arm64",
+		"apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+		entriesList[1])
+
+	// Not an APEX system server jar.
+	result = preparers.RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			installable: true,
+			srcs: ["a.java"],
+		}`)
+	ctx = result.TestContext
+	module = ctx.ModuleForTests("foo", "android_common")
+
+	entriesList = android.AndroidMkEntriesForTest(t, ctx, module.Module())
+	entriesList = filterDexpreoptEntriesList(entriesList)
+
+	android.AssertIntEquals(t, "entries count", 0, len(entriesList))
+}
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 8e39f40..1c6fbac 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -218,7 +218,7 @@
 var _ android.ExcludeFromVisibilityEnforcementTag = hiddenAPIStubsDependencyTag{}
 var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{}
 var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{}
-var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{}
+var _ android.SdkMemberDependencyTag = hiddenAPIStubsDependencyTag{}
 
 // hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs
 // needed to produce the hidden API monolithic stub flags file.
@@ -1194,13 +1194,6 @@
 // deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by
 // Soong but should instead only be reported in ninja if the file is actually built.
 func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool {
-	// TODO(b/179354495): Remove this workaround when it is unnecessary.
-	// Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So,
-	// create a fake one that will cause a build error only if it is used.
-	if ctx.Config().AlwaysUsePrebuiltSdks() {
-		return true
-	}
-
 	// Any missing dependency should be allowed.
 	if ctx.Config().AllowMissingDependencies() {
 		return true
diff --git a/java/java.go b/java/java.go
index 4c2ca9b..2ca4ac8 100644
--- a/java/java.go
+++ b/java/java.go
@@ -286,6 +286,7 @@
 	frameworkResTag         = dependencyTag{name: "framework-res"}
 	kotlinStdlibTag         = dependencyTag{name: "kotlin-stdlib"}
 	kotlinAnnotationsTag    = dependencyTag{name: "kotlin-annotations"}
+	kotlinPluginTag         = dependencyTag{name: "kotlin-plugin"}
 	proguardRaiseTag        = dependencyTag{name: "proguard-raise"}
 	certificateTag          = dependencyTag{name: "certificate"}
 	instrumentationForTag   = dependencyTag{name: "instrumentation_for"}
@@ -380,6 +381,7 @@
 	aidlPreprocess          android.OptionalPath
 	kotlinStdlib            android.Paths
 	kotlinAnnotations       android.Paths
+	kotlinPlugins           android.Paths
 
 	disableTurbine bool
 }
@@ -487,7 +489,7 @@
 	}
 
 	// Store uncompressed dex files that are preopted on /system.
-	if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !odexOnSystemOther(ctx, dexpreopter.installPath)) {
+	if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !dexpreopter.odexOnSystemOther(ctx, dexpreopter.installPath)) {
 		return true
 	}
 	if ctx.Config().UncompressPrivAppDex() &&
@@ -508,7 +510,8 @@
 	}
 
 	j.checkSdkVersions(ctx)
-	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
+	j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
+		ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
 	j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
 	if j.dexProperties.Uncompress_dex == nil {
 		// If the value was not force-set by the user, use reasonable default based on the module.
@@ -574,8 +577,8 @@
 	copyEverythingToSnapshot = false
 )
 
-func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (mt *librarySdkMemberType) IsInstance(module android.Module) bool {
@@ -875,8 +878,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (mt *testSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (mt *testSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (mt *testSdkMemberType) IsInstance(module android.Module) bool {
@@ -1368,7 +1371,8 @@
 
 			// Dex compilation
 
-			j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
+			j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
+				ctx, android.PathForModuleInstall(ctx, "framework", jarName))
 			if j.dexProperties.Uncompress_dex == nil {
 				// If the value was not force-set by the user, use reasonable default based on the module.
 				j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
@@ -1509,7 +1513,7 @@
 	return Bool(j.properties.Installable)
 }
 
-var _ dexpreopterInterface = (*Import)(nil)
+var _ DexpreopterInterface = (*Import)(nil)
 
 // java_import imports one or more `.jar` files into the build graph as if they were built by a java_library module.
 //
@@ -1622,7 +1626,8 @@
 		j.hideApexVariantFromMake = true
 	}
 
-	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
+	j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
+		ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
 	j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
 
 	inputJar := ctx.ExpandSource(j.properties.Jars[0], "jars")
diff --git a/java/kotlin.go b/java/kotlin.go
index 3a6fc0f..e4f1bc1 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -81,6 +81,7 @@
 
 	var deps android.Paths
 	deps = append(deps, flags.kotlincClasspath...)
+	deps = append(deps, flags.kotlincDeps...)
 	deps = append(deps, srcJars...)
 	deps = append(deps, commonSrcFiles...)
 
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index db30696..cac0af3 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -281,3 +281,46 @@
 		})
 	}
 }
+
+func TestKotlinCompose(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		java_library {
+			name: "androidx.compose.runtime_runtime",
+		}
+
+		java_library_host {
+			name: "androidx.compose.compiler_compiler-hosted",
+		}
+
+		java_library {
+			name: "withcompose",
+			srcs: ["a.kt"],
+			static_libs: ["androidx.compose.runtime_runtime"],
+		}
+
+		java_library {
+			name: "nocompose",
+			srcs: ["a.kt"],
+		}
+	`)
+
+	buildOS := result.Config.BuildOS.String()
+
+	composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted", buildOS+"_common").Rule("combineJar").Output
+	withCompose := result.ModuleForTests("withcompose", "android_common")
+	noCompose := result.ModuleForTests("nocompose", "android_common")
+
+	android.AssertStringListContains(t, "missing compose compiler dependency",
+		withCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String())
+
+	android.AssertStringDoesContain(t, "missing compose compiler plugin",
+		withCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String())
+
+	android.AssertStringListDoesNotContain(t, "unexpected compose compiler dependency",
+		noCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String())
+
+	android.AssertStringDoesNotContain(t, "unexpected compose compiler plugin",
+		noCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String())
+}
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 8c401a7..7749310 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -163,17 +163,27 @@
 	}
 }
 
-func useLegacyCorePlatformApi(ctx android.EarlyModuleContext) bool {
-	return useLegacyCorePlatformApiByName(ctx.ModuleName())
+var legacyCorePlatformApiLookupKey = android.NewOnceKey("legacyCorePlatformApiLookup")
+
+func getLegacyCorePlatformApiLookup(config android.Config) map[string]struct{} {
+	return config.Once(legacyCorePlatformApiLookupKey, func() interface{} {
+		return legacyCorePlatformApiLookup
+	}).(map[string]struct{})
 }
 
-func useLegacyCorePlatformApiByName(name string) bool {
-	_, found := legacyCorePlatformApiLookup[name]
+// useLegacyCorePlatformApi checks to see whether the supplied module name is in the list of modules
+// that are able to use the legacy core platform API and returns true if it does, false otherwise.
+//
+// This method takes the module name separately from the context as this may be being called for a
+// module that is not the target of the supplied context.
+func useLegacyCorePlatformApi(ctx android.EarlyModuleContext, moduleName string) bool {
+	lookup := getLegacyCorePlatformApiLookup(ctx.Config())
+	_, found := lookup[moduleName]
 	return found
 }
 
 func corePlatformSystemModules(ctx android.EarlyModuleContext) string {
-	if useLegacyCorePlatformApi(ctx) {
+	if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) {
 		return config.LegacyCorePlatformSystemModules
 	} else {
 		return config.StableCorePlatformSystemModules
@@ -181,7 +191,7 @@
 }
 
 func corePlatformBootclasspathLibraries(ctx android.EarlyModuleContext) []string {
-	if useLegacyCorePlatformApi(ctx) {
+	if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) {
 		return config.LegacyCorePlatformBootclasspathLibraries
 	} else {
 		return config.StableCorePlatformBootclasspathLibraries
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 712c2a2..0d8ebac 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -134,8 +134,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (b *compatConfigMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (b *compatConfigMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (b *compatConfigMemberType) IsInstance(module android.Module) bool {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index c50e077..2d8aef7 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -376,6 +376,9 @@
 }
 
 type sdkLibraryProperties struct {
+	// List of source files that are needed to compile the API, but are not part of runtime library.
+	Api_srcs []string `android:"arch_variant"`
+
 	// Visibility for impl library module. If not specified then defaults to the
 	// visibility property.
 	Impl_library_visibility []string
@@ -1438,6 +1441,7 @@
 	props.Name = proptools.StringPtr(name)
 	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility)
 	props.Srcs = append(props.Srcs, module.properties.Srcs...)
+	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
 	props.Sdk_version = module.deviceProperties.Sdk_version
 	props.System_modules = module.deviceProperties.System_modules
 	props.Installable = proptools.BoolPtr(false)
@@ -1907,6 +1911,7 @@
 	android.SdkBase
 
 	hiddenAPI
+	dexpreopter
 
 	properties sdkLibraryImportProperties
 
@@ -2111,6 +2116,14 @@
 	}
 }
 
+func (module *SdkLibraryImport) AndroidMkEntries() []android.AndroidMkEntries {
+	// For an SDK library imported from a prebuilt APEX, we don't need a Make module for itself, as we
+	// don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it
+	// is preopted.
+	dexpreoptEntries := module.dexpreopter.AndroidMkEntriesForApex()
+	return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true})
+}
+
 var _ android.ApexModule = (*SdkLibraryImport)(nil)
 
 // Implements android.ApexModule
@@ -2208,8 +2221,16 @@
 			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
 			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
 				module.dexJarFile = dexOutputPath
-				module.installFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName()))
+				installPath := android.PathForModuleInPartitionInstall(
+					ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName()))
+				module.installFile = installPath
 				module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
+
+				// Dexpreopting.
+				module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, installPath)
+				module.dexpreopter.isSDKLibrary = true
+				module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &module.dexpreopter)
+				module.dexpreopt(ctx, dexOutputPath)
 			} else {
 				// This should never happen as a variant for a prebuilt_apex is only created if the
 				// prebuilt_apex has been configured to export the java library dex file.
@@ -2328,6 +2349,11 @@
 	}
 }
 
+// to satisfy java.DexpreopterInterface interface
+func (module *SdkLibraryImport) IsInstallable() bool {
+	return true
+}
+
 var _ android.RequiredFilesFromPrebuiltApex = (*SdkLibraryImport)(nil)
 
 func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
@@ -2471,8 +2497,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (s *sdkLibrarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (s *sdkLibrarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (s *sdkLibrarySdkMemberType) IsInstance(module android.Module) bool {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 938bb28..d6c0946 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -598,6 +598,7 @@
 	}
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
+		`dex2oatd`,
 		`prebuilt_sdklib.stubs`,
 		`prebuilt_sdklib.stubs.source.test`,
 		`prebuilt_sdklib.stubs.system`,
@@ -674,7 +675,6 @@
 		`)
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
-		`dex2oatd`,
 		`prebuilt_sdklib`,
 		`sdklib.impl`,
 		`sdklib.stubs`,
@@ -683,6 +683,7 @@
 	})
 
 	CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
+		`dex2oatd`,
 		`prebuilt_sdklib.stubs`,
 		`sdklib.impl`,
 		`sdklib.xml`,
diff --git a/java/system_modules.go b/java/system_modules.go
index d0dc74a..fec8eba 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -245,8 +245,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (mt *systemModulesSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (mt *systemModulesSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (mt *systemModulesSdkMemberType) IsInstance(module android.Module) bool {
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 5311f62..de2a978 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -123,10 +123,16 @@
 	blueprint.BaseDependencyTag
 }
 
+// The systemserverclasspath_fragment contents must never depend on prebuilts.
+func (systemServerClasspathFragmentContentDependencyTag) ReplaceSourceWithPrebuilt() bool {
+	return false
+}
+
 // Contents of system server fragments in an apex are considered to be directly in the apex, as if
 // they were listed in java_libs.
 func (systemServerClasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {}
 
+var _ android.ReplaceSourceWithPrebuilt = systemServerClasspathFragmentContentDepTag
 var _ android.CopyDirectlyInAnyApexTag = systemServerClasspathFragmentContentDepTag
 
 // The tag used for the dependency between the systemserverclasspath_fragment module and its contents.
diff --git a/java/testing.go b/java/testing.go
index 8860b45..a642753 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -229,6 +229,26 @@
 	)
 }
 
+// FixtureUseLegacyCorePlatformApi prepares the fixture by setting the exception list of those
+// modules that are allowed to use the legacy core platform API to be the ones supplied.
+func FixtureUseLegacyCorePlatformApi(moduleNames ...string) android.FixturePreparer {
+	lookup := make(map[string]struct{})
+	for _, moduleName := range moduleNames {
+		lookup[moduleName] = struct{}{}
+	}
+	return android.FixtureModifyConfig(func(config android.Config) {
+		// Try and set the legacyCorePlatformApiLookup in the config, the returned value will be the
+		// actual value that is set.
+		cached := config.Once(legacyCorePlatformApiLookupKey, func() interface{} {
+			return lookup
+		})
+		// Make sure that the cached value is the one we need.
+		if !reflect.DeepEqual(cached, lookup) {
+			panic(fmt.Errorf("attempting to set legacyCorePlatformApiLookupKey to %q but it has already been set to %q", lookup, cached))
+		}
+	})
+}
+
 // registerRequiredBuildComponentsForTest registers the build components used by
 // PrepareForTestWithJavaDefaultModules.
 //
@@ -431,3 +451,45 @@
 	output := sourceGlobalCompatConfig.Output(allOutputs[0])
 	android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits)
 }
+
+// Register the fake APEX mutator to `android.InitRegistrationContext` as if the real mutator exists
+// at runtime. This must be called in `init()` of a test if the test is going to use the fake APEX
+// mutator. Otherwise, we will be missing the runtime mutator because "soong-apex" is not a
+// dependency, which will cause an inconsistency between testing and runtime mutators.
+func RegisterFakeRuntimeApexMutator() {
+	registerFakeApexMutator(android.InitRegistrationContext)
+}
+
+var PrepareForTestWithFakeApexMutator = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(registerFakeApexMutator),
+)
+
+func registerFakeApexMutator(ctx android.RegistrationContext) {
+	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("apex", fakeApexMutator).Parallel()
+	})
+}
+
+type apexModuleBase interface {
+	ApexAvailable() []string
+}
+
+var _ apexModuleBase = (*Library)(nil)
+var _ apexModuleBase = (*SdkLibrary)(nil)
+
+// A fake APEX mutator that creates a platform variant and an APEX variant for modules with
+// `apex_available`. It helps us avoid a dependency on the real mutator defined in "soong-apex",
+// which will cause a cyclic dependency, and it provides an easy way to create an APEX variant for
+// testing without dealing with all the complexities in the real mutator.
+func fakeApexMutator(mctx android.BottomUpMutatorContext) {
+	switch mctx.Module().(type) {
+	case *Library, *SdkLibrary:
+		if len(mctx.Module().(apexModuleBase).ApexAvailable()) > 0 {
+			modules := mctx.CreateVariations("", "apex1000")
+			apexInfo := android.ApexInfo{
+				ApexVariationName: "apex1000",
+			}
+			mctx.SetVariationProvider(modules[1], android.ApexInfoProvider, apexInfo)
+		}
+	}
+}
diff --git a/mk2rbc/Android.bp b/mk2rbc/Android.bp
index 3ea3f7f..b18bfc7 100644
--- a/mk2rbc/Android.bp
+++ b/mk2rbc/Android.bp
@@ -13,6 +13,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 blueprint_go_binary {
     name: "mk2rbc",
     srcs: ["cmd/mk2rbc.go"],
@@ -34,6 +38,7 @@
         "soong_variables.go",
         "types.go",
         "variable.go",
+        "version_defaults.go",
     ],
     deps: ["androidmk-parser"],
 }
diff --git a/mk2rbc/cmd/mk2rbc.go b/mk2rbc/cmd/mk2rbc.go
index 72525c4..7b5f298 100644
--- a/mk2rbc/cmd/mk2rbc.go
+++ b/mk2rbc/cmd/mk2rbc.go
@@ -81,6 +81,7 @@
 var tracedVariables []string
 var errorLogger = errorsByType{data: make(map[string]datum)}
 var makefileFinder = &LinuxMakefileFinder{}
+var versionDefaultsMk = filepath.Join("build", "make", "core", "version_defaults.mk")
 
 func main() {
 	flag.Usage = func() {
@@ -165,13 +166,24 @@
 			quit(fmt.Errorf("cannot generate configuration launcher for %s, it is not a known product",
 				product))
 		}
+		versionDefaults, err := generateVersionDefaults()
+		if err != nil {
+			quit(err)
+		}
 		ok = convertOne(path) && ok
-		err := writeGenerated(*launcher, mk2rbc.Launcher(outputFilePath(path), mk2rbc.MakePath2ModuleName(path)))
+		versionDefaultsPath := outputFilePath(versionDefaultsMk)
+		err = writeGenerated(versionDefaultsPath, versionDefaults)
 		if err != nil {
 			fmt.Fprintf(os.Stderr, "%s:%s", path, err)
 			ok = false
 		}
 
+		err = writeGenerated(*launcher, mk2rbc.Launcher(outputFilePath(path), versionDefaultsPath,
+			mk2rbc.MakePath2ModuleName(path)))
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "%s:%s", path, err)
+			ok = false
+		}
 	} else {
 		files := flag.Args()
 		if *allInSource {
@@ -194,6 +206,15 @@
 	}
 }
 
+func generateVersionDefaults() (string, error) {
+	versionSettings, err := mk2rbc.ParseVersionDefaults(filepath.Join(*rootDir, versionDefaultsMk))
+	if err != nil {
+		return "", err
+	}
+	return mk2rbc.VersionDefaults(versionSettings), nil
+
+}
+
 func quit(s interface{}) {
 	fmt.Fprintln(os.Stderr, s)
 	os.Exit(2)
@@ -202,8 +223,7 @@
 func buildProductConfigMap() map[string]string {
 	const androidProductsMk = "AndroidProducts.mk"
 	// Build the list of AndroidProducts.mk files: it's
-	// build/make/target/product/AndroidProducts.mk plus
-	// device/**/AndroidProducts.mk
+	// build/make/target/product/AndroidProducts.mk + device/**/AndroidProducts.mk plus + vendor/**/AndroidProducts.mk
 	targetAndroidProductsFile := filepath.Join(*rootDir, "build", "make", "target", "product", androidProductsMk)
 	if _, err := os.Stat(targetAndroidProductsFile); err != nil {
 		fmt.Fprintf(os.Stderr, "%s: %s\n(hint: %s is not a source tree root)\n",
@@ -213,17 +233,19 @@
 	if err := mk2rbc.UpdateProductConfigMap(productConfigMap, targetAndroidProductsFile); err != nil {
 		fmt.Fprintf(os.Stderr, "%s: %s\n", targetAndroidProductsFile, err)
 	}
-	_ = filepath.Walk(filepath.Join(*rootDir, "device"),
-		func(path string, info os.FileInfo, err error) error {
-			if info.IsDir() || filepath.Base(path) != androidProductsMk {
+	for _, t := range []string{"device", "vendor"} {
+		_ = filepath.WalkDir(filepath.Join(*rootDir, t),
+			func(path string, d os.DirEntry, err error) error {
+				if err != nil || d.IsDir() || filepath.Base(path) != androidProductsMk {
+					return nil
+				}
+				if err2 := mk2rbc.UpdateProductConfigMap(productConfigMap, path); err2 != nil {
+					fmt.Fprintf(os.Stderr, "%s: %s\n", path, err)
+					// Keep going, we want to find all such errors in a single run
+				}
 				return nil
-			}
-			if err2 := mk2rbc.UpdateProductConfigMap(productConfigMap, path); err2 != nil {
-				fmt.Fprintf(os.Stderr, "%s: %s\n", path, err)
-				// Keep going, we want to find all such errors in a single run
-			}
-			return nil
-		})
+			})
+	}
 	return productConfigMap
 }
 
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index b05d340..b9b7e2c 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -1618,12 +1618,12 @@
 	return starScript, nil
 }
 
-func Launcher(path, name string) string {
+func Launcher(mainModuleUri, versionDefaultsUri, mainModuleName string) string {
 	var buf bytes.Buffer
 	fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName)
-	fmt.Fprintf(&buf, "load(%q, \"init\")\n", path)
-	fmt.Fprintf(&buf, "g, config = %s(%q, init)\n", cfnMain, name)
-	fmt.Fprintf(&buf, "%s(g, config)\n", cfnPrintVars)
+	fmt.Fprintf(&buf, "load(%q, \"version_defaults\")\n", versionDefaultsUri)
+	fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri)
+	fmt.Fprintf(&buf, "%s(%s(%q, init, version_defaults))\n", cfnPrintVars, cfnMain, mainModuleName)
 	return buf.String()
 }
 
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 46212ee..ca7fe6f 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -414,7 +414,7 @@
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  if rblf.filter(g.get("PRODUCT_LIST", ""), g["TARGET_PRODUCT"]):
+  if rblf.filter(g.get("PRODUCT_LIST", []), g["TARGET_PRODUCT"]):
     pass
 `,
 	},
diff --git a/mk2rbc/test/version_defaults.mk.test b/mk2rbc/test/version_defaults.mk.test
new file mode 100644
index 0000000..1666392
--- /dev/null
+++ b/mk2rbc/test/version_defaults.mk.test
@@ -0,0 +1,22 @@
+INTERNAL_BUILD_ID_MAKEFILE := $(wildcard $(BUILD_SYSTEM)/build_id.mk)
+ifdef INTERNAL_BUILD_ID_MAKEFILE
+  include $(INTERNAL_BUILD_ID_MAKEFILE)
+endif
+
+DEFAULT_PLATFORM_VERSION := TP1A
+.KATI_READONLY := DEFAULT_PLATFORM_VERSION
+MIN_PLATFORM_VERSION := TP1A
+MAX_PLATFORM_VERSION := TP1A
+PLATFORM_VERSION_LAST_STABLE := 12
+PLATFORM_VERSION_CODENAME.SP2A := Sv2
+PLATFORM_VERSION_CODENAME.TP1A := Tiramisu
+ifndef PLATFORM_SDK_VERSION
+  PLATFORM_SDK_VERSION := 31
+endif
+.KATI_READONLY := PLATFORM_SDK_VERSION
+PLATFORM_SDK_EXTENSION_VERSION := 1
+PLATFORM_BASE_SDK_EXTENSION_VERSION := 0
+ifndef PLATFORM_SECURITY_PATCH
+    PLATFORM_SECURITY_PATCH := 2021-10-05
+endif
+include $(BUILD_SYSTEM)/version_util.mk
diff --git a/mk2rbc/variable.go b/mk2rbc/variable.go
index 88d63c9..4bb9ed5 100644
--- a/mk2rbc/variable.go
+++ b/mk2rbc/variable.go
@@ -299,6 +299,10 @@
 					vt = vi.valueType
 				}
 			}
+			if strings.HasSuffix(name, "_LIST") && vt == starlarkTypeUnknown {
+				// Heuristics: Variables with "_LIST" suffix are lists
+				vt = starlarkTypeList
+			}
 			v = &otherGlobalVariable{baseVariable{nam: name, typ: vt}}
 		}
 		ctx.variables[name] = v
diff --git a/mk2rbc/version_defaults.go b/mk2rbc/version_defaults.go
new file mode 100644
index 0000000..27e8198
--- /dev/null
+++ b/mk2rbc/version_defaults.go
@@ -0,0 +1,109 @@
+// Copyright 2021 Google LLC
+//
+// 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 mk2rbc
+
+import (
+	mkparser "android/soong/androidmk/parser"
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"sort"
+	"strconv"
+	"strings"
+)
+
+const codenamePrefix = "PLATFORM_VERSION_CODENAME."
+
+// ParseVersionDefaults extracts version settings from the given file
+// and returns the map.
+func ParseVersionDefaults(path string) (map[string]string, error) {
+	contents, err := ioutil.ReadFile(path)
+	if err != nil {
+		return nil, err
+	}
+	parser := mkparser.NewParser(path, bytes.NewBuffer(contents))
+	nodes, errs := parser.Parse()
+	if len(errs) > 0 {
+		for _, e := range errs {
+			fmt.Fprintln(os.Stderr, "ERROR:", e)
+		}
+		return nil, fmt.Errorf("cannot parse %s", path)
+	}
+
+	result := map[string]string{
+		"DEFAULT_PLATFORM_VERSION":            "",
+		"MAX_PLATFORM_VERSION":                "",
+		"MIN_PLATFORM_VERSION":                "A",
+		"PLATFORM_BASE_SDK_EXTENSION_VERSION": "",
+		"PLATFORM_SDK_EXTENSION_VERSION":      "",
+		"PLATFORM_SDK_VERSION":                "",
+		"PLATFORM_SECURITY_PATCH":             "",
+		"PLATFORM_VERSION_LAST_STABLE":        "",
+	}
+	for _, node := range nodes {
+		asgn, ok := node.(*mkparser.Assignment)
+		if !(ok && asgn.Name.Const()) {
+			continue
+		}
+		s := asgn.Name.Strings[0]
+		_, ok = result[s]
+		if !ok {
+			ok = strings.HasPrefix(s, codenamePrefix)
+		}
+		if !ok {
+			continue
+		}
+		v := asgn.Value
+		if !v.Const() {
+			return nil, fmt.Errorf("the value of %s should be constant", s)
+		}
+		result[s] = strings.TrimSpace(v.Strings[0])
+	}
+	return result, nil
+}
+
+func genericValue(s string) interface{} {
+	if ival, err := strconv.ParseInt(s, 0, 0); err == nil {
+		return ival
+	}
+	return s
+}
+
+// VersionDefaults generates the contents of the version_defaults.rbc file
+func VersionDefaults(values map[string]string) string {
+	var sink bytes.Buffer
+	var lines []string
+	var codenames []string
+	for name, value := range values {
+		if strings.HasPrefix(name, codenamePrefix) {
+			codenames = append(codenames,
+				fmt.Sprintf("%q: %q", strings.TrimPrefix(name, codenamePrefix), value))
+		} else {
+			// Print numbers as such
+			lines = append(lines, fmt.Sprintf("    %s = %#v,\n",
+				strings.ToLower(name), genericValue(value)))
+		}
+	}
+	sort.Strings(lines)
+	sink.WriteString("version_defaults = struct(\n")
+	for _, l := range lines {
+		sink.WriteString(l)
+	}
+	sink.WriteString("    codenames = { ")
+	sink.WriteString(strings.Join(codenames, ", "))
+	sink.WriteString(" }\n)\n")
+	return sink.String()
+}
diff --git a/mk2rbc/version_defaults_test.go b/mk2rbc/version_defaults_test.go
new file mode 100644
index 0000000..c78fa32
--- /dev/null
+++ b/mk2rbc/version_defaults_test.go
@@ -0,0 +1,60 @@
+package mk2rbc
+
+import (
+	"path/filepath"
+	"reflect"
+	"strings"
+	"testing"
+)
+
+func TestParseVersionDefaults(t *testing.T) {
+	testDir := getTestDirectory()
+	abspath := func(relPath string) string { return filepath.Join(testDir, relPath) }
+	actualProducts, err := ParseVersionDefaults(abspath("version_defaults.mk.test"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	expectedProducts := map[string]string{
+		"DEFAULT_PLATFORM_VERSION":            "TP1A",
+		"MAX_PLATFORM_VERSION":                "TP1A",
+		"MIN_PLATFORM_VERSION":                "TP1A",
+		"PLATFORM_BASE_SDK_EXTENSION_VERSION": "0",
+		"PLATFORM_SDK_EXTENSION_VERSION":      "1",
+		"PLATFORM_SDK_VERSION":                "31",
+		"PLATFORM_SECURITY_PATCH":             "2021-10-05",
+		"PLATFORM_VERSION_LAST_STABLE":        "12",
+		"PLATFORM_VERSION_CODENAME.SP2A":      "Sv2",
+		"PLATFORM_VERSION_CODENAME.TP1A":      "Tiramisu",
+	}
+	if !reflect.DeepEqual(actualProducts, expectedProducts) {
+		t.Errorf("\nExpected: %v\n  Actual: %v", expectedProducts, actualProducts)
+	}
+}
+
+func TestVersionDefaults(t *testing.T) {
+	testDir := getTestDirectory()
+	abspath := func(relPath string) string { return filepath.Join(testDir, relPath) }
+	actualProducts, err := ParseVersionDefaults(abspath("version_defaults.mk.test"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	expectedString := `version_defaults = struct(
+    default_platform_version = "TP1A",
+    max_platform_version = "TP1A",
+    min_platform_version = "TP1A",
+    platform_base_sdk_extension_version = 0,
+    platform_sdk_extension_version = 1,
+    platform_sdk_version = 31,
+    platform_security_patch = "2021-10-05",
+    platform_version_last_stable = 12,
+    codenames = { "SP2A": "Sv2", "TP1A": "Tiramisu" }
+)
+`
+	actualString := VersionDefaults(actualProducts)
+	if !reflect.DeepEqual(actualString, expectedString) {
+		t.Errorf("\nExpected: %v\nActual:\n%v",
+			strings.ReplaceAll(expectedString, "\n", "␤\n"),
+			strings.ReplaceAll(actualString, "\n", "␤\n"))
+	}
+
+}
diff --git a/python/python.go b/python/python.go
index 83844e6..f900172 100644
--- a/python/python.go
+++ b/python/python.go
@@ -45,7 +45,7 @@
 type VersionProperties struct {
 	// whether the module is required to be built with this version.
 	// Defaults to true for Python 3, and false otherwise.
-	Enabled *bool `android:"arch_variant"`
+	Enabled *bool
 
 	// list of source files specific to this Python version.
 	// Using the syntax ":module", srcs may reference the outputs of other modules that produce source files,
@@ -60,7 +60,7 @@
 	Libs []string `android:"arch_variant"`
 
 	// whether the binary is required to be built with embedded launcher for this version, defaults to false.
-	Embedded_launcher *bool `android:"arch_variant"` // TODO(b/174041232): Remove this property
+	Embedded_launcher *bool // TODO(b/174041232): Remove this property
 }
 
 // properties that apply to all python modules
@@ -70,10 +70,10 @@
 	// eg. Pkg_path = "a/b/c"; Other packages can reference this module by using
 	// (from a.b.c import ...) statement.
 	// if left unspecified, all the source/data files path is unchanged within zip file.
-	Pkg_path *string `android:"arch_variant"`
+	Pkg_path *string
 
 	// true, if the Python module is used internally, eg, Python std libs.
-	Is_internal *bool `android:"arch_variant"`
+	Is_internal *bool
 
 	// list of source (.py) files compatible both with Python2 and Python3 used to compile the
 	// Python module.
@@ -310,13 +310,16 @@
 // HostToolPath returns a path if appropriate such that this module can be used as a host tool,
 // fulfilling HostToolProvider interface.
 func (p *Module) HostToolPath() android.OptionalPath {
-	if p.installer == nil {
-		// python_library is just meta module, and doesn't have any installer.
-		return android.OptionalPath{}
+	if p.installer != nil {
+		if bin, ok := p.installer.(*binaryDecorator); ok {
+			// TODO: This should only be set when building host binaries -- tests built for device would be
+			// setting this incorrectly.
+			return android.OptionalPathForPath(bin.path)
+		}
 	}
-	// TODO: This should only be set when building host binaries -- tests built for device would be
-	// setting this incorrectly.
-	return android.OptionalPathForPath(p.installer.(*binaryDecorator).path)
+
+	return android.OptionalPath{}
+
 }
 
 // OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
diff --git a/rust/bindgen.go b/rust/bindgen.go
index be9e71e..845f258 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r428724"
+	bindgenClangVersion = "clang-r433403"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 63a8f04..47ca3a7 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -28,6 +28,7 @@
 		"system/librustutils",
 		"system/logging/liblog",
 		"system/logging/rust",
+		"system/nfc",
 		"system/security",
 		"system/tools/aidl",
 		"tools/security/fuzzing/example_rust_fuzzer",
diff --git a/rust/config/global.go b/rust/config/global.go
index e5b334d..b163bb6 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.54.0"
+	RustDefaultVersion = "1.55.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index 0aa534f..c10afd8 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -26,6 +26,7 @@
 		"-B${cc_config.ClangBin}",
 		"-fuse-ld=lld",
 		"-Wl,--undefined-version",
+		"--sysroot ${cc_config.LinuxGccRoot}/sysroot",
 	}
 	linuxX86Rustflags   = []string{}
 	linuxX86Linkflags   = []string{}
diff --git a/rust/coverage.go b/rust/coverage.go
index 050b811..8fdfa23 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -57,7 +57,18 @@
 		flags.RustFlags = append(flags.RustFlags,
 			"-Z instrument-coverage", "-g")
 		flags.LinkFlags = append(flags.LinkFlags,
-			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
+			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open",
+			// Upstream LLVM change 6d2d3bd0a6 made
+			// -z,start-stop-gc the default.  It drops metadata
+			// sections like __llvm_prf_data unless they are marked
+			// SHF_GNU_RETAIN.  https://reviews.llvm.org/D97448
+			// marks generated sections, including __llvm_prf_data
+			// as SHF_GNU_RETAIN.  However this change is not in
+			// the Rust toolchain.  Since we link Rust libs with
+			// new lld, we should use nostart-stop-gc until the
+			// Rust toolchain updates past D97448.
+			"-Wl,-z,nostart-stop-gc",
+		)
 		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
 	}
 
diff --git a/scripts/check_boot_jars/check_boot_jars.py b/scripts/check_boot_jars/check_boot_jars.py
index c271211..b711f9d 100755
--- a/scripts/check_boot_jars/check_boot_jars.py
+++ b/scripts/check_boot_jars/check_boot_jars.py
@@ -1,101 +1,102 @@
 #!/usr/bin/env python
+"""Check boot jars.
 
+Usage: check_boot_jars.py <dexdump_path> <package_allow_list_file> <jar1> \
+<jar2> ...
 """
-Check boot jars.
-
-Usage: check_boot_jars.py <dexdump_path> <package_allow_list_file> <jar1> <jar2> ...
-"""
+from __future__ import print_function
 import logging
-import os.path
 import re
 import subprocess
 import sys
 import xml.etree.ElementTree
 
-
 # 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
+    """ 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 #pylint: disable=global-statement
+    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 CheckDexJar(dexdump_path, allow_list_path, jar):
-  """Check a dex jar file.
-  """
-  # Use dexdump to generate the XML representation of the dex jar file.
-  p = subprocess.Popen(args='%s -l xml %s' % (dexdump_path, jar),
-      stdout=subprocess.PIPE, shell=True)
-  stdout, _ = p.communicate()
-  if p.returncode != 0:
-    return False
+    """Check a dex jar file."""
+    # Use dexdump to generate the XML representation of the dex jar file.
+    p = subprocess.Popen(
+        args='%s -l xml %s' % (dexdump_path, jar),
+        stdout=subprocess.PIPE,
+        shell=True)
+    stdout, _ = p.communicate()
+    if p.returncode != 0:
+        return False
 
-  packages = 0
-  try:
-    # TODO(b/172063475) - improve performance
-    root = xml.etree.ElementTree.fromstring(stdout)
-  except xml.etree.ElementTree.ParseError as e:
-    print >> sys.stderr, 'Error processing jar %s - %s' % (jar, e)
-    print >> sys.stderr, stdout
-    return False
-  for package_elt in root.iterfind('package'):
-    packages += 1
-    package_name = package_elt.get('name')
-    if not package_name or not allow_list_re.match(package_name):
-      # Report the name of a class in the package as it is easier to navigate to
-      # the source of a concrete class than to a package which is often required
-      # to investigate this failure.
-      class_name = package_elt[0].get('name')
-      if package_name != "":
-        class_name = package_name + "." + class_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, class_name, package_name, allow_list_path))
-      return False
-  if packages == 0:
-    print >> sys.stderr, ('Error: %s does not contain any packages.' % jar)
-    return False
-  return True
-
+    packages = 0
+    try:
+        # TODO(b/172063475) - improve performance
+        root = xml.etree.ElementTree.fromstring(stdout)
+    except xml.etree.ElementTree.ParseError as e:
+        print('Error processing jar %s - %s' % (jar, e), file=sys.stderr)
+        print(stdout, file=sys.stderr)
+        return False
+    for package_elt in root.iterfind('package'):
+        packages += 1
+        package_name = package_elt.get('name')
+        if not package_name or not allow_list_re.match(package_name):
+            # Report the name of a class in the package as it is easier to
+            # navigate to the source of a concrete class than to a package
+            # which is often required to investigate this failure.
+            class_name = package_elt[0].get('name')
+            if package_name:
+                class_name = package_name + '.' + class_name
+            print((
+                '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, class_name, package_name, allow_list_path)),
+                  file=sys.stderr)
+            return False
+    if packages == 0:
+        print(('Error: %s does not contain any packages.' % jar),
+              file=sys.stderr)
+        return False
+    return True
 
 def main(argv):
-  if len(argv) < 3:
-    print __doc__
-    return 1
-  dexdump_path = argv[0]
-  allow_list_path = argv[1]
+    if len(argv) < 3:
+        print(__doc__)
+        return 1
+    dexdump_path = argv[0]
+    allow_list_path = argv[1]
 
-  if not LoadAllowList(allow_list_path):
-    return 1
+    if not LoadAllowList(allow_list_path):
+        return 1
 
-  passed = True
-  for jar in argv[2:]:
-    if not CheckDexJar(dexdump_path, allow_list_path, jar):
-      passed = False
-  if not passed:
-    return 1
+    passed = True
+    for jar in argv[2:]:
+        if not CheckDexJar(dexdump_path, allow_list_path, jar):
+            passed = False
+    if not passed:
+        return 1
 
-  return 0
+    return 0
 
 
 if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
+    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
index 18ab427..942f26a 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -69,6 +69,7 @@
 javax\.xml\.transform\.stream
 javax\.xml\.validation
 javax\.xml\.xpath
+jdk\.internal\.math
 jdk\.internal\.util
 jdk\.internal\.vm\.annotation
 jdk\.net
diff --git a/scripts/generate-notice-files.py b/scripts/generate-notice-files.py
index 49011b2..1b4acfa 100755
--- a/scripts/generate-notice-files.py
+++ b/scripts/generate-notice-files.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2012 The Android Open Source Project
 #
@@ -30,20 +30,18 @@
 import os
 import os.path
 import re
+import struct
 import sys
 
 MD5_BLOCKSIZE = 1024 * 1024
 HTML_ESCAPE_TABLE = {
-    "&": "&amp;",
-    '"': "&quot;",
-    "'": "&apos;",
-    ">": "&gt;",
-    "<": "&lt;",
+    b"&": b"&amp;",
+    b'"': b"&quot;",
+    b"'": b"&apos;",
+    b">": b"&gt;",
+    b"<": b"&lt;",
     }
 
-def hexify(s):
-    return ("%02x"*len(s)) % tuple(map(ord, s))
-
 def md5sum(filename):
     """Calculate an MD5 of the file given by FILENAME,
     and return hex digest as a string.
@@ -57,20 +55,26 @@
             break
         sum.update(block)
     f.close()
-    return hexify(sum.digest())
+    return sum.hexdigest()
 
 
 def html_escape(text):
     """Produce entities within text."""
-    return "".join(HTML_ESCAPE_TABLE.get(c,c) for c in text)
+    # Using for i in text doesn't work since i will be an int, not a byte.
+    # There are multiple ways to solve this, but the most performant way
+    # to iterate over a byte array is to use unpack. Using the
+    # for i in range(len(text)) and using that to get a byte using array
+    # slices is twice as slow as this method.
+    return b"".join(HTML_ESCAPE_TABLE.get(i,i) for i in struct.unpack(str(len(text)) + 'c', text))
 
-HTML_OUTPUT_CSS="""
+HTML_OUTPUT_CSS=b"""
 <style type="text/css">
 body { padding: 0; font-family: sans-serif; }
 .same-license { background-color: #eeeeee; border-top: 20px solid white; padding: 10px; }
 .label { font-weight: bold; }
 .file-list { margin-left: 1em; color: blue; }
 </style>
+
 """
 
 def combine_notice_files_html(file_hash, input_dir, output_filename):
@@ -90,13 +94,13 @@
     # Open the output file, and output the header pieces
     output_file = open(output_filename, "wb")
 
-    print >> output_file, "<html><head>"
-    print >> output_file, HTML_OUTPUT_CSS
-    print >> output_file, '</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">'
+    output_file.write(b"<html><head>\n")
+    output_file.write(HTML_OUTPUT_CSS)
+    output_file.write(b'</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">\n')
 
     # Output our table of contents
-    print >> output_file, '<div class="toc">'
-    print >> output_file, "<ul>"
+    output_file.write(b'<div class="toc">\n')
+    output_file.write(b"<ul>\n")
 
     # Flatten the list of lists into a single list of filenames
     sorted_filenames = sorted(itertools.chain.from_iterable(file_hash))
@@ -104,31 +108,28 @@
     # Print out a nice table of contents
     for filename in sorted_filenames:
         stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
-        print >> output_file, '<li><a href="#id%d">%s</a></li>' % (id_table.get(filename), stripped_filename)
+        output_file.write(('<li><a href="#id%d">%s</a></li>\n' % (id_table.get(filename), stripped_filename)).encode())
 
-    print >> output_file, "</ul>"
-    print >> output_file, "</div><!-- table of contents -->"
+    output_file.write(b"</ul>\n")
+    output_file.write(b"</div><!-- table of contents -->\n")
     # Output the individual notice file lists
-    print >>output_file, '<table cellpadding="0" cellspacing="0" border="0">'
+    output_file.write(b'<table cellpadding="0" cellspacing="0" border="0">\n')
     for value in file_hash:
-        print >> output_file, '<tr id="id%d"><td class="same-license">' % id_table.get(value[0])
-        print >> output_file, '<div class="label">Notices for file(s):</div>'
-        print >> output_file, '<div class="file-list">'
+        output_file.write(('<tr id="id%d"><td class="same-license">\n' % id_table.get(value[0])).encode())
+        output_file.write(b'<div class="label">Notices for file(s):</div>\n')
+        output_file.write(b'<div class="file-list">\n')
         for filename in value:
-            print >> output_file, "%s <br/>" % (SRC_DIR_STRIP_RE.sub(r"\1", filename))
-        print >> output_file, "</div><!-- file-list -->"
-        print >> output_file
-        print >> output_file, '<pre class="license-text">'
-        print >> output_file, html_escape(open(value[0]).read())
-        print >> output_file, "</pre><!-- license-text -->"
-        print >> output_file, "</td></tr><!-- same-license -->"
-        print >> output_file
-        print >> output_file
-        print >> output_file
+            output_file.write(("%s <br/>\n" % (SRC_DIR_STRIP_RE.sub(r"\1", filename))).encode())
+        output_file.write(b"</div><!-- file-list -->\n\n")
+        output_file.write(b'<pre class="license-text">\n')
+        with open(value[0], "rb") as notice_file:
+            output_file.write(html_escape(notice_file.read()))
+        output_file.write(b"\n</pre><!-- license-text -->\n")
+        output_file.write(b"</td></tr><!-- same-license -->\n\n\n\n")
 
     # Finish off the file output
-    print >> output_file, "</table>"
-    print >> output_file, "</body></html>"
+    output_file.write(b"</table>\n")
+    output_file.write(b"</body></html>\n")
     output_file.close()
 
 def combine_notice_files_text(file_hash, input_dir, output_filename, file_title):
@@ -136,14 +137,18 @@
 
     SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
     output_file = open(output_filename, "wb")
-    print >> output_file, file_title
+    output_file.write(file_title.encode())
+    output_file.write(b"\n")
     for value in file_hash:
-      print >> output_file, "============================================================"
-      print >> output_file, "Notices for file(s):"
-      for filename in value:
-        print >> output_file, SRC_DIR_STRIP_RE.sub(r"\1", filename)
-      print >> output_file, "------------------------------------------------------------"
-      print >> output_file, open(value[0]).read()
+        output_file.write(b"============================================================\n")
+        output_file.write(b"Notices for file(s):\n")
+        for filename in value:
+            output_file.write(SRC_DIR_STRIP_RE.sub(r"\1", filename).encode())
+            output_file.write(b"\n")
+        output_file.write(b"------------------------------------------------------------\n")
+        with open(value[0], "rb") as notice_file:
+            output_file.write(notice_file.read())
+            output_file.write(b"\n")
     output_file.close()
 
 def combine_notice_files_xml(files_with_same_hash, input_dir, output_filename):
@@ -154,26 +159,24 @@
     # Set up a filename to row id table (anchors inside tables don't work in
     # most browsers, but href's to table row ids do)
     id_table = {}
-    for file_key in files_with_same_hash.keys():
-        for filename in files_with_same_hash[file_key]:
+    for file_key, files in files_with_same_hash.items():
+        for filename in files:
              id_table[filename] = file_key
 
     # Open the output file, and output the header pieces
     output_file = open(output_filename, "wb")
 
-    print >> output_file, '<?xml version="1.0" encoding="utf-8"?>'
-    print >> output_file, "<licenses>"
+    output_file.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
+    output_file.write(b"<licenses>\n")
 
     # Flatten the list of lists into a single list of filenames
-    sorted_filenames = sorted(id_table.keys())
+    sorted_filenames = sorted(list(id_table))
 
     # Print out a nice table of contents
     for filename in sorted_filenames:
         stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
-        print >> output_file, '<file-name contentId="%s">%s</file-name>' % (id_table.get(filename), stripped_filename)
-
-    print >> output_file
-    print >> output_file
+        output_file.write(('<file-name contentId="%s">%s</file-name>\n' % (id_table.get(filename), stripped_filename)).encode())
+    output_file.write(b"\n\n")
 
     processed_file_keys = []
     # Output the individual notice file lists
@@ -183,11 +186,13 @@
             continue
         processed_file_keys.append(file_key)
 
-        print >> output_file, '<file-content contentId="%s"><![CDATA[%s]]></file-content>' % (file_key, html_escape(open(filename).read()))
-        print >> output_file
+        output_file.write(('<file-content contentId="%s"><![CDATA[' % file_key).encode())
+        with open(filename, "rb") as notice_file:
+            output_file.write(html_escape(notice_file.read()))
+        output_file.write(b"]]></file-content>\n\n")
 
     # Finish off the file output
-    print >> output_file, "</licenses>"
+    output_file.write(b"</licenses>\n")
     output_file.close()
 
 def get_args():
@@ -253,7 +258,7 @@
                 file_md5sum = md5sum(filename)
                 files_with_same_hash[file_md5sum].append(filename)
 
-    filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(files_with_same_hash.keys())]
+    filesets = [sorted(files_with_same_hash[md5]) for md5 in sorted(list(files_with_same_hash))]
 
     combine_notice_files_text(filesets, input_dir, txt_output_file, file_title)
 
diff --git a/scripts/get_clang_version.py b/scripts/get_clang_version.py
index f6efc5f..64d922a 100755
--- a/scripts/get_clang_version.py
+++ b/scripts/get_clang_version.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2021 The Android Open Source Project
 #
@@ -21,8 +21,12 @@
 
 
 ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP", ".")
+LLVM_PREBUILTS_VERSION = os.environ.get("LLVM_PREBUILTS_VERSION")
 
 def get_clang_prebuilts_version(global_go):
+  if LLVM_PREBUILTS_VERSION:
+    return LLVM_PREBUILTS_VERSION
+
   # TODO(b/187231324): Get clang version from the json file once it is no longer
   # hard-coded in global.go
   if global_go is None:
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists.py b/scripts/hiddenapi/generate_hiddenapi_lists.py
index 5ab93d1..35e0948 100755
--- a/scripts/hiddenapi/generate_hiddenapi_lists.py
+++ b/scripts/hiddenapi/generate_hiddenapi_lists.py
@@ -16,8 +16,6 @@
 """Generate API lists for non-SDK API enforcement."""
 import argparse
 from collections import defaultdict, namedtuple
-import functools
-import os
 import re
 import sys
 
@@ -60,15 +58,15 @@
 # For example, the max-target-P list is checked in as it was in P,
 # but signatures have changes since then. The flag instructs this
 # script to skip any entries which do not exist any more.
-FLAG_IGNORE_CONFLICTS = "ignore-conflicts"
+FLAG_IGNORE_CONFLICTS = 'ignore-conflicts'
 
 # Option specified after one of FLAGS_API_LIST to express that all
 # apis within a given set of packages should be assign the given flag.
-FLAG_PACKAGES = "packages"
+FLAG_PACKAGES = 'packages'
 
 # Option specified after one of FLAGS_API_LIST to indicate an extra
 # tag that should be added to the matching APIs.
-FLAG_TAG = "tag"
+FLAG_TAG = 'tag'
 
 # Regex patterns of fields/methods used in serialization. These are
 # considered public API despite being hidden.
@@ -84,24 +82,30 @@
 
 # Single regex used to match serialization API. It combines all the
 # SERIALIZATION_PATTERNS into a single regular expression.
-SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
+SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) +
+                                 r')$')
 
 # Predicates to be used with filter_apis.
-HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
+HAS_NO_API_LIST_ASSIGNED = \
+    lambda api,flags: not FLAGS_API_LIST_SET.intersection(flags)
+
 IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
 
 
 class StoreOrderedOptions(argparse.Action):
-    """An argparse action that stores a number of option arguments in the order that
-    they were specified.
+    """An argparse action that stores a number of option arguments in the order
+
+    that they were specified.
     """
-    def __call__(self, parser, args, values, option_string = None):
+
+    def __call__(self, parser, args, values, option_string=None):
         items = getattr(args, self.dest, None)
         if items is None:
             items = []
         items.append([option_string.lstrip('-'), values])
         setattr(args, self.dest, items)
 
+
 def get_args():
     """Parses command line arguments.
 
@@ -110,22 +114,43 @@
     """
     parser = argparse.ArgumentParser()
     parser.add_argument('--output', required=True)
-    parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
+    parser.add_argument(
+        '--csv',
+        nargs='*',
+        default=[],
+        metavar='CSV_FILE',
         help='CSV files to be merged into output')
 
     for flag in ALL_FLAGS:
-        parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE',
-            action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"')
-    parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0,
-        action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned '
-        'entries should be assign the given flag. Must follow a list of entries and applies '
-        'to the preceding such list.')
-    parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0,
-        action=StoreOrderedOptions, help='Indicates that the previous list of entries '
-        'is a list of packages. All members in those packages will be given the flag. '
-        'Must follow a list of entries and applies to the preceding such list.')
-    parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1,
-        action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. '
+        parser.add_argument(
+            '--' + flag,
+            dest='ordered_flags',
+            metavar='TXT_FILE',
+            action=StoreOrderedOptions,
+            help='lists of entries with flag "' + flag + '"')
+    parser.add_argument(
+        '--' + FLAG_IGNORE_CONFLICTS,
+        dest='ordered_flags',
+        nargs=0,
+        action=StoreOrderedOptions,
+        help='Indicates that only known and otherwise unassigned '
+        'entries should be assign the given flag. Must follow a list of '
+        'entries and applies to the preceding such list.')
+    parser.add_argument(
+        '--' + FLAG_PACKAGES,
+        dest='ordered_flags',
+        nargs=0,
+        action=StoreOrderedOptions,
+        help='Indicates that the previous list of entries '
+        'is a list of packages. All members in those packages will be given '
+        'the flag. Must follow a list of entries and applies to the preceding '
+        'such list.')
+    parser.add_argument(
+        '--' + FLAG_TAG,
+        dest='ordered_flags',
+        nargs=1,
+        action=StoreOrderedOptions,
+        help='Adds an extra tag to the previous list of entries. '
         'Must follow a list of entries and applies to the preceding such list.')
 
     return parser.parse_args()
@@ -143,9 +168,9 @@
         Lines of the file as a list of string.
     """
     with open(filename, 'r') as f:
-        lines = f.readlines();
-    lines = filter(lambda line: not line.startswith('#'), lines)
-    lines = map(lambda line: line.strip(), lines)
+        lines = f.readlines()
+    lines = [line for line in lines if not line.startswith('#')]
+    lines = [line.strip() for line in lines]
     return set(lines)
 
 
@@ -156,7 +181,7 @@
         filename (string): Path to the file to be writing into.
         lines (list): List of strings to write into the file.
     """
-    lines = map(lambda line: line + '\n', lines)
+    lines = [line + '\n' for line in lines]
     with open(filename, 'w') as f:
         f.writelines(lines)
 
@@ -170,17 +195,19 @@
     Returns:
         The package name of the class containing the field/method.
     """
-    full_class_name = signature.split(";->")[0]
+    full_class_name = signature.split(';->')[0]
     # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
-    if (full_class_name[0] != "L"):
-        raise ValueError("Expected to start with 'L': %s" % full_class_name)
+    if full_class_name[0] != 'L':
+        raise ValueError("Expected to start with 'L': %s"
+                         % full_class_name)
     full_class_name = full_class_name[1:]
     # If full_class_name doesn't contain '/', then package_name will be ''.
-    package_name = full_class_name.rpartition("/")[0]
+    package_name = full_class_name.rpartition('/')[0]
     return package_name.replace('/', '.')
 
 
 class FlagsDict:
+
     def __init__(self):
         self._dict_keyset = set()
         self._dict = defaultdict(set)
@@ -188,37 +215,43 @@
     def _check_entries_set(self, keys_subset, source):
         assert isinstance(keys_subset, set)
         assert keys_subset.issubset(self._dict_keyset), (
-            "Error: {} specifies signatures not present in code:\n"
-            "{}"
-            "Please visit go/hiddenapi for more information.").format(
-                source, "".join(map(lambda x: "  " + str(x) + "\n", keys_subset - self._dict_keyset)))
+            'Error: {} specifies signatures not present in code:\n'
+            '{}'
+            'Please visit go/hiddenapi for more information.').format(
+                source, ''.join(
+                    ['  ' + str(x) + '\n' for x in
+                     keys_subset - self._dict_keyset]))
 
     def _check_flags_set(self, flags_subset, source):
         assert isinstance(flags_subset, set)
         assert flags_subset.issubset(ALL_FLAGS_SET), (
-            "Error processing: {}\n"
-            "The following flags were not recognized: \n"
-            "{}\n"
-            "Please visit go/hiddenapi for more information.").format(
-                source, "\n".join(flags_subset - ALL_FLAGS_SET))
+            'Error processing: {}\n'
+            'The following flags were not recognized: \n'
+            '{}\n'
+            'Please visit go/hiddenapi for more information.').format(
+                source, '\n'.join(flags_subset - ALL_FLAGS_SET))
 
     def filter_apis(self, filter_fn):
         """Returns APIs which match a given predicate.
 
-        This is a helper function which allows to filter on both signatures (keys) and
-        flags (values). The built-in filter() invokes the lambda only with dict's keys.
+        This is a helper function which allows to filter on both signatures
+        (keys) and
+        flags (values). The built-in filter() invokes the lambda only with
+        dict's keys.
 
         Args:
-            filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
+            filter_fn : Function which takes two arguments (signature/flags) and
+              returns a boolean.
 
         Returns:
             A set of APIs which match the predicate.
         """
-        return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
+        return {x for x in self._dict_keyset if filter_fn(x, self._dict[x])}
 
     def get_valid_subset_of_unassigned_apis(self, api_subset):
-        """Sanitizes a key set input to only include keys which exist in the dictionary
-        and have not been assigned any API list flags.
+        """Sanitizes a key set input to only include keys which exist in the
+
+        dictionary and have not been assigned any API list flags.
 
         Args:
             entries_subset (set/list): Key set to be sanitized.
@@ -227,7 +260,8 @@
             Sanitized key set.
         """
         assert isinstance(api_subset, set)
-        return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
+        return api_subset.intersection(
+            self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
 
     def generate_csv(self):
         """Constructs CSV entries from a dictionary.
@@ -235,15 +269,16 @@
         Old versions of flags are used to generate the file.
 
         Returns:
-            List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
+            List of lines comprising a CSV file. See "parse_and_merge_csv" for
+            format description.
         """
         lines = []
         for api in self._dict:
-          flags = sorted(self._dict[api])
-          lines.append(",".join([api] + flags))
+            flags = sorted(self._dict[api])
+            lines.append(','.join([api] + flags))
         return sorted(lines)
 
-    def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
+    def parse_and_merge_csv(self, csv_lines, source='<unknown>'):
         """Parses CSV entries and merges them into a given dictionary.
 
         The expected CSV format is:
@@ -251,21 +286,20 @@
 
         Args:
             csv_lines (list of strings): Lines read from a CSV file.
-            source (string): Origin of `csv_lines`. Will be printed in error messages.
-
-        Throws:
-            AssertionError if parsed flags are invalid.
+            source (string): Origin of `csv_lines`. Will be printed in error
+              messages.
+        Throws: AssertionError if parsed flags are invalid.
         """
         # Split CSV lines into arrays of values.
-        csv_values = [ line.split(',') for line in csv_lines ]
+        csv_values = [line.split(',') for line in csv_lines]
 
         # Update the full set of API signatures.
-        self._dict_keyset.update([ csv[0] for csv in csv_values ])
+        self._dict_keyset.update([csv[0] for csv in csv_values])
 
         # Check that all flags are known.
         csv_flags = set()
         for csv in csv_values:
-          csv_flags.update(csv[1:])
+            csv_flags.update(csv[1:])
         self._check_flags_set(csv_flags, source)
 
         # Iterate over all CSV lines, find entry in dict and append flags to it.
@@ -275,47 +309,53 @@
                 flags.append(FLAG_SDK)
             self._dict[csv[0]].update(flags)
 
-    def assign_flag(self, flag, apis, source="<unknown>", tag = None):
+    def assign_flag(self, flag, apis, source='<unknown>', tag=None):
         """Assigns a flag to given subset of entries.
 
         Args:
             flag (string): One of ALL_FLAGS.
             apis (set): Subset of APIs to receive the flag.
-            source (string): Origin of `entries_subset`. Will be printed in error messages.
-
-        Throws:
-            AssertionError if parsed API signatures of flags are invalid.
+            source (string): Origin of `entries_subset`. Will be printed in
+              error messages.
+        Throws: AssertionError if parsed API signatures of flags are invalid.
         """
         # Check that all APIs exist in the dict.
         self._check_entries_set(apis, source)
 
         # Check that the flag is known.
-        self._check_flags_set(set([ flag ]), source)
+        self._check_flags_set(set([flag]), source)
 
-        # Iterate over the API subset, find each entry in dict and assign the flag to it.
+        # Iterate over the API subset, find each entry in dict and assign the
+        # flag to it.
         for api in apis:
             self._dict[api].add(flag)
             if tag:
                 self._dict[api].add(tag)
 
 
-FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
+FlagFile = namedtuple('FlagFile',
+                      ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
+
 
 def parse_ordered_flags(ordered_flags):
     r = []
-    currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None
+    currentflag, file, ignore_conflicts, packages, tag = None, None, False, \
+        False, None
     for flag_value in ordered_flags:
         flag, value = flag_value[0], flag_value[1]
         if flag in ALL_FLAGS_SET:
             if currentflag:
-                r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
+                r.append(
+                    FlagFile(currentflag, file, ignore_conflicts, packages,
+                             tag))
                 ignore_conflicts, packages, tag = False, False, None
             currentflag = flag
             file = value
         else:
             if currentflag is None:
-                raise argparse.ArgumentError('--%s is only allowed after one of %s' % (
-                    flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
+                raise argparse.ArgumentError( #pylint: disable=no-value-for-parameter
+                    '--%s is only allowed after one of %s' %
+                    (flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
             if flag == FLAG_IGNORE_CONFLICTS:
                 ignore_conflicts = True
             elif flag == FLAG_PACKAGES:
@@ -323,13 +363,12 @@
             elif flag == FLAG_TAG:
                 tag = value[0]
 
-
     if currentflag:
         r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
     return r
 
 
-def main(argv):
+def main(argv): #pylint: disable=unused-argument
     # Parse arguments.
     args = vars(get_args())
     flagfiles = parse_ordered_flags(args['ordered_flags'] or [])
@@ -342,7 +381,7 @@
     # contain the full set of APIs. Subsequent additions from text files
     # will be able to detect invalid entries, and/or filter all as-yet
     # unassigned entries.
-    for filename in args["csv"]:
+    for filename in args['csv']:
         flags.parse_and_merge_csv(read_lines(filename), filename)
 
     # Combine inputs which do not require any particular order.
@@ -352,24 +391,28 @@
     # (2) Merge text files with a known flag into the dictionary.
     for info in flagfiles:
         if (not info.ignore_conflicts) and (not info.packages):
-            flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag)
+            flags.assign_flag(info.flag, read_lines(info.file), info.file,
+                              info.tag)
 
     # Merge text files where conflicts should be ignored.
     # This will only assign the given flag if:
     # (a) the entry exists, and
     # (b) it has not been assigned any other flag.
-    # Because of (b), this must run after all strict assignments have been performed.
+    # Because of (b), this must run after all strict assignments have been
+    # performed.
     for info in flagfiles:
         if info.ignore_conflicts:
-            valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file))
-            flags.assign_flag(info.flag, valid_entries, filename, info.tag)
+            valid_entries = flags.get_valid_subset_of_unassigned_apis(
+                read_lines(info.file))
+            flags.assign_flag(info.flag, valid_entries, filename, info.tag) #pylint: disable=undefined-loop-variable
 
-    # All members in the specified packages will be assigned the appropriate flag.
+    # All members in the specified packages will be assigned the appropriate
+    # flag.
     for info in flagfiles:
         if info.packages:
             packages_needing_list = set(read_lines(info.file))
-            should_add_signature_to_list = lambda sig,lists: extract_package(
-                sig) in packages_needing_list and not lists
+            should_add_signature_to_list = lambda sig, lists: extract_package(
+                sig) in packages_needing_list and not lists #pylint: disable=cell-var-from-loop
             valid_entries = flags.filter_apis(should_add_signature_to_list)
             flags.assign_flag(info.flag, valid_entries, info.file, info.tag)
 
@@ -377,7 +420,8 @@
     flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
 
     # Write output.
-    write_lines(args["output"], flags.generate_csv())
+    write_lines(args['output'], flags.generate_csv())
 
-if __name__ == "__main__":
+
+if __name__ == '__main__':
     main(sys.argv)
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists_test.py b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
index b81424b..204de97 100755
--- a/scripts/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
@@ -15,34 +15,39 @@
 # limitations under the License.
 """Unit tests for Hidden API list generation."""
 import unittest
-from generate_hiddenapi_lists import *
+from generate_hiddenapi_lists import *  # pylint: disable=wildcard-import,unused-wildcard-import
+
 
 class TestHiddenapiListGeneration(unittest.TestCase):
-
     def test_filter_apis(self):
         # Initialize flags so that A and B are put on the allow list and
         # C, D, E are left unassigned. Try filtering for the unassigned ones.
         flags = FlagsDict()
-        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK,
-                        'C', 'D', 'E'])
+        flags.parse_and_merge_csv(
+            ['A,' + FLAG_SDK, 'B,' + FLAG_SDK, 'C', 'D', 'E']
+        )
         filter_set = flags.filter_apis(lambda api, flags: not flags)
         self.assertTrue(isinstance(filter_set, set))
-        self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
+        self.assertEqual(filter_set, set(['C', 'D', 'E']))
 
     def test_get_valid_subset_of_unassigned_keys(self):
         # Create flags where only A is unassigned.
         flags = FlagsDict()
         flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C'])
         flags.assign_flag(FLAG_UNSUPPORTED, set(['C']))
-        self.assertEqual(flags.generate_csv(),
-            [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ])
+        self.assertEqual(
+            flags.generate_csv(),
+            ['A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED],
+        )
 
         # Check three things:
         # (1) B is selected as valid unassigned
         # (2) A is not selected because it is assigned to the allow list
         # (3) D is not selected because it is not a valid key
         self.assertEqual(
-            flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
+            flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])),
+            set(['B']),
+        )
 
     def test_parse_and_merge_csv(self):
         flags = FlagsDict()
@@ -51,41 +56,48 @@
         self.assertEqual(flags.generate_csv(), [])
 
         # Test new additions.
-        flags.parse_and_merge_csv([
-            'A,' + FLAG_UNSUPPORTED,
-            'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
-            'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
-            'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
-            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
-        ])
-        self.assertEqual(flags.generate_csv(), [
-            'A,' + FLAG_UNSUPPORTED,
-            'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
-            'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
-            'D,' + FLAG_TEST_API + ',' + FLAG_UNSUPPORTED,
-            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
-        ])
+        flags.parse_and_merge_csv(
+            [
+                'A,' + FLAG_UNSUPPORTED,
+                'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
+                'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
+                'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
+                'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
+            ]
+        )
+        self.assertEqual(
+            flags.generate_csv(),
+            [
+                'A,' + FLAG_UNSUPPORTED,
+                'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
+                'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
+                'D,' + FLAG_TEST_API + ',' + FLAG_UNSUPPORTED,
+                'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
+            ],
+        )
 
         # Test unknown flag.
         with self.assertRaises(AssertionError):
-            flags.parse_and_merge_csv([ 'Z,foo' ])
+            flags.parse_and_merge_csv(['Z,foo'])
 
     def test_assign_flag(self):
         flags = FlagsDict()
         flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B'])
 
         # Test new additions.
-        flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ]))
-        self.assertEqual(flags.generate_csv(),
-            [ 'A,' + FLAG_SDK + "," + FLAG_UNSUPPORTED, 'B,' + FLAG_UNSUPPORTED ])
+        flags.assign_flag(FLAG_UNSUPPORTED, set(['A', 'B']))
+        self.assertEqual(
+            flags.generate_csv(),
+            ['A,' + FLAG_SDK + "," + FLAG_UNSUPPORTED, 'B,' + FLAG_UNSUPPORTED],
+        )
 
         # Test invalid API signature.
         with self.assertRaises(AssertionError):
-            flags.assign_flag(FLAG_SDK, set([ 'C' ]))
+            flags.assign_flag(FLAG_SDK, set(['C']))
 
         # Test invalid flag.
         with self.assertRaises(AssertionError):
-            flags.assign_flag('foo', set([ 'A' ]))
+            flags.assign_flag('foo', set(['A']))
 
     def test_extract_package(self):
         signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;'
@@ -100,5 +112,6 @@
         expected_package = 'com.foo_bar.baz'
         self.assertEqual(extract_package(signature), expected_package)
 
+
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/merge_csv.py b/scripts/hiddenapi/merge_csv.py
index a65326c..c17ec25 100755
--- a/scripts/hiddenapi/merge_csv.py
+++ b/scripts/hiddenapi/merge_csv.py
@@ -13,8 +13,7 @@
 # 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.
-"""
-Merge multiple CSV files, possibly with different columns.
+"""Merge multiple CSV files, possibly with different columns.
 """
 
 import argparse
@@ -26,34 +25,52 @@
 
 from zipfile import ZipFile
 
-args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
-args_parser.add_argument('--header', help='Comma separated field names; '
-                                          'if missing determines the header from input files.')
-args_parser.add_argument('--zip_input', help='Treat files as ZIP archives containing CSV files to merge.',
-                         action="store_true")
-args_parser.add_argument('--key_field', help='The name of the field by which the rows should be sorted. '
-                                             'Must be in the field names. '
-                                             'Will be the first field in the output. '
-                                             'All input files must be sorted by that field.')
-args_parser.add_argument('--output', help='Output file for merged CSV.',
-                         default='-', type=argparse.FileType('w'))
+args_parser = argparse.ArgumentParser(
+    description='Merge given CSV files into a single one.'
+)
+args_parser.add_argument(
+    '--header',
+    help='Comma separated field names; '
+    'if missing determines the header from input files.',
+)
+args_parser.add_argument(
+    '--zip_input',
+    help='Treat files as ZIP archives containing CSV files to merge.',
+    action="store_true",
+)
+args_parser.add_argument(
+    '--key_field',
+    help='The name of the field by which the rows should be sorted. '
+    'Must be in the field names. '
+    'Will be the first field in the output. '
+    'All input files must be sorted by that field.',
+)
+args_parser.add_argument(
+    '--output',
+    help='Output file for merged CSV.',
+    default='-',
+    type=argparse.FileType('w'),
+)
 args_parser.add_argument('files', nargs=argparse.REMAINDER)
 args = args_parser.parse_args()
 
 
-def dict_reader(input):
-    return csv.DictReader(input, delimiter=',', quotechar='|')
+def dict_reader(csvfile):
+    return csv.DictReader(csvfile, delimiter=',', quotechar='|')
+
 
 csv_readers = []
-if not(args.zip_input):
+if not args.zip_input:
     for file in args.files:
         csv_readers.append(dict_reader(open(file, 'r')))
 else:
     for file in args.files:
-        with ZipFile(file) as zip:
-            for entry in zip.namelist():
+        with ZipFile(file) as zipfile:
+            for entry in zipfile.namelist():
                 if entry.endswith('.uau'):
-                    csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
+                    csv_readers.append(
+                        dict_reader(io.TextIOWrapper(zipfile.open(entry, 'r')))
+                    )
 
 if args.header:
     fieldnames = args.header.split(',')
@@ -73,8 +90,8 @@
     keyField = args.key_field
     if keyField:
         assert keyField in fieldnames, (
-            "--key_field {} not found, must be one of {}\n").format(
-            keyField, ",".join(fieldnames))
+            "--key_field {} not found, must be one of {}\n"
+        ).format(keyField, ",".join(fieldnames))
         # Make the key field the first field in the output
         keyFieldIndex = fieldnames.index(args.key_field)
         fieldnames.insert(0, fieldnames.pop(keyFieldIndex))
@@ -83,11 +100,17 @@
         all_rows = heapq.merge(*csv_readers, key=operator.itemgetter(keyField))
 
 # Write all rows from the input files to the output:
-writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
-                        dialect='unix', fieldnames=fieldnames)
+writer = csv.DictWriter(
+    args.output,
+    delimiter=',',
+    quotechar='|',
+    quoting=csv.QUOTE_MINIMAL,
+    dialect='unix',
+    fieldnames=fieldnames,
+)
 writer.writeheader()
 
 # Read all the rows from the input and write them to the output in the correct
 # order:
 for row in all_rows:
-  writer.writerow(row)
+    writer.writerow(row)
diff --git a/scripts/hiddenapi/signature_patterns.py b/scripts/hiddenapi/signature_patterns.py
index a7c5bb4..0acb2a0 100755
--- a/scripts/hiddenapi/signature_patterns.py
+++ b/scripts/hiddenapi/signature_patterns.py
@@ -13,22 +13,26 @@
 # 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.
-"""
-Generate a set of signature patterns from the modular flags generated by a
+"""Generate a set of signature patterns from the modular flags generated by a
 bootclasspath_fragment that can be used to select a subset of monolithic flags
 against which the modular flags can be compared.
 """
 
 import argparse
 import csv
+import sys
 
-def dict_reader(input):
-    return csv.DictReader(input, delimiter=',', quotechar='|', fieldnames=['signature'])
+def dict_reader(csvfile):
+    return csv.DictReader(
+        csvfile, delimiter=',', quotechar='|', fieldnames=['signature']
+    )
+
 
 def produce_patterns_from_file(file):
     with open(file, 'r') as f:
         return produce_patterns_from_stream(f)
 
+
 def produce_patterns_from_stream(stream):
     # Read in all the signatures into a list and remove member names.
     patterns = set()
@@ -38,18 +42,26 @@
         # Remove the class specific member signature
         pieces = text.split(";->")
         qualifiedClassName = pieces[0]
-            # Remove inner class names as they cannot be separated from the containing outer class.
+        # Remove inner class names as they cannot be separated
+        # from the containing outer class.
         pieces = qualifiedClassName.split("$", maxsplit=1)
         pattern = pieces[0]
         patterns.add(pattern)
 
-    patterns = list(patterns)
+    patterns = list(patterns) #pylint: disable=redefined-variable-type
     patterns.sort()
     return patterns
 
+
 def main(args):
-    args_parser = argparse.ArgumentParser(description='Generate a set of signature patterns that select a subset of monolithic hidden API files.')
-    args_parser.add_argument('--flags', help='The stub flags file which contains an entry for every dex member')
+    args_parser = argparse.ArgumentParser(
+        description='Generate a set of signature patterns '
+        'that select a subset of monolithic hidden API files.'
+    )
+    args_parser.add_argument(
+        '--flags',
+        help='The stub flags file which contains an entry for every dex member',
+    )
     args_parser.add_argument('--output', help='Generated signature prefixes')
     args = args_parser.parse_args(args)
 
@@ -62,5 +74,6 @@
             outputFile.write(pattern)
             outputFile.write("\n")
 
+
 if __name__ == "__main__":
     main(sys.argv[1:])
diff --git a/scripts/hiddenapi/signature_patterns_test.py b/scripts/hiddenapi/signature_patterns_test.py
index 0431f45..3babe54 100755
--- a/scripts/hiddenapi/signature_patterns_test.py
+++ b/scripts/hiddenapi/signature_patterns_test.py
@@ -18,21 +18,25 @@
 import io
 import unittest
 
-from signature_patterns import *
+from signature_patterns import * #pylint: disable=unused-wildcard-import,wildcard-import
+
 
 class TestGeneratedPatterns(unittest.TestCase):
-
-    def produce_patterns_from_string(self, csv):
-        with io.StringIO(csv) as f:
+    def produce_patterns_from_string(self, csvdata):
+        with io.StringIO(csvdata) as f:
             return produce_patterns_from_stream(f)
 
     def test_generate(self):
-        patterns = self.produce_patterns_from_string('''
+        #pylint: disable=line-too-long
+        patterns = self.produce_patterns_from_string(
+            '''
 Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
 Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,public-api
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
 Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
-''')
+'''
+        )
+        #pylint: enable=line-too-long
         expected = [
             "java/lang/Character",
             "java/lang/Object",
@@ -40,5 +44,6 @@
         ]
         self.assertEqual(expected, patterns)
 
+
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py
index 6432bf1..4cd7e63 100755
--- a/scripts/hiddenapi/verify_overlaps.py
+++ b/scripts/hiddenapi/verify_overlaps.py
@@ -13,8 +13,7 @@
 # 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.
-"""
-Verify that one set of hidden API flags is a subset of another.
+"""Verify that one set of hidden API flags is a subset of another.
 """
 
 import argparse
@@ -22,9 +21,9 @@
 import sys
 from itertools import chain
 
+#pylint: disable=line-too-long
 class InteriorNode:
-    """
-    An interior node in a trie.
+    """An interior node in a trie.
 
     Each interior node has a dict that maps from an element of a signature to
     either another interior node or a leaf. Each interior node represents either
@@ -52,19 +51,21 @@
 
     Attributes:
         nodes: a dict from an element of the signature to the Node/Leaf
-        containing the next element/value.
+          containing the next element/value.
     """
+    #pylint: enable=line-too-long
+
     def __init__(self):
         self.nodes = {}
 
+    #pylint: disable=line-too-long
     def signatureToElements(self, signature):
-        """
-        Split a signature or a prefix into a number of elements:
+        """Split a signature or a prefix into a number of elements:
         1. The packages (excluding the leading L preceding the first package).
         2. The class names, from outermost to innermost.
         3. The member signature.
-
-        e.g. Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+        e.g.
+        Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
         will be broken down into these elements:
         1. package:java
         2. package:lang
@@ -88,19 +89,21 @@
         elements = parts[0].split("/")
         packages = elements[0:-1]
         className = elements[-1]
-        if className == "*" or className == "**":
+        if className in ("*" , "**"): #pylint: disable=no-else-return
             # Cannot specify a wildcard and target a specific member
             if len(member) != 0:
-                raise Exception("Invalid signature %s: contains wildcard %s and member signature %s"
-                                % (signature, className, member[0]))
+                raise Exception(
+                    "Invalid signature %s: contains wildcard %s and member " \
+                    "signature %s"
+                    % (signature, className, member[0]))
             wildcard = [className]
             # Assemble the parts into a single list, adding prefixes to identify
             # the different parts.
             #  0 - package:java
             #  1 - package:lang
             #  2 - *
-            return list(chain(map(lambda x : "package:" + x, packages),
-                              wildcard))
+            return list(
+                chain(["package:" + x for x in packages], wildcard))
         else:
             # Split the class name into outer / inner classes
             #  0 - Character
@@ -113,13 +116,16 @@
             #  2 - class:Character
             #  3 - class:UnicodeScript
             #  4 - member:of(I)Ljava/lang/Character$UnicodeScript;
-            return list(chain(map(lambda x : "package:" + x, packages),
-                              map(lambda x : "class:" + x, classes),
-                              map(lambda x : "member:" + x, member)))
+            return list(
+                chain(
+                    ["package:" + x for x in packages],
+                    ["class:" + x for x in classes],
+                    ["member:" + x for x in member]))
+    #pylint: enable=line-too-long
 
     def add(self, signature, value):
-        """
-        Associate the value with the specific signature.
+        """Associate the value with the specific signature.
+
         :param signature: the member signature
         :param value: the value to associated with the signature
         :return: n/a
@@ -132,21 +138,22 @@
             if element in node.nodes:
                 node = node.nodes[element]
             else:
-                next = InteriorNode()
-                node.nodes[element] = next
-                node = next
+                next_node = InteriorNode()
+                node.nodes[element] = next_node
+                node = next_node
         # Add a Leaf containing the value and associate it with the member
         # signature within the class.
         lastElement = elements[-1]
         if not lastElement.startswith("member:"):
-            raise Exception("Invalid signature: %s, does not identify a specific member" % signature)
+            raise Exception(
+                "Invalid signature: %s, does not identify a specific member" %
+                signature)
         if lastElement in node.nodes:
             raise Exception("Duplicate signature: %s" % signature)
         node.nodes[lastElement] = Leaf(value)
 
     def getMatchingRows(self, pattern):
-        """
-        Get the values (plural) associated with the pattern.
+        """Get the values (plural) associated with the pattern.
 
         e.g. If the pattern is a full signature then this will return a list
         containing the value associated with that signature.
@@ -175,13 +182,13 @@
         elements = self.signatureToElements(pattern)
         node = self
         # Include all values from this node and all its children.
-        selector = lambda x : True
+        selector = lambda x: True
         lastElement = elements[-1]
-        if lastElement == "*" or lastElement == "**":
+        if lastElement in ("*", "**"):
             elements = elements[:-1]
             if lastElement == "*":
                 # Do not include values from sub-packages.
-                selector = lambda x : not x.startswith("package:")
+                selector = lambda x: not x.startswith("package:")
         for element in elements:
             if element in node.nodes:
                 node = node.nodes[element]
@@ -190,19 +197,18 @@
         return chain.from_iterable(node.values(selector))
 
     def values(self, selector):
-        """
-        :param selector: a function that can be applied to a key in the nodes
+        """:param selector: a function that can be applied to a key in the nodes
         attribute to determine whether to return its values.
-        :return: A list of iterables of all the values associated with this
-        node and its children.
+
+        :return: A list of iterables of all the values associated with
+        this node and its children.
         """
         values = []
         self.appendValues(values, selector)
         return values
 
     def appendValues(self, values, selector):
-        """
-        Append the values associated with this node and its children to the
+        """Append the values associated with this node and its children to the
         list.
 
         For each item (key, child) in nodes the child node's values are returned
@@ -216,105 +222,116 @@
         """
         for key, node in self.nodes.items():
             if selector(key):
-                node.appendValues(values, lambda x : True)
+                node.appendValues(values, lambda x: True)
+
 
 class Leaf:
-    """
-    A leaf of the trie
+    """A leaf of the trie
 
     Attributes:
         value: the value associated with this leaf.
     """
+
     def __init__(self, value):
         self.value = value
 
-    def values(self, selector):
-        """
-        :return: A list of a list of the value associated with this node.
+    def values(self, selector): #pylint: disable=unused-argument
+        """:return: A list of a list of the value associated with this node.
         """
         return [[self.value]]
 
-    def appendValues(self, values, selector):
-        """
-        Appends a list of the value associated with this node to the list.
+    def appendValues(self, values, selector): #pylint: disable=unused-argument
+        """Appends a list of the value associated with this node to the list.
+
         :param values: a list of a iterables of values.
         """
         values.append([self.value])
 
-def dict_reader(input):
-    return csv.DictReader(input, delimiter=',', quotechar='|', fieldnames=['signature'])
+
+def dict_reader(csvfile):
+    return csv.DictReader(
+        csvfile, delimiter=",", quotechar="|", fieldnames=["signature"])
+
 
 def read_flag_trie_from_file(file):
-    with open(file, 'r') as stream:
+    with open(file, "r") as stream:
         return read_flag_trie_from_stream(stream)
 
+
 def read_flag_trie_from_stream(stream):
     trie = InteriorNode()
     reader = dict_reader(stream)
     for row in reader:
-        signature = row['signature']
+        signature = row["signature"]
         trie.add(signature, row)
     return trie
 
-def extract_subset_from_monolithic_flags_as_dict_from_file(monolithicTrie, patternsFile):
-    """
-    Extract a subset of flags from the dict containing all the monolithic flags.
+
+def extract_subset_from_monolithic_flags_as_dict_from_file(
+        monolithicTrie, patternsFile):
+    """Extract a subset of flags from the dict containing all the monolithic
+    flags.
 
     :param monolithicFlagsDict: the dict containing all the monolithic flags.
     :param patternsFile: a file containing a list of signature patterns that
     define the subset.
     :return: the dict from signature to row.
     """
-    with open(patternsFile, 'r') as stream:
-        return extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicTrie, stream)
+    with open(patternsFile, "r") as stream:
+        return extract_subset_from_monolithic_flags_as_dict_from_stream(
+            monolithicTrie, stream)
 
-def extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicTrie, stream):
-    """
-    Extract a subset of flags from the trie containing all the monolithic flags.
+
+def extract_subset_from_monolithic_flags_as_dict_from_stream(
+        monolithicTrie, stream):
+    """Extract a subset of flags from the trie containing all the monolithic
+    flags.
 
     :param monolithicTrie: the trie containing all the monolithic flags.
     :param stream: a stream containing a list of signature patterns that define
     the subset.
     :return: the dict from signature to row.
     """
-    dict = {}
+    dict_signature_to_row = {}
     for pattern in stream:
         pattern = pattern.rstrip()
         rows = monolithicTrie.getMatchingRows(pattern)
         for row in rows:
-            signature = row['signature']
-            dict[signature] = row
-    return dict
+            signature = row["signature"]
+            dict_signature_to_row[signature] = row
+    return dict_signature_to_row
+
 
 def read_signature_csv_from_stream_as_dict(stream):
-    """
-    Read the csv contents from the stream into a dict. The first column is assumed to be the
-    signature and used as the key. The whole row is stored as the value.
+    """Read the csv contents from the stream into a dict. The first column is
+    assumed to be the signature and used as the key.
 
+    The whole row is stored as the value.
     :param stream: the csv contents to read
     :return: the dict from signature to row.
     """
-    dict = {}
+    dict_signature_to_row = {}
     reader = dict_reader(stream)
     for row in reader:
-        signature = row['signature']
-        dict[signature] = row
-    return dict
+        signature = row["signature"]
+        dict_signature_to_row[signature] = row
+    return dict_signature_to_row
+
 
 def read_signature_csv_from_file_as_dict(csvFile):
-    """
-    Read the csvFile into a dict. The first column is assumed to be the
-    signature and used as the key. The whole row is stored as the value.
+    """Read the csvFile into a dict. The first column is assumed to be the
+    signature and used as the key.
 
+    The whole row is stored as the value.
     :param csvFile: the csv file to read
     :return: the dict from signature to row.
     """
-    with open(csvFile, 'r') as f:
+    with open(csvFile, "r") as f:
         return read_signature_csv_from_stream_as_dict(f)
 
+
 def compare_signature_flags(monolithicFlagsDict, modularFlagsDict):
-    """
-    Compare the signature flags between the two dicts.
+    """Compare the signature flags between the two dicts.
 
     :param monolithicFlagsDict: the dict containing the subset of the monolithic
     flags that should be equal to the modular flags.
@@ -327,7 +344,8 @@
     mismatchingSignatures = []
     # Create a sorted set of all the signatures from both the monolithic and
     # modular dicts.
-    allSignatures = sorted(set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys())))
+    allSignatures = sorted(
+        set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys())))
     for signature in allSignatures:
         monolithicRow = monolithicFlagsDict.get(signature, {})
         monolithicFlags = monolithicRow.get(None, [])
@@ -337,13 +355,21 @@
         else:
             modularFlags = ["blocked"]
         if monolithicFlags != modularFlags:
-            mismatchingSignatures.append((signature, modularFlags, monolithicFlags))
+            mismatchingSignatures.append(
+                (signature, modularFlags, monolithicFlags))
     return mismatchingSignatures
 
+
 def main(argv):
-    args_parser = argparse.ArgumentParser(description='Verify that sets of hidden API flags are each a subset of the monolithic flag file.')
-    args_parser.add_argument('monolithicFlags', help='The monolithic flag file')
-    args_parser.add_argument('modularFlags', nargs=argparse.REMAINDER, help='Flags produced by individual bootclasspath_fragment modules')
+    args_parser = argparse.ArgumentParser(
+        description="Verify that sets of hidden API flags are each a subset of "
+        "the monolithic flag file."
+    )
+    args_parser.add_argument("monolithicFlags", help="The monolithic flag file")
+    args_parser.add_argument(
+        "modularFlags",
+        nargs=argparse.REMAINDER,
+        help="Flags produced by individual bootclasspath_fragment modules")
     args = args_parser.parse_args(argv[1:])
 
     # Read in all the flags into the trie
@@ -358,9 +384,13 @@
         parts = modularPair.split(":")
         modularFlagsPath = parts[0]
         modularPatternsPath = parts[1]
-        modularFlagsDict = read_signature_csv_from_file_as_dict(modularFlagsPath)
-        monolithicFlagsSubsetDict = extract_subset_from_monolithic_flags_as_dict_from_file(monolithicTrie, modularPatternsPath)
-        mismatchingSignatures = compare_signature_flags(monolithicFlagsSubsetDict, modularFlagsDict)
+        modularFlagsDict = read_signature_csv_from_file_as_dict(
+            modularFlagsPath)
+        monolithicFlagsSubsetDict = \
+            extract_subset_from_monolithic_flags_as_dict_from_file(
+            monolithicTrie, modularPatternsPath)
+        mismatchingSignatures = compare_signature_flags(
+            monolithicFlagsSubsetDict, modularFlagsDict)
         if mismatchingSignatures:
             failed = True
             print("ERROR: Hidden API flags are inconsistent:")
@@ -369,11 +399,12 @@
             for mismatch in mismatchingSignatures:
                 signature = mismatch[0]
                 print()
-                print("< " + ",".join([signature]+ mismatch[1]))
-                print("> " + ",".join([signature]+ mismatch[2]))
+                print("< " + ",".join([signature] + mismatch[1]))
+                print("> " + ",".join([signature] + mismatch[2]))
 
     if failed:
         sys.exit(1)
 
+
 if __name__ == "__main__":
     main(sys.argv)
diff --git a/scripts/hiddenapi/verify_overlaps_test.py b/scripts/hiddenapi/verify_overlaps_test.py
index 00c0611..22a1cdf 100755
--- a/scripts/hiddenapi/verify_overlaps_test.py
+++ b/scripts/hiddenapi/verify_overlaps_test.py
@@ -13,12 +13,12 @@
 # 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.
-
 """Unit tests for verify_overlaps_test.py."""
 import io
 import unittest
 
-from verify_overlaps import *
+from verify_overlaps import * #pylint: disable=unused-wildcard-import,wildcard-import
+
 
 class TestSignatureToElements(unittest.TestCase):
 
@@ -34,8 +34,10 @@
             'class:1',
             'member:<init>()V',
         ]
-        self.assertEqual(expected, self.signatureToElements(
-            "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V"))
+        self.assertEqual(
+            expected,
+            self.signatureToElements(
+                'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V'))
 
     def test_signatureToElements_2(self):
         expected = [
@@ -44,8 +46,9 @@
             'class:Object',
             'member:hashCode()I',
         ]
-        self.assertEqual(expected, self.signatureToElements(
-            "Ljava/lang/Object;->hashCode()I"))
+        self.assertEqual(
+            expected,
+            self.signatureToElements('Ljava/lang/Object;->hashCode()I'))
 
     def test_signatureToElements_3(self):
         expected = [
@@ -56,39 +59,46 @@
             'class:ExternalSyntheticLambda0',
             'member:<init>(Ljava/lang/CharSequence;)V',
         ]
-        self.assertEqual(expected, self.signatureToElements(
-            "Ljava/lang/CharSequence$$ExternalSyntheticLambda0;"
-            "-><init>(Ljava/lang/CharSequence;)V"))
+        self.assertEqual(
+            expected,
+            self.signatureToElements(
+                'Ljava/lang/CharSequence$$ExternalSyntheticLambda0;'
+                '-><init>(Ljava/lang/CharSequence;)V'))
 
+#pylint: disable=line-too-long
 class TestDetectOverlaps(unittest.TestCase):
 
-    def read_flag_trie_from_string(self, csv):
-        with io.StringIO(csv) as f:
+    def read_flag_trie_from_string(self, csvdata):
+        with io.StringIO(csvdata) as f:
             return read_flag_trie_from_stream(f)
 
-    def read_signature_csv_from_string_as_dict(self, csv):
-        with io.StringIO(csv) as f:
+    def read_signature_csv_from_string_as_dict(self, csvdata):
+        with io.StringIO(csvdata) as f:
             return read_signature_csv_from_stream_as_dict(f)
 
-    def extract_subset_from_monolithic_flags_as_dict_from_string(self, monolithic, patterns):
+    def extract_subset_from_monolithic_flags_as_dict_from_string(
+            self, monolithic, patterns):
         with io.StringIO(patterns) as f:
-            return extract_subset_from_monolithic_flags_as_dict_from_stream(monolithic, f)
+            return extract_subset_from_monolithic_flags_as_dict_from_stream(
+                monolithic, f)
 
-    extractInput = '''
+    extractInput = """
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
 Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
 Ljava/util/zip/ZipFile;-><clinit>()V,blocked
 Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,blocked
 Ljava/lang/Character;->serialVersionUID:J,sdk
 Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
-'''
+"""
 
     def test_extract_subset_signature(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'Ljava/lang/Object;->hashCode()I'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
             'Ljava/lang/Object;->hashCode()I': {
                 None: ['public-api', 'system-api', 'test-api'],
@@ -98,11 +108,13 @@
         self.assertEqual(expected, subset)
 
     def test_extract_subset_class(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'java/lang/Object'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
             'Ljava/lang/Object;->hashCode()I': {
                 None: ['public-api', 'system-api', 'test-api'],
@@ -116,16 +128,20 @@
         self.assertEqual(expected, subset)
 
     def test_extract_subset_outer_class(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'java/lang/Character'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-            },
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
             'Ljava/lang/Character;->serialVersionUID:J': {
                 None: ['sdk'],
                 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
@@ -134,30 +150,38 @@
         self.assertEqual(expected, subset)
 
     def test_extract_subset_nested_class(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'java/lang/Character$UnicodeScript'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-            },
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
         }
         self.assertEqual(expected, subset)
 
     def test_extract_subset_package(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'java/lang/*'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-            },
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
             'Ljava/lang/Character;->serialVersionUID:J': {
                 None: ['sdk'],
                 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
@@ -178,16 +202,20 @@
         self.assertEqual(expected, subset)
 
     def test_extract_subset_recursive_package(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'java/**'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-            },
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
             'Ljava/lang/Character;->serialVersionUID:J': {
                 None: ['sdk'],
                 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
@@ -212,47 +240,53 @@
         self.assertEqual(expected, subset)
 
     def test_extract_subset_invalid_pattern_wildcard_and_member(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'Ljava/lang/*;->hashCode()I'
 
         with self.assertRaises(Exception) as context:
-            self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
-        self.assertTrue("contains wildcard * and member signature hashCode()I" in str(context.exception))
+            self.extract_subset_from_monolithic_flags_as_dict_from_string(
+                monolithic, patterns)
+        self.assertTrue('contains wildcard * and member signature hashCode()I'
+                        in str(context.exception))
 
     def test_read_trie_duplicate(self):
         with self.assertRaises(Exception) as context:
-            self.read_flag_trie_from_string('''
+            self.read_flag_trie_from_string("""
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
 Ljava/lang/Object;->hashCode()I,blocked
-''')
-        self.assertTrue("Duplicate signature: Ljava/lang/Object;->hashCode()I" in str(context.exception))
+""")
+        self.assertTrue('Duplicate signature: Ljava/lang/Object;->hashCode()I'
+                        in str(context.exception))
 
     def test_read_trie_missing_member(self):
         with self.assertRaises(Exception) as context:
-            self.read_flag_trie_from_string('''
+            self.read_flag_trie_from_string("""
 Ljava/lang/Object,public-api,system-api,test-api
-''')
-        self.assertTrue("Invalid signature: Ljava/lang/Object, does not identify a specific member" in str(context.exception))
+""")
+        self.assertTrue(
+            'Invalid signature: Ljava/lang/Object, does not identify a specific member'
+            in str(context.exception))
 
     def test_match(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
-''')
-        modular = self.read_signature_csv_from_string_as_dict('''
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
-''')
+""")
         mismatches = compare_signature_flags(monolithic, modular)
         expected = []
         self.assertEqual(expected, mismatches)
 
     def test_mismatch_overlapping_flags(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api
-''')
-        modular = self.read_signature_csv_from_string_as_dict('''
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
-''')
+""")
         mismatches = compare_signature_flags(monolithic, modular)
         expected = [
             (
@@ -263,14 +297,13 @@
         ]
         self.assertEqual(expected, mismatches)
 
-
     def test_mismatch_monolithic_blocked(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
-''')
-        modular = self.read_signature_csv_from_string_as_dict('''
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
-''')
+""")
         mismatches = compare_signature_flags(monolithic, modular)
         expected = [
             (
@@ -282,12 +315,12 @@
         self.assertEqual(expected, mismatches)
 
     def test_mismatch_modular_blocked(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
-''')
-        modular = self.read_signature_csv_from_string_as_dict('''
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
-''')
+""")
         mismatches = compare_signature_flags(monolithic, modular)
         expected = [
             (
@@ -300,9 +333,9 @@
 
     def test_match_treat_missing_from_modular_as_blocked(self):
         monolithic = self.read_signature_csv_from_string_as_dict('')
-        modular = self.read_signature_csv_from_string_as_dict('''
+        modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
-''')
+""")
         mismatches = compare_signature_flags(monolithic, modular)
         expected = [
             (
@@ -314,9 +347,9 @@
         self.assertEqual(expected, mismatches)
 
     def test_mismatch_treat_missing_from_modular_as_blocked(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
-''')
+""")
         modular = {}
         mismatches = compare_signature_flags(monolithic, modular)
         expected = [
@@ -329,13 +362,14 @@
         self.assertEqual(expected, mismatches)
 
     def test_blocked_missing_from_modular(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->hashCode()I,blocked
-''')
+""")
         modular = {}
         mismatches = compare_signature_flags(monolithic, modular)
         expected = []
         self.assertEqual(expected, mismatches)
+#pylint: enable=line-too-long
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index 4ef4399..b4936b8 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -25,7 +25,6 @@
 import sys
 from xml.dom import minidom
 
-
 from manifest import android_ns
 from manifest import get_children_with_tag
 from manifest import parse_manifest
@@ -33,49 +32,61 @@
 
 
 class ManifestMismatchError(Exception):
-  pass
+    pass
 
 
 def parse_args():
-  """Parse commandline arguments."""
+    """Parse commandline arguments."""
 
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--uses-library', dest='uses_libraries',
-                      action='append',
-                      help='specify uses-library entries known to the build system')
-  parser.add_argument('--optional-uses-library',
-                      dest='optional_uses_libraries',
-                      action='append',
-                      help='specify uses-library entries known to the build system with required:false')
-  parser.add_argument('--enforce-uses-libraries',
-                      dest='enforce_uses_libraries',
-                      action='store_true',
-                      help='check the uses-library entries known to the build system against the manifest')
-  parser.add_argument('--enforce-uses-libraries-relax',
-                      dest='enforce_uses_libraries_relax',
-                      action='store_true',
-                      help='do not fail immediately, just save the error message to file')
-  parser.add_argument('--enforce-uses-libraries-status',
-                      dest='enforce_uses_libraries_status',
-                      help='output file to store check status (error message)')
-  parser.add_argument('--extract-target-sdk-version',
-                      dest='extract_target_sdk_version',
-                      action='store_true',
-                      help='print the targetSdkVersion from the manifest')
-  parser.add_argument('--dexpreopt-config',
-                      dest='dexpreopt_configs',
-                      action='append',
-                      help='a paths to a dexpreopt.config of some library')
-  parser.add_argument('--aapt',
-                      dest='aapt',
-                      help='path to aapt executable')
-  parser.add_argument('--output', '-o', dest='output', help='output AndroidManifest.xml file')
-  parser.add_argument('input', help='input AndroidManifest.xml file')
-  return parser.parse_args()
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        '--uses-library',
+        dest='uses_libraries',
+        action='append',
+        help='specify uses-library entries known to the build system')
+    parser.add_argument(
+        '--optional-uses-library',
+        dest='optional_uses_libraries',
+        action='append',
+        help='specify uses-library entries known to the build system with '
+        'required:false'
+    )
+    parser.add_argument(
+        '--enforce-uses-libraries',
+        dest='enforce_uses_libraries',
+        action='store_true',
+        help='check the uses-library entries known to the build system against '
+        'the manifest'
+    )
+    parser.add_argument(
+        '--enforce-uses-libraries-relax',
+        dest='enforce_uses_libraries_relax',
+        action='store_true',
+        help='do not fail immediately, just save the error message to file')
+    parser.add_argument(
+        '--enforce-uses-libraries-status',
+        dest='enforce_uses_libraries_status',
+        help='output file to store check status (error message)')
+    parser.add_argument(
+        '--extract-target-sdk-version',
+        dest='extract_target_sdk_version',
+        action='store_true',
+        help='print the targetSdkVersion from the manifest')
+    parser.add_argument(
+        '--dexpreopt-config',
+        dest='dexpreopt_configs',
+        action='append',
+        help='a paths to a dexpreopt.config of some library')
+    parser.add_argument('--aapt', dest='aapt', help='path to aapt executable')
+    parser.add_argument(
+        '--output', '-o', dest='output', help='output AndroidManifest.xml file')
+    parser.add_argument('input', help='input AndroidManifest.xml file')
+    return parser.parse_args()
 
 
 def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
-  """Verify that the <uses-library> tags in the manifest match those provided
+    """Verify that the <uses-library> tags in the manifest match those provided
+
   by the build system.
 
   Args:
@@ -84,274 +95,294 @@
     optional: optional libs known to the build system
     relax:    if true, suppress error on mismatch and just write it to file
     is_apk:   if the manifest comes from an APK or an XML file
-  """
-  if is_apk:
-    manifest_required, manifest_optional, tags = extract_uses_libs_apk(manifest)
-  else:
-    manifest_required, manifest_optional, tags = extract_uses_libs_xml(manifest)
+    """
+    if is_apk:
+        manifest_required, manifest_optional, tags = extract_uses_libs_apk(
+            manifest)
+    else:
+        manifest_required, manifest_optional, tags = extract_uses_libs_xml(
+            manifest)
 
-  # Trim namespace component. Normally Soong does that automatically when it
-  # handles module names specified in Android.bp properties. However not all
-  # <uses-library> entries in the manifest correspond to real modules: some of
-  # the optional libraries may be missing at build time. Therefor this script
-  # accepts raw module names as spelled in Android.bp/Amdroid.mk and trims the
-  # optional namespace part manually.
-  required = trim_namespace_parts(required)
-  optional = trim_namespace_parts(optional)
+    # Trim namespace component. Normally Soong does that automatically when it
+    # handles module names specified in Android.bp properties. However not all
+    # <uses-library> entries in the manifest correspond to real modules: some of
+    # the optional libraries may be missing at build time. Therefor this script
+    # accepts raw module names as spelled in Android.bp/Amdroid.mk and trims the
+    # optional namespace part manually.
+    required = trim_namespace_parts(required)
+    optional = trim_namespace_parts(optional)
 
-  if manifest_required == required and manifest_optional == optional:
-    return None
+    if manifest_required == required and manifest_optional == optional:
+        return None
 
-  errmsg = ''.join([
-    'mismatch in the <uses-library> tags between the build system and the '
-      'manifest:\n',
-    '\t- required libraries in build system: [%s]\n' % ', '.join(required),
-    '\t                 vs. in the manifest: [%s]\n' % ', '.join(manifest_required),
-    '\t- optional libraries in build system: [%s]\n' % ', '.join(optional),
-    '\t                 vs. in the manifest: [%s]\n' % ', '.join(manifest_optional),
-    '\t- tags in the manifest (%s):\n' % path,
-    '\t\t%s\n' % '\t\t'.join(tags),
-      'note: the following options are available:\n',
-    '\t- to temporarily disable the check on command line, rebuild with ',
-      'RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" ',
-      'and disable AOT-compilation in dexpreopt)\n',
-    '\t- to temporarily disable the check for the whole product, set ',
-      'PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles\n',
-    '\t- to fix the check, make build system properties coherent with the '
-      'manifest\n',
-    '\t- see build/make/Changes.md for details\n'])
+    #pylint: disable=line-too-long
+    errmsg = ''.join([
+        'mismatch in the <uses-library> tags between the build system and the '
+        'manifest:\n',
+        '\t- required libraries in build system: [%s]\n' % ', '.join(required),
+        '\t                 vs. in the manifest: [%s]\n' %
+        ', '.join(manifest_required),
+        '\t- optional libraries in build system: [%s]\n' % ', '.join(optional),
+        '\t                 vs. in the manifest: [%s]\n' %
+        ', '.join(manifest_optional),
+        '\t- tags in the manifest (%s):\n' % path,
+        '\t\t%s\n' % '\t\t'.join(tags),
+        'note: the following options are available:\n',
+        '\t- to temporarily disable the check on command line, rebuild with ',
+        'RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" ',
+        'and disable AOT-compilation in dexpreopt)\n',
+        '\t- to temporarily disable the check for the whole product, set ',
+        'PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles\n',
+        '\t- to fix the check, make build system properties coherent with the '
+        'manifest\n', '\t- see build/make/Changes.md for details\n'
+    ])
+    #pylint: enable=line-too-long
 
-  if not relax:
-    raise ManifestMismatchError(errmsg)
+    if not relax:
+        raise ManifestMismatchError(errmsg)
 
-  return errmsg
+    return errmsg
 
 
-MODULE_NAMESPACE = re.compile("^//[^:]+:")
+MODULE_NAMESPACE = re.compile('^//[^:]+:')
+
 
 def trim_namespace_parts(modules):
-  """Trim the namespace part of each module, if present. Leave only the name."""
+    """Trim the namespace part of each module, if present.
 
-  trimmed = []
-  for module in modules:
-    trimmed.append(MODULE_NAMESPACE.sub('', module))
-  return trimmed
+    Leave only the name.
+    """
+
+    trimmed = []
+    for module in modules:
+        trimmed.append(MODULE_NAMESPACE.sub('', module))
+    return trimmed
 
 
 def extract_uses_libs_apk(badging):
-  """Extract <uses-library> tags from the manifest of an APK."""
+    """Extract <uses-library> tags from the manifest of an APK."""
 
-  pattern = re.compile("^uses-library(-not-required)?:'(.*)'$", re.MULTILINE)
+    pattern = re.compile("^uses-library(-not-required)?:'(.*)'$", re.MULTILINE)
 
-  required = []
-  optional = []
-  lines = []
-  for match in re.finditer(pattern, badging):
-    lines.append(match.group(0))
-    libname = match.group(2)
-    if match.group(1) == None:
-      required.append(libname)
-    else:
-      optional.append(libname)
+    required = []
+    optional = []
+    lines = []
+    for match in re.finditer(pattern, badging):
+        lines.append(match.group(0))
+        libname = match.group(2)
+        if match.group(1) is None:
+            required.append(libname)
+        else:
+            optional.append(libname)
 
-  required = first_unique_elements(required)
-  optional = first_unique_elements(optional)
-  tags = first_unique_elements(lines)
-  return required, optional, tags
+    required = first_unique_elements(required)
+    optional = first_unique_elements(optional)
+    tags = first_unique_elements(lines)
+    return required, optional, tags
 
 
-def extract_uses_libs_xml(xml):
-  """Extract <uses-library> tags from the manifest."""
+def extract_uses_libs_xml(xml): #pylint: disable=inconsistent-return-statements
+    """Extract <uses-library> tags from the manifest."""
 
-  manifest = parse_manifest(xml)
-  elems = get_children_with_tag(manifest, 'application')
-  application = elems[0] if len(elems) == 1 else None
-  if len(elems) > 1:
-    raise RuntimeError('found multiple <application> tags')
-  elif not elems:
-    if uses_libraries or optional_uses_libraries:
-      raise ManifestMismatchError('no <application> tag found')
-    return
+    manifest = parse_manifest(xml)
+    elems = get_children_with_tag(manifest, 'application')
+    application = elems[0] if len(elems) == 1 else None
+    if len(elems) > 1: #pylint: disable=no-else-raise
+        raise RuntimeError('found multiple <application> tags')
+    elif not elems:
+        if uses_libraries or optional_uses_libraries: #pylint: disable=undefined-variable
+            raise ManifestMismatchError('no <application> tag found')
+        return
 
-  libs = get_children_with_tag(application, 'uses-library')
+    libs = get_children_with_tag(application, 'uses-library')
 
-  required = [uses_library_name(x) for x in libs if uses_library_required(x)]
-  optional = [uses_library_name(x) for x in libs if not uses_library_required(x)]
+    required = [uses_library_name(x) for x in libs if uses_library_required(x)]
+    optional = [
+        uses_library_name(x) for x in libs if not uses_library_required(x)
+    ]
 
-  # render <uses-library> tags as XML for a pretty error message
-  tags = []
-  for lib in libs:
-    tags.append(lib.toprettyxml())
+    # render <uses-library> tags as XML for a pretty error message
+    tags = []
+    for lib in libs:
+        tags.append(lib.toprettyxml())
 
-  required = first_unique_elements(required)
-  optional = first_unique_elements(optional)
-  tags = first_unique_elements(tags)
-  return required, optional, tags
+    required = first_unique_elements(required)
+    optional = first_unique_elements(optional)
+    tags = first_unique_elements(tags)
+    return required, optional, tags
 
 
 def first_unique_elements(l):
-  result = []
-  [result.append(x) for x in l if x not in result]
-  return result
+    result = []
+    for x in l:
+        if x not in result:
+            result.append(x)
+    return result
 
 
 def uses_library_name(lib):
-  """Extract the name attribute of a uses-library tag.
+    """Extract the name attribute of a uses-library tag.
 
   Args:
     lib: a <uses-library> tag.
-  """
-  name = lib.getAttributeNodeNS(android_ns, 'name')
-  return name.value if name is not None else ""
+    """
+    name = lib.getAttributeNodeNS(android_ns, 'name')
+    return name.value if name is not None else ''
 
 
 def uses_library_required(lib):
-  """Extract the required attribute of a uses-library tag.
+    """Extract the required attribute of a uses-library tag.
 
   Args:
     lib: a <uses-library> tag.
-  """
-  required = lib.getAttributeNodeNS(android_ns, 'required')
-  return (required.value == 'true') if required is not None else True
+    """
+    required = lib.getAttributeNodeNS(android_ns, 'required')
+    return (required.value == 'true') if required is not None else True
 
 
-def extract_target_sdk_version(manifest, is_apk = False):
-  """Returns the targetSdkVersion from the manifest.
+def extract_target_sdk_version(manifest, is_apk=False):
+    """Returns the targetSdkVersion from the manifest.
 
   Args:
     manifest: manifest (either parsed XML or aapt dump of APK)
     is_apk:   if the manifest comes from an APK or an XML file
-  """
-  if is_apk:
-    return extract_target_sdk_version_apk(manifest)
-  else:
-    return extract_target_sdk_version_xml(manifest)
+    """
+    if is_apk: #pylint: disable=no-else-return
+        return extract_target_sdk_version_apk(manifest)
+    else:
+        return extract_target_sdk_version_xml(manifest)
 
 
 def extract_target_sdk_version_apk(badging):
-  """Extract targetSdkVersion tags from the manifest of an APK."""
+    """Extract targetSdkVersion tags from the manifest of an APK."""
 
-  pattern = re.compile("^targetSdkVersion?:'(.*)'$", re.MULTILINE)
+    pattern = re.compile("^targetSdkVersion?:'(.*)'$", re.MULTILINE)
 
-  for match in re.finditer(pattern, badging):
-    return match.group(1)
+    for match in re.finditer(pattern, badging):
+        return match.group(1)
 
-  raise RuntimeError('cannot find targetSdkVersion in the manifest')
+    raise RuntimeError('cannot find targetSdkVersion in the manifest')
 
 
 def extract_target_sdk_version_xml(xml):
-  """Extract targetSdkVersion tags from the manifest."""
+    """Extract targetSdkVersion tags from the manifest."""
 
-  manifest = parse_manifest(xml)
+    manifest = parse_manifest(xml)
 
-  # Get or insert the uses-sdk element
-  uses_sdk = get_children_with_tag(manifest, 'uses-sdk')
-  if len(uses_sdk) > 1:
-    raise RuntimeError('found multiple uses-sdk elements')
-  elif len(uses_sdk) == 0:
-    raise RuntimeError('missing uses-sdk element')
+    # Get or insert the uses-sdk element
+    uses_sdk = get_children_with_tag(manifest, 'uses-sdk')
+    if len(uses_sdk) > 1: #pylint: disable=no-else-raise
+        raise RuntimeError('found multiple uses-sdk elements')
+    elif len(uses_sdk) == 0:
+        raise RuntimeError('missing uses-sdk element')
 
-  uses_sdk = uses_sdk[0]
+    uses_sdk = uses_sdk[0]
 
-  min_attr = uses_sdk.getAttributeNodeNS(android_ns, 'minSdkVersion')
-  if min_attr is None:
-    raise RuntimeError('minSdkVersion is not specified')
+    min_attr = uses_sdk.getAttributeNodeNS(android_ns, 'minSdkVersion')
+    if min_attr is None:
+        raise RuntimeError('minSdkVersion is not specified')
 
-  target_attr = uses_sdk.getAttributeNodeNS(android_ns, 'targetSdkVersion')
-  if target_attr is None:
-    target_attr = min_attr
+    target_attr = uses_sdk.getAttributeNodeNS(android_ns, 'targetSdkVersion')
+    if target_attr is None:
+        target_attr = min_attr
 
-  return target_attr.value
+    return target_attr.value
 
 
 def load_dexpreopt_configs(configs):
-  """Load dexpreopt.config files and map module names to library names."""
-  module_to_libname = {}
+    """Load dexpreopt.config files and map module names to library names."""
+    module_to_libname = {}
 
-  if configs is None:
-    configs = []
+    if configs is None:
+        configs = []
 
-  for config in configs:
-    with open(config, 'r') as f:
-      contents = json.load(f)
-    module_to_libname[contents['Name']] = contents['ProvidesUsesLibrary']
+    for config in configs:
+        with open(config, 'r') as f:
+            contents = json.load(f)
+        module_to_libname[contents['Name']] = contents['ProvidesUsesLibrary']
 
-  return module_to_libname
+    return module_to_libname
 
 
 def translate_libnames(modules, module_to_libname):
-  """Translate module names into library names using the mapping."""
-  if modules is None:
-    modules = []
+    """Translate module names into library names using the mapping."""
+    if modules is None:
+        modules = []
 
-  libnames = []
-  for name in modules:
-    if name in module_to_libname:
-      name = module_to_libname[name]
-    libnames.append(name)
+    libnames = []
+    for name in modules:
+        if name in module_to_libname:
+            name = module_to_libname[name]
+        libnames.append(name)
 
-  return libnames
+    return libnames
 
 
 def main():
-  """Program entry point."""
-  try:
-    args = parse_args()
+    """Program entry point."""
+    try:
+        args = parse_args()
 
-    # The input can be either an XML manifest or an APK, they are parsed and
-    # processed in different ways.
-    is_apk = args.input.endswith('.apk')
-    if is_apk:
-      aapt = args.aapt if args.aapt != None else "aapt"
-      manifest = subprocess.check_output([aapt, "dump", "badging", args.input])
-    else:
-      manifest = minidom.parse(args.input)
+        # The input can be either an XML manifest or an APK, they are parsed and
+        # processed in different ways.
+        is_apk = args.input.endswith('.apk')
+        if is_apk:
+            aapt = args.aapt if args.aapt is not None else 'aapt'
+            manifest = subprocess.check_output(
+                [aapt, 'dump', 'badging', args.input])
+        else:
+            manifest = minidom.parse(args.input)
 
-    if args.enforce_uses_libraries:
-      # Load dexpreopt.config files and build a mapping from module names to
-      # library names. This is necessary because build system addresses
-      # libraries by their module name (`uses_libs`, `optional_uses_libs`,
-      # `LOCAL_USES_LIBRARIES`, `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain
-      # module names), while the manifest addresses libraries by their name.
-      mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs)
-      required = translate_libnames(args.uses_libraries, mod_to_lib)
-      optional = translate_libnames(args.optional_uses_libraries, mod_to_lib)
+        if args.enforce_uses_libraries:
+            # Load dexpreopt.config files and build a mapping from module
+            # names to library names. This is necessary because build system
+            # addresses libraries by their module name (`uses_libs`,
+            # `optional_uses_libs`, `LOCAL_USES_LIBRARIES`,
+            # `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain module names), while
+            # the manifest addresses libraries by their name.
+            mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs)
+            required = translate_libnames(args.uses_libraries, mod_to_lib)
+            optional = translate_libnames(args.optional_uses_libraries,
+                                          mod_to_lib)
 
-      # Check if the <uses-library> lists in the build system agree with those
-      # in the manifest. Raise an exception on mismatch, unless the script was
-      # passed a special parameter to suppress exceptions.
-      errmsg = enforce_uses_libraries(manifest, required, optional,
-        args.enforce_uses_libraries_relax, is_apk, args.input)
+            # Check if the <uses-library> lists in the build system agree with
+            # those in the manifest. Raise an exception on mismatch, unless the
+            # script was passed a special parameter to suppress exceptions.
+            errmsg = enforce_uses_libraries(manifest, required, optional,
+                                            args.enforce_uses_libraries_relax,
+                                            is_apk, args.input)
 
-      # Create a status file that is empty on success, or contains an error
-      # message on failure. When exceptions are suppressed, dexpreopt command
-      # command will check file size to determine if the check has failed.
-      if args.enforce_uses_libraries_status:
-        with open(args.enforce_uses_libraries_status, 'w') as f:
-          if not errmsg == None:
-            f.write("%s\n" % errmsg)
+            # Create a status file that is empty on success, or contains an
+            # error message on failure. When exceptions are suppressed,
+            # dexpreopt command command will check file size to determine if
+            # the check has failed.
+            if args.enforce_uses_libraries_status:
+                with open(args.enforce_uses_libraries_status, 'w') as f:
+                    if errmsg is not None:
+                        f.write('%s\n' % errmsg)
 
-    if args.extract_target_sdk_version:
-      try:
-        print(extract_target_sdk_version(manifest, is_apk))
-      except:
-        # Failed; don't crash, return "any" SDK version. This will result in
-        # dexpreopt not adding any compatibility libraries.
-        print(10000)
+        if args.extract_target_sdk_version:
+            try:
+                print(extract_target_sdk_version(manifest, is_apk))
+            except: #pylint: disable=bare-except
+                # Failed; don't crash, return "any" SDK version. This will
+                # result in dexpreopt not adding any compatibility libraries.
+                print(10000)
 
-    if args.output:
-      # XML output is supposed to be written only when this script is invoked
-      # with XML input manifest, not with an APK.
-      if is_apk:
-        raise RuntimeError('cannot save APK manifest as XML')
+        if args.output:
+            # XML output is supposed to be written only when this script is
+            # invoked with XML input manifest, not with an APK.
+            if is_apk:
+                raise RuntimeError('cannot save APK manifest as XML')
 
-      with open(args.output, 'wb') as f:
-        write_xml(f, manifest)
+            with open(args.output, 'wb') as f:
+                write_xml(f, manifest)
 
-  # pylint: disable=broad-except
-  except Exception as err:
-    print('error: ' + str(err), file=sys.stderr)
-    sys.exit(-1)
+    # pylint: disable=broad-except
+    except Exception as err:
+        print('error: ' + str(err), file=sys.stderr)
+        sys.exit(-1)
+
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py
index e3e8ac4..3be7a30 100755
--- a/scripts/manifest_check_test.py
+++ b/scripts/manifest_check_test.py
@@ -26,202 +26,235 @@
 
 
 def uses_library_xml(name, attr=''):
-  return '<uses-library android:name="%s"%s />' % (name, attr)
+    return '<uses-library android:name="%s"%s />' % (name, attr)
 
 
 def required_xml(value):
-  return ' android:required="%s"' % ('true' if value else 'false')
+    return ' android:required="%s"' % ('true' if value else 'false')
 
 
 def uses_library_apk(name, sfx=''):
-  return "uses-library%s:'%s'" % (sfx, name)
+    return "uses-library%s:'%s'" % (sfx, name)
 
 
 def required_apk(value):
-  return '' if value else '-not-required'
+    return '' if value else '-not-required'
 
 
 class EnforceUsesLibrariesTest(unittest.TestCase):
-  """Unit tests for add_extract_native_libs function."""
+    """Unit tests for add_extract_native_libs function."""
 
-  def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[]):
-    doc = minidom.parseString(xml)
-    try:
-      relax = False
-      manifest_check.enforce_uses_libraries(doc, uses_libraries,
-        optional_uses_libraries, relax, False, 'path/to/X/AndroidManifest.xml')
-      manifest_check.enforce_uses_libraries(apk, uses_libraries,
-        optional_uses_libraries, relax, True, 'path/to/X/X.apk')
-      return True
-    except manifest_check.ManifestMismatchError:
-      return False
+    def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[]): #pylint: disable=dangerous-default-value
+        doc = minidom.parseString(xml)
+        try:
+            relax = False
+            manifest_check.enforce_uses_libraries(
+                doc, uses_libraries, optional_uses_libraries, relax, False,
+                'path/to/X/AndroidManifest.xml')
+            manifest_check.enforce_uses_libraries(apk, uses_libraries,
+                                                  optional_uses_libraries,
+                                                  relax, True,
+                                                  'path/to/X/X.apk')
+            return True
+        except manifest_check.ManifestMismatchError:
+            return False
 
-  xml_tmpl = (
-      '<?xml version="1.0" encoding="utf-8"?>\n'
-      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
-      '    <application>\n'
-      '    %s\n'
-      '    </application>\n'
-      '</manifest>\n')
+    xml_tmpl = (
+        '<?xml version="1.0" encoding="utf-8"?>\n<manifest '
+        'xmlns:android="http://schemas.android.com/apk/res/android">\n    '
+        '<application>\n    %s\n    </application>\n</manifest>\n')
 
-  apk_tmpl = (
-      "package: name='com.google.android.something' versionCode='100'\n"
-      "sdkVersion:'29'\n"
-      "targetSdkVersion:'29'\n"
-      "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n"
-      "%s\n"
-      "densities: '160' '240' '320' '480' '640' '65534")
+    apk_tmpl = (
+        "package: name='com.google.android.something' versionCode='100'\n"
+        "sdkVersion:'29'\n"
+        "targetSdkVersion:'29'\n"
+        "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n"
+        '%s\n'
+        "densities: '160' '240' '320' '480' '640' '65534")
 
-  def test_uses_library(self):
-    xml = self.xml_tmpl % (uses_library_xml('foo'))
-    apk = self.apk_tmpl % (uses_library_apk('foo'))
-    matches = self.run_test(xml, apk, uses_libraries=['foo'])
-    self.assertTrue(matches)
+    def test_uses_library(self):
+        xml = self.xml_tmpl % (uses_library_xml('foo'))
+        apk = self.apk_tmpl % (uses_library_apk('foo'))
+        matches = self.run_test(xml, apk, uses_libraries=['foo'])
+        self.assertTrue(matches)
 
-  def test_uses_library_required(self):
-    xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(True)))
-    apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(True)))
-    matches = self.run_test(xml, apk, uses_libraries=['foo'])
-    self.assertTrue(matches)
+    def test_uses_library_required(self):
+        xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(True)))
+        apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(True)))
+        matches = self.run_test(xml, apk, uses_libraries=['foo'])
+        self.assertTrue(matches)
 
-  def test_optional_uses_library(self):
-    xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
-    apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
-    matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
-    self.assertTrue(matches)
+    def test_optional_uses_library(self):
+        xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
+        apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
+        matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
+        self.assertTrue(matches)
 
-  def test_expected_uses_library(self):
-    xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
-    apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
-    matches = self.run_test(xml, apk, uses_libraries=['foo'])
-    self.assertFalse(matches)
+    def test_expected_uses_library(self):
+        xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
+        apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
+        matches = self.run_test(xml, apk, uses_libraries=['foo'])
+        self.assertFalse(matches)
 
-  def test_expected_optional_uses_library(self):
-    xml = self.xml_tmpl % (uses_library_xml('foo'))
-    apk = self.apk_tmpl % (uses_library_apk('foo'))
-    matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
-    self.assertFalse(matches)
+    def test_expected_optional_uses_library(self):
+        xml = self.xml_tmpl % (uses_library_xml('foo'))
+        apk = self.apk_tmpl % (uses_library_apk('foo'))
+        matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
+        self.assertFalse(matches)
 
-  def test_missing_uses_library(self):
-    xml = self.xml_tmpl % ('')
-    apk = self.apk_tmpl % ('')
-    matches = self.run_test(xml, apk, uses_libraries=['foo'])
-    self.assertFalse(matches)
+    def test_missing_uses_library(self):
+        xml = self.xml_tmpl % ('')
+        apk = self.apk_tmpl % ('')
+        matches = self.run_test(xml, apk, uses_libraries=['foo'])
+        self.assertFalse(matches)
 
-  def test_missing_optional_uses_library(self):
-    xml = self.xml_tmpl % ('')
-    apk = self.apk_tmpl % ('')
-    matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
-    self.assertFalse(matches)
+    def test_missing_optional_uses_library(self):
+        xml = self.xml_tmpl % ('')
+        apk = self.apk_tmpl % ('')
+        matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
+        self.assertFalse(matches)
 
-  def test_extra_uses_library(self):
-    xml = self.xml_tmpl % (uses_library_xml('foo'))
-    apk = self.apk_tmpl % (uses_library_xml('foo'))
-    matches = self.run_test(xml, apk)
-    self.assertFalse(matches)
+    def test_extra_uses_library(self):
+        xml = self.xml_tmpl % (uses_library_xml('foo'))
+        apk = self.apk_tmpl % (uses_library_xml('foo'))
+        matches = self.run_test(xml, apk)
+        self.assertFalse(matches)
 
-  def test_extra_optional_uses_library(self):
-    xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
-    apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
-    matches = self.run_test(xml, apk)
-    self.assertFalse(matches)
+    def test_extra_optional_uses_library(self):
+        xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
+        apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
+        matches = self.run_test(xml, apk)
+        self.assertFalse(matches)
 
-  def test_multiple_uses_library(self):
-    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
-                                      uses_library_xml('bar')]))
-    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
-                                      uses_library_apk('bar')]))
-    matches = self.run_test(xml, apk, uses_libraries=['foo', 'bar'])
-    self.assertTrue(matches)
+    def test_multiple_uses_library(self):
+        xml = self.xml_tmpl % ('\n'.join(
+            [uses_library_xml('foo'),
+             uses_library_xml('bar')]))
+        apk = self.apk_tmpl % ('\n'.join(
+            [uses_library_apk('foo'),
+             uses_library_apk('bar')]))
+        matches = self.run_test(xml, apk, uses_libraries=['foo', 'bar'])
+        self.assertTrue(matches)
 
-  def test_multiple_optional_uses_library(self):
-    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
-                                      uses_library_xml('bar', required_xml(False))]))
-    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
-                                      uses_library_apk('bar', required_apk(False))]))
-    matches = self.run_test(xml, apk, optional_uses_libraries=['foo', 'bar'])
-    self.assertTrue(matches)
+    def test_multiple_optional_uses_library(self):
+        xml = self.xml_tmpl % ('\n'.join([
+            uses_library_xml('foo', required_xml(False)),
+            uses_library_xml('bar', required_xml(False))
+        ]))
+        apk = self.apk_tmpl % ('\n'.join([
+            uses_library_apk('foo', required_apk(False)),
+            uses_library_apk('bar', required_apk(False))
+        ]))
+        matches = self.run_test(
+            xml, apk, optional_uses_libraries=['foo', 'bar'])
+        self.assertTrue(matches)
 
-  def test_order_uses_library(self):
-    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
-                                      uses_library_xml('bar')]))
-    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
-                                      uses_library_apk('bar')]))
-    matches = self.run_test(xml, apk, uses_libraries=['bar', 'foo'])
-    self.assertFalse(matches)
+    def test_order_uses_library(self):
+        xml = self.xml_tmpl % ('\n'.join(
+            [uses_library_xml('foo'),
+             uses_library_xml('bar')]))
+        apk = self.apk_tmpl % ('\n'.join(
+            [uses_library_apk('foo'),
+             uses_library_apk('bar')]))
+        matches = self.run_test(xml, apk, uses_libraries=['bar', 'foo'])
+        self.assertFalse(matches)
 
-  def test_order_optional_uses_library(self):
-    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
-                                      uses_library_xml('bar', required_xml(False))]))
-    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
-                                      uses_library_apk('bar', required_apk(False))]))
-    matches = self.run_test(xml, apk, optional_uses_libraries=['bar', 'foo'])
-    self.assertFalse(matches)
+    def test_order_optional_uses_library(self):
+        xml = self.xml_tmpl % ('\n'.join([
+            uses_library_xml('foo', required_xml(False)),
+            uses_library_xml('bar', required_xml(False))
+        ]))
+        apk = self.apk_tmpl % ('\n'.join([
+            uses_library_apk('foo', required_apk(False)),
+            uses_library_apk('bar', required_apk(False))
+        ]))
+        matches = self.run_test(
+            xml, apk, optional_uses_libraries=['bar', 'foo'])
+        self.assertFalse(matches)
 
-  def test_duplicate_uses_library(self):
-    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
-                                      uses_library_xml('foo')]))
-    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
-                                      uses_library_apk('foo')]))
-    matches = self.run_test(xml, apk, uses_libraries=['foo'])
-    self.assertTrue(matches)
+    def test_duplicate_uses_library(self):
+        xml = self.xml_tmpl % ('\n'.join(
+            [uses_library_xml('foo'),
+             uses_library_xml('foo')]))
+        apk = self.apk_tmpl % ('\n'.join(
+            [uses_library_apk('foo'),
+             uses_library_apk('foo')]))
+        matches = self.run_test(xml, apk, uses_libraries=['foo'])
+        self.assertTrue(matches)
 
-  def test_duplicate_optional_uses_library(self):
-    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
-                                      uses_library_xml('foo', required_xml(False))]))
-    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
-                                      uses_library_apk('foo', required_apk(False))]))
-    matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
-    self.assertTrue(matches)
+    def test_duplicate_optional_uses_library(self):
+        xml = self.xml_tmpl % ('\n'.join([
+            uses_library_xml('foo', required_xml(False)),
+            uses_library_xml('foo', required_xml(False))
+        ]))
+        apk = self.apk_tmpl % ('\n'.join([
+            uses_library_apk('foo', required_apk(False)),
+            uses_library_apk('foo', required_apk(False))
+        ]))
+        matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
+        self.assertTrue(matches)
 
-  def test_mixed(self):
-    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
-                                      uses_library_xml('bar', required_xml(False))]))
-    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
-                                      uses_library_apk('bar', required_apk(False))]))
-    matches = self.run_test(xml, apk, uses_libraries=['foo'],
-                            optional_uses_libraries=['bar'])
-    self.assertTrue(matches)
+    def test_mixed(self):
+        xml = self.xml_tmpl % ('\n'.join([
+            uses_library_xml('foo'),
+            uses_library_xml('bar', required_xml(False))
+        ]))
+        apk = self.apk_tmpl % ('\n'.join([
+            uses_library_apk('foo'),
+            uses_library_apk('bar', required_apk(False))
+        ]))
+        matches = self.run_test(
+            xml, apk, uses_libraries=['foo'], optional_uses_libraries=['bar'])
+        self.assertTrue(matches)
 
-  def test_mixed_with_namespace(self):
-    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
-                                      uses_library_xml('bar', required_xml(False))]))
-    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
-                                      uses_library_apk('bar', required_apk(False))]))
-    matches = self.run_test(xml, apk, uses_libraries=['//x/y/z:foo'],
-                            optional_uses_libraries=['//x/y/z:bar'])
-    self.assertTrue(matches)
+    def test_mixed_with_namespace(self):
+        xml = self.xml_tmpl % ('\n'.join([
+            uses_library_xml('foo'),
+            uses_library_xml('bar', required_xml(False))
+        ]))
+        apk = self.apk_tmpl % ('\n'.join([
+            uses_library_apk('foo'),
+            uses_library_apk('bar', required_apk(False))
+        ]))
+        matches = self.run_test(
+            xml,
+            apk,
+            uses_libraries=['//x/y/z:foo'],
+            optional_uses_libraries=['//x/y/z:bar'])
+        self.assertTrue(matches)
 
 
 class ExtractTargetSdkVersionTest(unittest.TestCase):
-  def run_test(self, xml, apk, version):
-    doc = minidom.parseString(xml)
-    v = manifest_check.extract_target_sdk_version(doc, is_apk=False)
-    self.assertEqual(v, version)
-    v = manifest_check.extract_target_sdk_version(apk, is_apk=True)
-    self.assertEqual(v, version)
 
-  xml_tmpl = (
-      '<?xml version="1.0" encoding="utf-8"?>\n'
-      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
-      '    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="%s" />\n'
-      '</manifest>\n')
+    def run_test(self, xml, apk, version):
+        doc = minidom.parseString(xml)
+        v = manifest_check.extract_target_sdk_version(doc, is_apk=False)
+        self.assertEqual(v, version)
+        v = manifest_check.extract_target_sdk_version(apk, is_apk=True)
+        self.assertEqual(v, version)
 
-  apk_tmpl = (
-      "package: name='com.google.android.something' versionCode='100'\n"
-      "sdkVersion:'28'\n"
-      "targetSdkVersion:'%s'\n"
-      "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n")
+    xml_tmpl = (
+        '<?xml version="1.0" encoding="utf-8"?>\n<manifest '
+        'xmlns:android="http://schemas.android.com/apk/res/android">\n    '
+        '<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="%s" '
+        '/>\n</manifest>\n')
 
-  def test_targert_sdk_version_28(self):
-    xml = self.xml_tmpl % "28"
-    apk = self.apk_tmpl % "28"
-    self.run_test(xml, apk, "28")
+    apk_tmpl = (
+        "package: name='com.google.android.something' versionCode='100'\n"
+        "sdkVersion:'28'\n"
+        "targetSdkVersion:'%s'\n"
+        "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n")
 
-  def test_targert_sdk_version_29(self):
-    xml = self.xml_tmpl % "29"
-    apk = self.apk_tmpl % "29"
-    self.run_test(xml, apk, "29")
+    def test_targert_sdk_version_28(self):
+        xml = self.xml_tmpl % '28'
+        apk = self.apk_tmpl % '28'
+        self.run_test(xml, apk, '28')
+
+    def test_targert_sdk_version_29(self):
+        xml = self.xml_tmpl % '29'
+        apk = self.apk_tmpl % '29'
+        self.run_test(xml, apk, '29')
+
 
 if __name__ == '__main__':
-  unittest.main(verbosity=2)
+    unittest.main(verbosity=2)
diff --git a/sdk/Android.bp b/sdk/Android.bp
index 368c03a..0c9bf27 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -16,6 +16,7 @@
     srcs: [
         "bp.go",
         "exports.go",
+        "member_type.go",
         "sdk.go",
         "update.go",
     ],
diff --git a/sdk/member_type.go b/sdk/member_type.go
new file mode 100644
index 0000000..9aab61d
--- /dev/null
+++ b/sdk/member_type.go
@@ -0,0 +1,164 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// 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 sdk
+
+import (
+	"reflect"
+
+	"android/soong/android"
+	"github.com/google/blueprint/proptools"
+)
+
+// Contains information about the sdk properties that list sdk members by type, e.g.
+// Java_header_libs.
+type sdkMemberTypeListProperty struct {
+	// getter for the list of member names
+	getter func(properties interface{}) []string
+
+	// setter for the list of member names
+	setter func(properties interface{}, list []string)
+
+	// the type of member referenced in the list
+	memberType android.SdkMemberType
+
+	// the dependency tag used for items in this list that can be used to determine the memberType
+	// for a resolved dependency.
+	dependencyTag android.SdkMemberDependencyTag
+}
+
+func (p *sdkMemberTypeListProperty) propertyName() string {
+	return p.memberType.SdkPropertyName()
+}
+
+// Cache of dynamically generated dynamicSdkMemberTypes objects. The key is the pointer
+// to a slice of SdkMemberType instances held in android.SdkMemberTypes.
+var dynamicSdkMemberTypesMap android.OncePer
+
+// A dynamically generated set of member list properties and associated structure type.
+type dynamicSdkMemberTypes struct {
+	// The dynamically generated structure type.
+	//
+	// Contains one []string exported field for each android.SdkMemberTypes. The name of the field
+	// is the exported form of the value returned by SdkMemberType.SdkPropertyName().
+	propertiesStructType reflect.Type
+
+	// Information about each of the member type specific list properties.
+	memberTypeListProperties []*sdkMemberTypeListProperty
+
+	memberTypeToProperty map[android.SdkMemberType]*sdkMemberTypeListProperty
+}
+
+func (d *dynamicSdkMemberTypes) createMemberTypeListProperties() interface{} {
+	return reflect.New(d.propertiesStructType).Interface()
+}
+
+func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes {
+
+	// Get a key that uniquely identifies the registry contents.
+	key := registry.UniqueOnceKey()
+
+	// Get the registered types.
+	registeredTypes := registry.RegisteredTypes()
+
+	// Get the cached value, creating new instance if necessary.
+	return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
+		return createDynamicSdkMemberTypes(registeredTypes)
+	}).(*dynamicSdkMemberTypes)
+}
+
+// Create the dynamicSdkMemberTypes from the list of registered member types.
+//
+// A struct is created which contains one exported field per member type corresponding to
+// the SdkMemberType.SdkPropertyName() value.
+//
+// A list of sdkMemberTypeListProperty instances is created, one per member type that provides:
+// * a reference to the member type.
+// * a getter for the corresponding field in the properties struct.
+// * a dependency tag that identifies the member type of a resolved dependency.
+//
+func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
+
+	var listProperties []*sdkMemberTypeListProperty
+	memberTypeToProperty := map[android.SdkMemberType]*sdkMemberTypeListProperty{}
+	var fields []reflect.StructField
+
+	// Iterate over the member types creating StructField and sdkMemberTypeListProperty objects.
+	nextFieldIndex := 0
+	for _, memberType := range sdkMemberTypes {
+
+		p := memberType.SdkPropertyName()
+
+		var getter func(properties interface{}) []string
+		var setter func(properties interface{}, list []string)
+		if memberType.RequiresBpProperty() {
+			// Create a dynamic exported field for the member type's property.
+			fields = append(fields, reflect.StructField{
+				Name: proptools.FieldNameForProperty(p),
+				Type: reflect.TypeOf([]string{}),
+				Tag:  `android:"arch_variant"`,
+			})
+
+			// Copy the field index for use in the getter func as using the loop variable directly will
+			// cause all funcs to use the last value.
+			fieldIndex := nextFieldIndex
+			nextFieldIndex += 1
+
+			getter = func(properties interface{}) []string {
+				// The properties is expected to be of the following form (where
+				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
+				//     properties *struct {<Module_types> []string, ....}
+				//
+				// Although it accesses the field by index the following reflection code is equivalent to:
+				//    *properties.<Module_types>
+				//
+				list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
+				return list
+			}
+
+			setter = func(properties interface{}, list []string) {
+				// The properties is expected to be of the following form (where
+				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
+				//     properties *struct {<Module_types> []string, ....}
+				//
+				// Although it accesses the field by index the following reflection code is equivalent to:
+				//    *properties.<Module_types> = list
+				//
+				reflect.ValueOf(properties).Elem().Field(fieldIndex).Set(reflect.ValueOf(list))
+			}
+		}
+
+		// Create an sdkMemberTypeListProperty for the member type.
+		memberListProperty := &sdkMemberTypeListProperty{
+			getter:     getter,
+			setter:     setter,
+			memberType: memberType,
+
+			// Dependencies added directly from member properties are always exported.
+			dependencyTag: android.DependencyTagForSdkMemberType(memberType, true),
+		}
+
+		memberTypeToProperty[memberType] = memberListProperty
+		listProperties = append(listProperties, memberListProperty)
+	}
+
+	// Create a dynamic struct from the collated fields.
+	propertiesStructType := reflect.StructOf(fields)
+
+	return &dynamicSdkMemberTypes{
+		memberTypeListProperties: listProperties,
+		memberTypeToProperty:     memberTypeToProperty,
+		propertiesStructType:     propertiesStructType,
+	}
+}
diff --git a/sdk/sdk.go b/sdk/sdk.go
index b1c8aeb..6dea752 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"io"
-	"reflect"
 	"strconv"
 
 	"github.com/google/blueprint"
@@ -50,7 +49,7 @@
 	// The dynamically generated information about the registered SdkMemberType
 	dynamicSdkMemberTypes *dynamicSdkMemberTypes
 
-	// The dynamically created instance of the properties struct containing the sdk member
+	// The dynamically created instance of the properties struct containing the sdk member type
 	// list properties, e.g. java_libs.
 	dynamicMemberTypeListProperties interface{}
 
@@ -95,148 +94,6 @@
 	Prebuilt_visibility []string
 }
 
-// Contains information about the sdk properties that list sdk members, e.g.
-// Java_header_libs.
-type sdkMemberListProperty struct {
-	// getter for the list of member names
-	getter func(properties interface{}) []string
-
-	// setter for the list of member names
-	setter func(properties interface{}, list []string)
-
-	// the type of member referenced in the list
-	memberType android.SdkMemberType
-
-	// the dependency tag used for items in this list that can be used to determine the memberType
-	// for a resolved dependency.
-	dependencyTag android.SdkMemberTypeDependencyTag
-}
-
-func (p *sdkMemberListProperty) propertyName() string {
-	return p.memberType.SdkPropertyName()
-}
-
-// Cache of dynamically generated dynamicSdkMemberTypes objects. The key is the pointer
-// to a slice of SdkMemberType instances held in android.SdkMemberTypes.
-var dynamicSdkMemberTypesMap android.OncePer
-
-// A dynamically generated set of member list properties and associated structure type.
-type dynamicSdkMemberTypes struct {
-	// The dynamically generated structure type.
-	//
-	// Contains one []string exported field for each android.SdkMemberTypes. The name of the field
-	// is the exported form of the value returned by SdkMemberType.SdkPropertyName().
-	propertiesStructType reflect.Type
-
-	// Information about each of the member type specific list properties.
-	memberListProperties []*sdkMemberListProperty
-
-	memberTypeToProperty map[android.SdkMemberType]*sdkMemberListProperty
-}
-
-func (d *dynamicSdkMemberTypes) createMemberListProperties() interface{} {
-	return reflect.New(d.propertiesStructType).Interface()
-}
-
-func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes {
-
-	// Get a key that uniquely identifies the registry contents.
-	key := registry.UniqueOnceKey()
-
-	// Get the registered types.
-	registeredTypes := registry.RegisteredTypes()
-
-	// Get the cached value, creating new instance if necessary.
-	return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
-		return createDynamicSdkMemberTypes(registeredTypes)
-	}).(*dynamicSdkMemberTypes)
-}
-
-// Create the dynamicSdkMemberTypes from the list of registered member types.
-//
-// A struct is created which contains one exported field per member type corresponding to
-// the SdkMemberType.SdkPropertyName() value.
-//
-// A list of sdkMemberListProperty instances is created, one per member type that provides:
-// * a reference to the member type.
-// * a getter for the corresponding field in the properties struct.
-// * a dependency tag that identifies the member type of a resolved dependency.
-//
-func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
-
-	var listProperties []*sdkMemberListProperty
-	memberTypeToProperty := map[android.SdkMemberType]*sdkMemberListProperty{}
-	var fields []reflect.StructField
-
-	// Iterate over the member types creating StructField and sdkMemberListProperty objects.
-	nextFieldIndex := 0
-	for _, memberType := range sdkMemberTypes {
-
-		p := memberType.SdkPropertyName()
-
-		var getter func(properties interface{}) []string
-		var setter func(properties interface{}, list []string)
-		if memberType.RequiresBpProperty() {
-			// Create a dynamic exported field for the member type's property.
-			fields = append(fields, reflect.StructField{
-				Name: proptools.FieldNameForProperty(p),
-				Type: reflect.TypeOf([]string{}),
-				Tag:  `android:"arch_variant"`,
-			})
-
-			// Copy the field index for use in the getter func as using the loop variable directly will
-			// cause all funcs to use the last value.
-			fieldIndex := nextFieldIndex
-			nextFieldIndex += 1
-
-			getter = func(properties interface{}) []string {
-				// The properties is expected to be of the following form (where
-				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
-				//     properties *struct {<Module_types> []string, ....}
-				//
-				// Although it accesses the field by index the following reflection code is equivalent to:
-				//    *properties.<Module_types>
-				//
-				list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
-				return list
-			}
-
-			setter = func(properties interface{}, list []string) {
-				// The properties is expected to be of the following form (where
-				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
-				//     properties *struct {<Module_types> []string, ....}
-				//
-				// Although it accesses the field by index the following reflection code is equivalent to:
-				//    *properties.<Module_types> = list
-				//
-				reflect.ValueOf(properties).Elem().Field(fieldIndex).Set(reflect.ValueOf(list))
-			}
-		}
-
-		// Create an sdkMemberListProperty for the member type.
-		memberListProperty := &sdkMemberListProperty{
-			getter:     getter,
-			setter:     setter,
-			memberType: memberType,
-
-			// Dependencies added directly from member properties are always exported.
-			dependencyTag: android.DependencyTagForSdkMemberType(memberType, true),
-		}
-
-		memberTypeToProperty[memberType] = memberListProperty
-		listProperties = append(listProperties, memberListProperty)
-	}
-
-	// Create a dynamic struct from the collated fields.
-	propertiesStructType := reflect.StructOf(fields)
-
-	return &dynamicSdkMemberTypes{
-		memberListProperties: listProperties,
-		memberTypeToProperty: memberTypeToProperty,
-		propertiesStructType: propertiesStructType,
-	}
-}
-
 // sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.)
 // which Mainline modules like APEX can choose to build with.
 func SdkModuleFactory() android.Module {
@@ -247,16 +104,16 @@
 	s := &sdk{}
 	s.properties.Module_exports = moduleExports
 	// Get the dynamic sdk member type data for the currently registered sdk member types.
-	var registry *android.SdkMemberTypesRegistry
+	var typeRegistry *android.SdkMemberTypesRegistry
 	if moduleExports {
-		registry = android.ModuleExportsMemberTypes
+		typeRegistry = android.ModuleExportsMemberTypes
 	} else {
-		registry = android.SdkMemberTypes
+		typeRegistry = android.SdkMemberTypes
 	}
-	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(registry)
+	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(typeRegistry)
 	// Create an instance of the dynamically created struct that contains all the
 	// properties for the member type specific list properties.
-	s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberListProperties()
+	s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberTypeListProperties()
 	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
 
 	// Make sure that the prebuilt visibility property is verified for errors.
@@ -280,11 +137,11 @@
 	return s
 }
 
-func (s *sdk) memberListProperties() []*sdkMemberListProperty {
-	return s.dynamicSdkMemberTypes.memberListProperties
+func (s *sdk) memberTypeListProperties() []*sdkMemberTypeListProperty {
+	return s.dynamicSdkMemberTypes.memberTypeListProperties
 }
 
-func (s *sdk) memberListProperty(memberType android.SdkMemberType) *sdkMemberListProperty {
+func (s *sdk) memberTypeListProperty(memberType android.SdkMemberType) *sdkMemberTypeListProperty {
 	return s.dynamicSdkMemberTypes.memberTypeToProperty[memberType]
 }
 
@@ -341,6 +198,19 @@
 	}}
 }
 
+// newDependencyContext creates a new SdkDependencyContext for this sdk.
+func (s *sdk) newDependencyContext(mctx android.BottomUpMutatorContext) android.SdkDependencyContext {
+	return &dependencyContext{
+		BottomUpMutatorContext: mctx,
+	}
+}
+
+type dependencyContext struct {
+	android.BottomUpMutatorContext
+}
+
+var _ android.SdkDependencyContext = (*dependencyContext)(nil)
+
 // RegisterPreDepsMutators registers pre-deps mutators to support modules implementing SdkAware
 // interface and the sdk module type. This function has been made public to be called by tests
 // outside of the sdk package
@@ -410,14 +280,15 @@
 	if s, ok := mctx.Module().(*sdk); ok {
 		// Add dependencies from enabled and non CommonOS variants to the sdk member variants.
 		if s.Enabled() && !s.IsCommonOSVariant() {
-			for _, memberListProperty := range s.memberListProperties() {
+			ctx := s.newDependencyContext(mctx)
+			for _, memberListProperty := range s.memberTypeListProperties() {
 				if memberListProperty.getter == nil {
 					continue
 				}
 				names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
 				if len(names) > 0 {
 					tag := memberListProperty.dependencyTag
-					memberListProperty.memberType.AddDependencies(mctx, tag, names)
+					memberListProperty.memberType.AddDependencies(ctx, tag, names)
 				}
 			}
 		}
diff --git a/sdk/update.go b/sdk/update.go
index 1cd8f13..89a5c92 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -185,7 +185,7 @@
 	s.multilibUsages = multilibNone
 	ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
 		tag := ctx.OtherModuleDependencyTag(child)
-		if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok {
+		if memberTag, ok := tag.(android.SdkMemberDependencyTag); ok {
 			memberType := memberTag.SdkMemberType(child)
 
 			// If a nil SdkMemberType was returned then this module should not be added to the sdk.
@@ -251,7 +251,7 @@
 	}
 
 	var members []*sdkMember
-	for _, memberListProperty := range s.memberListProperties() {
+	for _, memberListProperty := range s.memberTypeListProperties() {
 		membersOfType := byType[memberListProperty.memberType]
 		members = append(members, membersOfType...)
 	}
@@ -667,7 +667,7 @@
 		staticProperties := &snapshotModuleStaticProperties{
 			Compile_multilib: sdkVariant.multilibUsages.String(),
 		}
-		dynamicProperties := s.dynamicSdkMemberTypes.createMemberListProperties()
+		dynamicProperties := s.dynamicSdkMemberTypes.createMemberTypeListProperties()
 
 		combinedProperties := &combinedSnapshotModuleProperties{
 			sdkVariant:        sdkVariant,
@@ -687,7 +687,7 @@
 		}
 
 		combined := sdkVariantToCombinedProperties[memberVariantDep.sdkVariant]
-		memberListProperty := s.memberListProperty(memberVariantDep.memberType)
+		memberListProperty := s.memberTypeListProperty(memberVariantDep.memberType)
 		memberName := ctx.OtherModuleName(memberVariantDep.variant)
 
 		if memberListProperty.getter == nil {
@@ -717,7 +717,7 @@
 	}
 
 	// Extract the common members, removing them from the original properties.
-	commonDynamicProperties := s.dynamicSdkMemberTypes.createMemberListProperties()
+	commonDynamicProperties := s.dynamicSdkMemberTypes.createMemberTypeListProperties()
 	extractor := newCommonValueExtractor(commonDynamicProperties)
 	extractCommonProperties(ctx, extractor, commonDynamicProperties, propertyContainers)
 
@@ -750,7 +750,7 @@
 	}
 
 	dynamicMemberTypeListProperties := combined.dynamicProperties
-	for _, memberListProperty := range s.memberListProperties() {
+	for _, memberListProperty := range s.memberTypeListProperties() {
 		if memberListProperty.getter == nil {
 			continue
 		}
@@ -765,6 +765,8 @@
 	name string
 }
 
+var _ android.BpPropertyTag = propertyTag{}
+
 // A BpPropertyTag to add to a property that contains references to other sdk members.
 //
 // This will cause the references to be rewritten to a versioned reference in the version
@@ -1563,10 +1565,6 @@
 	return archInfo
 }
 
-func (archInfo *archTypeSpecificInfo) optimizableProperties() interface{} {
-	return archInfo.Properties
-}
-
 // Get the link type of the variant
 //
 // If the variant is not differentiated by link type then it returns "",
@@ -1608,8 +1606,7 @@
 	addSdkMemberPropertiesToSet(ctx, archInfo.Properties, archTypePropertySet)
 
 	for _, linkInfo := range archInfo.linkInfos {
-		linkPropertySet := archTypePropertySet.AddPropertySet(linkInfo.linkType)
-		addSdkMemberPropertiesToSet(ctx, linkInfo.Properties, linkPropertySet)
+		linkInfo.addToPropertySet(ctx, archTypePropertySet)
 	}
 }
 
@@ -1640,6 +1637,11 @@
 	return linkInfo
 }
 
+func (l *linkTypeSpecificInfo) addToPropertySet(ctx *memberContext, propertySet android.BpPropertySet) {
+	linkPropertySet := propertySet.AddPropertySet(l.linkType)
+	addSdkMemberPropertiesToSet(ctx, l.Properties, linkPropertySet)
+}
+
 func (l *linkTypeSpecificInfo) String() string {
 	return fmt.Sprintf("LinkType{%s}", l.linkType)
 }
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index c6e98c7..db66ae2 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -26,6 +26,7 @@
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/cc"
+	"android/soong/snapshot"
 	"android/soong/tradefed"
 )
 
@@ -195,6 +196,9 @@
 	return proptools.String(s.properties.Sub_dir)
 }
 
+func (s *ShBinary) RelativeInstallPath() string {
+	return s.SubDir()
+}
 func (s *ShBinary) Installable() bool {
 	return s.properties.Installable == nil || proptools.Bool(s.properties.Installable)
 }
@@ -548,3 +552,5 @@
 }
 
 var Bool = proptools.Bool
+
+var _ snapshot.RelativeInstallPath = (*ShBinary)(nil)
diff --git a/snapshot/Android.bp b/snapshot/Android.bp
index f17ac53..3354993 100644
--- a/snapshot/Android.bp
+++ b/snapshot/Android.bp
@@ -11,12 +11,20 @@
         "soong",
         "soong-android",
     ],
+    // Source file name convention is to include _snapshot as a
+    // file suffix for files that are generating snapshots.
     srcs: [
+        "host_fake_snapshot.go",
+        "host_snapshot.go",
         "recovery_snapshot.go",
         "snapshot.go",
         "snapshot_base.go",
         "util.go",
         "vendor_snapshot.go",
     ],
+    testSrcs: [
+        "host_test.go",
+        "test.go",
+    ],
     pluginFor: ["soong_build"],
 }
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
new file mode 100644
index 0000000..6b4e12b
--- /dev/null
+++ b/snapshot/host_fake_snapshot.go
@@ -0,0 +1,149 @@
+// Copyright 2021 The Android Open Source Project
+//
+// 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 snapshot
+
+import (
+	"encoding/json"
+	"path/filepath"
+
+	"android/soong/android"
+)
+
+// The host_snapshot module creates a snapshot of host tools to be used
+// in a minimal source tree.   In order to create the host_snapshot the
+// user must explicitly list the modules to be included.  The
+// host-fake-snapshot, defined in this file, is a utility to help determine
+// which host modules are being used in the minimal source tree.
+//
+// The host-fake-snapshot is designed to run in a full source tree and
+// will result in a snapshot that contains an empty file for each host
+// tool found in the tree.  The fake snapshot is only used to determine
+// the host modules that the minimal source tree depends on, hence the
+// snapshot uses an empty file for each module and saves on having to
+// actually build any tool to generate the snapshot.  The fake snapshot
+// is compatible with an actual host_snapshot and is installed into a
+// minimal source tree via the development/vendor_snapshot/update.py
+// script.
+//
+// After generating the fake snapshot and installing into the minimal
+// source tree, the dependent modules are determined via the
+// development/vendor_snapshot/update.py script (see script for more
+// information).  These modules are then used to define the actual
+// host_snapshot to be used.  This is a similar process to the other
+// snapshots (vendor, recovery,...)
+//
+// Example
+//
+// Full source tree:
+//   1/ Generate fake host snapshot
+//
+// Minimal source tree:
+//   2/ Install the fake host snapshot
+//   3/ List the host modules used from the snapshot
+//   4/ Remove fake host snapshot
+//
+// Full source tree:
+//   4/ Create host_snapshot with modules identified in step 3
+//
+// Minimal source tree:
+//   5/ Install host snapshot
+//   6/ Build
+//
+// The host-fake-snapshot is a singleton module, that will be built
+// if HOST_FAKE_SNAPSHOT_ENABLE=true.
+
+func init() {
+	registerHostSnapshotComponents(android.InitRegistrationContext)
+}
+
+func registerHostSnapshotComponents(ctx android.RegistrationContext) {
+	ctx.RegisterSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
+}
+
+type hostFakeSingleton struct {
+	snapshotDir string
+	zipFile     android.OptionalPath
+}
+
+func (c *hostFakeSingleton) init() {
+	c.snapshotDir = "host-fake-snapshot"
+
+}
+func HostToolsFakeAndroidSingleton() android.Singleton {
+	singleton := &hostFakeSingleton{}
+	singleton.init()
+	return singleton
+}
+
+func (c *hostFakeSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	if !ctx.DeviceConfig().HostFakeSnapshotEnabled() {
+		return
+	}
+	// Find all host binary modules add 'fake' versions to snapshot
+	var outputs android.Paths
+	seen := make(map[string]bool)
+	var jsonData []SnapshotJsonFlags
+	ctx.VisitAllModules(func(module android.Module) {
+		if module.Target().Os != ctx.Config().BuildOSTarget.Os {
+			return
+		}
+		if module.Target().Arch.ArchType != ctx.Config().BuildOSTarget.Arch.ArchType {
+			return
+		}
+
+		if android.IsModulePrebuilt(module) {
+			return
+		}
+
+		if !module.Enabled() || module.IsHideFromMake() {
+			return
+		}
+		apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+		if !apexInfo.IsForPlatform() {
+			return
+		}
+		path := hostBinToolPath(module)
+		if path.Valid() && path.String() != "" {
+			outFile := filepath.Join(c.snapshotDir, path.String())
+			if !seen[outFile] {
+				seen[outFile] = true
+				outputs = append(outputs, WriteStringToFileRule(ctx, "", outFile))
+				jsonData = append(jsonData, *hostBinJsonDesc(module))
+			}
+		}
+	})
+
+	marsh, err := json.Marshal(jsonData)
+	if err != nil {
+		ctx.Errorf("host fake snapshot json marshal failure: %#v", err)
+		return
+	}
+	outputs = append(outputs, WriteStringToFileRule(ctx, string(marsh), filepath.Join(c.snapshotDir, "host_snapshot.json")))
+	c.zipFile = zipSnapshot(ctx, c.snapshotDir, c.snapshotDir, outputs)
+
+}
+func (c *hostFakeSingleton) MakeVars(ctx android.MakeVarsContext) {
+	if !c.zipFile.Valid() {
+		return
+	}
+	ctx.Phony(
+		"host-fake-snapshot",
+		c.zipFile.Path())
+
+	ctx.DistForGoal(
+		"host-fake-snapshot",
+		c.zipFile.Path())
+
+}
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
new file mode 100644
index 0000000..2a25a00
--- /dev/null
+++ b/snapshot/host_snapshot.go
@@ -0,0 +1,221 @@
+// Copyright 2021 The Android Open Source Project
+//
+// 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 snapshot
+
+import (
+	"encoding/json"
+	"fmt"
+	"path/filepath"
+	"sort"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+//
+// The host_snapshot module creates a snapshot of the modules defined in
+// the deps property.  The modules within the deps property (host tools)
+// are ones that return a valid path via HostToolPath() of the
+// HostToolProvider.  The created snapshot contains the binaries and any
+// transitive PackagingSpecs of the included host tools, along with a JSON
+// meta file.
+//
+// The snapshot is installed into a source tree via
+// development/vendor_snapshot/update.py, the included modules are
+// provided as preferred prebuilts.
+//
+// To determine which tools to include in the host snapshot see
+// host_fake_snapshot.go.
+
+func init() {
+	registerHostBuildComponents(android.InitRegistrationContext)
+}
+
+func registerHostBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("host_snapshot", hostSnapshotFactory)
+}
+
+// Relative installation path
+type RelativeInstallPath interface {
+	RelativeInstallPath() string
+}
+
+type hostSnapshot struct {
+	android.ModuleBase
+	android.PackagingBase
+
+	zipFile    android.OptionalPath
+	installDir android.InstallPath
+}
+
+func hostSnapshotFactory() android.Module {
+	module := &hostSnapshot{}
+	initHostToolsModule(module)
+	return module
+}
+func initHostToolsModule(module *hostSnapshot) {
+	android.InitPackageModule(module)
+	android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon)
+}
+
+var dependencyTag = struct {
+	blueprint.BaseDependencyTag
+	android.InstallAlwaysNeededDependencyTag
+	android.PackagingItemAlwaysDepTag
+}{}
+
+func (f *hostSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
+	f.AddDeps(ctx, dependencyTag)
+}
+func (f *hostSnapshot) installFileName() string {
+	return f.Name() + ".zip"
+}
+
+// Create zipfile with JSON description, notice files... for dependent modules
+func (f *hostSnapshot) CreateMetaData(ctx android.ModuleContext, fileName string) android.OutputPath {
+	var jsonData []SnapshotJsonFlags
+	var metaPaths android.Paths
+
+	metaZipFile := android.PathForModuleOut(ctx, fileName).OutputPath
+
+	// Create JSON file based on the direct dependencies
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		desc := hostBinJsonDesc(dep)
+		if desc != nil {
+			jsonData = append(jsonData, *desc)
+		}
+		if len(dep.EffectiveLicenseFiles()) > 0 {
+			noticeFile := android.PathForModuleOut(ctx, "NOTICE_FILES", dep.Name()+".txt").OutputPath
+			android.CatFileRule(ctx, dep.EffectiveLicenseFiles(), noticeFile)
+			metaPaths = append(metaPaths, noticeFile)
+		}
+
+	})
+	// Sort notice paths and json data for repeatble build
+	sort.Slice(jsonData, func(i, j int) bool {
+		return (jsonData[i].ModuleName < jsonData[j].ModuleName)
+	})
+	sort.Slice(metaPaths, func(i, j int) bool {
+		return (metaPaths[i].String() < metaPaths[j].String())
+	})
+
+	marsh, err := json.Marshal(jsonData)
+	if err != nil {
+		ctx.ModuleErrorf("host snapshot json marshal failure: %#v", err)
+		return android.OutputPath{}
+	}
+
+	jsonZipFile := android.PathForModuleOut(ctx, "host_snapshot.json").OutputPath
+	metaPaths = append(metaPaths, jsonZipFile)
+	rspFile := android.PathForModuleOut(ctx, "host_snapshot.rsp").OutputPath
+	android.WriteFileRule(ctx, jsonZipFile, string(marsh))
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+
+	builder.Command().
+		BuiltTool("soong_zip").
+		FlagWithArg("-C ", android.PathForModuleOut(ctx).OutputPath.String()).
+		FlagWithOutput("-o ", metaZipFile).
+		FlagWithRspFileInputList("-r ", rspFile, metaPaths)
+	builder.Build("zip_meta", fmt.Sprintf("zipping meta data for %s", ctx.ModuleName()))
+
+	return metaZipFile
+}
+
+// Create the host tool zip file
+func (f *hostSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Create a zip file for the binaries, and a zip of the meta data, then merge zips
+	depsZipFile := android.PathForModuleOut(ctx, f.Name()+"_deps.zip").OutputPath
+	modsZipFile := android.PathForModuleOut(ctx, f.Name()+"_mods.zip").OutputPath
+	outputFile := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
+
+	f.installDir = android.PathForModuleInstall(ctx)
+
+	f.CopyDepsToZip(ctx, depsZipFile)
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().
+		BuiltTool("zip2zip").
+		FlagWithInput("-i ", depsZipFile).
+		FlagWithOutput("-o ", modsZipFile).
+		Text("**/*:" + proptools.ShellEscape(f.installDir.String()))
+
+	metaZipFile := f.CreateMetaData(ctx, f.Name()+"_meta.zip")
+
+	builder.Command().
+		BuiltTool("merge_zips").
+		Output(outputFile).
+		Input(metaZipFile).
+		Input(modsZipFile)
+
+	builder.Build("manifest", fmt.Sprintf("Adding manifest %s", f.installFileName()))
+	zip := ctx.InstallFile(f.installDir, f.installFileName(), outputFile)
+	f.zipFile = android.OptionalPathForPath(zip)
+
+}
+
+// Implements android.AndroidMkEntriesProvider
+func (f *hostSnapshot) AndroidMkEntries() []android.AndroidMkEntries {
+	if !f.zipFile.Valid() {
+		return []android.AndroidMkEntries{}
+	}
+
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: f.zipFile,
+		DistFiles:  android.MakeDefaultDistFiles(f.zipFile.Path()),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
+			},
+		},
+	}}
+}
+
+// Get host tools path and relative install string helpers
+func hostBinToolPath(m android.Module) android.OptionalPath {
+	if provider, ok := m.(android.HostToolProvider); ok {
+		return provider.HostToolPath()
+	}
+	return android.OptionalPath{}
+
+}
+func hostRelativePathString(m android.Module) string {
+	var outString string
+	if rel, ok := m.(RelativeInstallPath); ok {
+		outString = rel.RelativeInstallPath()
+	}
+	return outString
+}
+
+// Create JSON description for given module, only create descriptions for binary modueles which
+// provide a valid HostToolPath
+func hostBinJsonDesc(m android.Module) *SnapshotJsonFlags {
+	path := hostBinToolPath(m)
+	relPath := hostRelativePathString(m)
+	if path.Valid() && path.String() != "" {
+		return &SnapshotJsonFlags{
+			ModuleName:          m.Name(),
+			ModuleStemName:      filepath.Base(path.String()),
+			Filename:            path.String(),
+			Required:            append(m.HostRequiredModuleNames(), m.RequiredModuleNames()...),
+			RelativeInstallPath: relPath,
+		}
+	}
+	return nil
+}
diff --git a/snapshot/host_test.go b/snapshot/host_test.go
new file mode 100644
index 0000000..ab9fedd
--- /dev/null
+++ b/snapshot/host_test.go
@@ -0,0 +1,170 @@
+// Copyright 2021 The Android Open Source Project
+//
+// 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 snapshot
+
+import (
+	"path/filepath"
+	"testing"
+
+	"android/soong/android"
+)
+
+// host_snapshot and host-fake-snapshot test functions
+
+type hostTestModule struct {
+	android.ModuleBase
+	props struct {
+		Deps []string
+	}
+}
+
+func hostTestBinOut(bin string) string {
+	return filepath.Join("out", "bin", bin)
+}
+
+func (c *hostTestModule) HostToolPath() android.OptionalPath {
+	return (android.OptionalPathForPath(android.PathForTesting(hostTestBinOut(c.Name()))))
+}
+
+func hostTestModuleFactory() android.Module {
+	m := &hostTestModule{}
+	m.AddProperties(&m.props)
+	android.InitAndroidArchModule(m, android.HostSupported, android.MultilibFirst)
+	return m
+}
+func (m *hostTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	builtFile := android.PathForModuleOut(ctx, m.Name())
+	dir := ctx.Target().Arch.ArchType.Multilib
+	installDir := android.PathForModuleInstall(ctx, dir)
+	ctx.InstallFile(installDir, m.Name(), builtFile)
+}
+
+// Common blueprint used for testing
+var hostTestBp = `
+		license_kind {
+			name: "test_notice",
+			conditions: ["notice"],
+		}
+		license {
+			name: "host_test_license",
+			visibility: ["//visibility:public"],
+			license_kinds: [
+				"test_notice"
+			],
+			license_text: [
+				"NOTICE",
+			],
+		}
+		component {
+			name: "foo",
+			deps: ["bar"],
+		}
+		component {
+			name: "bar",
+			licenses: ["host_test_license"],
+		}
+		`
+
+var hostTestModBp = `
+		host_snapshot {
+			name: "test-host-snapshot",
+			deps: [
+				"foo",
+			],
+		}
+		`
+
+var prepareForHostTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithAndroidBuildComponents,
+	android.PrepareForTestWithLicenses,
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("component", hostTestModuleFactory)
+	}),
+)
+
+// Prepare for host_snapshot test
+var prepareForHostModTest = android.GroupFixturePreparers(
+	prepareForHostTest,
+	android.FixtureWithRootAndroidBp(hostTestBp+hostTestModBp),
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		registerHostBuildComponents(ctx)
+	}),
+)
+
+// Prepare for fake host snapshot test disabled
+var prepareForFakeHostTest = android.GroupFixturePreparers(
+	prepareForHostTest,
+	android.FixtureWithRootAndroidBp(hostTestBp),
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		registerHostSnapshotComponents(ctx)
+	}),
+)
+
+// Prepare for fake host snapshot test enabled
+var prepareForFakeHostTestEnabled = android.GroupFixturePreparers(
+	prepareForFakeHostTest,
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.HostFakeSnapshotEnabled = true
+	}),
+)
+
+// Validate that a hostSnapshot object is created containing zip files and JSON file
+// content of zip file is not validated as this is done by PackagingSpecs
+func TestHostSnapshot(t *testing.T) {
+	result := prepareForHostModTest.RunTest(t)
+	t.Helper()
+	ctx := result.TestContext.ModuleForTests("test-host-snapshot", result.Config.BuildOS.String()+"_common")
+	mod := ctx.Module().(*hostSnapshot)
+	if ctx.MaybeOutput("host_snapshot.json").Rule == nil {
+		t.Error("Manifest file not found")
+	}
+	zips := []string{"_deps.zip", "_mods.zip", ".zip"}
+
+	for _, zip := range zips {
+		zFile := mod.Name() + zip
+		if ctx.MaybeOutput(zFile).Rule == nil {
+			t.Error("Zip file ", zFile, "not found")
+		}
+
+	}
+}
+
+// Validate fake host snapshot contains binary modules as well as the JSON meta file
+func TestFakeHostSnapshotEnable(t *testing.T) {
+	result := prepareForFakeHostTestEnabled.RunTest(t)
+	t.Helper()
+	bins := []string{"foo", "bar"}
+	ctx := result.TestContext.SingletonForTests("host-fake-snapshot")
+	if ctx.MaybeOutput(filepath.Join("host-fake-snapshot", "host_snapshot.json")).Rule == nil {
+		t.Error("Manifest file not found")
+	}
+	for _, bin := range bins {
+		if ctx.MaybeOutput(filepath.Join("host-fake-snapshot", hostTestBinOut(bin))).Rule == nil {
+			t.Error("Binary file ", bin, "not found")
+		}
+
+	}
+}
+
+// Validate not fake host snapshot if HostFakeSnapshotEnabled has not been set to true
+func TestFakeHostSnapshotDisable(t *testing.T) {
+	result := prepareForFakeHostTest.RunTest(t)
+	t.Helper()
+	ctx := result.TestContext.SingletonForTests("host-fake-snapshot")
+	if len(ctx.AllOutputs()) != 0 {
+		t.Error("Fake host snapshot not empty when disabled")
+	}
+
+}
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index de93f3e..79d3cf6 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -102,3 +102,19 @@
 		return isDirectoryExcluded(filepath.Dir(dir), excludedMap, includedMap)
 	}
 }
+
+// This is to be saved as .json files, which is for development/vendor_snapshot/update.py.
+// These flags become Android.bp snapshot module properties.
+//
+// Attributes are optional and will be populated based on each module's need.
+// Common attributes are defined here, languages may extend this struct to add
+// additional attributes.
+type SnapshotJsonFlags struct {
+	ModuleName          string `json:",omitempty"`
+	RelativeInstallPath string `json:",omitempty"`
+	Filename            string `json:",omitempty"`
+	ModuleStemName      string `json:",omitempty"`
+
+	// dependencies
+	Required []string `json:",omitempty"`
+}
diff --git a/snapshot/test.go b/snapshot/test.go
new file mode 100644
index 0000000..346af2b
--- /dev/null
+++ b/snapshot/test.go
@@ -0,0 +1,24 @@
+// Copyright 2021 The Android Open Source Project
+//
+// 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 snapshot
+
+import (
+	"os"
+	"testing"
+)
+
+func TestMain(m *testing.M) {
+	os.Exit(m.Run())
+}
diff --git a/snapshot/util.go b/snapshot/util.go
index 2297dfc..f447052 100644
--- a/snapshot/util.go
+++ b/snapshot/util.go
@@ -34,3 +34,22 @@
 	})
 	return outPath
 }
+
+// zip snapshot
+func zipSnapshot(ctx android.SingletonContext, dir string, baseName string, snapshotOutputs android.Paths) android.OptionalPath {
+	zipPath := android.PathForOutput(
+		ctx, dir, baseName+".zip")
+
+	zipRule := android.NewRuleBuilder(pctx, ctx)
+	rspFile := android.PathForOutput(
+		ctx, dir, baseName+"_list.rsp")
+
+	zipRule.Command().
+		BuiltTool("soong_zip").
+		FlagWithOutput("-o ", zipPath).
+		FlagWithArg("-C ", android.PathForOutput(ctx, dir).String()).
+		FlagWithRspFileInputList("-r ", rspFile, snapshotOutputs)
+
+	zipRule.Build(zipPath.String(), baseName+" snapshot "+zipPath.String())
+	return android.OptionalPathForPath(zipPath)
+}
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 610e427..a22adc5 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -144,7 +144,7 @@
   run_soong
   local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  local glob_deps_file=out/soong/.bootstrap/globs/0.d
+  local glob_deps_file=out/soong/globs/build/0.d
 
   if [ -e "$glob_deps_file" ]; then
     fail "Glob deps file unexpectedly written on first build"
@@ -472,17 +472,35 @@
   fi
 }
 
-function test_null_build_after_docs {
+function test_soong_docs_smoke() {
   setup
-  run_soong
-  local mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  prebuilts/build-tools/linux-x86/bin/ninja -f out/combined.ninja soong_docs
+  run_soong soong_docs
+
+  [[ -e "out/soong/docs/soong_build.html" ]] || fail "Documentation for main page not created"
+  [[ -e "out/soong/docs/cc.html" ]] || fail "Documentation for C++ modules not created"
+}
+
+function test_null_build_after_soong_docs() {
+  setup
 
   run_soong
-  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  if [[ "$mtime1" != "$mtime2" ]]; then
+  run_soong soong_docs
+  local docs_mtime1=$(stat -c "%y" out/soong/docs/soong_build.html)
+
+  run_soong soong_docs
+  local docs_mtime2=$(stat -c "%y" out/soong/docs/soong_build.html)
+
+  if [[ "$docs_mtime1" != "$docs_mtime2" ]]; then
+    fail "Output Ninja file changed on null build"
+  fi
+
+  run_soong
+  local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+  if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
     fail "Output Ninja file changed on null build"
   fi
 }
@@ -522,7 +540,7 @@
 
 function test_bp2build_smoke {
   setup
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   [[ -e out/soong/.bootstrap/bp2build_workspace_marker ]] || fail "bp2build marker file not created"
   [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
 }
@@ -531,7 +549,7 @@
   setup
   create_mock_bazel
 
-  run_bp2build
+  run_soong bp2build
 
   if [[ ! -f "./out/soong/.bootstrap/bp2build_workspace_marker" ]]; then
     fail "Marker file was not generated"
@@ -551,7 +569,7 @@
 }
 EOF
 
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   [[ -e out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created"
   [[ -L out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked"
 
@@ -565,7 +583,7 @@
 }
 EOF
 
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   [[ -e out/soong/bp2build/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created"
   [[ -L out/soong/workspace/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked"
 }
@@ -573,10 +591,10 @@
 function test_bp2build_null_build {
   setup
 
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   local mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   local mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
   if [[ "$mtime1" != "$mtime2" ]]; then
@@ -597,18 +615,35 @@
 }
 EOF
 
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   grep -q a1.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a1.txt not in ${GENERATED_BUILD_FILE_NAME} file"
 
   touch a/a2.txt
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   grep -q a2.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a2.txt not in ${GENERATED_BUILD_FILE_NAME} file"
 }
 
+function test_multiple_soong_build_modes() {
+  setup
+  run_soong json-module-graph bp2build nothing
+  if [[ ! -f "out/soong/.bootstrap/bp2build_workspace_marker" ]]; then
+    fail "bp2build marker file was not generated"
+  fi
+
+
+  if [[ ! -f "out/soong/module-graph.json" ]]; then
+    fail "JSON file was not created"
+  fi
+
+  if [[ ! -f "out/soong/build.ninja" ]]; then
+    fail "Main build.ninja file was not created"
+  fi
+}
+
 function test_dump_json_module_graph() {
   setup
-  GENERATE_JSON_MODULE_GRAPH=1 run_soong
-  if [[ ! -r "out/soong//module-graph.json" ]]; then
+  run_soong json-module-graph
+  if [[ ! -r "out/soong/module-graph.json" ]]; then
     fail "JSON file was not created"
   fi
 }
@@ -619,7 +654,7 @@
   run_soong
   local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  GENERATE_JSON_MODULE_GRAPH=1 run_soong
+  run_soong json-module-graph
   local json_mtime1=$(stat -c "%y" out/soong/module-graph.json)
 
   run_soong
@@ -628,7 +663,7 @@
     fail "Output Ninja file changed after writing JSON module graph"
   fi
 
-  GENERATE_JSON_MODULE_GRAPH=1 run_soong
+  run_soong json-module-graph
   local json_mtime2=$(stat -c "%y" out/soong/module-graph.json)
   if [[ "$json_mtime1" != "$json_mtime2" ]]; then
     fail "JSON module graph file changed after writing Ninja file"
@@ -651,7 +686,7 @@
 }
 EOF
 
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
   [[ -d out/soong/workspace/a/b ]] || fail "module directory not a directory"
   [[ -L "out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked"
@@ -675,10 +710,10 @@
 }
 EOF
 
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
 
   touch a/a2.txt  # No reference in the .bp file needed
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   [[ -L out/soong/workspace/a/a2.txt ]] || fail "a/a2.txt not symlinked"
 }
 
@@ -696,7 +731,7 @@
 }
 EOF
 
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   [[ -L "out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked"
   [[ "$(readlink -f out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/${GENERATED_BUILD_FILE_NAME}"$ ]] \
     || fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place"
@@ -725,7 +760,7 @@
 }
 EOF
 
-  if GENERATE_BAZEL_FILES=1 run_soong >& "$MOCK_TOP/errors"; then
+  if run_soong bp2build >& "$MOCK_TOP/errors"; then
     fail "Build should have failed"
   fi
 
@@ -739,7 +774,7 @@
   run_soong
   local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
   if [[ "$output_mtime1" != "$output_mtime2" ]]; then
     fail "Output Ninja file changed when switching to bp2build"
@@ -757,7 +792,7 @@
     fail "bp2build marker file changed when switching to regular build from bp2build"
   fi
 
-  GENERATE_BAZEL_FILES=1 run_soong
+  run_soong bp2build
   local output_mtime4=$(stat -c "%y" out/soong/build.ninja)
   local marker_mtime3=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
   if [[ "$output_mtime1" != "$output_mtime4" ]]; then
@@ -768,9 +803,32 @@
   fi
 }
 
+function test_queryview_smoke() {
+  setup
+
+  run_soong queryview
+  [[ -e out/soong/queryview/WORKSPACE ]] || fail "queryview WORKSPACE file not created"
+
+}
+
+function test_queryview_null_build() {
+  setup
+
+  run_soong queryview
+  local output_mtime1=$(stat -c "%y" out/soong/queryview.marker)
+
+  run_soong queryview
+  local output_mtime2=$(stat -c "%y" out/soong/queryview.marker)
+
+  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+    fail "Queryview marker file changed on null build"
+  fi
+}
+
 test_smoke
 test_null_build
-test_null_build_after_docs
+test_soong_docs_smoke
+test_null_build_after_soong_docs
 test_soong_build_rebuilt_if_blueprint_changes
 test_glob_noop_incremental
 test_add_file_to_glob
@@ -780,9 +838,12 @@
 test_add_file_to_soong_build
 test_glob_during_bootstrapping
 test_soong_build_rerun_iff_environment_changes
+test_multiple_soong_build_modes
 test_dump_json_module_graph
 test_json_module_graph_back_and_forth_null_build
 test_write_to_source_tree
+test_queryview_smoke
+test_queryview_null_build
 test_bp2build_smoke
 test_bp2build_generates_marker_file
 test_bp2build_null_build
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 9bd85a4..379eb65 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -10,10 +10,10 @@
 
 function test_bp2build_null_build() {
   setup
-  run_bp2build
+  run_soong bp2build
   local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
-  run_bp2build
+  run_soong bp2build
   local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
   if [[ "$output_mtime1" != "$output_mtime2" ]]; then
@@ -35,10 +35,10 @@
 EOF
   touch foo/bar/a.txt foo/bar/b.txt
 
-  run_bp2build
+  run_soong bp2build
   local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
-  run_bp2build
+  run_soong bp2build
   local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
   if [[ "$output_mtime1" != "$output_mtime2" ]]; then
@@ -80,7 +80,7 @@
   }
 EOF
 
-  run_bp2build
+  run_soong bp2build
 
   if [[ ! -f "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then
     fail "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME} was not generated"
diff --git a/tests/lib.sh b/tests/lib.sh
index 813a9dd..e777820 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -124,10 +124,6 @@
   tools/bazel "$@"
 }
 
-run_bp2build() {
-  GENERATE_BAZEL_FILES=true build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests nothing
-}
-
 run_ninja() {
   build/soong/soong_ui.bash --make-mode --skip-make --skip-soong-tests "$@"
 }
diff --git a/ui/build/build.go b/ui/build/build.go
index d869bf0..2e44aaa 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -248,6 +248,16 @@
 		what = what &^ RunNinja
 	}
 
+	if !config.SoongBuildInvocationNeeded() {
+		// This means that the output of soong_build is not needed and thus it would
+		// run unnecessarily. In addition, if this code wasn't there invocations
+		// with only special-cased target names like "m bp2build" would result in
+		// passing Ninja the empty target list and it would then build the default
+		// targets which is not what the user asked for.
+		what = what &^ RunNinja
+		what = what &^ RunKati
+	}
+
 	if config.StartGoma() {
 		startGoma(ctx, config)
 	}
@@ -278,16 +288,6 @@
 
 	if what&RunSoong != 0 {
 		runSoong(ctx, config)
-
-		if config.bazelBuildMode() == generateBuildFiles {
-			// Return early, if we're using Soong as solely the generator of BUILD files.
-			return
-		}
-
-		if config.bazelBuildMode() == generateJsonModuleGraph {
-			// Return early, if we're using Soong as solely the generator of the JSON module graph
-			return
-		}
 	}
 
 	if what&RunKati != 0 {
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 65b91bc..a3a1aaf 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -250,7 +250,10 @@
 	newFile = filepath.Join(basePath, newFile)
 	oldFile := newFile + ".previous"
 
-	if _, err := os.Stat(newFile); err != nil {
+	if _, err := os.Stat(newFile); os.IsNotExist(err) {
+		// If the file doesn't exist, assume no installed files exist either
+		return
+	} else if err != nil {
 		ctx.Fatalf("Expected %q to be readable", newFile)
 	}
 
diff --git a/ui/build/config.go b/ui/build/config.go
index 6a05f4f..35dacf2 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -33,7 +33,8 @@
 type Config struct{ *configImpl }
 
 type configImpl struct {
-	// From the environment
+	// Some targets that are implemented in soong_build
+	// (bp2build, json-module-graph) are not here and have their own bits below.
 	arguments     []string
 	goma          bool
 	environ       *Environment
@@ -41,17 +42,21 @@
 	buildDateTime string
 
 	// From the arguments
-	parallel       int
-	keepGoing      int
-	verbose        bool
-	checkbuild     bool
-	dist           bool
-	skipConfig     bool
-	skipKati       bool
-	skipKatiNinja  bool
-	skipSoong      bool
-	skipNinja      bool
-	skipSoongTests bool
+	parallel        int
+	keepGoing       int
+	verbose         bool
+	checkbuild      bool
+	dist            bool
+	jsonModuleGraph bool
+	bp2build        bool
+	queryview       bool
+	soongDocs       bool
+	skipConfig      bool
+	skipKati        bool
+	skipKatiNinja   bool
+	skipSoong       bool
+	skipNinja       bool
+	skipSoongTests  bool
 
 	// From the product config
 	katiArgs        []string
@@ -106,12 +111,6 @@
 	// Don't use bazel at all.
 	noBazel bazelBuildMode = iota
 
-	// Only generate build files (in a subdirectory of the out directory) and exit.
-	generateBuildFiles
-
-	// Only generate the Soong json module graph for use with jq, and exit.
-	generateJsonModuleGraph
-
 	// Generate synthetic build files and incorporate these files into a build which
 	// partially uses Bazel. Build metadata may come from Android.bp or BUILD files.
 	mixedBuild
@@ -639,6 +638,14 @@
 			c.environ.Set(k, v)
 		} else if arg == "dist" {
 			c.dist = true
+		} else if arg == "json-module-graph" {
+			c.jsonModuleGraph = true
+		} else if arg == "bp2build" {
+			c.bp2build = true
+		} else if arg == "queryview" {
+			c.queryview = true
+		} else if arg == "soong_docs" {
+			c.soongDocs = true
 		} else {
 			if arg == "checkbuild" {
 				c.checkbuild = true
@@ -705,6 +712,26 @@
 	return c.arguments
 }
 
+func (c *configImpl) SoongBuildInvocationNeeded() bool {
+	if c.Dist() {
+		return true
+	}
+
+	if len(c.Arguments()) > 0 {
+		// Explicit targets requested that are not special targets like b2pbuild
+		// or the JSON module graph
+		return true
+	}
+
+	if !c.JsonModuleGraph() && !c.Bp2Build() && !c.Queryview() && !c.SoongDocs() {
+		// Command line was empty, the default Ninja target is built
+		return true
+	}
+
+	// build.ninja doesn't need to be generated
+	return false
+}
+
 func (c *configImpl) OutDir() string {
 	if outDir, ok := c.environ.Get("OUT_DIR"); ok {
 		return outDir
@@ -739,6 +766,28 @@
 	return filepath.Join(c.OutDir(), "soong")
 }
 
+func (c *configImpl) PrebuiltOS() string {
+	switch runtime.GOOS {
+	case "linux":
+		return "linux-x86"
+	case "darwin":
+		return "darwin-x86"
+	default:
+		panic("Unknown GOOS")
+	}
+}
+func (c *configImpl) HostToolDir() string {
+	return filepath.Join(c.SoongOutDir(), "host", c.PrebuiltOS(), "bin")
+}
+
+func (c *configImpl) NamedGlobFile(name string) string {
+	return shared.JoinPath(c.SoongOutDir(), ".bootstrap/build-globs."+name+".ninja")
+}
+
+func (c *configImpl) UsedEnvFile(tag string) string {
+	return shared.JoinPath(c.SoongOutDir(), usedEnvFile+"."+tag)
+}
+
 func (c *configImpl) MainNinjaFile() string {
 	return shared.JoinPath(c.SoongOutDir(), "build.ninja")
 }
@@ -747,6 +796,14 @@
 	return shared.JoinPath(c.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
 }
 
+func (c *configImpl) SoongDocsHtml() string {
+	return shared.JoinPath(c.SoongOutDir(), "docs/soong_build.html")
+}
+
+func (c *configImpl) QueryviewMarkerFile() string {
+	return shared.JoinPath(c.SoongOutDir(), "queryview.marker")
+}
+
 func (c *configImpl) ModuleGraphFile() string {
 	return shared.JoinPath(c.SoongOutDir(), "module-graph.json")
 }
@@ -776,6 +833,22 @@
 	return c.dist
 }
 
+func (c *configImpl) JsonModuleGraph() bool {
+	return c.jsonModuleGraph
+}
+
+func (c *configImpl) Bp2Build() bool {
+	return c.bp2build
+}
+
+func (c *configImpl) Queryview() bool {
+	return c.queryview
+}
+
+func (c *configImpl) SoongDocs() bool {
+	return c.soongDocs
+}
+
 func (c *configImpl) IsVerbose() bool {
 	return c.verbose
 }
@@ -921,10 +994,6 @@
 func (c *configImpl) bazelBuildMode() bazelBuildMode {
 	if c.Environment().IsEnvTrue("USE_BAZEL_ANALYSIS") {
 		return mixedBuild
-	} else if c.Environment().IsEnvTrue("GENERATE_BAZEL_FILES") {
-		return generateBuildFiles
-	} else if c.Environment().IsEnvTrue("GENERATE_JSON_MODULE_GRAPH") {
-		return generateJsonModuleGraph
 	} else {
 		return noBazel
 	}
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 09d53cc..8f74969 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -15,15 +15,16 @@
 package build
 
 import (
-	"android/soong/finder"
-	"android/soong/finder/fs"
-	"android/soong/ui/logger"
 	"bytes"
 	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strings"
 
+	"android/soong/finder"
+	"android/soong/finder/fs"
+	"android/soong/ui/logger"
+
 	"android/soong/ui/metrics"
 )
 
@@ -72,8 +73,6 @@
 			"AndroidProducts.mk",
 			// General Soong build definitions, using the Blueprint syntax.
 			"Android.bp",
-			// build/blueprint build definitions, using the Blueprint syntax.
-			"Blueprints",
 			// Bazel build definitions.
 			"BUILD.bazel",
 			// Bazel build definitions.
@@ -165,8 +164,6 @@
 
 	// Recursively look for all Android.bp files
 	androidBps := f.FindNamedAt(".", "Android.bp")
-	// The files are named "Blueprints" only in the build/blueprint directory.
-	androidBps = append(androidBps, f.FindNamedAt("build/blueprint", "Blueprints")...)
 	if len(androidBps) == 0 {
 		ctx.Fatalf("No Android.bp found")
 	}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 058f819..617d293 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -37,6 +37,12 @@
 const (
 	availableEnvFile = "soong.environment.available"
 	usedEnvFile      = "soong.environment.used"
+
+	soongBuildTag      = "build"
+	bp2buildTag        = "bp2build"
+	jsonModuleGraphTag = "modulegraph"
+	queryviewTag       = "queryview"
+	soongDocsTag       = "soong_docs"
 )
 
 func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string) error {
@@ -71,9 +77,17 @@
 // A tiny struct used to tell Blueprint that it's in bootstrap mode. It would
 // probably be nicer to use a flag in bootstrap.Args instead.
 type BlueprintConfig struct {
-	soongOutDir      string
-	outDir           string
-	debugCompilation bool
+	toolDir                   string
+	soongOutDir               string
+	outDir                    string
+	runGoTests                bool
+	debugCompilation          bool
+	subninjas                 []string
+	primaryBuilderInvocations []bootstrap.PrimaryBuilderInvocation
+}
+
+func (c BlueprintConfig) HostToolDir() string {
+	return c.toolDir
 }
 
 func (c BlueprintConfig) SoongOutDir() string {
@@ -84,14 +98,26 @@
 	return c.outDir
 }
 
+func (c BlueprintConfig) RunGoTests() bool {
+	return c.runGoTests
+}
+
 func (c BlueprintConfig) DebugCompilation() bool {
 	return c.debugCompilation
 }
 
-func environmentArgs(config Config, suffix string) []string {
+func (c BlueprintConfig) Subninjas() []string {
+	return c.subninjas
+}
+
+func (c BlueprintConfig) PrimaryBuilderInvocations() []bootstrap.PrimaryBuilderInvocation {
+	return c.primaryBuilderInvocations
+}
+
+func environmentArgs(config Config, tag string) []string {
 	return []string{
 		"--available_env", shared.JoinPath(config.SoongOutDir(), availableEnvFile),
-		"--used_env", shared.JoinPath(config.SoongOutDir(), usedEnvFile+suffix),
+		"--used_env", config.UsedEnvFile(tag),
 	}
 }
 
@@ -109,112 +135,134 @@
 	}
 }
 
+func primaryBuilderInvocation(config Config, name string, output string, specificArgs []string) bootstrap.PrimaryBuilderInvocation {
+	commonArgs := make([]string, 0, 0)
+
+	if !config.skipSoongTests {
+		commonArgs = append(commonArgs, "-t")
+	}
+
+	commonArgs = append(commonArgs, "-l", filepath.Join(config.FileListDir(), "Android.bp.list"))
+
+	if os.Getenv("SOONG_DELVE") != "" {
+		commonArgs = append(commonArgs, "--delve_listen", os.Getenv("SOONG_DELVE"))
+		commonArgs = append(commonArgs, "--delve_path", shared.ResolveDelveBinary())
+	}
+
+	allArgs := make([]string, 0, 0)
+	allArgs = append(allArgs, specificArgs...)
+	allArgs = append(allArgs,
+		"--globListDir", name,
+		"--globFile", config.NamedGlobFile(name))
+
+	allArgs = append(allArgs, commonArgs...)
+	allArgs = append(allArgs, environmentArgs(config, name)...)
+	allArgs = append(allArgs, "Android.bp")
+
+	return bootstrap.PrimaryBuilderInvocation{
+		Inputs:  []string{"Android.bp"},
+		Outputs: []string{output},
+		Args:    allArgs,
+	}
+}
+
 func bootstrapBlueprint(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
 	defer ctx.EndTrace()
 
-	var args bootstrap.Args
-
-	bootstrapGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.ninja")
-	bp2buildGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.bp2build.ninja")
-	moduleGraphGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.modulegraph.ninja")
-
-	// The glob .ninja files are subninja'd. However, they are generated during
-	// the build itself so we write an empty file so that the subninja doesn't
-	// fail on clean builds
-	writeEmptyGlobFile(ctx, bootstrapGlobFile)
-	writeEmptyGlobFile(ctx, bp2buildGlobFile)
-	bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
-
-	args.RunGoTests = !config.skipSoongTests
-	args.UseValidations = true // Use validations to depend on tests
-	args.BuildDir = config.SoongOutDir()
-	args.NinjaBuildDir = config.OutDir()
-	args.TopFile = "Android.bp"
-	args.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
-	args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
-	// The primary builder (aka soong_build) will use bootstrapGlobFile as the globFile to generate build.ninja(.d)
-	// Building soong_build does not require a glob file
-	// Using "" instead of "<soong_build_glob>.ninja" will ensure that an unused glob file is not written to out/soong/.bootstrap during StagePrimary
-	args.Subninjas = []string{bootstrapGlobFile, bp2buildGlobFile}
-	args.GeneratingPrimaryBuilder = true
-	args.EmptyNinjaFile = config.EmptyNinjaFile()
-
-	args.DelveListen = os.Getenv("SOONG_DELVE")
-	if args.DelveListen != "" {
-		args.DelvePath = shared.ResolveDelveBinary()
+	mainSoongBuildExtraArgs := []string{"-o", config.MainNinjaFile()}
+	if config.EmptyNinjaFile() {
+		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--empty-ninja-file")
 	}
 
-	commonArgs := bootstrap.PrimaryBuilderExtraFlags(args, config.MainNinjaFile())
-	mainSoongBuildInputs := []string{"Android.bp"}
+	mainSoongBuildInvocation := primaryBuilderInvocation(
+		config,
+		soongBuildTag,
+		config.MainNinjaFile(),
+		mainSoongBuildExtraArgs)
 
 	if config.bazelBuildMode() == mixedBuild {
-		mainSoongBuildInputs = append(mainSoongBuildInputs, config.Bp2BuildMarkerFile())
+		// Mixed builds call Bazel from soong_build and they therefore need the
+		// Bazel workspace to be available. Make that so by adding a dependency on
+		// the bp2build marker file to the action that invokes soong_build .
+		mainSoongBuildInvocation.Inputs = append(mainSoongBuildInvocation.Inputs,
+			config.Bp2BuildMarkerFile())
 	}
 
-	soongBuildArgs := []string{
-		"--globListDir", "globs",
-		"--globFile", bootstrapGlobFile,
+	bp2buildInvocation := primaryBuilderInvocation(
+		config,
+		bp2buildTag,
+		config.Bp2BuildMarkerFile(),
+		[]string{
+			"--bp2build_marker", config.Bp2BuildMarkerFile(),
+		})
+
+	jsonModuleGraphInvocation := primaryBuilderInvocation(
+		config,
+		jsonModuleGraphTag,
+		config.ModuleGraphFile(),
+		[]string{
+			"--module_graph_file", config.ModuleGraphFile(),
+		})
+
+	queryviewInvocation := primaryBuilderInvocation(
+		config,
+		queryviewTag,
+		config.QueryviewMarkerFile(),
+		[]string{
+			"--bazel_queryview_dir", filepath.Join(config.SoongOutDir(), "queryview"),
+		})
+
+	soongDocsInvocation := primaryBuilderInvocation(
+		config,
+		soongDocsTag,
+		config.SoongDocsHtml(),
+		[]string{
+			"--soong_docs", config.SoongDocsHtml(),
+		})
+
+	globFiles := []string{
+		config.NamedGlobFile(soongBuildTag),
+		config.NamedGlobFile(bp2buildTag),
+		config.NamedGlobFile(jsonModuleGraphTag),
+		config.NamedGlobFile(queryviewTag),
+		config.NamedGlobFile(soongDocsTag),
 	}
 
-	soongBuildArgs = append(soongBuildArgs, commonArgs...)
-	soongBuildArgs = append(soongBuildArgs, environmentArgs(config, "")...)
-	soongBuildArgs = append(soongBuildArgs, "Android.bp")
-
-	mainSoongBuildInvocation := bootstrap.PrimaryBuilderInvocation{
-		Inputs:  mainSoongBuildInputs,
-		Outputs: []string{config.MainNinjaFile()},
-		Args:    soongBuildArgs,
+	// The glob .ninja files are subninja'd. However, they are generated during
+	// the build itself so we write an empty file if the file does not exist yet
+	// so that the subninja doesn't fail on clean builds
+	for _, globFile := range globFiles {
+		writeEmptyGlobFile(ctx, globFile)
 	}
 
-	bp2buildArgs := []string{
-		"--bp2build_marker", config.Bp2BuildMarkerFile(),
-		"--globListDir", "globs.bp2build",
-		"--globFile", bp2buildGlobFile,
-	}
+	var blueprintArgs bootstrap.Args
 
-	bp2buildArgs = append(bp2buildArgs, commonArgs...)
-	bp2buildArgs = append(bp2buildArgs, environmentArgs(config, ".bp2build")...)
-	bp2buildArgs = append(bp2buildArgs, "Android.bp")
-
-	bp2buildInvocation := bootstrap.PrimaryBuilderInvocation{
-		Inputs:  []string{"Android.bp"},
-		Outputs: []string{config.Bp2BuildMarkerFile()},
-		Args:    bp2buildArgs,
-	}
-
-	moduleGraphArgs := []string{
-		"--module_graph_file", config.ModuleGraphFile(),
-		"--globListDir", "globs.modulegraph",
-		"--globFile", moduleGraphGlobFile,
-	}
-
-	moduleGraphArgs = append(moduleGraphArgs, commonArgs...)
-	moduleGraphArgs = append(moduleGraphArgs, environmentArgs(config, ".modulegraph")...)
-	moduleGraphArgs = append(moduleGraphArgs, "Android.bp")
-
-	moduleGraphInvocation := bootstrap.PrimaryBuilderInvocation{
-		Inputs:  []string{"Android.bp"},
-		Outputs: []string{config.ModuleGraphFile()},
-		Args:    moduleGraphArgs,
-	}
-
-	args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{
-		bp2buildInvocation,
-		mainSoongBuildInvocation,
-		moduleGraphInvocation,
-	}
+	blueprintArgs.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
+	blueprintArgs.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
+	blueprintArgs.EmptyNinjaFile = false
 
 	blueprintCtx := blueprint.NewContext()
 	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
 	blueprintConfig := BlueprintConfig{
-		soongOutDir:      config.SoongOutDir(),
-		outDir:           config.OutDir(),
+		soongOutDir: config.SoongOutDir(),
+		toolDir:     config.HostToolDir(),
+		outDir:      config.OutDir(),
+		runGoTests:  !config.skipSoongTests,
+		// If we want to debug soong_build, we need to compile it for debugging
 		debugCompilation: os.Getenv("SOONG_DELVE") != "",
+		subninjas:        globFiles,
+		primaryBuilderInvocations: []bootstrap.PrimaryBuilderInvocation{
+			mainSoongBuildInvocation,
+			bp2buildInvocation,
+			jsonModuleGraphInvocation,
+			queryviewInvocation,
+			soongDocsInvocation},
 	}
 
-	bootstrapDeps := bootstrap.RunBlueprint(args, blueprintCtx, blueprintConfig)
-	err := deptools.WriteDepFile(bootstrapDepFile, args.OutFile, bootstrapDeps)
+	bootstrapDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
+	bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
+	err := deptools.WriteDepFile(bootstrapDepFile, blueprintArgs.OutFile, bootstrapDeps)
 	if err != nil {
 		ctx.Fatalf("Error writing depfile '%s': %s", bootstrapDepFile, err)
 	}
@@ -225,6 +273,7 @@
 		v, _ := currentEnv.Get(k)
 		return v
 	}
+
 	if stale, _ := shared.StaleEnvFile(envFile, getenv); stale {
 		os.Remove(envFile)
 	}
@@ -246,7 +295,7 @@
 	}
 
 	buildMode := config.bazelBuildMode()
-	integratedBp2Build := (buildMode == mixedBuild) || (buildMode == generateBuildFiles)
+	integratedBp2Build := buildMode == mixedBuild
 
 	// This is done unconditionally, but does not take a measurable amount of time
 	bootstrapBlueprint(ctx, config)
@@ -274,16 +323,26 @@
 		ctx.BeginTrace(metrics.RunSoong, "environment check")
 		defer ctx.EndTrace()
 
-		soongBuildEnvFile := filepath.Join(config.SoongOutDir(), usedEnvFile)
-		checkEnvironmentFile(soongBuildEnv, soongBuildEnvFile)
+		checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongBuildTag))
 
-		if integratedBp2Build {
-			bp2buildEnvFile := filepath.Join(config.SoongOutDir(), usedEnvFile+".bp2build")
-			checkEnvironmentFile(soongBuildEnv, bp2buildEnvFile)
+		if integratedBp2Build || config.Bp2Build() {
+			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(bp2buildTag))
+		}
+
+		if config.JsonModuleGraph() {
+			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(jsonModuleGraphTag))
+		}
+
+		if config.Queryview() {
+			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(queryviewTag))
+		}
+
+		if config.SoongDocs() {
+			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongDocsTag))
 		}
 	}()
 
-	runMicrofactory(ctx, config, ".bootstrap/bpglob", "github.com/google/blueprint/bootstrap/bpglob",
+	runMicrofactory(ctx, config, filepath.Join(config.HostToolDir(), "bpglob"), "github.com/google/blueprint/bootstrap/bpglob",
 		map[string]string{"github.com/google/blueprint": "build/blueprint"})
 
 	ninja := func(name, ninjaFile string, targets ...string) {
@@ -322,18 +381,30 @@
 		cmd.RunAndStreamOrFatal()
 	}
 
-	var target string
+	targets := make([]string, 0, 0)
 
-	if config.bazelBuildMode() == generateBuildFiles {
-		target = config.Bp2BuildMarkerFile()
-	} else if config.bazelBuildMode() == generateJsonModuleGraph {
-		target = config.ModuleGraphFile()
-	} else {
-		// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
-		target = config.MainNinjaFile()
+	if config.JsonModuleGraph() {
+		targets = append(targets, config.ModuleGraphFile())
 	}
 
-	ninja("bootstrap", ".bootstrap/build.ninja", target)
+	if config.Bp2Build() {
+		targets = append(targets, config.Bp2BuildMarkerFile())
+	}
+
+	if config.Queryview() {
+		targets = append(targets, config.QueryviewMarkerFile())
+	}
+
+	if config.SoongDocs() {
+		targets = append(targets, config.SoongDocsHtml())
+	}
+
+	if config.SoongBuildInvocationNeeded() {
+		// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
+		targets = append(targets, config.MainNinjaFile())
+	}
+
+	ninja("bootstrap", ".bootstrap/build.ninja", targets...)
 
 	var soongBuildMetrics *soong_metrics_proto.SoongBuildMetrics
 	if shouldCollectBuildSoongMetrics(config) {
@@ -375,7 +446,7 @@
 func shouldCollectBuildSoongMetrics(config Config) bool {
 	// Do not collect metrics protobuf if the soong_build binary ran as the
 	// bp2build converter or the JSON graph dump.
-	return config.bazelBuildMode() != generateBuildFiles && config.bazelBuildMode() != generateJsonModuleGraph
+	return config.SoongBuildInvocationNeeded()
 }
 
 func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics {
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 57ceaba..f9a60b6 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -51,7 +51,6 @@
 	executable := config.PrebuiltBuildTool("ninja")
 
 	commonArgs := []string{}
-	commonArgs = append(commonArgs, config.NinjaArgs()...)
 	commonArgs = append(commonArgs, "-f", config.CombinedNinjaFile())
 	args := append(commonArgs, "-t", "targets", "rule")